//------------------------------------------------------------
// $Id: objectSet.cc,v 1.1.1.1 1999/11/25 06:26:09 yamauchi Exp $
//------------------------------------------------------------
// 	object set implementation
//	(M\pi)^2 
//
//	object を PE で保持する際に利用するクラス。
//	引数に sub 空間の位置を与えるとその空間内の object の数と 
//	object が取り出せる。
// 	実装は空間と速度のトレードオフで変化する予定
//	1996/7/16 
//		まずは3次元可変長配列で挑戦
//		オブジェクト空間分割型並列計算に基づき担当空間分のみの
//		空間情報を確保  
//	Copyright (C) 1996--1998 YAMAUCHI Hitoshi 山内 斉
//------------------------------------------------------------
#include <assert.h>

// #include <stdlib.h>
// #include "constants.h"

#include "PE.h"
#include "ProcObj.h"
#include "mpi2exit.h"
#include "objectSet.h"
#include "Option.h"

//------------------------------------------------------------
// protected functions
//------------------------------------------------------------
void objectSetClass::
delElems(void)
{
    if(this->mpppObjArray != NULL)
    {
	const int SpcNumX = this->mTotalSpcNum.get(IntVec3::X);
	const int SpcNumY = this->mTotalSpcNum.get(IntVec3::Y);
	const int SpcNumZ = this->mTotalSpcNum.get(IntVec3::Z);
	if((SpcNumX <= 0) || (SpcNumY <= 0) || (SpcNumZ <= 0))
	{
	    cerr << "Error! : illegal number of spaces at delElems." << 
		this->mTotalSpcNum << endl;
	    commMpi2_exit(1);
	}
	int x, y;
	for(x = 0; x < SpcNumX; x++)
	{
	    for(y = 0; y < SpcNumY; y++)	    
	    {
		delete [] this->mpppObjArray[x][y];
	    }
	    delete [] this->mpppObjArray[x];	    
	}
	this->mpppObjArray = NULL;	    	
	this->mTotalSpcNum = IllegalPEID;	// set(-2, -2, -2);	
    }
}

//------------------------------
// index の範囲が正しいかチェックする
//------------------------------
YorN objectSetClass::
idxOK(const IntVec3& a3dIdx) const
{
    if((0 <= a3dIdx.get(IntVec3::X)) &&
       (0 <= a3dIdx.get(IntVec3::Y)) &&
       (0 <= a3dIdx.get(IntVec3::Z)) &&
       (a3dIdx.get(IntVec3::X) < this->mTotalSpcNum.get(IntVec3::X)) &&
       (a3dIdx.get(IntVec3::Y) < this->mTotalSpcNum.get(IntVec3::Y)) &&
       (a3dIdx.get(IntVec3::Z) < this->mTotalSpcNum.get(IntVec3::Z)))
    {
	return(YES);
    }else
    {
	if(Option::getInstance()->getDebugMode() == YES)
	{
	    cout << DEBUGHEAD << "debug :: objectSetClass::idxOK idx = "
	    << a3dIdx << ", total space num = " << this->mTotalSpcNum << endl;
	}
	return(NO);
    }
}

//------------------------------------------------------------
// public functions
//------------------------------------------------------------
objectSetClass::
~objectSetClass(void)
{
    this->delElems();
}

//------------------------------
// 扱う副空間数を受けて初期化する
//------------------------------
void objectSetClass ::
init(const IntVec3& aTotalSpcNum)
{
    const int localSpcNumX = aTotalSpcNum.get(IntVec3::X);
    const int localSpcNumY = aTotalSpcNum.get(IntVec3::Y);
    const int localSpcNumZ = aTotalSpcNum.get(IntVec3::Z);
    if((localSpcNumX < 0) || (localSpcNumY < 0) || (localSpcNumZ < 0))
    {
	cerr << "Error! : illegal number of spaces objectSetClass::init"
	    << aTotalSpcNum << endl;
	commMpi2_exit(1);
    }
    this->delElems();

    int x, y;
    this->mpppObjArray = new objectsArrayClass** [localSpcNumX];	    
    for(x = 0; x < localSpcNumX; x++)
    {
	this->mpppObjArray[x] = new objectsArrayClass* [localSpcNumY];	    
	for(y = 0; y < localSpcNumY; y++)	    
	{
	    this->mpppObjArray[x][y] = new objectsArrayClass [localSpcNumZ];
	}
    }
    this->mTotalSpcNum = aTotalSpcNum;
}

//------------------------------
// 配列要素の長さを初期化する 空間の番号をそのまま利用できる
//------------------------------
void objectSetClass::
setLength(const IntVec3& a3dIdx, int aLen)
{
    IntVec3 innerIdx = GpPE->getLocalIdxFromSpcPos(a3dIdx);
    assert(this->idxOK(innerIdx) == YES);    
    this->mpppObjArray[innerIdx.get(IntVec3::X)]
	[innerIdx.get(IntVec3::Y)][innerIdx.get(IntVec3::Z)].setLength(aLen);
}

//------------------------------
// 配列要素の長さを初期化する 内部の配列番号を利用する 危険であるが、
// メモリ量を減らせる 
//------------------------------
void objectSetClass::
setLengthWithLocalIdx(const IntVec3& innerIdx, int aLen)
{
    assert(this->idxOK(innerIdx) == YES);    
    this->mpppObjArray[innerIdx.get(IntVec3::X)]
	[innerIdx.get(IntVec3::Y)][innerIdx.get(IntVec3::Z)].setLength(aLen);
}

