/*	
 *	clipwkst.c
 *
 *	ワークシート(多階層ハッシュテーブル)
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015-2016 Naoyuki Sawa
 *
 *	* Mon Jul 13 21:22:16 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	* Wed Jul 15 01:12:55 JST 2015 Naoyuki Sawa
 *	- Worksheet_ref_l(),Worksheet_ref_a(),Worksheet_ref_v()を追加しました。
 *	- Worksheet_sum_l(),Worksheet_sum_a(),Worksheet_sum_v()を追加しました。
 *	* Thu Jul 16 22:21:18 JST 2015 Naoyuki Sawa
 *	- Worksheet_sort(),Worksheet_sort_l(),Worksheet_sort_a(),Worksheet_sort_v()を追加しました。
 *	* Sun Jul 19 12:59:05 JST 2015 Naoyuki Sawa
 *	- コメントのみ追記:
 *	  当モジュールの関数は、キーや値に指定できるのは数値のみで、文字列は指定できません。
 *	  キーや値に文字列を指定したい場合は、本日clipstr.cモジュールに追加した、quark_from_string(),quark_to_string()を併用してください。
 *	  <例>
 *	  │Worksheet_set_l(ws,quark_from_string("Value"),quark_from_string("Key"),-1);
 *	  │iVal=Worksheet_get_l(ws,quark_from_string("Key"),-1);
 *	  │if(iVal!=-1){puts(quark_to_string(iVal));}
 *	* Fri Dec 11 21:38:17 JST 2015 Naoyuki Sawa
 *	- Worksheet_incr_l()等が、これまでは戻り値無しでしたが、(1)増加後の値を返すようにしました。
 *	  カウンタとして用いる場合、セルの値を(1)増加して、すぐにその値を参照する事が多いからです。
 *	- 戻り値を無視すれば動作は変わっていないので、既存のアプリケーションへの影響は有りません。
 *	* Sun Jul 17 21:03:07 JST 2016 Naoyuki Sawa
 *	- Worksheet_ref_l(),Worksheet_get_l(),Worksheet_sum_l(),Worksheet_set_l(),Worksheet_unset_l(),Worksheet_incr_l(),Worksheet_first_l(),Worksheet_sort_l(),
 *	  Worksheet_ref_a(),Worksheet_get_a(),Worksheet_sum_a(),Worksheet_set_a(),Worksheet_unset_a(),Worksheet_incr_a(),Worksheet_first_a(),Worksheet_sort_a()をアセンブラ化しました。
 *	  主な目的は、コードサイズの低減です。
 *	  当モジュールコードサイズが、1468バイト⇒1016バイトに減りました。
 *	* Sun Jul 17 22:34:56 JST 2016 Naoyuki Sawa
 *	- USE_WORKSHEET_GTREEシンボルを定義すると、当モジュールがgtree.cモジュールを使用するようにしました。
 *	  USE_WORKSHEET_GTREEシンボルを定義しなければ、これまで通り、ght_hash_table.cモジュールを使用します。
 *	  当面は、USE_WORKSHEET_GTREEシンボルを定義して、gtree.cモジュールを使用するようにして、動作を検証しようと思います。
 *	  万一、動作上の問題が生じたら、ght_hash_table.cモジュールを使用するように戻して下さい。
 *	- gtree.cモジュールを使用するように変更したい理由は、ソート関数(Worksheet_sort()等)が不要になるからです。
 *	  ツリーは常にソートされているので、アプリケーションがイテレーション等の前に、明示的にソートする必要が無くなります。
 *	  USE_WORKSHEET_GTREEが定義されている場合はソート関数はダミー処理となります。
 *	  USE_WORKSHEET_GTREEが定義されている場合はソート関数を無効にしても良いのですが、ソート関数を呼び出している既存のアプリケーションのために残しておく事にしました。
 */
#include "clip.h"
/****************************************************************************
 *	作成/開放
 ****************************************************************************/
