import java.util.ArrayList;
import java.util.Arrays;

public class BinPackingProblem implements Annealable {
    public static java.util.Random random 
	= new java.util.Random();
    public int min;
    public int max;
    public int items;
    public int bins;
    public int[] sizes;
    public int[] placements;
    public int[] binLoads;
    public int lastItem = -1;
    public int lastItem2 = -1;
    public int lastPlacement = -1;
    public boolean swapping = false;

    public BinPackingProblem() {
	this(1, 100, 100, 10);
    }

    public BinPackingProblem(int min, int max, int items, int bins) {
	this.min = min;
	this.max = max;
	this.items = items;
	this.bins = bins;
	sizes = new int[items];
	placements = new int[items];
	binLoads = new int[bins];
	for (int i = 0; i < items; i++) {
	    sizes[i] = random.nextInt(max - min + 1) + min;
	    placements[i] = random.nextInt(bins);
	    binLoads[placements[i]] += sizes[i];
	}
    }

    public void next() {
	if (swapping) {
	    lastItem = random.nextInt(items);
	    lastItem2 = random.nextInt(items);
	    while (lastItem == lastItem2) 
		lastItem2 = random.nextInt(items);
	    int temp = placements[lastItem];
	    placements[lastItem] = placements[lastItem2];
	    placements[lastItem2] = temp;
	    int diff = sizes[lastItem] - sizes[lastItem2];
	    binLoads[placements[lastItem]] += diff;
	    binLoads[placements[lastItem2]] -= diff;
	}
	else {
	    lastItem = random.nextInt(items);
	    lastPlacement = placements[lastItem];
	    while (placements[lastItem] == lastPlacement)
		placements[lastItem] = random.nextInt(bins);
	    binLoads[lastPlacement] -= sizes[lastItem];
	    binLoads[placements[lastItem]] += sizes[lastItem];
	}
    }

    public void reject() {
	if (swapping) {
	    int temp = placements[lastItem];
	    placements[lastItem] = placements[lastItem2];
	    placements[lastItem2] = temp;
	    int diff = sizes[lastItem] - sizes[lastItem2];
	    binLoads[placements[lastItem]] += diff;
	    binLoads[placements[lastItem2]] -= diff;
	}
	else {
	    binLoads[placements[lastItem]] -= sizes[lastItem];
	    binLoads[lastPlacement] += sizes[lastItem];
	    placements[lastItem] = lastPlacement;
	}
    }
	
    public double energy() {
	int min = binLoads[0];
	int max = binLoads[0];	
	for (int i = 1; i < bins; i++) {
	    if (binLoads[i] < min)
		min = binLoads[i];
	    if (binLoads[i] > max)
		max = binLoads[i];
	}
	return max - min;
    }

    public Object clone() {
	try {
	    BinPackingProblem bp = (BinPackingProblem) super.clone();
	    bp.sizes = sizes.clone();
	    bp.placements = placements.clone();
	    bp.binLoads = binLoads.clone();
	    return bp;
	}
	catch (CloneNotSupportedException e) {
	    System.out.println(e);
	}
	return null;
    }

    public String toString() {
	StringBuffer sb = new StringBuffer();
	int[] loads = new int[bins];
	ArrayList[] contents = new ArrayList[bins];
	for (int b = 0; b < bins; b++)
	    contents[b] = new ArrayList<Integer>();
	for (int i = 0; i < items; i++) {
	    loads[placements[i]] += sizes[i];
	    contents[placements[i]].add(sizes[i]);
	}
	for (int b = 0; b < bins; b++) {
	    sb.append(loads[b] + " = ");
	    Object[] content = contents[b].toArray();
	    Arrays.sort(content);
	    for (int i = content.length - 1; i >= 0 ; i--) {
		if (i < content.length - 1)
		    sb.append("+ ");
		sb.append(content[i] + " ");
	    }
	    sb.append("\n");
	}
	sb.append("Energy = " + energy() + "\n");
	return sb.toString();
    }
}



