GArray.cs

Posted on June 6, 2020  Leave a comment on GArray.cs

Used By Lz77.cs

using System;
using System.Collections.Generic;
using System.Linq;
[Serializable]
public class GArray<T>
{
    private readonly int _capacity = 4096;
    private          int _count;
    private          int _position;
    private          T[] Thing;
    public GArray()
    {
        _capacity = 4096;
        _position = 0;
        _count    = 0;
        Thing     = new T[_capacity];
    }
    public GArray(IEnumerable<T> collection)
    {
        if (collection == null)
            throw new Exception("Collection cannot be null.");
        var Collection = collection as T[] ?? collection.ToArray();
        var count      = Collection.Count();
        _capacity = 32;
        _position = 0;
        _count    = count;
        Thing     = new T[count];
        Array.Copy(Collection, 0, Thing, 0, count);
    }
    public bool ReadEnd => _position == Thing.Length;
    public void Write(IEnumerable<T> collection)
    {
        if (collection == null)
            throw new Exception("Collection cannot be null.");
        var Collection = collection as T[] ?? collection.ToArray();
        var count      = Collection.Count();
        EnsureSize(_count + count);
        Array.Copy(Collection, 0, Thing, _count, count);
        _count += count;
    }
    public void Write(T value)
    {
        EnsureSize(_count + 1);
        Thing[_count] = value;
        _count++;
    }
    /// <summary>
    ///     Read an array of items from the main array starting from read position of count items.
    /// </summary>
    public T[] Read(int count)
    {
        if (_position + count > Thing.Length - 1)
            throw new ArgumentException("Position out of range.");
        var tarray = new T[count];
        Array.Copy(Thing, _position, tarray, 0, count);
        _position += count;
        return tarray;
    }
    /// <summary>
    ///     Reads one items from the array
    /// </summary>
    public T Read()
    {
        if (_position > Thing.Length - 1)
            throw new ArgumentException("Position out of range.");
        return Thing[_position++];
    }
    private void EnsureSize(int MinimumSize)
    {
        if (Thing.Length >= MinimumSize) return;
        var NewLength                          = Thing.Length == 0 ? 4096 : Thing.Length * 2;
        if (NewLength < MinimumSize) NewLength = MinimumSize;
        var newtArray                          = new T[NewLength];
        Array.Copy(Thing, 0, newtArray, 0, _count);
        Thing = newtArray;
    }
    /// <summary>
    ///     Returns an array of type T items.
    /// </summary>
    public T[] ToArray()
    {
        var objArray = new T[_count];
        Array.Copy(Thing, 0, objArray, 0, _count);
        return objArray;
    }
}

Lz77.cs

Posted on June 6, 2020  Leave a comment on Lz77.cs

LZ77 Compression Algorithm

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
public static class Lz77
{
    private const           int      RingBufferSize   = 1024 * 4;
    private const           int      UpperMatchLength = 18;
    private const           int      LowerMatchLength = 3;
    private const           int      None             = RingBufferSize;
    private static readonly int[]    Parent           = new int[RingBufferSize                       + 1];
    private static readonly int[]    LeftChild        = new int[RingBufferSize                       + 1];
    private static readonly int[]    RightChild       = new int[RingBufferSize                       + 257];
    private static readonly ushort[] Buffer           = new ushort[RingBufferSize + UpperMatchLength - 1];
    private static          int      matchPosition, matchLength;
    /// <summary>
    ///     Size of the compressed code during and after compression
    /// </summary>
    public static int CompressedSize
    {
        get;
        set;
    }
    /// <summary>
    ///     Size of the original code packet while decompressing
    /// </summary>
    public static int DeCompressedSize
    {
        get;
        set;
    }
    public static double Ratio => (double) CompressedSize / DeCompressedSize * 100.0;
    public static byte[] Lz77Decompress(this byte[] ins)
    {
        if (ins == null)
            throw new Exception("Input buffer is null.");
        if (ins.Length == 0)
            throw new Exception("Input buffer is empty.");
        var outa = new GArray<byte>();
        var ina  = new GArray<byte>(ins);
        CompressedSize   = 0;
        DeCompressedSize = ina.Read(4).ToInt();
        for (var i = 0; i < RingBufferSize - UpperMatchLength; i++)
            Buffer[i] = 0;
        var  r     = RingBufferSize - UpperMatchLength;
        uint flags = 7;
        var  z     = 7;
        while (true)
        {
            flags <<= 1;
            z++;
            if (z == 8)
            {
                if (ina.ReadEnd)
                    break;
                flags = ina.Read();
                z     = 0;
            }
            if ((flags & 0x80) == 0)
            {
                if (ina.ReadEnd)
                    break;
                var c = ina.Read();
                if (CompressedSize < DeCompressedSize)
                    outa.Write(c);
                Buffer[r++] =  c;
                r           &= RingBufferSize - 1;
                CompressedSize++;
            }
            else
            {
                if (ina.ReadEnd)
                    break;
                int i = ina.Read();
                if (ina.ReadEnd)
                    break;
                int j = ina.Read();
                j = j | ((i << 8) & 0xF00);
                i = ((i     >> 4) & 0xF) + LowerMatchLength;
                for (var k = 0; k <= i; k++)
                {
                    var c = Buffer[(r - j - 1) & (RingBufferSize - 1)];
                    if (CompressedSize < DeCompressedSize)
                        outa.Write((byte) c);
                    Buffer[r++] =  (byte) c;
                    r           &= RingBufferSize - 1;
                    CompressedSize++;
                }
            }
        }
        return outa.ToArray();
    }
    /// <summary>
    ///     E:12.5, R:17.3 E:25.0, R:35.9 E:32.3, R:47.7 E:37.5, R:56.5 E:41.5, R:63.0 E:44.8, R:67.6 E:47.6, R:71.7 E:50.0,
    ///     R:75.8 E:52.1, R:79.9 E:54.0, R:83.9 E:55.7, R:87.7
    ///     E:57.3, R:91.0 E:58.8, R:93.9 E:60.1, R:96.6 E:61.3, R:98.8 E:62.5, R:100.6 E:63.6, R:102.1 E:64.6, R:103.5 E:65.6,
    ///     R:104.7 E:66.5, R:105.6 E:67.4, R:106.5
    ///     E:68.2, R:107.2 E:69.0, R:107.8 E:69.8, R:108.3 E:70.5, R:108.7
    /// </summary>
    public static byte[] Lz77Compress(this byte[] ins, bool TestForCompressibility = false)
    {
        if (ins == null)
            throw new Exception("Input buffer is null.");
        if (ins.Length == 0)
            throw new Exception("Input buffer is empty.");
        if (TestForCompressibility)
            if ((int) Entropy(ins) > 61)
                throw new Exception("Input buffer Cannot be compressed.");
        matchLength      = 0;
        matchPosition    = 0;
        CompressedSize   = 0;
        DeCompressedSize = ins.Length;
        int length;
        var codeBuffer = new int[UpperMatchLength - 1];
        var outa       = new GArray<byte>();
        var ina        = new GArray<byte>(ins);
        outa.Write(DeCompressedSize.GetBytes(0, 4));
        InitTree();
        codeBuffer[0] = 0;
        var codeBufferPointer = 1;
        var mask              = 0x80;
        var s                 = 0;
        var r                 = RingBufferSize - UpperMatchLength;
        for (var i = s; i < r; i++)
            Buffer[i] = 0xFFFF;
        for (length = 0; length < UpperMatchLength && !ina.ReadEnd; length++)
            Buffer[r + length] = ina.Read();
        if (length == 0)
            throw new Exception("No Data to Compress.");
        for (var i = 1; i <= UpperMatchLength; i++)
            InsertNode(r - i);
        InsertNode(r);
        do
        {
            if (matchLength > length)
                matchLength = length;
            if (matchLength <= LowerMatchLength)
            {
                matchLength                     = 1;
                codeBuffer[codeBufferPointer++] = Buffer[r];
            }
            else
            {
                codeBuffer[0]                   |= mask;
                codeBuffer[codeBufferPointer++] =  (byte) (((r - matchPosition - 1) >> 8) & 0xF) | ((matchLength - (LowerMatchLength + 1)) << 4);
                codeBuffer[codeBufferPointer++] =  (byte) ((r - matchPosition                                    - 1) & 0xFF);
            }
            if ((mask >>= 1) == 0)
            {
                for (var i = 0; i < codeBufferPointer; i++)
                    outa.Write((byte) codeBuffer[i]);
                CompressedSize    += codeBufferPointer;
                codeBuffer[0]     =  0;
                codeBufferPointer =  1;
                mask              =  0x80;
            }
            var lastMatchLength = matchLength;
            var ii              = 0;
            for (ii = 0; ii < lastMatchLength && !ina.ReadEnd; ii++)
            {
                DeleteNode(s);
                var c = ina.Read();
                Buffer[s] = c;
                if (s < UpperMatchLength - 1)
                    Buffer[s + RingBufferSize] = c;
                s = (s + 1) & (RingBufferSize - 1);
                r = (r + 1) & (RingBufferSize - 1);
                InsertNode(r);
            }
            while (ii++ < lastMatchLength)
            {
                DeleteNode(s);
                s = (s + 1) & (RingBufferSize - 1);
                r = (r + 1) & (RingBufferSize - 1);
                if (--length != 0)
                    InsertNode(r);
            }
        } while (length > 0);
        if (codeBufferPointer > 1)
        {
            for (var i = 0; i < codeBufferPointer; i++)
                outa.Write((byte) codeBuffer[i]);
            CompressedSize += codeBufferPointer;
        }
        if (CompressedSize % 4 != 0)
            for (var i = 0; i < 4 - CompressedSize % 4; i++)
                outa.Write(0);
        return outa.ToArray();
    }
    private static void InitTree()
    {
        for (var i = RingBufferSize + 1; i <= RingBufferSize + 256; i++)
            RightChild[i] = None;
        for (var i = 0; i < RingBufferSize; i++)
            Parent[i] = None;
    }
    private static void InsertNode(int r)
    {
        var cmp                      = 1;
        var p                        = RingBufferSize + 1 + (Buffer[r] == 0xFFFF ? 0 : Buffer[r]);
        RightChild[r] = LeftChild[r] = None;
        matchLength   = 0;
        while (true)
        {
            if (cmp >= 0)
            {
                if (RightChild[p] != None)
                {
                    p = RightChild[p];
                }
                else
                {
                    RightChild[p] = r;
                    Parent[r]     = p;
                    return;
                }
            }
            else
            {
                if (LeftChild[p] != None)
                {
                    p = LeftChild[p];
                }
                else
                {
                    LeftChild[p] = r;
                    Parent[r]    = p;
                    return;
                }
            }
            int i;
            for (i = 1; i < UpperMatchLength; i++)
                if ((cmp = Buffer[r + i] - Buffer[p + i]) != 0)
                    break;
            if (i > matchLength)
            {
                matchPosition = p;
                if ((matchLength = i) >= UpperMatchLength)
                    break;
            }
        }
        Parent[r]             = Parent[p];
        LeftChild[r]          = LeftChild[p];
        RightChild[r]         = RightChild[p];
        Parent[LeftChild[p]]  = r;
        Parent[RightChild[p]] = r;
        if (RightChild[Parent[p]] == p)
            RightChild[Parent[p]] = r;
        else LeftChild[Parent[p]] = r;
        Parent[p] = None;
    }
    private static void DeleteNode(int p)
    {
        int q;
        if (Parent[p] == None)
            return;
        if (RightChild[p] == None)
        {
            q = LeftChild[p];
        }
        else if (LeftChild[p] == None)
        {
            q = RightChild[p];
        }
        else
        {
            q = LeftChild[p];
            if (RightChild[q] != None)
            {
                do
                {
                    q = RightChild[q];
                } while (RightChild[q] != None);
                RightChild[Parent[q]] = LeftChild[q];
                Parent[LeftChild[q]]  = Parent[q];
                LeftChild[q]          = LeftChild[p];
                Parent[LeftChild[p]]  = q;
            }
            RightChild[q]         = RightChild[p];
            Parent[RightChild[p]] = q;
        }
        Parent[q] = Parent[p];
        if (RightChild[Parent[p]] == p)
            RightChild[Parent[p]] = q;
        else LeftChild[Parent[p]] = q;
        Parent[p] = None;
    }
    private static double Entropy(byte[] ba)
    {
        var map = new Dictionary<byte, int>();
        foreach (var c in ba)
            if (!map.ContainsKey(c))
                map.Add(c, 1);
            else
                map[c]++;
        double Len = ba.Length;
        var    re  = map.Select(item => item.Value / Len).Aggregate(0.0, (current, frequency) => current - frequency * (Math.Log(frequency) / Math.Log(2)));
        return re / 8.00D * 100D;
    }
}

FixedBigIntegerRandomNumberGenerator.cs

Posted on June 5, 2020  Leave a comment on FixedBigIntegerRandomNumberGenerator.cs

Fixed BigInteger Random Number Generator

using System;
using System.Security.Cryptography;
public class FixedBigIntegerRandomNumberGenerator : RandomNumberGenerator
{
    private readonly int             _bitWidthHold;
    private readonly byte[]          _buffer;
    private readonly JitterCacheRng  _crng;
    private readonly BigDecimal      _dBi;
    private readonly FixedBigInteger _maxValueHold = new FixedBigInteger(0, 0);
    public FixedBigIntegerRandomNumberGenerator(int bitWidth)
    {
        if (bitWidth < 32)
            bitWidth = 32;
        var maxValue = GetMaxValue(bitWidth);
        var vdp      = maxValue.GetDecimalPlaces();
        if (vdp > BigDecimal.MaxPrecision)
            BigDecimal.MaxPrecision = vdp;
        _bitWidthHold = bitWidth;
        if (_bitWidthHold < 32)
            _bitWidthHold = 32;
        _maxValueHold = maxValue;
        _dBi          = BigDecimal.One / (BigDecimal) maxValue;
        _buffer       = new byte[_bitWidthHold >> 3];
        _crng         = new JitterCacheRng(1000000);
    }
    public FixedBigIntegerRandomNumberGenerator(int bitWidth, int cacheSize)
    {
        if (bitWidth < 32)
            bitWidth = 32;
        var maxValue = GetMaxValue(bitWidth);
        var vdp      = maxValue.GetDecimalPlaces();
        if (vdp > BigDecimal.MaxPrecision)
            BigDecimal.MaxPrecision = vdp;
        _bitWidthHold = bitWidth;
        if (_bitWidthHold < 32)
            _bitWidthHold = 32;
        _maxValueHold = maxValue;
        _dBi          = BigDecimal.One / (BigDecimal) maxValue;
        _buffer       = new byte[_bitWidthHold >> 3];
        _crng         = new JitterCacheRng(cacheSize);
    }
    public bool OddsOnly
    {
        get;
        set;
    }
    public bool Unsigned
    {
        get;
        set;
    }
    protected override void Dispose(bool disposing)
    {
        _crng.Dispose();
    }
    private FixedBigInteger GetMaxValue(int bitWidth)
    {
        var r = new FixedBigInteger(0, bitWidth);
        for (var i = 0; i < r.Data.Length; ++i)
            r.Data[i] = uint.MaxValue;
        r.Data[r.Data.Length - 1] = int.MaxValue;
        return r;
    }
    private FixedBigInteger GetMaxValue()
    {
        var r = new FixedBigInteger(0, _bitWidthHold);
        for (var i = 0; i < r.Data.Length; ++i)
            r.Data[i] = uint.MaxValue;
        r.Data[r.Data.Length - 1] = int.MaxValue;
        return r;
    }
    private BigDecimal Sample()
    {
        FixedBigInteger Internal()
        {
            _crng.GetBytes(_buffer);
            var n = new FixedBigInteger(_buffer, _bitWidthHold);
            return !OddsOnly ? n : n | 1;
        }
        var s = (BigDecimal) Internal() * _dBi;
        if (s.Sign == -1)
            s = s * -1;
        if (s.IsZero)
            throw new Exception("Sample is zero. SEE: MaxPrecision in BigDecimal.cs. Default value is 308.");
        return s;
    }
    public FixedBigInteger Next(FixedBigInteger minValue, FixedBigInteger maxValue)
    {
        var sa = Sample();
        var fi = (BigDecimal) (maxValue - minValue + minValue);
        var n  = new FixedBigInteger(sa * fi, 0);
        n = !OddsOnly ? n : n | 1;
        if (Unsigned)
            return n.Sign < 0 ? new FixedBigInteger(1, n.Data, 0) : n;
        var buffer = new byte[1];
        _crng.GetBytes(buffer);
        if (buffer[0] > 127)
            return n.Sign < 0 ? n : new FixedBigInteger(-1, n.Data, 0);
        return n.Sign < 0 ? new FixedBigInteger(1, n.Data, 0) : n;
    }
    public FixedBigInteger Next(FixedBigInteger maxValue)
    {
        return Next(0, maxValue);
    }
    public FixedBigInteger Next()
    {
        return Next(0, _maxValueHold);
    }
    public override void GetBytes(byte[] data)
    {
        if (data == null)
            throw new ArgumentException("The buffer cannot be null.");
        _crng.GetBytes(data);
    }
    public void NextBytes(byte[] buffer)
    {
        if (buffer == null)
            throw new ArgumentNullException("The buffer cannot be null.");
        for (var index = 0; index < buffer.Length; ++index)
            buffer[index] = (byte) (Sample() * byte.MaxValue);
    }
    public FixedBigInteger[] Next(FixedBigInteger minValue, FixedBigInteger maxValue, int arraySize)
    {
        var array = new FixedBigInteger[arraySize];
        for (var i = 0; i < arraySize; ++i)
            array[i] = Next(minValue, maxValue);
        return array;
    }
    public char[] GetNextCharArray(int size)
    {
        var ca  = new char[size];
        var ptr = 0;
        do
        {
            ca[ptr++] = (char) Next(32, 128);
        } while (ptr < size);
        return ca;
    }
    public byte[] GetNextByteArray(int size)
    {
        var ba = new byte[size];
        _crng.GetBytes(ba);
        return ba;
    }
    public string GetRandomString(int minLen, int maxLen)
    {
        if (minLen == maxLen)
            return new string(GetNextCharArray(minLen));
        return new string(GetNextCharArray((int) Next((uint) minLen, (uint) maxLen)));
    }
}