#ifdef  USE_WORKSHEET_GTREE
static int Worksheet_key_compare_func(const void* a, const void* b, void* user_data) {
	if((int)a < (int)b) { return -1; }
	if((int)a > (int)b) { return  1; }
	return 0;
}
#endif//USE_WORKSHEET_GTREE
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//集計表の単一階層を作成する。
// - この階層を列と見なすか行と見なすかは、アプリケーション側の解釈次第です。
ST_Worksheet* Worksheet_new() {
	//集計表の単一階層のメモリを確保する。
	ST_Worksheet* ws = calloc(1, sizeof(ST_Worksheet));
	if(!ws) { DIE(); }
#ifndef USE_WORKSHEET_GTREE
	//ハッシュテーブルを作成する。
	ws->tKey = ght_create(0);	//初期サイズを指定しない。
	ght_set_rehash(ws->tKey, 1);	//自動リサイズを有効とする。
#else //USE_WORKSHEET_GTREE
	//ツリーを作成する。
	ws->tKey = g_tree_new_full(Worksheet_key_compare_func, NULL, NULL, (void (*)(void*))Worksheet_free);
#endif//USE_WORKSHEET_GTREE
	//集計表の単一階層を返す。
	return ws;
}
/*--------------------------------------------------------------------------*/
//集計表の全階層を開放する。
void Worksheet_free(ST_Worksheet* ws/*ヌル可*/) {
	//ヌルポインタに対して呼出された場合は、何もしない。
	if(!ws) { return; }	//ここまで
#ifndef USE_WORKSHEET_GTREE
	//子階層を全て開放する。
	{
		ST_Worksheet_Iterator it;
		int iKey;
		for(iKey = Worksheet_first_l(ws, &it, -1);
		    iKey != -1;
		    iKey = Worksheet_next(&it)) { Worksheet_unset_l(ws, iKey, -1); }
	}
	//この階層を開放する。
	ght_finalize(ws->tKey);
#else //USE_WORKSHEET_GTREE
	//子階層を全て開放すると同時に、この階層を開放する。
	g_tree_unref(ws->tKey);
#endif//USE_WORKSHEET_GTREE
	free(ws);
}
/****************************************************************************
 *	参照
 ****************************************************************************/
