import java.io.*;
import java.util.*;

public class PowerLawList {
    private int nodes;
    private double scale;
    private double gamma;
    private int minDegree;
    private int nodeSum;
    private int edgeSum;
    private int[] degreeHist;

    PowerLawList (int nodes, double gamma, int minDegree) {
		this.nodes = nodes;
		this.gamma = gamma;
		this.minDegree = minDegree;
		initScale();
		calcList();
//		print("List Initial Histogram");
    };
    public double Y(double x) {
		return scale*Math.pow(x,gamma);
    }
    public double Yinv(double y) {
		return Math.exp((Math.log(y/scale))/gamma);
    }
    public int[] degreeHist() {
		return degreeHist;
    }
    public int edges() {
		return edgeSum;
    }
    public int nodes() {
		return nodes;
    }
    public int minDegree() {
		return minDegree;
    }
    public void print(String title) {
		System.out.print(title);
		int iNodes = 0, iEdges = 0;
		for (int i=0; i<degreeHist.length; i++) {
			int jNodes = degreeHist[i];
			int jEdges = i+minDegree;
			System.out.print("\t"+jEdges+":"+jNodes);
			iNodes += jNodes;
			iEdges += jEdges*jNodes;
		}
		System.out.println("  [Nodes: "+iNodes+" Edges: "+(iEdges/2)+"]");
    }

    private double lop(double d) {
		return lop(d, 2);
    }
    private double lop(double d, int i) {
		double fact = (double)Math.pow(10, i);
		return Math.round(d*fact)/fact;
    }
    private void initScale() {
		scale = nodes/Math.pow(minDegree,gamma);
		System.out.println("Initial Histogram: N="+nodes
						   +", G="+gamma
						   +", K="+minDegree);
		double x, xN, nodeSum=0, edgeSum=0;
		for (x=minDegree,xN=Yinv(0.5); x<=xN; x++) {
			double y = Y(x);
			System.out.print((int)x+":"+lop(y,1)+"\t");
			nodeSum += y;
			edgeSum += y*x;
		}
		System.out.println();
		scale = scale*nodes/nodeSum;
		System.out.println("Totals: nodeSum="+lop(nodeSum)
						   +", scale="+lop(scale)
						   +", edgeSum="+lop(edgeSum));
    }
    private void buildList() {
		degreeHist = new int[(int)Yinv(0.5)-minDegree+1]; //truncate x-> 0 
		nodeSum = 0; edgeSum = 0;
		for (int x=0; x<degreeHist.length; x++) {
			int y = (int)Math.round(Y(x+minDegree));
			degreeHist[x] = y;
			nodeSum += y;
			edgeSum += y*(x+minDegree);
		}
    }
    private void calcList() {
		buildList();
		if ( nodes != nodeSum ) {
			System.out.print("Recalc: nodeSum="+nodeSum
							 +", scale: "+lop(scale));
			scale = scale*nodes/nodeSum;
			System.out.println(" -> "+lop(scale));
			buildList();
		}
		if ( nodes != nodeSum ) {
			System.out.println("Recalc: nodeSum="+nodeSum
                               +", edge delta="+((nodes-nodeSum)*minDegree));
			degreeHist[0] += nodes-nodeSum;
			edgeSum += (nodes-nodeSum)*minDegree;
			nodeSum = nodes;
		}
		if ( edgeSum%2 != 0 ) {
			degreeHist[0] += 1;
			degreeHist[1] -= 1;
			edgeSum -= 1;
			System.out.println("Recalc: even edgeSum="+edgeSum);
		}
		System.out.println("Initial Scale="
						   +lop(nodes/Math.pow(minDegree,gamma))
						   +", Final Scale="+lop(scale));
    }
}

