/*	
 *	cliplot.c
 *
 *	抽選処理
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2015 Naoyuki Sawa
 *
 *	* Tue May 29 00:54:45 JST 2012 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sat Aug 03 00:18:29 JST 2013 Naoyuki Sawa
 *	- 抽選表のデータ構造を変更し、抽選列の数を追加しました。
 *	  LOT_draw_table()に不正な抽選列インデクスを指定した時、エラー停止するようにしました。
 *	- tool/MkLotTbl.awk を変更し、新しいデータ構造に沿った抽選表を出力するようにしました。
 *	  もう、古いデータ構造の抽選表を使用する事はありません。
 *	* Tue Jan 14 23:30:11 JST 2014 Naoyuki Sawa
 *	- 抽選表のデータ構造を変更し、抽選列のidを追加しました。
 *	  LOT_draw_table()の仕様を変更し、抽選列をインデクスでなくidとスキップ回数で指定するようにしました。
 *	- tool/MkLotTbl.awk を変更し、新しいデータ構造に沿った抽選表を出力するようにしました。
 *	  もう、古いデータ構造の抽選表を使用する事はありません。
 *	* Fri Jan 31 01:01:32 JST 2014 Naoyuki Sawa
 *	- LOT_get_column_weight()とLOT_get_table_weight()を追加しました。
 *	* Fri Feb 28 01:16:14 JST 2014 Naoyuki Sawa
 *	- LOT_get_table_size()とLOT_get_table_column_id()を追加しました。
 *	* Sat Aug 23 11:56:18 JST 2014 Naoyuki Sawa
 *	- LOT_draw_table_gl(),LOT_draw_table_ga(),LOT_draw_table_gv()を追加しました。
 *	- LOT_get_table_weight_gl(),LOT_get_table_weight_ga(),LOT_get_table_weight_gv()を追加しました。
 *	- LOT_get_table_size_g()を追加しました。
 *	* Mon Aug 25 20:01:41 JST 2014 Naoyuki Sawa
 *	- 「抽選表」の形式を変更しました。グループヘッダの概念は無くなりました。
 *	  既存の「「抽選表」に対する抽選処理」と「「グループヘッダ付き抽選表」に対する抽選処理」を削除して、
 *	  新しい「「抽選表」に対する抽選処理」を定義しました。
 *	* Thu Dec 04 21:39:33 JST 2014 Naoyuki Sawa
 *	- LotSym_ConvTo(),LotSym_ConvFrom()を追加しました。
 *	* Sun Mar 15 21:44:53 JST 2015 Naoyuki Sawa
 *	- LOT_get_column_weight()をリニアサーチ⇒バイナリサーチに変更しました。
 *	  関数仕様は変更有りません。
 *	  詳細は「clip/tool/dLotTblC/winapp.h」の2015/03/15コメントを参照してください。
 */
#include "clip.h"

/****************************************************************************
 *	「抽選列」に対する抽選処理
 ****************************************************************************/

