import java.util.*;
import java.io.*;

public class Rank2
{
	public static final double DAMPING=0.15;
	public static int usersize=0;
	public static void main(String[] args)
	{
		if(args.length!=1)
		{
			System.out.println("incorrect args\nshould be: dir_of_files");
			return;
		}
		String[] result=getlist(args[0]);
		int n=result.length;
		usersize=n;

		Runtime.getRuntime().gc();
		long before=usedMemory(),after;
		int list=0, col, dead=0;
		
		User[] users=new User[n];
		for(int i=0; i<n; i++)
			users[i]=new User(0);
		
		for(int i=0; i<n; i++)
		{
			String x=result[i];
			try
			{
				list=0;
				dead=0;
				users[i].resize(numlines(args[0]+"/"+x));

				Scanner inf=new Scanner(new File(args[0]+"/"+x));

				while(inf.hasNext())
				{
					String f=inf.nextLine().split(" ")[0];
					col=indexOf(result,f);
					if(col>-1)
						users[i].links[list++]=users[col];
					else
						dead++;
				}

				inf.close();
				users[i].trim(dead);	//remove null elements from links array
			}
			catch(FileNotFoundException e){System.out.println("Couldn't find "+x);}
			Runtime.getRuntime().gc();
			after=usedMemory();
			System.out.println("Row "+(i+1)+"/"+n+" completed.\t("+(100*(i+1)/n)+"%) \t"+list+"L "+(dead==0?"  ":(dead+"D"))
					+"\tw="+users[i].weightsum+"\tMemory Usage="+(after-before));
		}
		
		System.out.println("Users built.");
		
		final double MAXD=0.000001;
		double maxdiff=9, mdt, dampvalue;
		for(int iter=0; maxdiff>MAXD; iter++)
		{
			maxdiff=0;
			dampvalue=0;
			
			for(int i=0; i<n; i++)
				dampvalue+=users[i].out * DAMPING/users[i].weightsum;
			
			for(int i=0; i<n; i++)
			{
				users[i].adddamping(dampvalue);
				users[i].spread();
			}
			for(int i=0; i<n; i++)
			{
				mdt=users[i].reset();
				if(mdt>maxdiff)
					maxdiff=mdt;
			}
			System.out.println("Power "+(iter+1)+" done. Maxdiff="+maxdiff+" vs. "+MAXD);
		}
		
		double max=0, min=999,		//what are the bounds on the .out variables?
			adjust=1000000;					//what will be the new MAX?
			
		for(int i=0; i<n; i++)
		{
			users[i].calcPopularity();

			if(users[i].out>max)
				max=users[i].out;
			if(users[i].out<min)
				min=users[i].out;			//find the node with the smallest value,
		}

		for(int i=0; i<n; i++)
		{
			System.out.println(result[i]+"#"+
				(int)((users[i].out-min)/(max-min)*(adjust-1)+1)
				+"#"+users[i].size+"#"+users[i].popularity);
		}
	}
	public static String[] getlist(String dir)
	{
		String s=null;
		String[] result=null;
		try
		{
			Process p = Runtime.getRuntime().exec("ls "+dir);
			BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
			BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            int i=0;
			while ((s = stdInput.readLine()) != null)
				i++;
			
			result=new String[i];

			p = Runtime.getRuntime().exec("ls "+dir);
			stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
			stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            i=0;
			while ((s = stdInput.readLine()) != null)
				result[i++]=s;
		}
		catch(IOException e)
		{
		}
		return sort(result);
	}
	public static int numlines(String file)
	{
		int ret=0;
		try{
		Scanner scan=new Scanner(new File(file));
		while(scan.hasNext())
		{
			scan.nextLine();
			ret++;
		}
		}
		catch(IOException e){}
		return ret;
	}
	public static int indexOf(String[] list, String target){return indexOf(list,target,0,list.length-1);}
	public static int indexOf(String[] list, String target, int low, int high)
	{
		if(low>high)
			return -1;
		int mid=low+(high-low)/2;
		if(target.compareTo(list[mid])<0)
			return indexOf(list,target,low,mid-1);
		if(target.compareTo(list[mid])>0)
			return indexOf(list,target,mid+1,high);
		return mid;
	}
	public static String[] sort(String[] list)
	{
		for(int i=0; i<list.length-1; i++)
		{
			int target=i;
			for(int j=i+1; j<list.length; j++)
				if(list[target].compareTo(list[j])>0)
					target=j;
			if(target!=i)
			{
				String temp=list[i];
				list[i]=list[target];
				list[target]=temp;
			}
		}
		return list;
	}
	private static long usedMemory()
    {
		Runtime s_runtime = Runtime.getRuntime ();
        return s_runtime.totalMemory() - s_runtime.freeMemory();
    }
}

class User
{
	public static final double DAMPING=Rank2.DAMPING;
	
	public double weightsum;
	public User[] links;
	public double in, out;
	public int size;
	public int popularity;
	public User(int x)
	{
		size=x;
		weightsum=DAMPING*(Rank2.usersize-1)+(1-DAMPING)*(size);
		links=new User[size];
		in=0;
		out=1;
		popularity=0;
	}
	public void resize(int x)
	{
		size=x;
		links=new User[size];
		weightsum=DAMPING*(Rank2.usersize-1)+(1-DAMPING)*(size);
	}
	public void adddamping(double dampvalue)
	{
		this.in+=dampvalue - DAMPING/weightsum * out;
	}
	public void spread()
	{
		for(int i=0; i<size; i++)
			links[i].in+=(1-DAMPING)/weightsum * out;
	}
	public double reset()
	{
		double temp=out-in;
		out=in;
		in=0;
		return (temp>0)?temp:(0-temp);
	}
	public void trim(int dead)
	{
		User[] links2=new User[size-dead];
		int pos=0;
		for(User x:links)
			if(x!=null)
				links2[pos++]=x;
		links=links2;
		size-=dead;
		weightsum=DAMPING*(Rank2.usersize-1)+(1-DAMPING)*(size);
	}
	public void calcPopularity()
	{
		for(User x:links)
			x.popularity++;
	}
}
