//
//	cliplot.cs
//
//	抽選処理
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Tue Feb 28 21:46:05 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//	* Fri Mar 03 22:34:24 JST 2017 Naoyuki Sawa
//	- clipptr_net.csモジュールを追加した事に伴い、ポインタの扱いを大幅に変更しました。
//	  これまでは、配列とインデクスを保持する必要が有りましたが、今後はC言語版と同様に、配列ポインタ一つを保持すれば良くなりました。
//	- 今回は変更量がかなり多かったので、変更前のコードはコメントアウトして残していません。
//	  変更前のコードを参照する場合は、前回のアーカイブを参照して下さい。
//
#define USE_LOT_EQUALLY
using System;
using System.Collections.Generic;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	「抽選列」に対する抽選処理
		//*****************************************************************************
		public static int LOT_draw_column(UInt16Ptr column, int column_size, int rndval) {
			int hitval, weight;
			for(;;) {
				//抽選列の終端に到達したら、-1を返します。
				if(--column_size < 0) { return -1; }
				// 当選値   を取得します。
				hitval = column.Read();
				//(重み値-1)を取得します。
				weight = column.Read();
				//乱数値から重み値を引き、0未満になったら当選です。
				if((rndval -= (weight + 1)) < 0) { return hitval; }
			}
		}
		//-----------------------------------------------------------------------------
#if     USE_LOT_EQUALLY
		public static int LOT_draw_column_equally(UInt16Ptr column, int column_size, int rndval) {
			if(column_size <= 0) { throw new ApplicationException(); }
			rndval = (int)((uint)rndval % (uint)column_size);
			return column[(rndval * 2/*{hitval,weight}*/) + 0/*hitval*/];
		}
