/*
 * $Id: PE.java,v 1.25.4.3 2000/06/18 22:42:42 yamauchi Exp $
 *     (M\pi)^2 : PE (Processing Element)
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source PE.h PE.cc
 *
 *	PE の実装
 * 1999年8月 4日(水) 
 */

package jp.ac.tohoku.is.archi.mpi2.common;

// import  java.util.*;

import  jp.ac.tohoku.is.archi.mpi2.dda.*;
import  jp.ac.tohoku.is.archi.mpi2.net.*;
import  jp.ac.tohoku.is.archi.mpi2.obj.*;
import  jp.ac.tohoku.is.archi.mpi2.radiosity.*;
import  jp.ac.tohoku.is.archi.mpi2.raytrace.*;
import  jp.ac.tohoku.is.archi.mpi2.sysparam.*;
import  jp.ac.tohoku.is.archi.mpi2.shading.*;
import  jp.ac.tohoku.is.archi.mpi2.util.*;

import  jp.ac.tohoku.is.archi.mpi2.jdk12src.Map;
import  jp.ac.tohoku.is.archi.mpi2.jdk12src.Iterator;
import  jp.ac.tohoku.is.archi.mpi2.jdk12src.HashMap;
import  jp.ac.tohoku.is.archi.mpi2.jdk12src.Vector;


/**
 * PE の実装
 */
public final class PE 
implements Mpi2Enum, Mpi2Constants, Config
{
    /**
     * singleton 
     */
    private static PE mPE = null;
    public static PE getInstance()
    {
	if(mPE == null)
	{
	    mPE = new PE();
	}
	return(mPE);
    }

    /**
     * system, hardware, rendering parameters
     */
    private IntVec3	mID;	
    private int		mMyRank;
    private SysInfo	mSysInfo; 
    private ViewData	mViewData;
    private SpaceParam	mSpcParam;

    /*------------------------------------------------------------
     * Local memory contents of this PE 
     *------------------------------------------------------------*/
    private HashMap	mSubSpaceMap; 
    
    /**
     * materials
     */ 
    private Vector      mMaterialVec; 
    private Material []	mMatArray;    

    /**
     * Ray の queue は rasiosity, raytracing で共に使用するのでここに
     * 置く
     */
    private Queue	mRayQueue; 		
    private Queue	mHostCommRecvQueue; 	

    /**
     * DDA 部分の分離 (FIXHERE)
     */
    private SearchNextSpcDDA	mDDAUnit;    
    private NetIntUnit		mNIUnit;

    /*------------------------------------------------------------
     * processor status 
     *-----------------------------------------------------------*/
    private boolean mIsIdle;		   
    private boolean mSendingRayStopComToHost;

    /*------------------------------------------------------------
     * Statistics : 統計情報
     *-----------------------------------------------------------*/
    private PEStatistics mStatistics;    
    public  PEStatistics peekStatistics()
    {
	return(this.mStatistics);
    }

    /**
     * 統計情報の出力
     *	@param stagename 	計算した stage 名
     */
    public void printStatInfo(String stagename)
    {
	String mes = Mpi2Option.toHead(NORMALHEAD, this.getMyRank());
	System.out.println(this.mStatistics.toStringWithHead(NORMALHEAD));
	this.mNIUnit.printStat(mes, stagename);
    }

    /*------------------------------------------------------------
     * initializers
     *-----------------------------------------------------------*/
    /**
     * initialize after receiving sysinfo, viewdata, spaceparam
     * PE と PE の各ユニットの初期化
     */
    private void initInternalUnits()
    {
	this.initRankNID();

	this.mNIUnit = new NetIntUnit();
	this.mNIUnit.initSpc2PE(this.mSysInfo.getAllocationScheme(),
				this.mSysInfo.peekPEDim(),
				this.mSpcParam.getSubDivNum(), 
				new IntVec3(this.peekID())); 

	/*
	 * init DDA Unit 
	 */
	Vector3 defmin = this.mSpcParam.peekDefSpaceMin();
	int [] idxMin   = new int[3];
	int [] idxMaxP1 = new int[3];
	for(int i = 0; i < 3; i++)
	{
	    idxMin[i]   = 0;
	    idxMaxP1[i] = (this.mSpcParam.peekMaxDefSpcIdxPlusOne()).get(i);
	}
	this.mDDAUnit = new SearchNextSpcDDA();
	this.mDDAUnit.
	    initWithoutObjInSpcMap(new Vector3(defmin), 
				   this.mSpcParam.getCellLen(),
				   idxMin, idxMaxP1,
				   this.mSysInfo.getRayPropSkipMethod(),
				   this.mSysInfo.getSystemNetworkType());
	
	this.mSubSpaceMap = new HashMap();

	Mpi2Option.getInstance().
	    verboseOut("NIN: PRCUnit : initInternalUnits : PE.java");

	Mpi2Option.getInstance().
	    verboseOut("NIN: frustum : initInternalUnits : PE.java");
    }

    /**
     * initialize Network Interface Unit
     */
    public void initNetIntRayBuff()
    {
	Assert.out(this.mRayQueue != null, 
		   "Internal Error: this.mRayQueue queue must be");
	Assert.out(this.mHostCommRecvQueue != null, 
		   "Internal Error: this.mHostCommRecvQueue queue must be");

	String s = "queue has some contents, cannot start next stage. " + 
	    "PE.initNetIntRayBuff";
	if(this.mRayQueue.length() != 0)
	{
	    throw new RuntimeException("Error!: mRayQueue " + s);
	}
	if(this.mHostCommRecvQueue.length() != 0)
	{
	    throw new RuntimeException("Error!: mHostCommRecvQueue " + s);
	}

	this.mNIUnit.initRayBuff(this.mSysInfo.getTotalPENum(),
				 this.mSysInfo.getProcRadioAtOnce(), 
				 this.mSysInfo.getSendInt2FBAtOnce(),
				 this.getMyRank(),
				 SysStat.getInstance().getHostRank(),
				 this.mRayQueue,
				 this.mHostCommRecvQueue);
    }

    /**
     * init object in space map of DDAUnit (SearchNextSpcDDA)
     */
    public void initDDASpcMap(Array3D objectInSpaceMap)
    {
	this.mDDAUnit.initObjInSpcMap(objectInSpaceMap);
    }
     
    
    /**
     * initRankNID 
     *	child of initInternalUnits()
     */
    private void initRankNID()
    {
	int myrank = SysStat.getInstance().getMyRank();
	IntVec3 pe = this.mSysInfo.peekPEDim();
	int PEx = pe.get(IntVec3.X);
	int PEy = pe.get(IntVec3.Y);
	// int PEz = pe.get(IntVec3.Z);
	
	int idx, idy, idz;
	idz = myrank / (PEx * PEy);
	idy = (myrank - (idz * (PEx * PEy))) / PEx;    
	idx = myrank - (idy * PEx) - (idz * (PEx * PEy));
	this.mID 	= new IntVec3(idx, idy, idz);
	this.mMyRank 	= myrank;	
    }
    
    /**
     * initialize after receiving object data
     */
    private void initObjectMaterialSet()
    {
	this.initMaterialSet();
	this.initObjectSet();
    }

    /**
     * initialize material array after material and object received
     */
    public void initMaterialSet()
    {
	int size = this.mMaterialVec.size();
	this.mMatArray = new Material[size];
	for(int i = 0; i < size; i++)
	{
	    this.mMatArray[i] = null;	    
	}
	for(int i = 0; i < size; i++)
	{
	    Material m = (Material)(this.mMaterialVec.elementAt(i));
	    if(this.mMatArray[m.getID()] != null)
	    {
		throw new RuntimeException("Error! Illegal material ID.");
	    }
	    this.mMatArray[m.getID()] = m;
	}
    }

    /**
     * initialize object set after material and object received
     */
    private void initObjectSet()
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN");
    }

    /**
     * output object distribution statistics information
     */
    private void outRecvObjectInfo()
    {
	final IntVec3 nDefspc = this.mSpcParam.peekMaxDefSpcIdxPlusOne();
	final int numberOfTotalSpaces = nDefspc.get(IntVec3.X) * 
	    nDefspc.get(IntVec3.Y) * nDefspc.get(IntVec3.Z);
	    
	int [] numObjInSpc = new int [numberOfTotalSpaces];
	for(int i = 0; i < numberOfTotalSpaces; i++)
	{
	    numObjInSpc[i] = 0;
	}

	int idx = 0;
	for(Iterator i = this.mSubSpaceMap.values().iterator(); i.hasNext(); ) 
	{
	    Vector oneSpc = (Vector)(i.next());
	    numObjInSpc[idx] = oneSpc.size();	    
	}

	double sum     = 0.0;
	double sqrdSum = 0.0;
	double spacenum = (double)numberOfTotalSpaces;
	for(int i = 0; i < numberOfTotalSpaces; i++)
	{
	    sum     += (double)(numObjInSpc[i]);
	    sqrdSum += (double)(numObjInSpc[i]) * (double)(numObjInSpc[i]);
	}
	
	final double average  = sum / spacenum;
	final double variance =
	    (sqrdSum / spacenum) - ((sum / spacenum) * (sum / spacenum));
	
        /*
	 * 副空間中に物体が存在する場合を選び、その物体数の平均・分散
	 * を求める  
	 */
	sum         = 0.0;
	sqrdSum     = 0.0;
	int spacenumWithObjs = 0;
	for(int i = 0; i < numberOfTotalSpaces; i++)
	{
	    if(numObjInSpc[i] != 0)
	    {
		sum     += (double)(numObjInSpc[i]);
		sqrdSum += (double)(numObjInSpc[i]) * (double)(numObjInSpc[i]);
		spacenumWithObjs++;
	    }
	}
	final int noObjectSpaces = numberOfTotalSpaces - spacenumWithObjs;
	double noObjSpaceAverage  = -1.0;
	double noObjSpaceVariance = -1.0; 

	if(spacenumWithObjs == 0)
	{
	    noObjSpaceAverage   = 0.0;
	    noObjSpaceVariance  = 0.0;
	}else
	{
	    noObjSpaceAverage   = sum / spacenumWithObjs;
	    noObjSpaceVariance  = 
		(sqrdSum / spacenumWithObjs) - 
                ((sum / spacenumWithObjs) * (sum / spacenumWithObjs));
	}
	
	String res =
	    NORMALHEAD + " PE : r " 	+ this.getMyRank() + 
	    " : object diststat : num " + this.mSubSpaceMap.size() +
	    " spc " 	+ numberOfTotalSpaces 	+ "\n" + 
	    " av " 	+ average + " V " 	+ variance  + "\n" + 
	    " noObj " 	+ noObjectSpaces	+ "\n" + 
	    " NObjav " 	+ noObjSpaceAverage 	+ " NV " + noObjSpaceVariance;
	System.out.println(res);
    }

    /**
     * exit 
     */
    private void exitprog()
    {
	System.out.println("PE " + this.mID + " : exit");
	Mpi2CommFactory.getInstance().exit(1);
    }

    /**
     * global load balancing 
     */
    public void globalLoadBalance()
    {
	if(this.mSendingRayStopComToHost == true)
	{
	    if(this.mRayQueue.length() < this.mSysInfo.getResumeRayNums())
	    {
		PEStat pestat = new PEStat();
		pestat.setReqResumeGenRay(this.getMyRank());
		this.mNIUnit.sendPEStatToHost(pestat);
		
		this.mSendingRayStopComToHost = false;
		Mpi2Option.getInstance().
		    verboseOut("request restart gen rays from " + 
			       this.getMyRank());
	    }
	}else
	{
	    if(this.mRayQueue.length() > this.mSysInfo.getSuspendRayNums())
	    {
		PEStat pestat = new PEStat();
		pestat.setReqStopGenRay(this.getMyRank());
		this.mNIUnit.sendPEStatToHost(pestat);

		this.mSendingRayStopComToHost = true;
		
		Mpi2Option.getInstance().
		    verboseOut("request resume gen rays from " + 
			       this.getMyRank());
	    }
	}
    }

    /*------------------------------------------------------------
     * set, peek 関数群
     *-----------------------------------------------------------*/
    /**
     * peek sysinfo
     */
    public SysInfo peekSysInfo()
    {
	return(this.mSysInfo);
    }

    /**
     * peek viewdata 
     */
    public ViewData peekViewData()
    {
	return(this.mViewData);
    }

    /**
     * peek spaceparam
     */
    public SpaceParam peekSpaceParam()
    {
	return(this.mSpcParam);
    }

    /**
     * peek Space Map
     */
    public HashMap peekSpaceMap()
    {
	return(this.mSubSpaceMap);
    }

    /**
     * peek Material Array
     */
    public Material [] peekMatArray()
    {
	return(this.mMatArray);
    }

    /**
     * peek DDA Unit
     */
    public SearchNextSpcDDA peekDDAUnit()
    {
	return(this.mDDAUnit);
    }

    /**
     * peek DDA Unit
     */
    public NetIntUnit peekNIUnit()
    {
	return(this.mNIUnit);
    }

    /**
     * peek RayQueue 
     */
    public Queue peekRayQueue()
    {
	return(this.mRayQueue);
    }

    /**
     * peek HostCommQueue 
     */
    public Queue peekHostCommRecvQueue()
    {
	return(this.mHostCommRecvQueue);
    }

    /*------------------------------------------------------------
     * PE Status : radiosity and raytracing common method
     *-----------------------------------------------------------*/
    /**
     * initialize PE Stat
     */
    public void initBusyIdleStat() 
    { 
	this.mIsIdle = true; 	// idle -> busy へ変化させる
	this.toBusy();		// 初期状態は働く状態 : Host への状態送信含む
    }
    /**
     * to Busy :  if idle then change to busy and send busy status to Host
     */
    public void toBusy()
    {
	if(this.mIsIdle == true)
	{
	    if(InDEBUG)
	    {
		if(Mpi2Option.getInstance().getOutPEmove())
		{
		    // to busy
		    System.out.println(NORMALHEAD + " ## PE [" +
				       this.getMyRank() + "]: B");
		}
	    }
	    this.mIsIdle = false;
	    // NIN pm_change_stat(PC_proc_ray_e);
	    this.mNIUnit.sendHostToThisPEIsBusy(true, this.getMyRank()); 
	}
    }
   
    /**
     * to Idle : if busy then change to idle and send idle status to Host
     */
    public void toIdle()
    {
	if(this.mIsIdle == false)
	{
	    if(InDEBUG)
	    {
		if(Mpi2Option.getInstance().getOutPEmove() == true)
		{			
		    // to idle
		    System.out.println(NORMALHEAD + " ## PE[" + 
				       this.getMyRank() + "]:I");
		}
	    }
	    this.mIsIdle = true; // 暇へと変化
	    // NIN pm_change_stat(PC_idle_e);
	    this.mNIUnit.sendHostToThisPEIsBusy(false, this.getMyRank());
	}
    }
    /**
     * is Idle?
     */
    public boolean isIdle() 	
    {
	return(this.mIsIdle);	
    }

    /**
     * 交差点が担当部分空間内部に存在するのか?
     *	複数の部分空間を占める球のようなプリミティブの場合には交差点が
     *	現在見ている部分空間の外に存在することがある．その場合には hit 
     *	していないことにしなくてはならないのでその判定用
     */
    public boolean checkHitPosIsInThisSpcPos(Vector3 hitpoint, 
					     IntVec3 spcidx)
    {
	Vector3 defmin  = new Vector3(this.mSpcParam.peekDefSpaceMin());
	double  celllen = this.mSpcParam.getCellLen();
	for(int i = 0; i < 3; i++)
	{
	    if(((int)((hitpoint.get(i) - defmin.get(i)) / celllen)) != 
	       spcidx.get(i))
	    {
		return(false);
	    }
	}
	return(true);
    }

    /**
     * host からの命令を受信してみる．あれば true．
     */
    public boolean probeHostCommand()
    {
	this.mNIUnit.recvHostComm();
	if(this.mHostCommRecvQueue.length() == 0)
	{
	    return(false);	// host からのコマンドは無かった
	}else
	{
	    return(true);
	}
    }

    /*
    //  2 次レイ作成
    boolean newSecondaryRadiosityRays(Ray *pRay, materialClass *pMat,
				   objBaseClass * pHitObj);
    boolean newSecondaryRayTracingRays(Ray *pRay, materialClass *pMat,
				    objBaseClass * pHitObj);
    */
    
    /**
     * 次空間探索とレイの移動 
     */
    public boolean moveRayToNextSubspace(Ray ray)
    {
	// 空間スキップ法によってレイの伝搬を変化させる
	switch(this.mSysInfo.getRayPropSkipMethod())
	{
	case NoSkip_rayprop:
	    return(this.mDDAUnit.moveRayToNextSubspaceNoSkip(ray));
	case SobokuSkip_rayprop:
	    return(this.mDDAUnit.moveRayWithSobokuSkip(ray));
	case ProximityClouds_rayprop:
	    throw new RuntimeException("Internal Error! : PRC method is NIN.");
	    //isSendRay = this.mPRCUnit.moveRayToNextCalcSubspaceWithSkip(ray);
	    //break;
	default:
	    throw new RuntimeException("Internal Error! : no such ray " + 
				       "propagation method.");
	}
    }
    
    //---------- network interface unit ----------    
    /**
     * 受信操作
     */
    public void recvRays()
    {
	if(this.mNIUnit.recvRays() == true)
	{
	    this.toBusy();
	}
    }

    /**
     * set a ray to the Network Interface Unit
     * 空間から送り先 PE を引き、その PE へと送信する
     * このバージョンは空間位置から送信する PE を指定しているので空間の 
     * skip を考慮している。
     */
    public void setRayToNI(Ray r)
    {
	Assert.out(r.isOK(), "Error!: something wring in a ray.");
	IntVec3 PEid = this.mNIUnit.getNewPEIDFromSpcPos(r.peekSpcPos());
	int toPERank = this.getRank(PEid);
	if(toPERank == this.mMyRank)
	{
	    this.setRayToThisPE(r); // 自分宛て
	}else
	{
	    Assert.out(r.peekSpcDDAInfo().isDDAinited() == true,
		       "Error ! : NON init dda is");
	    this.mNIUnit.sendRays(r, toPERank);
	}
    }

    /*------------------------------------------------------------
     * 輝度情報の送信
     *-----------------------------------------------------------*/
    public void insertIntensityIntoSendBuffer(Ray ray)
    {
	int scrX = ray.getScreenX();
	int scrY = ray.getScreenY();
	Assert.out(scrX >= 0, "Error!:illegal Xpos insertIntensityIntoBuffer");
	Assert.out(scrY >= 0, "Error!:illegal Ypos insertIntensityIntoBuffer");
	Vector3 color = ray.peekColor();

	if((color.get(Vector3.X) > 0.0) ||
	   (color.get(Vector3.Y) > 0.0) ||
	   (color.get(Vector3.Z) > 0.0))
	{
	    if(Mpi2Option.getInstance().getDebugMode())
	    {
		/*
		if(IsTraceRay)
		{
		    String s = Mpi2Option.toHead(DEBUGHEAD, this.getMyRank())
			+ "send FB ray " + ray.peekID();
		    System.out.println(s);
		}
		*/
	    }
	    Intensity intensity = new Intensity(scrX, scrY, color);
	    this.mNIUnit.sendIntensity(intensity);
	}else
	{
	    // DELETEME System.out.println("DEBUG : intensity is zero");
	}
    }

    /**
     * constructor
     */
    public PE()
    {
    	this.mID		= null;
	this.mMyRank		= -1;
	this.mSysInfo		= null;
	this.mViewData		= null;
	this.mSpcParam		= null;

	this.mSubSpaceMap 	= null;

	this.mMaterialVec  	= new Vector();
	this.mMatArray		= null;

	this.mRayQueue		= new RingBufferQueue(); // PROF
	this.mHostCommRecvQueue	= new RingBufferQueue(); // PROF

	this.mDDAUnit   	= null;    
	this.mNIUnit		= null;

	this.mIsIdle 		= false;
	this.mSendingRayStopComToHost	= false; // 停止していない
	this.mStatistics	= new PEStatistics();
    }

    public IntVec3 peekID()
    { 
	return(this.mID);	
    }

    public int getMyRank() 
    {
	return(this.mMyRank) ; 
    }
    
    /**
     * get Rank (hostComputerClass. にもある)
     */
    public int getRank(IntVec3 PEid) 
    {
	int rank = 
	    this.mSysInfo.peekPEDim().get(IntVec3.X) * 
	    (PEid.get(IntVec3.Z) * this.mSysInfo.peekPEDim().get(IntVec3.Y)
	     + PEid.get(IntVec3.Y)) + PEid.get(IntVec3.X);
	return(rank);
    }

    /*============================================================
     * communication
     *===========================================================*/
    /*------------------------------------------------------------
     * for stand alone
     *------------------------------------------------------------*/
    public void receiveSysinfoNinitSpaceInfoStdAlone(SysInfo    sys,
						     ViewData   vw,
						     SpaceParam sp)
    {
	Assert.out(Mpi2CommFactory.getInstance().getName().equals("OnePE"),
		   "Erorr! : this routine is only for One PE version.\n" +
		   "receiveSysinfoNinitSpaceInfoStdAlone");
	Mpi2Option.getInstance().
	    verboseOut("NIN: stage consistency check: " + 
		       "receiveSysinfoNinitSpaceInfoStdAlone");
	this.mSysInfo	= sys;
	this.mViewData	= vw;
	this.mSpcParam	= sp;

	this.initInternalUnits();
	
	Mpi2Option.getInstance().verboseOut(this.peekID() 
					    + " : PE : receive sysinfo data.");
    }

    /**
     * receive Material : stand alone mode
     */
    public void receiveMaterialStdAlone(Material aMat)
    {
	Assert.out(Mpi2CommFactory.getInstance().getName().equals("OnePE"),
		   "Erorr! : this routine is only for One PE version.\n" +
		   "receiveMaterialStdAlone");
	if(this.mMaterialVec == null)
	{
	    this.mMaterialVec = new Vector();
	}
	this.mMaterialVec.add(aMat);
    }

    /**
     * set object to this PE 
     */
    public void setObjectToThisPE(ObjectPrimitive obj)
    {
	if(this.mSubSpaceMap.containsKey(obj.peekSpcPos()))
	{
	    Vector oneSubspaceObjectList = 
		(Vector)(this.mSubSpaceMap.get(obj.peekSpcPos()));
	    oneSubspaceObjectList.add(obj);
	}
	else
	{
	    Vector oneSubspaceObjectList = new Vector();
	    oneSubspaceObjectList.add(obj);
	    this.mSubSpaceMap.put(obj.peekSpcPos(), oneSubspaceObjectList);
	}
    }

    /*------------------------------------------------------------
     * for parallel version
     *------------------------------------------------------------*/
    /**
     * get sysinfo and initialize ID and this Spc2PE
     */
    public void receiveSysInfoNinitSpaceInfo()
    {
	Assert.out(Mpi2CommFactory.getInstance().getName().equals("Parallel"),
		   "Erorr! : this routine is only for parallel version.\n" +
		   "receiveSysinfoNinitSpaceInfo");
	
	SysInfo    [] sys 	= new SysInfo[1];
	ViewData   [] view 	= new ViewData[1];
	SpaceParam [] spcparam 	= new SpaceParam[1];

	Mpi2CommFactory.getInstance().broadSysinfo(sys, view, spcparam);
	
	this.mSysInfo 	= sys[0];
	this.mViewData 	= view[0];
	this.mSpcParam	= spcparam[0];

	//System.out.println(this.mSysInfo.toStringWithHead("DEBUG:parallel"));
	//System.out.println(this.mViewData.toStringWithHead("DEBUG:parallel"));
	//System.out.println(this.mSpcParam.toStringWithHead("DEBUG:parallel"));
	
	// initialize after receiving sysinfo, viewdata, spaceparam
	this.initInternalUnits();

	Mpi2Option.getInstance().verboseOut(this.peekID() 
					    + " : PE : receive sysinfo data.");
    }

    /**
     * 材質の受信
     */
    public void receiveMaterials()
    {
	boolean isEnd = false;
	while(isEnd == false)
	{
	    Material [] mat = new Material[1];
	    Mpi2CommFactory.getInstance().broadMaterial(mat);

	    if(!(mat[0].peekName().equals(Material.NULLMatName)))
	    {
		// null material ではない
		Material newmat = new Material(mat[0]);
		// System.out.println("DEBUG : materialvec "
		// 		   + this.mMaterialVec);  DELETEME
		this.mMaterialVec.add(newmat); 
	    }else
	    {
		// null material で終了
		isEnd = true;
		
		Mpi2Option.getInstance().
		    verboseOut(" PE. r " + this.getMyRank() + 
			       " : get null material");
	    }
	}
	// this.outRecvMaterialInfo(); 
	// とりたてて有益な情報ではないので出力しない
	// System.out.println("DEBUG : here comes null material, over");
    }

    /**
     * 物体(sizeof = 一定)の受信
     */
    public void receiveObjects()
    {
	int [] srcrank = new int[1];
	ObjectPrimitive [] obj = new ObjectPrimitive[1];

	// 空間位置確認のための情報
	int   	spcDevNum  = this.mSpcParam.getSubDivNum();
	Vector3 spcPartMin = new Vector3(this.mSpcParam.peekPartVolumeMin());
	Vector3 spcPartMax = new Vector3(this.mSpcParam.peekPartVolumeMax());
	// subSpcSize = (spcPartMax - spcPartMin) / spcDevNum
	Vector3 subSpcSize = 
	    (spcPartMax.minusNew(spcPartMin)).divScalerNew(spcDevNum);

	boolean isEnd = false;
	// int recvobjnum = 0;
	while(isEnd == false)
	{
	    Mpi2CommFactory.getInstance().recvObject(srcrank, obj);
	    if(obj[0].who() == ObjectPrimitive.OBJ_NULL)
	    {
		System.out.println(NORMALHEAD + " PE : " + this.peekID() + 
				   " : r " + this.getMyRank() + 
				   " get null object");
		isEnd = true;
	    }else
	    {
		// 空間中の位置確認
		if(!(obj[0].isAtValidSpcPos(spcPartMin,subSpcSize, spcDevNum)))
		{
		    String s = "Error! : inconsistency occur between spcpos "+
			"and object : PE rank " + this.getMyRank();
		    throw new RuntimeException(s);
		}
		//System.out.println("DEBUG:recv obj PE " + this.getMyRank());
		
		this.setObjectToThisPE(obj[0]);
		// recvobjnum++;
	    }
	    /*
	      if(recvobjnum % 2000 == 0)
	      {
	      // Why Java version consume so much memory? 
	      // This code is not work. 
	      System.gc();
	      //Mpi2Option.getInstance().verboseOut("gc rank " + 
	      //this.getMyRank());
	      }
	      */
	}
	this.initObjectMaterialSet();
	this.outRecvObjectInfo();
    }

    /**
     * volume の受信
     */
    public void receiveVolume()
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN PE receiveVolume");	
	/*
	  VolumeClass *pVolume = null;
	  boolean isEnd = NO;
	  while(isEnd == NO)
	  {
	  commMpi2_recvVolume(SysStat.get().getHostRank(), 
	  this.getMyRank(), pVolume);
	  if(pVolume.isNullVolume() == true)
	  {
	  System.out.println("// PE : " + this.mID + " : r " + 
	  this.getMyRank()
	  + " get null volume" + endl;
	  isEnd = true;
	  }else
	  {
	  this.mVolList.append(pVolume);
	  }
	  }
	  // already got null volume, initialize now
	  this.initRecvVolumeSet();
	  */
    }

    /**
     * 空間内物体存在情報を受信する。クラスの実装と独立させるために情
     * 報は一次元の配列になって送信されてくるのでそれを objInSpcClass 
     * に格納する.
     * (ref. host.cc : hostComputerClass.readAndDeliverGeoData())  
     */
    public void receiveObjInSpcMap()
    {
	switch(this.mSysInfo.getRayPropSkipMethod())
	{
	case NoSkip_rayprop: // skip なしでも物体情報の統計のためにdeliverする
	case SobokuSkip_rayprop:
	    this.receiveObjInSpaceInfoSoboku();
	    break;
	case ProximityClouds_rayprop:
	    Mpi2Option.getInstance().
		verboseOut("NIN : receiveObjInSubSpaceInfo");	    
	    Mpi2CommFactory.getInstance().exit(1);
	    // NIN  this.PRC_deliverSkipInfoMap(FileInfo::get(). 
	    // 		 getDistanceDataFileName());
	    break;
	default:
	    System.out.println("Internal Error!: Host.deliverObjInSpaceInfo");
	    Mpi2CommFactory.getInstance().exit(1);
	    break;
	}
	// 表の内容を出力する
 	if(Mpi2Option.getInstance().getShowNumobjInSpace()) 
 	{
	    SearchNextSpcDDA.
		printObjInSpcMapCommon(NORMALHEAD + " PE[" + this.getMyRank() +
				       "]:: ",
				       this.mDDAUnit.peekObjInSpcMap());
 	}
    }

    /**
     * 素朴な skip 用の物体存在情報マップを受信する
     */
    private void receiveObjInSpaceInfoSoboku()
    {
	// 存在情報を送信
	Array3D [] a3d = new Array3D[1];
	Mpi2CommFactory.getInstance().broadObjectInSpaceMap(a3d);

	// Map 領域作成と初期化
	this.mDDAUnit.initObjInSpcMap(a3d[0]); 
    }

    /**
     * host に radiosity の結果を送信しなくてはいけないことがわかっている
     * 時にこの関数で待つ．host から命令が来たら送信する．
     * radiosity の計算結果を host に送信する
     */
    public void sendOutRadioResultToHostAccordHostComm()
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN sendOutRadioResultToHostAccordHostComm");
	/*
    while(1)
    {
	this.mNIUnit.mRecvHostCommBuf.recv(); // 受信
	if(this.mHostCommRecvQueue.length() == 0)
	{
	    retrySleep();	// ホストからのコマンドはない
	}
	else
	{
	    // Host のコマンドを受けた
	    Assert.out(this.mHostCommRecvQueue.length() > 0, "Error!:");
	    HostCommClass *pRecvHostCom = this.mHostCommRecvQueue.deq();
	    HostCommClass recvHCom = (*pRecvHostCom);
	    delete pRecvHostCom; // 受信したものは new されている
	    
	    if(recvHCom.getCommand() == HOSTCOM_Send_Patch_Descriptions_e)
	    {
		PEoutDebugMes(this.getMyRank(), recvHCom.toString());
		this.sendOutRadioResultToHost();
		return;		// 送信終了したのでこのコマンドから戻る
	    }
	    else
	    {
		System.out.println( + "Error! : received illegal host command. " +
	    	"PEClass.sendOutRadioResultToHostAccordHostComm"  + endl;
		System.out.println( + "this rank = " + this.getMyRank() + endl;
	    }
	}
    }
    */
    }

    /**
     * radiosity の計算結果を host に送信する
     * 	この関数が呼ばれればそのまま送信
     */
    public void sendOutRadioResultToHost()
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN sendOutRadioResultToHost");
	/*
    int totalnum = 0;
    Pix pidx;
    for(pidx = this.mObjList.first(); pidx != null; this.mObjList.next(pidx))
    {
	commMpi2_sendObject(SysStat.get().getHostRank(), 
			    this.mObjList(pidx));
	totalnum++;
    }
    ObjectPrimitive *pNullObject = new nullObjectClass;
    commMpi2_sendObject(SysStat.get().getHostRank(), pNullObject);
    delete pNullObject;

    // output debug message
    char buf[256];
    sprintf(buf, "send objects to host, total objes %d", totalnum);
    PEoutVerboseMes(this.getMyRank(), buf);
    */
    }

    // output intermediate result

    /**
     * Radiosity の中間結果を出力する
     */
    public void outRadiosityMpi2Data(String outBaseFname, boolean isShotRad)
    {
	Mpi2Option.getInstance().
	    verboseOut("NIN: 並列版の場合には同期してある地点の結果を" + 
		       "出力する必要があるが，それを実装していない．");
	// ここで同期して上記の sendOutRadioResultToHost を呼ぶ計画
    }

    /*
    void writeGeometryFile(FILE *pFp, boolean isShotRad);
    void outRadiosityIntermediateResult(); // 中間結果の出力用
    // for PRC method
    void receivePRCSkipMap(); 
    #endif
    */

    /**
     * Radiosity Stage の計算
     */
    public void doRadiosity()
    {
	RadioRenderer radiorender = new RadioRenderer(this);
	radiorender.doRadiosity();
    }

    /**
     * Shading Stage の計算
     */
    public void doPrepareShading()
    {
	ProfileMon.getInstance().startStage(ProfileMon.ProfSt_prepareShading);
	
	ShadeCalculator shadecalculator = new ShadeCalculator(this);
	shadecalculator.doPrepareShading();	
	
	ProfileMon.getInstance().endStage(ProfileMon.ProfSt_prepareShading);
    }

    /**
     * Ray-Tracing Stage の計算
     */
    public void doRayTracing()
    {
	RayTracer raytracer = new RayTracer(this);
	raytracer.doRayTracing();
    }


    /**
     * 全体のバリア同期 : host は time out あり失敗すると exit
     */
    private int syncID = 0;	// 何回目の barrierSync かを示す
    public void barrierSync()
    {
	System.out.println(NORMALHEAD + " PE : r " + this.getMyRank() + 
			   " : waiting other outputs, syncID = " + syncID);
	// pm_change_area(PA_sync_wait_e);

	// 同期命令発行
	Mpi2CommFactory.getInstance().barrier();
	// pm_change_area(PA_sync_done_e);    
	System.out.println(NORMALHEAD + " PE : r " + this.getMyRank() +
			   " : done barrier sync." + syncID);
	syncID++;
    }    

    /**
     * set ray to this PE : この PE に ray をセットする．
     */
    public void setRayToThisPE(Ray r)
    {				
	Assert.out(r.isOK(), "Error!: this ray has something wrong.");
	this.mRayQueue.enq(r);	
    }

    /*
    // void setRayPtrToOtherPE(int recvRank, Ray* r);
    IntVec3 getLocalIdxFromSpcPos(IntVec3 spcpos)
    {
	return(this.mNIUnit.mpSpc2PE.getLocalIdxFromSpcPos(spcpos));
    }
    */
    
    /*------------------------------------------------------------
     * print out routine
     *------------------------------------------------------------*/
    /**
     * print out sysinfo
     */
    public void printSysInfo(final String head)
    {
	String s = head + "----- sysinfo ----- PE ID = " + this.mID + "\n";
	s += this.mSysInfo.toStringWithHead(head) + "\n";
	s += head + "----- view data\n"; 
	s += this.mViewData.toStringWithHead(head) + "\n";
	s += head + "----- space paramater\n";   
	s += this.mSpcParam.toStringWithHead(head);
	System.out.println(s);
    }
    /**
     * print out objects
     */
    public void printObjects(final String head)
    {
	System.out.println(head + "----- object ----- PE ID = " + this.mID);
	for(Iterator i = this.mSubSpaceMap.values().iterator(); i.hasNext(); ) 
	{
	    Vector objlist = (Vector)(i.next());
	    for(int j = 0; j < objlist.size(); j++)
	    {
		System.out.println(((ObjectPrimitive)
				    (objlist.elementAt(j))).
				   toStringWithHead(head));
	    }
	}
    }
    /**
     * print out materials
     */
    public void printMaterials(final String head)
    {
	System.out.println(head + "----- material ----- PE ID = " + this.mID);
	for(int i = 0; i < this.mMatArray.length; i++) 
	{
	    System.out.println(this.mMatArray[i].toStringWithHead(head));
	}
    }

    /**
     * 空間情報を出力(各 PE は skip のために全空間分持っている)
     */
    public void printObjInSpcMap(final String head)
    {
	String s = head + "----- obj in subspace map ----- PE ID = " 
	    + this.mID;
	System.out.println(s);

	s = head + " PE:: " + mMyRank;
	switch(this.mSysInfo.getRayPropSkipMethod())
	{
	case NoSkip_rayprop:
	case SobokuSkip_rayprop:
	    SearchNextSpcDDA.
		printObjInSpcMapCommon(s, this.mDDAUnit.peekObjInSpcMap());
	    break;
	case ProximityClouds_rayprop:
	    // NIN printObjInSpcMapCommon(s, this.mPRCUnit.peekObjInSpcMap());
	    break;
	default:
	    System.out.println("Error! : can't print objects in " + 
			       "subspace map.");
	    this.exitprog();
	    break;
	}
    }

    // network interface unit related 
    /**
     * この PE の担当空間か
     *	Does this PE deal with indicated spcpos?
     */
    public boolean isDealSpace(IntVec3 spcpos)
    {
	return(this.mID.equals(this.mNIUnit.getNewPEIDFromSpcPos(spcpos)));
    }
    
    
    /*
      protected:
      //tokunaga distribute Rays
    frustumSRGClass mFrustum;	
    boolean secondRayGenWithDistrbute(Ray *r, materialClass *pMat,
				   objBaseClass * pHitObj);
    boolean secondRayGenWithoutDistribute(Ray *r, materialClass *pMat,
				       objBaseClass * pHitObj);
public:    
void makeDDMap();
}

*/

}
