/*-
 * Copyright (c) 2009, Alexandre P. Francisco <aplf@ist.utl.pt>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package rank;

//import it.unimi.dsi.logging.ProgressLogger;

//import org.apache.log4j.Logger;

import cern.colt.GenericSorting;
import cern.colt.Swapper;
import cern.colt.function.IntComparator;
import cern.colt.list.IntArrayList;
import cern.colt.map.OpenIntDoubleHashMap;

public abstract class RankMethod {

	//private static Logger LOGGER = Logger.getLogger(RankMethod.class.getName());
	
	//final private ProgressLogger pl = new ProgressLogger(LOGGER, ClusterIt.LOGINT);

	protected WeightedImmutableGraph ig;
	final protected double alpha;
	final protected int maxIter;
	protected int currIter;

	public RankMethod(WeightedImmutableGraph graph, double alpha, int maxIter) {
		this.ig = graph;
		this.maxIter = maxIter;
		this.currIter = 0;
		this.alpha = alpha;
	}

	public void reset() {
		currIter = 0;
	}
	
	public OpenIntDoubleHashMap compute(OpenIntDoubleHashMap rankVector) {
		//pl.start("Computing rank vector...");
		//pl.expectedUpdates = maxIter - currIter;
		//pl.itemsName = "steps";
		while (currIter++ < maxIter) {
			rankVector = this.computeOneStep(rankVector);
			//pl.update();
		}
		//pl.done();

		return rankVector;
	}

	public OpenIntDoubleHashMap	computeWithTruncation(OpenIntDoubleHashMap rankVector, double threshold) {

		//pl.start("Computing rank vector...");
		//pl.expectedUpdates = maxIter - currIter;
		//pl.itemsName = "steps";
		while (currIter++ < maxIter) {
			rankVector = this.computeOneStep(rankVector);

			for (int i = 0; i < rankVector.size(); i++)
				if (rankVector.get(i) < threshold)
					rankVector.removeKey(i);
			rankVector.trimToSize();

			//pl.update();
		}
		//pl.done();

		return rankVector;
	}

	private class InnerSwapper implements Swapper {

		int[] sortedIndex;

		public InnerSwapper(int[] sortedIndex) {
			this.sortedIndex = sortedIndex;
		}

		public void swap(int a, int b) {
			int t = sortedIndex[a];
			sortedIndex[a] = sortedIndex[b];
			sortedIndex[b] = t;
		}
	}

	private class InnerIntComparator implements IntComparator {

		OpenIntDoubleHashMap rankVector;
		int[] sortedIndex;

		public InnerIntComparator(OpenIntDoubleHashMap rankVector, int[] sortedIndex) {
			this.rankVector = rankVector;
			this.sortedIndex = sortedIndex;
		}

		public int compare(int a, int b) {
			return rankVector.get(sortedIndex[a]) >
				rankVector.get(sortedIndex[b]) ?
				-1 :
				rankVector.get(sortedIndex[a]) < 
				rankVector.get(sortedIndex[b]) ?
				1 : 
				(sortedIndex[a] < sortedIndex[b] ?
				 -1 :
				 (sortedIndex[a] == sortedIndex [b] ? 0 : 1));
		}
	}

	public OpenIntDoubleHashMap	computeWithTruncation(OpenIntDoubleHashMap rankVector, int size) {

		//pl.start("Computing rank vector...");
		//pl.expectedUpdates = maxIter - currIter;
		//pl.itemsName = "steps";
		while (currIter++ < maxIter) {
			rankVector = this.computeOneStep(rankVector);

			IntArrayList rankIndexList = rankVector.keys(); 
			int[] sortedIndex = rankIndexList.elements();
			Swapper swapper = new InnerSwapper(sortedIndex);
			IntComparator comp = new InnerIntComparator(rankVector, sortedIndex);
			GenericSorting.quickSort(0, sortedIndex.length, comp, swapper);

			for (int i = size + 1; i < sortedIndex.length; i++)
					rankVector.removeKey(sortedIndex[i]);
			rankVector.trimToSize();

			//pl.update();
		}
		//pl.done();


		return rankVector;
	}

	public abstract OpenIntDoubleHashMap computeOneStep(OpenIntDoubleHashMap p);
}
