//元コード(MCP出力抜粋) http://  pastebin.com/N9VpfQqY
//実行結果(ダイヤ赤石) http://  pastebin.com/cungQRLv
//実行結果(鉄・金鉱石) http://  pastebin.com/WTTuZbCu

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

class Ideone
{
	static final int loopCount=10000;//10000*1000;
	static final int oreSize=7;

	//Minecraftからメソッド名だけ流用
    private int numberOfBlocks;
	//Minecraftから流用、手抜きWorldに対応化
    public boolean generate_1_8_0(World worldIn, Random p_180709_2_, BlockPos p_180709_3_)
    {
        float var4 = p_180709_2_.nextFloat() * (float)Math.PI;
        double var5 = (double)((float)(p_180709_3_.getX() + 8) + MathHelper.sin(var4) * (float)this.numberOfBlocks / 8.0F);
        double var7 = (double)((float)(p_180709_3_.getX() + 8) - MathHelper.sin(var4) * (float)this.numberOfBlocks / 8.0F);
        double var9 = (double)((float)(p_180709_3_.getZ() + 8) + MathHelper.cos(var4) * (float)this.numberOfBlocks / 8.0F);
        double var11 = (double)((float)(p_180709_3_.getZ() + 8) - MathHelper.cos(var4) * (float)this.numberOfBlocks / 8.0F);
        double var13 = (double)(p_180709_3_.getY() + p_180709_2_.nextInt(3) - 2);
        double var15 = (double)(p_180709_3_.getY() + p_180709_2_.nextInt(3) - 2);

        for (int var17 = 0; var17 < this.numberOfBlocks; ++var17)
        {
            float var18 = (float)var17 / (float)this.numberOfBlocks;
            double var19 = var5 + (var7 - var5) * (double)var18;
            double var21 = var13 + (var15 - var13) * (double)var18;
            double var23 = var9 + (var11 - var9) * (double)var18;
            double var25 = p_180709_2_.nextDouble() * (double)this.numberOfBlocks / 16.0D;
            double var27 = (double)(MathHelper.sin((float)Math.PI * var18) + 1.0F) * var25 + 1.0D;
            double var29 = (double)(MathHelper.sin((float)Math.PI * var18) + 1.0F) * var25 + 1.0D;
            int var31 = MathHelper.floor_double(var19 - var27 / 2.0D);
            int var32 = MathHelper.floor_double(var21 - var29 / 2.0D);
            int var33 = MathHelper.floor_double(var23 - var27 / 2.0D);
            int var34 = MathHelper.floor_double(var19 + var27 / 2.0D);
            int var35 = MathHelper.floor_double(var21 + var29 / 2.0D);
            int var36 = MathHelper.floor_double(var23 + var27 / 2.0D);

            for (int var37 = var31; var37 <= var34; ++var37)
            {
                double var38 = ((double)var37 + 0.5D - var19) / (var27 / 2.0D);

                if (var38 * var38 < 1.0D)
                {
                    for (int var40 = var32; var40 <= var35; ++var40)
                    {
                        double var41 = ((double)var40 + 0.5D - var21) / (var29 / 2.0D);

                        if (var38 * var38 + var41 * var41 < 1.0D)
                        {
                            for (int var43 = var33; var43 <= var36; ++var43)
                            {
                                double var44 = ((double)var43 + 0.5D - var23) / (var27 / 2.0D);

                                if (var38 * var38 + var41 * var41 + var44 * var44 < 1.0D)
                                {
                                    worldIn.set(var37, var40, var43);
                                    //BlockPos var46 = new BlockPos(var37, var40, var43);
                                    //
                                    //if (this.field_175919_c.apply(worldIn.getBlockState(var46)))
                                    //{
                                    //    worldIn.setBlockState(var46, this.oreBlock, 2);
                                    //}
                                }
                            }
                        }
                    }
                }
            }
        }

        return true;
    }
	//Minecraftから流用、手抜きWorldに対応化
    public boolean generate_1_7_10(World p_76484_1_, Random p_76484_2_, int p_76484_3_, int p_76484_4_, int p_76484_5_)
    {
        float var6 = p_76484_2_.nextFloat() * (float)Math.PI;
        double var7 = (double)((float)(p_76484_3_ + 8) + MathHelper.sin(var6) * (float)this.numberOfBlocks / 8.0F);
        double var9 = (double)((float)(p_76484_3_ + 8) - MathHelper.sin(var6) * (float)this.numberOfBlocks / 8.0F);
        double var11 = (double)((float)(p_76484_5_ + 8) + MathHelper.cos(var6) * (float)this.numberOfBlocks / 8.0F);
        double var13 = (double)((float)(p_76484_5_ + 8) - MathHelper.cos(var6) * (float)this.numberOfBlocks / 8.0F);
        double var15 = (double)(p_76484_4_ + p_76484_2_.nextInt(3) - 2);
        double var17 = (double)(p_76484_4_ + p_76484_2_.nextInt(3) - 2);

        for (int var19 = 0; var19 <= this.numberOfBlocks; ++var19)
        {
            double var20 = var7 + (var9 - var7) * (double)var19 / (double)this.numberOfBlocks;
            double var22 = var15 + (var17 - var15) * (double)var19 / (double)this.numberOfBlocks;
            double var24 = var11 + (var13 - var11) * (double)var19 / (double)this.numberOfBlocks;
            double var26 = p_76484_2_.nextDouble() * (double)this.numberOfBlocks / 16.0D;
            double var28 = (double)(MathHelper.sin((float)var19 * (float)Math.PI / (float)this.numberOfBlocks) + 1.0F) * var26 + 1.0D;
            double var30 = (double)(MathHelper.sin((float)var19 * (float)Math.PI / (float)this.numberOfBlocks) + 1.0F) * var26 + 1.0D;
            int var32 = MathHelper.floor_double(var20 - var28 / 2.0D);
            int var33 = MathHelper.floor_double(var22 - var30 / 2.0D);
            int var34 = MathHelper.floor_double(var24 - var28 / 2.0D);
            int var35 = MathHelper.floor_double(var20 + var28 / 2.0D);
            int var36 = MathHelper.floor_double(var22 + var30 / 2.0D);
            int var37 = MathHelper.floor_double(var24 + var28 / 2.0D);

            for (int var38 = var32; var38 <= var35; ++var38)
            {
                double var39 = ((double)var38 + 0.5D - var20) / (var28 / 2.0D);

                if (var39 * var39 < 1.0D)
                {
                    for (int var41 = var33; var41 <= var36; ++var41)
                    {
                        double var42 = ((double)var41 + 0.5D - var22) / (var30 / 2.0D);

                        if (var39 * var39 + var42 * var42 < 1.0D)
                        {
                            for (int var44 = var34; var44 <= var37; ++var44)
                            {
                                double var45 = ((double)var44 + 0.5D - var24) / (var28 / 2.0D);

                                if (var39 * var39 + var42 * var42 + var45 * var45 < 1.0D)
                                //if (var39 * var39 + var42 * var42 + var45 * var45 < 1.0D && p_76484_1_.getBlock(var38, var41, var44) == this.field_150518_c)
                                {
                                    p_76484_1_.set(var38, var41, var44);
                                    //p_76484_1_.setBlock(var38, var41, var44, this.field_150519_a, 0, 2);
                                }
                            }
                        }
                    }
                }
            }
        }

        return true;
    }
	//Minecraftから一部のみ流用
	static class MathHelper{
		private static final float[] SIN_TABLE = new float[65536];
		public static float sin(float p_76126_0_)
		{
		    return SIN_TABLE[(int)(p_76126_0_ * 10430.378F) & 65535];
		}
		public static float cos(float p_76134_0_)
		{
		    return SIN_TABLE[(int)(p_76134_0_ * 10430.378F + 16384.0F) & 65535];
		}
		public static int floor_double(double p_76128_0_)
		{
			int var2 = (int)p_76128_0_;
			return p_76128_0_ < (double)var2 ? var2 - 1 : var2;
		}
		static
		{
			for (int var0 = 0; var0 < 65536; ++var0)
			{
			    SIN_TABLE[var0] = (float)Math.sin((double)var0 * Math.PI * 2.0D / 65536.0D);
			}
		}
	}
	//クラス名、一部のメソッド名だけMinecraftから手抜き流用
	//Setコレクション対応、Tree(Sort)対応
	class BlockPos implements Comparable<BlockPos>{
		public int hash=0;
		public final int x,y,z;
		public BlockPos(int _x,int _y, int _z){
			x=_x;y=_y;z=_z;hash=0;
		}
		public int getX(){return this.x;}
		public int getY(){return this.y;}
		public int getZ(){return this.z;}
		public boolean eq(BlockPos p){
			return (this.x==p.x&&this.y==p.y&&this.z==p.z);
		}
		@Override public boolean equals(Object p){
			if(!(p instanceof BlockPos))return false;
			return eq((BlockPos)p);
		}
		@Override public int hashCode(){
			//ハッシュの衝突率がヤバかったのでStringのハッシュに丸投げ
			//return x*0x101+y*0x10001+z*0x1000001;
			if(hash!=0)return hash;
			return hash=this.toString().hashCode();
		}
		@Override public String toString(){
			String s;s="("+x+","+y+","+z+")";
			return s;
		}
		@Override public int compareTo(BlockPos o){
			if(x!=o.x)return x-o.x;
			if(y!=o.y)return y-o.y;
			if(z!=o.z)return z-o.z;
			return 0;
		}
	}
	//クラス名だけMinecraftから手抜き流用
	//Setコレクション対応
	class World{
		public int hash=0;
		public TreeSet<BlockPos> list;
		public TreeSet<BlockPos> worklist;
		public World(){
			list=new TreeSet<BlockPos>();
			worklist=new TreeSet<BlockPos>();
			hash=0;
		}
		public World(World p){
			list=new TreeSet<BlockPos>(p.list);
			worklist=new TreeSet<BlockPos>(p.worklist);
			hash=0;
		}
		public void cls(){
			list.clear();
			hash=0;
		}
		public void set(int x,int y,int z){
			list.add(new BlockPos(x,y,z));
			hash=0;
		}
		//各軸の最小値が0になるよう正規化
		public void fix(){
			int mx,my,mz;mx=my=mz=Integer.MAX_VALUE;
			Iterator<BlockPos> i=list.iterator();
			while(i.hasNext()){
				BlockPos p=(BlockPos)i.next();
				mx=Math.min(mx,p.x);
				my=Math.min(my,p.y);
				mz=Math.min(mz,p.z);
			}
			i=list.iterator();
			while(i.hasNext()){
				BlockPos p=(BlockPos)i.next();
				worklist.add(new BlockPos(p.x-mx,p.y-my,p.z-mz));
			}
			list.clear();
			TreeSet<BlockPos> tmp=list;
			list=worklist;
			worklist=tmp;
			hash=0;
		}
		//0～15の値から上下反転、前後反転、水平回転の組み合わせを求めてflip()する
		public World flip(int count){
			return flip((count&8)!=0,(count&4)!=0,count&3);
		}
		//現在の状態を新しいWorldインスタンスに複製し、上下反転、前後反転、水平回転を行ってからfix()したものを返す
		public World flip(boolean flipX,boolean flipY,int rotXZ){
			int nx,ny,nz;
			World r=new World();
			Iterator<BlockPos> i=list.iterator();
			while(i.hasNext()){
				BlockPos p=(BlockPos)i.next();
				nx=p.x*(flipX?-1:1);
				ny=p.y*(flipY?-1:1);
				nz=p.z;
				if((rotXZ&2)!=0){
					nx=-nx;
					nz=-nz;
				}
				if((rotXZ&1)!=0){
					int t=nz;
					nz=nx;
					nx=-t;
				}
				r.set(nx,ny,nz);
			}
			r.fix();
			return r;
		}
		//現在の状態を新しいWorldインスタンスに複製し、返す
		public World clone(){
			return new World(this);
		}
		//回転・反転対象な物を含めた形状比較
		//public boolean eq(World p){
		//	return
		//		list.equals(p.flip( 0).list)||
		//		list.equals(p.flip( 1).list)||
		//		list.equals(p.flip( 2).list)||
		//		list.equals(p.flip( 3).list)||
		//		list.equals(p.flip( 4).list)||
		//		list.equals(p.flip( 5).list)||
		//		list.equals(p.flip( 6).list)||
		//		list.equals(p.flip( 7).list)||
		//		list.equals(p.flip( 8).list)||
		//		list.equals(p.flip( 9).list)||
		//		list.equals(p.flip(10).list)||
		//		list.equals(p.flip(11).list)||
		//		list.equals(p.flip(12).list)||
		//		list.equals(p.flip(13).list)||
		//		list.equals(p.flip(14).list)||
		//		list.equals(p.flip(15).list);
        //
		//}
		@Override public boolean equals(Object p){
			if(!(p instanceof World))return false;
			return list.equals(((World)p).list);
		}
		@Override public int hashCode(){
			//ハッシュの衝突率がヤバかったのでStringのハッシュに丸投げ
			//return list.hashCode();
			if(hash!=0)return hash;
			return hash=this.toString().hashCode();
		}
		@Override public String toString(){
			int mx,mz;mx=mz=Integer.MIN_VALUE;
			String s,sep="";
			Iterator<BlockPos> i=list.iterator();
			if(list.size()==0)mx=mz=0;
			s="count="+list.size()+" list=";
			while(i.hasNext()){
				BlockPos p=(BlockPos)i.next();
				s=s+sep+p.toString();
				mx=Math.max(mx,p.x+1);
				mz=Math.max(mz,p.z+1);
				sep="/";
			}
			s="width=["+Math.max(mx,mz)+"-"+Math.min(mx,mz)+"] "+s;
			return s;
		}
	}
	public static void main (String[] args) throws java.lang.Exception
	{
		try{
			Ideone i=new Ideone();
			i.main2();
		}catch(Exception e){
			e.printStackTrace(System.out);
		}
	}
	//本体
	public void main2() throws java.lang.Exception{
		Random   r=new Random();
		BlockPos p=new BlockPos(100,100,100);
		World    w;
		HashMap<World,Integer> list17=new HashMap<World,Integer>();
		HashMap<World,Integer> list18=new HashMap<World,Integer>();
		//テストする実装(1.7.10、1.8)の数だけループ
		for(int stage=0;stage<2;stage++){
			HashMap<World,Integer> list;
			w=new World();
			if(stage==0){
				//1.7.10用の処理、oreSizeはそのまま
				list=list17;
				this.numberOfBlocks=oreSize;
			}else{
				//1.8用の処理、oreSizeは+1
				list=list18;
				this.numberOfBlocks=oreSize+1;
			}
			//シミュレーションループ
			for(int i=0;i<loopCount;i++){
				//プログレス
				if((i&0x0fff)==0)System.err.print("stage="+stage+" loopCount="+i+"/"+loopCount+"    \r");
				//簡易ワールドをクリアして鉱脈生成
				w.cls();
				if(stage==0)
					generate_1_7_10(w,r,100,100,100);
				else
					generate_1_8_0(w,r,p);
				//とりあえずfixだけしてマップに追加、追加済みならインクリメント
				w.fix();
				if(list.containsKey(w))
					list.put(w.clone(),list.get(w)+1);
				else
					list.put(w.clone(),1);
			}
			//回転・反転のバリエーションを統合
			HashMap<World,Integer> worklist=new HashMap<World,Integer>();
			Iterator<World> j=new HashSet<World>(list.keySet()).iterator();//HashMap操作するから気休め程度にクローンしておく
			while(j.hasNext()){
				w=(World)j.next();
				//ハッシュコードが最大となる形状に統合する。
				int index=0,hash=w.hashCode(),count=0;
				for(int k=0;k<16;k++){//上下反転(2パターン)×前後反転(2パターン)×水平回転(4パターン)=16
					World f=w.flip(k);
					//最大値がコリジョンしたら統合先が一意に定まらないので最後の共通形状判定で見落とす…
					if(f.hashCode()==hash && index!=k && !w.flip(index).equals(f)){
						System.err.println("");
						System.err.println("hash collision. index"+index+"#"+hash+" == index"+k+"#"+f.hashCode());
						System.err.println(" index"+index+" "+w.flip(index));
						System.err.println(" index"+k+" "+f);
						System.out.println("hash collision. index"+index+"#"+hash+" == index"+k+"#"+f.hashCode());
						System.out.println(" index"+index+" "+w.flip(index));
						System.out.println(" index"+k+" "+f);
					}
					if(f.hashCode()>hash){
						index=k;
						hash=f.hashCode();
					}
					if(list.containsKey(f)){
						count+=list.get(f);
						list.put(f,0);
					}
				}
				list.put(w.flip(index),count);
			}
			//統合の結果不要になった要素を消去
			j=list.keySet().iterator();
			while(j.hasNext()){
				w=(World)j.next();
				if(list.get(w)<=0)
					j.remove();
			}
		}
		System.err.println("");
		//表示
		for(int stage=0;stage<2;stage++){
			HashMap<World,Integer> list,anot;
			if(stage==0){
				list=list17;
				anot=list18;
				System.out.println("1.7/count="+list.size());
			}else{
				list=list18;
				anot=list17;
				System.out.println("1.8/count="+list.size());
			}
			Iterator<World> i=list.keySet().iterator();
			while(i.hasNext()){
				World k=(World)i.next();
				if(list.get(k)>0)
					System.out.println(list.get(k)+"ref "+(anot.containsKey(k)?"comm ":"uniq ")+k.toString());
			}
		}
	}
}
