Std

Mainly a port of Rust's std.
git clone https://git.philomathiclife.com/repos/Std
Log | Files | Refs | README

U128.cs (20880B)


      1 using Std.Clone;
      2 using Std.Cmp;
      3 using static Std.Cmp.Ordering;
      4 using Std.Convert;
      5 using Std.Hashing;
      6 using Std.Maybe;
      7 using static Std.Maybe.Maybe<Std.Cmp.Ordering>;
      8 using static Std.Maybe.Maybe<uint>;
      9 using Std.Net;
     10 using Std.Ops;
     11 using Std.Result;
     12 using static Std.Result.Result<Std.Num.U128, Std.Bottom>;
     13 using static Std.Result.Result<string, Std.Bottom>;
     14 using System;
     15 using System.Runtime.CompilerServices;
     16 using System.Runtime.InteropServices;
     17 #region Namespaces
     18 namespace Std.Num {
     19     #region Types
     20     [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode, Pack = 8, Size = 16)]
     21     public readonly struct U128: IClone<U128>, IEquality<U128>, IHashable, IInto<U128>, IInto<string>, IInto<IPv6Addr>, IOrd<U128>, ITryInto<NonZeroU128, TryIntoIntError>, ITryInto<I128, TryIntoIntError>, ITryInto<byte, TryIntoIntError>, ITryInto<int, TryIntoIntError>, ITryInto<long, TryIntoIntError>, ITryInto<sbyte, TryIntoIntError>, ITryInto<short, TryIntoIntError>, ITryInto<uint, TryIntoIntError>, ITryInto<ulong, TryIntoIntError>, ITryInto<ushort, TryIntoIntError>, IFrom<U128, byte>, IFrom<U128, ushort>, IFrom<U128, uint>, IFrom<U128, ulong>, IFrom<U128, bool>, ITryFrom<U128, sbyte, TryFromIntError>, ITryFrom<U128, short, TryFromIntError>, ITryFrom<U128, int, TryFromIntError>, ITryFrom<U128, long, TryFromIntError>  {
     22 
     23         #region Type-level Constructors
     24         #endregion
     25 
     26         #region Instance Constructors
     27         [MethodImpl(MethodImplOptions.AggressiveInlining)]
     28         public U128() => (Low, High) = (ulong.MinValue, ulong.MinValue);
     29         [MethodImpl(MethodImplOptions.AggressiveInlining)]
     30         public U128(ulong low) => (Low, High) = (low, ulong.MinValue);
     31         [MethodImpl(MethodImplOptions.AggressiveInlining)]
     32         public U128(ulong low, ulong high) => (Low, High) = (low, high);
     33         #endregion
     34 
     35         #region Type-level Fields
     36         public static readonly U128 MaxValue = new(ulong.MaxValue, ulong.MaxValue);
     37         public static readonly U128 MinValue = new();
     38         static readonly Fn<Prod<U128, U128>> _fnException = () => throw new DivideByZeroException();
     39         #endregion
     40 
     41         #region Instance Fields
     42         [FieldOffset(0)] public readonly ulong Low;
     43         [FieldOffset(8)] public readonly ulong High;
     44         #endregion
     45 
     46         #region Type-level Properties
     47         #endregion
     48 
     49         #region Instance Properties
     50         #endregion
     51 
     52         #region Type-level Functions
     53         public static Prod<U128, U128> DivRem(U128 dividend, U128 divisor) => NonZeroU128.New(divisor).MapOrElse(_fnException, (div) => DivRem(dividend, div));
     54         public static Prod<U128, U128> DivRem(U128 dividend, NonZeroU128 divisor) {
     55 
     56             if (divisor.Value > dividend) {
     57                 return new(MinValue, dividend);
     58             } else {
     59                 var div = divisor.Value;
     60                 var shift = (int)MSBLEDistance(dividend, div);
     61                 div <<= shift;
     62                 var quot = MinValue;
     63                 U128 one = new(1ul);
     64 
     65                 while (shift >= 0) {
     66                     quot <<= 1;
     67 
     68                     if (dividend >= div) {
     69                         dividend -= div;
     70                         quot |= one;
     71                     }
     72                     div >>= 1;
     73                     shift--;
     74                 }
     75                 return new(quot, dividend);
     76             }
     77         }
     78         public static U128 FromBEBytes(ReadOnlySpan<byte> val) => new(((ulong)val[8] << 56) | ((ulong)val[9] << 48) | ((ulong)val[10] << 40) | ((ulong)val[11] << 32) | ((ulong)val[12] << 24) | ((ulong)val[13] << 16) | ((ulong)val[14] << 8) | val[15], ((ulong)val[0] << 56) | ((ulong)val[1] << 48) | ((ulong)val[2] << 40) | ((ulong)val[3] << 32) | ((ulong)val[4] << 24) | ((ulong)val[5] << 16) | ((ulong)val[6] << 8) | val[7]);
     79         public static U128 FromLEBytes(ReadOnlySpan<byte> val) {
     80 
     81             unsafe {
     82                 fixed (byte* ptr = val) {
     83                     var ptr2 = (ulong*)ptr;
     84                     return new(*ptr2++, *ptr2);
     85                 }
     86             }
     87         }
     88         // Calling code MUST ensure val is not 0.
     89         internal static uint MSBLE(U128 val) {
     90             for (var i = uint.MinValue; i <= 127u; i++) { if ((val >>= 1) == MinValue) { return i; } }
     91             throw new InvalidOperationException("Std.Num.U128.MSBLE was passed zero.");
     92         }
     93         // Calling code MUST ensure val0 and val1 are not 0.
     94         internal static uint MSBLEDistance(U128 val0, U128 val1) {
     95 
     96             var v0 = MSBLE(val0);
     97             var v1 = MSBLE(val1);
     98             return v0 >= v1 ? v0 - v1 : v1 - v0;
     99         }
    100         public static U128 New(ulong low) => new(low);
    101         public static U128 New(ulong low, ulong high) => new(low, high);
    102         public static Result<U128, ParseIntError> TryFrom(ReadOnlySpan<char> val) {
    103 
    104             if (val.Length is 0 or > 39) { return new(ParseIntError.InvalidLength); }
    105             ulong digit;
    106             var u128 = MinValue;
    107             U128 ten = new(10ul);
    108 
    109             for (var i = 0; i < val.Length; i++) {
    110                 digit = val[i];
    111                 if (digit is < 48ul or > 57ul) { return new(ParseIntError.InvalidDigit); }
    112                 u128 = u128.WrappingMul(ten).WrappingAdd(new U128(digit - 48ul));
    113             }
    114             // In the case there are 39 digits, it is likely there is overflow; however overflow is easily detected
    115             // by checking to see if the resulting value is less than the smallest 39-digit value (i.e., 10^38).
    116             return val.Length == 39 && u128 < new U128(687399551400673280ul, 5421010862427522170ul) ? new(ParseIntError.OverflowOrUnderflow) : new(u128);
    117         }
    118         public static Result<U128, ParseIntError> TryFromAllowLeading0s(ReadOnlySpan<char> val) {
    119 
    120             var start = 0;
    121             for (var i = 0; i < val.Length; i++) { if (val[i] != '0') { start = i; break; } }
    122             return val.Length == 0 ? new(ParseIntError.InvalidLength) : start == 0 ? new(MinValue) : TryFrom(val[start..]);
    123         }
    124         public static U128 From(bool val) => val ? new(1ul) : MinValue;
    125         public static U128 From(byte val) => new(val);
    126         public static U128 From(uint val) => new(val);
    127         public static U128 From(ulong val) => new(val);
    128         public static U128 From(ushort val) => new(val);
    129         static Result<U128, Bottom> ITryFrom<U128, bool, Bottom>.TryFrom(bool val) => new(From(val));
    130         static Result<U128, Bottom> ITryFrom<U128, byte, Bottom>.TryFrom(byte val) => new(From(val));
    131         public static Result<U128, TryFromIntError> TryFrom(int val) => val < 0 ? new(TryFromIntError.OverflowOrUnderflow) : new((U128)val);
    132         public static Result<U128, TryFromIntError> TryFrom(long val) => val < 0L ? new(TryFromIntError.OverflowOrUnderflow) : new((U128)val);
    133         public static Result<U128, TryFromIntError> TryFrom(sbyte val) => val < 0 ? new(TryFromIntError.OverflowOrUnderflow) : new((U128)val);
    134         public static Result<U128, TryFromIntError> TryFrom(short val) => val < 0 ? new(TryFromIntError.OverflowOrUnderflow) : new((U128)val);
    135         public static Result<U128, ParseIntError> TryFrom(string val) => TryFrom(val.AsSpan());
    136         static Result<U128, Bottom> ITryFrom<U128, uint, Bottom>.TryFrom(uint val) => new(From(val));
    137         static Result<U128, Bottom> ITryFrom<U128, ulong, Bottom>.TryFrom(ulong val) => new(From(val));
    138         static Result<U128, Bottom> ITryFrom<U128, ushort, Bottom>.TryFrom(ushort val) => new(From(val));
    139         #endregion
    140 
    141         #region Instance Functions
    142         public readonly U128 Clone() => this;
    143         public readonly Ordering Cmp(in U128 other) => High.CompareTo(other.High) switch {
    144             < 0 => Less,
    145             > 0 => Greater,
    146             0 => Low.CompareTo(other.Low) switch {
    147                 < 0 => Less,
    148                 > 0 => Greater,
    149                 0 => Equivalent,
    150             }
    151         };
    152         public readonly bool Equals(in U128 other) => this == other;
    153         public override readonly bool Equals(object? _) => false;
    154         public override readonly int GetHashCode() => 0;
    155         public readonly Unit Hash<THasher>(ref THasher hasher) where THasher: notnull, IHasher => hasher.WriteUnsafe(this);
    156         public readonly U128 Into() => this;
    157         public readonly byte[] IntoBEBytes() {
    158 
    159             var copy = this;
    160             ReadOnlySpan<byte> val;
    161             unsafe { val = new(&copy, 16); }
    162             return new byte[] { val[15], val[14], val[13], val[12], val[11], val[10], val[9], val[8], val[7], val[6], val[5], val[4], val[3], val[2], val[1], val[0] };
    163         }
    164         public readonly Unit IntoBEBytes(Span<byte> bytes) {
    165 
    166             var copy = this;
    167             ReadOnlySpan<byte> val;
    168             unsafe { val = new(&copy, 16); }
    169             (bytes[15], bytes[14], bytes[13], bytes[12], bytes[11], bytes[10], bytes[9], bytes[8], bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0]) = (val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7], val[8], val[9], val[10], val[11], val[12], val[13], val[14], val[15]);
    170             return new Unit();
    171         }
    172         public readonly byte[] IntoLEBytes() {
    173 
    174             var bytes = new byte[16];
    175             unsafe { fixed (byte* ptr = bytes) { *(U128*)ptr = this; } }
    176             return bytes;
    177         }
    178         public readonly Unit IntoLEBytes(Span<byte> bytes) {
    179 
    180             if (bytes.Length >= 16) {
    181                 unsafe { fixed (byte* ptr = bytes) { *(U128*)ptr = this; } }
    182                 return new Unit();
    183             } else {
    184                 throw new ArgumentException("bytes does not have a length of at least 16.");
    185             }
    186         }
    187         public readonly string IntoString() => ToString();
    188         public readonly IPv6Addr IntoIPv6Addr() => new(this);
    189         readonly string IInto<string>.Into() => IntoString();
    190         readonly IPv6Addr IInto<IPv6Addr>.Into() => IntoIPv6Addr();
    191         public readonly Maybe<Ordering> PartialCmp(in U128 other) => Some(Cmp(in other));
    192         public override readonly string ToString() {
    193 
    194             if (High == ulong.MinValue) { return Low.ToString(); }
    195             unsafe {
    196                 var digits = stackalloc char[39];
    197                 var len = 0;
    198                 var ten = NonZeroU128.New(new(10ul)).Unwrap();
    199                 var divRem = new Prod<U128, U128>(this, MinValue);
    200 
    201                 while (divRem.Item0 > MinValue) {
    202                     divRem = DivRem(divRem.Item0, ten);
    203                     digits[38 - len++] = (char)((uint)divRem.Item1 + 48u);
    204                 }
    205                 return new string(digits, 39 - len, len);
    206             }
    207         }
    208         readonly Result<U128, Bottom> ITryInto<U128, Bottom>.TryInto() => OK(this);
    209         readonly Result<string, Bottom> ITryInto<string, Bottom>.TryInto() => new(ToString());
    210         readonly Result<IPv6Addr, Bottom> ITryInto<IPv6Addr, Bottom>.TryInto() => new(IntoIPv6Addr());
    211         readonly Result<I128, TryIntoIntError> ITryInto<I128, TryIntoIntError>.TryInto() => TryIntoI128();
    212         readonly Result<NonZeroU128, TryIntoIntError> ITryInto<NonZeroU128, TryIntoIntError>.TryInto() => TryIntoNonZeroU128();
    213         readonly Result<byte, TryIntoIntError> ITryInto<byte, TryIntoIntError>.TryInto() => TryIntoByte();
    214         readonly Result<int, TryIntoIntError> ITryInto<int, TryIntoIntError>.TryInto() => TryIntoInt();
    215         readonly Result<long, TryIntoIntError> ITryInto<long, TryIntoIntError>.TryInto() => TryIntoLong();
    216         readonly Result<sbyte, TryIntoIntError> ITryInto<sbyte, TryIntoIntError>.TryInto() => TryIntoSbyte();
    217         readonly Result<short, TryIntoIntError> ITryInto<short, TryIntoIntError>.TryInto() => TryIntoShort();
    218         readonly Result<uint, TryIntoIntError> ITryInto<uint, TryIntoIntError>.TryInto() => TryIntoUint();
    219         readonly Result<ulong, TryIntoIntError> ITryInto<ulong, TryIntoIntError>.TryInto() => TryIntoUlong();
    220         readonly Result<ushort, TryIntoIntError> ITryInto<ushort, TryIntoIntError>.TryInto() => TryIntoUshort();
    221         public readonly Result<I128, TryIntoIntError> TryIntoI128() => this > (U128)I128.MaxValue ? new(TryIntoIntError.Overflow) : new((I128)this);
    222         public readonly Result<NonZeroU128, TryIntoIntError> TryIntoNonZeroU128() => NonZeroU128.New(this).OKOr(TryIntoIntError.Overflow);
    223         public readonly Result<byte, TryIntoIntError> TryIntoByte() => this > (U128)byte.MaxValue ? new(TryIntoIntError.Overflow) : new((byte)this);
    224         public readonly Result<int, TryIntoIntError> TryIntoInt() => this > (U128)int.MaxValue ? new(TryIntoIntError.Overflow) : new((int)this);
    225         public readonly Result<long, TryIntoIntError> TryIntoLong() => this > (U128)long.MaxValue ? new(TryIntoIntError.Overflow) : new((long)this);
    226         public readonly Result<sbyte, TryIntoIntError> TryIntoSbyte() => this > (U128)sbyte.MaxValue ? new(TryIntoIntError.Overflow) : new((sbyte)this);
    227         public readonly Result<short, TryIntoIntError> TryIntoShort() => this > (U128)short.MaxValue ? new(TryIntoIntError.Overflow) : new((short)this);
    228         public readonly Result<uint, TryIntoIntError> TryIntoUint() => this > (U128)uint.MaxValue ? new(TryIntoIntError.Overflow) : new((uint)this);
    229         public readonly Result<ulong, TryIntoIntError> TryIntoUlong() => this > (U128)ulong.MaxValue ? new(TryIntoIntError.Overflow) : new((ulong)this);
    230         public readonly Result<ushort, TryIntoIntError> TryIntoUshort() => this > (U128)ushort.MaxValue ? new(TryIntoIntError.Overflow) : new((ushort)this);
    231         #endregion
    232 
    233         #region Operators
    234         public static bool operator !=(U128 val0, U128 val1) => !(val0 == val1);
    235         public static U128 operator %(U128 dividend, U128 divisor) => DivRem(dividend, divisor).Item1;
    236         public static U128 operator %(U128 dividend, NonZeroU128 divisor) => DivRem(dividend, divisor).Item1;
    237         public static U128 operator &(U128 val0, U128 val1) => new(val0.Low & val1.Low, val0.High & val1.High);
    238         public static U128 operator *(U128 val0, U128 val1) {
    239 
    240             var u0 = val0.Low & 0xFFFFFFFFul;
    241             var v0 = val1.Low & 0xFFFFFFFFul;
    242             var carry = u0 * v0;
    243             var r0 = carry & 0xFFFFFFFFul;
    244             var v1 = val1.Low >> 32;
    245             carry = (carry >> 32) + (u0 * v1);
    246             var r1 = carry >> 32;
    247             var u1 = val0.Low >> 32;
    248             carry = (carry & 0xFFFFFFFFul) + (u1 * v0);
    249             return new((carry << 32) | r0, (carry >> 32) + r1 + (u1 * v1) + (val0.High * val1.Low) + (val0.Low * val1.High));
    250         }
    251         public static U128 operator +(U128 val) => val;
    252         public static U128 operator +(U128 val0, U128 val1) {
    253 
    254             var low = val0.Low + val1.Low;
    255             return new(low, val0.High + val1.High + (low < val0.Low ? 1ul : ulong.MinValue));
    256         }
    257         public static U128 operator ++(U128 val) => val + new U128(1ul);
    258         public static U128 operator -(U128 val) => MinValue - val;
    259         public static U128 operator -(U128 val0, U128 val1) => new(val0.Low - val1.Low, val0.High - val1.High - (val0.Low < val1.Low ? 1ul : ulong.MinValue));
    260         public static U128 operator --(U128 val) => val - new U128(1ul);
    261         public static U128 operator /(U128 dividend, U128 divisor) => DivRem(dividend, divisor).Item0;
    262         public static U128 operator /(U128 dividend, NonZeroU128 divisor) => DivRem(dividend, divisor).Item0;
    263         public static bool operator <(U128 val0, U128 val1) => val0.High < val1.High || (val0.High == val1.High && val0.Low < val1.Low);
    264         public static U128 operator <<(U128 val, int shift) => (shift &= 127) == 0 ? val : (shift < 64 ? new(val.Low << shift, (val.High << shift) | (val.Low >> (64 - shift))) : new(ulong.MinValue, val.Low << (shift - 64)));
    265         public static bool operator <=(U128 val0, U128 val1) => !(val0 > val1);
    266         public static bool operator ==(U128 val0, U128 val1) => val0.Low == val1.Low && val0.High == val1.High;
    267         public static bool operator >(U128 val0, U128 val1) => val0.High > val1.High || (val0.High == val1.High && val0.Low > val1.Low);
    268         public static bool operator >=(U128 val0, U128 val1) => !(val0 < val1);
    269         public static U128 operator >>(U128 val, int shift) => (shift &= 127) == 0 ? val : (shift < 64 ? new((val.Low >> shift) | (val.High << (64 - shift)), val.High >> shift) : new(val.High >> (shift - 64), ulong.MinValue));
    270         public static U128 operator ^(U128 val0, U128 val1) => new(val0.Low ^ val1.Low, val0.High ^ val1.High);
    271         public static U128 operator |(U128 val0, U128 val1) => new(val0.Low | val1.Low, val0.High | val1.High);
    272         public static U128 operator |(U128 val0, NonZeroU128 val1) => val0 | val1.Value;
    273         public static U128 operator ~(U128 val) => new(~val.Low, ~val.High);
    274         public static explicit operator U128(byte val) => (U128)(ulong)val;
    275         public static explicit operator U128(decimal val) {
    276 
    277             if (val <= decimal.Zero) {
    278                 return MinValue;
    279             } else {
    280                 var bits = decimal.GetBits(val);
    281                 unsafe {
    282                     fixed (int* ptr = &bits[0]) {
    283                         var ptr2 = (ulong*)ptr;
    284                         return new(*ptr2, (uint)*++ptr2);
    285                     }
    286                 }
    287             }
    288         }
    289         public static explicit operator U128(double val) {
    290 
    291             ulong dbl;
    292             // Re-interpret the bits as a ulong.
    293             unsafe {
    294                 var ptr = (ulong*)&val;
    295                 dbl = *ptr;
    296             }
    297             // Convert from binary per IEEE 754 binary64.
    298             var exp = (dbl << 1) >> 53;
    299 
    300             if (((dbl & 0x8000000000000000ul) == 0x8000000000000000ul) || (exp < 1023ul)) {
    301                 return MinValue;
    302             } else if (exp > 1150ul) {
    303                 return exp == 2047ul && ((dbl << 12) != ulong.MinValue) ? MinValue : MaxValue;
    304             } else if (exp == 1023ul) {
    305                 return new(1ul);
    306             }
    307             exp -= 1023ul;
    308             return ((new U128(dbl) << 76) >> (128 - (int)exp)) | (new U128(1ul) << (int)exp);
    309         }
    310         public static explicit operator U128(float val) => (U128)(double)val;
    311         public static explicit operator U128(int val) => (U128)(long)val;
    312         public static explicit operator U128(long val) => new((ulong)val, val < 0L ? ulong.MaxValue : ulong.MinValue);
    313         public static explicit operator U128(sbyte val) => (U128)(long)val;
    314         public static explicit operator byte(U128 val) => (byte)val.Low;
    315         public static explicit operator decimal(U128 val) {
    316 
    317             unsafe {
    318                 var ptr = (int*)&val;
    319                 return new decimal(*ptr, *++ptr, *++ptr, false, byte.MinValue);
    320             }
    321         }
    322         public static explicit operator double(U128 val) {
    323 
    324             if (val == MinValue) {
    325                 return 0.0d;
    326             } else {
    327                 // Verified above val is not 0 per the requirements of MSBLE.
    328                 var msb = MSBLE(val);
    329                 // First convert val via bitshifts per IEEE 754 binary64.
    330                 val <<= 0x7f - (int)msb;
    331                 U128 round = new(ulong.MinValue, 0x400ul);
    332                 U128 add = new(ulong.MinValue, 0x800ul);
    333 
    334                 if (((round & val) == round) && (((add & val) == add) || ((new U128(ulong.MaxValue, 0x3fful) & val) > MinValue))) {
    335                     round = val;
    336                     val += add;
    337 
    338                     if (val < round) {
    339                         msb += 1u;
    340                     }
    341                 }
    342                 // Last, perform last bitshifts before re-interpreting the raw bits as binary64 (i.e., double).
    343                 unsafe {
    344                     var dbl = ((1023ul + msb) << 52) | ((ulong)((val << 1) >> 76));
    345                     var ptr = &dbl;
    346                     return *(double*)ptr;
    347                 }
    348             }
    349         }
    350         public static explicit operator float(U128 val) => (float)(double)val;
    351         public static explicit operator int(U128 val) => (int)val.Low;
    352         public static explicit operator long(U128 val) => (long)val.Low;
    353         public static explicit operator sbyte(U128 val) => (sbyte)val.Low;
    354         public static explicit operator short(U128 val) => (short)val.Low;
    355         public static explicit operator uint(U128 val) => (uint)val.Low;
    356         public static explicit operator ulong(U128 val) => val.Low;
    357         public static explicit operator ushort(U128 val) => (ushort)val.Low;
    358         public static explicit operator U128(uint val) => (U128)(ulong)val;
    359         public static explicit operator U128(ulong val) => new(val);
    360         public static explicit operator U128(ushort val) => (U128)(ulong)val;
    361         #endregion
    362 
    363         #region Types
    364         #endregion
    365     }
    366     #endregion
    367 
    368     #region Namespaces
    369     #endregion
    370 }
    371 #endregion