using System; using System.Collections.Generic; using System.IO; namespace miew.BitArray { using String = System.String; public class BitArr : IEquatable<BitArr> { const int IgnoreHintThreshold = 15; const int bits_per_UInt64 = sizeof(UInt64) * 8; const int bits_per_byte = sizeof(byte) * 8; const int idx_mask = 0x0000003F; const int idx_shift = 6; // for hash code const int NotCalculated = -1; readonly UInt64[] m_arr; readonly int m_c_bits; readonly int m_c_qw; readonly int m_c_dist; int hash_code = NotCalculated; public BitArr(int c_bits) { m_c_bits = c_bits; m_c_qw = (c_bits / bits_per_UInt64) + 1; m_c_dist = (m_c_qw / bits_per_UInt64) + 1; m_arr = new UInt64[m_c_qw + m_c_dist]; } public BitArr(BitArr arg) : this(arg.m_c_bits) { this.CopyFrom(arg); } public void CopyFrom(BitArr arg) { #if DEBUG if (m_c_bits != arg.m_c_bits) throw new ArgumentException(); #endif arg.m_arr.CopyTo(this.m_arr, 0); this.hash_code = arg.hash_code; } public BitArr(int c_bits, BinaryReader br) : this(c_bits) { for (int i = 0; i < m_c_qw + m_c_dist; i++) m_arr[i] = br.ReadUInt64(); } public void Write(BinaryWriter bw) { for (int i = 0; i < m_c_qw + m_c_dist; i++) bw.Write(m_arr[i]); } public int Count { get { return m_c_bits; } } public bool this[int idx] { get { if (idx >= m_c_bits) throw new IndexOutOfRangeException(); int b_i = idx / bits_per_UInt64; idx -= b_i * bits_per_UInt64; return (m_arr[b_i] & ((UInt64)1 << idx)) > 0; } set { if (idx >= m_c_bits) throw new IndexOutOfRangeException(); int b_i = idx >> idx_shift; if (value) { if ((m_arr[b_i] |= ((UInt64)1 << (idx & idx_mask))) > 0) { int z = m_c_qw + (b_i >> idx_shift); m_arr[z] |= ((UInt64)1 << (b_i & idx_mask)); } } else { if ((m_arr[b_i] &= ~((UInt64)1 << (idx & idx_mask))) == 0) { int z = m_c_qw + (b_i >> idx_shift); m_arr[z] &= ~((UInt64)1 << (b_i & idx_mask)); } } hash_code = NotCalculated; //Check(); } } public void SetAll() { int i = 0; /// set all bits while (i < m_c_qw - 1) m_arr[i++] = 0xFFFFFFFFFFFFFFFF; m_arr[i++] = (0xFFFFFFFFFFFFFFFF >> (bits_per_UInt64 - (m_c_bits & idx_mask))); /// set all distilled bits while (i < m_c_qw + m_c_dist - 1) m_arr[i++] = 0xFFFFFFFFFFFFFFFF; m_arr[i] = (0xFFFFFFFFFFFFFFFF >> (bits_per_UInt64 - (m_c_qw & idx_mask))); hash_code = NotCalculated; } public static BitArr operator ~(BitArr arg) { #if CHECK_CBITS if (arg1.m_c_bits != arg2.m_c_bits) throw new ArgumentException(); #endif BitArr ret = new BitArr(arg.m_c_bits); int c_qw = ret.m_c_qw; UInt64 m = 1; for (int i = 0; i < c_qw; i++) { if ((ret.m_arr[i] = ~arg.m_arr[i]) != 0) { int z = c_qw + (i >> idx_shift); ret.m_arr[z] |= m; } if ((m <<= 1) == 0) m = 1; } //ret.Check(); return ret; } public static BitArr operator &(BitArr arg1, BitArr arg2) { #if CHECK_CBITS if (arg1.m_c_bits != arg2.m_c_bits) throw new ArgumentException(); #endif BitArr ret = new BitArr(arg1.m_c_bits); int c_qw = ret.m_c_qw; UInt64 d = 0; UInt64 di = 1; for (int i = 0; i < c_qw; i++) { if ((ret.m_arr[i] = arg1.m_arr[i] & arg2.m_arr[i]) > 0) d |= di; if ((di <<= 1) == 0) { ret.m_arr[c_qw + (i >> idx_shift)] = d; d = 0; di = 1; } } ret.m_arr[c_qw + ((c_qw - 1) >> idx_shift)] = d; //ret.Check(); return ret; } public BitArr AndWithHash(BitArr arg) { #if CHECK_CBITS if (this.m_c_bits != arg2.m_c_bits) throw new ArgumentException(); #endif BitArr ret = new BitArr(m_c_bits); int c_qw = ret.m_c_qw; UInt64 di = 1; UInt64 v, d, h = d = v = 0; for (int i = 0; i < c_qw; i++) { if ((v = ret.m_arr[i] = m_arr[i] & arg.m_arr[i]) > 0) { d |= di; h += v; } if ((di <<= 1) == 0) { ret.m_arr[c_qw + (i >> idx_shift)] = d; d = 0; di = 1; } } ret.m_arr[c_qw + ((c_qw - 1) >> idx_shift)] = d; if ((ret.hash_code = (int)h ^ (int)(h >> 32)) == NotCalculated) // avoid special hash ret.hash_code = 0; //ret.Check(); return ret; } public bool AndGivesExact(BitArr arg) { int i_stop = m_c_qw + m_c_dist; for (int i = m_c_qw; i < i_stop; i++) { UInt64 tx; if (((tx = m_arr[i]) & arg.m_arr[i]) != tx) return false; else if (tx > 0) { int n = 0; UInt64 t = tx; int jx = (i - m_c_qw) * bits_per_UInt64; do { UInt64 lowest_bit = t & (UInt64)(-(Int64)t); int j = jx + lu64[(int)((lowest_bit * 0x07EDD5E59A4E28C2) >> 58)]; /// if more than a certain number of hint bits is set then we're wasting time finding /// their logarithms, so just switch over to checking all of the actual bits and ignore /// the current hint quad if (++n > IgnoreHintThreshold) { for (; j < m_c_qw; j++) if (((tx = m_arr[j]) & arg.m_arr[j]) != tx) return false; return true; } if (((tx = m_arr[j]) & arg.m_arr[j]) != tx) return false; t &= ~lowest_bit; } while (t > 0); } } return true; } public void OrEq(BitArr arg) { #if CHECK_CBITS if (m_c_bits != arg.m_c_bits) throw new ArgumentException(); #endif UInt64 di = 1; UInt64 v, h = v = 0; for (int i = 0; i < m_c_qw; i++) { if ((v = (m_arr[i] |= arg.m_arr[i])) > 0) { int z = m_c_qw + (i >> idx_shift); m_arr[z] |= di; h += v; } if ((di <<= 1) == 0) di = 1; } if ((hash_code = (int)h ^ (int)(h >> 32)) == NotCalculated) // avoid special hash hash_code = 0; //Check(); } public bool AndEq(BitArr arg) { #if CHECK_CBITS if (m_c_bits != arg.m_c_bits) throw new ArgumentException(); #endif bool f_any = false; UInt64 di = 1; for (int i = 0; i < m_c_qw; i++) { if ((m_arr[i] &= arg.m_arr[i]) > 0) f_any = true; else { int z = m_c_qw + (i >> idx_shift); m_arr[z] &= ~di; } if ((di <<= 1) == 0) di = 1; } hash_code = NotCalculated; //Check(); return f_any; } #if NEED_DISTILL public static bool AndToDest(BitArr dest, BitArr arg1, BitArr arg2) { if (dest.m_c_bits != arg1.m_c_bits || dest.m_c_bits != arg2.m_c_bits) throw new ArgumentException(); bool f_any = false; for (int i = 0; i < dest.m_c_qw; i++) if ((dest.m_arr[i] = arg1.m_arr[i] & arg2.m_arr[i]) > 0) f_any = true; dest.f_hash_valid = false; return f_any; } #endif public bool FastTest(BitArr arg) { int i_stop = m_c_qw + m_c_dist; for (int i = m_c_qw; i < i_stop; i++) { /// any zero in the 'and' of the two any-ones hints positively rules out a /// quad that doesn't need to be checked. we start by extracting only the 1-bits, /// counting how many we do. If it looks like we might end up extracting almost /// every bit, we switch to just checking all of the target quads. UInt64 t = (m_arr[i] & arg.m_arr[i]); if (t > 0) { int n = 0; int jx = (i - m_c_qw) * bits_per_UInt64; do { UInt64 lowest_bit = t & (UInt64)(-(Int64)t); int j = jx + lu64[(int)((lowest_bit * 0x07EDD5E59A4E28C2) >> 58)]; /// if more than a certain number of hint bits is set then we're wasting time finding /// their logarithms, so just switch over to checking all of the actual bits and ignore /// the current hint quad if (++n > IgnoreHintThreshold) { for (; j < m_c_qw; j++) if ((m_arr[j] & arg.m_arr[j]) > 0) return true; return false; } if ((m_arr[j] & arg.m_arr[j]) > 0) return true; t &= ~lowest_bit; } while (t > 0); } } return false; } public bool FastTestNotArg(BitArr arg) { //if (m_c_bits != arg.m_c_bits) //throw new ArgumentException(); for (int i = 0; i < m_c_qw; i++) if ((m_arr[i] & ~arg.m_arr[i]) > 0) return true; return false; } public IEnumerable<int> OnesPositions() { UInt64 ui64; for (int i = 0; i < m_c_qw; i++) if ((ui64 = m_arr[i]) > 0) { do { UInt64 lowest_bit = ui64 & (UInt64)(-(Int64)ui64); yield return (i * bits_per_UInt64) + lu64[(int)((lowest_bit * 0x07EDD5E59A4E28C2) >> 58)]; ui64 &= ~lowest_bit; } while (ui64 > 0); } } unsafe public int OnesCount() { int count = 0; int c_bytes = (m_c_bits / bits_per_byte) + 1; fixed (UInt64* pui64 = &m_arr[0]) { byte* pb = (byte*)pui64; for (int i = 0; i < c_bytes; i++) count += BitsSetTable256[*pb++]; } return count; } public void Check() { UInt64 di = 1; for (int i = 0; i < m_c_qw; i++) { if (m_arr[i] > 0) { if ((m_arr[m_c_qw + (i >> idx_shift)] & di) == 0) System.Diagnostics.Debugger.Break(); } else { if ((m_arr[m_c_qw + (i >> idx_shift)] & di) > 0) System.Diagnostics.Debugger.Break(); } if ((di <<= 1) == 0) di = 1; } } #if false public static String ConstrainedDisplay(this BitArr ba) { return ba.Count > 50 ? ba.ToHex() : ba.ToString(); } public static BitArr AndAll(IEnumerable<BitArr> ieba) { BitArr ba = null; return ba; } #endif public override string ToString() { Char[] wc_arr = new Char[m_c_bits]; for (int i = 0; i < m_c_bits; i++) wc_arr[i] = this[m_c_bits - i - 1] ? '1' : '0'; return new String(wc_arr); } public String ToHex(Char sep_char = default(Char)) { int c = m_c_bits / bits_per_UInt64; int cch = (m_c_bits / 8) + 1; if (sep_char != default(Char)) cch += c; int end = cch; Char[] wc_arr = new Char[cch]; String s; int i; for (i = 0; i < c; i++) { if (sep_char != default(Char)) { s = m_arr[i].ToString("X"); cch -= s.Length; } else { s = m_arr[i].ToString("X16"); cch -= 8; } s.ToCharArray().CopyTo(wc_arr, cch); if (sep_char != default(Char)) wc_arr[--cch] = sep_char; } if (sep_char != default(Char)) s = m_arr[i].ToString("X"); else s = m_arr[i].ToString("X" + cch.ToString()); cch -= s.Length; s.ToCharArray().CopyTo(wc_arr, cch); return new String(wc_arr, cch, end - cch); } public override bool Equals(object obj) { throw new NotImplementedException(); } public bool Equals(BitArr arg) { #if DEBUG if (m_c_bits != arg.m_c_bits) throw new ArgumentException(); #endif if (hash_code != arg.hash_code && hash_code != NotCalculated && arg.hash_code != NotCalculated) return false; #if true || unsafe_is_slower for (int i = 0; i < m_c_qw; i++) if (m_arr[i] != arg.m_arr[i]) return false; #else fixed (UInt64* _p0 = &m_arr[0], _p1 = &arg.m_arr[0]) { UInt64* p0 = _p0; UInt64* p1 = _p1; for (int i = 0; i < m_c_qw; i++) if (*p0++ != *p1++) return false; } #endif #if NEVER_NEEDED if (f_hash_valid != arg.f_hash_valid) { if (f_hash_valid) arg.hash_code = this.hash_code; else this.hash_code = arg.hash_code; } #endif return true; } unsafe public override int GetHashCode() { if (hash_code == NotCalculated) { UInt64 h = 0; fixed (UInt64* pui64 = &m_arr[0]) { UInt64* pi = pui64, pi_end = pi + m_c_qw; while (pi < pi_end) h += *pi++; } if ((hash_code = (int)h ^ (int)(h >> 32)) == NotCalculated) // avoid special hash hash_code = 0; } return hash_code; } static byte[] BitsSetTable256 = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; const String lu64 = "\u003F\u0000\u003A\u0001\u003B\u002F\u0035\u0002\u003C\u0027\u0030\u001B\u0036\u0021\u002A\u0003\u003D\u0033\u0025" + "\u0028\u0031\u0012\u001C\u0014\u0037\u001E\u0022\u000B\u002B\u000E\u0016\u0004\u003E\u0039\u002E\u0034\u0026\u001A" + "\u0020\u0029\u0032\u0024\u0011\u0013\u001D\u000A\u000D\u0015\u0038\u002D\u0019\u001F\u0023\u0010\u0009\u000C\u002C" + "\u0018\u000F\u0008\u0017\u0007\u0006\u0005"; static int LowestOne(UInt64 ui64) { ui64 &= (UInt64)(-(Int64)ui64); return lu64[(int)((ui64 * 0x07EDD5E59A4E28C2) >> 58)]; } static int HighestOne(UInt64 ui64) { int o = 0; if (ui64 != 0) { if ((ui64 & 0xFFFFFFFF00000000) > 0) { ui64 >>= 32; o += 32; } if ((ui64 & 0xFFFF0000) > 0) { ui64 >>= 16; o += 16; } if ((ui64 & 0xFF00) > 0) { ui64 >>= 8; o += 8; } if ((ui64 & 0xF0) > 0) { ui64 >>= 4; o += 4; } if ((ui64 & 0xC) > 0) { ui64 >>= 2; o += 2; } if ((ui64 & 0x2) > 0) o++; } return o; } int HighestOne() { int c = m_c_bits / bits_per_UInt64; int i; UInt64 dw; for (i = c; i >= 0; i--) if ((dw = m_arr[i]) > 0) { int o = 0; if ((dw & 0xFFFFFFFF00000000) > 0) { dw >>= 32; o += 32; } if ((dw & 0xFFFF0000) > 0) { dw >>= 16; o += 16; } if ((dw & 0xFF00) > 0) { dw >>= 8; o += 8; } if ((dw & 0xF0) > 0) { dw >>= 4; o += 4; } if ((dw & 0xC) > 0) { dw >>= 2; o += 2; } if ((dw & 0x2) > 0) o++; return i * bits_per_UInt64 + o; } return -1; } int LowestOne() { int i; UInt64 dw; for (i = 0; i < m_c_qw; i++) if ((dw = m_arr[i]) > 0) return i * bits_per_UInt64 + LowestOne(dw); return -1; } public IEnumerable<int> EnumerateOnesBits() { for (int b = 0, ix = 0; ix < m_c_qw; ix++, b += 64) { UInt64 v = m_arr[ix]; while (v != 0) { UInt64 z = v & (UInt64)(-(Int64)v); int c = ((z & 0xAAAAAAAAAAAAAAAA) > 0) ? 1 : 0; if ((z & 0xCCCCCCCCCCCCCCCC) > 0) c |= 0x2; if ((z & 0xF0F0F0F0F0F0F0F0) > 0) c |= 0x4; if ((z & 0xFF00FF00FF00FF00) > 0) c |= 0x8; if ((z & 0xFFFF0000FFFF0000) > 0) c |= 0x10; if ((z & 0xFFFFFFFF00000000) > 0) c |= 0x20; yield return b + c; v &= ~z; } } } // not used by this module: public static int HighestOne(UInt32 ui32) { int o = 0; if (ui32 != 0) { if ((ui32 & 0xFFFF0000) > 0) { ui32 >>= 16; o += 16; } if ((ui32 & 0xFF00) > 0) { ui32 >>= 8; o += 8; } if ((ui32 & 0xF0) > 0) { ui32 >>= 4; o += 4; } if ((ui32 & 0xC) > 0) { ui32 >>= 2; o += 2; } if ((ui32 & 0x2) > 0) o++; } return o; } public static int HighestOne(Int32 i32) { int o = 0; if (i32 != 0) { if ((i32 & 0xFFFF0000) > 0) { i32 >>= 16; o += 16; } if ((i32 & 0xFF00) > 0) { i32 >>= 8; o += 8; } if ((i32 & 0xF0) > 0) { i32 >>= 4; o += 4; } if ((i32 & 0xC) > 0) { i32 >>= 2; o += 2; } if ((i32 & 0x2) > 0) o++; } return o; } }; public static class Extensions { static readonly byte[] _bits_set_256 = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static int OnesCount(this uint v) { int c = _bits_set_256[v & 0xFF]; v >>= 8; c += _bits_set_256[v & 0xFF]; v >>= 8; c += _bits_set_256[v & 0xFF]; v >>= 8; return c + _bits_set_256[v & 0xFF]; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// ONLY WORKS WHEN THERE IS EXACTLY ONE BIT SET /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static int OnlyBitPosition(this uint z) { int c = ((z & 0xAAAAAAAA) > 0) ? 1 : 0; if ((z & 0xCCCCCCCC) > 0) c |= 0x2; if ((z & 0xF0F0F0F0) > 0) c |= 0x4; if ((z & 0xFF00FF00) > 0) c |= 0x8; if ((z & 0xFFFF0000) > 0) c |= 0x10; return c; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static int LowestOne(this short z) { z = (short)(z & -z); int c = ((z & 0xAAAA) > 0) ? 1 : 0; if ((z & 0xCCCC) > 0) c |= 0x2; if ((z & 0xF0F0) > 0) c |= 0x4; if ((z & 0xFF00) > 0) c |= 0x8; return c; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static bool IsOdd(this int i) { return (i & 1) == 1; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// /// </summary> /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static bool IsEven(this int i) { return (i & 1) == 0; } } }