BigDecimal.cs

Posted on June 5, 2020  Leave a comment on BigDecimal.cs

Arbitrary Precision Signed Decimal

using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[Serializable]
[DebuggerDisplay("{DDisplay}")]
public class BigDecimal : IComparable, IComparable<BigDecimal>, IEquatable<BigDecimal>
{
    private const           int          MaxFactorials    = 200;
    public static           int          MaxPrecision     = 308;
    private static readonly BigInteger   DoublePrecision  = BigInteger.Pow(10, 308);
    private static readonly BigInteger   DoubleMaxValue   = (BigInteger) double.MaxValue;
    private static readonly BigInteger   DoubleMinValue   = (BigInteger) double.MinValue;
    private static readonly BigInteger   DecimalPrecision = BigInteger.Pow(10, 28);
    private static readonly BigInteger   DecimalMaxValue  = (BigInteger) decimal.MaxValue;
    private static readonly BigInteger   DecimalMinValue  = (BigInteger) decimal.MinValue;
    private static          BigDecimal[] Factorials;
    private                 int          _scale;
    private                 BigInteger   _unscaledValue;
    public BigDecimal() : this(0, 0)
    {
    }
    public BigDecimal(BigInteger value) : this(value, 0)
    {
    }
    public BigDecimal(FixedBigInteger value) : this((BigInteger) value, 0)
    {
    }
    public BigDecimal(BigInteger value, int scale)
    {
        _unscaledValue = value;
        Scale          = scale;
    }
    public BigDecimal(long value) : this(value, 0)
    {
    }
    public BigDecimal(double value) : this((decimal) value)
    {
    }
    public BigDecimal(float value) : this((decimal) value)
    {
    }
    public BigDecimal(byte[] value)
    {
        var number = new byte[value.Length - 4];
        var flags  = new byte[4];
        Array.Copy(value, 0,                number, 0, number.Length);
        Array.Copy(value, value.Length - 4, flags,  0, 4);
        _unscaledValue = new BigInteger(number);
        Scale          = BitConverter.ToInt32(flags, 0);
    }
    public BigDecimal(BigRational value)
    {
        var num       = (BigDecimal) value.Numerator;
        var den       = (BigDecimal) value.Denominator;
        var bigDecRes = num / den;
        _unscaledValue = bigDecRes._unscaledValue;
        Scale          = bigDecRes.Scale;
    }
    public BigDecimal(decimal value)
    {
        var bytes = new byte[16];
        var bits  = decimal.GetBits(value);
        var lo    = bits[0];
        var mid   = bits[1];
        var hi    = bits[2];
        var flags = bits[3];
        bytes[0]  = (byte) lo;
        bytes[1]  = (byte) (lo >> 8);
        bytes[2]  = (byte) (lo >> 0x10);
        bytes[3]  = (byte) (lo >> 0x18);
        bytes[4]  = (byte) mid;
        bytes[5]  = (byte) (mid >> 8);
        bytes[6]  = (byte) (mid >> 0x10);
        bytes[7]  = (byte) (mid >> 0x18);
        bytes[8]  = (byte) hi;
        bytes[9]  = (byte) (hi >> 8);
        bytes[10] = (byte) (hi >> 0x10);
        bytes[11] = (byte) (hi >> 0x18);
        bytes[12] = (byte) flags;
        bytes[13] = (byte) (flags >> 8);
        bytes[14] = (byte) (flags >> 0x10);
        bytes[15] = (byte) (flags >> 0x18);
        var unscaledValueBytes = new byte[12];
        Array.Copy(bytes, unscaledValueBytes, unscaledValueBytes.Length);
        var unscaledValue = new BigInteger(unscaledValueBytes);
        var scale         = bytes[14];
        if (bytes[15] == 128)
            unscaledValue *= BigInteger.MinusOne;
        _unscaledValue = unscaledValue;
        Scale          = scale;
    }
    public BigDecimal(string value)
    {
        if (!value.ContainsOnly("0123456789+-.eE"))
            throw new Exception($"Input value must only contain these '0123456789+-.eE', value'{value}");
        var len      = value.Length;
        var start    = 0;
        var point    = 0;
        var dot      = -1;
        var negative = false;
        if (value[0] == '+')
        {
            ++start;
            ++point;
        }
        else if (value[0] == '-')
        {
            ++start;
            ++point;
            negative = true;
        }
        while (point < len)
        {
            var c = value[point];
            if (c == '.')
            {
                if (dot >= 0)
                    throw new Exception($"There are multiple '.'s in the value {value}");
                dot = point;
            }
            else if (c == 'e' || c == 'E')
            {
                break;
            }
            ++point;
        }
        string val;
        if (dot >= 0)
        {
            val   = value.Substring(start, dot) + value.Substring(dot + 1, point - (dot + 1));
            Scale = point - 1                   - dot;
        }
        else
        {
            val   = value.Substring(start, point);
            Scale = 0;
        }
        if (val.Length == 0)
            throw new Exception($"There are no digits in the value {value}");
        if (negative)
            val = "-" + val;
        _unscaledValue = val.BigIntegerBase10();
        if (point < len)
            try
            {
                point++;
                switch (value[point])
                {
                    case '+':
                    {
                        point++;
                        if (point >= len)
                            throw new Exception($"No exponent following e or E. Value {value}");
                        var scale = value.Substring(point);
                        Scale -= int.Parse(scale);
                        return;
                    }
                    case '-':
                    {
                        point++;
                        if (point >= len)
                            throw new Exception($"No exponent following e or E. Value {value}");
                        var scale = value.Substring(point);
                        Scale += int.Parse(scale);
                        return;
                    }
                    default:
                        throw new Exception($"Malformed exponent in value {value}");
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"Malformed exponent in value {value}");
            }
    }
    private string DDisplay => ToString();
    public static BigDecimal Zero
    {
        get;
    } = new BigDecimal(BigInteger.Zero);
    public static BigDecimal One
    {
        get;
    } = new BigDecimal(BigInteger.One);
    public static BigDecimal MinusOne
    {
        get;
    } = new BigDecimal(BigInteger.MinusOne);
    public bool IsEven       => _unscaledValue.IsEven;
    public bool IsOne        => _unscaledValue.IsOne;
    public bool IsPowerOfTwo => _unscaledValue.IsPowerOfTwo;
    public bool IsZero       => _unscaledValue.IsZero;
    public int  Sign         => _unscaledValue.Sign;
    public int Scale
    {
        get => _scale;
        private set => _scale = value;
    }
    public BigInteger UnscaledValue  => _unscaledValue;
    public BigDecimal WholePart      => BigInteger.Divide(_unscaledValue, BigInteger.Pow(10, Scale));
    public BigDecimal FractionalPart => this - WholePart;
    int IComparable.CompareTo(object obj)
    {
        if (obj == null)
            return 1;
        if (!(obj is BigDecimal))
            throw new Exception("Argument must be of type BigDecimal.");
        return Compare(this, (BigDecimal) obj);
    }
    public int CompareTo(BigDecimal other)
    {
        return Compare(this, other);
    }
    public bool Equals(BigDecimal other)
    {
        if (other is null)
            return false;
        return _unscaledValue == other._unscaledValue && Scale == other.Scale;
    }
    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        if (!(obj is BigDecimal))
            return false;
        return Equals((BigDecimal) obj);
    }
    public static BigDecimal Round(BigDecimal number, int decimalPlaces)
    {
        BigDecimal power = BigInteger.Pow(10, decimalPlaces);
        number *= power;
        return number >= 0 ? (BigInteger) (number + 0.5) / power : (BigInteger) (number - 0.5) / power;
    }
    public void Round(int decimalPlaces)
    {
        var        number = this;
        BigDecimal power  = BigInteger.Pow(10, decimalPlaces);
        number *= power;
        var n = number >= 0 ? (BigInteger) (number + 0.5) / power : (BigInteger) (number - 0.5) / power;
        _unscaledValue = n._unscaledValue;
        Scale          = n.Scale;
    }
    public override int GetHashCode()
    {
        return UnscaledValue.GetHashCode() ^ Scale.GetHashCode();
    }
    public string ToString()
    {
        var number = _unscaledValue.ToString("G");
        if (Scale > 0 && WholePart != 0 && number.Length - Scale >= 0)
            return number.Insert(number.Length - Scale, ".");
        StringBuilder buf;
        var           intString      = _unscaledValue.ToString();
        var           insertionPoint = intString.Length - Scale;
        if (insertionPoint == 0)
            return (Sign < 0 ? "-0." : "0.") + intString;
        if (insertionPoint > 0)
        {
            buf = new StringBuilder(intString);
            buf.Insert(insertionPoint, '.');
            if (Sign < 0)
                buf.Insert(0, '-');
        }
        else
        {
            buf = new StringBuilder(3 - insertionPoint + intString.Length);
            buf.Append(Sign < 0 ? "-0." : "0.");
            for (var i = 0; i < -insertionPoint; i++)
                buf.Append('0');
            buf.Append(intString);
        }
        if (Scale == 0)
            buf.Append("0");
        return buf.ToString();
    }
    public static BigDecimal Parse(string value)
    {
        return new BigDecimal(value);
    }
    public byte[] ToByteArray()
    {
        var unscaledValue = _unscaledValue.ToByteArray();
        var scale         = BitConverter.GetBytes(Scale);
        var bytes         = new byte[unscaledValue.Length + scale.Length];
        Array.Copy(unscaledValue, 0, bytes, 0,                    unscaledValue.Length);
        Array.Copy(scale,         0, bytes, unscaledValue.Length, scale.Length);
        return bytes;
    }
    public (byte[] unscaledValue, byte[] scale) ToByteArrays()
    {
        return (_unscaledValue.ToByteArray(), BitConverter.GetBytes(Scale));
    }
    public static BigDecimal Abs(BigDecimal value)
    {
        return value._unscaledValue.Sign < 0 ? -value : value;
    }
    public static BigDecimal Negate(BigDecimal value)
    {
        return new BigDecimal(BigInteger.Negate(value._unscaledValue), value.Scale);
    }
    public static BigDecimal Add(BigDecimal x, BigDecimal y)
    {
        return x + y;
    }
    public static BigDecimal Subtract(BigDecimal x, BigDecimal y)
    {
        return x - y;
    }
    public static BigDecimal Multiply(BigDecimal x, BigDecimal y)
    {
        return x * y;
    }
    public static BigDecimal Divide(BigDecimal dividend, BigDecimal divisor)
    {
        return dividend / divisor;
    }
    public static BigDecimal Pow(BigDecimal baseValue, BigInteger exponent)
    {
        if (exponent.Sign == 0)
            return One;
        if (exponent.Sign < 0)
        {
            if (baseValue == Zero)
                throw new Exception("Cannot raise zero to a negative power.");
            baseValue = One / baseValue;
            exponent  = BigInteger.Negate(exponent);
        }
        var result = baseValue;
        while (exponent > BigInteger.One)
        {
            result *= baseValue;
            exponent--;
        }
        return result;
    }
    public static int Compare(BigDecimal r1, BigDecimal r2)
    {
        return (r1 - r2)._unscaledValue.Sign;
    }
    public static bool operator ==(BigDecimal x, BigDecimal y)
    {
        return x.Equals(y);
    }
    public static bool operator !=(BigDecimal x, BigDecimal y)
    {
        return !x.Equals(y);
    }
    public static bool operator <(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) < 0;
    }
    public static bool operator <=(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) <= 0;
    }
    public static bool operator >(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) > 0;
    }
    public static bool operator >=(BigDecimal x, BigDecimal y)
    {
        return Compare(x, y) >= 0;
    }
    public static BigDecimal operator +(BigDecimal value)
    {
        return value;
    }
    public static BigDecimal operator -(BigDecimal value)
    {
        return new BigDecimal(-value._unscaledValue, value.Scale);
    }
    public static BigDecimal operator ++(BigDecimal value)
    {
        return value + One;
    }
    public static BigDecimal operator --(BigDecimal value)
    {
        return value - One;
    }
    public static BigDecimal operator +(BigDecimal left, BigDecimal right)
    {
        BigDecimal ret;
        if (left.Scale >= right.Scale)
        {
            ret = left;
        }
        else
        {
            var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale);
            ret = new BigDecimal(value, right.Scale);
        }
        BigDecimal ret1;
        if (right.Scale >= left.Scale)
        {
            ret1 = right;
        }
        else
        {
            var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale);
            ret1 = new BigDecimal(value1, left.Scale);
        }
        return new BigDecimal(ret._unscaledValue + ret1._unscaledValue, ret.Scale);
    }
    public static BigDecimal operator -(BigDecimal left, BigDecimal right)
    {
        BigDecimal ret;
        if (left.Scale >= right.Scale)
        {
            ret = left;
        }
        else
        {
            var value = left._unscaledValue * BigInteger.Pow(10, right.Scale - left.Scale);
            ret = new BigDecimal(value, right.Scale);
        }
        BigDecimal ret1;
        if (right.Scale >= left.Scale)
        {
            ret1 = right;
        }
        else
        {
            var value1 = right._unscaledValue * BigInteger.Pow(10, left.Scale - right.Scale);
            ret1 = new BigDecimal(value1, left.Scale);
        }
        return new BigDecimal(ret._unscaledValue - ret1._unscaledValue, ret.Scale);
    }
    public static BigDecimal operator *(BigDecimal left, BigDecimal right)
    {
        return new BigDecimal(left._unscaledValue * right._unscaledValue, left.Scale + right.Scale);
    }
    public static BigDecimal operator /(BigDecimal left, BigDecimal right)
    {
        var value = left._unscaledValue;
        var scale = left.Scale;
        while (scale > 0 && value % 10 == 0)
        {
            value /= 10;
            scale--;
        }
        var v1     = new BigDecimal(value, scale);
        var value1 = right._unscaledValue;
        var scale1 = right.Scale;
        while (scale1 > 0 && value1 % 10 == 0)
        {
            value1 /= 10;
            scale1--;
        }
        var v2 = new BigDecimal(value1, scale1);
        while (v1.Scale > 0 || v2.Scale > 0)
        {
            if (v1.Scale > 0)
                v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1);
            else
                v1 = new BigDecimal(v1._unscaledValue * 10, 0);
            if (v2.Scale > 0)
                v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1);
            else
                v2 = new BigDecimal(v2._unscaledValue * 10, 0);
        }
        var factor  = 0;
        var v1Value = v1._unscaledValue;
        while (factor < MaxPrecision && v1Value % v2._unscaledValue != 0)
        {
            v1Value *= 10;
            factor++;
        }
        return new BigDecimal(v1Value / v2._unscaledValue, factor);
    }
    public static BigDecimal operator %(BigDecimal left, BigDecimal right)
    {
        var value = left._unscaledValue;
        var scale = left.Scale;
        while (scale > 0 && value % 10 == 0)
        {
            value /= 10;
            scale--;
        }
        var v1     = new BigDecimal(value, scale);
        var value1 = right._unscaledValue;
        var scale1 = right.Scale;
        while (scale1 > 0 && value1 % 10 == 0)
        {
            value1 /= 10;
            scale1--;
        }
        var v2 = new BigDecimal(value1, scale1);
        while (v1.Scale > 0 || v2.Scale > 0)
        {
            if (v1.Scale > 0)
                v1 = new BigDecimal(v1._unscaledValue, v1.Scale - 1);
            else
                v1 = new BigDecimal(v1._unscaledValue * 10, 0);
            if (v2.Scale > 0)
                v2 = new BigDecimal(v2._unscaledValue, v2.Scale - 1);
            else
                v2 = new BigDecimal(v2._unscaledValue * 10, 0);
        }
        return new BigDecimal(v1._unscaledValue % v2._unscaledValue);
    }
    public static explicit operator sbyte(BigDecimal value)
    {
        return (sbyte) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator ushort(BigDecimal value)
    {
        return (ushort) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator uint(BigDecimal value)
    {
        return (uint) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator ulong(BigDecimal value)
    {
        return (ulong) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator byte(BigDecimal value)
    {
        return (byte) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator short(BigDecimal value)
    {
        return (short) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator int(BigDecimal value)
    {
        return (int) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator long(BigDecimal value)
    {
        return (long) BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale - 1));
    }
    public static explicit operator BigInteger(BigDecimal value)
    {
        return BigInteger.Divide(value._unscaledValue, (BigInteger) Math.Pow(10, value.Scale));
    }
    public static explicit operator FixedBigInteger(BigDecimal value)
    {
        return FixedBigInteger.Divide(value._unscaledValue, (FixedBigInteger) Math.Pow(10, value.Scale));
    }
    public static explicit operator float(BigDecimal value)
    {
        return (float) (double) value;
    }
    public static explicit operator double(BigDecimal value)
    {
        var factor = BigInteger.Pow(10, value.Scale);
        if (SafeCastToDouble(value._unscaledValue) && SafeCastToDouble(factor))
            return (double) value._unscaledValue / (double) factor;
        var dnv = value._unscaledValue * DoublePrecision / factor;
        if (dnv.IsZero)
            return value.Sign < 0 ? BitConverter.Int64BitsToDouble(unchecked((long) 0x8000000000000000)) : 0d;
        double result   = 0;
        var    isDouble = false;
        var    scale    = 308;
        while (scale > 0)
        {
            if (!isDouble)
            {
                if (SafeCastToDouble(dnv))
                {
                    result   = (double) dnv;
                    isDouble = true;
                }
                else
                {
                    dnv /= 10;
                }
            }
            result /= 10;
            scale--;
        }
        if (!isDouble)
            return value.Sign < 0 ? double.NegativeInfinity : double.PositiveInfinity;
        return result;
    }
    public static explicit operator decimal(BigDecimal value)
    {
        var factor = BigInteger.Pow(10, value.Scale);
        if (SafeCastToDecimal(value._unscaledValue) && SafeCastToDecimal(factor))
            return (decimal) value._unscaledValue / (decimal) factor;
        var dnv = value._unscaledValue * DecimalPrecision / factor;
        if (dnv.IsZero)
            return decimal.Zero;
        for (var scale = 28; scale >= 0; scale--)
            if (!SafeCastToDecimal(dnv))
            {
                dnv /= 10;
            }
            else
            {
                var dec = new DecimalUInt32();
                dec.dec   = (decimal) dnv;
                dec.flags = (dec.flags & ~0x00FF0000) | (scale << 16);
                return dec.dec;
            }
        throw new Exception("Value was either too large or too small for a Decimal.");
    }
    public static implicit operator BigDecimal(sbyte value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(ushort value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(uint value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(ulong value)
    {
        return new BigDecimal((BigInteger) value);
    }
    public static implicit operator BigDecimal(byte value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(short value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(int value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(long value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(BigInteger value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(FixedBigInteger value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(float value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(double value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(decimal value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(BigRational value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(BigIntX value)
    {
        return new BigDecimal(value.ToString());
    }
    private static bool SafeCastToDouble(BigInteger value)
    {
        return DoubleMinValue <= value && value <= DoubleMaxValue;
    }
    private static bool SafeCastToDecimal(BigInteger value)
    {
        return DecimalMinValue <= value && value <= DecimalMaxValue;
    }
    private static BigInteger GetLastDigit(BigInteger value)
    {
        return value % new BigInteger(10);
    }
    private static int GetNumberOfDigits(BigInteger value)
    {
        return BigInteger.Abs(value).ToString().Length;
    }
    private static BigDecimal Factorial(BigDecimal x)
    {
        BigDecimal r = 1;
        BigDecimal c = 1;
        while (c <= x)
        {
            r *= c;
            c++;
        }
        return r;
    }
    public static BigDecimal Exp(BigDecimal x)
    {
        BigDecimal r  = 0;
        BigDecimal r1 = 0;
        var        k  = 0;
        while (true)
        {
            r += Pow(x, k) / Factorial(k);
            if (r == r1)
                break;
            r1 = r;
            k++;
        }
        return r;
    }
    public static BigDecimal Sine(BigDecimal ar, int n)
    {
        if (Factorials == null)
        {
            Factorials = new BigDecimal[MaxFactorials];
            for (var i = 0; i < MaxFactorials; i++)
                Factorials[i] = new BigDecimal();
            for (var i = 1; i < MaxFactorials + 1; i++)
                Factorials[i - 1] = Factorial(i);
        }
        var sin = ar;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2 + 1);
            trm /= Factorials[i * 2];
            if ((i & 1) == 1)
                sin -= trm;
            else
                sin += trm;
        }
        return sin;
    }
    public static BigDecimal Atan(BigDecimal ar, int n)
    {
        var atan = ar;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2 + 1);
            trm /= i * 2;
            if ((i & 1) == 1)
                atan -= trm;
            else
                atan += trm;
        }
        return atan;
    }
    public static BigDecimal Cosine(BigDecimal ar, int n)
    {
        if (Factorials == null)
        {
            Factorials = new BigDecimal[MaxFactorials];
            for (var i = 0; i < MaxFactorials; i++)
                Factorials[i] = new BigDecimal();
            for (var i = 1; i < MaxFactorials + 1; i++)
                Factorials[i - 1] = Factorial(i);
        }
        BigDecimal cos = 1.0;
        for (var i = 1; i <= n; i++)
        {
            var trm = Pow(ar, i * 2);
            trm /= Factorials[i * 2 - 1];
            if ((i & 1) == 1)
                cos -= trm;
            else
                cos += trm;
        }
        return cos;
    }
    private static BigDecimal GetE(int n)
    {
        BigDecimal e = 1.0;
        var        c = n;
        while (c > 0)
        {
            BigDecimal f = 0;
            if (c == 1)
            {
                f = 1;
            }
            else
            {
                var i = c - 1;
                f = c;
                while (i > 0)
                {
                    f *= i;
                    i--;
                }
            }
            c--;
            e += 1.0 / f;
        }
        return e;
    }
    public static BigDecimal Tangent(BigDecimal ar, int n)
    {
        return Sine(ar, n) / Cosine(ar, n);
    }
    public static BigDecimal CoTangent(BigDecimal ar, int n)
    {
        return Cosine(ar, n) / Sine(ar, n);
    }
    public static BigDecimal Secant(BigDecimal ar, int n)
    {
        return 1.0 / Cosine(ar, n);
    }
    public static BigDecimal NthRoot(BigDecimal value, int nth)
    {
        BigDecimal lx;
        var        a = value;
        var        n = nth;
        BigDecimal s = 1.0;
        do
        {
            var t = s;
            lx = a / Pow(s, n - 1);
            var r = (n        - 1) * s;
            s = (lx + r) / n;
        } while (lx != s);
        return s;
    }
    public static BigDecimal LogN(BigDecimal value)
    {
        var        E = GetE(MaxFactorials);
        BigDecimal a;
        var        p = value;
        BigDecimal n = 0.0;
        while (p >= E)
        {
            p /= E;
            n++;
        }
        n += p / E;
        p =  value;
        do
        {
            a = n;
            var lx = p         / Exp(n - 1.0);
            var r  = (n - 1.0) * E;
            n = (lx + r) / E;
        } while (n != a);
        return n;
    }
    public static BigDecimal Log(BigDecimal n, int b)
    {
        return LogN(n) / LogN(b);
    }
    public static BigDecimal CoSecant(BigDecimal ar, int n)
    {
        return 1.0 / Sine(ar, n);
    }
    public void Ceiling(int precision)
    {
        RemoveTrailingZeros();
        var diff = GetNumberOfDigits(_unscaledValue) - precision;
        if (diff > 0)
        {
            for (var i = 0; i < diff; i++)
            {
                _unscaledValue = BigInteger.Divide(_unscaledValue, 10);
                Scale--;
            }
            if (_unscaledValue.Sign < 0)
                _unscaledValue--;
            else
                _unscaledValue++;
        }
    }
    public void Floor(int precision)
    {
        RemoveTrailingZeros();
        var diff = GetNumberOfDigits(_unscaledValue) - precision;
        if (diff > 0)
        {
            for (var i = 0; i < diff; i++)
            {
                _unscaledValue = BigInteger.Divide(_unscaledValue, 10);
                Scale--;
            }
            if (_unscaledValue.Sign > 0)
                _unscaledValue--;
            else
                _unscaledValue++;
        }
    }
    private void RemoveTrailingZeros()
    {
        BigInteger remainder;
        do
        {
            var shortened = BigInteger.DivRem(_unscaledValue, 10, out remainder);
            if (remainder == BigInteger.Zero)
            {
                _unscaledValue = shortened;
                Scale--;
            }
        } while (remainder == BigInteger.Zero);
    }
    public BigDecimal Min(BigDecimal value)
    {
        return CompareTo(value) <= 0 ? this : value;
    }
    public BigDecimal Max(BigDecimal value)
    {
        return CompareTo(value) >= 0 ? this : value;
    }
    public static BigDecimal Sqrt(BigDecimal value)
    {
        return BigRational.Sqrt(value);
    }
    [StructLayout(LayoutKind.Explicit)]
    internal struct DecimalUInt32
    {
        [FieldOffset(0)] public decimal dec;
        [FieldOffset(0)] public int     flags;
    }
}

ChiSquaredImageComparison.cs

Posted on June 5, 20202 Comments on ChiSquaredImageComparison.cs

Chi Squared Image Comparison (Work In Progress)

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
public class ChiSquaredImageComparison
{
    private readonly int[]                               _expectedHistogram  = new int[256];
    private readonly ConcurrentDictionary<string, float> _finalDistList      = new ConcurrentDictionary<string, float>();
    private          ConcurrentDictionary<string, int[]> _observedHistograms = new ConcurrentDictionary<string, int[]>();
    private volatile float                               c, p, t, s1, s2;
    private volatile int                                 ExpectedLength;
    private volatile int                                 ExpectedMetric;
    private volatile int                                 XYExpectedLength;
    public void SetExpectedHistogram(string path)
    {
        try
        {
            using (var org = new Bitmap(path))
            {
                using (var img = new MemoryBitmap(org))
                {
                    XYExpectedLength = img.Height * img.Width;
                    for (var y = 0; y < img.Height; y++)
                    for (var x = 0; x < img.Width; x++)
                    {
                        var pixel = img.GetPixel(x, y);
                        _expectedHistogram[(pixel.R + pixel.G + pixel.B) / 3]++;
                    }
                }
            }
        }
        catch
        {
        }
        ExpectedLength = _expectedHistogram.Sum(e => e);
        ExpectedMetric = _expectedHistogram.Count(e => e > 0);
    }
    public void BuildHistograms(EnumBase.IEnumBase[] paths)
    {
        if (File.Exists(LocalSettings.ProgramDir + "C_JPG_HISTOGRAM.bin"))
        {
            var file = LoadFromFile(LocalSettings.ProgramDir + "C_JPG_HISTOGRAM.bin");
            _observedHistograms = new ConcurrentDictionary<string, int[]>(file);
            var list1 = paths.Select(e => e.PathString).ToList();
            var list2 = file.Select(e => e.Key).ToList();
            var list3 = list1.Except(list2).ToArray();
            foreach (var e in list3)
                try
                {
                    using (var org = new Bitmap(e))
                    {
                        var observedHistogram = new int[256];
                        using (var img = new MemoryBitmap(org))
                        {
                            c++;
                            p = c / t * 100f;
                            for (var y = 0; y < img.Height; y++)
                            for (var x = 0; x < img.Width; x++)
                            {
                                var pixel = img.GetPixel(x, y);
                                observedHistogram[(pixel.R + pixel.G + pixel.B) / 3]++;
                            }
                            if (!_observedHistograms.ContainsKey(e))
                                _observedHistograms.TryAdd(e, observedHistogram);
                        }
                    }
                }
                catch
                {
                }
        }
        else
        {
            t = paths.Length;
            paths.AsParallel().WithDegreeOfParallelism(8).ForAll(i =>
            {
                try
                {
                    using (var org = new Bitmap(i.PathString))
                    {
                        var observedHistogram = new int[256];
                        using (var img = new MemoryBitmap(org))
                        {
                            c++;
                            p = c / t * 100f;
                            for (var y = 0; y < img.Height; y++)
                            for (var x = 0; x < img.Width; x++)
                            {
                                var pixel = img.GetPixel(x, y);
                                observedHistogram[(pixel.R + pixel.G + pixel.B) / 3]++;
                            }
                            _observedHistograms.TryAdd(i.PathString, observedHistogram);
                        }
                    }
                }
                catch
                {
                }
            });
        }
        var kpa = _observedHistograms.ToArray();
        SaveToFile(kpa, LocalSettings.ProgramDir + "C_JPG_HISTOGRAM.bin");
    }
    public void SaveToFile(KeyValuePair<string, int[]>[] kpa, string path)
    {
        using (var wrtr = new Writer(path))
        {
            foreach (var i in kpa)
            {
                var k = i.Key;
                var v = i.Value;
                wrtr.WriteString(k);
                wrtr.WriteInts(v);
            }
        }
    }
    public KeyValuePair<string, int[]>[] LoadFromFile(string path)
    {
        var kpd = new Dictionary<string, int[]>();
        using (var reader = new Reader(path))
        {
            while (true)
                try
                {
                    var k = reader.ReadString();
                    var v = reader.ReadInts();
                    kpd.Add(k, v);
                }
                catch
                {
                    return kpd.ToArray();
                }
        }
        return kpd.ToArray();
    }
    public ConcurrentDictionary<string, float> HistogramChiSquared(EnumBase.IEnumBase[] paths)
    {
        t = paths.Length;
        paths.AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount - 2).ForAll(i =>
        {
            try
            {
                using (var org = new Bitmap(i.PathString))
                {
                    var observedHistogram = new int[256];
                    using (var img = new MemoryBitmap(org))
                    {
                        c++;
                        p = c / t * 100f;
                        var XYObservedLength = img.Height * img.Width;
                        if (XYObservedLength != XYExpectedLength)
                        {
                            s1++;
                            return;
                        }
                        for (var y = 0; y < img.Height; y++)
                        for (var x = 0; x < img.Width; x++)
                        {
                            var pixel = img.GetPixel(x, y);
                            observedHistogram[(pixel.R + pixel.G + pixel.B) / 3]++;
                        }
                        var observedLength = observedHistogram.Sum(e => e);
                        if (observedLength > ExpectedLength)
                        {
                            s2++;
                            return;
                        }
                        var ChiSqrd = 0.0f;
                        for (var j = 0; j < 256; j++)
                            if (_expectedHistogram[j] + observedHistogram[j] != 0)
                                ChiSqrd += (float) Math.Pow(_expectedHistogram[j] - observedHistogram[j], 2) / (_expectedHistogram[j] + observedHistogram[j]);
                        var fp = Math.Abs(ChiSqrd / ExpectedLength * 100f);
                        if (fp >= 0 && fp < 15)
                            _finalDistList.TryAdd(i.PathString, fp);
                    }
                }
            }
            catch
            {
            }
        });
        return _finalDistList;
    }
    public ConcurrentDictionary<string, float> LoadedHistogramChiSquared()
    {
        var file = LoadFromFile(LocalSettings.ProgramDir + "C_JPG_HISTOGRAM.bin");
        t = file.Length;
        file.AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount - 2).ForAll(i =>
        {
            try
            {
                c++;
                p = c / t * 100f;
                var d = 0.0f;
                for (var j = 0; j < 256; j++)
                {
                    var e = _expectedHistogram[j];
                    var o = i.Value[j];
                    if (e + o != 0)
                        d += (float) Math.Pow(e - o, 2) / (e + o);
                }
                var dd = d / 100;
                if (dd >= 0 && dd < 500)
                    _finalDistList.TryAdd(i.Key, dd);
            }
            catch
            {
            }
        });
        var srt = _finalDistList.OrderBy(x => x.Value);
        return _finalDistList;
    }
    public ConcurrentDictionary<string, float> LoadedHistogramIntersection()
    {
        var file = LoadFromFile(LocalSettings.ProgramDir + "C_JPG_HISTOGRAM.bin");
        t = file.Length;
        file.AsParallel().WithDegreeOfParallelism(Environment.ProcessorCount - 2).ForAll(i =>
        {
            try
            {
                c++;
                p = c / t * 100f;
                var d = 0.0f;
                for (var j = 0; j < 256; j++)
                {
                    var e = _expectedHistogram[j];
                    var o = i.Value[j];
                    d += e + o - Math.Abs(e - o);
                }
                var observedLength = i.Value.Sum(e => e);
                var dd             = 0.5f * d / Math.Max(ExpectedLength, observedLength);
                if (d >= 0 && d < 5)
                    _finalDistList.TryAdd(i.Key, d);
            }
            catch
            {
            }
        });
        var srt = _finalDistList.OrderBy(x => x.Value);
        return _finalDistList;
    }
}

Rdtsc.cs

Posted on June 4, 2020  Leave a comment on Rdtsc.cs

Rdtsc 32 and 64 Bit In C#

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security;
public static class Rdtsc
{
    [SuppressUnmanagedCodeSecurity]
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate ulong FuncUInt64();
    private const          uint       PAGE_READWRITE         = 0x04;
    private const          uint       PAGE_EXECUTE           = 0x10;
    private const          uint       PAGE_EXECUTE_READWRITE = 0x40;
    private const          uint       MEM_COMMIT             = 0x1000;
    private const          uint       MEM_RELEASE            = 0x8000;
    public static readonly FuncUInt64 Timestamp;
    public static readonly FuncUInt64 TimestampP;
    public static readonly bool       IsRdtscSupported;
    public static readonly bool       IsRdtscPSupported;
    static Rdtsc()
    {
        SystemInfo systemInfo;
        GetNativeSystemInfo(out systemInfo);
        if (systemInfo.wProcessorArchitecture != 0 && systemInfo.wProcessorArchitecture != 9)
        {
            Timestamp         = StopwatchGetTimestamp;
            TimestampP        = StopwatchGetTimestamp;
            IsRdtscSupported  = false;
            IsRdtscPSupported = false;
            return;
        }
        byte[] cpuid;
        byte[] rdtsc;
        byte[] rdtscp;
        byte[] rdtsccpuid;
        IsRdtscSupported = true;
        if (Is64Bit())
        {
            cpuid = new byte[]
            {
                0x53, 0xB8, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xA2, 0xBB, 0x01, 0x00, 0x00, 0x80, 0x39, 0xD8, 0x72, 0x16, 0x89, 0xD8,
                0x48, 0xC7, 0xC2, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xA2, 0x89, 0xC8, 0x48, 0xC1, 0xE0, 0x20, 0x48, 0x09, 0xD0, 0xEB,
                0x03, 0x48, 0x31, 0xC0, 0x5B, 0xC3
            };
            rdtsc  = new byte[] {0x0F, 0x31, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0xC3};
            rdtscp = new byte[] {0x0F, 0x01, 0xF9, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0xC3};
            rdtsccpuid = new byte[]
                {0x53, 0x31, 0xC0, 0x0F, 0xA2, 0x0F, 0x31, 0x48, 0xC1, 0xE2, 0x20, 0x48, 0x09, 0xD0, 0x5B, 0xC3};
        }
        else
        {
            cpuid = new byte[]
            {
                0x53, 0xB8, 0x00, 0x00, 0x00, 0x80, 0x0F, 0xA2, 0xBB, 0x01, 0x00, 0x00, 0x80, 0x39, 0xD8, 0x72, 0x0A, 0x89, 0xD8,
                0x0F, 0xA2, 0x89, 0xD0, 0x89, 0xCA, 0xEB, 0x04, 0x31, 0xC0, 0x31, 0xD2, 0x5B, 0xC3
            };
            rdtsc      = new byte[] {0x0F, 0x31, 0xC3};
            rdtscp     = new byte[] {0x0F, 0x01, 0xF9, 0xC3};
            rdtsccpuid = new byte[] {0x53, 0x31, 0xC0, 0x0F, 0xA2, 0x0F, 0x31, 0x5B, 0xC3};
        }
        var buf = IntPtr.Zero;
        try
        {
            var cpuidLength      = (cpuid.Length      & 63) != 0 ? (cpuid.Length      | 63) + 1 : cpuid.Length;
            var rdtscLength      = (rdtsc.Length      & 63) != 0 ? (rdtsc.Length      | 63) + 1 : rdtsc.Length;
            var rdtscpLength     = (rdtscp.Length     & 63) != 0 ? (rdtscp.Length     | 63) + 1 : rdtscp.Length;
            var rdtsccpuidLength = (rdtsccpuid.Length & 63) != 0 ? (rdtsccpuid.Length | 63) + 1 : rdtsccpuid.Length;
            var totalLength      = cpuidLength + rdtscLength + Math.Max(rdtscpLength, rdtsccpuidLength);
            buf = VirtualAlloc(IntPtr.Zero, (IntPtr) totalLength, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
            if (buf != IntPtr.Zero)
            {
                Marshal.Copy(cpuid, 0, buf, cpuid.Length);
                for (var i = cpuid.Length; i < cpuidLength; i++)
                    Marshal.WriteByte(buf, i, 0x90);
                Marshal.Copy(rdtsc, 0, buf + cpuidLength, rdtsc.Length);
                for (var i = rdtsc.Length; i < rdtscLength; i++)
                    Marshal.WriteByte(buf, cpuidLength + i, 0x90);
                var    cpuidFunc         = (FuncUInt64) Marshal.GetDelegateForFunctionPointer(buf, typeof(FuncUInt64));
                var    supportedFeatures = cpuidFunc();
                byte[] rdtscpSelected;
                int    rdtscpSelectedLength;
                if ((supportedFeatures & (1L << 27)) != 0)
                {
                    rdtscpSelected       = rdtscp;
                    rdtscpSelectedLength = rdtscpLength;
                    IsRdtscPSupported    = true;
                }
                else
                {
                    rdtscpSelected       = rdtsccpuid;
                    rdtscpSelectedLength = rdtsccpuidLength;
                    IsRdtscPSupported    = false;
                }
                Marshal.Copy(rdtscpSelected, 0, buf + cpuidLength + rdtscLength, rdtscpSelected.Length);
                for (var i = rdtscpSelected.Length; i < rdtscpSelectedLength; i++)
                    Marshal.WriteByte(buf, cpuidLength + rdtscLength + i, 0x90);
                var result = VirtualProtect(buf, (IntPtr) totalLength, PAGE_EXECUTE, out var oldProtection);
                if (result)
                {
                    Timestamp = (FuncUInt64) Marshal.GetDelegateForFunctionPointer(buf + cpuidLength, typeof(FuncUInt64));
                    TimestampP = (FuncUInt64) Marshal.GetDelegateForFunctionPointer(buf + cpuidLength + rdtscLength,
                        typeof(FuncUInt64));
                    buf = IntPtr.Zero;
                }
            }
        }
        finally
        {
            if (buf != IntPtr.Zero)
                VirtualFree(buf, IntPtr.Zero, MEM_RELEASE);
        }
    }
    private static bool Is64Bit()
    {
        return Marshal.SizeOf(typeof(IntPtr)) == 8;
    }
    [DllImport("kernel32.dll", ExactSpelling = true)]
    private static extern void GetNativeSystemInfo(out SystemInfo lpSystemInfo);
    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
    private static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, out uint lpflOldProtect);
    [DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, uint dwFreeType);
    private static ulong StopwatchGetTimestamp()
    {
        return unchecked((ulong) Stopwatch.GetTimestamp());
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct SystemInfo
    {
        public readonly ushort wProcessorArchitecture;
        public readonly ushort wReserved;
        public readonly uint   dwPageSize;
        public readonly IntPtr lpMinimumApplicationAddress;
        public readonly IntPtr lpMaximumApplicationAddress;
        public readonly IntPtr dwActiveProcessorMask;
        public readonly uint   dwNumberOfProcessors;
        public readonly uint   dwProcessorType;
        public readonly uint   dwAllocationGranularity;
        public readonly ushort wProcessorLevel;
        public readonly ushort wProcessorRevision;
    }
}

ChiSquared.cs

Posted on June 4, 2020  Leave a comment on ChiSquared.cs

Chi Squared Data/Byte/Text Test

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
public static class ChiSquared
{
    /// <summary>
    ///     Calculated from an English word dictionary containing over 466,000 words.
    /// </summary>
    private static readonly float[] _expectedPercentages = {.0846f, .0189f, .0420f, .0353f, .1098f, .0125f, .0243f, .0274f, .0864f, .0018f, .0089f, .0574f, .0292f, .0715f, .0709f, .0310f, .0019f, .0704f, .0705f, .0647f, .0363f, .0099f, .0085f, .0028f, .0192f, .0041f};
    /// <summary>
    ///     Not accurate 100% all of the time.
    /// </summary>
    /// <param name="path"></param>
    public static bool IsFileCompressed(this string path)
    {
        var arr = File.ReadAllBytes(path);
        var r1  = arr.ChiSquaredTest();
        return r1.isRandom;
    }
    /// <summary>
    ///     Tests a buffer for randomness. Returns chi squared values.
    ///     isRandom - is the buffer a random sequence.
    ///     Quality - Less than 1 or greater than 1 is off target. Observed is off expected.
    ///     Entropy - Calculates a 8 bit Entropy level of the buffer as a percentage of perfect disorder 100%
    ///     ExpectedChiSq - The expected chi squared value.
    ///     LowLimit - (R - (2*sqrt(R)))
    ///     chiSqValue - The observed chi squared value.
    ///     UpperLimit - (R + (2*sqrt(R)))
    /// </summary>
    /// <param name="bArr">The byte Array</param>
    public static (bool isRandom, float Quality, float Entropy, int ExpectedChiSq, float LowLimit, float chiSqValue, float UpperLimit) ChiSquaredTest(this byte[] bArr)
    {
        if (bArr != null)
        {
            var iArr = Ia(bArr);
            var ent  = Entropy(bArr);
            if (ent < 80)
                return (false, 0, ent, 0, 0, 0, 0);
            var aLen = iArr.Length;
            var rLim = aLen / 10;
            var n    = aLen;
            var r    = rLim - 1;
            var freq = new ConcurrentDictionary<int, int>();
            iArr.AsParallel().WithDegreeOfParallelism(2).ForAll(I =>
            {
                var iT = Math.Abs(Math.Abs(I) % rLim - rLim);
                if (!freq.ContainsKey(iT))
                    freq.TryAdd(iT, 1);
                else
                    freq[iT] += 1;
            });
            var t  = freq.Sum(e => (float) Math.Pow(e.Value, 2));
            var cS = Math.Abs(r * t / n - n);
            var fL = r - 2.0f * (float) Math.Sqrt(r);
            var fH = r + 2.0f * (float) Math.Sqrt(r);
            var iR = (fL <= cS) & (fH >= cS);
            var q  = cS / r;
            return (iR, q, ent, r, fL, cS, fH);
        }
        return default;
    }
    private static int[] Ia(byte[] ba)
    {
        var bal        = ba.Length;
        var dWordCount = bal / 4 + (bal % 4 == 0 ? 0 : 1);
        var arr        = new int[dWordCount];
        Buffer.BlockCopy(ba, 0, arr, 0, bal);
        return arr;
    }
    private static float Entropy(byte[] s)
    {
        float len = s.Length;
        var   map = new int[256];
        for (var i = 0; i < (int) len; i++)
            map[s[i]]++;
        var result = 0f;
        for (var idx = 0; idx < map.Length; idx++)
        {
            var frequency = map[idx] / len;
            if (frequency > 0)
                result -= frequency * (float) Math.Log(frequency, 2);
        }
        return result / 8f * 100f;
    }
    public static int ChiSquaredCount(this byte[] s, byte b)
    {
        float len = s.Length;
        var   map = new int[256];
        for (var i = 0; i < (int) len; i++)
            map[s[i]]++;
        return map[b];
    }
    public static int ChiSquaredCount(this string s, char b)
    {
        float len = s.Length;
        var   map = new int[256];
        for (var i = 0; i < (int) len; i++)
            map[s[i]]++;
        return map[b];
    }
    public static float ChiSquaredAsPercent(this string s, char b)
    {
        float len = s.Length;
        var   map = new int[256];
        for (var i = 0; i < (int) len; i++)
            map[s[i]]++;
        return map[b] / len;
    }
    /// <summary>
    ///     Compute the letter frequencies within the English language.
    ///     Use a large English language text block for accurate testing.
    /// </summary>
    /// <param name="s">String that contains the large English text</param>
    public static KeyValuePair<char, float>[] ChiSquaredTextAsPercent(this string s)
    {
        float len = s.Length;
        s = s.ToLower(CultureInfo.CurrentCulture);
        var lst = new Dictionary<char, float>();
        var map = new int[256];
        for (var i = 0; i < (int) len; i++)
            if (s[i].IsLetter())
                map[s[i]]++;
        var t = map.Sum(e => e);
        foreach (var l in "abcdefghijklmnopqrstuvwxyz")
            lst.Add(l, map[l] / (float) t);
        var klst      = lst.OrderBy(e => e.Key).ToArray();
        var KeyList   = "";
        var ValueList = "";
        foreach (var kv in klst)
        {
            KeyList   += $"{kv.Key},";
            ValueList += $"{kv.Value:.0000},";
        }
        var nlst = lst.OrderBy(e => e.Value).ToArray();
        return nlst;
    }
    public static float ChiSquaredTextTest(this string s)
    {
        var realLen = 0;
        s = s.ToLower(CultureInfo.CurrentCulture);
        var observed = new Dictionary<char, int>();
        foreach (var c in s)
            if (c.IsLetter())
            {
                if (!observed.ContainsKey(c))
                    observed.Add(c, 1);
                else
                    observed[c]++;
                realLen++;
            }
        var expected = new Dictionary<char, float>();
        for (var i = 0; i < 26; i++)
            expected.Add((char) (i + 97), _expectedPercentages[i] * realLen);
        var cSList = new List<float>();
        foreach (var item in expected)
        {
            var c = item.Key;
            if (observed.ContainsKey(c))
                cSList.Add((float) Math.Pow(observed[c] - expected[c], 2) / expected[c]);
        }
        return cSList.Sum(e => e) / realLen * 100f;
    }
    /// <summary>
    ///     The value of 10 as a combined chi-squared total distance percentage threshold is subjective.
    ///     Determined from about 40 test runs. Most non-text files have readings
    ///     in the 100's
    /// </summary>
    /// <param name="path">Path to the file to test</param>
    public static bool IsTextFile(this string path)
    {
        return File.ReadAllText(path).ChiSquaredTextTest() < 10;
    }
    
}

FixedBigInteger.cs

Posted on June 4, 2020June 4, 2020  Leave a comment on FixedBigInteger.cs

Adjustable Fixed Bit Width Signed Integer 32,64,128,256,…

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Text;
[Serializable]
[StructLayout(LayoutKind.Sequential, Pack = 1)]
[TypeConverter(typeof(FixedBigIntegerConverter))]
[DebuggerDisplay("{DDisplay}")]
public class FixedBigInteger : IComparable<FixedBigInteger>, IComparable, IEquatable<FixedBigInteger>, IConvertible, IFormattable, ISerializable
{
    private const          int               DataSize       = sizeof(uint);
    private const          int               DataShiftCount = 5;
    private const          uint              AllBits        = ~(uint) 0;
    private const          int               DataSizeBits   = sizeof(uint) * 8;
    private const          uint              HiNeg          = (uint) 1 << (DataSizeBits - 1);
    private static         int               DataBitWidth;
    private static         int               DataLength;
    public static readonly FixedBigInteger   One   = new FixedBigInteger(1,  DataBitWidth);
    public static readonly FixedBigInteger   Two   = new FixedBigInteger(2,  DataBitWidth);
    public static readonly FixedBigInteger   Zero  = new FixedBigInteger(0,  DataBitWidth);
    public static readonly FixedBigInteger   Ten   = new FixedBigInteger(10, DataBitWidth);
    public static readonly FixedBigInteger   Three = new FixedBigInteger(3,  DataBitWidth);
    private readonly       SerializationInfo SInfo;
    public                 uint[]            Data;
    public FixedBigInteger(FixedBigInteger value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        CalculateMinDataLength(value.Data.Length);
        Data = new uint[DataLength];
        value.Data.CopyTo(Data, 0);
    }
    public FixedBigInteger(string value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        if (!TryParse(value, out var result))
            throw new Exception("TryParse Failed.");
        CalculateMinDataLength(result.Data.Length);
        Data = new uint[DataLength];
        result.Data.CopyTo(Data, 0);
    }
    public FixedBigInteger(byte value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public FixedBigInteger(bool value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        Data[0]      = (uint) (value ? 1 : 0);
    }
    public FixedBigInteger(char value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public FixedBigInteger(BigDecimal value, int bitLength)
    {
        var ba = value.WholePart.ToByteArray();
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        var len = ba.Length / DataSize;
        CalculateMinDataLength(len);
        Data = new uint[DataLength];
        for (var i = 0; i < Data.Length; i++)
            Data[i] = BitConverter.ToUInt32(ba, i * DataSize);
    }
    public FixedBigInteger(decimal value, int bitLength)
    {
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        CalculateMinDataLength(3);
        Data = new uint[DataLength];
        if (value < 0)
        {
            var n = -new FixedBigInteger(-value, DataBitWidth);
            n.Data.CopyTo(Data, 0);
            return;
        }
        var bits = decimal.GetBits(value);
        Data[2] = (uint) bits[2];
        Data[1] = (uint) bits[1];
        Data[0] = (uint) bits[0];
    }
    public FixedBigInteger(double value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public FixedBigInteger(float value, int bitLength) : this((decimal) value, bitLength)
    {
    }
    public FixedBigInteger(short value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new FixedBigInteger(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public FixedBigInteger(int value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new FixedBigInteger(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public FixedBigInteger(long value, int bitLength)
    {
        if (bitLength < 64)
            bitLength = 64;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new FixedBigInteger(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[1] = (uint) ((value >> 32) & 0xffffffff);
        Data[0] = (uint) (value         & 0xffffffff);
    }
    public FixedBigInteger(sbyte value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        if (value < 0)
        {
            var n = -new FixedBigInteger(-(value + 1), DataBitWidth) - 1;
            n.Data.CopyTo(Data, 0);
            return;
        }
        Data[0] = (uint) value;
    }
    public FixedBigInteger(ushort value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public FixedBigInteger(uint value, int bitLength)
    {
        if (bitLength < 32)
            bitLength = 32;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        Data[0]      = value;
    }
    public FixedBigInteger(ulong value, int bitLength)
    {
        if (bitLength < 96)
            bitLength = 96;
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        Data         = new uint[DataLength];
        Data[1]      = (uint) ((value >> 32) & 0xffffffff);
        Data[0]      = (uint) (value         & 0xffffffff);
    }
    public FixedBigInteger(BigInteger value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public FixedBigInteger(Guid value, int bitLength) : this(value.ToByteArray(), bitLength)
    {
    }
    public FixedBigInteger(byte[] value, int bitLength)
    {
        var minSize = value.Length / DataSize;
        if (value == null)
            throw new ArgumentNullException("value");
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        CalculateMinDataLength(minSize);
        var byteCount      = value.Length;
        var isNegative     = byteCount > 0 && (value[byteCount - 1] & 0x80) == 0x80;
        var unalignedBytes = byteCount % DataSize;
        var dwordCount     = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
        Data = new uint[Math.Max(dwordCount, DataLength)];
        if (byteCount == 0)
            return;
        int curDword, curByte, byteInDword;
        curByte = 3;
        for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
        {
            byteInDword = 0;
            while (byteInDword < DataSize)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
                curByte--;
                byteInDword++;
            }
            curByte += 8;
        }
        if (unalignedBytes != 0)
        {
            if (isNegative)
                Data[dwordCount - 1] = 0xffffffff;
            for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  value[curByte];
            }
        }
    }
    public FixedBigInteger(int sign, uint[] array, int bitLength)
    {
        if (array == null)
            throw new Exception("Array cannot be null.");
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        CalculateMinDataLength(array.Length);
        if (array.Length != DataLength)
            Array.Resize(ref array, DataLength);
        Data = new uint[DataLength];
        var ba = new byte[DataSize];
        for (var i = 0; i < Data.Length; i++)
        {
            Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
            Data[i] = BitConverter.ToUInt32(ba, 0);
        }
        if (sign < 0)
            Data[DataLength - 1] |= HiNeg;
        else
            Data[DataLength - 1] &= ~HiNeg;
    }
    public FixedBigInteger(uint[] array, int bitLength)
    {
        if (array == null)
            throw new Exception("Array cannot be null.");
        DataBitWidth = bitLength;
        DataLength   = DataBitWidth >> DataShiftCount;
        if (array.Length != DataLength)
            Array.Resize(ref array, DataLength);
        Data = new uint[DataLength];
        var ba = new byte[DataSize];
        for (var i = 0; i < Data.Length; i++)
        {
            Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
            Data[i] = BitConverter.ToUInt32(ba, 0);
        }
    }
    protected FixedBigInteger(SerializationInfo info, StreamingContext context)
    {
        SInfo = info;
    }
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private string DDisplay => ToString();
    public FixedBigInteger MaxValue
    {
        get
        {
            var r = new FixedBigInteger(0, DataBitWidth);
            for (var i = 0; i < r.Data.Length; ++i)
                r.Data[i] = uint.MaxValue;
            r.Data[r.Data.Length - 1] = int.MaxValue;
            return r;
        }
    }
    public int BitWidth
    {
        get
        {
            FixedBigInteger bw = 1;
            var             v  = new FixedBigInteger(this, 0);
            while ((v >>= 1) > 0)
                bw++;
            if (bw < 8)
                bw = 8;
            while (bw % 8 != 0)
                bw++;
            return (int) bw;
        }
    }
    public int Sign
    {
        get
        {
            var allZero = true;
            var ba      = Data;
            for (var i = 0; i < ba.Length; i++)
                if (ba[i] != 0)
                {
                    allZero = false;
                    break;
                }
            if (allZero)
                return 0;
            return (Data[Data.Length - 1] & HiNeg) == 0 ? 1 : -1;
        }
    }
    public bool IsOne      => this       == 1;
    public bool IsEven     => (this & 1) == 0;
    public bool IsNegative => Sign       < 0;
    public bool IsZero
    {
        get
        {
            for (var i = 0; i < Data.Length; i++)
                if (Data[i] != 0)
                    return false;
            return true;
        }
    }
    public int DataUsed
    {
        get
        {
            var DataUsed = Data.Length;
            if (!IsNegative)
            {
                while (DataUsed > 1 && Data[DataUsed - 1] == 0)
                    --DataUsed;
                if (DataUsed == 0)
                    DataUsed = 1;
            }
            return DataUsed;
        }
    }
    int IComparable.CompareTo(object obj)
    {
        return Compare(this, obj);
    }
    public int CompareTo(FixedBigInteger value)
    {
        return Compare(this, value);
    }
    TypeCode IConvertible.GetTypeCode()
    {
        return TypeCode.Object;
    }
    bool IConvertible.ToBoolean(IFormatProvider provider)
    {
        return (bool) this;
    }
    byte IConvertible.ToByte(IFormatProvider provider)
    {
        return (byte) this;
    }
    char IConvertible.ToChar(IFormatProvider provider)
    {
        return (char) this;
    }
    DateTime IConvertible.ToDateTime(IFormatProvider provider)
    {
        throw new InvalidCastException();
    }
    decimal IConvertible.ToDecimal(IFormatProvider provider)
    {
        return (decimal) this;
    }
    double IConvertible.ToDouble(IFormatProvider provider)
    {
        return (double) this;
    }
    short IConvertible.ToInt16(IFormatProvider provider)
    {
        return (short) this;
    }
    int IConvertible.ToInt32(IFormatProvider provider)
    {
        return (int) this;
    }
    long IConvertible.ToInt64(IFormatProvider provider)
    {
        return (long) this;
    }
    sbyte IConvertible.ToSByte(IFormatProvider provider)
    {
        return (sbyte) this;
    }
    float IConvertible.ToSingle(IFormatProvider provider)
    {
        return (float) this;
    }
    string IConvertible.ToString(IFormatProvider provider)
    {
        return ToString(null, provider);
    }
    public object ToType(Type conversionType, IFormatProvider provider)
    {
        object value;
        if (TryConvert(conversionType, provider, out value))
            return value;
        throw new InvalidCastException();
    }
    ushort IConvertible.ToUInt16(IFormatProvider provider)
    {
        if (Data[1] != 0)
            throw new OverflowException();
        return Convert.ToUInt16(Data[0]);
    }
    uint IConvertible.ToUInt32(IFormatProvider provider)
    {
        if (Data[1] != 0)
            throw new OverflowException();
        return Convert.ToUInt32(Data[0]);
    }
    ulong IConvertible.ToUInt64(IFormatProvider provider)
    {
        if (Data[1] != 0)
            return ((ulong) Data[1] << 32) | Data[0];
        return Data[0];
    }
    public bool Equals(FixedBigInteger obj)
    {
        if (ReferenceEquals(obj, null))
            return false;
        if (ReferenceEquals(this, obj))
            return true;
        if (Data.Length != obj.Data.Length)
        {
            var len = Math.Max(Data.Length, obj.Data.Length);
            if (Data.Length < len)
            {
                var tData = new uint[len];
                Array.Copy(Data, 0, tData, 0, Data.Length);
                Data = tData;
            }
            if (obj.Data.Length < len)
                Resize(ref obj, len);
        }
        if (Sign != obj.Sign)
            return false;
        for (var i = 0; i < Data.Length; i++)
            if (Data[i] != obj.Data[i])
                return false;
        return true;
    }
    public string ToString(string format, IFormatProvider formatProvider)
    {
        if (formatProvider == null)
            formatProvider = CultureInfo.CurrentCulture;
        if (!string.IsNullOrEmpty(format))
        {
            var ch = format[0];
            if (ch == 'x' || ch == 'X')
            {
                int.TryParse(format.Substring(1).Trim(), out var min);
                return ToHexString(ch == 'X');
            }
            if (ch != 'G' && ch != 'g' && ch != 'D' && ch != 'd')
                throw new NotSupportedException("Not supported format: " + format);
        }
        return ToString((NumberFormatInfo) formatProvider.GetFormat(typeof(NumberFormatInfo)), 10);
    }
    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("Bits", DataBitWidth);
        info.AddValue("Data", Data, typeof(uint[]));
    }
    private static void CalculateMinDataLength(int minSize)
    {
        if (minSize > DataLength)
        {
            DataBitWidth = 32 * minSize;
            DataLength   = minSize;
        }
    }
    public void OnDeserialization(object sender)
    {
        if (SInfo == null)
            return;
        DataBitWidth = SInfo.GetInt32("Bits");
        if (DataBitWidth != 0)
        {
            DataLength = DataBitWidth >> DataShiftCount;
            var array = (uint[]) SInfo.GetValue("Data", typeof(uint[]));
            if (array == null)
                throw new Exception("Array cannot be null.");
            if (array.Length != DataLength)
                Array.Resize(ref array, DataLength);
            Data = new uint[DataLength];
            var ba = new byte[4];
            for (var i = 0; i < DataLength; i++)
            {
                Array.Copy(BitConverter.GetBytes(array[i]), 0, ba, 0, DataSize);
                Data[i] = BitConverter.ToUInt32(ba, 0);
            }
        }
    }
    public static int GetSign(uint[] value)
    {
        var allZero = true;
        for (var i = 0; i < value.Length; i++)
            if (value[i] != 0)
            {
                allZero = false;
                break;
            }
        if (allZero)
            return 0;
        return (value[value.Length - 1] & HiNeg) == 0 ? 1 : -1;
    }
    private static int GetDataUsed(uint[] array)
    {
        var neg      = GetSign(array) < 0;
        var DataUsed = array.Length;
        if (!neg)
        {
            while (DataUsed > 1 && array[DataUsed - 1] == 0)
                --DataUsed;
            if (DataUsed == 0)
                DataUsed = 1;
        }
        return DataUsed;
    }
    public int GetDecimalPlaces()
    {
        var dPlaces = 0;
        if (Sign == 0)
            return 1;
        var a = new FixedBigInteger(this, 0);
        if (Sign < 0)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
                return 0;
            }
        var biRadix = new FixedBigInteger(10, DataBitWidth);
        while (a > 0)
            try
            {
                Divide(a, biRadix, out var remainder, out var quotient);
                a = quotient;
                dPlaces++;
            }
            catch (Exception ex)
            {
                break;
            }
        return dPlaces;
    }
    private uint[] TwosComplement(uint[] d)
    {
        var  i = 0;
        uint v = 0;
        for (; i < d.Length; i++)
        {
            v    = ~d[i] + 1;
            d[i] = v;
            if (v != 0)
            {
                i++;
                break;
            }
        }
        if (v != 0)
        {
            for (; i < d.Length; i++)
                d[i] = ~d[i];
        }
        else
        {
            Array.Resize(ref d, d.Length + 1);
            d[d.Length - 1] = 1;
        }
        return d;
    }
    public void ConstructFromArray(byte[] array)
    {
        if (array == null)
            throw new ArgumentNullException("value");
        var byteCount      = array.Length;
        var isNegative     = byteCount > 0 && (array[byteCount - 1] & 0x80) == 0x80;
        var unalignedBytes = byteCount % DataSize;
        var dwordCount     = byteCount / DataSize + (unalignedBytes == 0 ? 0 : 1);
        Data = new uint[Math.Max(dwordCount, DataLength)];
        if (byteCount == 0)
            return;
        int curDword, curByte, byteInDword;
        curByte = 3;
        for (curDword = 0; curDword < dwordCount - (unalignedBytes == 0 ? 0 : 1); curDword++)
        {
            byteInDword = 0;
            while (byteInDword < DataSize)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  array[curByte];
                curByte--;
                byteInDword++;
            }
            curByte += 8;
        }
        if (unalignedBytes != 0)
        {
            if (isNegative)
                Data[dwordCount - 1] = 0xffffffff;
            for (curByte = byteCount - 1; curByte >= byteCount - unalignedBytes; curByte--)
            {
                Data[curDword] <<= 8;
                Data[curDword] |=  array[curByte];
            }
        }
    }
    private static byte[] ToByteArray(ulong[] value)
    {
        var ba = new byte[value.Length << 3];
        Buffer.BlockCopy(value, 0, ba, 0, value.Length << 3);
        return ba;
    }
    private static byte[] ToByteArray(uint[] value)
    {
        var ba = new byte[value.Length << 2];
        Buffer.BlockCopy(value, 0, ba, 0, value.Length << 2);
        return ba;
    }
    public override int GetHashCode()
    {
        var hash = 0x811c9dc5;
        for (var i = 0; i < Data.Length; i++)
        {
            hash ^= ((hash << 13) | (hash >> 19)) ^ Data[i];
            hash *= 0x1000193;
        }
        return (int) hash;
    }
    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override string ToString()
    {
        return ToString(null, null);
    }
    public string ToString(string format)
    {
        return ToString(format, null);
    }
    public string ToHexString(bool caps)
    {
        var bytes = ToByteArray().Invert();
        var sb    = new StringBuilder();
        var x     = caps ? "X" : "x";
        foreach (var b in bytes)
        {
            var hex = b.ToString($"{x}2");
            sb.Append(hex);
        }
        return sb.ToString();
    }
    private string ToString(NumberFormatInfo info, int radix)
    {
        if (radix < 2 || radix > 36)
            throw new ArgumentOutOfRangeException("radix");
        if (Sign == 0)
            return "0";
        var negative = Sign < 0;
        var a        = new FixedBigInteger(this, 0);
        if (negative)
            try
            {
                a = -a;
            }
            catch (Exception ex)
            {
            }
        var          biRadix = new FixedBigInteger(radix, DataBitWidth);
        const string charSet = "0123456789abcdefghijklmnopqrstuvwxyz";
        var          al      = new ArrayList();
        while (a > 0)
            try
            {
                Divide(a, biRadix, out var remainder, out var quotient);
                al.Insert(0, charSet[(int) remainder.Data[0]]);
                a = quotient;
            }
            catch (Exception ex)
            {
                break;
            }
        var result = new string((char[]) al.ToArray(typeof(char)));
        if (radix == 10 && negative)
            return "-" + result;
        return result;
    }
    public static FixedBigInteger Abs(FixedBigInteger value)
    {
        if (ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        if (value.Sign < 0)
            return -value;
        return value;
    }
    public bool TryConvert(Type conversionType, IFormatProvider provider, out object value)
    {
        if (conversionType == typeof(bool))
        {
            value = (bool) this;
            return true;
        }
        if (conversionType == typeof(byte))
        {
            value = (byte) this;
            return true;
        }
        if (conversionType == typeof(char))
        {
            value = (char) this;
            return true;
        }
        if (conversionType == typeof(decimal))
        {
            value = (decimal) this;
            return true;
        }
        if (conversionType == typeof(double))
        {
            value = (double) this;
            return true;
        }
        if (conversionType == typeof(short))
        {
            value = (short) this;
            return true;
        }
        if (conversionType == typeof(int))
        {
            value = (int) this;
            return true;
        }
        if (conversionType == typeof(long))
        {
            value = (long) this;
            return true;
        }
        if (conversionType == typeof(sbyte))
        {
            value = (sbyte) this;
            return true;
        }
        if (conversionType == typeof(float))
        {
            value = (float) this;
            return true;
        }
        if (conversionType == typeof(string))
        {
            value = ToString(null, provider);
            return true;
        }
        if (conversionType == typeof(ushort))
        {
            value = (ushort) this;
            return true;
        }
        if (conversionType == typeof(uint))
        {
            value = (uint) this;
            return true;
        }
        if (conversionType == typeof(ulong))
        {
            value = (ulong) this;
            return true;
        }
        if (conversionType == typeof(byte[]))
        {
            value = ToByteArray();
            return true;
        }
        if (conversionType == typeof(Guid))
        {
            value = new Guid(ToByteArray());
            return true;
        }
        value = null;
        return false;
    }
    public static FixedBigInteger Parse(string value)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
    }
    public static FixedBigInteger Parse(string value, NumberStyles style)
    {
        return Parse(value, style, NumberFormatInfo.CurrentInfo);
    }
    public static FixedBigInteger Parse(string value, IFormatProvider provider)
    {
        return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
    }
    public static FixedBigInteger Parse(string value, NumberStyles style, IFormatProvider provider)
    {
        if (!TryParse(value, style, provider, out var result))
            throw new Exception($"TryParse value {value} failure.");
        return result;
    }
    public static bool TryParse(string value, out FixedBigInteger result)
    {
        return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result);
    }
    public static bool TryParse(string value, NumberStyles style, IFormatProvider provider, out FixedBigInteger result)
    {
        result = 0;
        if (string.IsNullOrEmpty(value))
            return false;
        if (value.StartsWith("x", StringComparison.OrdinalIgnoreCase))
        {
            style |= NumberStyles.AllowHexSpecifier;
            value =  value.Substring(1);
        }
        else
        {
            if (value.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
            {
                style |= NumberStyles.AllowHexSpecifier;
                value =  value.Substring(2);
            }
        }
        if ((style & NumberStyles.AllowHexSpecifier) == NumberStyles.AllowHexSpecifier)
            return TryParseNum(value, 16, out result);
        return TryParseNum(value, 10, out result);
    }
    public static bool TryParseNum(string digits, int radix, out FixedBigInteger result)
    {
        result = new FixedBigInteger(0, DataBitWidth);
        if (digits == null)
            return false;
        var multiplier = new FixedBigInteger(1, DataBitWidth);
        digits = digits.ToUpper(CultureInfo.CurrentCulture).Trim();
        var nDigits = digits[0] == '-' ? 1 : 0;
        for (var idx = digits.Length - 1; idx >= nDigits; idx--)
        {
            var d = (int) digits[idx];
            if (d >= '0' && d <= '9')
                d -= '0';
            else if (d >= 'A' && d <= 'Z')
                d = d - 'A' + 10;
            else
                return false;
            if (d >= radix)
                return false;
            result     += multiplier * d;
            multiplier *= radix;
        }
        if (digits[0] == '-')
            result = -result;
        return true;
    }
    public static int Compare(FixedBigInteger left, object right)
    {
        if (right is FixedBigInteger)
            return Compare(left, (FixedBigInteger) right);
        if (right is bool)
            return Compare(left, new FixedBigInteger((bool) right, DataBitWidth));
        if (right is byte)
            return Compare(left, new FixedBigInteger((byte) right, DataBitWidth));
        if (right is char)
            return Compare(left, new FixedBigInteger((char) right, DataBitWidth));
        if (right is decimal)
            return Compare(left, new FixedBigInteger((decimal) right, DataBitWidth));
        if (right is double)
            return Compare(left, new FixedBigInteger((double) right, DataBitWidth));
        if (right is short)
            return Compare(left, new FixedBigInteger((short) right, DataBitWidth));
        if (right is int)
            return Compare(left, new FixedBigInteger((int) right, DataBitWidth));
        if (right is long)
            return Compare(left, new FixedBigInteger((long) right, DataBitWidth));
        if (right is sbyte)
            return Compare(left, new FixedBigInteger((sbyte) right, DataBitWidth));
        if (right is float)
            return Compare(left, new FixedBigInteger((float) right, DataBitWidth));
        if (right is ushort)
            return Compare(left, new FixedBigInteger((ushort) right, DataBitWidth));
        if (right is uint)
            return Compare(left, new FixedBigInteger((uint) right, DataBitWidth));
        if (right is ulong)
            return Compare(left, new FixedBigInteger((ulong) right, DataBitWidth));
        var bytes = right as byte[];
        if (bytes != null)
            return Compare(left, new FixedBigInteger(bytes, DataBitWidth));
        if (right is Guid)
            return Compare(left, new FixedBigInteger((Guid) right, DataBitWidth));
        throw new ArgumentException();
    }
    public static int Compare(FixedBigInteger left, FixedBigInteger right)
    {
        MakeLikeLengths(ref left, ref right);
        if (ReferenceEquals(left, right))
            return 0;
        if (left.Sign >= 0 && right.Sign < 0)
            return 1;
        if (left.Sign < 0 && right.Sign >= 0)
            return -1;
        for (var i = left.Data.Length - 1; i > 0; i--)
            if (left.Data[i] != right.Data[i])
                return left.Data[i].CompareTo(right.Data[i]);
        return left.Data[0].CompareTo(right.Data[0]);
    }
    public static implicit operator FixedBigInteger(bool value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(byte value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(char value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static explicit operator FixedBigInteger(decimal value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static explicit operator FixedBigInteger(double value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(short value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(int value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(long value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(sbyte value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static explicit operator FixedBigInteger(float value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(ushort value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(uint value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(ulong value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(BigInteger value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static implicit operator FixedBigInteger(BigDecimal value)
    {
        return new FixedBigInteger(value, DataBitWidth);
    }
    public static explicit operator bool(FixedBigInteger value)
    {
        return (byte) value.Data[0] != 0;
    }
    public static explicit operator byte(FixedBigInteger value)
    {
        return (byte) value.Data[0];
    }
    public static explicit operator char(FixedBigInteger value)
    {
        return (char) (ushort) value.Data[0];
    }
    public static explicit operator decimal(FixedBigInteger value)
    {
        if (value.Sign == 0)
            return 0;
        if (value.Data.Length == 1)
            return new decimal((int) value.Data[0], 0, 0, value.Sign < 0, 0);
        if (value.Data.Length == 2)
            return new decimal((int) value.Data[0], (int) value.Data[1], 0, value.Sign < 0, 0);
        if (value.Data.Length == 3)
            return new decimal((int) value.Data[0], (int) value.Data[1], (int) value.Data[2], value.Sign < 0, 0);
        throw new ArgumentException("Value length exceeds decimal length.");
    }
    public static explicit operator double(FixedBigInteger value)
    {
        if (value.Sign == 0)
            return 0;
        var nfi = CultureInfo.InvariantCulture.NumberFormat;
        if (!double.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var d))
            throw new OverflowException();
        return d;
    }
    public static explicit operator float(FixedBigInteger value)
    {
        if (value.Sign == 0)
            return 0;
        var nfi = CultureInfo.InvariantCulture.NumberFormat;
        if (!float.TryParse(value.ToString(nfi, 10), NumberStyles.Number, nfi, out var f))
            throw new OverflowException();
        return f;
    }
    public static explicit operator short(FixedBigInteger value)
    {
        if (value.Data[0] > 0x8000)
            throw new OverflowException();
        if (value.Data[0] == 0x8000 && value.Sign > 0)
            throw new OverflowException();
        return (short) ((int) value.Data[0] * value.Sign);
    }
    public static explicit operator int(FixedBigInteger value)
    {
        if (value.Sign == 0)
            return 0;
        return (int) value.Data[0] * value.Sign;
    }
    public static explicit operator long(FixedBigInteger value)
    {
        if (value.Sign == 0)
            return 0;
        if (value.Data[0] > int.MaxValue)
            throw new OverflowException();
        if (value.Data.Length > 1)
            if (value.Data[1] != 0)
                return (long) (((ulong) value.Data[1] << 32) | value.Data[0]) * value.Sign;
        return value.Data[0] * value.Sign;
    }
    public static explicit operator uint(FixedBigInteger value)
    {
        if (value.Sign == 0)
            return 0;
        return value.Data[0];
    }
    public static explicit operator ushort(FixedBigInteger value)
    {
        if (value.Sign == 0)
            return 0;
        return (ushort) value.Data[0];
    }
    public static explicit operator ulong(FixedBigInteger value)
    {
        if (value.Data.Length > 1)
            if (value.Data[1] != 0)
                return ((ulong) value.Data[1] << 32) | value.Data[0];
        return value.Data[0];
    }
    public static explicit operator BigInteger(FixedBigInteger value)
    {
        return new BigInteger(value.ToByteArray());
    }
    public static bool operator >(FixedBigInteger left, FixedBigInteger right)
    {
        return left.CompareTo(right) > 0;
    }
    private static void MakeLikeLengths(ref FixedBigInteger left, ref FixedBigInteger right)
    {
        if (left.Data.Length != right.Data.Length)
        {
            var len = Math.Max(left.Data.Length, right.Data.Length);
            Resize(ref left,  len);
            Resize(ref right, len);
        }
    }
    private static void Resize(ref FixedBigInteger value, int newSize)
    {
        var IsNeg = value.IsNegative;
        var nData = new uint[newSize];
        var len   = value.Data.Length;
        for (var i = 0; i < len; i++)
        {
            nData[i] = value.Data[i];
            if (IsNeg && i == len - 1)
                nData[i] &= ~HiNeg;
        }
        if (IsNeg)
            nData[nData.Length - 1] |= HiNeg;
        value.Data = (uint[]) nData.Clone();
    }
    public static bool operator <(FixedBigInteger left, FixedBigInteger right)
    {
        return Compare(left, right) < 0;
    }
    public static bool operator >=(FixedBigInteger left, FixedBigInteger right)
    {
        return Compare(left, right) >= 0;
    }
    public static bool operator <=(FixedBigInteger left, FixedBigInteger right)
    {
        return Compare(left, right) <= 0;
    }
    public static bool operator !=(FixedBigInteger left, FixedBigInteger right)
    {
        return !left.Equals(right);
    }
    public static bool operator ==(FixedBigInteger left, FixedBigInteger right)
    {
        return left.Equals(right);
    }
    public static FixedBigInteger operator +(FixedBigInteger value)
    {
        return value;
    }
    public static FixedBigInteger operator ~(FixedBigInteger value)
    {
        var da = new uint[DataLength];
        for (var idx = 0; idx < DataLength; idx++)
            da[idx] = ~value.Data[idx];
        return new FixedBigInteger(da, DataBitWidth);
    }
    public static FixedBigInteger operator -(FixedBigInteger value)
    {
        if (ReferenceEquals(value, null))
            throw new ArgumentNullException("value");
        if (value.IsZero)
            return Zero;
        var da = new uint[DataLength];
        for (var i = 0; i < da.Length; i++)
            da[i] = ~value.Data[i];
        var carry = true;
        var index = 0;
        while (carry && index < da.Length)
        {
            var val = (long) da[index] + 1;
            da[index] = (uint) (val & AllBits);
            carry     = val >> DataSizeBits > 0;
            index++;
        }
        return new FixedBigInteger(da, DataBitWidth);
    }
    public static FixedBigInteger operator ++(FixedBigInteger value)
    {
        return value + 1;
    }
    public static FixedBigInteger operator --(FixedBigInteger value)
    {
        return value - 1;
    }
    public static FixedBigInteger Negate(FixedBigInteger value)
    {
        var ldata = (uint[]) value.Data.Clone();
        for (var i = 0; i < value.Data.Length; i++)
            ldata[i] = ~value.Data[i];
        return new FixedBigInteger(value.Sign, ldata, DataBitWidth);
    }
    public static FixedBigInteger operator +(FixedBigInteger left, FixedBigInteger right)
    {
        if (right.IsZero)
            return left;
        if (left.IsZero)
            return right;
        MakeLikeLengths(ref left, ref right);
        var  dl     = left.Data.Length > right.Data.Length ? left.Data.Length : right.Data.Length;
        var  result = new uint[dl];
        long carry  = 0;
        for (var i = 0; i < dl; i++)
        {
            var sum = left.Data[i] + (long) right.Data[i] + carry;
            carry     = sum >> 32;
            result[i] = (uint) (sum & 0xFFFFFFFF);
        }
        if (carry != 0)
        {
            var idx = 0;
            while (idx < result.Length - 1)
            {
                if (result[idx] == 0)
                    break;
                idx++;
            }
            result[idx] = (uint) carry;
        }
        return new FixedBigInteger(left.Sign * right.Sign, result, DataBitWidth);
    }
    public static FixedBigInteger operator -(FixedBigInteger left, FixedBigInteger right)
    {
        if (right.IsZero)
            return left;
        if (left.IsZero)
            return -right;
        MakeLikeLengths(ref left, ref right);
        var  da    = new uint[DataLength];
        long carry = 0;
        for (var i = 0; i < DataLength && i < left.Data.Length && i < right.Data.Length; i++)
        {
            var diff = left.Data[i] - (long) right.Data[i] - carry;
            da[i] = (uint) (diff & AllBits);
            carry = diff < 0 ? 1 : 0;
        }
        return new FixedBigInteger(da, DataBitWidth);
    }
    public static FixedBigInteger Add(FixedBigInteger left, FixedBigInteger right)
    {
        return left + right;
    }
    public static FixedBigInteger Subtract(FixedBigInteger left, FixedBigInteger right)
    {
        return left - right;
    }
    public static FixedBigInteger Divide(FixedBigInteger dividend, FixedBigInteger divisor)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        return DivRem(dividend, divisor, out var integer);
    }
    public static void Divide(FixedBigInteger dividend, FixedBigInteger divisor, out FixedBigInteger remainder, out FixedBigInteger quotient)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quo, out var rem);
        remainder = new FixedBigInteger(1,                            rem, DataBitWidth);
        quotient  = new FixedBigInteger(dividend.Sign * divisor.Sign, quo, DataBitWidth);
    }
    public static FixedBigInteger DivRem(FixedBigInteger dividend, FixedBigInteger divisor, out FixedBigInteger remainder)
    {
        if (divisor == 0)
            throw new DivideByZeroException();
        DivRem(dividend.Data, divisor.Data, out var quotient, out var rem);
        remainder = new FixedBigInteger(1, rem, DataBitWidth);
        return new FixedBigInteger(dividend.Sign * divisor.Sign, quotient, DataBitWidth);
    }
    private static void DivRem(uint[] dividend, uint[] divisor, out uint[] quotient, out uint[] remainder)
    {
        const ulong hiBit       = 0x100000000;
        var         divisorLen  = GetLength(divisor);
        var         dividendLen = GetLength(dividend);
        if (divisorLen <= 1)
        {
            ulong rem = 0;
            var   div = divisor[0];
            quotient  = new uint[dividendLen];
            remainder = new uint[1];
            for (var i = dividendLen - 1; i >= 0; i--)
            {
                rem *= hiBit;
                rem += dividend[i];
                var q = rem / div;
                rem         -= q * div;
                quotient[i] =  (uint) q;
            }
            remainder[0] = (uint) rem;
            return;
        }
        if (dividendLen >= divisorLen)
        {
            var shift        = GetNormalizeShift(divisor[divisorLen - 1]);
            var normDividend = new uint[dividendLen + 1];
            var normDivisor  = new uint[divisorLen];
            Normalize(dividend, dividendLen, normDividend, shift);
            Normalize(divisor,  divisorLen,  normDivisor,  shift);
            quotient = new uint[dividendLen - divisorLen + 1];
            for (var j = dividendLen - divisorLen; j >= 0; j--)
            {
                var dx = hiBit * normDividend[j + divisorLen] + normDividend[j + divisorLen - 1];
                var qj = dx / normDivisor[divisorLen                                        - 1];
                dx -= qj * normDivisor[divisorLen - 1];
                do
                {
                    if (qj < hiBit && qj * normDivisor[divisorLen - 2] <= dx * hiBit + normDividend[j + divisorLen - 2])
                        break;
                    qj -= 1L;
                    dx += normDivisor[divisorLen - 1];
                } while (dx < hiBit);
                ulong di = 0;
                ulong dj;
                var   index = 0;
                while (index < divisorLen)
                {
                    var dqj = normDivisor[index] * qj;
                    dj                      = normDividend[index + j] - (uint) dqj - di;
                    normDividend[index + j] = (uint) dj;
                    dqj                     = dqj >> 32;
                    dj                      = dj  >> 32;
                    di                      = dqj - dj;
                    index++;
                }
                dj                           = normDividend[j + divisorLen] - di;
                normDividend[j + divisorLen] = (uint) dj;
                quotient[j]                  = (uint) qj;
                if ((long) dj < 0)
                {
                    quotient[j]--;
                    ulong sum = 0;
                    for (index = 0; index < divisorLen; index++)
                    {
                        sum                     = normDivisor[index] + normDividend[j + index] + sum;
                        normDividend[j + index] = (uint) sum;
                        sum                     = sum >> 32;
                    }
                    sum += normDividend[j + divisorLen];
                    normDividend[j        + divisorLen] = (uint) sum;
                }
            }
            remainder = Unnormalize(normDividend, shift);
            return;
        }
        quotient  = new uint[1];
        remainder = dividend;
    }
    private static int GetLength(uint[] uints)
    {
        var index = uints.Length - 1;
        while (index >= 0 && uints[index] == 0)
            index--;
        return index + 1;
    }
    private static int GetNormalizeShift(uint ui)
    {
        var shift = 0;
        if ((ui & 0xffff0000) == 0)
        {
            ui    =  ui << 16;
            shift += 16;
        }
        if ((ui & 0xff000000) == 0)
        {
            ui    =  ui << 8;
            shift += 8;
        }
        if ((ui & 0xf0000000) == 0)
        {
            ui    =  ui << 4;
            shift += 4;
        }
        if ((ui & 0xc0000000) == 0)
        {
            ui    =  ui << 2;
            shift += 2;
        }
        if ((ui & 0x80000000) == 0)
            shift++;
        return shift;
    }
    private static uint[] Unnormalize(uint[] normalized, int shift)
    {
        var len          = GetLength(normalized);
        var unnormalized = new uint[len];
        if (shift > 0)
        {
            var  rshift = 32 - shift;
            uint r      = 0;
            for (var i = len - 1; i >= 0; i--)
            {
                unnormalized[i] = (normalized[i] >> shift) | r;
                r               = normalized[i] << rshift;
            }
        }
        else
        {
            for (var j = 0; j < len; j++)
                unnormalized[j] = normalized[j];
        }
        return unnormalized;
    }
    private static void Normalize(uint[] unormalized, int len, uint[] normalized, int shift)
    {
        int  i;
        uint n = 0;
        if (shift > 0)
        {
            var rShift = 32 - shift;
            for (i = 0; i < len; i++)
            {
                normalized[i] = (unormalized[i] << shift) | n;
                n             = unormalized[i] >> rShift;
            }
        }
        else
        {
            i = 0;
            while (i < len)
            {
                normalized[i] = unormalized[i];
                i++;
            }
        }
        while (i < normalized.Length)
            normalized[i++] = 0;
        if (n != 0)
            normalized[len] = n;
    }
    public static FixedBigInteger Remainder(FixedBigInteger dividend, FixedBigInteger divisor)
    {
        DivRem(dividend, divisor, out var remainder);
        return remainder;
    }
    public static FixedBigInteger Max(FixedBigInteger left, FixedBigInteger right)
    {
        return left.CompareTo(right) < 0 ? right : left;
    }
    public static FixedBigInteger Min(FixedBigInteger left, FixedBigInteger right)
    {
        return left.CompareTo(right) <= 0 ? left : right;
    }
    public static int GetBitWidth(FixedBigInteger n)
    {
        FixedBigInteger bw = 1;
        var             v  = n;
        while ((v >>= 1) > 0)
            bw++;
        if (bw < 8)
            bw = 8;
        while (bw % 8 != 0)
            bw++;
        return (int) bw;
    }
    public static int GetBitWidth<T>(T n)
    {
        ulong   bw = 1;
        dynamic v  = new FixedBigInteger((dynamic) n, 0);
        while ((v >>= 1) > 0)
            bw++;
        if (bw < 8)
            bw = 8;
        while (bw % 8 != 0)
            bw++;
        return (int) bw;
    }
    public static FixedBigInteger operator %(FixedBigInteger dividend, FixedBigInteger divisor)
    {
        return Remainder(dividend, divisor);
    }
    public static FixedBigInteger operator /(FixedBigInteger dividend, FixedBigInteger divisor)
    {
        return Divide(dividend, divisor);
    }
    public ulong[] ToUIn64Array()
    {
        var al = Data.Length >> 1;
        if (al * 2 != Data.Length)
            al++;
        var arr = new ulong[al];
        Buffer.BlockCopy(Data, 0, arr, 0, Data.Length << 2);
        return arr;
    }
    public uint[] ToUIn32Array()
    {
        return Data;
    }
    public byte[] ToByteArray()
    {
        var ba = new byte[DataUsed * DataSize];
        Buffer.BlockCopy(Data, 0, ba, 0, DataUsed * DataSize);
        return ba;
    }
    private void TrimToMsb()
    {
        var dataUsed = Data.Length;
        while (dataUsed > 1 && Data[dataUsed - 1] == 0)
            --dataUsed;
        if (dataUsed != Data.Length)
        {
            var tData = new uint[dataUsed];
            for (var i = 0; i < dataUsed; i++)
                tData[i] = Data[i];
            Data = (uint[]) tData.Clone();
        }
    }
    public static FixedBigInteger Multiply(FixedBigInteger left, FixedBigInteger right)
    {
        if (left == 0 || right == 0)
            return Zero;
        if (left == 1 && right != 1)
            return right;
        if (left != 1 && right == 1)
            return left;
        if (left == 1 && right == 1)
            return One;
        var xInts   = left.Data;
        var yInts   = right.Data;
        var mulInts = new uint[Math.Max(xInts.Length, yInts.Length) << 1];
        for (var i = 0; i < xInts.Length; i++)
        {
            var   index     = i;
            ulong remainder = 0;
            foreach (var yi in yInts)
            {
                remainder        = remainder + (ulong) xInts[i] * yi + mulInts[index];
                mulInts[index++] = (uint) remainder;
                remainder        = remainder >> 32;
            }
            while (remainder != 0)
            {
                remainder        += mulInts[index];
                mulInts[index++] =  (uint) remainder;
                remainder        =  remainder >> 32;
            }
        }
        var du = GetDataUsed(mulInts);
        Array.Resize(ref mulInts, du);
        return new FixedBigInteger(left.Sign * right.Sign, mulInts, 0);
    }
    public static FixedBigInteger operator *(FixedBigInteger left, FixedBigInteger right)
    {
        return Multiply(left, right);
    }
    public static FixedBigInteger operator >>(FixedBigInteger value, int shift)
    {
        if (shift == 0)
            return value;
        if (shift == int.MinValue)
            return value << int.MaxValue << 1;
        if (shift < 0)
            return value << -shift;
        var xd          = value.Data;
        var shiftAmount = 32;
        var invShift    = 0;
        var bufLen      = xd.Length;
        while (bufLen > 1 && xd[bufLen - 1] == 0)
            bufLen--;
        for (var count = shift; count > 0; count -= shiftAmount)
        {
            if (count < shiftAmount)
            {
                shiftAmount = count;
                invShift    = 32 - shiftAmount;
            }
            ulong carry = 0;
            for (var i = bufLen - 1; i >= 0; i--)
            {
                var val = (ulong) xd[i] >> shiftAmount;
                val   |= carry;
                carry =  (ulong) xd[i] << invShift;
                xd[i] =  (uint) val;
            }
        }
        return new FixedBigInteger(value.Sign, xd, DataBitWidth);
    }
    public static FixedBigInteger operator <<(FixedBigInteger value, int shift)
    {
        if (shift == 0)
            return value;
        if (shift == int.MinValue)
            return value >> int.MaxValue >> 1;
        if (shift < 0)
            return value >> -shift;
        var digitShift = shift / 32;
        var smallShift = shift - digitShift * 32;
        var xd         = value.Data;
        var xl         = xd.Length;
        var zd         = new uint[xl + digitShift + 1];
        if (smallShift == 0)
        {
            for (var index = 0; index < xl; ++index)
                zd[index + digitShift] = xd[index];
        }
        else
        {
            var  carryShift = 32 - smallShift;
            uint carry      = 0;
            int  index;
            for (index = 0; index < xl; ++index)
            {
                var rot = xd[index];
                zd[index + digitShift] = (rot << smallShift) | carry;
                carry                  = rot >> carryShift;
            }
            zd[index + digitShift] = carry;
        }
        return new FixedBigInteger(value.Sign, zd, DataBitWidth);
    }
    public static FixedBigInteger operator |(FixedBigInteger left, FixedBigInteger right)
    {
        if (left == 0)
            return right;
        if (right == 0)
            return left;
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu | yu;
        }
        return new FixedBigInteger(left.Sign * right.Sign, z, DataBitWidth);
    }
    public static FixedBigInteger operator ^(FixedBigInteger left, FixedBigInteger right)
    {
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu ^ yu;
        }
        return new FixedBigInteger(left.Sign * right.Sign, z, DataBitWidth);
    }
    public static FixedBigInteger operator &(FixedBigInteger left, FixedBigInteger right)
    {
        if (left == 0 || right == 0)
            return 0;
        var z    = new uint[Math.Max(left.Data.Length, right.Data.Length)];
        var lExt = left.Sign  < 0 ? uint.MaxValue : 0U;
        var rExt = right.Sign < 0 ? uint.MaxValue : 0U;
        for (var i = 0; i < z.Length; i++)
        {
            var xu = i < left.Data.Length ? left.Data[i] : lExt;
            var yu = i < right.Data.Length ? right.Data[i] : rExt;
            z[i] = xu & yu;
        }
        return new FixedBigInteger(left.Sign * right.Sign, z, DataBitWidth);
    }
    public FixedBigInteger Sqrt()
    {
        var n     = this;
        var q     = One << ((int) Math.Ceiling(Log(n, 2)) >> 1);
        var steps = 0;
        var m     = Zero;
        while (Abs(q - m) >= 1)
        {
            m = q;
            q = (m + n / m) >> 1;
            steps++;
        }
        return q;
    }
    public FixedBigInteger Pow(int e)
    {
        var ans = this;
        if (e == 1)
            return ans;
        if (e == 0)
            return 1;
        for (var i = 1; i != e; i++)
            ans *= this;
        return ans;
    }
    public static FixedBigInteger ModPow(FixedBigInteger n, FixedBigInteger e, FixedBigInteger m)
    {
        var n1 = new BigIntX(n);
        var e1 = new BigIntX(e);
        var m1 = new BigIntX(m);
        var r  = new BigIntX(1);
        while (e1 != 0)
        {
            if (e1 % 2 == 1)
                r = r * n1 % m1;
            e1 >>= 1;
            n1 =   n1 * n1 % m1;
        }
        return new FixedBigInteger(r.ToByteArray(), DataBitWidth);
    }
    public bool Fermat(FixedBigInteger candidate)
    {
        uint[] wits = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
        var    pmo  = candidate - 1;
        for (var i = 0; i < wits.Length; i++)
        {
            var r = ModPow(wits[i], pmo, candidate);
            if (r != One)
                return false;
        }
        return true;
    }
    public bool IsPrime()
    {
        return Fermat(this);
    }
    public static double Log(FixedBigInteger value, double baseValue)
    {
        var c          = 0.0;
        var d          = 0.5;
        var dataLength = Length(value.Data);
        var topBits    = BitLengthOfUInt(value.Data[dataLength - 1]);
        var bitLength  = (dataLength - 1) * 32 + topBits;
        var bit        = (uint) (1 << (topBits - 1));
        for (var index = dataLength - 1; index >= 0; --index)
        {
            for (; bit != 0U; bit >>= 1)
            {
                if (((int) value.Data[index] & (int) bit) != 0)
                    c += d;
                d *= 0.5;
            }
            bit = 2147483648U;
        }
        return (Math.Log(c) + 0.693147180559945 * bitLength) / Math.Log(baseValue);
    }
    private static int BitLengthOfUInt(uint x)
    {
        var numBits = 0;
        while (x > 0)
        {
            x >>= 1;
            numBits++;
        }
        return numBits;
    }
    private static int Length(uint[] rgu)
    {
        var length = rgu.Length;
        return rgu[length - 1] != 0U ? length : length - 1;
    }
    public static List<FixedBigInteger> GetFactors(FixedBigInteger n)
    {
        var Factors = new List<FixedBigInteger>();
        var s       = n.Sqrt();
        var a       = Three;
        while (a < s)
        {
            if (n % a == 0)
            {
                Factors.Add(a);
                if (a * a != n)
                    Factors.Add(n / a);
            }
            a += 2;
        }
        return Factors;
    }
    public static FixedBigInteger GreatestCommonDivisor(FixedBigInteger a, FixedBigInteger b)
    {
        while (b > 0)
        {
            var r = a % b;
            a = b;
            b = r;
        }
        return a;
    }
    public static FixedBigInteger LeastCommonMultiple(FixedBigInteger a, FixedBigInteger b)
    {
        return a * b / a.Gcd(b);
    }
    public static double Log10(FixedBigInteger value)
    {
        return Log(value, 10.0);
    }
    public static double LogN(FixedBigInteger value)
    {
        return Log(value, 2.0);
    }
    private class FixedBigIntegerConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            if (value != null)
                if (TryParse($"{value}", out var i))
                    return i;
            return new FixedBigInteger(0, DataBitWidth);
        }
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
        }
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            return destinationType == typeof(string) ? $"{value}" : base.ConvertTo(context, culture, value, destinationType);
        }
    }
}
public class FixedIntXComparer : IComparer<FixedBigInteger>
{
    public int Compare(FixedBigInteger left, FixedBigInteger right)
    {
        return left.CompareTo(right);
    }
    public bool Equals(FixedBigInteger left, FixedBigInteger right)
    {
        if (left == null || right == null)
            return false;
        return left.Equals(right);
    }
    public int GetHashCode(FixedBigInteger obj)
    {
        return obj.GetHashCode();
    }
}

Random64.cs

Posted on June 4, 2020June 13, 2020  Leave a comment on Random64.cs
Example Code:
Add a 256x256 panel to a form.

            var lrng = new Random64(65536, 256, 2);
            var g = panel1.CreateGraphics();
            var bm  = new Bitmap(256, 256);
            var buf = lrng.GetNextBoolArrayLimit(65536);
            var ptr = 0;
            for (var y = 0; y < 256; y++)
            for (var x = 0; x < 256; x++)
            {
                var r = buf[ptr++];
                if (r)
                {
                    bm.SetPixel(x, y, Color.White);
                }
                else
                {
                    bm.SetPixel(x, y, Color.Black);
                }
            }
            g.DrawImageUnscaled(bm, 0, 0);
using System;
using System.Security.Cryptography;
[Serializable]
public class Random64 : RandomNumberGenerator
{
    private byte[]         _buffer;
    private JitterCacheRng _crng;
    private double         _dBi;
    private ulong          UpperLimit = ulong.MaxValue;
    public Random64()
    {
        SetDataUse = 8;
        _crng      = new JitterCacheRng(1024 << 10);
    }
    /// <summary>
    /// </summary>
    /// <param name="cacheSize">Size of the cache in bytes</param>
    public Random64(int cacheSize)
    {
        SetDataUse = 8;
        _crng      = new JitterCacheRng(cacheSize * 8, 128, 64, 4);
    }
    /// <summary>
    /// </summary>
    /// <param name="cacheSize">Size of the cache in bytes</param>
    /// <param name="seedSize">Size of the seed key as generated by Jitter.cs</param>
    public Random64(int cacheSize, int seedSize)
    {
        SetDataUse = 8;
        _crng      = new JitterCacheRng(cacheSize * 8, seedSize, 64, 4);
    }
    /// <summary>
    /// </summary>
    /// <param name="cacheSize">Size of the cache in bytes</param>
    /// <param name="seedSize">Size of the seed key as generated by Jitter.cs</param>
    /// <param name="dataSize">
    ///     Use SetDataUse to conserve the jitter buffer depletion. 
    ///     Sets the data size in bytes to be used by the GetBytes buffer. Also determines the inverse
    ///     MaxValue
    /// </param>
    public Random64(int cacheSize, int seedSize, int dataSize, int sha3Size = 64, int sha3Rounds = 4)
    {
        SetDataUse = dataSize;
        _crng      = new JitterCacheRng(cacheSize * dataSize, seedSize, sha3Size, sha3Rounds);
    }
    /// <summary>
    ///     Very, Very Slow...
    /// </summary>
    public bool Protect
    {
        get => _crng.Protect;
        set => _crng.Protect = value;
    }
    /// <summary>
    ///     Use SetDataUse to conserve the jitter buffer depletion
    /// </summary>
    public int SetDataUse
    {
        get => _buffer.Length;
        set
        {
            var v = value;
            if (v < 1 || v > 8 || v == 3 || v == 5 || v == 6 || v == 7)
                throw new ArgumentException($"Value {v} must be either 1 or 2 or 4 or 8");
            switch (v)
            {
                case 1:
                    _dBi       = 1.0D / byte.MaxValue;
                    UpperLimit = byte.MaxValue;
                    _buffer    = new byte[1];
                    break;
                case 2:
                    _dBi       = 1.0D / ushort.MaxValue;
                    UpperLimit = ushort.MaxValue;
                    _buffer    = new byte[2];
                    break;
                case 4:
                    _dBi       = 1.0D / uint.MaxValue;
                    UpperLimit = uint.MaxValue;
                    _buffer    = new byte[4];
                    break;
                case 8:
                    _dBi       = 1.0D / ulong.MaxValue;
                    UpperLimit = ulong.MaxValue;
                    _buffer    = new byte[8];
                    break;
                default:
                    _dBi       = 1.0D / ulong.MaxValue;
                    UpperLimit = ulong.MaxValue;
                    _buffer    = new byte[8];
                    break;
            }
        }
    }
    /// <summary>
    ///     Will generate only odd values
    /// </summary>
    public bool OddsOnly
    {
        get;
        set;
    }
    private double Sample()
    {
        ulong Internal()
        {
            _crng.GetBytes(_buffer);
            return BufferToLong(_buffer);
        }
        return Internal() * _dBi;
    }
    public ulong Next(ulong minValue, ulong maxValue)
    {
        var sa = Sample();
        var fi = (double) (maxValue - minValue + minValue);
        var n  = (ulong) (sa * fi);
        n = !OddsOnly ? n : n | 1;
        return n;
    }
    public ulong Next(ulong maxValue)
    {
        return Next(0, maxValue);
    }
    public ulong Next()
    {
        return Next(0, UpperLimit);
    }
    public unsafe double NextDouble()
    {
        var buf = new byte[8];
        GetBytes(buf);
        fixed (byte* ptr = buf)
        {
            return *(ulong*) ptr * _dBi * ulong.MaxValue;
        }
    }
    public char[] GetNextCharArray(int size)
    {
        var xbc = new byte[1];
        var ca  = new char[size];
        var ptr = 0;
        do
        {
            _crng.GetBytes(xbc);
            var c = xbc[0];
            if (c >= 32 && c <= 127)
                ca[ptr++] = (char) c;
        } while (ptr < size);
        return ca;
    }
    public byte[] GetNextByteArray(int size)
    {
        var ba = new byte[size];
        _crng.GetBytes(ba);
        return ba;
    }
    /// <summary>
    ///     Next(0,2)==0?false:true; has a distribution error of 1% weighted toward zero.
    ///     The distribution error here is 0.000000046% which is statistically insignificant in this context.
    /// </summary>
    /// <param name="size"></param>
    /// <returns></returns>
    public bool[] GetNextBoolArrayLimit(int size)
    {
        var       ba = new bool[size];
        const uint ll = uint.MaxValue >> 1;
        for (var i = 0; i < size; ++i)
            ba[i] = Next(0, uint.MaxValue) > ll;
        return ba;
    }
    public byte[] GetNextByteArrayLimit(int size, ulong minValue, ulong maxValue)
    {
        var ba = new byte[size];
        for (var i = 0; i < size; ++i)
            ba[i] = (byte) Next(minValue, maxValue);
        return ba;
    }
    public ushort[] GetNextUShortArrayLimit(int size, ulong minValue, ulong maxValue)
    {
        var ba = new ushort[size];
        for (var i = 0; i < size; ++i)
            ba[i] = (ushort) Next(minValue, maxValue);
        return ba;
    }
    public uint[] GetNextUIntArrayLimit(int size, ulong minValue, ulong maxValue)
    {
        var ba = new uint[size];
        for (var i = 0; i < size; ++i)
            ba[i] = (uint) Next(minValue, maxValue);
        return ba;
    }
    public ulong[] GetNextULongArrayLimit(int size, ulong minValue, ulong maxValue)
    {
        var ba = new ulong[size];
        for (var i = 0; i < size; ++i)
            ba[i] = Next(minValue, maxValue);
        return ba;
    }
    public string GetRandomString(int minLen, int maxLen)
    {
        if (minLen == maxLen)
            return new string(GetNextCharArray(minLen));
        return new string(GetNextCharArray((int) Next((ulong) minLen, (ulong) maxLen)));
    }
    public override void GetBytes(byte[] data)
    {
        if (data == null)
            throw new ArgumentException("The buffer cannot be null.");
        _crng.GetBytes(data);
    }
    public void NextBytes(byte[] buffer)
    {
        if (buffer == null)
            throw new ArgumentNullException("The buffer cannot be null.");
        for (var index = 0; index < buffer.Length; ++index)
            buffer[index] = (byte) (Sample() * byte.MaxValue);
    }
    private unsafe ulong BufferToLong(byte[] buffer)
    {
        var len = buffer.Length;
        if (len < 1 || len > 8)
            throw new ArgumentException($"The array length {len} must be between 1 and 8");
        fixed (byte* Ptr = &buffer[0])
        {
            switch (len)
            {
                case 1:
                    return *Ptr;
                case 2:
                    return (uint) (*Ptr | (Ptr[1] << 8));
                case 3:
                    return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16));
                case 4:
                    return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24));
                case 5:
                    return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) Ptr[4] << 32);
                case 6:
                    return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) (Ptr[4] | (Ptr[5] << 8)) << 32);
                case 7:
                    return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) (Ptr[4] | (Ptr[5] << 8) | (Ptr[6] << 16)) << 32);
                case 8:
                    return (uint) (*Ptr | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24)) | ((ulong) (Ptr[4] | (Ptr[5] << 8) | (Ptr[6] << 16) | (Ptr[7] << 24)) << 32);
                default:
                    return 0;
            }
        }
    }
}

JitterCacheRng.cs

Posted on June 4, 2020June 28, 2020Tags , , 1 Comment on JitterCacheRng.cs
using System;
using System.Security.Cryptography;
using System.Threading;
[Serializable]
public class JitterCacheRng : RandomNumberGenerator
{
    private const    int        ReSecureThreshold = 10;
    private readonly SHA3ModInt _algorithm;
    private readonly JitterEx   _jit;
    private readonly int        _moveSize;
    private          int        _availableCacheBytes;
    private          byte[]     _cache;
    private          byte[]     _cacheBuffer;
    private          int        _cacheSize;
    private          int        _ptr;
    public           int        cacheFills;
    public JitterCacheRng() : this(1048576, 256, 256, 4)
    {
    }
    public JitterCacheRng(int cacheSize) : this(cacheSize, 256, 256, 4)
    {
    }
    public JitterCacheRng(int cacheSize, int seedSize) : this(cacheSize, seedSize, 256, 4)
    {
    }
    public JitterCacheRng(int cacheSize, int seedSize, int sha3Size) : this(cacheSize, seedSize, sha3Size, 4)
    {
    }
    public JitterCacheRng(int cacheSize, int seedSize, int sha3Size, int sha3Rounds)
    {
        if (cacheSize == 0)
            throw new ArgumentException("Cache Size cannot be zero");
        if (seedSize < 256)
            throw new ArgumentException("The seed size should be 256 or more bytes.");
        if (sha3Size < 64 && sha3Size % 64 != 0)
            throw new ArgumentException("The bitWidth of the SHA3 hash algorithm need to be at least 64 bits and a multiple of 64.");
        if (sha3Rounds < 4)
            throw new ArgumentException("Sha3 rounds of less than 4 might produce some short run repetitive sequences.");
        _cacheSize   = cacheSize;
        _jit         = new JitterEx(seedSize);
        _cacheBuffer = _jit.GetBuffer();
        _cache       = new byte[_cacheSize];
        _algorithm   = new SHA3ModInt(sha3Size, sha3Rounds);
        _moveSize    = _algorithm.ComputeHash(2.GetBytes()).Length;
        FillCache();
    }
    public bool Protect
    {
        get;
        set;
    }
    protected override void Dispose(bool disposing)
    {
        _cache.Fill(0);
        _availableCacheBytes = 0;
        _algorithm.Dispose();
    }
    public override void GetBytes(byte[] data)
    {
        if (data.Length > _cacheSize)
        {
            _cacheSize = data.Length;
            _cache     = new byte[_cacheSize];
            FillCache();
        }
        if (_availableCacheBytes == 0 || _availableCacheBytes < data.Length)
        {
            if (_cacheSize < data.Length)
            {
                _cacheSize = data.Length;
                _cache     = new byte[_cacheSize];
            }
            FillCache();
        }
        if (_ptr + data.Length > _cacheSize)
            FillCache();
        if (Protect)
            ProtectedMemory.Unprotect(_cache, MemoryProtectionScope.SameLogon);
        Buffer.BlockCopy(_cache, _ptr, data, 0, data.Length);
        if (Protect)
            ProtectedMemory.Protect(_cache, MemoryProtectionScope.SameLogon);
        _ptr                 += data.Length;
        _availableCacheBytes -= data.Length;
    }
    private void FillCache()
    {
        var btrd = new Thread(() =>
        {
            _availableCacheBytes = 0;
            _ptr                 = 0;
            var p        = 0;
            var moveSize = _moveSize;
            cacheFills++;
            if (cacheFills % ReSecureThreshold == 0)
                _cacheBuffer = _jit.GetBuffer();
            if (Protect)
                ProtectedMemory.Unprotect(_cache, MemoryProtectionScope.SameLogon);
            while (true)
            {
                var remainingBytesToMove = _cacheSize - _availableCacheBytes;
                if (remainingBytesToMove < moveSize)
                    moveSize = remainingBytesToMove;
                if (remainingBytesToMove <= 0)
                    break;
                _cacheBuffer = _algorithm.ComputeHash(_cacheBuffer);
                Buffer.BlockCopy(_cacheBuffer, 0, _cache, p, moveSize);
                p                    += moveSize;
                _availableCacheBytes += moveSize;
            }
            if (Protect)
                ProtectedMemory.Protect(_cache, MemoryProtectionScope.SameLogon);
        }) {Priority = ThreadPriority.Highest};
        btrd.Start();
        btrd.Join();
    }
}