using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using StringBuilder = System.Text.StringBuilder; using CultureInfo = System.Globalization.CultureInfo; using DotNetTransformer.Extensions; using DotNetTransformer.Math.Group; namespace DotNetTransformer.Math.Permutation { using P = PermutationInt64; [Serializable] [DebuggerDisplay("{ToString()}, CycleLength = {CycleLength}")] public struct PermutationInt64 : IPermutation

{ [DebuggerBrowsable(DebuggerBrowsableState.Never)] internal readonly long _value; internal PermutationInt64(long value) { _value = value; } public PermutationInt64(params byte[] array) : this((IEnumerable)array) { } public PermutationInt64(IEnumerable collection) { if(ReferenceEquals(collection, null)) throw new ArgumentNullException(); IEnumerator e = collection.GetEnumerator(); byte count = 0, digit; _value = 0L; short digitFlag = 0; while(e.MoveNext()) { if(count >= _count) _throwArray(string.Format( "Collection size ({2}) is out of range ({0}, {1}).", 0, _count + 1, count )); digit = e.Current; if(digit >= _count) _throwArray(string.Format( "Value \"{2}\" is out of range ({0}, {1}).", 0, _count, digit )); if((1 << digit & digitFlag) != 0) _throwArray(string.Format( "Value \"{0}\" is duplicated.", digit )); digitFlag |= (short)(1 << digit); _value |= (long)(digit ^ count) << (count << _s); ++count; } digit = 0; while(((short)(1 << digit) & digitFlag) != 0) ++digit; if(((short)(-1 << digit) & digitFlag) != 0) _throwArray(string.Format( "Value \"{0}\" is not found.", digit )); } private const long _mix = -0x123456789ABCDF0L, _mask = 0xFL; private const byte _s = 2, _count = 16, _len = _count << _s; private const string _charPattern = "[0-9A-Fa-f]"; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public long Value { get { return _value ^ _mix; } } public int this[int index] { get { return (int)(Value >> (index << _s) & _mask); } } public int CycleLength { get { short multFlag = 0; long t = Value; short digitFlag = 0; for(byte i = 0; i < _count; ++i) { if((1 << i & digitFlag) != 0) continue; byte digit = i; byte cLen = 0; do { ++cLen; digitFlag |= (short)(1 << digit); digit = (byte)(t >> (digit << _s) & _mask); } while((1 << digit & digitFlag) == 0); multFlag |= (short)(1 << --cLen); } if(multFlag == 1) return 1; if((multFlag & -0x2000) != 0) return (multFlag >> 14 & 3) + 14; int r = 1; if((multFlag & 0x0AAA) != 0) r *= 2; if((multFlag & 0x0924) != 0) r *= 3; if((multFlag & 0x0888) != 0) r *= 2; if((multFlag & 0x0210) != 0) r *= 5; if((multFlag & 0x0040) != 0) r *= 7; if((multFlag & 0x0080) != 0) r *= 2; if((multFlag & 0x0100) != 0) r *= 3; if((multFlag & 0x0400) != 0) r *= 11; if((multFlag & 0x1000) != 0) r *= 13; return r; } } public P InverseElement { get { long t = Value, r = 0L; byte i = 0; do r |= (long)i << ((int)(t >> (i << _s) & _mask) << _s); while(++i < _count); return new P(r ^ _mix); } } public P Add(P other) { long t = Value, o = other.Value, r = 0L; byte i = 0; do { r |= (t >> (int)((o >> i & _mask) << _s) & _mask) << i; i += 1 << _s; } while(i < _len); return new P(r ^ _mix); } public P Subtract(P other) { long t = Value, o = other.Value, r = 0L; byte i = 0; do { r |= (t >> i & _mask) << (int)((o >> i & _mask) << _s); i += 1 << _s; } while(i < _len); return new P(r ^ _mix); } public P Times(int count) { return this.Times

(count); } public P GetNextPermutation(int maxLength, Order match) { int[] a = ToArray(); a.ApplyNextPermutation(maxLength, match); long r = 0; for(int i = 0; i < _count; ++i) r |= (long)a[i] << (i << _s); return new P(r ^ _mix); } public P GetNextPermutation(int maxLength) { return GetNextPermutation(maxLength, (int l, int r) => l >= r); } public P GetPreviousPermutation(int maxLength) { return GetNextPermutation(maxLength, (int l, int r) => l <= r); } public List

GetCycles(Predicate

match) { List

list = new List

(_count); long t = Value; short digitFlag = 0; for(byte i = 0; i < _count; ++i) { if((1 << i & digitFlag) != 0) continue; byte digit = i; long value = 0; do { value |= _mask << (digit << _s) & _value; digitFlag |= (short)(1 << digit); digit = (byte)(t >> (digit << _s) & _mask); } while((1 << digit & digitFlag) == 0); P p = new P(value); if(match(p)) list.Add(p); } return list; } public int GetCyclesCount(Predicate match) { int count = 0; long t = Value; short digitFlag = 0; for(byte i = 0; i < _count; ++i) { if((1 << i & digitFlag) != 0) continue; byte digit = i; byte cLen = 0; do { ++cLen; digitFlag |= (short)(1 << digit); digit = (byte)(t >> (digit << _s) & _mask); } while((1 << digit & digitFlag) == 0); if(match(cLen)) ++count; } return count; } public override int GetHashCode() { return (int)(_value >> 32 ^ _value); } public override bool Equals(object o) { return o is P && Equals((P)o); } public bool Equals(P o) { return _value == o._value; } public override string ToString() { return _toString(_count); } public string ToString(byte minLength) { if(minLength > _count) minLength = _count; long t = Value; byte i = _count; if(minLength > 0) --minLength; while(--i > minLength && (t >> (i << _s) & _mask) == i) ; if(minLength < i) minLength = i; return _toString(++minLength); } private string _toString(byte length) { StringBuilder sb = new StringBuilder(length, length); length <<= _s; long t = Value; byte i = 0, digit; do { digit = (byte)(t >> i & _mask); sb.Append((char)((digit < 10 ? '0' : '7') + digit)); i += 1 << _s; } while(i < length); return sb.ToString(); } public IEnumerator GetEnumerator() { long t = Value; byte i = 0; do { yield return (int)(t >> i & _mask); i += 1 << _s; } while(i < _len); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public int[] ToArray() { long v = Value; int[] a = new int[_count]; int i = 0; do { a[i] = (int)(v & _mask); v >>= 1 << _s; } while(++i < _count); return a; } /// /// /// Invalid . /// /// public static P FromString(string s) { if(ReferenceEquals(s, null)) throw new ArgumentNullException(); if(s.Length > _count) _throwString(string.Format( "String length ({2}) is out of range ({0}, {1}).", 0, _count + 1, s.Length )); if(s.Length < 1) return new P(); long value = 0L; byte startIndex = 0; for(byte digit = 0; digit < _count; ++digit) { byte i = 0; char c; do { c = s[i]; if(c >= 'a' && c <= 'f') c &= '\xFFDF'; if(c < '0' || c > 'F' || (c > '9' && c < 'A')) _throwString(string.Format( "\'{0}\' is not a digit from {1}.", c, _charPattern )); if(c >= 'A') c -= '\x0007'; } while((c & _mask) != digit && ++i < s.Length); if(i == s.Length) if(startIndex >= digit || i > digit) _throwString(string.Format( "Digit \'{0}\' is not found.", (char)((digit < 10 ? '0' : '7') + digit) )); else return new P(((1L << (digit << _s)) - 1L) & _mix ^ value); else { value |= (long)digit << (i << _s); if(startIndex < i) startIndex = i; } } return new P(_mix ^ value); } public static P FromInt64(long value) { byte startIndex = 0; for(byte digit = 0; digit < _count; ++digit) { byte i = 0; while(i < _count && (value >> (i << _s) & _mask) != digit) ++i; if(i == _count) if(startIndex >= digit || (value & (-1L << (digit << _s))) != 0L) _throwInt64(string.Format( "Digit \'{0}\' is not found.", (char)((digit < 10 ? '0' : '7') + digit) )); else return new P(((1L << (digit << _s)) - 1L) & _mix ^ value); else if(startIndex < i) startIndex = i; } return new P(_mix ^ value); } [DebuggerStepThrough] private static void _throwString(string message) { throw new ArgumentException(string.Concat(message, " Use unique digits from ", _charPattern, ". Example: \"0123456789ABCDEF\"." )); } [DebuggerStepThrough] private static void _throwInt64(string message) { throw new ArgumentException(string.Concat(message, " Use hexadecimal format and unique digits from ", _charPattern, ". Example: -0x123456789ABCDF0L." )); } [DebuggerStepThrough] private static void _throwArray(string message) { StringBuilder sb = new StringBuilder(message); sb.AppendFormat( " Use unique values from range ({0}, {1}).", 0, _count ); sb.Append(" Example: {"); byte i = 0; sb.AppendFormat( CultureInfo.InvariantCulture, " {0}", i ); while(++i < _count) sb.AppendFormat( CultureInfo.InvariantCulture, ", {0}", i ); sb.Append(" }."); throw new ArgumentException(sb.ToString()); } public static bool operator ==(P l, P r) { return l.Equals(r); } public static bool operator !=(P l, P r) { return !l.Equals(r); } public static P operator +(P o) { return o; } public static P operator -(P o) { return o.InverseElement; } public static P operator +(P l, P r) { return l.Add(r); } public static P operator -(P l, P r) { return l.Subtract(r); } public static P operator *(P l, int r) { return l.Times(r); } public static P operator *(int l, P r) { return r.Times(l); } public static implicit operator P(string o) { return FromString(o); } public static implicit operator P(long o) { return FromInt64(o); } [CLSCompliant(false)] public static implicit operator P(ulong o) { return FromInt64((long)o); } } }