// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Buffers.Binary; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics.X86; namespace System { /// <summary>Represents a 128-bit unsigned integer.</summary> [CLSCompliant(false)] [Intrinsic] [StructLayout(LayoutKind.Sequential)] public readonly struct UInt128 : IBinaryInteger<UInt128>, IMinMaxValue<UInt128>, IUnsignedNumber<UInt128>, IUtf8SpanFormattable, IBinaryIntegerParseAndFormatInfo<UInt128> { internal const int Size = 16; #if BIGENDIAN private readonly ulong _upper; private readonly ulong _lower; #else private readonly ulong _lower; private readonly ulong _upper; #endif /// <summary>Initializes a new instance of the <see cref="UInt128" /> struct.</summary> /// <param name="upper">The upper 64-bits of the 128-bit value.</param> /// <param name="lower">The lower 64-bits of the 128-bit value.</param> [CLSCompliant(false)] public UInt128(ulong upper, ulong lower) { _lower = lower; _upper = upper; } internal ulong Lower => _lower; internal ulong Upper => _upper; /// <inheritdoc cref="IComparable.CompareTo(object)" /> public int CompareTo(object? value) { if (value is UInt128 other) { return CompareTo(other); } else if (value is null) { return 1; } else { throw new ArgumentException(SR.Arg_MustBeUInt128); } } /// <inheritdoc cref="IComparable{T}.CompareTo(T)" /> public int CompareTo(UInt128 value) { if (this < value) { return -1; } else if (this > value) { return 1; } else { return 0; } } /// <inheritdoc cref="object.Equals(object?)" /> public override bool Equals([NotNullWhen(true)] object? obj) { return (obj is UInt128 other) && Equals(other); } /// <inheritdoc cref="IEquatable{T}.Equals(T)" /> public bool Equals(UInt128 other) { return this == other; } /// <inheritdoc cref="object.GetHashCode()" /> public override int GetHashCode() => HashCode.Combine(_lower, _upper); /// <inheritdoc cref="object.ToString()" /> public override string ToString() { return Number.UInt128ToDecStr(this); } public string ToString(IFormatProvider? provider) { return Number.FormatUInt128(this, null, provider); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format) { return Number.FormatUInt128(this, format, null); } public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider) { return Number.FormatUInt128(this, format, provider); } public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) { return Number.TryFormatUInt128(this, format, provider, destination, out charsWritten); } /// <inheritdoc cref="IUtf8SpanFormattable.TryFormat" /> public bool TryFormat(Span<byte> utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null) { return Number.TryFormatUInt128(this, format, provider, utf8Destination, out bytesWritten); } public static UInt128 Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); public static UInt128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null); public static UInt128 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static UInt128 Parse(string s, NumberStyles style, IFormatProvider? provider) { if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } public static UInt128 Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.ParseBinaryInteger<char, UInt128>(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse(ReadOnlySpan<char> s, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); /// <summary>Tries to convert a UTF-8 character span containing the string representation of a number to its 128-bit unsigned integer equivalent.</summary> /// <param name="utf8Text">A span containing the UTF-8 characters representing the number to convert.</param> /// <param name="result">When this method returns, contains the 128-bit unsigned integer value equivalent to the number contained in <paramref name="utf8Text" /> if the conversion succeeded, or zero if the conversion failed. This parameter is passed uninitialized; any value originally supplied in result will be overwritten.</param> /// <returns><c>true</c> if <paramref name="utf8Text" /> was converted successfully; otherwise, false.</returns> public static bool TryParse(ReadOnlySpan<byte> utf8Text, out UInt128 result) => TryParse(utf8Text, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // // Explicit Conversions From UInt128 // /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="byte" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="byte" />.</returns> public static explicit operator byte(UInt128 value) => (byte)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="byte" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="byte" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked byte(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((byte)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="char" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="char" />.</returns> public static explicit operator char(UInt128 value) => (char)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="char" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="char" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked char(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((char)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="decimal" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="decimal" />.</returns> public static explicit operator decimal(UInt128 value) { ulong lo64 = value._lower; if (value._upper > uint.MaxValue) { // The default behavior of decimal conversions is to always throw on overflow Number.ThrowOverflowException(SR.Overflow_Decimal); } uint hi32 = (uint)(value._upper); return new decimal((int)(lo64), (int)(lo64 >> 32), (int)(hi32), isNegative: false, scale: 0); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="double" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="double" />.</returns> public static explicit operator double(UInt128 value) { // This code is based on `u128_to_f64_round` from m-ou-se/floatconv // Copyright (c) 2020 Mara Bos <m-ou.se@m-ou.se>. All rights reserved. // // Licensed under the BSD 2 - Clause "Simplified" License // See THIRD-PARTY-NOTICES.TXT for the full license text const double TwoPow52 = 4503599627370496.0; const double TwoPow76 = 75557863725914323419136.0; const double TwoPow104 = 20282409603651670423947251286016.0; const double TwoPow128 = 340282366920938463463374607431768211456.0; const ulong TwoPow52Bits = 0x4330000000000000; const ulong TwoPow76Bits = 0x44B0000000000000; const ulong TwoPow104Bits = 0x4670000000000000; const ulong TwoPow128Bits = 0x47F0000000000000; if (value._upper == 0) { // For values between 0 and ulong.MaxValue, we just use the existing conversion return (double)(value._lower); } else if ((value._upper >> 40) == 0) // value < (2^104) { // For values greater than ulong.MaxValue but less than 2^104 this takes advantage // that we can represent both "halves" of the uint128 within the 52-bit mantissa of // a pair of doubles. double lower = BitConverter.UInt64BitsToDouble(TwoPow52Bits | ((value._lower << 12) >> 12)) - TwoPow52; double upper = BitConverter.UInt64BitsToDouble(TwoPow104Bits | (ulong)(value >> 52)) - TwoPow104; return lower + upper; } else { // For values greater than 2^104 we basically do the same as before but we need to account // for the precision loss that double will have. As such, the lower value effectively drops the // lowest 24 bits and then or's them back to ensure rounding stays correct. double lower = BitConverter.UInt64BitsToDouble(TwoPow76Bits | ((ulong)(value >> 12) >> 12) | (value._lower & 0xFFFFFF)) - TwoPow76; double upper = BitConverter.UInt64BitsToDouble(TwoPow128Bits | (value._upper >> 12)) - TwoPow128; return lower + upper; } } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="Half" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="Half" />.</returns> public static explicit operator Half(UInt128 value) => (Half)(double)(value); /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="short" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="short" />.</returns> public static explicit operator short(UInt128 value) => (short)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="short" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="short" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked short(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((short)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="int" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="int" />.</returns> public static explicit operator int(UInt128 value) => (int)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="int" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="int" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked int(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((int)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="long" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="long" />.</returns> public static explicit operator long(UInt128 value) => (long)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="long" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="long" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked long(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((long)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="Int128" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="Int128" />.</returns> [CLSCompliant(false)] public static explicit operator Int128(UInt128 value) => new Int128(value._upper, value._lower); /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="Int128" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="Int128" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> [CLSCompliant(false)] public static explicit operator checked Int128(UInt128 value) { if ((long)value._upper < 0) { ThrowHelper.ThrowOverflowException(); } return new Int128(value._upper, value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="IntPtr" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="IntPtr" />.</returns> public static explicit operator nint(UInt128 value) => (nint)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="IntPtr" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="IntPtr" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked nint(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((nint)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="sbyte" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="sbyte" />.</returns> [CLSCompliant(false)] public static explicit operator sbyte(UInt128 value) => (sbyte)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="sbyte" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="sbyte" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> [CLSCompliant(false)] public static explicit operator checked sbyte(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((sbyte)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="float" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="float" />.</returns> public static explicit operator float(UInt128 value) => (float)(double)(value); /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="ushort" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="ushort" />.</returns> [CLSCompliant(false)] public static explicit operator ushort(UInt128 value) => (ushort)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="ushort" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="ushort" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> [CLSCompliant(false)] public static explicit operator checked ushort(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((ushort)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="uint" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="uint" />.</returns> [CLSCompliant(false)] public static explicit operator uint(UInt128 value) => (uint)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="uint" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="uint" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> [CLSCompliant(false)] public static explicit operator checked uint(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((uint)value._lower); } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="ulong" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="ulong" />.</returns> [CLSCompliant(false)] public static explicit operator ulong(UInt128 value) => value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="ulong" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="ulong" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> [CLSCompliant(false)] public static explicit operator checked ulong(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return value._lower; } /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="UIntPtr" /> value.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="UIntPtr" />.</returns> [CLSCompliant(false)] public static explicit operator nuint(UInt128 value) => (nuint)value._lower; /// <summary>Explicitly converts a 128-bit unsigned integer to a <see cref="UIntPtr" /> value, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a <see cref="UIntPtr" />.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> [CLSCompliant(false)] public static explicit operator checked nuint(UInt128 value) { if (value._upper != 0) { ThrowHelper.ThrowOverflowException(); } return checked((nuint)value._lower); } // // Explicit Conversions To UInt128 // /// <summary>Explicitly converts a <see cref="decimal" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static explicit operator UInt128(decimal value) { value = decimal.Truncate(value); if (value < 0.0m) { ThrowHelper.ThrowOverflowException(); } return new UInt128(value.High, value.Low64); } /// <summary>Explicitly converts a <see cref="double" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static explicit operator UInt128(double value) { const double TwoPow128 = 340282366920938463463374607431768211456.0; if (double.IsNegative(value) || double.IsNaN(value)) { return MinValue; } else if (value >= TwoPow128) { return MaxValue; } return ToUInt128(value); } /// <summary>Explicitly converts a <see cref="double" /> value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked UInt128(double value) { const double TwoPow128 = 340282366920938463463374607431768211456.0; // We need to convert -0.0 to 0 and not throw, so we compare // value against 0 rather than checking IsNegative if ((value < 0.0) || double.IsNaN(value) || (value >= TwoPow128)) { ThrowHelper.ThrowOverflowException(); } return ToUInt128(value); } internal static UInt128 ToUInt128(double value) { const double TwoPow128 = 340282366920938463463374607431768211456.0; Debug.Assert(value >= 0); Debug.Assert(double.IsFinite(value)); Debug.Assert(value < TwoPow128); // This code is based on `f64_to_u128` from m-ou-se/floatconv // Copyright (c) 2020 Mara Bos <m-ou.se@m-ou.se>. All rights reserved. // // Licensed under the BSD 2 - Clause "Simplified" License // See THIRD-PARTY-NOTICES.TXT for the full license text if (value >= 1.0) { // In order to convert from double to uint128 we first need to extract the signficand, // including the implicit leading bit, as a full 128-bit significand. We can then adjust // this down to the represented integer by right shifting by the unbiased exponent, taking // into account the significand is now represented as 128-bits. ulong bits = BitConverter.DoubleToUInt64Bits(value); UInt128 result = new UInt128((bits << 12) >> 1 | 0x8000_0000_0000_0000, 0x0000_0000_0000_0000); result >>= (1023 + 128 - 1 - (int)(bits >> 52)); return result; } else { return MinValue; } } /// <summary>Explicitly converts a <see cref="short" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static explicit operator UInt128(short value) { long lower = value; return new UInt128((ulong)(lower >> 63), (ulong)lower); } /// <summary>Explicitly converts a <see cref="short" /> value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked UInt128(short value) { if (value < 0) { ThrowHelper.ThrowOverflowException(); } return new UInt128(0, (ushort)value); } /// <summary>Explicitly converts a <see cref="int" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static explicit operator UInt128(int value) { long lower = value; return new UInt128((ulong)(lower >> 63), (ulong)lower); } /// <summary>Explicitly converts a <see cref="int" /> value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked UInt128(int value) { if (value < 0) { ThrowHelper.ThrowOverflowException(); } return new UInt128(0, (uint)value); } /// <summary>Explicitly converts a <see cref="long" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static explicit operator UInt128(long value) { long lower = value; return new UInt128((ulong)(lower >> 63), (ulong)lower); } /// <summary>Explicitly converts a <see cref="long" /> value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked UInt128(long value) { if (value < 0) { ThrowHelper.ThrowOverflowException(); } return new UInt128(0, (ulong)value); } /// <summary>Explicitly converts a <see cref="IntPtr" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static explicit operator UInt128(nint value) { long lower = value; return new UInt128((ulong)(lower >> 63), (ulong)lower); } /// <summary>Explicitly converts a <see cref="IntPtr" /> value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked UInt128(nint value) { if (value < 0) { ThrowHelper.ThrowOverflowException(); } return new UInt128(0, (nuint)value); } /// <summary>Explicitly converts a <see cref="sbyte" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> [CLSCompliant(false)] public static explicit operator UInt128(sbyte value) { long lower = value; return new UInt128((ulong)(lower >> 63), (ulong)lower); } /// <summary>Explicitly converts a <see cref="sbyte" /> value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> [CLSCompliant(false)] public static explicit operator checked UInt128(sbyte value) { if (value < 0) { ThrowHelper.ThrowOverflowException(); } return new UInt128(0, (byte)value); } /// <summary>Explicitly converts a <see cref="float" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static explicit operator UInt128(float value) => (UInt128)(double)(value); /// <summary>Explicitly converts a <see cref="float" /> value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="UInt128" />.</exception> public static explicit operator checked UInt128(float value) => checked((UInt128)(double)(value)); // // Implicit Conversions To UInt128 // /// <summary>Implicitly converts a <see cref="byte" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static implicit operator UInt128(byte value) => new UInt128(0, value); /// <summary>Implicitly converts a <see cref="char" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> public static implicit operator UInt128(char value) => new UInt128(0, value); /// <summary>Implicitly converts a <see cref="ushort" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> [CLSCompliant(false)] public static implicit operator UInt128(ushort value) => new UInt128(0, value); /// <summary>Implicitly converts a <see cref="uint" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> [CLSCompliant(false)] public static implicit operator UInt128(uint value) => new UInt128(0, value); /// <summary>Implicitly converts a <see cref="ulong" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> [CLSCompliant(false)] public static implicit operator UInt128(ulong value) => new UInt128(0, value); /// <summary>Implicitly converts a <see cref="UIntPtr" /> value to a 128-bit unsigned integer.</summary> /// <param name="value">The value to convert.</param> /// <returns><paramref name="value" /> converted to a 128-bit unsigned integer.</returns> [CLSCompliant(false)] public static implicit operator UInt128(nuint value) => new UInt128(0, value); // // IAdditionOperators // /// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" /> public static UInt128 operator +(UInt128 left, UInt128 right) { // For unsigned addition, we can detect overflow by checking `(x + y) < x` // This gives us the carry to add to upper to compute the correct result ulong lower = left._lower + right._lower; ulong carry = (lower < left._lower) ? 1UL : 0UL; ulong upper = left._upper + right._upper + carry; return new UInt128(upper, lower); } /// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" /> public static UInt128 operator checked +(UInt128 left, UInt128 right) { // For unsigned addition, we can detect overflow by checking `(x + y) < x` // This gives us the carry to add to upper to compute the correct result ulong lower = left._lower + right._lower; ulong carry = (lower < left._lower) ? 1UL : 0UL; ulong upper = checked(left._upper + right._upper + carry); return new UInt128(upper, lower); } // // IAdditiveIdentity // /// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" /> static UInt128 IAdditiveIdentity<UInt128, UInt128>.AdditiveIdentity => default; // // IBinaryInteger // /// <inheritdoc cref="IBinaryInteger{TSelf}.DivRem(TSelf, TSelf)" /> public static (UInt128 Quotient, UInt128 Remainder) DivRem(UInt128 left, UInt128 right) { if (right._upper == 0) { return Divide128BitsBy64Bits(left, right._lower); } if (right._upper >= left._upper) { return left._upper == right._upper && left._lower >= right._lower ? (One, left._lower - right._lower) : (Zero, left); } return DivideSlow(left, right); static (ulong Quotient, UInt128 Remainder) DivideSlow(UInt128 left, UInt128 right) { Debug.Assert(left > right); Debug.Assert(right > ulong.MaxValue); // Executes the "grammar-school" algorithm for computing q = a / b. // Before calculating q_i, we get more bits into the highest bit // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. ulong divHi = right._upper; ulong divLo = right._lower; ulong valHi; ulong valMi = left._upper; ulong valLo = left._lower; // We measure the leading zeros of the divisor int shift = BitOperations.LeadingZeroCount(divHi); // And, we make sure the most significant bit is set if (shift > 0) { int backShift = sizeof(ulong) * 8 - shift; divHi = (divHi << shift) | (divLo >> backShift); divLo <<= shift; valHi = valMi >> backShift; valMi = (valMi << shift) | (valLo >> backShift); valLo <<= shift; } else { valHi = 0; } // First guess for the current digit of the quotient, // which naturally must have only native-width bits... ulong q = valHi < divHi ? Divide128BitsBy64BitsCore(valHi, valMi, divHi).Quotient : ulong.MaxValue; // We multiply the two most significant limbs of the divisor // with the current guess for the quotient. If those are bigger // than the three most significant limbs of the current dividend // we return true, which means the current guess is still too big. UInt128 valMiLo = new UInt128(valMi, valLo); ulong chkHi = Math.BigMul(divHi, q, out ulong chkHiLo); ulong chkLoHi = Math.BigMul(divLo, q, out ulong chkLo); ulong chkMi = chkHiLo + chkLoHi; if (chkMi < chkLoHi) { chkHi++; } UInt128 divisor = new UInt128(divHi, divLo); UInt128 chkMiLo = new UInt128(chkMi, chkLo); while ((chkHi > valHi) || ((chkHi == valHi) && chkMiLo > valMiLo)) { q--; if (chkMiLo < divisor) { chkHi--; } chkMiLo -= divisor; } UInt128 rem = valMiLo - chkMiLo; Debug.Assert(valHi - chkHi is 0 or 1); Debug.Assert(left == right * q + (rem >> shift)); return (q, rem >> shift); } static (UInt128 Quotient, ulong Remainder) Divide128BitsBy64Bits(UInt128 left, ulong divisor) { if (divisor == 0) { ThrowHelper.ThrowDivideByZeroException(); } ulong highRes, leftUpper; if (left._upper < divisor) { if (left._upper == 0) { // left and right are both uint64 return ulong.DivRem(left._lower, divisor); } highRes = 0; leftUpper = left._upper; } else { (highRes, leftUpper) = Math.DivRem(left._upper, divisor); } #pragma warning disable SYSLIB5004 // X86Base.DivRem is experimental if (X86Base.X64.IsSupported) { (ulong lowRes, ulong remainder) = X86Base.X64.DivRem(left._lower, leftUpper, divisor); return (new UInt128(highRes, lowRes), remainder); } #pragma warning restore SYSLIB5004 else { ulong leftLower = left._lower; int shift = BitOperations.LeadingZeroCount(divisor); if (shift > 0) { divisor <<= shift; leftUpper = (leftUpper << shift) | (leftLower >> (64 - shift)); leftLower <<= shift; } (ulong lowRes, ulong remainder) = Divide128BitsBy64BitsCore(leftUpper, leftLower, divisor); return (new UInt128(highRes, lowRes), remainder >> shift); } } static (ulong Quotient, ulong Remainder) Divide128BitsBy64BitsCore(ulong hi, ulong lo, ulong divisor) { // Compute (hi * 2^64 + lo) / divisor. // hi < divisor is guaranteed by callers, so quotient fits in 64 bits. Debug.Assert(hi < divisor); Debug.Assert(BitOperations.LeadingZeroCount(divisor) == 0); if (hi == 0) { return ulong.DivRem(lo, divisor); } #pragma warning disable SYSLIB5004 // X86Base.DivRem is experimental if (X86Base.X64.IsSupported) { return X86Base.X64.DivRem(lo, hi, divisor); } #pragma warning restore SYSLIB5004 // Perform 128-bit/64-bit division by splitting it into 32-bit parts. // // dividend = | hi | lo | // divisor = | | divisor | (ulong q1, ulong r1) = Divide96BitsBy64Bits(hi, (uint)(lo >> 32), divisor); (ulong q2, ulong rem) = Divide96BitsBy64Bits(r1, (uint)lo, divisor); ulong quo = (q1 << 32) | q2; Debug.Assert(rem < divisor); Debug.Assert(Math.BigMul(quo, divisor) + rem == (new UInt128(hi, lo))); return (quo, rem); } static (uint Quotient, ulong Remainder) Divide96BitsBy64Bits(ulong hiMi, uint lo, ulong divisor) { // Divide 96-bits by 64-bits // dividend = | hi | mi | lo | // divisor = | | divisor | // First guess for the current digit of the quotient, // which naturally must have only 32 bits... ulong qUL = hiMi / (divisor >> 32); uint q = qUL > 0xFFFFFFFF ? 0xFFFFFFFF : (uint)qUL; // Our first guess may be a little bit to big // and subtract our current quotient -> (hi:lm) -= divisor * q // In the original Knuth's algorithm, the method is determined // using only the high bits of the left and right values, which // can lead to cases where `left < q * right`. // However, in this 96-bit by 64-bit division, all bits are // used, so that concern does not apply. // // current | mHi | mLo | // - | divisor * q | // --------------------------------- // remainder | | | mi | uint valHi = (uint)(hiMi >> 32); ulong valMiLo = (hiMi << 32) | lo; // We multiply the two most significant limbs of the divisor // with the current guess for the quotient. If those are bigger // than the three most significant limbs of the current dividend // we return true, which means the current guess is still too big. ulong chkHiUL = Math.BigMul(divisor, q, out ulong chkLo); Debug.Assert(chkHiUL <= uint.MaxValue); uint chkHi = (uint)chkHiUL; while ((chkHi > valHi) || ((chkHi == valHi) && (chkLo > valMiLo))) { Debug.Assert(new UInt128(valHi, valMiLo) < new UInt128(chkHi, chkLo)); q--; if (chkLo < divisor) { chkHi--; } chkLo -= divisor; } ulong remainder = valMiLo - chkLo; Debug.Assert(remainder < divisor); Debug.Assert(new UInt128(valHi, valMiLo) == new UInt128(chkHi, chkLo) + remainder); return (q, remainder); } } /// <inheritdoc cref="IBinaryInteger{TSelf}.LeadingZeroCount(TSelf)" /> public static UInt128 LeadingZeroCount(UInt128 value) => (uint)LeadingZeroCountAsInt32(value); /// <summary>Computes the number of leading zero bits in this value.</summary> [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int LeadingZeroCountAsInt32(UInt128 value) { if (value._upper == 0) { return 64 + BitOperations.LeadingZeroCount(value._lower); } return BitOperations.LeadingZeroCount(value._upper); } /// <inheritdoc cref="IBinaryInteger{TSelf}.Log10(TSelf)" /> public static UInt128 Log10(UInt128 value) { if (value._upper == 0) { return ulong.Log10(value._lower); } // Approximate log10 via log2, then correct with a powers of 10 lookup table. // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 value |= 1U; uint log2 = (uint)Log2(value) + 1; uint approx = (log2 * 1233) >> 12; return value < PowersOf10[(int)approx] ? approx - 1 : approx; } // Lookup table for power-of-10 boundaries corrections private static readonly UInt128[] PowersOf10 = [ new UInt128(0, 1UL), new UInt128(0, 10UL), new UInt128(0, 100UL), new UInt128(0, 1_000UL), new UInt128(0, 10_000UL), new UInt128(0, 100_000UL), new UInt128(0, 1_000_000UL), new UInt128(0, 10_000_000UL), new UInt128(0, 100_000_000UL), new UInt128(0, 1_000_000_000UL), new UInt128(0, 10_000_000_000UL), new UInt128(0, 100_000_000_000UL), new UInt128(0, 1_000_000_000_000UL), new UInt128(0, 10_000_000_000_000UL), new UInt128(0, 100_000_000_000_000UL), new UInt128(0, 1_000_000_000_000_000UL), new UInt128(0, 10_000_000_000_000_000UL), new UInt128(0, 100_000_000_000_000_000UL), new UInt128(0, 1_000_000_000_000_000_000UL), new UInt128(0, 10_000_000_000_000_000_000UL), new UInt128(5, 7766279631452241920UL), new UInt128(54, 3875820019684212736UL), new UInt128(542, 1864712049423024128UL), new UInt128(5421, 200376420520689664UL), new UInt128(54210, 2003764205206896640UL), new UInt128(542101, 1590897978359414784UL), new UInt128(5421010, 15908979783594147840UL), new UInt128(54210108, 11515845246265065472UL), new UInt128(542101086, 4477988020393345024UL), new UInt128(5421010862, 7886392056514347008UL), new UInt128(54210108624, 5076944270305263616UL), new UInt128(542101086242, 13875954555633532928UL), new UInt128(5421010862427, 9632337040368467968UL), new UInt128(54210108624275, 4089650035136921600UL), new UInt128(542101086242752, 4003012203950112768UL), new UInt128(5421010862427522, 3136633892082024448UL), new UInt128(54210108624275221, 12919594847110692864UL), new UInt128(542101086242752217, 68739955140067328UL), new UInt128(5421010862427522170, 687399551400673280UL), ]; /// <inheritdoc cref="IBinaryInteger{TSelf}.PopCount(TSelf)" /> public static UInt128 PopCount(UInt128 value) => ulong.PopCount(value._lower) + ulong.PopCount(value._upper); /// <inheritdoc cref="IBinaryInteger{TSelf}.RotateLeft(TSelf, int)" /> public static UInt128 RotateLeft(UInt128 value, int rotateAmount) => (value << rotateAmount) | (value >>> (128 - rotateAmount)); /// <inheritdoc cref="IBinaryInteger{TSelf}.RotateRight(TSelf, int)" /> public static UInt128 RotateRight(UInt128 value, int rotateAmount) => (value >>> rotateAmount) | (value << (128 - rotateAmount)); /// <inheritdoc cref="IBinaryInteger{TSelf}.TrailingZeroCount(TSelf)" /> public static UInt128 TrailingZeroCount(UInt128 value) { if (value._lower == 0) { return 64 + ulong.TrailingZeroCount(value._upper); } return ulong.TrailingZeroCount(value._lower); } /// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadBigEndian(ReadOnlySpan{byte}, bool, out TSelf)" /> static bool IBinaryInteger<UInt128>.TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out UInt128 value) { UInt128 result = default; if (source.Length != 0) { if (!isUnsigned && sbyte.IsNegative((sbyte)source[0])) { // When we are signed and the sign bit is set, we are negative and therefore // definitely out of range value = result; return false; } if ((source.Length > Size) && (source[..^Size].ContainsAnyExcept((byte)0x00))) { // When we have any non-zero leading data, we are a large positive and therefore // definitely out of range value = result; return false; } if (source.Length >= Size) { // We have at least 16 bytes, so just read the ones we need directly result = BinaryPrimitives.ReadUInt128BigEndian(source.Slice(source.Length - Size)); } else { // We have between 1 and 15 bytes, so construct the relevant value directly // since the data is in Big Endian format, we can just read the bytes and // shift left by 8-bits for each subsequent part for (int i = 0; i < source.Length; i++) { result <<= 8; result |= source[i]; } } } value = result; return true; } /// <inheritdoc cref="IBinaryInteger{TSelf}.TryReadLittleEndian(ReadOnlySpan{byte}, bool, out TSelf)" /> static bool IBinaryInteger<UInt128>.TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out UInt128 value) { UInt128 result = default; if (source.Length != 0) { if (!isUnsigned && sbyte.IsNegative((sbyte)source[^1])) { // When we are signed and the sign bit is set, we are negative and therefore // definitely out of range value = result; return false; } if ((source.Length > Size) && (source[Size..].ContainsAnyExcept((byte)0x00))) { // When we have any non-zero leading data, we are a large positive and therefore // definitely out of range value = result; return false; } if (source.Length >= Size) { // We have at least 16 bytes, so just read the ones we need directly result = BinaryPrimitives.ReadUInt128LittleEndian(source); } else { // We have between 1 and 15 bytes, so construct the relevant value directly // since the data is in Little Endian format, we can just read the bytes and // shift left by 8-bits for each subsequent part, then reverse endianness to // ensure the order is correct. This is more efficient than iterating in reverse // due to current JIT limitations for (int i = 0; i < source.Length; i++) { UInt128 part = source[i]; part <<= (i * 8); result |= part; } } } value = result; return true; } /// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" /> int IBinaryInteger<UInt128>.GetShortestBitLength() { return (Size * 8) - LeadingZeroCountAsInt32(this); } /// <inheritdoc cref="IBinaryInteger{TSelf}.GetByteCount()" /> int IBinaryInteger<UInt128>.GetByteCount() => Size; /// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteBigEndian(Span{byte}, out int)" /> bool IBinaryInteger<UInt128>.TryWriteBigEndian(Span<byte> destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt128BigEndian(destination, this)) { bytesWritten = Size; return true; } bytesWritten = 0; return false; } /// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteLittleEndian(Span{byte}, out int)" /> bool IBinaryInteger<UInt128>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten) { if (BinaryPrimitives.TryWriteUInt128LittleEndian(destination, this)) { bytesWritten = Size; return true; } bytesWritten = 0; return false; } // // IBinaryNumber // /// <inheritdoc cref="IBinaryNumber{TSelf}.AllBitsSet" /> static UInt128 IBinaryNumber<UInt128>.AllBitsSet => new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF); /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" /> public static bool IsPow2(UInt128 value) => PopCount(value) == 1U; /// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" /> public static UInt128 Log2(UInt128 value) { if (value._upper == 0) { return ulong.Log2(value._lower); } return 64 + ulong.Log2(value._upper); } // // IBitwiseOperators // /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_BitwiseAnd(TSelf, TOther)" /> public static UInt128 operator &(UInt128 left, UInt128 right) => new UInt128(left._upper & right._upper, left._lower & right._lower); /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_BitwiseOr(TSelf, TOther)" /> public static UInt128 operator |(UInt128 left, UInt128 right) => new UInt128(left._upper | right._upper, left._lower | right._lower); /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_ExclusiveOr(TSelf, TOther)" /> public static UInt128 operator ^(UInt128 left, UInt128 right) => new UInt128(left._upper ^ right._upper, left._lower ^ right._lower); /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_OnesComplement(TSelf)" /> public static UInt128 operator ~(UInt128 value) => new UInt128(~value._upper, ~value._lower); // // IComparisonOperators // /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThan(TSelf, TOther)" /> public static bool operator <(UInt128 left, UInt128 right) { return (left._upper < right._upper) || (left._upper == right._upper) && (left._lower < right._lower); } /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_LessThanOrEqual(TSelf, TOther)" /> public static bool operator <=(UInt128 left, UInt128 right) { return (left._upper < right._upper) || (left._upper == right._upper) && (left._lower <= right._lower); } /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThan(TSelf, TOther)" /> public static bool operator >(UInt128 left, UInt128 right) { return (left._upper > right._upper) || (left._upper == right._upper) && (left._lower > right._lower); } /// <inheritdoc cref="IComparisonOperators{TSelf, TOther, TResult}.op_GreaterThanOrEqual(TSelf, TOther)" /> public static bool operator >=(UInt128 left, UInt128 right) { return (left._upper > right._upper) || (left._upper == right._upper) && (left._lower >= right._lower); } // // IDecrementOperators // /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" /> public static UInt128 operator --(UInt128 value) => value - One; /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" /> public static UInt128 operator checked --(UInt128 value) => checked(value - One); // // IDivisionOperators // /// <inheritdoc cref="IDivisionOperators{TSelf, TOther, TResult}.op_Division(TSelf, TOther)" /> public static UInt128 operator /(UInt128 left, UInt128 right) => DivRem(left, right).Quotient; /// <inheritdoc cref="IDivisionOperators{TSelf, TOther, TResult}.op_CheckedDivision(TSelf, TOther)" /> public static UInt128 operator checked /(UInt128 left, UInt128 right) => left / right; // // IEqualityOperators // /// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Equality(TSelf, TOther)" /> public static bool operator ==(UInt128 left, UInt128 right) => (left._lower == right._lower) && (left._upper == right._upper); /// <inheritdoc cref="IEqualityOperators{TSelf, TOther, TResult}.op_Inequality(TSelf, TOther)" /> public static bool operator !=(UInt128 left, UInt128 right) => (left._lower != right._lower) || (left._upper != right._upper); // // IIncrementOperators // /// <inheritdoc cref="IIncrementOperators{TSelf}.op_Increment(TSelf)" /> public static UInt128 operator ++(UInt128 value) => value + One; /// <inheritdoc cref="IIncrementOperators{TSelf}.op_CheckedIncrement(TSelf)" /> public static UInt128 operator checked ++(UInt128 value) => checked(value + One); // // IMinMaxValue // /// <inheritdoc cref="IMinMaxValue{TSelf}.MinValue" /> public static UInt128 MinValue => new UInt128(0, 0); /// <inheritdoc cref="IMinMaxValue{TSelf}.MaxValue" /> public static UInt128 MaxValue => new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF); // // IModulusOperators // /// <inheritdoc cref="IModulusOperators{TSelf, TOther, TResult}.op_Modulus(TSelf, TOther)" /> public static UInt128 operator %(UInt128 left, UInt128 right) => DivRem(left, right).Remainder; // // IMultiplicativeIdentity // /// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" /> static UInt128 IMultiplicativeIdentity<UInt128, UInt128>.MultiplicativeIdentity => One; // // IMultiplyOperators // /// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_Multiply(TSelf, TOther)" /> public static UInt128 operator *(UInt128 left, UInt128 right) { ulong upper = Math.BigMul(left._lower, right._lower, out ulong lower); upper += (left._upper * right._lower) + (left._lower * right._upper); return new UInt128(upper, lower); } /// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_CheckedMultiply(TSelf, TOther)" /> public static UInt128 operator checked *(UInt128 left, UInt128 right) { UInt128 upper = BigMul(left, right, out UInt128 lower); if (upper != 0U) { ThrowHelper.ThrowOverflowException(); } return lower; } /// <summary>Produces the full product of two unsigned native integers.</summary> /// <param name="left">The integer to multiply with <paramref name="right" />.</param> /// <param name="right">The integer to multiply with <paramref name="left" />.</param> /// <param name="lower">The lower half of the full product.</param> /// <returns>The upper half of the full product.</returns> public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) { // Adaptation of algorithm for multiplication // of 32-bit unsigned integers described // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8 // Basically, it's an optimized version of FOIL method applied to // low and high qwords of each operand ulong al = left._lower; ulong ah = left._upper; ulong bl = right._lower; ulong bh = right._upper; UInt128 mull = Math.BigMul(al, bl); UInt128 t = Math.BigMul(ah, bl) + mull._upper; UInt128 tl = Math.BigMul(al, bh) + t._lower; lower = new UInt128(tl._lower, mull._lower); return Math.BigMul(ah, bh) + t._upper + tl._upper; } // // INumber // /// <inheritdoc cref="INumber{TSelf}.Clamp(TSelf, TSelf, TSelf)" /> public static UInt128 Clamp(UInt128 value, UInt128 min, UInt128 max) { if (min > max) { Math.ThrowMinMaxException(min, max); } if (value < min) { return min; } else if (value > max) { return max; } return value; } /// <inheritdoc cref="INumber{TSelf}.CopySign(TSelf, TSelf)" /> static UInt128 INumber<UInt128>.CopySign(UInt128 value, UInt128 sign) => value; /// <inheritdoc cref="INumber{TSelf}.Max(TSelf, TSelf)" /> public static UInt128 Max(UInt128 x, UInt128 y) => (x >= y) ? x : y; /// <inheritdoc cref="INumber{TSelf}.MaxNumber(TSelf, TSelf)" /> static UInt128 INumber<UInt128>.MaxNumber(UInt128 x, UInt128 y) => Max(x, y); /// <inheritdoc cref="INumber{TSelf}.Min(TSelf, TSelf)" /> public static UInt128 Min(UInt128 x, UInt128 y) => (x <= y) ? x : y; /// <inheritdoc cref="INumber{TSelf}.MinNumber(TSelf, TSelf)" /> static UInt128 INumber<UInt128>.MinNumber(UInt128 x, UInt128 y) => Min(x, y); /// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" /> public static int Sign(UInt128 value) => (value == 0U) ? 0 : 1; // // INumberBase // /// <inheritdoc cref="INumberBase{TSelf}.One" /> public static UInt128 One => new UInt128(0, 1); /// <inheritdoc cref="INumberBase{TSelf}.Radix" /> static int INumberBase<UInt128>.Radix => 2; /// <inheritdoc cref="INumberBase{TSelf}.Zero" /> public static UInt128 Zero => default; /// <inheritdoc cref="INumberBase{TSelf}.Abs(TSelf)" /> static UInt128 INumberBase<UInt128>.Abs(UInt128 value) => value; /// <inheritdoc cref="INumberBase{TSelf}.CreateChecked{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static UInt128 CreateChecked<TOther>(TOther value) where TOther : INumberBase<TOther> { UInt128 result; if (typeof(TOther) == typeof(UInt128)) { result = (UInt128)(object)value; } else if (!TryConvertFromChecked(value, out result) && !TOther.TryConvertToChecked(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.CreateSaturating{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static UInt128 CreateSaturating<TOther>(TOther value) where TOther : INumberBase<TOther> { UInt128 result; if (typeof(TOther) == typeof(UInt128)) { result = (UInt128)(object)value; } else if (!TryConvertFromSaturating(value, out result) && !TOther.TryConvertToSaturating(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.CreateTruncating{TOther}(TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] public static UInt128 CreateTruncating<TOther>(TOther value) where TOther : INumberBase<TOther> { UInt128 result; if (typeof(TOther) == typeof(UInt128)) { result = (UInt128)(object)value; } else if (!TryConvertFromTruncating(value, out result) && !TOther.TryConvertToTruncating(value, out result)) { ThrowHelper.ThrowNotSupportedException(); } return result; } /// <inheritdoc cref="INumberBase{TSelf}.IsCanonical(TSelf)" /> static bool INumberBase<UInt128>.IsCanonical(UInt128 value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsComplexNumber(TSelf)" /> static bool INumberBase<UInt128>.IsComplexNumber(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsEvenInteger(TSelf)" /> public static bool IsEvenInteger(UInt128 value) => (value._lower & 1) == 0; /// <inheritdoc cref="INumberBase{TSelf}.IsFinite(TSelf)" /> static bool INumberBase<UInt128>.IsFinite(UInt128 value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsImaginaryNumber(TSelf)" /> static bool INumberBase<UInt128>.IsImaginaryNumber(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsInfinity(TSelf)" /> static bool INumberBase<UInt128>.IsInfinity(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsInteger(TSelf)" /> static bool INumberBase<UInt128>.IsInteger(UInt128 value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsNaN(TSelf)" /> static bool INumberBase<UInt128>.IsNaN(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsNegative(TSelf)" /> static bool INumberBase<UInt128>.IsNegative(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsNegativeInfinity(TSelf)" /> static bool INumberBase<UInt128>.IsNegativeInfinity(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsNormal(TSelf)" /> static bool INumberBase<UInt128>.IsNormal(UInt128 value) => value != 0U; /// <inheritdoc cref="INumberBase{TSelf}.IsOddInteger(TSelf)" /> public static bool IsOddInteger(UInt128 value) => (value._lower & 1) != 0; /// <inheritdoc cref="INumberBase{TSelf}.IsPositive(TSelf)" /> static bool INumberBase<UInt128>.IsPositive(UInt128 value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsPositiveInfinity(TSelf)" /> static bool INumberBase<UInt128>.IsPositiveInfinity(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsRealNumber(TSelf)" /> static bool INumberBase<UInt128>.IsRealNumber(UInt128 value) => true; /// <inheritdoc cref="INumberBase{TSelf}.IsSubnormal(TSelf)" /> static bool INumberBase<UInt128>.IsSubnormal(UInt128 value) => false; /// <inheritdoc cref="INumberBase{TSelf}.IsZero(TSelf)" /> static bool INumberBase<UInt128>.IsZero(UInt128 value) => (value == 0U); /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitude(TSelf, TSelf)" /> static UInt128 INumberBase<UInt128>.MaxMagnitude(UInt128 x, UInt128 y) => Max(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MaxMagnitudeNumber(TSelf, TSelf)" /> static UInt128 INumberBase<UInt128>.MaxMagnitudeNumber(UInt128 x, UInt128 y) => Max(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitude(TSelf, TSelf)" /> static UInt128 INumberBase<UInt128>.MinMagnitude(UInt128 x, UInt128 y) => Min(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MinMagnitudeNumber(TSelf, TSelf)" /> static UInt128 INumberBase<UInt128>.MinMagnitudeNumber(UInt128 x, UInt128 y) => Min(x, y); /// <inheritdoc cref="INumberBase{TSelf}.MultiplyAddEstimate(TSelf, TSelf, TSelf)" /> static UInt128 INumberBase<UInt128>.MultiplyAddEstimate(UInt128 left, UInt128 right, UInt128 addend) => (left * right) + addend; /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromChecked{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<UInt128>.TryConvertFromChecked<TOther>(TOther value, out UInt128 result) => TryConvertFromChecked(value, out result); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertFromChecked<TOther>(TOther value, out UInt128 result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `UInt128` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(byte)) { byte actualValue = (byte)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(char)) { char actualValue = (char)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(decimal)) { decimal actualValue = (decimal)(object)value; result = checked((UInt128)actualValue); return true; } else if (typeof(TOther) == typeof(ushort)) { ushort actualValue = (ushort)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(uint)) { uint actualValue = (uint)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(ulong)) { ulong actualValue = (ulong)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(nuint)) { nuint actualValue = (nuint)(object)value; result = actualValue; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromSaturating{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<UInt128>.TryConvertFromSaturating<TOther>(TOther value, out UInt128 result) => TryConvertFromSaturating(value, out result); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertFromSaturating<TOther>(TOther value, out UInt128 result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `UInt128` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(byte)) { byte actualValue = (byte)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(char)) { char actualValue = (char)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(decimal)) { decimal actualValue = (decimal)(object)value; result = (actualValue < 0) ? MinValue : (UInt128)actualValue; return true; } else if (typeof(TOther) == typeof(ushort)) { ushort actualValue = (ushort)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(uint)) { uint actualValue = (uint)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(ulong)) { ulong actualValue = (ulong)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(nuint)) { nuint actualValue = (nuint)(object)value; result = actualValue; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertFromTruncating{TOther}(TOther, out TSelf)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<UInt128>.TryConvertFromTruncating<TOther>(TOther value, out UInt128 result) => TryConvertFromTruncating(value, out result); [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool TryConvertFromTruncating<TOther>(TOther value, out UInt128 result) where TOther : INumberBase<TOther> { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `UInt128` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(byte)) { byte actualValue = (byte)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(char)) { char actualValue = (char)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(decimal)) { decimal actualValue = (decimal)(object)value; result = (actualValue < 0) ? MinValue : (UInt128)actualValue; return true; } else if (typeof(TOther) == typeof(ushort)) { ushort actualValue = (ushort)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(uint)) { uint actualValue = (uint)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(ulong)) { ulong actualValue = (ulong)(object)value; result = actualValue; return true; } else if (typeof(TOther) == typeof(nuint)) { nuint actualValue = (nuint)(object)value; result = actualValue; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToChecked{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<UInt128>.TryConvertToChecked<TOther>(UInt128 value, [MaybeNullWhen(false)] out TOther result) { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `UInt128` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(double)) { double actualResult = (double)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Half)) { Half actualResult = (Half)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(short)) { short actualResult = checked((short)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(int)) { int actualResult = checked((int)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(long)) { long actualResult = checked((long)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Int128)) { Int128 actualResult = checked((Int128)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(nint)) { nint actualResult = checked((nint)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(sbyte)) { sbyte actualResult = checked((sbyte)value); result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(float)) { float actualResult = (float)value; result = (TOther)(object)actualResult; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToSaturating{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<UInt128>.TryConvertToSaturating<TOther>(UInt128 value, [MaybeNullWhen(false)] out TOther result) { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `UInt128` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(double)) { double actualResult = (double)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Half)) { Half actualResult = (Half)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(short)) { short actualResult = (value >= new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_7FFF)) ? short.MaxValue : (short)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(int)) { int actualResult = (value >= new UInt128(0x0000_0000_0000_0000, 0x0000_0000_7FFF_FFFF)) ? int.MaxValue : (int)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(long)) { long actualResult = (value >= new UInt128(0x0000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF)) ? long.MaxValue : (long)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Int128)) { Int128 actualResult = (value >= new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? Int128.MaxValue : (Int128)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(nint)) { #if TARGET_32BIT nint actualResult = (value >= new UInt128(0x0000_0000_0000_0000, 0x0000_0000_7FFF_FFFF)) ? nint.MaxValue : (nint)value; result = (TOther)(object)actualResult; return true; #else nint actualResult = (value >= new UInt128(0x0000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF)) ? nint.MaxValue : (nint)value; result = (TOther)(object)actualResult; return true; #endif } else if (typeof(TOther) == typeof(sbyte)) { sbyte actualResult = (value >= new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_007F)) ? sbyte.MaxValue : (sbyte)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(float)) { float actualResult = (float)value; result = (TOther)(object)actualResult; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryConvertToTruncating{TOther}(TSelf, out TOther)" /> [MethodImpl(MethodImplOptions.AggressiveInlining)] static bool INumberBase<UInt128>.TryConvertToTruncating<TOther>(UInt128 value, [MaybeNullWhen(false)] out TOther result) { // In order to reduce overall code duplication and improve the inlinabilty of these // methods for the corelib types we have `ConvertFrom` handle the same sign and // `ConvertTo` handle the opposite sign. However, since there is an uneven split // between signed and unsigned types, the one that handles unsigned will also // handle `Decimal`. // // That is, `ConvertFrom` for `UInt128` will handle the other unsigned types and // `ConvertTo` will handle the signed types if (typeof(TOther) == typeof(double)) { double actualResult = (double)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Half)) { Half actualResult = (Half)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(short)) { short actualResult = (short)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(int)) { int actualResult = (int)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(long)) { long actualResult = (long)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(Int128)) { Int128 actualResult = (Int128)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(nint)) { nint actualResult = (nint)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(sbyte)) { sbyte actualResult = (sbyte)value; result = (TOther)(object)actualResult; return true; } else if (typeof(TOther) == typeof(float)) { float actualResult = (float)value; result = (TOther)(object)actualResult; return true; } else { result = default; return false; } } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(string, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result, out int charsConsumed) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.TryParseBinaryInteger(s.AsSpan(), style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{char}, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out UInt128 result, out int charsConsumed) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.TryParseBinaryInteger(s, style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; } /// <inheritdoc cref="INumberBase{TSelf}.TryParsePartial(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf, out int)" /> public static bool TryParsePartial(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out UInt128 result, out int bytesConsumed) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.TryParseBinaryInteger(utf8Text, style | Number.AllowTrailingInvalidCharacters, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; } // // IParsable // /// <inheritdoc cref="IParsable{TSelf}.TryParse(string?, IFormatProvider?, out TSelf)" /> public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider, out result); // // IShiftOperators // /// <inheritdoc cref="IShiftOperators{TSelf, TOther, TResult}.op_LeftShift(TSelf, TOther)" /> public static UInt128 operator <<(UInt128 value, int shiftAmount) { // C# automatically masks the shift amount for UInt64 to be 0x3F. So we // need to specially handle things if the 7th bit is set. shiftAmount &= 0x7F; if ((shiftAmount & 0x40) != 0) { // In the case it is set, we know the entire lower bits must be zero // and so the upper bits are just the lower shifted by the remaining // masked amount ulong upper = value._lower << shiftAmount; return new UInt128(upper, 0); } else if (shiftAmount != 0) { // Otherwise we need to shift both upper and lower halves by the masked // amount and then or that with whatever bits were shifted "out" of lower ulong lower = value._lower << shiftAmount; ulong upper = (value._upper << shiftAmount) | (value._lower >> (64 - shiftAmount)); return new UInt128(upper, lower); } else { return value; } } /// <inheritdoc cref="IShiftOperators{TSelf, TOther, TResult}.op_RightShift(TSelf, TOther)" /> public static UInt128 operator >>(UInt128 value, int shiftAmount) => value >>> shiftAmount; /// <inheritdoc cref="IShiftOperators{TSelf, TOther, TResult}.op_UnsignedRightShift(TSelf, TOther)" /> public static UInt128 operator >>>(UInt128 value, int shiftAmount) { // C# automatically masks the shift amount for UInt64 to be 0x3F. So we // need to specially handle things if the 7th bit is set. shiftAmount &= 0x7F; if ((shiftAmount & 0x40) != 0) { // In the case it is set, we know the entire upper bits must be zero // and so the lower bits are just the upper shifted by the remaining // masked amount ulong lower = value._upper >> shiftAmount; return new UInt128(0, lower); } else if (shiftAmount != 0) { // Otherwise we need to shift both upper and lower halves by the masked // amount and then or that with whatever bits were shifted "out" of upper ulong lower = (value._lower >> shiftAmount) | (value._upper << (64 - shiftAmount)); ulong upper = value._upper >> shiftAmount; return new UInt128(upper, lower); } else { return value; } } // // ISpanParsable // /// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" /> public static UInt128 Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); /// <inheritdoc cref="ISpanParsable{TSelf}.TryParse(ReadOnlySpan{char}, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider, out result); // // ISubtractionOperators // /// <inheritdoc cref="ISubtractionOperators{TSelf, TOther, TResult}.op_Subtraction(TSelf, TOther)" /> public static UInt128 operator -(UInt128 left, UInt128 right) { // For unsigned subtract, we can detect overflow by checking `(x - y) > x` // This gives us the borrow to subtract from upper to compute the correct result ulong lower = left._lower - right._lower; ulong borrow = (lower > left._lower) ? 1UL : 0UL; ulong upper = left._upper - right._upper - borrow; return new UInt128(upper, lower); } /// <inheritdoc cref="ISubtractionOperators{TSelf, TOther, TResult}.op_CheckedSubtraction(TSelf, TOther)" /> public static UInt128 operator checked -(UInt128 left, UInt128 right) { // For unsigned subtract, we can detect overflow by checking `(x - y) > x` // This gives us the borrow to subtract from upper to compute the correct result ulong lower = left._lower - right._lower; ulong borrow = (lower > left._lower) ? 1UL : 0UL; ulong upper = checked(left._upper - right._upper - borrow); return new UInt128(upper, lower); } // // IUnaryNegationOperators // /// <inheritdoc cref="IUnaryNegationOperators{TSelf, TResult}.op_UnaryNegation(TSelf)" /> public static UInt128 operator -(UInt128 value) => Zero - value; /// <inheritdoc cref="IUnaryNegationOperators{TSelf, TResult}.op_CheckedUnaryNegation(TSelf)" /> public static UInt128 operator checked -(UInt128 value) => checked(Zero - value); // // IUnaryPlusOperators // /// <inheritdoc cref="IUnaryPlusOperators{TSelf, TResult}.op_UnaryPlus(TSelf)" /> public static UInt128 operator +(UInt128 value) => value; // // IUtf8SpanParsable // /// <inheritdoc cref="INumberBase{TSelf}.Parse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?)" /> public static UInt128 Parse(ReadOnlySpan<byte> utf8Text, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.ParseBinaryInteger<byte, UInt128>(utf8Text, style, NumberFormatInfo.GetInstance(provider)); } /// <inheritdoc cref="INumberBase{TSelf}.TryParse(ReadOnlySpan{byte}, NumberStyles, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<byte> utf8Text, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.Parse(ReadOnlySpan{byte}, IFormatProvider?)" /> public static UInt128 Parse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Integer, provider); /// <inheritdoc cref="IUtf8SpanParsable{TSelf}.TryParse(ReadOnlySpan{byte}, IFormatProvider?, out TSelf)" /> public static bool TryParse(ReadOnlySpan<byte> utf8Text, IFormatProvider? provider, out UInt128 result) => TryParse(utf8Text, NumberStyles.Integer, provider, out result); // // IBinaryIntegerParseAndFormatInfo // static bool IBinaryIntegerParseAndFormatInfo<UInt128>.IsSigned => false; static int IBinaryIntegerParseAndFormatInfo<UInt128>.MaxDigitCount => 39; // 340_282_366_920_938_463_463_374_607_431_768_211_455 static int IBinaryIntegerParseAndFormatInfo<UInt128>.MaxHexDigitCount => 32; // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF static UInt128 IBinaryIntegerParseAndFormatInfo<UInt128>.MaxValueDiv10 => new UInt128(0x1999_9999_9999_9999, 0x9999_9999_9999_9999); static string IBinaryIntegerParseAndFormatInfo<UInt128>.OverflowMessage => SR.Overflow_UInt128; static bool IBinaryIntegerParseAndFormatInfo<UInt128>.IsGreaterThanAsUnsigned(UInt128 left, UInt128 right) => left > right; static UInt128 IBinaryIntegerParseAndFormatInfo<UInt128>.MultiplyBy10(UInt128 value) => value * 10; static UInt128 IBinaryIntegerParseAndFormatInfo<UInt128>.MultiplyBy16(UInt128 value) => value * 16; } }