#endif//USE_LOT_EQUALLY
		//-----------------------------------------------------------------------------
		public static int LOT_get_column_weight(UInt16Ptr column, int column_size/*>=0*/, int hitval_sel) {
			UInt16Ptr mid;
			int hitval, weight;
			if((uint)hitval_sel <= short.MaxValue) {	//当選値(0～65535)の範囲外の値が指定された場合は、指定された当選値の行が見つからなかったのと同じで0を返します。
				while(column_size != 0) {
					mid = column + ((column_size >> 1) * 2/*{hitval,weight}*/);
					hitval = mid.Read();
					weight = mid.Read();
					hitval -= hitval_sel;
					if(hitval == 0) { return weight + 1; }	//指定された当選値の行が見つかったら重み値(1～65536)を返します。
					if(hitval <  0) {
						column = mid;	//bsearch()の場合は(mid+size)を代入する所だが、当関数の場合midは既に(mid+size)へ進んでいるのでmidの代入で済む。
						column_size--;
					}
					column_size >>= 1;
				}
			}
			return 0;	//指定された当選値の行が見つからなければ0を返します。
		}
		//*****************************************************************************
		//	「抽選列」に対する抽選処理
		//*****************************************************************************
		private static int LOT_seek_and_call(UInt16Ptr table, int param, IEnumerable<int> pid, Func<UInt16Ptr/*table*/,int/*column_size*/,int/*param*/,int> fn) {
			int table_size, id_size, column_size, n_id, diff;
			IEnumerator<int> e = pid.GetEnumerator();
			//抽選表の列数を取得します。
			table_size = table.Read();
			//抽選列のid行数を取得します。
			id_size    = table.Read();
			for(;;) {
				//抽選表の最終列を超えていたら、エラー停止します。
				if(--table_size < 0) { throw new ApplicationException(); }
				//抽選列のidを比較します。
				n_id = id_size;
				diff = 0;
				while(--n_id >= 0) { diff |= table.Read() - (e.MoveNext() ? e.Current : -1); }	//アプリケーションが誤って抽選列のid行数を少なく指定した場合、ここで不一致となる。
				//抽選列の要素数を取得します。
				column_size = table.Read();
				//指定されたidの組合せを持つ抽選列が見つかったら、抜けます。
				if(diff == 0) { break; }
				//指定されたidの組合せのポインタを戻します。
				e.Reset();
				//抽選列をスキップします。
				table += column_size * 2/*{hitval,weight}*/;
			}
			//指定された抽選列のidの組合せの、終端を確認します。
			if(e.MoveNext()) { throw new ApplicationException(); }					//アプリケーションが誤って抽選列のid行数を多く指定した場合、ここでエラー停止する。
			//関数を実行します。
			return fn.Invoke(table, column_size, param);
		}
		//-----------------------------------------------------------------------------
		public static int LOT_draw_table_l(UInt16Ptr table, int rndval, params int[] pid) {
			return LOT_draw_table_v(table, rndval, pid);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int LOT_draw_table_v(UInt16Ptr table, int rndval, IEnumerable<int> pid) {
			return LOT_seek_and_call(table, rndval, pid, LOT_draw_column);
		}
		//-----------------------------------------------------------------------------
#if     USE_LOT_EQUALLY
		public static int LOT_draw_table_equally_l(UInt16Ptr table, int rndval, params int[] pid) {
			return LOT_draw_table_equally_v(table, rndval, pid);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int LOT_draw_table_equally_v(UInt16Ptr table, int rndval, IEnumerable<int> pid) {
			return LOT_seek_and_call(table, rndval, pid, LOT_draw_column_equally);
		}
#endif//USE_LOT_EQUALLY
		//-----------------------------------------------------------------------------
		public static int LOT_get_table_weight_l(UInt16Ptr table, int hitval_sel, params int[] pid) {
			return LOT_get_table_weight_v(table, hitval_sel, pid);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int LOT_get_table_weight_v(UInt16Ptr table, int hitval_sel, IEnumerable<int> pid) {
			return LOT_seek_and_call(table, hitval_sel, pid, LOT_get_column_weight);
		}
		//-----------------------------------------------------------------------------
		public static int LOT_get_table_size(UInt16Ptr table) {
			return table[0/*table_size*/];
		}
		//-----------------------------------------------------------------------------
		public static int LOT_get_table_id_size(UInt16Ptr table) {
			return table[1/*id_size*/];
		}
		//-----------------------------------------------------------------------------
		public static int[] LOT_get_table_column_id(UInt16Ptr table, int i_column) {
			int table_size, id_size, column_size;
			//抽選表の列数を取得します。
			table_size = table.Read();
			//抽選列のid行数を取得します。
			id_size    = table.Read();
			//抽選表の列数が、抽選列の順番(0～)以下である場合は、エラー停止します。
			if(table_size <= i_column) { throw new ApplicationException(); }
			//(i_column)番目の抽選列の、idの組合せのポインタを取得します。
			while(--i_column >= 0) {
				table += id_size;
				column_size = table.Read();
				table += column_size * 2/*{hitval,weight}*/;
			}
			//idの組合せを格納します。
			int[] pid = new int[id_size];
			for(int i = 0; i < id_size; i++) { pid[i] = table[i]; }
			return pid;
		}
		//*****************************************************************************
		//	抽選列idのシンボル値変換(汎用的なテーブル変換にも利用可能)
		//*****************************************************************************
		public static int LotSym_ConvTo(ushort[,] pTBL, int x) {	//[0]⇒[1]変換
			for(int i = 0; /** no job **/; i++) {
				int xValue = pTBL[i, 0];			//[0]の値を読み出す。
				int yValue = pTBL[i, 1];			//[1]の値を読み出す。
				if(xValue == ushort.MaxValue) { throw new ApplicationException(); }	//(pTBL[i][0]=USHRT_MAX)ならば終端。	←どちらの関数も[0]の値で終端を判断することに注意せよ。
				if(xValue == x) { return yValue; }		//[0]の値に一致したら[1]の値を返す。
			}
		}
		//-----------------------------------------------------------------------------
		public static int LotSym_ConvFrom(ushort[,] pTBL, int y) {	//[1]⇒[0]変換
			for(int i = 0; /** no job **/; i++) {
				int xValue = pTBL[i, 0];			//[0]の値を読み出す。
				int yValue = pTBL[i, 1];			//[1]の値を読み出す。
				if(xValue == ushort.MaxValue) { throw new ApplicationException(); }	//(pTBL[i][0]=USHRT_MAX)ならば終端。	←どちらの関数も[0]の値で終端を判断することに注意せよ。
				if(yValue == y) { return xValue; }		//[1]の値に一致したら[0]の値を返す。
			}
		}
		//*****************************************************************************
		//	16ビットで大きな値を表現するための、指数表現
		//*****************************************************************************
		public static int Exp10_Decode(int _x) {
			uint x = (uint)_x, e;
			if(x > ushort.MaxValue) { throw new ApplicationException(); }	//呼び出し側のバグ
			//指数を求める。
			e = x % 6;			//0～5
			//仮数を求める。
			x = x / 6;			//0～10922
			//(仮数×10^指数)を求める。
			while(e-- != 0) { x *= 10; }	//0～1092100000(=0x41181FA0)
			return (int)x;
		}
		//-----------------------------------------------------------------------------
		public static int Exp10_Encode(int _x) {
			uint x = (uint)_x, e = 0;
			//指数を求める。
			while((x != 0) && ((x % 10) == 0) && (e < 5)) {
				x /= 10;
				e++;
			}
			//(仮数×6＋指数)を求める。
			if(x > ushort.MaxValue) { return -1; }	//エンコード値が16ビットを超えるならば、エンコード出来ない事を示す-1を返す。	「(x*6)+e」がオーバーフローする可能性が有るので、「(x*6)+e」の前でも調べる必要が有る事に注意せよ。
			x = (x * 6) + e;
			if(x > ushort.MaxValue) { return -1; }	//エンコード値が16ビットを超えるならば、エンコード出来ない事を示す-1を返す。
			return (int)x;
		}
	}
}