//集計表のノードを参照する。
// - 指定されたノードが存在しない場合は、ヌルポインタを返します。
// - 可変引数は、(0)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、引数wsを返します。
// - キーには、(-1)以外の任意の整数を指定出来ます。
#ifndef PIECE
/*--------------------------------------------------------------------------*/
ST_Worksheet* Worksheet_ref_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/) {
	ST_Worksheet* pChild;
	va_list ap;
	va_start(ap, ws);
	pChild = Worksheet_ref_a(ws, ap);
	va_end(ap);
	return pChild;
}
/*--------------------------------------------------------------------------*/
ST_Worksheet* Worksheet_ref_a(ST_Worksheet* ws/*ヌル可*/, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	return Worksheet_ref_v(ws, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
ST_Worksheet* Worksheet_ref_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/);
ST_Worksheet* Worksheet_ref_a(ST_Worksheet* ws/*ヌル可*/, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_ref_l
		.global		Worksheet_ref_a
Worksheet_ref_l:
		;//[%sp+0] := retp
		;//[%sp+4] := ws
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]		;//%r12 := ws
		xadd		%r13, %sp, 8		;//%r13 := ap = &...
Worksheet_ref_a:
		;//%r12    := ws
		;//%r13    := ap
		xjp		Worksheet_ref_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
ST_Worksheet* Worksheet_ref_v(ST_Worksheet* ws/*ヌル可*/, const int* aKey/*={key,key,...,key,-1}*/) {
	int iKey;
	ST_Worksheet* pChild;
	//ヌルポインタに対して呼出された場合は、指定されたノードが存在しないのと同義と見なして、ヌルポインタを返す。
	if(!ws) { return NULL; }	//ここまで
	//次のキーを取得する。
	iKey = *aKey++;
	//キーが終端ならば、引数wsを返す。
	if(iKey == -1) { return ws; }	//ここまで
	//キーに対応する子階層(有れば)を取得する。
#ifndef USE_WORKSHEET_GTREE
	pChild = ght_get(ws->tKey, sizeof(int), &iKey);
#else //USE_WORKSHEET_GTREE
	pChild = g_tree_lookup(ws->tKey, (void*)iKey);
#endif//USE_WORKSHEET_GTREE
	//キーに対応する子階層(有れば)に対して、再帰する。
	// - キーに対応する子階層が無かった場合も、同じ処理で安全である。
	return Worksheet_ref_v(pChild/*ヌル可*/, aKey);
}
/****************************************************************************
 *	取得
 ****************************************************************************/
//集計表のセルの値を取得する。
// - 集計表のセルの値が未設定だった場合は、(0)を返します。
//   未設定の(0)と、明示的に設定された(0)を、区別する方法は有りません。
// - 可変引数は、(0)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、この階層の値を返します。
// - キーには、(-1)以外の任意の整数を指定出来ます。
#ifndef PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_get_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/) {
	int iVal;
	va_list ap;
	va_start(ap, ws);
	iVal = Worksheet_get_a(ws, ap);
	va_end(ap);
	return iVal;
}
/*--------------------------------------------------------------------------*/
int Worksheet_get_a(ST_Worksheet* ws/*ヌル可*/, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	return Worksheet_get_v(ws, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
int Worksheet_get_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/);
int Worksheet_get_a(ST_Worksheet* ws/*ヌル可*/, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_get_l
		.global		Worksheet_get_a
Worksheet_get_l:
		;//[%sp+0] := retp
		;//[%sp+4] := ws
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]		;//%r12 := ws
		xadd		%r13, %sp, 8		;//%r13 := ap = &...
Worksheet_get_a:
		;//%r12    := ws
		;//%r13    := ap
		xjp		Worksheet_get_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_get_v(ST_Worksheet* ws/*ヌル可*/, const int* aKey/*={key,key,...,key,-1}*/) {
	//指定されたノード(有れば)を取得する。
	// - ヌルポインタに対して呼出された場合も、同じ処理で安全である。
	ws = Worksheet_ref_v(ws/*ヌル可*/, aKey);
	//指定されたノードが存在しない場合は、値が未設定と同義と見なして、(0)を返す。
	if(!ws) { return 0; }	//ここまで
	//指定されたノードの値を返す。
	return ws->iVal;
}
/****************************************************************************
 *	合計
 ****************************************************************************/
//集計表の、指定されたノード以下のツリーの値を合計する。
// - 指定されたノードが存在しない場合は、(0)を返します。
//   上記の(0)と、明示的に設定された値の合計が(0)である場合とを、区別する方法は有りません。
// - 可変引数は、(0)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、このノード以下のツリーの値を合計します。
// - キーには、(-1)以外の任意の整数を指定出来ます。
// - 指定されたノード自身の値も、合計に含みます。
//   ノードが値とキーを両方持っている場合は、どちらも合計に含みます。
//　　　　　 １ 　　　　
//　　　　　／＼　　　　例えは、左図のツリーの最上位のノードを指定した場合、
//　　　　２　　◇　　　合計値は(1+2+3+4+5=15)となります。
//　　　／　＼　　＼　　
//　　３　　　４　　５　
#ifndef PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_sum_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/) {
	int iSum;
	va_list ap;
	va_start(ap, ws);
	iSum = Worksheet_sum_a(ws, ap);
	va_end(ap);
	return iSum;
}
/*--------------------------------------------------------------------------*/
int Worksheet_sum_a(ST_Worksheet* ws/*ヌル可*/, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	return Worksheet_sum_v(ws, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
int Worksheet_sum_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/);
int Worksheet_sum_a(ST_Worksheet* ws/*ヌル可*/, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_sum_l
		.global		Worksheet_sum_a
Worksheet_sum_l:
		;//[%sp+0] := retp
		;//[%sp+4] := ws
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]		;//%r12 := ws
		xadd		%r13, %sp, 8		;//%r13 := ap = &...
Worksheet_sum_a:
		;//%r12    := ws
		;//%r13    := ap
		xjp		Worksheet_sum_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_sum_v(ST_Worksheet* ws/*ヌル可*/, const int* aKey/*={key,key,...,key,-1}*/) {
	ST_Worksheet_Iterator it;
	int iKey, iSum;
	//指定されたノード(有れば)を取得する。
	// - ヌルポインタに対して呼出された場合も、同じ処理で安全である。
	ws = Worksheet_ref_v(ws/*ヌル可*/, aKey);
	//指定されたノードの値を取得する。
	// - 指定されたノードが無かった場合も、同じ処理で安全である。
	iSum = Worksheet_get_l(ws/*ヌル可*/, -1);
	//子階層のキーに対して再帰し、値を加算する。
	// - 指定されたノードが無かった場合も、同じ処理で安全である。
	for(iKey = Worksheet_first_l(ws/*ヌル可*/, &it, -1);
	    iKey != -1;
	    iKey = Worksheet_next(&it)) { iSum += Worksheet_sum_l(ws, iKey, -1); }
	return iSum;
}
/****************************************************************************
 *	設定
 ****************************************************************************/
//集計表のセルの値を設定する。
// - 可変引数は、(0)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、この階層の値を設定します。
// - キーには、(-1)以外の任意の整数を指定出来ます。
// - 値には、任意の整数を指定出来ます。
#ifndef PIECE
/*--------------------------------------------------------------------------*/
void Worksheet_set_l(ST_Worksheet* ws, int iVal, ... /*,(-1)*/) {
	va_list ap;
	va_start(ap, iVal);
	Worksheet_set_a(ws, iVal, ap);
	va_end(ap);
}
/*--------------------------------------------------------------------------*/
void Worksheet_set_a(ST_Worksheet* ws, int iVal, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	Worksheet_set_v(ws, iVal, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
void Worksheet_set_l(ST_Worksheet* ws, int iVal, ... /*,(-1)*/);
void Worksheet_set_a(ST_Worksheet* ws, int iVal, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_set_l
		.global		Worksheet_set_a
Worksheet_set_l:
		;//%r12    := ws
		;//[%sp+0] := retp
		;//[%sp+4] := iVal
		;//[%sp+8] := ...
		xld.w		%r13, [%sp+4]		;//%r13 := iVal
		xadd		%r14, %sp, 8		;//%r14 := ap = &...
Worksheet_set_a:
		;//%r12    := ws
		;//%r13    := iVal
		;//%r14    := ap
		xjp		Worksheet_set_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
void Worksheet_set_v(ST_Worksheet* ws, int iVal, const int* aKey/*={key,key,...,key,-1}*/) {
	int iKey;
	ST_Worksheet* pChild;
	//ヌルポインタに対して呼出された場合は、エラー停止する。
	if(!ws) { DIE(); }
	//次のキーを取得する。
	iKey = *aKey++;
	//キーが終端ならば、この階層の値を設定する。
	if(iKey == -1) { ws->iVal = iVal; return; }	//ここまで
	//キーに対応する子階層(有れば)を取得する。
	pChild = Worksheet_ref_l(ws, iKey, -1);
	//キーに対応する子階層が無ければ…
	if(!pChild) {
		//集計表の単一階層を作成する。
		pChild = Worksheet_new();
		//キーに対応する子階層として、追加する。
#ifndef USE_WORKSHEET_GTREE
		ght_insert(ws->tKey, pChild, sizeof(int), &iKey);
#else //USE_WORKSHEET_GTREE
		g_tree_insert(ws->tKey, (void*)iKey, pChild);
#endif//USE_WORKSHEET_GTREE
	}
	//キーに対応する子階層に対して、再帰する。
	Worksheet_set_v(pChild, iVal, aKey);
}
/****************************************************************************
 *	削除
 ****************************************************************************/
//集計表の指定されたノード,及び,それ以下の階層を開放する。
// - 可変引数は、(1)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、エラー停止します。
// - キーには、(-1)以外の任意の整数を指定出来ます。
#ifndef PIECE
/*--------------------------------------------------------------------------*/
void Worksheet_unset_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/) {
	va_list ap;
	va_start(ap, ws);
	Worksheet_unset_a(ws, ap);
	va_end(ap);
}
/*--------------------------------------------------------------------------*/
void Worksheet_unset_a(ST_Worksheet* ws/*ヌル可*/, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	Worksheet_unset_v(ws, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
void Worksheet_unset_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/);
void Worksheet_unset_a(ST_Worksheet* ws/*ヌル可*/, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_unset_l
		.global		Worksheet_unset_a
Worksheet_unset_l:
		;//[%sp+0] := retp
		;//[%sp+4] := ws
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]		;//%r12 := ws
		xadd		%r13, %sp, 8		;//%r13 := ap = &...
Worksheet_unset_a:
		;//%r12    := ws
		;//%r13    := ap
		xjp		Worksheet_unset_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
void Worksheet_unset_v(ST_Worksheet* ws/*ヌル可*/, const int* aKey/*={key,key,...,key,-1}*/) {
	ST_Worksheet* pChild;
	//ヌルポインタに対して呼出された場合は、何もしない。
	if(!ws) { return; }	//ここまで
	//少なくとも(1)個以上のキーが指定されていなければならない。
	if(aKey[0] == -1) { DIE(); }
	//aKey[0]が最後のキーならば…
	if(aKey[1] == -1) {
#ifndef USE_WORKSHEET_GTREE
		//aKey[0]に対応する子階層(有れば)を取り外す。
		pChild = ght_remove(ws->tKey, sizeof(int), &aKey[0]);
		//取り外した子階層(有れば)を開放する。
		// - キーに対応する子階層が無かった場合も、同じ処理で安全である。
		Worksheet_free(pChild/*ヌル可*/);
#else //USE_WORKSHEET_GTREE
		//aKey[0]に対応する子階層が有れば、解放する。
		g_tree_remove(ws->tKey, (void*)aKey[0]);
#endif//USE_WORKSHEET_GTREE
		return;	//ここまで
	}
	//aKey[0]に対応する子階層(有れば)を取得する。
	pChild = Worksheet_ref_l(ws, aKey[0], -1);
	//aKey[0]に対応する子階層(有れば)に対して、再帰する。
	// - aKey[0]に対応する子階層が無かった場合も、同じ処理で安全である。
	Worksheet_unset_v(pChild/*ヌル可*/, &aKey[1]);
}
/****************************************************************************
 *	計数
 ****************************************************************************/
//集計表のセルの値を(1)増加し、(1)増加後の値を返す。
// - 集計表のセルの値が未設定だった場合は、(1)増加後の値は(1)になります。
// - 可変引数は、(0)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、この階層の値を(1)増加します。
// - キーには、(-1)以外の任意の整数を指定出来ます。
#ifndef PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_incr_l(ST_Worksheet* ws, ... /*,(-1)*/) {
	int iVal;
	va_list ap;
	va_start(ap, ws);
	iVal = Worksheet_incr_a(ws, ap);
	va_end(ap);
	return iVal;
}
/*--------------------------------------------------------------------------*/
int Worksheet_incr_a(ST_Worksheet* ws, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	return Worksheet_incr_v(ws, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
int Worksheet_incr_l(ST_Worksheet* ws, ... /*,(-1)*/);
int Worksheet_incr_a(ST_Worksheet* ws, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_incr_l
		.global		Worksheet_incr_a
Worksheet_incr_l:
		;//[%sp+0] := retp
		;//[%sp+4] := ws
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]		;//%r12 := ws
		xadd		%r13, %sp, 8		;//%r13 := ap = &...
Worksheet_incr_a:
		;//%r12    := ws
		;//%r13    := ap
		xjp		Worksheet_incr_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_incr_v(ST_Worksheet* ws, const int* aKey/*={key,key,...,key,-1}*/) {
	//現在の値を取得し(1)増加する。
	int iVal = Worksheet_get_v(ws, aKey) + 1;
	//(1)増加後の値を設定する。
	Worksheet_set_v(ws, iVal, aKey);
	//(1)増加後の値を返す。
	return iVal;
}
/****************************************************************************
 *	走査開始
 ****************************************************************************/
//集計表の子階層の最初のキーを取得します。
// - 子階層が無ければ、(-1)を返します。
// - 可変引数は、(0)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、この階層の子階層の最初のキーを取得します。
// - キーには、(-1)以外の任意の整数を指定出来ます。
#ifndef PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_first_l(ST_Worksheet* ws/*ヌル可*/, ST_Worksheet_Iterator* it, ... /*,(-1)*/) {
	int iKey;
	va_list ap;
	va_start(ap, it);
	iKey = Worksheet_first_a(ws, it, ap);
	va_end(ap);
	return iKey;
}
/*--------------------------------------------------------------------------*/
int Worksheet_first_a(ST_Worksheet* ws/*ヌル可*/, ST_Worksheet_Iterator* it, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	return Worksheet_first_v(ws, it, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
int Worksheet_first_l(ST_Worksheet* ws/*ヌル可*/, ST_Worksheet_Iterator* it, ... /*,(-1)*/);
int Worksheet_first_a(ST_Worksheet* ws/*ヌル可*/, ST_Worksheet_Iterator* it, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_first_l
		.global		Worksheet_first_a
Worksheet_first_l:
		;//%r12    := ws
		;//[%sp+0] := retp
		;//[%sp+4] := it
		;//[%sp+8] := ...
		xld.w		%r13, [%sp+4]		;//%r13 := it
		xadd		%r14, %sp, 8		;//%r14 := ap = &...
Worksheet_first_a:
		;//%r12    := ws
		;//%r13    := it
		;//%r14    := ap
		xjp		Worksheet_first_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
int Worksheet_first_v(ST_Worksheet* ws/*ヌル可*/, ST_Worksheet_Iterator* it, const int* aKey/*={key,key,...,key,-1}*/) {
	int iKey;
	ST_Worksheet* pChild;
	//ヌルポインタに対して呼出された場合は、子階層が無いのと同義と見なして、(-1)を返す。
	if(!ws) { return -1; }	//ここまで
	//次のキーを取得する。
	iKey = *aKey++;
	//キーが終端ならば、この階層の子階層を走査開始する。
	if(iKey == -1) {
#ifndef USE_WORKSHEET_GTREE
		int* pKey;
		//イテレータを初期化すると同時に、子階層の最初のキーを取得する。
		it->p_ht = ws->tKey;
		if(!ght_first(it->p_ht, &it->it, (const void**)&pKey)) { return -1; }	//子階層の最初のキーが無ければ、(-1)を返す。
		//子階層の最初のキーを返す。
		return *pKey;	//ここまで
#else //USE_WORKSHEET_GTREE
		//イテレータを初期化すると同時に、子階層の最初のキーを取得する。
		it->tree = ws->tKey;
		if(!g_tree_first_np(it->tree, &it->it, (void**)&iKey, NULL)) { return -1; }	//子階層の最初のキーが無ければ、(-1)を返す。
		//子階層の最初のキーを返す。
		return iKey;	//ここまで
#endif//USE_WORKSHEET_GTREE
	}
	//キーに対応する子階層(有れば)を取得する。
	pChild = Worksheet_ref_l(ws, iKey, -1);
	//キーに対応する子階層に対して、再帰する。
	return Worksheet_first_v(pChild, it, aKey);
}
/****************************************************************************
 *	走査継続
 ****************************************************************************/
//集計表の子階層の次のキーを取得します。
// - 次のキーが無ければ、(-1)を返します。
int Worksheet_next(ST_Worksheet_Iterator* it) {
#ifndef USE_WORKSHEET_GTREE
	int* pKey;
	//子階層の次のキーを取得する。
	if(!ght_next(it->p_ht, &it->it, (const void**)&pKey)) { return -1; }	//子階層の次のキーが無ければ、(-1)を返す。
	//子階層の次のキーを返す。
	return *pKey;
#else //USE_WORKSHEET_GTREE
	int iKey;
	//子階層の次のキーを取得する。
	if(!g_tree_next_np(it->tree, &it->it, (void**)&iKey, NULL)) { return -1; }	//子階層の次のキーが無ければ、(-1)を返す。
	//子階層の次のキーを返す。
	return iKey;
#endif//USE_WORKSHEET_GTREE
}
/****************************************************************************
 *	ソート
 ****************************************************************************/
//集計表の全階層をソートする。
void Worksheet_sort(ST_Worksheet* ws/*ヌル可*/) {
	ST_Worksheet_Iterator it;
	int iKey;
	//この階層をソートする。
	// - ヌルポインタに対して呼出された場合も、同じ処理で安全である。
	Worksheet_sort_l(ws/*ヌル可*/, -1);
	//子階層を全てソートする。
	// - ヌルポインタに対して呼出された場合も、同じ処理で安全である。
	for(iKey = Worksheet_first_l(ws/*ヌル可*/, &it, -1);
	    iKey != -1;
	    iKey = Worksheet_next(&it)) {
		ST_Worksheet* pChild = Worksheet_ref_l(ws, iKey, -1);
		Worksheet_sort(pChild);
	}
}
/*--------------------------------------------------------------------------*/
//集計表の単一階層をソートする。
// - 可変引数は、(0)個以上のキーの並びと、終端の(-1)です。
//   キーの並びが(0)個だった場合は、この階層の子階層をソートします。
// - キーには、(-1)以外の任意の整数を指定出来ます。
#ifndef PIECE
/*--------------------------------------------------------------------------*/
void Worksheet_sort_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/) {
	va_list ap;
	va_start(ap, ws);
	Worksheet_sort_a(ws, ap);
	va_end(ap);
}
/*--------------------------------------------------------------------------*/
void Worksheet_sort_a(ST_Worksheet* ws/*ヌル可*/, va_list ap) {
	int aKey[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((aKey[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(aKey)) { DIE(); }	//ここで停止した場合は、aKey[]不足か,又は,呼出し側が終端を忘れている。たぶん後者だろう。
	}
	Worksheet_sort_v(ws, aKey);
}
/*--------------------------------------------------------------------------*/
#else //PIECE
void Worksheet_sort_l(ST_Worksheet* ws/*ヌル可*/, ... /*,(-1)*/);
void Worksheet_sort_a(ST_Worksheet* ws/*ヌル可*/, va_list ap);
asm("
		.code
		.align		1
		.global		Worksheet_sort_l
		.global		Worksheet_sort_a
Worksheet_sort_l:
		;//[%sp+0] := retp
		;//[%sp+4] := ws
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]		;//%r12 := ws
		xadd		%r13, %sp, 8		;//%r13 := ap = &...
Worksheet_sort_a:
		;//%r12    := ws
		;//%r13    := ap
		xjp		Worksheet_sort_v
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
#ifndef USE_WORKSHEET_GTREE
static int Worksheet_sort_Compare(LIST_ENTRY* _x, LIST_ENTRY* _y) {
	//当関数の処理は、ght_hash_table.h/cモジュールの動作に依存しています。
	//外部からght_hash_table_t.le_iteratorの順番を並び替えても、動作上問題有りません。
	ght_hash_entry_t* x = CONTAINING_RECORD(_x, ght_hash_entry_t, le_iterator);
	ght_hash_entry_t* y = CONTAINING_RECORD(_y, ght_hash_entry_t, le_iterator);
	if((x->i_key_size != sizeof(int)) || (y->i_key_size != sizeof(int))) { DIE(); }
	if(*(int*)x->key_data < *(int*)y->key_data) { return -1; }
	if(*(int*)x->key_data > *(int*)y->key_data) { return  1; }
	DIE();	//同一階層に同じ値のキーが存在する事は無いはず。ここで停止したらバグです。
}
#endif//USE_WORKSHEET_GTREE
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
void Worksheet_sort_v(ST_Worksheet* ws/*ヌル可*/, const int* aKey/*={key,key,...,key,-1}*/) {
	ws = Worksheet_ref_v(ws/*ヌル可*/, aKey);
	if(!ws) { return; }	//ここまで
#ifndef USE_WORKSHEET_GTREE
	//ワークシートの内部の、ハッシュテーブルの内部の、ダブルリンクリストをソートする。
	SortList(&ws->tKey->le_iterator, Worksheet_sort_Compare);
#endif//USE_WORKSHEET_GTREE
}
/****************************************************************************
 *	ダンプ
 ****************************************************************************/
static void Worksheet_dump_indent(int indent) {
	while(indent--) { putchar('\t'); }
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void Worksheet_dump_iter(ST_Worksheet* ws/*ヌル可*/, int indent) {
	ST_Worksheet_Iterator it;
	int iKey;
	//子階層を走査する。
	// - ヌルポインタに対して呼出された場合も、同じ処理で安全である。
	for(iKey = Worksheet_first_l(ws/*ヌル可*/, &it, -1);
	    iKey != -1;
	    iKey = Worksheet_next(&it)) {
		ST_Worksheet* pChild = Worksheet_ref_l(ws, iKey, -1);
		//子階層の値が設定されていたら、値を出力する。
		if(pChild->iVal) {
			Worksheet_dump_indent(indent);
			printf("%d\t=\t%d\n", iKey, pChild->iVal);
		}
		//子階層のキーが空でなければ、キーを出力する。
#ifndef USE_WORKSHEET_GTREE
		if(ght_size(pChild->tKey)) {
#else //USE_WORKSHEET_GTREE
		if(g_tree_nnodes(pChild->tKey)) {
#endif//USE_WORKSHEET_GTREE
			Worksheet_dump_indent(indent);
			printf("%d\n", iKey);
			Worksheet_dump_iter(pChild, indent + 1);
			Worksheet_dump_indent(indent);
			printf("end\n");
		}
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//集計表の全階層を、レジストリトクリプト形式でダンプします。
// - レジストリスクリプトの制限は考慮しません。
//   具体的には、レジストリスクリプトのキーと値の範囲は(0)〜(65535)であるのに対し、
//   当モジュールのキーの範囲は(-1)を除く任意の整数,値の範囲は(-1)も含む任意の整数です。
//   レジストリスクリプトの制限から外れるこれらの値も、そのまま出力します。
//   その場合、レジストリスクリプトをコンパイルした時点でエラーとなります。
// - ルート階層に直接設定した値は出力しません。
//   レジストリスクリプトの構造上、'ルート階層の値'は表現出来ないからです。
void Worksheet_dump(ST_Worksheet* ws/*ヌル可*/) {
	Worksheet_sort(ws);	//キーの昇順にダンプする事にした。必須ではない。
	Worksheet_dump_iter(ws, 0);
}
/****************************************************************************
 *	使用例
 ****************************************************************************/
//※TODO:
