//
//	clipbary.cs
//
//	汎用ビット配列
//
//	CLiP - Common Library for P/ECE
//	Copyright (C) 2017 Naoyuki Sawa
//
//	* Mon Mar 06 23:24:18 JST 2017 Naoyuki Sawa
//	- 1st リリース。
//
using System;
namespace org.piece_me {
	public static partial class libclip {
		//*****************************************************************************
		//	
		//*****************************************************************************
		public static VoidPtr bitarray_alloc(int bit_len) {
			return new byte[(((bit_len) + 31) >> 5) * 4];	//指定されたbit数を、32bit単位に切り上げて、int配列として確保します。
		}
		//-----------------------------------------------------------------------------
		public static int bitarray_lsb1st_get(VoidPtr ptr, int pos, int bits) {	//LSB先行バージョン
			//
			//	                                              s2               s1
			//	                                              /                /
			//	                                        +----------+----------------------+
			//	MSB|........ .......x xxxxxxxx xxxxxxxx|xxxxxxxx xxx..... ........ ........|LSB
			//	                    +------------------------------+                      |
			//	                                   /               |                      |
			//	                                 bits             pos                     p
			//	             mask = 1_11111111_11111111_11111111_111
			//
			int value;
			int s1;
			int s2;
			Int32Ptr p;
			//32bit超過の操作は対応していません。
			if((uint)bits > 32) { throw new ApplicationException(); }
			//bits=0ならば、0を返します。
			if(bits == 0) { return 0; }
			//32bit単位のポインタを求めます。
			p = (Int32Ptr)ptr + (pos >> 5);
			//p[0]の位置のint値に含まれる、不要bit数を求めます。
			s1 = pos & 31;	//0～31
			//p[0]の位置のint値に含まれる、必要bit数を求めます。
			s2 = 32 - s1;	//1～32
			//p[0]の位置のint値から、必要bitを取得します。
			value = (int)((uint)p[0] >> s1);
			//p[1]の位置のint値に、必要bitがまたがっていたら…
			if(bits > s2) {
				//p[1]の位置のint値から、必要bitを取得します。
				value |= (int)((uint)p[1] << s2);
			}
			//不要bitをマスクします。(必要bitの符号拡張は行わない)
			value &= (int)(uint.MaxValue >> (32 - bits));
			return value;
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static int bitarray_msb1st_get(VoidPtr ptr, int pos, int bits) {	//MSB先行バージョン
			//
			//	                s1               s2
			//	                /                /
			//	    +----------------------+----------+
			//	MSB|........ ........ .....xxx xxxxxxxx|xxxxxxxx xxxxxxxx x....... ........|LSB
			//	    |                      +------------------------------+               |
			//	    |                      |              /                               |
			//	    p                     pos           bits                              p
			//	                    mask = 111_11111111_11111111_11111111_1
			//
			int value;
			int s1;
			int s2;
			Int32Ptr p;
			//32bit超過の操作は対応していません。
			if((uint)bits > 32) { throw new ApplicationException(); }
			//bits=0ならば、0を返します。
			if(bits == 0) { return 0; }
			//32bit単位のポインタを求めます。
			p = (Int32Ptr)ptr + (pos >> 5);
			//p[0]の位置のint値に含まれる、不要bit数を求めます。
			s1 = pos & 31;	//0～31
			//p[0]の位置のint値に含まれる、必要bit数を求めます。
			s2 = 32 - s1;	//1～32
			//p[0]の位置のint値から、必要bitを取得します。
			value = (int)((uint)p[0] << s1);
			//p[1]の位置のint値に、必要bitがまたがっていたら…
			if(bits > s2) {
				//p[1]の位置のint値から、必要bitを取得します。
				value |= (int)((uint)p[1] >> s2);
			}
			//不要bitをマスクします。(必要bitの符号拡張は行わない)
			value = (int)((uint)value >> (32 - bits));
			return value;
		}
		//-----------------------------------------------------------------------------
		public static void bitarray_lsb1st_set(VoidPtr ptr, int pos, int bits, int value) {	//LSB先行バージョン
			//
			//	                                              s2               s1
			//	                                              /                /
			//	                                        +----------+----------------------+
			//	MSB|........ .......x xxxxxxxx xxxxxxxx|xxxxxxxx xxx..... ........ ........|LSB
			//	                    +------------------------------+                      |
			//	                                   /               |                      |
			//	                                 bits             pos                     p
			//	             mask = 1_11111111_11111111_11111111_111
			//
			int s1;
			int s2;
			int mask;
			Int32Ptr p;
			//32bit超過の操作は対応していません。
			if((uint)bits > 32) { throw new ApplicationException(); }
			//bits=0ならば、何もせずに帰ります。
			if(bits == 0) { return; }
			//32bit単位のポインタを求めます。
			p = (Int32Ptr)ptr + (pos >> 5);
			//p[0]の位置のint値に含まれる、不要bit数を求めます。
			s1 = pos & 31;	//0～31
			//p[0]の位置のint値に含まれる、必要bit数を求めます。
			s2 = 32 - s1;	//1～32
			//格納する値の、不要bitをマスクします。
			mask  = (int)(uint.MaxValue >> (32 - bits));
			value &= mask;
			//p[0]の位置のint値に、必要bitを合成します。
			p[0] &= (int)(~((uint)mask  << s1));
			p[0] |= (int)(  (uint)value << s1 );
			//p[1]の位置のint値に、必要bitがまたがっていたら…
			if(bits > s2) {
				//p[1]の位置のint値に、必要bitを合成します。
				p[1] &= (int)(~((uint)mask  >> s2));
				p[1] |= (int)(  (uint)value >> s2 );
			}
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static void bitarray_msb1st_set(VoidPtr ptr, int pos, int bits, int value) {	//MSB先行バージョン
			//
			//	                s1               s2
			//	                /                /
			//	    +----------------------+----------+
			//	MSB|........ ........ .....xxx xxxxxxxx|xxxxxxxx xxxxxxxx x....... ........|LSB
			//	    |                      +------------------------------+               |
			//	    |                      |              /                               |
			//	    p                     pos           bits                              p
			//	                    mask = 111_11111111_11111111_11111111_1
			//
			int s1;
			int s2;
			int mask;
			Int32Ptr p;
			//32bit超過の操作は対応していません。
			if((uint)bits > 32) { throw new ApplicationException(); }
			//bits=0ならば、何もせずに帰ります。
			if(bits == 0) { return; }
			//32bit単位のポインタを求めます。
			p = (Int32Ptr)ptr + (pos >> 5);
			//p[0]の位置のint値に含まれる、不要bit数を求めます。
			s1 = pos & 31;	//0～31
			//p[0]の位置のint値に含まれる、必要bit数を求めます。
			s2 = 32 - s1;	//1～32
			//格納する値の、不要bitをマスクします。
			mask  = (int)(uint.MaxValue << (32 - bits));
			value = (int)((uint)value   << (32 - bits));
			//p[0]の位置のint値に、必要bitを合成します。
			p[0] &= (int)(~((uint)mask  >> s1));
			p[0] |= (int)(  (uint)value >> s1 );
			//p[1]の位置のint値に、必要bitがまたがっていたら…
			if(bits > s2) {
				//p[1]の位置のint値に、必要bitを合成します。
				p[1] &= (int)(~((uint)mask  << s2));
				p[1] |= (int)(  (uint)value << s2 );
			}
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static bool bitarray_test(Int32Ptr ptr, int pos, int bits, Func<VoidPtr,int,int,int> fnGet) {
			int m, n, v;
			ptr += pos >> 5;				//検査するビット範囲の先頭を含む、先頭ワードのアドレス。
			pos &= 31;					//検査するビット範囲の先頭の、先頭ワード内でのビット位置(0～31)。
			if(pos != 0) {					//上記ビット位置が0でなければ…
				m = 32 - pos;				//上記ビット位置から、先頭ワードの末尾までのビット数。
				n = bits;				//検査する総ビット数。
				if(m < n) { n = m; }			//先頭ワードから取得するビット数。
				v = fnGet.Invoke(ptr, pos, n);		//先頭ワードからビットを取得する。
				if(v != 0) { return true; }		//1のビットが有れば、0以外の値を返す。
				bits -= n;				//取得したビット数を、検査する総ビット数から差し引く。
				ptr++;					//次のワードへ進める。
			}
			while((bits -= 32) >= 0) {			//検査する総ビット数が32ビット以上残っていれば…
				v = ptr.Read();				//現在のワードを取得して、次のワードへ進める。
				if(v != 0) { return true; }		//1のビットが有れば、0以外の値を返す。
			}
			bits += 32;					//上記ループを抜ける時に一回引き過ぎているので戻す。
			v = fnGet.Invoke(ptr, 0, bits);			//検査する総ビット数が残っていれば、取得する。
			return v != 0;					//1のビットが有れば、0以外の値を返す。
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static bool bitarray_lsb1st_test(VoidPtr ptr, int pos, int bits) {	//LSB先行バージョン
			return bitarray_test(ptr, pos, bits, bitarray_lsb1st_get);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static bool bitarray_msb1st_test(VoidPtr ptr, int pos, int bits) {	//MSB先行バージョン
			return bitarray_test(ptr, pos, bits, bitarray_msb1st_get);
		}
		//*****************************************************************************
		//	
		//*****************************************************************************
		private static void bitarray_copy_subr(VoidPtr dst_ptr, int dst_pos, VoidPtr src_ptr, int src_pos, int bits, Func<VoidPtr,int,int,int> get, Action<VoidPtr,int,int,int> set) {
			int n, value;
			if(bits < 0) { throw new ApplicationException(); }
			while(bits != 0) {					//コピーするビット数が残っていたら…
				n = (bits < 32) ? bits : 32;			//今回コピーするビット数を決定する。
				value = get.Invoke(src_ptr, src_pos, n);	//コピー元ビット配列から値を取得する。
				        set.Invoke(dst_ptr, dst_pos, n, value);	//コピー先ビット配列に値を格納する。
				src_pos += n;					//コピー元のビット位置を進める。
				dst_pos += n;					//コピー先のビット位置を進める。
				bits    -= n;					//コピーするビット数を減らす。
			}
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static void bitarray_lsb1st_copy(VoidPtr dst_ptr, int dst_pos, VoidPtr src_ptr, int src_pos, int bits) {	//LSB先行バージョン
			bitarray_copy_subr(dst_ptr, dst_pos, src_ptr, src_pos, bits, bitarray_lsb1st_get, bitarray_lsb1st_set);
		}
		//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
		public static void bitarray_msb1st_copy(VoidPtr dst_ptr, int dst_pos, VoidPtr src_ptr, int src_pos, int bits) {	//MSB先行バージョン
			bitarray_copy_subr(dst_ptr, dst_pos, src_ptr, src_pos, bits, bitarray_msb1st_get, bitarray_msb1st_set);
		}
	}
}
