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(©, 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(©, 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