//------------------------------
// 指定された index の配列の長さを返す
//------------------------------
int objectSetClass::
getLength(const IntVec3& a3dIdx) const
{
    IntVec3 innerIdx = GpPE->getLocalIdxFromSpcPos(a3dIdx);
    assert(this->idxOK(innerIdx) == YES);
    return(this->mpppObjArray[innerIdx.get(IntVec3::X)]
	   [innerIdx.get(IntVec3::Y)][innerIdx.get(IntVec3::Z)].getLength());
}

//------------------------------
// object を指定された配列の最後に付加する
//------------------------------
void objectSetClass::
add(const IntVec3& a3dIdx, objBaseClass* pObj)
{
    IntVec3 innerIdx = GpPE->getLocalIdxFromSpcPos(a3dIdx);
    assert(this->idxOK(innerIdx) == YES);
    int x = innerIdx.get(IntVec3::X);
    int y = innerIdx.get(IntVec3::Y);
    int z = innerIdx.get(IntVec3::Z);
    this->mpppObjArray[x][y][z].add(pObj);
}

//------------------------------
// 指定された object 配列への参照を取り出す
//------------------------------
objectsArrayClass *objectSetClass::
get(const IntVec3& a3dIdx)
{
    IntVec3 innerIdx = GpPE->getLocalIdxFromSpcPos(a3dIdx);
    assert(this->idxOK(innerIdx) == YES);
    return(&(this->mpppObjArray[innerIdx.get(IntVec3::X)]
	     [innerIdx.get(IntVec3::Y)][innerIdx.get(IntVec3::Z)]));
}

//------------------------------------------------------------
// この object set の統計情報
//------------------------------------------------------------
// 総担当空間数
//------------------------------
void objectSetClass::
getStat(int    *pNumberOfTotalSpace, // 総担当空間数
	double *pAverage,	     // 物体数の平均
	double *pVariance,	     // 物体数の分散
	int    *pNoObjectSpaces,     // 物体の存在しない空間数
	double *pNoObjSpaceAverage, // 物体の存在しない空間を除いた物体数の平均
	double *pNoObjSpaceVariance // 物体の存在しない空間を除いた物体数の分散
	)
{
    // 総空間数
    const int numOfTotalSpc = 
	this->mTotalSpcNum.get(IntVec3::X) *
	    this->mTotalSpcNum.get(IntVec3::Y) * 
		this->mTotalSpcNum.get(IntVec3::Z);
    (*pNumberOfTotalSpace) = numOfTotalSpc;
    
    int *pNumObjInSpc = new int [numOfTotalSpc];
    int i, x, y, z;
    for(i = 0; i < numOfTotalSpc; i++)
    {
	pNumObjInSpc[i] = 0;
    }
    
    // 3 次元的に持っている物体数の情報を一次元に変換して計算を容易にする
    i = 0;
    for(z = 0; z < this->mTotalSpcNum.get(IntVec3::Z); z++)
    {
	for(y = 0; y < this->mTotalSpcNum.get(IntVec3::Y); y++)
	{
	    for(x = 0; x < this->mTotalSpcNum.get(IntVec3::X); x++)
	    {
		pNumObjInSpc[i] = this->mpppObjArray[x][y][z].getLength();
		i++;
	    }
	}
    }

    // 物体数の平均・分散を求める
    double sum 	   = 0.0;
    double sqrdSum = 0.0;
    const double spacenum = double(numOfTotalSpc);
    for(i = 0; i < numOfTotalSpc; i++)
    {
	sum 	+= double(pNumObjInSpc[i]);
	// double に変換してから乗ずるのは overflow をできるだけ避けるため
	sqrdSum	+= double(pNumObjInSpc[i]) * double(pNumObjInSpc[i]);
    }

    (*pAverage)	= sum / spacenum;
    (*pVariance)= (sqrdSum / spacenum) - ((sum / spacenum) * (sum / spacenum));
    
    // 副空間中に物体が存在する場合を選び、その物体数の平均・分散を求める
    sum		= 0.0;
    sqrdSum 	= 0.0;
    int spacenumWithObjs = 0;
    for(i = 0; i < numOfTotalSpc; i++)
    {
	if((pNumObjInSpc[i]) != 0)
	{
	    sum 	+= double(pNumObjInSpc[i]);
	    sqrdSum	+= double(pNumObjInSpc[i]) * double(pNumObjInSpc[i]);
	    spacenumWithObjs++;
	}
    }
    (*pNoObjectSpaces) = numOfTotalSpc - spacenumWithObjs;

    if(spacenumWithObjs == 0)
    {
	// この PE は物体を保持していない。
	(*pNoObjSpaceAverage)	= 0.0;
	(*pNoObjSpaceVariance) 	= 0.0;
    }else
    {
	(*pNoObjSpaceAverage)	= sum / spacenumWithObjs;
	(*pNoObjSpaceVariance)	= 
	    (sqrdSum / spacenumWithObjs) - 
		((sum / spacenumWithObjs) * (sum / spacenumWithObjs));
    }
    delete [] pNumObjInSpc;
}