#ifndef PIECE
int LOT_draw_column(const unsigned short* column, int column_size, int rndval) {
	int hitval, weight;
	for(;;) {
		/* 抽選列の終端に到達したら、-1を返します。 */
		if(--column_size < 0) { return -1; }
		/*  当選値   を取得します。 */
		hitval = *column++;
		/* (重み値-1)を取得します。 */
		weight = *column++;
		/* 乱数値から重み値を引き、0未満になったら当選です。 */
		if((rndval -= (weight + 1)) < 0) { return hitval; }
	}
}
#else /*PIECE*/
int LOT_draw_column(const unsigned short* column, int column_size, int rndval);
asm("
		.code
		.align		1
		.global		LOT_draw_column
LOT_draw_column:
		;// %r12 := column
		;// %r13 := column_size
		;// %r14 := rndval
LOT_draw_column_LOOP:
		sub		%r13, 1			;// %r13 := column_size--
		jrlt		LOT_draw_column_MISS	;// if(column_size < 0) return -1
		;//
		ld.uh		%r10, [%r12]+		;// %r10 := hitval = *column++
		ld.uh		%r11, [%r12]+		;// %r11 := weight = *column++
;//{{高速化
;//		add		%r11, 1			;// %r11 :=           (weight + 1)
;//		sub		%r14, %r11		;// %r14 := rndval -= (weight + 1)
;//		jrge		LOT_draw_column_LOOP	;// if(rndval <  0) return hitval
;//↓高速化
		sub		%r14, %r11		;// %r14 := rndval -= (weight    )
		jrgt.d		LOT_draw_column_LOOP	;// if(rndval <= 0) return hitval
		sub		%r14, 1			;// %r14 := rndval -= (         1)	*delay*
;//}}高速化
		ret					;// return  hitval
LOT_draw_column_MISS:
		ret.d					;// return  -1
		ld.w		%r10, -1		;// %r10 := -1				*delay*
");
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

//{{2015/03/15変更:LOT_get_column_weight()をリニアサーチ⇒バイナリサーチに変更しました。関数仕様は変更有りません。詳細は「clip/tool/dLotTblC/winapp.h」の2015/03/15コメントを参照してください。
//#ifndef PIECE
//int LOT_get_column_weight(const unsigned short* column, int column_size, int hitval_sel) {
//	int hitval, weight;
//	for(;;) {
//		/* 抽選列の終端に到達したら、0を返します。 */
//		if(--column_size < 0) { return 0; }
//		/*  当選値   を取得します。 */
//		hitval = *column++;
//		/* (重み値-1)を取得します。 */
//		weight = *column++;
//		/* 当選値が一致したら、重み値(1〜65536)を返します。 */
//		if(hitval == hitval_sel) { return (weight + 1); }
//	}
//}
//#else /*PIECE*/
//int LOT_get_column_weight(const unsigned short* column, int column_size, int hitval_sel);
//asm("
//		.code
//		.align		1
//		.global		LOT_get_column_weight
//LOT_get_column_weight:
//		;// %r12 := column
//		;// %r13 := column_size
//		;// %r14 := hitval_sel
//LOT_get_column_weight_LOOP:
//		sub		%r13, 1				;// %r13 := column_size--
//		jrlt		LOT_get_column_weight_MISS	;// if(column_size < 0) return 0
//		;//
//		ld.uh		%r11, [%r12]+			;// %r11 := hitval = *column++
//		ld.uh		%r10, [%r12]+			;// %r10 := weight = *column++
//		cmp		%r11, %r14			;// if(hitval == hitval_sel)
//		jrne		LOT_get_column_weight_LOOP
//		ret.d						;//   return  (weight + 1)
//		add		%r10, 1				;//   %r10 := (weight + 1)	*delay*
//LOT_get_column_weight_MISS:
//		ret.d						;// return  0
//		ld.w		%r10, 0				;// %r10 := 0			*delay*
//");
//#endif/*PIECE*/
//↓2015/03/15変更:LOT_get_column_weight()をリニアサーチ⇒バイナリサーチに変更しました。関数仕様は変更有りません。詳細は「clip/tool/dLotTblC/winapp.h」の2015/03/15コメントを参照してください。
#ifndef PIECE
static int LOT_get_column_weight_compar(const void* _x, const void* _y) {
	int x = *(unsigned short*)_x;	//x[2]={hitval,weight}のhitvalの部分のみを参照します。
	int y = *(unsigned short*)_y;	//y[2]={hitval,weight}のhitvalの部分のみを参照します。
	return x - y;	//(unsigned short)なのでオーバーフローの心配は無く単純な減算結果を返せば良い。
}
int LOT_get_column_weight(const unsigned short* column, int column_size, int hitval_sel) {
	//当選値(0〜65535)の範囲外の値が指定された場合は、指定された当選値の行が見つからなかったのと同じで0を返します。
	if((unsigned)hitval_sel > USHRT_MAX) { return 0; }
	//指定された当選値の行を検索します。
	column = bsearch(
		&hitval_sel,	//下位ハーフワード部分のみを参照します。
		column,
		column_size,
		sizeof(unsigned short) * 2/*{hitval,weight}*/,
		LOT_get_column_weight_compar);
	//指定された当選値の行が見つからなければ0を返します。
	if(!column) { return 0; }
	//指定された当選値の行が見つかったら重み値(1〜65536)を返します。
	return *(column + 1/*weight*/) + 1;
}
#else //PIECE
int LOT_get_column_weight(const unsigned short* column, int column_size, int hitval_sel);
asm("
		.code
		.align		1
		.global		LOT_get_column_weight
LOT_get_column_weight:
		;//%r12 := column
		;//%r13 := column_size
		;//%r14 := hitval_sel
		xcmp		%r14, 0xFFFF				;//if((unsigned)hitval_sel > USHRT_MAX) {
		jrugt.d		LOT_get_column_weight_RET		;//  %r10     := x = -1
		ld.w		%r10, -1				;//  goto RET }																		*delay*
		xsub		%sp, %sp, 8				;//
		xld.h		[%sp+4], %r14				;//[%sp+4].uh := hitval_sel
		ld.w		%r14, %r13				;//%r14       := column_size
		ld.w		%r13, %r12				;//%r13       := column
		ld.w		%r12, %sp				;//%r12       := &[%sp+0]
		xld.w		%r9, LOT_get_column_weight_compar	;//%r9        := LOT_get_column_weight_compar
		ld.w		[%r12]+, %r9				;//[%sp+0].w  := LOT_get_column_weight_compar, %r12 := &hitval_sel
		xcall.d		bsearch					;//%r10       := column = bsearch(&hitval_sel, column, column_size, sizeof(unsigned short) * 2/*{hitval,weight}*/, LOT_get_column_weight_compar)
		ld.w		%r15, 4					;//%r15       :=                                                    sizeof(unsigned short) * 2/*{hitval,weight}*/					*delay*
		sub		%r10, 1					;//%r10       := x = column - 1
		jrult.d		4					;//if(x != -1) {
		 xadd		%sp, %sp, 8				;//																			*delay*
		 add		%r10, 3					;//  %r10     :=   column + sizeof(unsigned short) = x + 1 + sizeof(unsigned short)
		 ld.uh		%r10, [%r10]				;//  %r10     := *(column + sizeof(unsigned short))
LOT_get_column_weight_RET:
		ret.d							;//return        x + 1
		add		%r10, 1					;//%r10       := x + 1																	*delay*
		;//-----------------------------------------------------;//
LOT_get_column_weight_compar:
		;//%r12 := _x
		;//%r13 := _y
		ld.uh		%r10, [%r12]				;//%r10 := x = *(unsigned short*)_x
		ld.uh		%r9 , [%r13]				;//%r9  := y = *(unsigned short*)_y
		ret.d							;//return  x - y
		sub		%r10, %r9				;//%r10 := x - y																	*delay*
");
#endif//PIECE
//}}2015/03/15変更:LOT_get_column_weight()をリニアサーチ⇒バイナリサーチに変更しました。関数仕様は変更有りません。詳細は「clip/tool/dLotTblC/winapp.h」の2015/03/15コメントを参照してください。

//{{2014/08/25削除:「抽選表」形式変更のために、古いコードを削除しました。古いコードは2014/08/24まで問題無く動作していたので、参考用としてコメント化したまま残しておきます。
//	
//	/****************************************************************************
//	 *	「抽選表」に対する抽選処理
//	 ****************************************************************************/
//	
//	#ifndef PIECE
//	static int LOT_seek_and_call(const unsigned short* table, int id_skip, int param, int (*fn)(const unsigned short* column, int column_size, int param)) {
//		int id, skip, table_size, column_id, column_size;
//		/* 抽選列のidとスキップ回数を分離します。 */
//		id   = (unsigned short)(id_skip >>  0); /* 0〜65535 */
//		skip = (unsigned short)(id_skip >> 16); /* 0〜65535 */
//		/* 抽選表の列数を取得します。 */
//		table_size = *table++;
//		for(;;) {
//			/* 抽選表の最終列を超えていたら、エラー停止します。 */
//			if(--table_size < 0) { DIE(); } /* データエラー: 抽選表の中に、指定されたidを持つ抽選列が、(skip+1)回以上出現しなかった。 */
//			/* 抽選列のidを取得します。 */
//			column_id   = *table++;
//			/* 抽選列の要素数を取得します。 */
//			column_size = *table++;
//			/* 指定されたidを持つ抽選列が見つかったら、スキップ回数を減らし0未満になったら、関数を実行します。 */
//			if((column_id == id) && (--skip < 0)) { return (*fn)(table, column_size, param); }
//			/* 抽選列をスキップします。 */
//			table += column_size * 2/*{hitval,weight}*/;
//		}
//	}
//	#else /*PIECE*/
//	/*static*/ int LOT_seek_and_call(const unsigned short* table, int id_skip, int param, int (*fn)(const unsigned short* column, int column_size, int param));
//	asm("
//			.code
//			.align		1
//	LOT_seek_and_call:
//			;// %r12        := table
//			;// %r13[15: 0] := id
//			;// %r13[31:16] := skip
//			;// %r14        := param
//			;// %r15        := fn
//			ld.uh		%r4, %r13		;// %r4  := id   = (unsigned short)(id_skip >>  0) /* 0〜65535 */
//			ld.w		%r5, %r13		;// %r5  := skip = (unsigned short)(id_skip >> 16) /* 0〜65535 */
//			xsrl		%r5, 16
//			ld.uh		%r6, [%r12]+		;// %r6  := table_size = *table++
//	LOT_seek_and_call_LOOP:					;// for(;;) {
//			sub		%r6, 1			;//   %r6  := table_size--
//			jrlt		LOT_seek_and_call_DIE	;//   if(table_size < 0) { DIE() } /* データエラー: 抽選表の中に、指定されたidを持つ抽選列が、(skip+1)回以上出現しなかった。 */
//			ld.uh		%r9, [%r12]+		;//   %r9  := column_id   = *table++
//			ld.uh		%r13, [%r12]+		;//   %r13 := column_size = *table++
//			cmp		%r9, %r4		;//   if((column_id == id)
//			jrne		3
//			 sub		%r5, 1			;//   && (--skip < 0)) { break }
//			 jrlt		LOT_seek_and_call_RET
//			sla		%r13, 2			;//   %r13 :=          column_size * 4
//			jp.d		LOT_seek_and_call_LOOP	;// }
//			add		%r12, %r13		;//   %r12 := table += column_size * 4	*delay*
//	LOT_seek_and_call_RET:
//			jp		%r15			;// return (*fn)(table, column_size, param)
//	");
//	static void __attribute__((noreturn,unused)) LOT_seek_and_call_DIE() { DIE(); /*抽選列インデクスが不正*/ } /*asmブロックから参照*/
//	#endif/*PIECE*/
//	
//	/*--------------------------------------------------------------------------*/
//	
//	#ifndef PIECE
//	int LOT_draw_table(const unsigned short* table, int id_skip, int rndval) {
//		return LOT_seek_and_call(table, id_skip, rndval, LOT_draw_column);
//	}
//	#else /*PIECE*/
//	int LOT_draw_table(const unsigned short* table, int id_skip, int rndval);
//	asm("
//			.code
//			.align		1
//			.global		LOT_draw_table
//	LOT_draw_table:
//			xld.w		%r15, LOT_draw_column
//			jp		LOT_seek_and_call
//	");
//	#endif/*PIECE*/
//	
//	/*--------------------------------------------------------------------------*/
//	
//	#ifndef PIECE
//	int LOT_get_table_weight(const unsigned short* table, int id_skip, int hitval_sel) {
//		return LOT_seek_and_call(table, id_skip, hitval_sel, LOT_get_column_weight);
//	}
//	#else /*PIECE*/
//	int LOT_get_table_weight(const unsigned short* table, int id_skip, int hitval_sel);
//	asm("
//			.code
//			.align		1
//			.global		LOT_get_table_weight
//	LOT_get_table_weight:
//			xld.w		%r15, LOT_get_column_weight
//			jp		LOT_seek_and_call
//	");
//	#endif/*PIECE*/
//	
//	/*--------------------------------------------------------------------------*/
//	
//	#ifndef PIECE
//	int LOT_get_table_column_id(const unsigned short* table, int i_column) {
//		int table_size, column_size, id, skip;
//		const unsigned short* column;
//		/* 抽選表の列数を取得します。 */
//		table_size = *table++;
//		/* 抽選表の列数が、抽選列の順番(0〜)以下である場合は、エラー停止します。 */
//		if(table_size <= i_column) { DIE(); }
//		/* (i_column)番目の抽選列の、idを取得します。 */
//		column = table;
//		while(--i_column >= 0) {
//			               column++; /*column_id*/
//			column_size = *column++;
//			               column += column_size * 2/*{hitval,weight}*/;
//		}
//		id = *column;
//		/* (i_column)番目の抽選列よりも前に、(i_column)番目の抽選列と同じidの抽選列が存在する場合、その列数をスキップ回数として数えます。 */
//		skip = 0;
//		while(table != column) {
//			           if(*table++ == id) { skip += (1<<16); } /*column_id*/
//			column_size = *table++;
//			               table += column_size * 2/*{hitval,weight}*/;
//		}
//		/* 抽選列のidと、スキップ回数を返します。 */
//		return id | skip;
//	}
//	#else /*PIECE*/
//	asm("
//			.code
//			.align		1
//			.global		LOT_get_table_column_id
//	LOT_get_table_column_id:
//			ld.uh		%r15, [%r12]+			;// %r15 := table_size = *table++;
//			cmp		%r15, %r13			;// if(table_size <= i_column) { DIE() }
//			jrle		LOT_get_table_column_id_DIE
//			ld.w		%r14, %r12			;// %r14 := column
//			;// %r12 := table	抽選列[0]を指している
//			;// %r13 := i_column
//			;// %r14 := column	抽選列[0]を指している
//	LOT_get_table_column_id_LOOP1:
//			sub		%r13, 1				;// while(--i_column >= 0) {
//			jrlt		LOT_get_table_column_id_EXIT1
//			add		%r14, 2				;//   %r14 :=                column++
//			ld.uh		%r15, [%r14]+			;//   %r15 := column_size = *column++
//			sla		%r15, 2				;//   %r15 :=                          (column_size * 4)
//			jp.d		LOT_get_table_column_id_LOOP1	;// }
//			add		%r14, %r15			;//   %r14 :=                column += (column_size * 4)	*delay*
//	LOT_get_table_column_id_EXIT1:
//			ld.uh		%r10, [%r14]			;// %r10 := id = *column
//			ld.w		%r11, 0				;// %r11 := skip = 0
//			;// %r10 := id		抽選列[i_column]のcolumn_id
//			;// %r11 := skip
//			;// %r12 := table	抽選列[0]を指している
//			;// %r14 := column	抽選列[i_column]を指している
//	LOT_get_table_column_id_LOOP2:
//			cmp		%r12, %r14			;// while(table != column) {
//			jreq		LOT_get_table_column_id_EXIT2
//			ld.uh		%r13, [%r12]+			;//   %r13 := column_id   = *table++
//			ld.uh		%r15, [%r12]+			;//   %r15 := column_size = *table++
//			cmp		%r13, %r10			;//   if(column_id == id) {
//			jrne.d		LOT_get_table_column_id_SKIP
//			 sla		%r15, 2				;//   %r15 :=                         (column_size * 4)		*delay*
//			 xadd		%r11, %r11, 0x10000		;//     %r11 := skip += (1<<16)							「ここで1加算し最後に16ビットシフトしてorする」よりも「ここで(1<<16)加算し最後にそのままorする」方が一命令節約できる。一般的な抽選表ではskip=0であることが多くここを通ることは少ないので性能低下はほぼ無いであろう。
//	LOT_get_table_column_id_SKIP:
//			jp.d		LOT_get_table_column_id_LOOP2	;// }
//			add		%r12, %r15			;//   %r12 :=                table += (column_size * 4)		*delay*
//	LOT_get_table_column_id_EXIT2:
//			ret.d						;// return  id | skip
//			or		%r10, %r11			;// %r10 := id | skip						*delay*
//	");
//	static void __attribute__((noreturn,unused)) LOT_get_table_column_id_DIE() { DIE(); }
//	#endif/*PIECE*/
//	
//	/****************************************************************************
//	 *	「グループヘッダ付き抽選表」に対する抽選処理
//	 ****************************************************************************/
//	
//	static int LOT_seek_and_call_g(const unsigned short* table_g, const int* vp, int param, int (*fn)(const unsigned short* column, int column_size, int param)) {
//		int i, num_grps, grp_cols, id_skip = 0;
//		/* グループ数を取得する。 */
//		num_grps = *table_g++;
//		for(;;) {
//			/* グループインデクス,又は,抽選列のidを取得する。 */
//			i = *vp++;
//			/* グループインデクス,又は,抽選列のidが、符号無しハーフワード値であることを確認する。 */
//			if((unsigned)i >= USHRT_MAX) { DIE(); }
//			/* グループが無ければ(=デクリメントする前に0だったら)、抜ける。 */				//iは抽選列のidである。
//			if(--num_grps < 0) { break; }
//			/* グループ列数を取得する。 */
//			grp_cols = *table_g++;
//			/* グループ列数が0でないことと、グループインデクスがグループ列数未満であることを確認する。 */	//iはグループインデクスである。
//			if((unsigned)i >= (unsigned)grp_cols) { DIE(); }
//			/* 上位のスキップ回数に今回のグループ列数を乗じて、グループインデクスを加える。 */
//			id_skip = (id_skip * grp_cols) + i;
//			/* スキップ回数が、符号無しハーフワード値であることを確認する。 */	//オーバーフローを見落とさないよう、最終結果だけではなく毎回確認する。
//			if((unsigned)id_skip >= USHRT_MAX) { DIE(); }
//		}
//		/* スキップ回数を上位ハーフワードへシフトして、下位ハーフワードに抽選列のidを格納する。 */
//		id_skip = (id_skip << 16) | i;
//		/* 可変引数の終端が、(-1)であることを確認する。 */
//		if(*vp != -1) { DIE(); }
//		/* スキップ回数と抽選列のidの組合せを用いて、関数を実行する。 */
//		return (*fn)(table_g, id_skip, param);
//	}
//	
//	/*--------------------------------------------------------------------------*/
//	
//	int LOT_draw_table_gl(const unsigned short* table_g, int rndval, ...) {
//		int retval;
//		va_list ap;
//		va_start(ap, rndval);
//		retval = LOT_draw_table_ga(table_g, ap, rndval);
//		va_end(ap);
//		return retval;
//	}
//	int LOT_draw_table_ga(const unsigned short* table_g, va_list ap, int rndval) {
//		int vp[(7+1)/*調整可*/], i = 0;			//グループ数7(終端含め8)有れば充分と考えた。不足した場合は増やせ。
//		for(;;) {
//			if((vp[i] = va_arg(ap, int)) == -1) { break; }
//			if(++i >= ARRAY_SIZE(vp)) { DIE(); }	//ここで停止した場合、vp[]不足か,又は,呼出し側が終端を忘れている。おそらく後者であろう。
//		}
//		return LOT_draw_table_gv(table_g, vp, rndval);
//	}
//	int LOT_draw_table_gv(const unsigned short* table_g, const int* vp, int rndval) {
//		return LOT_seek_and_call_g(table_g, vp, rndval, LOT_draw_table);
//	}
//	
//	/*--------------------------------------------------------------------------*/
//	
//	int LOT_get_table_weight_gl(const unsigned short* table_g, int hitval_sel, ...) {
//		int retval;
//		va_list ap;
//		va_start(ap, hitval_sel);
//		retval = LOT_get_table_weight_ga(table_g, ap, hitval_sel);
//		va_end(ap);
//		return retval;
//	}
//	int LOT_get_table_weight_ga(const unsigned short* table_g, va_list ap, int hitval_sel) {
//		int vp[(7+1)/*調整可*/], i = 0;			//グループ数7(終端含め8)有れば充分と考えた。不足した場合は増やせ。
//		for(;;) {
//			if((vp[i] = va_arg(ap, int)) == -1) { break; }
//			if(++i >= ARRAY_SIZE(vp)) { DIE(); }	//ここで停止した場合、vp[]不足か,又は,呼出し側が終端を忘れている。おそらく後者であろう。
//		}
//		return LOT_get_table_weight_gv(table_g, vp, hitval_sel);
//	}
//	int LOT_get_table_weight_gv(const unsigned short* table_g, const int* vp, int hitval_sel) {
//		return LOT_seek_and_call_g(table_g, vp, hitval_sel, LOT_get_table_weight);
//	}
//	
//}}2014/08/25削除:「抽選表」形式変更のために、古いコードを削除しました。古いコードは2014/08/24まで問題無く動作していたので、参考用としてコメント化したまま残しておきます。

//{{2014/08/25追加:これ以降は、2014/08/25「抽選表」形式変更後に追加した、新しいコードです。}}

/****************************************************************************
 *	「抽選表」に対する抽選処理
 ****************************************************************************/

#ifndef PIECE
static int LOT_seek_and_call(const unsigned short* table, const int* pid, int param, int (*fn)(const unsigned short* column, int column_size, int param)) {
	int table_size, id_size, column_size, n_id, diff;
	/* 抽選表の列数を取得します。 */
	table_size = *table++;
	/* 抽選列のid行数を取得します。 */
	id_size    = *table++;
	for(;;) {
		/* 抽選表の最終列を超えていたら、エラー停止します。 */
		if(--table_size < 0) { DIE(); }
		/* 抽選列のidを比較します。 */
		n_id = id_size;
		diff = 0;
		while(--n_id >= 0) { diff |= (*table++ - *pid++); }		//アプリケーションが誤って抽選列のid行数を少なく指定した場合、ここで不一致となる。
		/* 抽選列の要素数を取得します。 */
		column_size = *table++;
		/* 指定されたidの組合せを持つ抽選列が見つかったら、抜けます。 */
		if(!diff) { break; }
		/* 指定されたidの組合せのポインタを戻します。 */
		pid -= id_size;
		/* 抽選列をスキップします。 */
		table += column_size * 2/*{hitval,weight}*/;
	}
	/* 指定された抽選列のidの組合せの、終端を確認します。 */
	if(*pid != -1) { DIE(); }						//アプリケーションが誤って抽選列のid行数を多く指定した場合、ここでエラー停止する。
	/* 関数を実行します。 */
	return (*fn)(table, column_size, param);
}
#else /*PIECE*/
/*static*/ int LOT_seek_and_call(const unsigned short* table, const int* pid, int param, int (*fn)(const unsigned short* column, int column_size, int param));
asm("
		.code
		.align		1
LOT_seek_and_call:
		;//%r12 := table
		;//%r13 := pid
		;//%r14 := param
		;//%r15 := fn
		ld.uh		%r4, [%r12]+					;//%r4  := table_size = *table++
		ld.uh		%r5, [%r12]+					;//%r5  := id_size    = *table++
		sla		%r5, 2						;//%r5  := id_size * sizeof(int)
LOT_seek_and_call_L1:								;//for(;;) {
		;//%r4  := table_size
		;//%r5  := id_size * sizeof(int)
		;//%r12 := table
		;//%r13 := pid
		;//%r14 := param
		;//%r15 := fn
		sub		%r4, 1						;//%r4  := table_size--
		jrlt		LOT_seek_and_call_DIE				;//if(table_size < 0) { DIE() }
		ld.w		%r6, %r5					;//%r6  := n_id = id_size * sizeof(int)
		ld.w		%r7, 0						;//%r7  := diff = 0
LOT_seek_and_call_L2:								;//for(;;) {
		;//%r4  := table_size
		;//%r5  := id_size * sizeof(int)
		;//%r6  := n_id
		;//%r7  := diff
		;//%r12 := table
		;//%r13 := pid
		;//%r14 := param
		;//%r15 := fn
		sub		%r6, 4						;//%r6  := n_id -= sizeof(int)
		jrlt		LOT_seek_and_call_L3				;//if(n_id < 0) { break }
		ld.uh		%r10, [%r12]+					;//%r10 :=          id1       = *table++
		ld.w		%r11, [%r13]+					;//%r11 :=                id2 = *pid++
		sub		%r10, %r11					;//%r10 :=         (id1 - id2)
		jp.d		LOT_seek_and_call_L2				;//}
		or		%r7, %r10					;//%r7  := diff |= (id1 - id2)					*delay*
LOT_seek_and_call_L3:
		;//%r4  := table_size
		;//%r5  := id_size * sizeof(int)
		;//%r7  := diff
		;//%r12 := table
		;//%r13 := pid
		;//%r14 := param
		;//%r15 := fn
		ld.uh		%r6, [%r12]+					;//%r6  := column_size = *table++
		cmp		%r7, 0						;//if(!diff) { break }
		jreq		LOT_seek_and_call_L4
		sub		%r13, %r5					;//%r13 := pid -=   (    id_size *     sizeof(int)           )
		sla		%r6, 2						;//%r6  :=          (column_size * 2 * sizeof(unsigned short))
		jp.d		LOT_seek_and_call_L1				;//}
		add		%r12, %r6					;//%r12 := table += (column_size * 2 * sizeof(unsigned short))	*delay*
LOT_seek_and_call_L4:
		;//%r6  := column_size
		;//%r12 := table
		;//%r13 := pid
		;//%r14 := param
		;//%r15 := fn
		ld.w		%r4, [%r13]					;//%r4  := id2 = *pid
		ld.w		%r13, %r6					;//%r13 := column_size		←───┐			*anti-interlock*
		cmp		%r4, -1						;//if(id2 != -1) { DIE() }		│
		jrne		LOT_seek_and_call_DIE				;//					│
;//		ld.w		%r13, %r6					;//%r13 := column_size		────┘
		jp		%r15						;//return (*fn)(table, column_size, param)			(jp.d %rbは不可)
");
static void __attribute__((noreturn,unused/*asmブロックから参照*/)) LOT_seek_and_call_DIE() { DIE(); }
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
int LOT_draw_table_l(const unsigned short* table, int rndval, ...) {
	int retval;
	va_list ap;
	va_start(ap, rndval);
	retval = LOT_draw_table_a(table, ap, rndval);
	va_end(ap);
	return retval;
}
int LOT_draw_table_a(const unsigned short* table, va_list ap, int rndval) {
	int id[(7+1)/*調整可*/], i = 0;			//実用上、抽選列のid行数が7以下(終端含め8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((id[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(id)) { DIE(); }	//ここで停止した場合は、id[]不足か,又は,呼出し側が終端を忘れている。おそらく後者だろう。
	}
	return LOT_draw_table_v(table, id, rndval);
}
int LOT_draw_table_v(const unsigned short* table, const int* pid, int rndval) {
	return LOT_seek_and_call(table, pid, rndval, LOT_draw_column);
}
#else /*PIECE*/
int LOT_draw_table_l(const unsigned short* table, int rndval, ...);
int LOT_draw_table_a(const unsigned short* table, va_list ap, int rndval);
int LOT_draw_table_v(const unsigned short* table, const int* pid, int rndval);
asm("
		.align		1
		.global		LOT_draw_table_l
		.global		LOT_draw_table_a
		.global		LOT_draw_table_v
LOT_draw_table_l:
		;//%r12    := table
		;//[%sp+0] := retp
		;//[%sp+4] := rndval
		;//[%sp+8] := ...
		xadd		%r13, %sp, 8					;//%r13 := ap = &...
		xld.w		%r14, [%sp+4]					;//%r14 := rndval
LOT_draw_table_a:		;//┐アセンブラプログラムでは、
LOT_draw_table_v:		;//┘この二つの関数は同じです。
		;//%r12    := table
		;//%r13    := pid = ap
		;//%r14    := rndval
		xld.w		%r15, LOT_draw_column				;//%r15 := LOT_draw_column
		xjp		LOT_seek_and_call				;//return LOT_seek_and_call(table, pid, rndval, LOT_draw_column)
");
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
int LOT_get_table_weight_l(const unsigned short* table, int hitval_sel, ...) {
	int retval;
	va_list ap;
	va_start(ap, hitval_sel);
	retval = LOT_get_table_weight_a(table, ap, hitval_sel);
	va_end(ap);
	return retval;
}
int LOT_get_table_weight_a(const unsigned short* table, va_list ap, int hitval_sel) {
	int id[(7+1)/*調整可*/], i = 0;			//実用上、抽選列のid行数が7以下(終端含め8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((id[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(id)) { DIE(); }	//ここで停止した場合は、id[]不足か,又は,呼出し側が終端を忘れている。おそらく後者だろう。
	}
	return LOT_get_table_weight_v(table, id, hitval_sel);
}
int LOT_get_table_weight_v(const unsigned short* table, const int* pid, int hitval_sel) {
	return LOT_seek_and_call(table, pid, hitval_sel, LOT_get_column_weight);
}
#else /*PIECE*/
int LOT_get_table_weight_l(const unsigned short* table, int hitval_sel, ...);
int LOT_get_table_weight_a(const unsigned short* table, va_list ap, int hitval_sel);
int LOT_get_table_weight_v(const unsigned short* table, const int* pid, int hitval_sel);
asm("
		.align		1
		.global		LOT_get_table_weight_l
		.global		LOT_get_table_weight_a
		.global		LOT_get_table_weight_v
LOT_get_table_weight_l:
		;//%r12    := table
		;//[%sp+0] := retp
		;//[%sp+4] := hitval_sel
		;//[%sp+8] := ...
		xadd		%r13, %sp, 8					;//%r13 := ap = &...
		xld.w		%r14, [%sp+4]					;//%r14 := hitval_sel
LOT_get_table_weight_a:		;//┐アセンブラプログラムでは、
LOT_get_table_weight_v:		;//┘この二つの関数は同じです。
		;//%r12    := table
		;//%r13    := pid = ap
		;//%r14    := hitval_sel
		xld.w		%r15, LOT_get_column_weight			;//%r15 := LOT_get_column_weight
		xjp		LOT_seek_and_call				;//return LOT_seek_and_call(table, pid, hitval_sel, LOT_get_column_weight)
");
#endif/*PIECE*/

/*--------------------------------------------------------------------------*/

#ifndef PIECE
void LOT_get_table_column_id(const unsigned short* table, int i_column, int* pid) {
	int table_size, id_size, column_size;
	/* 抽選表の列数を取得します。 */
	table_size = *table++;
	/* 抽選列のid行数を取得します。 */
	id_size    = *table++;
	/* 抽選表の列数が、抽選列の順番(0〜)以下である場合は、エラー停止します。 */
	if(table_size <= i_column) { DIE(); }
	/* (i_column)番目の抽選列の、idの組合せのポインタを取得します。 */
	while(--i_column >= 0) {
		table += id_size;
		column_size = *table++;
		table += column_size * 2/*{hitval,weight}*/;
	}
	/* idの組合せを格納します。 */
	while(--id_size >= 0) { *pid++ = *table++; }
	*pid++ = -1/*終端*/;
}
#else /*PIECE*/
void LOT_get_table_column_id(const unsigned short* table, int i_column, int* pid);
asm("
		.code
		.align		1
		.global		LOT_get_table_column_id
LOT_get_table_column_id:
		;//%r12 := table
		;//%r13 := i_column
		;//%r14 := pid
		ld.uh		%r4, [%r12]+					;//%r4  := table_size = *table++
		ld.uh		%r5, [%r12]+					;//%r5  := id_size    = *table++
		cmp		%r4, %r13					;//if(table_size <= i_column) { DIE() }
		jrle		LOT_get_table_column_id_DIE
		sla		%r5, 1						;//%r5  := id_size * sizeof(unsigned short)
LOT_get_table_column_id_L1:							;//for(;;) {
		;//%r5  := id_size * sizeof(unsigned short)
		;//%r12 := table
		;//%r13 := i_column
		;//%r14 := pid
		sub		%r13, 1						;//%r13 := i_column--
		jrlt		LOT_get_table_column_id_L2			;//if(i_column < 0) { break }
		add		%r12, %r5					;//%r12 := table += (    id_size     * sizeof(unsigned short))
		ld.uh		%r6, [%r12]+					;//%r6  :=           column_size = *table++
		sla		%r6, 2						;//%r6  :=          (column_size * 2 * sizeof(unsigned short))
		jp.d		LOT_get_table_column_id_L1			;//}
		add		%r12, %r6					;//%r12 := table += (column_size * 2 * sizeof(unsigned short))	*delay*
LOT_get_table_column_id_L2:							;//for(;;) {
		;//%r5  := id_size * sizeof(unsigned short)
		;//%r12 := table
		;//%r14 := pid
		sub		%r5, 2						;//%r5  := (id_size * sizeof(unsigned short)) -= sizeof(unsigned short)
		jrlt		LOT_get_table_column_id_L3			;//     if((id_size * sizeof(unsigned short)) < 0) { break }
		ld.uh		%r4, [%r12]+					;//%r4  :=  id = *table++
		ld.w		[%r14]+, %r4					;//*pid++ = id
		jp		LOT_get_table_column_id_L2			;//}
LOT_get_table_column_id_L3:
		ld.w		%r4, -1						;//%r4  :=  id = -1
		ld.w		[%r14], %r4					;//*pid   = id
		ret
");
static void __attribute__((noreturn,unused/*asmブロックから参照*/)) LOT_get_table_column_id_DIE() { DIE(); }
#endif/*PIECE*/

/****************************************************************************
 *	抽選列idのシンボル値変換(汎用的なテーブル変換にも利用可能)
 ****************************************************************************/

#ifndef PIECE
int LotSym_ConvTo(const unsigned short (*pTBL)[2], int x) {	//[0]⇒[1]変換
	const unsigned short* p = (const unsigned short*)pTBL;
	for(;;) {
		int xValue = *p++;				//[0]の値を読み出す。
		int yValue = *p++;				//[1]の値を読み出す。
		if(xValue == USHRT_MAX) { DIE(); }		//(pTBL[i][0]=USHRT_MAX)ならば終端。	←どちらの関数も[0]の値で終端を判断することに注意せよ。
		if(xValue == x) { return yValue; }		//[0]の値に一致したら[1]の値を返す。
	}
}
int LotSym_ConvFrom(const unsigned short (*pTBL)[2], int y) {	//[1]⇒[0]変換
	const unsigned short* p = (const unsigned short*)pTBL;
	for(;;) {
		int xValue = *p++;				//[0]の値を読み出す。
		int yValue = *p++;				//[1]の値を読み出す。
		if(xValue == USHRT_MAX) { DIE(); }		//(pTBL[i][0]=USHRT_MAX)ならば終端。	←どちらの関数も[0]の値で終端を判断することに注意せよ。
		if(yValue == y) { return xValue; }		//[1]の値に一致したら[0]の値を返す。
	}
}
#else //PIECE
int LotSym_ConvTo(const unsigned short (*pTBL)[2], int x);
int LotSym_ConvFrom(const unsigned short (*pTBL)[2], int y);
asm("
		.code
		.align		1
		.global		LotSym_ConvTo
		.global		LotSym_ConvFrom
LotSym_ConvTo:
		xld.w		%r4, 0xFFFF			;//%r4  := USHRT_MAX
LotSym_ConvTo_LOOP:						;//for(;;) {
		ld.uh		%r5, [%r12]+			;//  %r5  := xValue = *p++
		ld.uh		%r10, [%r12]+			;//  %r10 := yValue = *p++
		cmp		%r5, %r4			;//  if(xValue == USHRT_MAX) { DIE() }
		jreq		LotSym_ConvTo_ConvFrom_DIE	;//
		cmp		%r5, %r13			;//  if(xValue == x) { break }
		jrne		LotSym_ConvTo_LOOP		;//}
		ret						;//return yValue
LotSym_ConvFrom:
		xld.w		%r4, 0xFFFF			;//%r4  := USHRT_MAX
LotSym_ConvFrom_LOOP:						;//for(;;) {
		ld.uh		%r10, [%r12]+			;//  %r10 := xValue = *p++
		ld.uh		%r5, [%r12]+			;//  %r5  := yValue = *p++
		cmp		%r10, %r4			;//  if(xValue == USHRT_MAX) { DIE() }
		jreq		LotSym_ConvTo_ConvFrom_DIE	;//
		cmp		%r5, %r13			;//  if(yValue == y) { break }
		jrne		LotSym_ConvFrom_LOOP		;//}
		ret						;//return xValue
");
static void __attribute__((noreturn,unused))/*asmブロックから参照*/ LotSym_ConvTo_ConvFrom_DIE() { DIE(); }
#endif//PIECE
