/*
 * $Id: Array3D.java,v 1.4 1999/11/25 04:09:46 maeda Exp $
 *     (M\pi)^2 : Array3D
 *	Copyright 1999 By YAMAUCHI Hitoshi 山内 斉 
 *	@author YAMAUCHI Hitoshi 山内 斉
 *	
 * original source array3d.h (C++ template class)
 *
 *	範囲をセットできる3次元配列をエミュレート
 *	範囲は   [mMinIdx, mMaxIdx] である．
 *	size は  mMaxIdx - mMinIdx + 1 (つまり実際に保持している数)
 *
 * 1999年8月 8日(日)
 */

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

import java.io.Serializable;
import jp.ac.tohoku.is.archi.mpi2.util.Assert;
import jp.ac.tohoku.is.archi.mpi2.util.IntVec3;
import jp.ac.tohoku.is.archi.mpi2.sysparam.Config;

public final class Array3D 
implements Serializable
{
    /** 
     * このクラスは ARRAY_3D_DIM 次元の配列を保持するように見える
     * (とはいえ，3 次元のものとして作っている．)
     * 3次元の index でアクセスするが、実装は 1 次元
     */
    public final static int ARRAY_3D_DIM = 3;

    private int [] 	mMinIdx;
    private int []	mMaxIdx;    
    private int []	mSize;    
    private Object [] 	mVal;

//     private copy(Array3D rhs)
//     {
//  	for(int i = 0; i < ARRAY_3D_DIM; i++)
//  	{
//  	    this.mMinIdx[i] = rhs.mMinIdx[i];
//  	    this.mMaxIdx[i] = rhs.mMaxIdx[i];    
//  	    this.mSize[i]   = rhs.mSize[i];    
//  	}
//  	int [] idx = new int[ARRAY_3D_DIM];
//  	Assert.out(ARRAY_3D_DIM == 3, "Error!: now implemented dim == 3 only");
//  	for(idx[2] = rhs.mMinIdx[2]; idx[2] <= rhs.mMaxIdx[2]; idx[2]++)
//  	{
//  	    for(idx[1] = rhs.mMinIdx[1]; idx[1] <= rhs.mMaxIdx[1]; idx[1]++)
//  	    {
//  		for(idx[0] = rhs.mMinIdx[0]; idx[0] <= rhs.mMaxIdx[0];
//  		    idx[0]++) 
//  		{
//  		    this.mpVal[this.getInternalIdx(idx)]
// 			new ???
//  			= rhs.mpVal[rhs.getInternalIdx(idx)];
//  		}
//  	    }
//  	}
//     }


    // 次元のテスト
    private boolean checkDim(int dim) 
    {
	if((dim < 0) || (ARRAY_3D_DIM <= dim))
	{
	    throw new RuntimeException("Error! : illegal dimension " + 
				       "Array3D.checkDim");
	}
	return(true);
    }
    
    /**
     * 内部の実体配列へのアクセス
     */
    private int getInternalIdx(int [] idx) 
    {
	int [] internalIdx = new int[ARRAY_3D_DIM];
	for(int i = 0; i < ARRAY_3D_DIM; i++)
	{
	    if (Config.InDEBUG) {
	      Assert.out(idx[i] >= 0, "Error!: index must be > 0, [" + i + 
		         "] = " + idx[i]);
	    }
	    if(!((this.mMinIdx[i] <= idx[i]) && (idx[i] <= this.mMaxIdx[i])))
	    {
		String s = "Error! : Array3D.set index is out of range." +
		    "dim=" + i + ", range[" + this.mMinIdx[i] + ", "	 +
		    this.mMaxIdx[i] + "], idx=" + idx[i];
		throw new RuntimeException(s);
	    }
	    internalIdx[i] = idx[i] - this.mMinIdx[i];
	    if (Config.InDEBUG) {
	      Assert.out(internalIdx[i] >= 0, "Error!: illegal internalidx");
	    }
	}
	// ここは ARRAY_3D_DIM = 3 を仮定して書いている
	if (Config.InDEBUG) {
	  Assert.out(ARRAY_3D_DIM == 3, "Error!: Now I assume DIM == 3");
	}
	int retIdx = 
	    internalIdx[0] + 
	    internalIdx[1] * this.mSize[0] + 
	    internalIdx[2] * this.mSize[0] * this.mSize[1];
	if (Config.InDEBUG) {
	  Assert.out(retIdx >= 0, "Error!: index must be > 0");
	}
	return(retIdx);
    }

    /**
     * constructor 
     */
    public Array3D()
    {
	this.mMinIdx	= new int[ARRAY_3D_DIM];
	this.mMaxIdx	= new int[ARRAY_3D_DIM];
	this.mSize   	= new int[ARRAY_3D_DIM];
	this.mVal	= null;
    }
//  public Array3D(Array3D rhs)
//     {
// 	this.init(rhs.mMinIdx, rhs.mMaxIdx);
// 	this.copy(rhs);
//     }

    /**
     * initialize
     * 以下は C++ の時のコメント．このようなことは Java ではない．
     * (ここは T が int, double などの基本型の場合には mpVal[] は初期化
     *  されないことに注意．)
     */
    public void init(int [] minIdx, int [] maxIdx)
    {
	for(int i = 0; i < ARRAY_3D_DIM; i++)
	{
	    Assert.out(maxIdx[i] - minIdx[i] >= 0, "Error!: illegal minIdx, "+
		       "maxIdx, [" + i + "] min = " + minIdx[i] + ", max = " +
		       maxIdx[i]);
	    this.mMinIdx[i] = minIdx[i];
	    this.mMaxIdx[i] = maxIdx[i];
	    this.mSize[i]   = maxIdx[i] - minIdx[i] + 1;
	}
	
	this.mVal = null;
	this.mVal = new Object[this.getTotalSize()];
    }

    /**
     * index が 0 からはじまる size の 3D 配列を作成する
     */
    public void initBySizeWithBaseZero(int [] size)
    {
	int [] minidx = new int[ARRAY_3D_DIM];
	int [] maxidx = new int[ARRAY_3D_DIM];
	for(int i = 0; i < ARRAY_3D_DIM; i++)
	{
	    minidx[i] = 0;
	    maxidx[i] = size[i] - 1;
	    Assert.out(maxidx[i] >= 0, "Error!: illegal size " + 
		       "initBySizeWithBaseZero");
	}
	this.init(minidx, maxidx);
    }

    /**
     * get and set functions (member)
     *     注意: これを使って for をまわす場合には
     *		 for(i = getMinIdx(0); i <= getMaxIdx(0); i++)
     *	   のように getMaxIdx に等号が必要になる．大きさでなく，有効な
     *     値が入っている．
     */
    public int getMinIdx(int dim) 	
    {
	Assert.out(checkDim(dim), "Error!: getMinIdx");
	return(this.mMinIdx[dim]);	
    }
    public int getMaxIdx(int dim) 	
    {
	Assert.out(checkDim(dim), "Error!: getMaxIdx");
	return(this.mMaxIdx[dim]);	
    }
    public int getSize(int dim) 
    {
	Assert.out(checkDim(dim), "Error!: getSize");
	return(this.mSize[dim]);	
    }
    
    /**
     * get and set functions (derived)
     */
    public boolean isInited() 
    {
	if(this.mVal == null)
	{
	    return(false);
	}else
	{
	    return(true);	    
	}
    }

    /**
     * これはキャッシュ可能 (IMPROVE)
     */
    public int getTotalSize() 
    {
	int i;
	int size = 1;
	for(i = 0; i < ARRAY_3D_DIM; i++)
	{
	    size *= this.mSize[i];
	}
	Assert.out(size >= 0, "Error!: getTotalSize");
	return(size);
    }

    public void set(int [] idx, Object val)
    {
	this.mVal[this.getInternalIdx(idx)] = val;
    }
    
    public Object get(int idx[]) 
    {
	return(this.mVal[this.getInternalIdx(idx)]);
    }

    public void setAsOneDimArray(int idx, Object val) 
    {
	Assert.out((0 <= idx) && (idx < this.getTotalSize()), 
		   "Error!: setAsOneDimArray");
	this.mVal[idx] = val;
    }

    /**
     * get one object with accessing one dimension array
     * 	1 次元の配列のようにアクセスする．空間の位置などと関係なく，全
     *  体をなめるアクセスの場合に使用する．
     */
    public Object peekAsOneDimArray(int idx) 
    {
	return(this.mVal[idx]);
    }

    /**
     * to String
     */
    public String toString() 
    {
	String s = "";
	for(int i = 0; i < 3; i++)
	{
	    s = "(" + this.mSize[i] + ")[" + this.mMinIdx[i] + "," 
		+ this.mMaxIdx[i] + "]";
	}
	s += "\n";	
	if(this.mVal == null)
	{
	    s += "Array3D contains no data.";
	}else 
	{
	    s += "Array3D has data.";
	}
	return(s);
    }

    /**
     * 全ての内容の比較
     */
    public boolean equals(Array3D a, Array3D b)
    {
	for(int i = 0; i < ARRAY_3D_DIM; i++)
	{
	    if((a.mMinIdx[i] 	!= b.mMinIdx[i]) ||
	       (a.mMaxIdx[i] 	!= b.mMaxIdx[i]) ||
	       (a.mSize[i] 	!= b.mSize[i]))
	    {
		return(false);
	    }
	}

	if((a.mVal == null) && (b.mVal == null))
	{
	    return(true);		// 両方 null
	}
	else if((a.mVal == null) || (b.mVal == null))
	{
	    return(false);		// 片方 null
	}

	int size = a.getTotalSize();
	for(int j = 0; j < size; j++)
	{
	    if(a.mVal[j].equals(b.mVal[j]) == false)
	    {
		return(false);	// fail
	    }
	}
	return(true);
    }

    /**
     * IntVec3 によるアクセス
     * 	set : convenient set 関数
     */
    public void set(IntVec3 anIdxVec, Object val)
    {
	int [] idx = anIdxVec.getNewAsArray();
	
	this.mVal[this.getInternalIdx(idx)] = val;
    }

    /**
     * IntVec3 によるアクセス
     * 	get : convenient get 関数
     */
    public Object get(IntVec3 anIdxVec) 
    {
	int [] idx = anIdxVec.getNewAsArray();

	return(this.mVal[this.getInternalIdx(idx)]);
    }
//      *----------------------------------------
//      * SerializableIF : double の表現が同じ計算機でないと上手くいかない    
//      *----------------------------------------
//     int getSerializedSize() 
//     {
// 	// mMinIdx[ARRAY_3D_DIM], mMaxIdx[ARRAY_3D_DIM], mSize[ARRAY_3D_DIM];
// 	// *mVal;
// 	Assert.out(this.mVal != null, "Error!:");
// 	int size = ARRAY_3D_DIM + ARRAY_3D_DIM + ARRAY_3D_DIM + 
// 	    this.getTotalSize() * this.mVal[0].getSerializedSize();
// 	return(size);
//     }
//     double *newSerializedArray()
//     {
// 	double *r = new double[this.getSerializedSize()];
// 	int idx = 0;
// 	int i;
// 	for(i = 0; i < 3; i++)
// 	{
// 	    r[idx] = (double)(this.mMinIdx[i]);
// 	    idx++;
// 	}
// 	for(i = 0; i < 3; i++)
// 	{
// 	    r[idx] = (double)(this.mMaxIdx[i]);
// 	    idx++;
// 	}
// 	for(i = 0; i < 3; i++)
// 	{
// 	    r[idx] = (double)(this.mSize[i]);
// 	    idx++;
// 	}

// 	for(i = 0; i < this.getTotalSize(); i++)
// 	{
// 	    double *serializedT = this.mVal[i].newSerializedArray();
// 	    for(int j = 0; j < this.mVal[i].getSerializedSize(); j++)
// 	    {
// 		r[idx] = serializedT[j];
// 		idx++;
// 	    }
// 	    delete [] serializedT; // ここだ! ここがメモリリークだった．
// 	}
// 	return(r);
//     }
//     void deserialize(double *pBuf)
//     {
// 	int idx = 0;
// 	int i;
// 	for(i = 0; i < 3; i++)
// 	{
// 	    this.mMinIdx[i] = (int)(pBuf[idx]);
// 	    idx++;
// 	}
// 	for(i = 0; i < 3; i++)
// 	{
// 	    this.mMaxIdx[i] = (int)(pBuf[idx]);
// 	    idx++;
// 	}
	
// 	// この grid を初期化，領域確保
// 	this.init(this.mMinIdx, this.mMaxIdx);
// 	for(i = 0; i < 3; i++)
// 	{
// 	    if(this.mSize[i] != (int)(pBuf[idx]))
// 	    {
// 		System.out.println( + "Error! : Array3D.deserialize, " 
// 		    "size is not valid." + endl;
// 		exit(1);
// 	    }
// 	    idx++;		// pBuf は進めておかねばならない
// 	}
// 	int size = this.getTotalSize();
// 	for(i = 0; i < size; i++)
// 	{
// 	    this.mVal[i].deserialize((pBuf[idx]));
// 	    idx += this.mVal[i].getSerializedSize();
// 	}
//     }
}
