BigDecimal.cs

Arbitrary Precision Signed BigDecimal

Updated: Jun-15,2021

using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
[Serializable]
[DebuggerDisplay("{DDisplay}")]
public struct BigDecimal : IComparable, IComparable<BigDecimal>, IEquatable<BigDecimal>
{
    private const           int          MaxFactorials    = 200;
    private static          int          _bitWidth        = 2048;
    public static           float        MaxPrecision     = _bitWidth / 64f * 20f;
    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(BigInteger value) : this(value, 0)
    {
    }
    public BigDecimal(xIntX value) : this((BigInteger)value, 0)
    {
    }
    public BigDecimal(BigIntX 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(BigDecimal value)
    {
        _unscaledValue = value._unscaledValue;
        _scale         = value.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}");
            }
    }
    public int BitWidth
    {
        get => _bitWidth;
        set
        {
            _bitWidth    = value;
            MaxPrecision = _bitWidth / 64f * 20f;
        }
    }
    private string DDisplay => ToString();
    public static BigDecimal Zero
    {
        get;
    } = new(BigInteger.Zero);
    public static BigDecimal One
    {
        get;
    } = new(BigInteger.One);
    public static BigDecimal MinusOne
    {
        get;
    } = new(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;
    public int DecimalPlaces
    {
        get
        {
            var a       = new BigDecimal(_unscaledValue);
            var dPlaces = 0;
            if (a.Sign == 0)
                return 1;
            if (a.Sign < 0)
                try
                {
                    a = -a;
                }
                catch (Exception ex)
                {
                    return 0;
                }
            var biRadix = new BigDecimal(10);
            while (a > 0)
                try
                {
                    a /= biRadix;
                    dPlaces++;
                }
                catch (Exception ex)
                {
                    break;
                }
            return dPlaces;
        }
    }
    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)
    {
        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 override string ToString()
    {
        return ToStringInt();
    }
    public string ToString(IFormatProvider provider)
    {
        var number = _unscaledValue.ToString("G");
        if (_scale > 0 && WholePart != 0 && number.Length - _scale >= 0)
            return number.Insert(number.Length - _scale, ".");
        return _unscaledValue.ToString(provider);
    }
    private string ToStringInt()
    {
        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 < (int)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.Pow(10, value.Scale));
    }
    public static explicit operator ushort(BigDecimal value)
    {
        return (ushort)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator uint(BigDecimal value)
    {
        return (uint)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator ulong(BigDecimal value)
    {
        return (ulong)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator byte(BigDecimal value)
    {
        return (byte)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator short(BigDecimal value)
    {
        return (short)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator int(BigDecimal value)
    {
        return (int)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator long(BigDecimal value)
    {
        return (long)BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator BigInteger(BigDecimal value)
    {
        return BigInteger.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator xIntX(BigDecimal value)
    {
        return xIntX.Divide(value._unscaledValue, BigInteger.Pow(10, value.Scale));
    }
    public static explicit operator BigIntX(BigDecimal value)
    {
        return BigIntX.Divide(value._unscaledValue, BigInteger.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(xIntX value)
    {
        return new BigDecimal(value);
    }
    public static implicit operator BigDecimal(BigIntX 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);
    }
    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(0, 0);
            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(0, 0);
            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;
    }
    public 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 static BigDecimal Ceiling(BigDecimal value, int precision)
    {
        var v1 = new BigDecimal(value);
        v1 = RemoveTrailingZeros(v1);
        var diff = GetNumberOfDigits(v1._unscaledValue) - precision;
        if (diff > 0)
        {
            for (var i = 0; i < diff; i++)
            {
                v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10);
                v1.Scale--;
            }
            if (v1._unscaledValue.Sign < 0)
                v1._unscaledValue--;
            else
                v1._unscaledValue++;
        }
        return v1;
    }
    public static BigDecimal Floor(BigDecimal value, int precision)
    {
        var v1 = new BigDecimal(value);
        v1 = RemoveTrailingZeros(v1);
        var diff = GetNumberOfDigits(v1._unscaledValue) - precision;
        if (diff > 0)
        {
            for (var i = 0; i < diff; i++)
            {
                v1._unscaledValue = BigInteger.Divide(v1._unscaledValue, 10);
                v1.Scale--;
            }
            if (v1._unscaledValue.Sign > 0)
                v1._unscaledValue--;
            else
                v1._unscaledValue++;
        }
        return v1;
    }
    private static BigDecimal RemoveTrailingZeros(BigDecimal value)
    {
        var        v1 = new BigDecimal(value);
        BigInteger remainder;
        do
        {
            var shortened = BigInteger.DivRem(v1._unscaledValue, 10, out remainder);
            if (remainder == BigInteger.Zero)
            {
                v1._unscaledValue = shortened;
                v1.Scale--;
            }
        } while (remainder == BigInteger.Zero);
        return v1;
    }
    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;
    }
}