Adding Int128 and UInt128 with a base software implementation (#69204)
authorTanner Gooding <tagoo@outlook.com>
Thu, 19 May 2022 04:41:17 +0000 (21:41 -0700)
committerGitHub <noreply@github.com>
Thu, 19 May 2022 04:41:17 +0000 (21:41 -0700)
* Adding barebones Int128 and UInt128 structs

* Special case Int128 and UInt128 alignment on x64 Unix and Arm64

* Implementing Int128 and UInt128

* Adding tests for Int128 and UInt128

* Updating Int128/UInt128 to respect the System V ABI ordering

* Fixing an issue with UInt128->BigInteger setting the wrong sign

* Don't use Unsafe.As in the Int128/UInt128 hex parsing logic

* Adding Int128 P/Invoke tests and ensure R2R correctly sets the packing

* Fixing some issues with the Int128 interop test for non-Windows

* Ensure that floating-point conversions exist for Int128 and UInt128

* Fixing the casing of a couple fields

* Revert "Don't use Unsafe.As in the Int128/UInt128 hex parsing logic"

This reverts commit 09e8bfc52d5b1b7f7b341f318bf1046b19768dd3.

* Adjusting the Int128/UInt128 generic math tests to have consistent ordering

* Responding to PR feedback

* Ensure that pNativeLayoutInfo alignment is initialized for Int128/UInt128

* Don't use Unsafe.As in the Int128/UInt128 hex parsing logic

* Skip the Interop/PInvoke/Int128 tests on Mono

54 files changed:
THIRD-PARTY-NOTICES.TXT
src/coreclr/tools/Common/Compiler/Int128FieldLayoutAlgorithm.cs [new file with mode: 0644]
src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs
src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs
src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj
src/coreclr/vm/classlayoutinfo.cpp
src/coreclr/vm/classnames.h
src/coreclr/vm/corelib.h
src/coreclr/vm/methodtablebuilder.cpp
src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Buffers/Text/FormattingHelpers.CountDigits.cs
src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
src/libraries/System.Private.CoreLib/src/System/Decimal.cs
src/libraries/System.Private.CoreLib/src/System/Double.cs
src/libraries/System.Private.CoreLib/src/System/Half.cs
src/libraries/System.Private.CoreLib/src/System/IComparable.cs
src/libraries/System.Private.CoreLib/src/System/IEquatable.cs
src/libraries/System.Private.CoreLib/src/System/Int128.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Math.cs
src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
src/libraries/System.Private.CoreLib/src/System/Number.NumberBuffer.cs
src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs
src/libraries/System.Private.CoreLib/src/System/Object.cs
src/libraries/System.Private.CoreLib/src/System/Single.cs
src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
src/libraries/System.Private.CoreLib/src/System/UInt128.cs [new file with mode: 0644]
src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs
src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx
src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs
src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs
src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj
src/libraries/System.Runtime/tests/System/DecimalTests.GenericMath.cs
src/libraries/System.Runtime/tests/System/DoubleTests.GenericMath.cs
src/libraries/System.Runtime/tests/System/HalfTests.GenericMath.cs
src/libraries/System.Runtime/tests/System/Int128Tests.GenericMath.cs [new file with mode: 0644]
src/libraries/System.Runtime/tests/System/Int128Tests.cs [new file with mode: 0644]
src/libraries/System.Runtime/tests/System/SingleTests.GenericMath.cs
src/libraries/System.Runtime/tests/System/UInt128Tests.GenericMath.cs [new file with mode: 0644]
src/libraries/System.Runtime/tests/System/UInt128Tests.cs [new file with mode: 0644]
src/tests/Interop/CMakeLists.txt
src/tests/Interop/PInvoke/Int128/CMakeLists.txt [new file with mode: 0644]
src/tests/Interop/PInvoke/Int128/Int128Native.cpp [new file with mode: 0644]
src/tests/Interop/PInvoke/Int128/Int128Test.cs [new file with mode: 0644]
src/tests/Interop/PInvoke/Int128/Int128Test.csproj [new file with mode: 0644]
src/tests/Interop/PInvoke/Int128/Program.cs [new file with mode: 0644]
src/tests/Interop/PInvoke/Int128/UInt128Native.cpp [new file with mode: 0644]
src/tests/Interop/PInvoke/Int128/UInt128Test.cs [new file with mode: 0644]
src/tests/issues.targets

index fc250aa..307da15 100644 (file)
@@ -1072,4 +1072,30 @@ Copyright (c) Microsoft Corporation.
 Licensed under the MIT License.
 
 Available at
-https://github.com/microsoft/msquic/blob/main/LICENSE
\ No newline at end of file
+https://github.com/microsoft/msquic/blob/main/LICENSE
+
+License notice for m-ou-se/floatconv
+-------------------------------
+
+Copyright (c) 2020 Mara Bos <m-ou.se@m-ou.se>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/coreclr/tools/Common/Compiler/Int128FieldLayoutAlgorithm.cs b/src/coreclr/tools/Common/Compiler/Int128FieldLayoutAlgorithm.cs
new file mode 100644 (file)
index 0000000..60be29f
--- /dev/null
@@ -0,0 +1,79 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Internal.TypeSystem;
+
+using Debug = System.Diagnostics.Debug;
+
+namespace ILCompiler
+{
+    /// <summary>
+    /// Represents an algorithm that computes field layout for intrinsic integer types (Int128/UInt128).
+    /// </summary>
+    public class Int128FieldLayoutAlgorithm : FieldLayoutAlgorithm
+    {
+        private readonly FieldLayoutAlgorithm _fallbackAlgorithm;
+
+        public Int128FieldLayoutAlgorithm(FieldLayoutAlgorithm fallbackAlgorithm)
+        {
+            _fallbackAlgorithm = fallbackAlgorithm;
+        }
+
+        public override ComputedInstanceFieldLayout ComputeInstanceLayout(DefType defType, InstanceLayoutKind layoutKind)
+        {
+            Debug.Assert(IsIntegerType(defType));
+
+            string name = defType.Name;
+            Debug.Assert((name == "Int128") || (name == "UInt128"));
+
+            ComputedInstanceFieldLayout layoutFromMetadata = _fallbackAlgorithm.ComputeInstanceLayout(defType, layoutKind);
+
+            if (defType.Context.Target.IsWindows || (defType.Context.Target.PointerSize == 4))
+            {
+                return layoutFromMetadata;
+            }
+
+            // 64-bit Unix systems follow the System V ABI and have a 16-byte packing requirement for Int128/UInt128
+
+            return new ComputedInstanceFieldLayout
+            {
+                ByteCountUnaligned = layoutFromMetadata.ByteCountUnaligned,
+                ByteCountAlignment = layoutFromMetadata.ByteCountAlignment,
+                FieldAlignment = new LayoutInt(16),
+                FieldSize = layoutFromMetadata.FieldSize,
+                Offsets = layoutFromMetadata.Offsets,
+                LayoutAbiStable = true
+            };
+        }
+
+        public override ComputedStaticFieldLayout ComputeStaticFieldLayout(DefType defType, StaticLayoutKind layoutKind)
+        {
+            return _fallbackAlgorithm.ComputeStaticFieldLayout(defType, layoutKind);
+        }
+
+        public override bool ComputeContainsGCPointers(DefType type)
+        {
+            Debug.Assert(!_fallbackAlgorithm.ComputeContainsGCPointers(type));
+            return false;
+        }
+
+        public override bool ComputeIsUnsafeValueType(DefType type)
+        {
+            Debug.Assert(!_fallbackAlgorithm.ComputeIsUnsafeValueType(type));
+            return false;
+        }
+
+        public override ValueTypeShapeCharacteristics ComputeValueTypeShapeCharacteristics(DefType type)
+        {
+            Debug.Assert(_fallbackAlgorithm.ComputeValueTypeShapeCharacteristics(type) == ValueTypeShapeCharacteristics.None);
+            return ValueTypeShapeCharacteristics.None;
+        }
+
+        public static bool IsIntegerType(DefType type)
+        {
+            return type.IsIntrinsic
+                && type.Namespace == "System."
+                && ((type.Name == "Int128") || (type.Name == "UInt128"));
+        }
+    }
+}
index c4105d1..0c331f9 100644 (file)
@@ -30,7 +30,7 @@ namespace Internal.JitInterface
                 FieldClassifications = new SystemVClassificationType[SYSTEMV_MAX_NUM_FIELDS_IN_REGISTER_PASSED_STRUCT];
                 FieldSizes = new int[SYSTEMV_MAX_NUM_FIELDS_IN_REGISTER_PASSED_STRUCT];
                 FieldOffsets = new int[SYSTEMV_MAX_NUM_FIELDS_IN_REGISTER_PASSED_STRUCT];
-                            
+
                 for (int i = 0; i < CLR_SYSTEMV_MAX_EIGHTBYTES_COUNT_TO_PASS_IN_REGISTERS; i++)
                 {
                     EightByteClassifications[i] = SystemVClassificationTypeNoClass;
@@ -94,7 +94,7 @@ namespace Internal.JitInterface
         {
             structPassInRegDescPtr = default;
             structPassInRegDescPtr.passedInRegisters = false;
-            
+
             int typeSize = typeDesc.GetElementSize().AsInt;
             if (typeDesc.IsValueType && (typeSize <= CLR_SYSTEMV_MAX_STRUCT_BYTES_TO_PASS_IN_REGISTERS))
             {
@@ -114,7 +114,7 @@ namespace Internal.JitInterface
                     structPassInRegDescPtr.eightByteClassifications0 = helper.EightByteClassifications[0];
                     structPassInRegDescPtr.eightByteSizes0 = (byte)helper.EightByteSizes[0];
                     structPassInRegDescPtr.eightByteOffsets0 = (byte)helper.EightByteOffsets[0];
-                        
+
                     structPassInRegDescPtr.eightByteClassifications1 = helper.EightByteClassifications[1];
                     structPassInRegDescPtr.eightByteSizes1 = (byte)helper.EightByteSizes[1];
                     structPassInRegDescPtr.eightByteOffsets1 = (byte)helper.EightByteOffsets[1];
@@ -216,7 +216,7 @@ namespace Internal.JitInterface
         /// <summary>
         /// Returns 'true' if the struct is passed in registers, 'false' otherwise.
         /// </summary>
-        private static bool ClassifyEightBytes(TypeDesc typeDesc, 
+        private static bool ClassifyEightBytes(TypeDesc typeDesc,
                                                ref SystemVStructRegisterPassingHelper helper,
                                                int startOffsetOfStruct)
         {
@@ -239,14 +239,15 @@ namespace Internal.JitInterface
                 return false;
             }
 
-            // The SIMD Intrinsic types are meant to be handled specially and should not be passed as struct registers
+            // The SIMD and Int128 Intrinsic types are meant to be handled specially and should not be passed as struct registers
             if (typeDesc.IsIntrinsic)
             {
                 InstantiatedType instantiatedType = typeDesc as InstantiatedType;
                 if (instantiatedType != null)
                 {
                     if (VectorFieldLayoutAlgorithm.IsVectorType(instantiatedType) ||
-                        VectorOfTFieldLayoutAlgorithm.IsVectorOfTType(instantiatedType))
+                        VectorOfTFieldLayoutAlgorithm.IsVectorOfTType(instantiatedType) ||
+                        Int128FieldLayoutAlgorithm.IsIntegerType(instantiatedType))
                     {
                         return false;
                     }
@@ -316,7 +317,7 @@ namespace Internal.JitInterface
 
                     bool structRet = false;
                     structRet = ClassifyEightBytes(field.FieldType, ref helper, normalizedFieldOffset);
-                    
+
                     helper.InEmbeddedStruct = inEmbeddedStructPrev;
 
                     if (!structRet)
@@ -482,7 +483,7 @@ namespace Internal.JitInterface
                         else if ((helper.EightByteClassifications[currentFieldEightByte] == SystemVClassificationTypeInteger) ||
                             (fieldClassificationType == SystemVClassificationTypeInteger))
                         {
-                            Debug.Assert((fieldClassificationType != SystemVClassificationTypeIntegerReference) && 
+                            Debug.Assert((fieldClassificationType != SystemVClassificationTypeIntegerReference) &&
                                             (fieldClassificationType != SystemVClassificationTypeIntegerByRef));
 
                             helper.EightByteClassifications[currentFieldEightByte] = SystemVClassificationTypeInteger;
index 66e6044..4bdb57b 100644 (file)
@@ -33,6 +33,7 @@ namespace ILCompiler
         private readonly RuntimeDeterminedFieldLayoutAlgorithm _runtimeDeterminedFieldLayoutAlgorithm = new RuntimeDeterminedFieldLayoutAlgorithm();
         private readonly VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm;
         private readonly VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm;
+        private readonly Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm;
 
         private TypeDesc[] _arrayOfTInterfaces;
         private ArrayOfTRuntimeInterfacesAlgorithm _arrayOfTRuntimeInterfacesAlgorithm;
@@ -45,6 +46,7 @@ namespace ILCompiler
 
             _vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
             _vectorFieldLayoutAlgorithm = new VectorFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
+            _int128FieldLayoutAlgorithm = new Int128FieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm);
 
             _delegateInfoHashtable = new DelegateInfoHashtable(delegateFeatures);
 
@@ -72,6 +74,8 @@ namespace ILCompiler
                 return _vectorOfTFieldLayoutAlgorithm;
             else if (VectorFieldLayoutAlgorithm.IsVectorType(type))
                 return _vectorFieldLayoutAlgorithm;
+            else if (Int128FieldLayoutAlgorithm.IsIntegerType(type))
+                return _int128FieldLayoutAlgorithm;
             else
                 return _metadataFieldLayoutAlgorithm;
         }
@@ -220,8 +224,8 @@ namespace ILCompiler
         // method table.
         public long UniversalCanonReflectionMethodRootHeuristic_InstantiationCount { get; }
 
-        // To avoid infinite generic recursion issues during debug type record generation, attempt to 
-        // use canonical form for types with high generic complexity. 
+        // To avoid infinite generic recursion issues during debug type record generation, attempt to
+        // use canonical form for types with high generic complexity.
         public long MaxGenericDepthOfDebugRecord { get; }
 
         public SharedGenericsConfiguration()
index dee4e19..60fe6d2 100644 (file)
     <Compile Include="..\..\Common\Compiler\HardwareIntrinsicHelpers.cs" Link="Compiler\HardwareIntrinsicHelpers.cs" />
     <Compile Include="..\..\Common\Compiler\ICompilationRootProvider.cs" Link="Compiler\ICompilationRootProvider.cs" />
     <Compile Include="..\..\Common\Compiler\InstructionSetSupport.cs" Link="Compiler\InstructionSetSupport.cs" />
+    <Compile Include="..\..\Common\Compiler\Int128FieldLayoutAlgorithm.cs" Link="Compiler\Int128FieldLayoutAlgorithm.cs" />
     <Compile Include="..\..\Common\Compiler\InternalCompilerErrorException.cs" Link="Compiler\InternalCompilerErrorException.cs" />
     <Compile Include="..\..\Common\Compiler\NameMangler.cs" Link="Compiler\NameMangler.cs" />
     <Compile Include="..\..\Common\Compiler\SingleMethodRootProvider.cs" Link="Compiler\SingleMethodRootProvider.cs" />
index fdb6fab..bd9eb74 100644 (file)
@@ -36,6 +36,7 @@ namespace ILCompiler
         private SystemObjectFieldLayoutAlgorithm _systemObjectFieldLayoutAlgorithm;
         private VectorOfTFieldLayoutAlgorithm _vectorOfTFieldLayoutAlgorithm;
         private VectorFieldLayoutAlgorithm _vectorFieldLayoutAlgorithm;
+        private Int128FieldLayoutAlgorithm _int128FieldLayoutAlgorithm;
 
         public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode genericsMode, bool bubbleIncludesCorelib, CompilerTypeSystemContext oldTypeSystemContext = null)
             : base(details, genericsMode)
@@ -55,6 +56,9 @@ namespace ILCompiler
             // No architecture has completely stable handling of Vector<T> in the abi (Arm64 may change to SVE)
             _vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_r2rFieldLayoutAlgorithm, _vectorFieldLayoutAlgorithm, matchingVectorType, bubbleIncludesCorelib);
 
+            // Int128 and UInt128 should be ABI stable on all currently supported platforms
+            _int128FieldLayoutAlgorithm = new Int128FieldLayoutAlgorithm(_r2rFieldLayoutAlgorithm);
+
             if (oldTypeSystemContext != null)
             {
                 InheritOpenModules(oldTypeSystemContext);
@@ -77,6 +81,10 @@ namespace ILCompiler
             {
                 return _vectorFieldLayoutAlgorithm;
             }
+            else if (Int128FieldLayoutAlgorithm.IsIntegerType(type))
+            {
+                return _int128FieldLayoutAlgorithm;
+            }
             else
             {
                 Debug.Assert(_r2rFieldLayoutAlgorithm != null);
index f779f57..e45ee89 100644 (file)
@@ -91,6 +91,7 @@
     <Compile Include="..\..\Common\Compiler\HardwareIntrinsicHelpers.cs" Link="Compiler\HardwareIntrinsicHelpers.cs" />
     <Compile Include="..\..\Common\Compiler\ICompilationRootProvider.cs" Link="Compiler\ICompilationRootProvider.cs" />
     <Compile Include="..\..\Common\Compiler\InstructionSetSupport.cs" Link="Compiler\InstructionSetSupport.cs" />
+    <Compile Include="..\..\Common\Compiler\Int128FieldLayoutAlgorithm.cs" Link="Compiler\Int128FieldLayoutAlgorithm.cs" />
     <Compile Include="..\..\Common\Compiler\InternalCompilerErrorException.cs" Link="Compiler\InternalCompilerErrorException.cs" />
     <Compile Include="..\..\Common\Compiler\NameMangler.cs" Link="Compiler\NameMangler.cs" />
     <Compile Include="..\..\Common\Compiler\SingleMethodRootProvider.cs" Link="Compiler\SingleMethodRootProvider.cs" />
index 2461214..c89488a 100644 (file)
@@ -955,7 +955,9 @@ EEClassNativeLayoutInfo* EEClassNativeLayoutInfo::CollectNativeLayoutFieldMetada
             pNativeLayoutInfo->m_alignmentRequirement = pEEClassLayoutInfo->m_ManagedLargestAlignmentRequirementOfAllMembers;
         }
         else
-        if (pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR64T)) ||
+        if (pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__INT128)) ||
+            pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__UINT128)) ||
+            pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR64T)) ||
             pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR128T)) ||
             pMT->HasSameTypeDefAs(CoreLibBinder::GetClass(CLASS__VECTOR256T)))
         {
index 7d71ce2..dee85ad 100644 (file)
 #define g_DateTimeOffsetClassName "System.DateTimeOffset"
 #define g_DecimalClassName "System.Decimal"
 
+#define g_Int128ClassName "System.Int128"
+#define g_Int128Name "Int128"
+
+#define g_UInt128ClassName "System.UInt128"
+#define g_UInt128Name "UInt128"
+
 #define g_Vector64ClassName "System.Runtime.Intrinsics.Vector64`1"
 #define g_Vector64Name "Vector64`1"
 
index 5d02b08..8a1583a 100644 (file)
@@ -282,6 +282,9 @@ DEFINE_FIELD(DELEGATE,            METHOD_PTR_AUX,         _methodPtrAux)
 DEFINE_METHOD(DELEGATE,             CONSTRUCT_DELEGATE,     DelegateConstruct,          IM_Obj_IntPtr_RetVoid)
 DEFINE_METHOD(DELEGATE,             GET_INVOKE_METHOD,      GetInvokeMethod,            IM_RetIntPtr)
 
+DEFINE_CLASS(INT128,               System,                 Int128)
+DEFINE_CLASS(UINT128,              System,                 UInt128)
+
 DEFINE_CLASS(DYNAMICMETHOD,         ReflectionEmit,         DynamicMethod)
 
 DEFINE_CLASS(DYNAMICRESOLVER,       ReflectionEmit,         DynamicResolver)
index bb330ee..47563cb 100644 (file)
@@ -9844,7 +9844,7 @@ void MethodTableBuilder::CheckForSystemTypes()
 
             if (strcmp(nameSpace, g_IntrinsicsNS) == 0)
             {
-                EEClassLayoutInfo * pLayout = pClass->GetLayoutInfo();
+                EEClassLayoutInfo* pLayout = pClass->GetLayoutInfo();
 
                 // The SIMD Hardware Intrinsic types correspond to fundamental data types in the underlying ABIs:
                 // * Vector64<T>:  __m64
@@ -9854,7 +9854,6 @@ void MethodTableBuilder::CheckForSystemTypes()
                 // These __m128 and __m256 types, among other requirements, are special in that they must always
                 // be aligned properly.
 
-
                 if (strcmp(name, g_Vector64Name) == 0)
                 {
                     // The System V ABI for i386 defaults to 8-byte alignment for __m64, except for parameter passing,
@@ -9898,6 +9897,21 @@ void MethodTableBuilder::CheckForSystemTypes()
 
                 return;
             }
+#if defined(UNIX_AMD64_ABI) || defined(TARGET_ARM64)
+            else if (strcmp(nameSpace, g_SystemNS) == 0)
+            {
+                EEClassLayoutInfo* pLayout = pClass->GetLayoutInfo();
+
+                // These types correspond to fundamental data types in the underlying ABIs:
+                // * Int128:  __int128
+                // * UInt128: unsigned __int128
+
+                if ((strcmp(name, g_Int128Name) == 0) || (strcmp(name, g_UInt128Name) == 0))
+                {
+                    pLayout->m_ManagedLargestAlignmentRequirementOfAllMembers = 16; // sizeof(__int128)
+                }
+            }
+#endif // UNIX_AMD64_ABI || TARGET_ARM64
         }
 
         if (g_pNullableClass != NULL)
index eaf0229..6664593 100644 (file)
   <data name="Arg_MustBeInt64" xml:space="preserve">
     <value>Object must be of type Int64.</value>
   </data>
+  <data name="Arg_MustBeInt128" xml:space="preserve">
+    <value>Object must be of type Int128.</value>
+  </data>
   <data name="Arg_MustBeIntPtr" xml:space="preserve">
     <value>Object must be of type IntPtr.</value>
   </data>
   <data name="Arg_MustBeUInt64" xml:space="preserve">
     <value>Object must be of type UInt64.</value>
   </data>
+  <data name="Arg_MustBeUInt128" xml:space="preserve">
+    <value>Object must be of type UInt128.</value>
+  </data>
   <data name="Arg_MustBeUIntPtr" xml:space="preserve">
     <value>Object must be of type UIntPtr.</value>
   </data>
   <data name="Overflow_Int64" xml:space="preserve">
     <value>Value was either too large or too small for an Int64.</value>
   </data>
+  <data name="Overflow_Int128" xml:space="preserve">
+    <value>Value was either too large or too small for an Int128.</value>
+  </data>
   <data name="Overflow_MutexReacquireCount" xml:space="preserve">
     <value>The current thread attempted to reacquire a mutex that has reached its maximum acquire count.</value>
   </data>
   <data name="Overflow_UInt64" xml:space="preserve">
     <value>Value was either too large or too small for a UInt64.</value>
   </data>
+  <data name="Overflow_UInt128" xml:space="preserve">
+    <value>Value was either too large or too small for a UInt128.</value>
+  </data>
   <data name="PlatformNotSupported_ArgIterator" xml:space="preserve">
     <value>ArgIterator is not supported on this platform.</value>
   </data>
index db7442f..7464a11 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Int16.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Int32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Int64.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Int128.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\IntPtr.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\InvalidCastException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\InvalidOperationException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\UInt16.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\UInt32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\UInt64.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\UInt128.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\UIntPtr.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\UnauthorizedAccessException.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\UnhandledExceptionEventArgs.cs" />
index bf4de4f..fd6ce30 100644 (file)
@@ -10,6 +10,32 @@ namespace System.Buffers.Text
     internal static partial class FormattingHelpers
     {
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int CountDigits(UInt128 value)
+        {
+            ulong upper = value.Upper;
+
+            if (upper < 5)
+            {
+                return CountDigits(value.Lower);
+            }
+
+            int digits = 19;
+
+            if (upper > 5)
+            {
+                digits++;
+                value /= new UInt128(0x5, 0x6BC7_5E2D_6310_0000); // value /= 1e20
+                digits += CountDigits(value.Lower);
+            }
+            else if (value.Lower >= 0x6BC75E2D63100000)
+            {
+                digits++;
+            }
+
+            return digits;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static int CountDigits(ulong value)
         {
             int digits = 1;
@@ -101,6 +127,13 @@ namespace System.Buffers.Text
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int CountHexDigits(UInt128 value)
+        {
+            // The number of hex digits is log16(value) + 1, or log2(value) / 4 + 1
+            return ((int)UInt128.Log2(value) >> 2) + 1;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static int CountHexDigits(ulong value)
         {
             // The number of hex digits is log16(value) + 1, or log2(value) / 4 + 1
index cde5b26..1a5834c 100644 (file)
@@ -17,7 +17,7 @@ namespace System
         internal uint Low => (uint)_lo64;
         internal uint Mid => (uint)(_lo64 >> 32);
 
-        private ulong Low64 => _lo64;
+        internal ulong Low64 => _lo64;
 
         private static ref DecCalc AsMutable(ref decimal d) => ref Unsafe.As<decimal, DecCalc>(ref d);
 
index 721b7c0..0427d18 100644 (file)
@@ -1296,6 +1296,10 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (decimal)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1320,6 +1324,10 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (decimal)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1364,6 +1372,12 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                var actualValue = (Int128)(object)value;
+                return (actualValue > new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? MaxValue :
+                       (actualValue < new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)) ? MinValue : (decimal)actualValue;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1388,6 +1402,11 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                var actualValue = (UInt128)(object)value;
+                return (actualValue > new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? MaxValue : (decimal)actualValue;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1432,6 +1451,21 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                var actualValue = (Int128)(object)value;
+
+                if (Int128.IsNegative(actualValue))
+                {
+                    actualValue = (-actualValue) & new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+                    return -(decimal)actualValue;
+                }
+                else
+                {
+                    actualValue &= new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+                    return (decimal)actualValue;
+                }
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1456,6 +1490,12 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                var actualValue = (UInt128)(object)value;
+                actualValue &= new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+                return (decimal)actualValue;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1531,6 +1571,19 @@ namespace System
                 result = (long)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                var actualValue = (Int128)(object)value;
+
+                if ((actualValue > new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) || (actualValue < new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (decimal)actualValue;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 result = (nint)(object)value;
@@ -1561,6 +1614,19 @@ namespace System
                 result = (ulong)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                var actualValue = (UInt128)(object)value;
+
+                if (actualValue > new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (decimal)actualValue;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 result = (nuint)(object)value;
index cae158e..98fed88 100644 (file)
@@ -1016,6 +1016,10 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (double)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1040,6 +1044,10 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (double)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1084,6 +1092,10 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (double)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1108,6 +1120,10 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (double)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1152,6 +1168,10 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (double)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1176,6 +1196,10 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (double)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1242,6 +1266,11 @@ namespace System
                 result = (long)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                result = (double)(Int128)(object)value;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 result = (nint)(object)value;
@@ -1272,6 +1301,11 @@ namespace System
                 result = (ulong)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                result = (double)(UInt128)(object)value;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 result = (nuint)(object)value;
index 219a409..c538d15 100644 (file)
@@ -1214,6 +1214,10 @@ namespace System
             {
                 return (Half)(long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (Half)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (Half)(long)(nint)(object)value;
@@ -1238,6 +1242,10 @@ namespace System
             {
                 return (Half)(ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (Half)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (Half)(ulong)(nuint)(object)value;
@@ -1282,6 +1290,10 @@ namespace System
             {
                 return (Half)(long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (Half)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (Half)(long)(nint)(object)value;
@@ -1306,6 +1318,10 @@ namespace System
             {
                 return (Half)(ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (Half)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (Half)(ulong)(nuint)(object)value;
@@ -1350,6 +1366,10 @@ namespace System
             {
                 return (Half)(long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (Half)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (Half)(long)(nint)(object)value;
@@ -1374,6 +1394,10 @@ namespace System
             {
                 return (Half)(ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (Half)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (Half)(ulong)(nuint)(object)value;
@@ -1440,6 +1464,11 @@ namespace System
                 result = (Half)(long)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                result = (Half)(Int128)(object)value;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 result = (Half)(long)(nint)(object)value;
@@ -1470,6 +1499,11 @@ namespace System
                 result = (Half)(ulong)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                result = (Half)(UInt128)(object)value;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 result = (Half)(ulong)(nuint)(object)value;
@@ -1488,10 +1522,10 @@ namespace System
         //
 
         /// <inheritdoc cref="INumberBase{TSelf}.One" />
-        static Half INumberBase<Half>.One => new Half(PositiveOneBits);
+        public static Half One => new Half(PositiveOneBits);
 
         /// <inheritdoc cref="INumberBase{TSelf}.Zero" />
-        static Half INumberBase<Half>.Zero => new Half(PositiveZeroBits);
+        public static Half Zero => new Half(PositiveZeroBits);
 
         //
         // IParsable
@@ -1527,7 +1561,7 @@ namespace System
         //
 
         /// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" />
-        static Half ISignedNumber<Half>.NegativeOne => new Half(NegativeOneBits);
+        public static Half NegativeOne => new Half(NegativeOneBits);
 
         //
         // ISpanParsable
index 3599bbd..0532833 100644 (file)
@@ -1,8 +1,6 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-using System.Diagnostics.CodeAnalysis;
-
 namespace System
 {
     // The IComparable interface is implemented by classes that support an
@@ -13,12 +11,31 @@ namespace System
     public interface IComparable
     {
         // Interface does not need to be marked with the serializable attribute
-        // Compares this object to another object, returning an integer that
-        // indicates the relationship. An implementation of this method must return
-        // a value less than zero if this is less than object, zero
-        // if this is equal to object, or a value greater than zero
-        // if this is greater than object.
-        //
+
+        /// <summary>Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.</summary>
+        /// <param name="obj">An object to compare with this instance.</param>
+        /// <returns>
+        ///     <para>A value that indicates the relative order of the objects being compared. The return value has these meanings:</para>
+        ///     <list type="table">
+        ///         <listheader>
+        ///             <term>Value</term>
+        ///             <description>Meaning</description>
+        ///         </listheader>
+        ///         <item>
+        ///             <term>Less than zero</term>
+        ///             <description>This instance precedes <paramref name="obj" /> in the sort order.</description>
+        ///         </item>
+        ///         <item>
+        ///             <term>Zero</term>
+        ///             <description>This instance occurs in the same position in the sort order as <paramref name="obj" />.</description>
+        ///         </item>
+        ///         <item>
+        ///             <term>Greater than zero</term>
+        ///             <description>This instance follows <paramref name="obj" /> in the sort order.</description>
+        ///         </item>
+        ///     </list>
+        /// </returns>
+        /// <exception cref="ArgumentException"><paramref name="obj" /> is not the same type as this instance.</exception>
         int CompareTo(object? obj);
     }
 
@@ -27,12 +44,30 @@ namespace System
     public interface IComparable<in T>
     {
         // Interface does not need to be marked with the serializable attribute
-        // Compares this object to another object, returning an integer that
-        // indicates the relationship. An implementation of this method must return
-        // a value less than zero if this is less than object, zero
-        // if this is equal to object, or a value greater than zero
-        // if this is greater than object.
-        //
+
+        /// <summary>Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.</summary>
+        /// <param name="other">An object to compare with this instance.</param>
+        /// <returns>
+        ///     <para>A value that indicates the relative order of the objects being compared. The return value has these meanings:</para>
+        ///     <list type="table">
+        ///         <listheader>
+        ///             <term>Value</term>
+        ///             <description>Meaning</description>
+        ///         </listheader>
+        ///         <item>
+        ///             <term>Less than zero</term>
+        ///             <description>This instance precedes <paramref name="other" /> in the sort order.</description>
+        ///         </item>
+        ///         <item>
+        ///             <term>Zero</term>
+        ///             <description>This instance occurs in the same position in the sort order as <paramref name="other" />.</description>
+        ///         </item>
+        ///         <item>
+        ///             <term>Greater than zero</term>
+        ///             <description>This instance follows <paramref name="other" /> in the sort order.</description>
+        ///         </item>
+        ///     </list>
+        /// </returns>
         int CompareTo(T? other);
     }
 }
index 89f0771..815f416 100644 (file)
@@ -1,12 +1,13 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
-using System.Diagnostics.CodeAnalysis;
-
 namespace System
 {
     public interface IEquatable<T> // invariant due to questionable semantics around equality and inheritance
     {
+        /// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
+        /// <param name="other">An object to compare with this object.</param>
+        /// <returns><c>true</c> if the current object is equal to the <paramref name="other" /> parameter; otherwise, <c>false</c>.</returns>
         bool Equals(T? other);
     }
 }
diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs
new file mode 100644 (file)
index 0000000..a5ab201
--- /dev/null
@@ -0,0 +1,1828 @@
+// 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;
+
+namespace System
+{
+    /// <summary>Represents a 128-bit signed integer.</summary>
+    [Intrinsic]
+    [StructLayout(LayoutKind.Sequential)]
+    public readonly struct Int128
+        : IBinaryInteger<Int128>,
+          IMinMaxValue<Int128>,
+          ISignedNumber<Int128>
+    {
+        internal const int Size = 16;
+
+        // Unix System V ABI actually requires this to be little endian
+        // order and not `upper, lower` on big endian systems.
+
+        private readonly ulong _lower;
+        private readonly ulong _upper;
+
+        /// <summary>Initializes a new instance of the <see cref="Int128" /> 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 Int128(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 Int128 other)
+            {
+                return CompareTo(other);
+            }
+            else if (value is null)
+            {
+                return 1;
+            }
+            else
+            {
+                throw new ArgumentException(SR.Arg_MustBeInt128);
+            }
+        }
+
+        /// <inheritdoc cref="IComparable{T}.CompareTo(T)" />
+        public int CompareTo(Int128 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 Int128 other) && Equals(other);
+        }
+
+        /// <inheritdoc cref="IEquatable{T}.Equals(T)" />
+        public bool Equals(Int128 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.Int128ToDecStr(this);
+        }
+
+        public string ToString(IFormatProvider? provider)
+        {
+            return Number.FormatInt128(this, null, provider);
+        }
+
+        public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format)
+        {
+            return Number.FormatInt128(this, format, null);
+        }
+
+        public string ToString([StringSyntax(StringSyntaxAttribute.NumericFormat)] string? format, IFormatProvider? provider)
+        {
+            return Number.FormatInt128(this, format, provider);
+        }
+
+        public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
+        {
+            return Number.TryFormatInt128(this, format, provider, destination, out charsWritten);
+        }
+
+        public static Int128 Parse(string s)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            return Number.ParseInt128(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+        }
+
+        public static Int128 Parse(string s, NumberStyles style)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.ParseInt128(s, style, NumberFormatInfo.CurrentInfo);
+        }
+
+        public static Int128 Parse(string s, IFormatProvider? provider)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            return Number.ParseInt128(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+        }
+
+        public static Int128 Parse(string s, NumberStyles style, IFormatProvider? provider)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.ParseInt128(s, style, NumberFormatInfo.GetInstance(provider));
+        }
+
+        public static Int128 Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
+        {
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.ParseInt128(s, style, NumberFormatInfo.GetInstance(provider));
+        }
+
+        public static bool TryParse([NotNullWhen(true)] string? s, out Int128 result)
+        {
+            if (s is not null)
+            {
+                return Number.TryParseInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
+            }
+            else
+            {
+                result = default;
+                return false;
+            }
+        }
+
+        public static bool TryParse(ReadOnlySpan<char> s, out Int128 result)
+        {
+            return Number.TryParseInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
+        }
+
+        public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Int128 result)
+        {
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+
+            if (s is not null)
+            {
+                return Number.TryParseInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+            }
+            else
+            {
+                result = default;
+                return false;
+            }
+        }
+
+        public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out Int128 result)
+        {
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.TryParseInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+        }
+
+        //
+        // Explicit Conversions From Int128
+        //
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (byte)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        public static explicit operator checked byte(Int128 value)
+        {
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((byte)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (char)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        public static explicit operator checked char(Int128 value)
+        {
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((char)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value)
+        {
+            if (IsNegative(value))
+            {
+                value = -value;
+                return -(decimal)(UInt128)(value);
+            }
+            return (decimal)(UInt128)(value);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value)
+        {
+            if (IsNegative(value))
+            {
+                value = -value;
+                return -(double)(UInt128)(value);
+            }
+            return (double)(UInt128)(value);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value)
+        {
+            if (IsNegative(value))
+            {
+                value = -value;
+                return -(Half)(UInt128)(value);
+            }
+            return (Half)(UInt128)(value);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (short)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        public static explicit operator checked short(Int128 value)
+        {
+            if (~value._upper == 0)
+            {
+                long lower = (long)value._lower;
+                return checked((short)lower);
+            }
+
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((short)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (int)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        public static explicit operator checked int(Int128 value)
+        {
+            if (~value._upper == 0)
+            {
+                long lower = (long)value._lower;
+                return checked((int)lower);
+            }
+
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((int)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (long)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        public static explicit operator checked long(Int128 value)
+        {
+            if (~value._upper == 0)
+            {
+                long lower = (long)value._lower;
+                return lower;
+            }
+
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((long)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (nint)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        public static explicit operator checked nint(Int128 value)
+        {
+            if (~value._upper == 0)
+            {
+                long lower = (long)value._lower;
+                return checked((nint)lower);
+            }
+
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((nint)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (sbyte)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        [CLSCompliant(false)]
+        public static explicit operator checked sbyte(Int128 value)
+        {
+            if (~value._upper == 0)
+            {
+                long lower = (long)value._lower;
+                return checked((sbyte)lower);
+            }
+
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((sbyte)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value)
+        {
+            if (IsNegative(value))
+            {
+                value = -value;
+                return -(float)(UInt128)(value);
+            }
+            return (float)(UInt128)(value);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (ushort)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        [CLSCompliant(false)]
+        public static explicit operator checked ushort(Int128 value)
+        {
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((ushort)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (uint)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        [CLSCompliant(false)]
+        public static explicit operator checked uint(Int128 value)
+        {
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((uint)value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        [CLSCompliant(false)]
+        public static explicit operator checked ulong(Int128 value)
+        {
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return value._lower;
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed integer to a <see cref="UInt128" /> value.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a <see cref="UInt128" />.</returns>
+        [CLSCompliant(false)]
+        public static explicit operator UInt128(Int128 value) => new UInt128(value._upper, value._lower);
+
+        /// <summary>Explicitly converts a 128-bit signed integer to a <see cref="UInt128" /> 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="UInt128" />.</returns>
+        /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="Int128" />.</exception>
+        [CLSCompliant(false)]
+        public static explicit operator checked UInt128(Int128 value)
+        {
+            if ((long)value._upper < 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return new UInt128(value._upper, value._lower);
+        }
+
+        /// <summary>Explicitly converts a 128-bit signed 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(Int128 value) => (nuint)value._lower;
+
+        /// <summary>Explicitly converts a 128-bit signed 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="Int128" />.</exception>
+        [CLSCompliant(false)]
+        public static explicit operator checked nuint(Int128 value)
+        {
+            if (value._upper != 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+            return checked((nuint)value._lower);
+        }
+
+        //
+        // Explicit Conversions To Int128
+        //
+
+        /// <summary>Explicitly converts a <see cref="decimal" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static explicit operator Int128(decimal value)
+        {
+            value = decimal.Truncate(value);
+            Int128 result = new Int128(value.High, value.Low64);
+
+            if (decimal.IsNegative(value))
+            {
+                result = -result;
+            }
+            return result;
+        }
+
+        /// <summary>Explicitly converts a <see cref="double" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static explicit operator Int128(double value)
+        {
+            const double TwoPow127 = 170141183460469231731687303715884105728.0;
+
+            if (value <= -TwoPow127)
+            {
+                return MinValue;
+            }
+            else if (double.IsNaN(value))
+            {
+                return 0;
+            }
+            else if (value >= +TwoPow127)
+            {
+                return MaxValue;
+            }
+
+            return ToInt128(value);
+        }
+
+        /// <summary>Explicitly converts a <see cref="double" /> value to a 128-bit signed 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 signed integer.</returns>
+        /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="Int128" />.</exception>
+        public static explicit operator checked Int128(double value)
+        {
+            const double TwoPow127 = 170141183460469231731687303715884105728.0;
+
+            if ((value < -TwoPow127) || double.IsNaN(value) || (value >= +TwoPow127))
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+
+            return ToInt128(value);
+        }
+
+        internal static Int128 ToInt128(double value)
+        {
+            const double TwoPow127 = 170141183460469231731687303715884105728.0;
+
+            Debug.Assert(value >= -TwoPow127);
+            Debug.Assert(double.IsFinite(value));
+            Debug.Assert(value < TwoPow127);
+
+            // This code is based on `f64_to_i128` 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
+
+            bool isNegative = double.IsNegative(value);
+
+            if (isNegative)
+            {
+                value = -value;
+            }
+
+            if (value >= 1.0)
+            {
+                // In order to convert from double to int128 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);
+                Int128 result = new Int128((bits << 12) >> 1 | 0x8000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+                result >>>= (1023 + 128 - 1 - (int)(bits >> 52));
+
+                if (isNegative)
+                {
+                    result = -result;
+                }
+                return result;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+
+        /// <summary>Explicitly converts a <see cref="Half" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static explicit operator Int128(Half value) => (Int128)(double)(value);
+
+        /// <summary>Explicitly converts a <see cref="Half" /> value to a 128-bit signed 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 signed integer.</returns>
+        /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="Int128" />.</exception>
+        public static explicit operator checked Int128(Half value) => checked((Int128)(double)(value));
+
+        /// <summary>Explicitly converts a <see cref="float" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static explicit operator Int128(float value) => (Int128)(double)(value);
+
+        /// <summary>Explicitly converts a <see cref="float" /> value to a 128-bit signed 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 signed integer.</returns>
+        /// <exception cref="OverflowException"><paramref name="value" /> is not representable by <see cref="Int128" />.</exception>
+        public static explicit operator checked Int128(float value) => checked((Int128)(double)(value));
+
+        //
+        // Implicit Conversions To Int128
+        //
+
+        /// <summary>Implicitly converts a <see cref="byte" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static implicit operator Int128(byte value) => new Int128(0, value);
+
+        /// <summary>Implicitly converts a <see cref="char" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static implicit operator Int128(char value) => new Int128(0, value);
+
+        /// <summary>Implicitly converts a <see cref="short" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static implicit operator Int128(short value)
+        {
+            long lower = value;
+            return new Int128((ulong)(lower >> 63), (ulong)lower);
+        }
+
+        /// <summary>Implicitly converts a <see cref="int" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static implicit operator Int128(int value)
+        {
+            long lower = value;
+            return new Int128((ulong)(lower >> 63), (ulong)lower);
+        }
+
+        /// <summary>Implicitly converts a <see cref="long" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static implicit operator Int128(long value)
+        {
+            long lower = value;
+            return new Int128((ulong)(lower >> 63), (ulong)lower);
+        }
+
+        /// <summary>Implicitly converts a <see cref="IntPtr" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        public static implicit operator Int128(nint value)
+        {
+            long lower = value;
+            return new Int128((ulong)(lower >> 63), (ulong)lower);
+        }
+
+        /// <summary>Implicitly converts a <see cref="sbyte" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        [CLSCompliant(false)]
+        public static implicit operator Int128(sbyte value)
+        {
+            long lower = value;
+            return new Int128((ulong)(lower >> 63), (ulong)lower);
+        }
+
+        /// <summary>Implicitly converts a <see cref="ushort" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        [CLSCompliant(false)]
+        public static implicit operator Int128(ushort value) => new Int128(0, value);
+
+        /// <summary>Implicitly converts a <see cref="uint" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        [CLSCompliant(false)]
+        public static implicit operator Int128(uint value) => new Int128(0, value);
+
+        /// <summary>Implicitly converts a <see cref="ulong" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        [CLSCompliant(false)]
+        public static implicit operator Int128(ulong value) => new Int128(0, value);
+
+        /// <summary>Implicitly converts a <see cref="UIntPtr" /> value to a 128-bit signed integer.</summary>
+        /// <param name="value">The value to convert.</param>
+        /// <returns><paramref name="value" /> converted to a 128-bit signed integer.</returns>
+        [CLSCompliant(false)]
+        public static implicit operator Int128(nuint value) => new Int128(0, value);
+
+        //
+        // IAdditionOperators
+        //
+
+        /// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" />
+        public static Int128 operator +(Int128 left, Int128 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 Int128(upper, lower);
+        }
+
+        /// <inheritdoc cref="IAdditionOperators{TSelf, TOther, TResult}.op_Addition(TSelf, TOther)" />
+        public static Int128 operator checked +(Int128 left, Int128 right)
+        {
+            // For signed addition, we can detect overflow by checking if the sign of
+            // both inputs are the same and then if that differs from the sign of the
+            // output.
+
+            Int128 result = left + right;
+
+            uint sign = (uint)(left._upper >> 63);
+
+            if (sign == (uint)(right._upper >> 63))
+            {
+                if (sign != (uint)(result._upper >> 63))
+                {
+                    ThrowHelper.ThrowOverflowException();
+                }
+            }
+            return result;
+        }
+
+        //
+        // IAdditiveIdentity
+        //
+
+        /// <inheritdoc cref="IAdditiveIdentity{TSelf, TResult}.AdditiveIdentity" />
+        static Int128 IAdditiveIdentity<Int128, Int128>.AdditiveIdentity => default;
+
+        //
+        // IBinaryInteger
+        //
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.DivRem(TSelf, TSelf)" />
+        public static (Int128 Quotient, Int128 Remainder) DivRem(Int128 left, Int128 right)
+        {
+            Int128 quotient = left / right;
+            return (quotient, left - (quotient * right));
+        }
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.LeadingZeroCount(TSelf)" />
+        public static Int128 LeadingZeroCount(Int128 value)
+        {
+            if (value._upper == 0)
+            {
+                return 64 + ulong.LeadingZeroCount(value._lower);
+            }
+            return ulong.LeadingZeroCount(value._upper);
+        }
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.PopCount(TSelf)" />
+        public static Int128 PopCount(Int128 value)
+            => ulong.PopCount(value._lower) + ulong.PopCount(value._upper);
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.RotateLeft(TSelf, int)" />
+        public static Int128 RotateLeft(Int128 value, int rotateAmount)
+            => (value << rotateAmount) | (value >>> (128 - rotateAmount));
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.RotateRight(TSelf, int)" />
+        public static Int128 RotateRight(Int128 value, int rotateAmount)
+            => (value >>> rotateAmount) | (value << (128 - rotateAmount));
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.TrailingZeroCount(TSelf)" />
+        public static Int128 TrailingZeroCount(Int128 value)
+        {
+            if (value._lower == 0)
+            {
+                return 64 + ulong.TrailingZeroCount(value._upper);
+            }
+            return ulong.TrailingZeroCount(value._lower);
+        }
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.GetShortestBitLength()" />
+        long IBinaryInteger<Int128>.GetShortestBitLength()
+        {
+            Int128 value = this;
+
+            if (IsPositive(value))
+            {
+                return (Size * 8) - BitOperations.LeadingZeroCount(value);
+            }
+            else
+            {
+                return (Size * 8) + 1 - BitOperations.LeadingZeroCount(~value);
+            }
+        }
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.GetByteCount()" />
+        int IBinaryInteger<Int128>.GetByteCount() => Size;
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteLittleEndian(Span{byte}, out int)" />
+        bool IBinaryInteger<Int128>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten)
+        {
+            if (destination.Length >= Size)
+            {
+                ulong lower = _lower;
+                ulong upper = _upper;
+
+                if (!BitConverter.IsLittleEndian)
+                {
+                    ulong tmp = lower;
+                    lower = BinaryPrimitives.ReverseEndianness(upper);
+                    upper = BinaryPrimitives.ReverseEndianness(tmp);
+                }
+
+                ref byte address = ref MemoryMarshal.GetReference(destination);
+
+                Unsafe.WriteUnaligned(ref address, lower);
+                Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref address, sizeof(ulong)), upper);
+
+                bytesWritten = Size;
+                return true;
+            }
+            else
+            {
+                bytesWritten = 0;
+                return false;
+            }
+        }
+
+        //
+        // IBinaryNumber
+        //
+
+        /// <inheritdoc cref="IBinaryNumber{TSelf}.IsPow2(TSelf)" />
+        public static bool IsPow2(Int128 value) => (PopCount(value) == 1U) && IsPositive(value);
+
+        /// <inheritdoc cref="IBinaryNumber{TSelf}.Log2(TSelf)" />
+        public static Int128 Log2(Int128 value)
+        {
+            if (IsNegative(value))
+            {
+                ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
+            }
+
+            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 Int128 operator &(Int128 left, Int128 right) => new Int128(left._upper & right._upper, left._lower & right._lower);
+
+        /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_BitwiseOr(TSelf, TOther)" />
+        public static Int128 operator |(Int128 left, Int128 right) => new Int128(left._upper | right._upper, left._lower | right._lower);
+
+        /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_ExclusiveOr(TSelf, TOther)" />
+        public static Int128 operator ^(Int128 left, Int128 right) => new Int128(left._upper ^ right._upper, left._lower ^ right._lower);
+
+        /// <inheritdoc cref="IBitwiseOperators{TSelf, TOther, TResult}.op_OnesComplement(TSelf)" />
+        public static Int128 operator ~(Int128 value) => new Int128(~value._upper, ~value._lower);
+
+        //
+        // IComparisonOperators
+        //
+
+        /// <inheritdoc cref="IComparisonOperators{TSelf, TOther}.op_LessThan(TSelf, TOther)" />
+        public static bool operator <(Int128 left, Int128 right)
+        {
+            if (IsNegative(left) == IsNegative(right))
+            {
+                return (left._upper < right._upper)
+                    || ((left._upper == right._upper) && (left._lower < right._lower));
+            }
+            else
+            {
+                return IsNegative(left);
+            }
+        }
+
+        /// <inheritdoc cref="IComparisonOperators{TSelf, TOther}.op_LessThanOrEqual(TSelf, TOther)" />
+        public static bool operator <=(Int128 left, Int128 right)
+        {
+            if (IsNegative(left) == IsNegative(right))
+            {
+                return (left._upper < right._upper)
+                    || ((left._upper == right._upper) && (left._lower <= right._lower));
+            }
+            else
+            {
+                return IsNegative(left);
+            }
+        }
+
+        /// <inheritdoc cref="IComparisonOperators{TSelf, TOther}.op_GreaterThan(TSelf, TOther)" />
+        public static bool operator >(Int128 left, Int128 right)
+        {
+            if (IsNegative(left) == IsNegative(right))
+            {
+                return (left._upper > right._upper)
+                    || ((left._upper == right._upper) && (left._lower > right._lower));
+            }
+            else
+            {
+                return IsNegative(right);
+            }
+        }
+
+        /// <inheritdoc cref="IComparisonOperators{TSelf, TOther}.op_GreaterThanOrEqual(TSelf, TOther)" />
+        public static bool operator >=(Int128 left, Int128 right)
+        {
+            if (IsNegative(left) == IsNegative(right))
+            {
+                return (left._upper > right._upper)
+                    || ((left._upper == right._upper) && (left._lower >= right._lower));
+            }
+            else
+            {
+                return IsNegative(right);
+            }
+        }
+
+        //
+        // IDecrementOperators
+        //
+
+        /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" />
+        public static Int128 operator --(Int128 value) => value - One;
+
+        /// <inheritdoc cref="IDecrementOperators{TSelf}.op_Decrement(TSelf)" />
+        public static Int128 operator checked --(Int128 value) => checked(value - One);
+
+        //
+        // IDivisionOperators
+        //
+
+        /// <inheritdoc cref="IDivisionOperators{TSelf, TOther, TResult}.op_Division(TSelf, TOther)" />
+        public static Int128 operator /(Int128 left, Int128 right)
+        {
+            if ((right == -1) && (left._upper == 0x8000_0000_0000_0000) && (left._lower == 0))
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+
+            // We simplify the logic here by just doing unsigned division on the
+            // one's complement representation and then taking the correct sign.
+
+            ulong sign = (left._upper ^ right._upper) & (1UL << 63);
+
+            if (IsNegative(left))
+            {
+                left = ~left + 1U;
+            }
+
+            if (IsNegative(right))
+            {
+                right = ~right + 1U;
+            }
+
+            UInt128 result = (UInt128)(left) / (UInt128)(right);
+
+            if (result == 0U)
+            {
+                sign = 0;
+            }
+            else if (sign != 0)
+            {
+                result = ~result + 1U;
+            }
+
+            return new Int128(
+                result.Upper | sign,
+                result.Lower
+            );
+        }
+
+        /// <inheritdoc cref="IDivisionOperators{TSelf, TOther, TResult}.op_CheckedDivision(TSelf, TOther)" />
+        public static Int128 operator checked /(Int128 left, Int128 right) => left / right;
+
+        //
+        // IEqualityOperators
+        //
+
+        /// <inheritdoc cref="IEqualityOperators{TSelf, TOther}.op_Equality(TSelf, TOther)" />
+        public static bool operator ==(Int128 left, Int128 right) => (left._lower == right._lower) && (left._upper == right._upper);
+
+        /// <inheritdoc cref="IEqualityOperators{TSelf, TOther}.op_Inequality(TSelf, TOther)" />
+        public static bool operator !=(Int128 left, Int128 right) => (left._lower != right._lower) || (left._upper != right._upper);
+
+        //
+        // IIncrementOperators
+        //
+
+        /// <inheritdoc cref="IIncrementOperators{TSelf}.op_Increment(TSelf)" />
+        public static Int128 operator ++(Int128 value) => value + One;
+
+        /// <inheritdoc cref="IIncrementOperators{TSelf}.op_CheckedIncrement(TSelf)" />
+        public static Int128 operator checked ++(Int128 value) => checked(value + One);
+
+        //
+        // IMinMaxValue
+        //
+
+        /// <inheritdoc cref="IMinMaxValue{TSelf}.MinValue" />
+        public static Int128 MinValue => new Int128(0x8000_0000_0000_0000, 0);
+
+        /// <inheritdoc cref="IMinMaxValue{TSelf}.MaxValue" />
+        public static Int128 MaxValue => new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+        //
+        // IModulusOperators
+        //
+
+        /// <inheritdoc cref="IModulusOperators{TSelf, TOther, TResult}.op_Modulus(TSelf, TOther)" />
+        public static Int128 operator %(Int128 left, Int128 right)
+        {
+            // We simplify the logic here by just doing unsigned modulus on the
+            // one's complement representation and then taking the correct sign.
+
+            ulong sign = (left._upper ^ right._upper) & (1UL << 63);
+
+            if (IsNegative(left))
+            {
+                left = ~left + 1U;
+            }
+
+            if (IsNegative(right))
+            {
+                right = ~right + 1U;
+            }
+
+            UInt128 result = (UInt128)(left) % (UInt128)(right);
+
+            if (result == 0U)
+            {
+                sign = 0;
+            }
+            else if (sign != 0)
+            {
+                result = ~result + 1U;
+            }
+
+            return new Int128(
+                result.Upper | sign,
+                result.Lower
+            );
+        }
+
+        //
+        // IMultiplicativeIdentity
+        //
+
+        /// <inheritdoc cref="IMultiplicativeIdentity{TSelf, TResult}.MultiplicativeIdentity" />
+        static Int128 IMultiplicativeIdentity<Int128, Int128>.MultiplicativeIdentity => One;
+
+        //
+        // IMultiplyOperators
+        //
+
+        /// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_Multiply(TSelf, TOther)" />
+        public static Int128 operator *(Int128 left, Int128 right)
+        {
+            // We simplify the logic here by just doing unsigned multiplication on
+            // the one's complement representation and then taking the correct sign.
+
+            ulong sign = (left._upper ^ right._upper) & (1UL << 63);
+
+            if (IsNegative(left))
+            {
+                left = ~left + 1U;
+            }
+
+            if (IsNegative(right))
+            {
+                right = ~right + 1U;
+            }
+
+            UInt128 result = (UInt128)(left) * (UInt128)(right);
+
+            if (result == 0U)
+            {
+                sign = 0;
+            }
+            else if (sign != 0)
+            {
+                result = ~result + 1U;
+            }
+
+            return new Int128(
+                result.Upper | sign,
+                result.Lower
+            );
+        }
+
+        /// <inheritdoc cref="IMultiplyOperators{TSelf, TOther, TResult}.op_CheckedMultiply(TSelf, TOther)" />
+        public static Int128 operator checked *(Int128 left, Int128 right)
+        {
+            // We simplify the logic here by just doing unsigned multiplication on
+            // the one's complement representation and then taking the correct sign.
+
+            ulong sign = (left._upper ^ right._upper) & (1UL << 63);
+
+            if (IsNegative(left))
+            {
+                left = ~left + 1U;
+            }
+
+            if (IsNegative(right))
+            {
+                right = ~right + 1U;
+            }
+
+            UInt128 result = checked((UInt128)(left) * (UInt128)(right));
+
+            if ((long)(result.Upper) < 0)
+            {
+                ThrowHelper.ThrowOverflowException();
+            }
+
+            if (result == 0U)
+            {
+                sign = 0;
+            }
+            else if (sign != 0)
+            {
+                result = ~result + 1U;
+            }
+
+            return new Int128(
+                result.Upper | sign,
+                result.Lower
+            );
+        }
+
+        //
+        // INumber
+        //
+
+        /// <inheritdoc cref="INumber{TSelf}.Abs(TSelf)" />
+        public static Int128 Abs(Int128 value)
+        {
+            if (IsNegative(value))
+            {
+                value = -value;
+
+                if (IsNegative(value))
+                {
+                    Math.ThrowNegateTwosCompOverflow();
+                }
+            }
+            return value;
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.Clamp(TSelf, TSelf, TSelf)" />
+        public static Int128 Clamp(Int128 value, Int128 min, Int128 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)" />
+        public static Int128 CopySign(Int128 value, Int128 sign)
+        {
+            Int128 absValue = value;
+
+            if (IsNegative(absValue))
+            {
+                absValue = -absValue;
+            }
+
+            if (IsPositive(sign))
+            {
+                if (IsNegative(absValue))
+                {
+                    Math.ThrowNegateTwosCompOverflow();
+                }
+                return absValue;
+            }
+            return -absValue;
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.CreateChecked{TOther}(TOther)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Int128 CreateChecked<TOther>(TOther value)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                return (byte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                return (char)(object)value;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                return checked((Int128)(decimal)(object)value);
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                return checked((Int128)(double)(object)value);
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                return checked((Int128)(Half)(object)value);
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                return (short)(object)value;
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                return (int)(object)value;
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                return (long)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                return (nint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                return (sbyte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                return checked((Int128)(float)(object)value);
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                return (ushort)(object)value;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                return (uint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                return (ulong)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                return (nuint)(object)value;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                return default;
+            }
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.CreateSaturating{TOther}(TOther)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Int128 CreateSaturating<TOther>(TOther value)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                return (byte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                return (char)(object)value;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                return (Int128)(decimal)(object)value;
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                return (Int128)(double)(object)value;
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                return (Int128)(Half)(object)value;
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                return (short)(object)value;
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                return (int)(object)value;
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                return (long)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                return (nint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                return (sbyte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                return (Int128)(float)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                return (ushort)(object)value;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                return (uint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                return (ulong)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                return (nuint)(object)value;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                return default;
+            }
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.CreateTruncating{TOther}(TOther)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static Int128 CreateTruncating<TOther>(TOther value)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                return (byte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                return (char)(object)value;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                return (Int128)(decimal)(object)value;
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                return (Int128)(double)(object)value;
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                return (Int128)(Half)(object)value;
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                return (short)(object)value;
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                return (int)(object)value;
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                return (long)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                return (nint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                return (sbyte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                return (Int128)(float)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                return (ushort)(object)value;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                return (uint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                return (ulong)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                return (nuint)(object)value;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                return default;
+            }
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.IsNegative(TSelf)" />
+        public static bool IsNegative(Int128 value) => (long)value._upper < 0;
+
+        internal static bool IsPositive(Int128 value) => (long)value._upper >= 0;
+
+        /// <inheritdoc cref="INumber{TSelf}.Max(TSelf, TSelf)" />
+        public static Int128 Max(Int128 x, Int128 y) => (x >= y) ? x : y;
+
+        /// <inheritdoc cref="INumber{TSelf}.MaxMagnitude(TSelf, TSelf)" />
+        public static Int128 MaxMagnitude(Int128 x, Int128 y)
+        {
+            Int128 absX = x;
+
+            if (IsNegative(absX))
+            {
+                absX = -absX;
+
+                if (IsNegative(absX))
+                {
+                    return x;
+                }
+            }
+
+            Int128 absY = y;
+
+            if (IsNegative(absY))
+            {
+                absY = -absY;
+
+                if (IsNegative(absY))
+                {
+                    return y;
+                }
+            }
+
+            return (absX >= absY) ? x : y;
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.Min(TSelf, TSelf)" />
+        public static Int128 Min(Int128 x, Int128 y) => (x <= y) ? x : y;
+
+        /// <inheritdoc cref="INumber{TSelf}.MinMagnitude(TSelf, TSelf)" />
+        public static Int128 MinMagnitude(Int128 x, Int128 y)
+        {
+            Int128 absX = x;
+
+            if (IsNegative(absX))
+            {
+                absX = -absX;
+
+                if (IsNegative(absX))
+                {
+                    return y;
+                }
+            }
+
+            Int128 absY = y;
+
+            if (IsNegative(absY))
+            {
+                absY = -absY;
+
+                if (IsNegative(absY))
+                {
+                    return x;
+                }
+            }
+
+            return (absX <= absY) ? x : y;
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" />
+        public static int Sign(Int128 value)
+        {
+            if (IsNegative(value))
+            {
+                return -1;
+            }
+            else if (value != default)
+            {
+                return 1;
+            }
+            else
+            {
+                return 0;
+            }
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.TryCreate{TOther}(TOther, out TSelf)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool TryCreate<TOther>(TOther value, out Int128 result)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                result = (byte)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                result = (char)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                result = (Int128)(decimal)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                var actualValue = (double)(object)value;
+
+                if ((actualValue < -170141183460469231731687303715884105728.0) || (actualValue >= +170141183460469231731687303715884105728.0) || double.IsNaN(actualValue))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (Int128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                var actualValue = (Half)(object)value;
+
+                if ((actualValue < Half.MinValue) || (actualValue > Half.MaxValue) || Half.IsNaN(actualValue))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (Int128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                result = (short)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                result = (int)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                result = (long)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                result = (nint)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                result = (sbyte)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                var actualValue = (float)(object)value;
+
+                if ((actualValue < -170141183460469231731687303715884105728.0f) || (actualValue >= +170141183460469231731687303715884105728.0f) || float.IsNaN(actualValue))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (Int128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                result = (ushort)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                result = (uint)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                result = (ulong)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                result = (nuint)(object)value;
+                return true;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                result = default;
+                return false;
+            }
+        }
+
+        //
+        // INumberBase
+        //
+
+        /// <inheritdoc cref="INumberBase{TSelf}.One" />
+        public static Int128 One => new Int128(0, 1);
+
+        /// <inheritdoc cref="INumberBase{TSelf}.Zero" />
+        public static Int128 Zero => default;
+
+        //
+        // IParsable
+        //
+
+        public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
+
+        //
+        // IShiftOperators
+        //
+
+        /// <inheritdoc cref="IShiftOperators{TSelf, TResult}.op_LeftShift(TSelf, int)" />
+        public static Int128 operator <<(Int128 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 Int128(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 Int128(upper, lower);
+            }
+            else
+            {
+                return value;
+            }
+        }
+
+        /// <inheritdoc cref="IShiftOperators{TSelf, TResult}.op_RightShift(TSelf, int)" />
+        public static Int128 operator >>(Int128 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 the sign
+                // and so the lower bits are just the upper shifted by the remaining
+                // masked amount
+
+                ulong lower = value._upper >> shiftAmount;
+                ulong upper = (ulong)((long)value._upper >> 63);
+
+                return new Int128(upper, 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 = (ulong)((long)value._upper >> shiftAmount);
+
+                return new Int128(upper, lower);
+            }
+            else
+            {
+                return value;
+            }
+        }
+
+        /// <inheritdoc cref="IShiftOperators{TSelf, TResult}.op_UnsignedRightShift(TSelf, int)" />
+        public static Int128 operator >>>(Int128 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 Int128(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 Int128(upper, lower);
+            }
+            else
+            {
+                return value;
+            }
+        }
+
+        //
+        // ISignedNumber
+        //
+
+        /// <inheritdoc cref="ISignedNumber{TSelf}.NegativeOne" />
+        public static Int128 NegativeOne => new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+        //
+        // ISpanParsable
+        //
+
+        /// <inheritdoc cref="ISpanParsable{TSelf}.Parse(ReadOnlySpan{char}, IFormatProvider?)" />
+        public static Int128 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 Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
+
+        //
+        // ISubtractionOperators
+        //
+
+        /// <inheritdoc cref="ISubtractionOperators{TSelf, TOther, TResult}.op_Subtraction(TSelf, TOther)" />
+        public static Int128 operator -(Int128 left, Int128 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 Int128(upper, lower);
+        }
+
+        /// <inheritdoc cref="ISubtractionOperators{TSelf, TOther, TResult}.op_CheckedSubtraction(TSelf, TOther)" />
+        public static Int128 operator checked -(Int128 left, Int128 right)
+        {
+            // For signed subtraction, we can detect overflow by checking if the sign of
+            // both inputs are different and then if that differs from the sign of the
+            // output.
+
+            Int128 result = left - right;
+
+            uint sign = (uint)(left._upper >> 63);
+
+            if (sign != (uint)(right._upper >> 63))
+            {
+                if (sign != (uint)(result._upper >> 63))
+                {
+                    ThrowHelper.ThrowOverflowException();
+                }
+            }
+            return result;
+        }
+
+        //
+        // IUnaryNegationOperators
+        //
+
+        /// <inheritdoc cref="IUnaryNegationOperators{TSelf, TResult}.op_UnaryNegation(TSelf)" />
+        public static Int128 operator -(Int128 value) => Zero - value;
+
+        /// <inheritdoc cref="IUnaryNegationOperators{TSelf, TResult}.op_CheckedUnaryNegation(TSelf)" />
+        public static Int128 operator checked -(Int128 value) => checked(Zero - value);
+
+        //
+        // IUnaryPlusOperators
+        //
+
+        /// <inheritdoc cref="IUnaryPlusOperators{TSelf, TResult}.op_UnaryPlus(TSelf)" />
+        public static Int128 operator +(Int128 value) => value;
+    }
+}
index 1c54464..c6951fb 100644 (file)
@@ -1476,7 +1476,7 @@ namespace System
         }
 
         [DoesNotReturn]
-        private static void ThrowMinMaxException<T>(T min, T max)
+        internal static void ThrowMinMaxException<T>(T min, T max)
         {
             throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max));
         }
index 728a680..d84220e 100644 (file)
@@ -1171,6 +1171,214 @@ namespace System
             }
         }
 
+        public static string FormatInt128(Int128 value, string? format, IFormatProvider? provider)
+        {
+            // Fast path for default format
+            if (string.IsNullOrEmpty(format))
+            {
+                return Int128.IsPositive(value)
+                     ? UInt128ToDecStr((UInt128)value, digits: -1)
+                     : NegativeInt128ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign);
+            }
+
+            return FormatInt128Slow(value, format, provider);
+
+            static unsafe string FormatInt128Slow(Int128 value, string? format, IFormatProvider? provider)
+            {
+                ReadOnlySpan<char> formatSpan = format;
+
+                char fmt = ParseFormatSpecifier(formatSpan, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return Int128.IsPositive(value)
+                        ? UInt128ToDecStr((UInt128)value, digits)
+                        : NegativeInt128ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return Int128ToHexStr(value, GetHexBase(fmt), digits);
+                }
+                else
+                {
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[Int128NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int128NumberBufferLength);
+
+                    Int128ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, formatSpan, info);
+                    }
+
+                    return sb.ToString();
+                }
+            }
+        }
+
+        public static bool TryFormatInt128(Int128 value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+        {
+            // Fast path for default format
+            if (format.Length == 0)
+            {
+                return Int128.IsPositive(value)
+                     ? TryUInt128ToDecStr((UInt128)value, digits: -1, destination, out charsWritten)
+                     : TryNegativeInt128ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
+            }
+
+            return TryFormatInt128Slow(value, format, provider, destination, out charsWritten);
+
+            static unsafe bool TryFormatInt128Slow(Int128 value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+            {
+                char fmt = ParseFormatSpecifier(format, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return Int128.IsPositive(value)
+                        ? TryUInt128ToDecStr((UInt128)value, digits, destination, out charsWritten)
+                        : TryNegativeInt128ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return TryInt128ToHexStr(value, GetHexBase(fmt), digits, destination, out charsWritten);
+                }
+                else
+                {
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[Int128NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int128NumberBufferLength);
+
+                    Int128ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, format, info);
+                    }
+
+                    return sb.TryCopyTo(destination, out charsWritten);
+                }
+            }
+        }
+
+        public static string FormatUInt128(UInt128 value, string? format, IFormatProvider? provider)
+        {
+            // Fast path for default format
+            if (string.IsNullOrEmpty(format))
+            {
+                return UInt128ToDecStr(value, digits: -1);
+            }
+
+            return FormatUInt128Slow(value, format, provider);
+
+            static unsafe string FormatUInt128Slow(UInt128 value, string? format, IFormatProvider? provider)
+            {
+                ReadOnlySpan<char> formatSpan = format;
+
+                char fmt = ParseFormatSpecifier(formatSpan, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return UInt128ToDecStr(value, digits);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return Int128ToHexStr((Int128)value, GetHexBase(fmt), digits);
+                }
+                else
+                {
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[UInt128NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt128NumberBufferLength);
+
+                    UInt128ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, formatSpan, info);
+                    }
+
+                    return sb.ToString();
+                }
+            }
+        }
+
+        public static bool TryFormatUInt128(UInt128 value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+        {
+            // Fast path for default format
+            if (format.Length == 0)
+            {
+                return TryUInt128ToDecStr(value, digits: -1, destination, out charsWritten);
+            }
+
+            return TryFormatUInt128Slow(value, format, provider, destination, out charsWritten);
+
+            static unsafe bool TryFormatUInt128Slow(UInt128 value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+            {
+                char fmt = ParseFormatSpecifier(format, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return TryUInt128ToDecStr(value, digits, destination, out charsWritten);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return TryInt128ToHexStr((Int128)value, GetHexBase(fmt), digits, destination, out charsWritten);
+                }
+                else
+                {
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[UInt128NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt128NumberBufferLength);
+
+                    UInt128ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, format, info);
+                    }
+
+                    return sb.TryCopyTo(destination, out charsWritten);
+                }
+            }
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static unsafe void Int32ToNumber(int value, ref NumberBuffer number)
         {
@@ -1788,6 +1996,290 @@ namespace System
             return true;
         }
 
+        private static unsafe void Int128ToNumber(Int128 value, ref NumberBuffer number)
+        {
+            number.DigitsCount = Int128Precision;
+
+            if (Int128.IsPositive(value))
+            {
+                number.IsNegative = false;
+            }
+            else
+            {
+                number.IsNegative = true;
+                value = -value;
+            }
+
+            byte* buffer = number.GetDigitsPointer();
+            byte* p = UInt128ToDecChars(buffer + Int128Precision, (UInt128)value, 0);
+
+            int i = (int)(buffer + Int128Precision - p);
+
+            number.DigitsCount = i;
+            number.Scale = i;
+
+            byte* dst = number.GetDigitsPointer();
+            while (--i >= 0)
+                *dst++ = *p++;
+            *dst = (byte)('\0');
+
+            number.CheckConsistency();
+        }
+
+        public static string Int128ToDecStr(Int128 value)
+        {
+            return Int128.IsPositive(value)
+                 ? UInt128ToDecStr((UInt128)value, -1)
+                 : NegativeInt128ToDecStr(value, -1, NumberFormatInfo.CurrentInfo.NegativeSign);
+        }
+
+        private static unsafe string NegativeInt128ToDecStr(Int128 value, int digits, string sNegative)
+        {
+            Debug.Assert(Int128.IsNegative(value));
+
+            if (digits < 1)
+                digits = 1;
+
+            UInt128 absValue = (UInt128)(-value);
+
+            int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(absValue)) + sNegative.Length;
+            string result = string.FastAllocateString(bufferLength);
+            fixed (char* buffer = result)
+            {
+                char* p = UInt128ToDecChars(buffer + bufferLength, absValue, digits);
+                Debug.Assert(p == buffer + sNegative.Length);
+
+                for (int i = sNegative.Length - 1; i >= 0; i--)
+                {
+                    *(--p) = sNegative[i];
+                }
+                Debug.Assert(p == buffer);
+            }
+            return result;
+        }
+
+        private static unsafe bool TryNegativeInt128ToDecStr(Int128 value, int digits, string sNegative, Span<char> destination, out int charsWritten)
+        {
+            Debug.Assert(Int128.IsNegative(value));
+
+            if (digits < 1)
+                digits = 1;
+
+            UInt128 absValue = (UInt128)(-value);
+
+            int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(absValue)) + sNegative.Length;
+            if (bufferLength > destination.Length)
+            {
+                charsWritten = 0;
+                return false;
+            }
+
+            charsWritten = bufferLength;
+            fixed (char* buffer = &MemoryMarshal.GetReference(destination))
+            {
+                char* p = UInt128ToDecChars(buffer + bufferLength, absValue, digits);
+                Debug.Assert(p == buffer + sNegative.Length);
+
+                for (int i = sNegative.Length - 1; i >= 0; i--)
+                {
+                    *(--p) = sNegative[i];
+                }
+                Debug.Assert(p == buffer);
+            }
+            return true;
+        }
+
+        private static unsafe string Int128ToHexStr(Int128 value, char hexBase, int digits)
+        {
+            if (digits < 1)
+                digits = 1;
+
+            UInt128 uValue = (UInt128)value;
+
+            int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits(uValue));
+            string result = string.FastAllocateString(bufferLength);
+            fixed (char* buffer = result)
+            {
+                char* p = Int128ToHexChars(buffer + bufferLength, uValue, hexBase, digits);
+                Debug.Assert(p == buffer);
+            }
+            return result;
+        }
+
+        private static unsafe bool TryInt128ToHexStr(Int128 value, char hexBase, int digits, Span<char> destination, out int charsWritten)
+        {
+            if (digits < 1)
+                digits = 1;
+
+            UInt128 uValue = (UInt128)value;
+
+            int bufferLength = Math.Max(digits, FormattingHelpers.CountHexDigits(uValue));
+            if (bufferLength > destination.Length)
+            {
+                charsWritten = 0;
+                return false;
+            }
+
+            charsWritten = bufferLength;
+            fixed (char* buffer = &MemoryMarshal.GetReference(destination))
+            {
+                char* p = Int128ToHexChars(buffer + bufferLength, uValue, hexBase, digits);
+                Debug.Assert(p == buffer);
+            }
+            return true;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static unsafe char* Int128ToHexChars(char* buffer, UInt128 value, int hexBase, int digits)
+        {
+            ulong lower = value.Lower;
+            ulong upper = value.Upper;
+
+            if (upper != 0)
+            {
+                buffer = Int64ToHexChars(buffer, lower, hexBase, 16);
+                return Int64ToHexChars(buffer, upper, hexBase, digits - 16);
+            }
+            else
+            {
+                return Int64ToHexChars(buffer, lower, hexBase, Math.Max(digits, 1));
+            }
+        }
+
+        private static unsafe void UInt128ToNumber(UInt128 value, ref NumberBuffer number)
+        {
+            number.DigitsCount = UInt128Precision;
+            number.IsNegative = false;
+
+            byte* buffer = number.GetDigitsPointer();
+            byte* p = UInt128ToDecChars(buffer + UInt128Precision, value, 0);
+
+            int i = (int)(buffer + UInt128Precision - p);
+
+            number.DigitsCount = i;
+            number.Scale = i;
+
+            byte* dst = number.GetDigitsPointer();
+            while (--i >= 0)
+                *dst++ = *p++;
+            *dst = (byte)('\0');
+
+            number.CheckConsistency();
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static ulong Int128DivMod1E19(ref UInt128 value)
+        {
+            UInt128 divisor = new UInt128(0, 10_000_000_000_000_000_000);
+            (value, UInt128 remainder) = UInt128.DivRem(value, divisor);
+            return remainder.Lower;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static unsafe byte* UInt128ToDecChars(byte* bufferEnd, UInt128 value)
+        {
+            while (value.Upper != 0)
+            {
+                bufferEnd = UInt64ToDecChars(bufferEnd, Int128DivMod1E19(ref value), 19);
+            }
+            return UInt64ToDecChars(bufferEnd, value.Lower);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static unsafe byte* UInt128ToDecChars(byte* bufferEnd, UInt128 value, int digits)
+        {
+            while (value.Upper != 0)
+            {
+                bufferEnd = UInt64ToDecChars(bufferEnd, Int128DivMod1E19(ref value), 19);
+                digits -= 19;
+            }
+            return UInt64ToDecChars(bufferEnd, value.Lower, digits);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static unsafe char* UInt128ToDecChars(char* bufferEnd, UInt128 value)
+        {
+            while (value.Upper != 0)
+            {
+                bufferEnd = UInt64ToDecChars(bufferEnd, Int128DivMod1E19(ref value), 19);
+            }
+            return UInt64ToDecChars(bufferEnd, value.Lower);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static unsafe char* UInt128ToDecChars(char* bufferEnd, UInt128 value, int digits)
+        {
+            while (value.Upper != 0)
+            {
+                bufferEnd = UInt64ToDecChars(bufferEnd, Int128DivMod1E19(ref value), 19);
+                digits -= 19;
+            }
+            return UInt64ToDecChars(bufferEnd, value.Lower, digits);
+        }
+
+        internal static unsafe string UInt128ToDecStr(UInt128 value)
+        {
+            // Intrinsified in mono interpreter
+            int bufferLength = FormattingHelpers.CountDigits(value);
+
+            // For single-digit values that are very common, especially 0 and 1, just return cached strings.
+            if (bufferLength == 1)
+            {
+                return s_singleDigitStringCache[value.Lower];
+            }
+
+            string result = string.FastAllocateString(bufferLength);
+            fixed (char* buffer = result)
+            {
+                char* p = buffer + bufferLength;
+                p = UInt128ToDecChars(p, value);
+                Debug.Assert(p == buffer);
+            }
+            return result;
+        }
+
+        internal static unsafe string UInt128ToDecStr(UInt128 value, int digits)
+        {
+            if (digits <= 1)
+                return UInt128ToDecStr(value);
+
+            int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value));
+            string result = string.FastAllocateString(bufferLength);
+            fixed (char* buffer = result)
+            {
+                char* p = buffer + bufferLength;
+                p = UInt128ToDecChars(p, value, digits);
+                Debug.Assert(p == buffer);
+            }
+            return result;
+        }
+
+        private static unsafe bool TryUInt128ToDecStr(UInt128 value, int digits, Span<char> destination, out int charsWritten)
+        {
+            int bufferLength = Math.Max(digits, FormattingHelpers.CountDigits(value));
+            if (bufferLength > destination.Length)
+            {
+                charsWritten = 0;
+                return false;
+            }
+
+            charsWritten = bufferLength;
+            fixed (char* buffer = &MemoryMarshal.GetReference(destination))
+            {
+                char* p = buffer + bufferLength;
+                if (digits <= 1)
+                {
+                    p = UInt128ToDecChars(p, value);
+                }
+                else
+                {
+                    p = UInt128ToDecChars(p, value, digits);
+                }
+                Debug.Assert(p == buffer);
+            }
+            return true;
+        }
+
         internal static unsafe char ParseFormatSpecifier(ReadOnlySpan<char> format, out int digits)
         {
             char c = default;
index b68fe37..b3f06b0 100644 (file)
@@ -14,10 +14,12 @@ namespace System
         internal const int DoubleNumberBufferLength = 767 + 1 + 1;  // 767 for the longest input + 1 for rounding: 4.9406564584124654E-324
         internal const int Int32NumberBufferLength = 10 + 1;    // 10 for the longest input: 2,147,483,647
         internal const int Int64NumberBufferLength = 19 + 1;    // 19 for the longest input: 9,223,372,036,854,775,807
+        internal const int Int128NumberBufferLength = 39 + 1;    // 39 for the longest input: 170,141,183,460,469,231,731,687,303,715,884,105,727
         internal const int SingleNumberBufferLength = 112 + 1 + 1;  // 112 for the longest input + 1 for rounding: 1.40129846E-45
         internal const int HalfNumberBufferLength = 21; // 19 for the longest input + 1 for rounding (+1 for the null terminator)
         internal const int UInt32NumberBufferLength = 10 + 1;   // 10 for the longest input: 4,294,967,295
         internal const int UInt64NumberBufferLength = 20 + 1;   // 20 for the longest input: 18,446,744,073,709,551,615
+        internal const int UInt128NumberBufferLength = 39 + 1; // 39 for the longest input: 340,282,366,920,938,463,463,374,607,431,768,211,455
 
         internal unsafe ref struct NumberBuffer
         {
index d9c0662..d0aef03 100644 (file)
@@ -30,6 +30,8 @@ namespace System
         private const int UInt32Precision = Int32Precision;
         private const int Int64Precision = 19;
         private const int UInt64Precision = 20;
+        private const int Int128Precision = 39;
+        private const int UInt128Precision = 39;
 
         private const int DoubleMaxExponent = 309;
         private const int DoubleMinExponent = -324;
@@ -129,6 +131,49 @@ namespace System
             return true;
         }
 
+        private static unsafe bool TryNumberToInt128(ref NumberBuffer number, ref Int128 value)
+        {
+            number.CheckConsistency();
+
+            int i = number.Scale;
+            if ((i > Int128Precision) || (i < number.DigitsCount))
+            {
+                return false;
+            }
+            byte* p = number.GetDigitsPointer();
+            Debug.Assert(p != null);
+            Int128 n = 0;
+            while (--i >= 0)
+            {
+                if ((UInt128)n > new UInt128(0x0CCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC)) // Int128.MaxValue / 10
+                {
+                    return false;
+                }
+                n *= 10;
+                if (*p != '\0')
+                {
+                    n += (*p++ - '0');
+                }
+            }
+            if (number.IsNegative)
+            {
+                n = -n;
+                if (n > 0)
+                {
+                    return false;
+                }
+            }
+            else
+            {
+                if (n < 0)
+                {
+                    return false;
+                }
+            }
+            value = n;
+            return true;
+        }
+
         private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value)
         {
             number.CheckConsistency();
@@ -197,6 +242,40 @@ namespace System
             return true;
         }
 
+        private static unsafe bool TryNumberToUInt128(ref NumberBuffer number, ref UInt128 value)
+        {
+            number.CheckConsistency();
+
+            int i = number.Scale;
+            if (i > UInt128Precision || (i < number.DigitsCount) || number.IsNegative)
+            {
+                return false;
+            }
+            byte* p = number.GetDigitsPointer();
+            Debug.Assert(p != null);
+            UInt128 n = 0U;
+            while (--i >= 0)
+            {
+                if (n > new UInt128(0x1999999999999999, 0x9999999999999999)) // UInt128.MaxValue / 10
+                {
+                    return false;
+                }
+                n *= 10U;
+                if (*p != '\0')
+                {
+                    UInt128 newN = n + (UInt128)(*p++ - '0');
+                    // Detect an overflow here...
+                    if (newN < n)
+                    {
+                        return false;
+                    }
+                    n = newN;
+                }
+            }
+            value = n;
+            return true;
+        }
+
         internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
             ParsingStatus status = TryParseInt32(value, styles, info, out int result);
@@ -219,6 +298,17 @@ namespace System
             return result;
         }
 
+        internal static Int128 ParseInt128(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
+        {
+            ParsingStatus status = TryParseInt128(value, styles, info, out Int128 result);
+            if (status != ParsingStatus.OK)
+            {
+                ThrowOverflowOrFormatExceptionInt128(status);
+            }
+
+            return result;
+        }
+
         internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
             ParsingStatus status = TryParseUInt32(value, styles, info, out uint result);
@@ -241,6 +331,17 @@ namespace System
             return result;
         }
 
+        internal static UInt128 ParseUInt128(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
+        {
+            ParsingStatus status = TryParseUInt128(value, styles, info, out UInt128 result);
+            if (status != ParsingStatus.OK)
+            {
+                ThrowOverflowOrFormatExceptionUInt128(status);
+            }
+
+            return result;
+        }
+
         private static unsafe bool TryParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
         {
             Debug.Assert(str != null);
@@ -518,18 +619,666 @@ namespace System
             return TryParseInt32Number(value, styles, info, out result);
         }
 
-        private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+        private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+        {
+            result = 0;
+            byte* pDigits = stackalloc byte[Int32NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
+            if (!TryStringToNumber(value, styles, ref number, info))
+            {
+                return ParsingStatus.Failed;
+            }
+
+            if (!TryNumberToInt32(ref number, ref result))
+            {
+                return ParsingStatus.Overflow;
+            }
+
+            return ParsingStatus.OK;
+        }
+
+        /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
+        internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+        {
+            Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
+
+            if (value.IsEmpty)
+                goto FalseExit;
+
+            int index = 0;
+            int num = value[0];
+
+            // Skip past any whitespace at the beginning.
+            if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
+            {
+                do
+                {
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
+                    num = value[index];
+                }
+                while (IsWhite(num));
+            }
+
+            // Parse leading sign.
+            int sign = 1;
+            if ((styles & NumberStyles.AllowLeadingSign) != 0)
+            {
+                if (info.HasInvariantNumberSigns)
+                {
+                    if (num == '-')
+                    {
+                        sign = -1;
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                    else if (num == '+')
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                }
+                else if (info.AllowHyphenDuringParsing && num == '-')
+                {
+                    sign = -1;
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
+                    num = value[index];
+                }
+                else
+                {
+                    value = value.Slice(index);
+                    index = 0;
+                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
+                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
+                    {
+                        index += positiveSign.Length;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
+                    {
+                        sign = -1;
+                        index += negativeSign.Length;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                }
+            }
+
+            bool overflow = false;
+            int answer = 0;
+
+            if (IsDigit(num))
+            {
+                // Skip past leading zeros.
+                if (num == '0')
+                {
+                    do
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEnd;
+                        num = value[index];
+                    } while (num == '0');
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
+                }
+
+                // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
+                answer = num - '0'; // first digit
+                index++;
+                for (int i = 0; i < 8; i++) // next 8 digits can't overflow
+                {
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEnd;
+                    num = value[index];
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
+                    index++;
+                    answer = 10 * answer + num - '0';
+                }
+
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEnd;
+                num = value[index];
+                if (!IsDigit(num))
+                    goto HasTrailingChars;
+                index++;
+                // Potential overflow now processing the 10th digit.
+                overflow = answer > int.MaxValue / 10;
+                answer = answer * 10 + num - '0';
+                overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31);
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
+
+                // At this point, we're either overflowing or hitting a formatting error.
+                // Format errors take precedence for compatibility.
+                num = value[index];
+                while (IsDigit(num))
+                {
+                    overflow = true;
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto OverflowExit;
+                    num = value[index];
+                }
+                goto HasTrailingChars;
+            }
+            goto FalseExit;
+
+        DoneAtEndButPotentialOverflow:
+            if (overflow)
+            {
+                goto OverflowExit;
+            }
+        DoneAtEnd:
+            result = answer * sign;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
+
+        HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
+            // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
+            if (IsWhite(num))
+            {
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
+                for (index++; index < value.Length; index++)
+                {
+                    if (!IsWhite(value[index]))
+                        break;
+                }
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
+            }
+
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
+            goto DoneAtEndButPotentialOverflow;
+        }
+
+        /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
+        internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+        {
+            Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
+
+            if (value.IsEmpty)
+                goto FalseExit;
+
+            int index = 0;
+            int num = value[0];
+
+            // Skip past any whitespace at the beginning.
+            if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
+            {
+                do
+                {
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
+                    num = value[index];
+                }
+                while (IsWhite(num));
+            }
+
+            // Parse leading sign.
+            int sign = 1;
+            if ((styles & NumberStyles.AllowLeadingSign) != 0)
+            {
+                if (info.HasInvariantNumberSigns)
+                {
+                    if (num == '-')
+                    {
+                        sign = -1;
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                    else if (num == '+')
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                }
+                else if (info.AllowHyphenDuringParsing && num == '-')
+                {
+                    sign = -1;
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
+                    num = value[index];
+                }
+                else
+                {
+                    value = value.Slice(index);
+                    index = 0;
+                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
+                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
+                    {
+                        index += positiveSign.Length;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
+                    {
+                        sign = -1;
+                        index += negativeSign.Length;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                }
+            }
+
+            bool overflow = false;
+            long answer = 0;
+
+            if (IsDigit(num))
+            {
+                // Skip past leading zeros.
+                if (num == '0')
+                {
+                    do
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEnd;
+                        num = value[index];
+                    } while (num == '0');
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
+                }
+
+                // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
+                answer = num - '0'; // first digit
+                index++;
+                for (int i = 0; i < 17; i++) // next 17 digits can't overflow
+                {
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEnd;
+                    num = value[index];
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
+                    index++;
+                    answer = 10 * answer + num - '0';
+                }
+
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEnd;
+                num = value[index];
+                if (!IsDigit(num))
+                    goto HasTrailingChars;
+                index++;
+                // Potential overflow now processing the 19th digit.
+                overflow = answer > long.MaxValue / 10;
+                answer = answer * 10 + num - '0';
+                overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31);
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
+
+                // At this point, we're either overflowing or hitting a formatting error.
+                // Format errors take precedence for compatibility.
+                num = value[index];
+                while (IsDigit(num))
+                {
+                    overflow = true;
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto OverflowExit;
+                    num = value[index];
+                }
+                goto HasTrailingChars;
+            }
+            goto FalseExit;
+
+        DoneAtEndButPotentialOverflow:
+            if (overflow)
+            {
+                goto OverflowExit;
+            }
+        DoneAtEnd:
+            result = answer * sign;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
+
+        HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
+            // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
+            if (IsWhite(num))
+            {
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
+                for (index++; index < value.Length; index++)
+                {
+                    if (!IsWhite(value[index]))
+                        break;
+                }
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
+            }
+
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
+            goto DoneAtEndButPotentialOverflow;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ParsingStatus TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+        {
+            if ((styles & ~NumberStyles.Integer) == 0)
+            {
+                // Optimized path for the common case of anything that's allowed for integer style.
+                return TryParseInt64IntegerStyle(value, styles, info, out result);
+            }
+
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+            {
+                result = 0;
+                return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result));
+            }
+
+            return TryParseInt64Number(value, styles, info, out result);
+        }
+
+        private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+        {
+            result = 0;
+            byte* pDigits = stackalloc byte[Int64NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
+            if (!TryStringToNumber(value, styles, ref number, info))
+            {
+                return ParsingStatus.Failed;
+            }
+
+            if (!TryNumberToInt64(ref number, ref result))
+            {
+                return ParsingStatus.Overflow;
+            }
+
+            return ParsingStatus.OK;
+        }
+
+        /// <summary>Parses Int128 inputs limited to styles that make up NumberStyles.Integer.</summary>
+        internal static ParsingStatus TryParseInt128IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out Int128 result)
+        {
+            Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
+
+            if (value.IsEmpty)
+                goto FalseExit;
+
+            int index = 0;
+            int num = value[0];
+
+            // Skip past any whitespace at the beginning.
+            if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
+            {
+                do
+                {
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
+                    num = value[index];
+                }
+                while (IsWhite(num));
+            }
+
+            // Parse leading sign.
+            int sign = 1;
+            if ((styles & NumberStyles.AllowLeadingSign) != 0)
+            {
+                if (info.HasInvariantNumberSigns)
+                {
+                    if (num == '-')
+                    {
+                        sign = -1;
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                    else if (num == '+')
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                }
+                else if (info.AllowHyphenDuringParsing && num == '-')
+                {
+                    sign = -1;
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto FalseExit;
+                    num = value[index];
+                }
+                else
+                {
+                    value = value.Slice(index);
+                    index = 0;
+                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
+                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
+                    {
+                        index += positiveSign.Length;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
+                    {
+                        sign = -1;
+                        index += negativeSign.Length;
+                        if ((uint)index >= (uint)value.Length)
+                            goto FalseExit;
+                        num = value[index];
+                    }
+                }
+            }
+
+            bool overflow = false;
+            Int128 answer = 0;
+
+            if (IsDigit(num))
+            {
+                // Skip past leading zeros.
+                if (num == '0')
+                {
+                    do
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length)
+                            goto DoneAtEnd;
+                        num = value[index];
+                    } while (num == '0');
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
+                }
+
+                // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
+                answer = num - '0'; // first digit
+                index++;
+                for (int i = 0; i < 37; i++) // next 37 digits can't overflow
+                {
+                    if ((uint)index >= (uint)value.Length)
+                        goto DoneAtEnd;
+                    num = value[index];
+                    if (!IsDigit(num))
+                        goto HasTrailingChars;
+                    index++;
+                    answer = 10 * answer + num - '0';
+                }
+
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEnd;
+                num = value[index];
+                if (!IsDigit(num))
+                    goto HasTrailingChars;
+                index++;
+                // Potential overflow now processing the 39th digit.
+                overflow = answer > new Int128(0x0CCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC); // Int128.MaxValue / 10
+                answer = answer * 10 + num - '0';
+                overflow |= (UInt128)answer > (UInt128)Int128.MaxValue + (((uint)sign) >> 31);
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
+
+                // At this point, we're either overflowing or hitting a formatting error.
+                // Format errors take precedence for compatibility.
+                num = value[index];
+                while (IsDigit(num))
+                {
+                    overflow = true;
+                    index++;
+                    if ((uint)index >= (uint)value.Length)
+                        goto OverflowExit;
+                    num = value[index];
+                }
+                goto HasTrailingChars;
+            }
+            goto FalseExit;
+
+        DoneAtEndButPotentialOverflow:
+            if (overflow)
+            {
+                goto OverflowExit;
+            }
+        DoneAtEnd:
+            result = answer * sign;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
+
+        HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
+            // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
+            if (IsWhite(num))
+            {
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0)
+                    goto FalseExit;
+                for (index++; index < value.Length; index++)
+                {
+                    if (!IsWhite(value[index]))
+                        break;
+                }
+                if ((uint)index >= (uint)value.Length)
+                    goto DoneAtEndButPotentialOverflow;
+            }
+
+            if (!TrailingZeros(value, index))
+                goto FalseExit;
+
+            goto DoneAtEndButPotentialOverflow;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ParsingStatus TryParseInt128(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out Int128 result)
+        {
+            if ((styles & ~NumberStyles.Integer) == 0)
+            {
+                // Optimized path for the common case of anything that's allowed for integer style.
+                return TryParseInt128IntegerStyle(value, styles, info, out result);
+            }
+
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+            {
+                ParsingStatus status = TryParseUInt128HexNumberStyle(value, styles, out UInt128 unsignedResult);
+                result = new Int128(unsignedResult.Upper, unsignedResult.Lower);
+                return status;
+            }
+
+            return TryParseInt128Number(value, styles, info, out result);
+        }
+
+        private static unsafe ParsingStatus TryParseInt128Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out Int128 result)
+        {
+            result = 0;
+            byte* pDigits = stackalloc byte[Int128NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int128NumberBufferLength);
+
+            if (!TryStringToNumber(value, styles, ref number, info))
+            {
+                return ParsingStatus.Failed;
+            }
+
+            if (!TryNumberToInt128(ref number, ref result))
+            {
+                return ParsingStatus.Overflow;
+            }
+
+            return ParsingStatus.OK;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ParsingStatus TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+        {
+            if ((styles & ~NumberStyles.Integer) == 0)
+            {
+                // Optimized path for the common case of anything that's allowed for integer style.
+                return TryParseUInt32IntegerStyle(value, styles, info, out result);
+            }
+
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+            {
+                return TryParseUInt32HexNumberStyle(value, styles, out result);
+            }
+
+            return TryParseUInt32Number(value, styles, info, out result);
+        }
+
+        private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
         {
             result = 0;
-            byte* pDigits = stackalloc byte[Int32NumberBufferLength];
-            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+            byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
                 return ParsingStatus.Failed;
             }
 
-            if (!TryNumberToInt32(ref number, ref result))
+            if (!TryNumberToUInt32(ref number, ref result))
             {
                 return ParsingStatus.Overflow;
             }
@@ -537,8 +1286,8 @@ namespace System
             return ParsingStatus.OK;
         }
 
-        /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
-        internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+        /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
+        internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
 
@@ -562,21 +1311,21 @@ namespace System
             }
 
             // Parse leading sign.
-            int sign = 1;
+            bool overflow = false;
             if ((styles & NumberStyles.AllowLeadingSign) != 0)
             {
                 if (info.HasInvariantNumberSigns)
                 {
-                    if (num == '-')
+                    if (num == '+')
                     {
-                        sign = -1;
                         index++;
                         if ((uint)index >= (uint)value.Length)
                             goto FalseExit;
                         num = value[index];
                     }
-                    else if (num == '+')
+                    else if (num == '-')
                     {
+                        overflow = true;
                         index++;
                         if ((uint)index >= (uint)value.Length)
                             goto FalseExit;
@@ -585,7 +1334,7 @@ namespace System
                 }
                 else if (info.AllowHyphenDuringParsing && num == '-')
                 {
-                    sign = -1;
+                    overflow = true;
                     index++;
                     if ((uint)index >= (uint)value.Length)
                         goto FalseExit;
@@ -605,7 +1354,7 @@ namespace System
                     }
                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
-                        sign = -1;
+                        overflow = true;
                         index += negativeSign.Length;
                         if ((uint)index >= (uint)value.Length)
                             goto FalseExit;
@@ -614,7 +1363,6 @@ namespace System
                 }
             }
 
-            bool overflow = false;
             int answer = 0;
 
             if (IsDigit(num))
@@ -630,7 +1378,7 @@ namespace System
                         num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num))
-                        goto HasTrailingChars;
+                        goto HasTrailingCharsZero;
                 }
 
                 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
@@ -639,7 +1387,7 @@ namespace System
                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEnd;
+                        goto DoneAtEndButPotentialOverflow;
                     num = value[index];
                     if (!IsDigit(num))
                         goto HasTrailingChars;
@@ -648,15 +1396,14 @@ namespace System
                 }
 
                 if ((uint)index >= (uint)value.Length)
-                    goto DoneAtEnd;
+                    goto DoneAtEndButPotentialOverflow;
                 num = value[index];
                 if (!IsDigit(num))
                     goto HasTrailingChars;
                 index++;
                 // Potential overflow now processing the 10th digit.
-                overflow = answer > int.MaxValue / 10;
+                overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5');
                 answer = answer * 10 + num - '0';
-                overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31);
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEndButPotentialOverflow;
 
@@ -681,7 +1428,7 @@ namespace System
                 goto OverflowExit;
             }
         DoneAtEnd:
-            result = answer * sign;
+            result = (uint)answer;
             ParsingStatus status = ParsingStatus.OK;
         Exit:
             return status;
@@ -695,6 +1442,8 @@ namespace System
             status = ParsingStatus.Overflow;
             goto Exit;
 
+        HasTrailingCharsZero:
+            overflow = false;
         HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
             // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
             if (IsWhite(num))
@@ -716,10 +1465,10 @@ namespace System
             goto DoneAtEndButPotentialOverflow;
         }
 
-        /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
-        internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+        /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
+        internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out uint result)
         {
-            Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
+            Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
 
             if (value.IsEmpty)
                 goto FalseExit;
@@ -740,63 +1489,10 @@ namespace System
                 while (IsWhite(num));
             }
 
-            // Parse leading sign.
-            int sign = 1;
-            if ((styles & NumberStyles.AllowLeadingSign) != 0)
-            {
-                if (info.HasInvariantNumberSigns)
-                {
-                    if (num == '-')
-                    {
-                        sign = -1;
-                        index++;
-                        if ((uint)index >= (uint)value.Length)
-                            goto FalseExit;
-                        num = value[index];
-                    }
-                    else if (num == '+')
-                    {
-                        index++;
-                        if ((uint)index >= (uint)value.Length)
-                            goto FalseExit;
-                        num = value[index];
-                    }
-                }
-                else if (info.AllowHyphenDuringParsing && num == '-')
-                {
-                    sign = -1;
-                    index++;
-                    if ((uint)index >= (uint)value.Length)
-                        goto FalseExit;
-                    num = value[index];
-                }
-                else
-                {
-                    value = value.Slice(index);
-                    index = 0;
-                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
-                    {
-                        index += positiveSign.Length;
-                        if ((uint)index >= (uint)value.Length)
-                            goto FalseExit;
-                        num = value[index];
-                    }
-                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
-                    {
-                        sign = -1;
-                        index += negativeSign.Length;
-                        if ((uint)index >= (uint)value.Length)
-                            goto FalseExit;
-                        num = value[index];
-                    }
-                }
-            }
-
             bool overflow = false;
-            long answer = 0;
+            uint answer = 0;
 
-            if (IsDigit(num))
+            if (HexConverter.IsHexChar(num))
             {
                 // Skip past leading zeros.
                 if (num == '0')
@@ -808,48 +1504,43 @@ namespace System
                             goto DoneAtEnd;
                         num = value[index];
                     } while (num == '0');
-                    if (!IsDigit(num))
+                    if (!HexConverter.IsHexChar(num))
                         goto HasTrailingChars;
                 }
 
-                // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits.
-                answer = num - '0'; // first digit
+                // Parse up through 8 digits, as no overflow is possible
+                answer = (uint)HexConverter.FromChar(num); // first digit
                 index++;
-                for (int i = 0; i < 17; i++) // next 17 digits can't overflow
+                for (int i = 0; i < 7; i++) // next 7 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
                         goto DoneAtEnd;
                     num = value[index];
-                    if (!IsDigit(num))
+
+                    uint numValue = (uint)HexConverter.FromChar(num);
+                    if (numValue == 0xFF)
                         goto HasTrailingChars;
                     index++;
-                    answer = 10 * answer + num - '0';
+                    answer = 16 * answer + numValue;
                 }
 
+                // If there's another digit, it's an overflow.
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEnd;
                 num = value[index];
-                if (!IsDigit(num))
+                if (!HexConverter.IsHexChar(num))
                     goto HasTrailingChars;
-                index++;
-                // Potential overflow now processing the 19th digit.
-                overflow = answer > long.MaxValue / 10;
-                answer = answer * 10 + num - '0';
-                overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31);
-                if ((uint)index >= (uint)value.Length)
-                    goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
-                // Format errors take precedence for compatibility.
-                num = value[index];
-                while (IsDigit(num))
+                // Format errors take precedence for compatibility. Read through any remaining digits.
+                do
                 {
-                    overflow = true;
                     index++;
                     if ((uint)index >= (uint)value.Length)
                         goto OverflowExit;
                     num = value[index];
-                }
+                } while (HexConverter.IsHexChar(num));
+                overflow = true;
                 goto HasTrailingChars;
             }
             goto FalseExit;
@@ -860,7 +1551,7 @@ namespace System
                 goto OverflowExit;
             }
         DoneAtEnd:
-            result = answer * sign;
+            result = answer;
             ParsingStatus status = ParsingStatus.OK;
         Exit:
             return status;
@@ -896,71 +1587,34 @@ namespace System
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static ParsingStatus TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
-        {
-            if ((styles & ~NumberStyles.Integer) == 0)
-            {
-                // Optimized path for the common case of anything that's allowed for integer style.
-                return TryParseInt64IntegerStyle(value, styles, info, out result);
-            }
-
-            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
-            {
-                result = 0;
-                return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result));
-            }
-
-            return TryParseInt64Number(value, styles, info, out result);
-        }
-
-        private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
-        {
-            result = 0;
-            byte* pDigits = stackalloc byte[Int64NumberBufferLength];
-            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
-
-            if (!TryStringToNumber(value, styles, ref number, info))
-            {
-                return ParsingStatus.Failed;
-            }
-
-            if (!TryNumberToInt64(ref number, ref result))
-            {
-                return ParsingStatus.Overflow;
-            }
-
-            return ParsingStatus.OK;
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static ParsingStatus TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+        internal static ParsingStatus TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
         {
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                return TryParseUInt32IntegerStyle(value, styles, info, out result);
+                return TryParseUInt64IntegerStyle(value, styles, info, out result);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                return TryParseUInt32HexNumberStyle(value, styles, out result);
+                return TryParseUInt64HexNumberStyle(value, styles, out result);
             }
 
-            return TryParseUInt32Number(value, styles, info, out result);
+            return TryParseUInt64Number(value, styles, info, out result);
         }
 
-        private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+        private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
         {
             result = 0;
-            byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
-            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+            byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
                 return ParsingStatus.Failed;
             }
 
-            if (!TryNumberToUInt32(ref number, ref result))
+            if (!TryNumberToUInt64(ref number, ref result))
             {
                 return ParsingStatus.Overflow;
             }
@@ -968,8 +1622,8 @@ namespace System
             return ParsingStatus.OK;
         }
 
-        /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
-        internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+        /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
+        internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
 
@@ -1045,7 +1699,7 @@ namespace System
                 }
             }
 
-            int answer = 0;
+            long answer = 0;
 
             if (IsDigit(num))
             {
@@ -1063,10 +1717,10 @@ namespace System
                         goto HasTrailingCharsZero;
                 }
 
-                // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
+                // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
                 answer = num - '0'; // first digit
                 index++;
-                for (int i = 0; i < 8; i++) // next 8 digits can't overflow
+                for (int i = 0; i < 18; i++) // next 18 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
                         goto DoneAtEndButPotentialOverflow;
@@ -1083,8 +1737,8 @@ namespace System
                 if (!IsDigit(num))
                     goto HasTrailingChars;
                 index++;
-                // Potential overflow now processing the 10th digit.
-                overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5');
+                // Potential overflow now processing the 20th digit.
+                overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5');
                 answer = answer * 10 + num - '0';
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEndButPotentialOverflow;
@@ -1110,7 +1764,7 @@ namespace System
                 goto OverflowExit;
             }
         DoneAtEnd:
-            result = (uint)answer;
+            result = (ulong)answer;
             ParsingStatus status = ParsingStatus.OK;
         Exit:
             return status;
@@ -1147,8 +1801,8 @@ namespace System
             goto DoneAtEndButPotentialOverflow;
         }
 
-        /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
-        internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out uint result)
+        /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
+        private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out ulong result)
         {
             Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
 
@@ -1172,7 +1826,7 @@ namespace System
             }
 
             bool overflow = false;
-            uint answer = 0;
+            ulong answer = 0;
 
             if (HexConverter.IsHexChar(num))
             {
@@ -1190,10 +1844,10 @@ namespace System
                         goto HasTrailingChars;
                 }
 
-                // Parse up through 8 digits, as no overflow is possible
+                // Parse up through 16 digits, as no overflow is possible
                 answer = (uint)HexConverter.FromChar(num); // first digit
                 index++;
-                for (int i = 0; i < 7; i++) // next 7 digits can't overflow
+                for (int i = 0; i < 15; i++) // next 15 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
                         goto DoneAtEnd;
@@ -1269,34 +1923,34 @@ namespace System
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static ParsingStatus TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+        internal static ParsingStatus TryParseUInt128(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out UInt128 result)
         {
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                return TryParseUInt64IntegerStyle(value, styles, info, out result);
+                return TryParseUInt128IntegerStyle(value, styles, info, out result);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                return TryParseUInt64HexNumberStyle(value, styles, out result);
+                return TryParseUInt128HexNumberStyle(value, styles, out result);
             }
 
-            return TryParseUInt64Number(value, styles, info, out result);
+            return TryParseUInt128Number(value, styles, info, out result);
         }
 
-        private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+        private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out UInt128 result)
         {
-            result = 0;
-            byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
-            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+            result = 0U;
+            byte* pDigits = stackalloc byte[UInt128NumberBufferLength];
+            NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt128NumberBufferLength);
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
                 return ParsingStatus.Failed;
             }
 
-            if (!TryNumberToUInt64(ref number, ref result))
+            if (!TryNumberToUInt128(ref number, ref result))
             {
                 return ParsingStatus.Overflow;
             }
@@ -1304,8 +1958,8 @@ namespace System
             return ParsingStatus.OK;
         }
 
-        /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
-        internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+        /// <summary>Parses UInt128 limited to styles that make up NumberStyles.Integer.</summary>
+        internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out UInt128 result)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
 
@@ -1381,7 +2035,7 @@ namespace System
                 }
             }
 
-            long answer = 0;
+            Int128 answer = 0;
 
             if (IsDigit(num))
             {
@@ -1399,10 +2053,10 @@ namespace System
                         goto HasTrailingCharsZero;
                 }
 
-                // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
+                // Parse most digits, up to the potential for overflow, which can't happen until after 38 digits.
                 answer = num - '0'; // first digit
                 index++;
-                for (int i = 0; i < 18; i++) // next 18 digits can't overflow
+                for (int i = 0; i < 37; i++) // next 37 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
                         goto DoneAtEndButPotentialOverflow;
@@ -1419,8 +2073,10 @@ namespace System
                 if (!IsDigit(num))
                     goto HasTrailingChars;
                 index++;
-                // Potential overflow now processing the 20th digit.
-                overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5');
+                // Potential overflow now processing the 39th digit.
+                UInt128 maxValueDiv10 = new UInt128(0x1999999999999999, 0x9999999999999999); // UInt128.MaxValue / 10
+                overflow |= (UInt128)answer > maxValueDiv10;
+                overflow |= ((UInt128)answer == maxValueDiv10 && num > '5');
                 answer = answer * 10 + num - '0';
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEndButPotentialOverflow;
@@ -1446,17 +2102,17 @@ namespace System
                 goto OverflowExit;
             }
         DoneAtEnd:
-            result = (ulong)answer;
+            result = (UInt128)answer;
             ParsingStatus status = ParsingStatus.OK;
         Exit:
             return status;
 
         FalseExit: // parsing failed
-            result = 0;
+            result = 0U;
             status = ParsingStatus.Failed;
             goto Exit;
         OverflowExit:
-            result = 0;
+            result = 0U;
             status = ParsingStatus.Overflow;
             goto Exit;
 
@@ -1483,8 +2139,8 @@ namespace System
             goto DoneAtEndButPotentialOverflow;
         }
 
-        /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
-        private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out ulong result)
+        /// <summary>Parses UInt128 limited to styles that make up NumberStyles.HexNumber.</summary>
+        private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out UInt128 result)
         {
             Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
 
@@ -1508,7 +2164,7 @@ namespace System
             }
 
             bool overflow = false;
-            ulong answer = 0;
+            UInt128 answer = 0U;
 
             if (HexConverter.IsHexChar(num))
             {
@@ -1526,10 +2182,10 @@ namespace System
                         goto HasTrailingChars;
                 }
 
-                // Parse up through 16 digits, as no overflow is possible
+                // Parse up through 32 digits, as no overflow is possible
                 answer = (uint)HexConverter.FromChar(num); // first digit
                 index++;
-                for (int i = 0; i < 15; i++) // next 15 digits can't overflow
+                for (int i = 0; i < 31; i++) // next 31 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
                         goto DoneAtEnd;
@@ -1539,7 +2195,7 @@ namespace System
                     if (numValue == 0xFF)
                         goto HasTrailingChars;
                     index++;
-                    answer = 16 * answer + numValue;
+                    answer = 16U * answer + numValue;
                 }
 
                 // If there's another digit, it's an overflow.
@@ -1575,11 +2231,11 @@ namespace System
             return status;
 
         FalseExit: // parsing failed
-            result = 0;
+            result = 0U;
             status = ParsingStatus.Failed;
             goto Exit;
         OverflowExit:
-            result = 0;
+            result = 0U;
             status = ParsingStatus.Overflow;
             goto Exit;
 
@@ -2086,6 +2742,12 @@ namespace System
         [DoesNotReturn]
         internal static void ThrowOverflowException(TypeCode type) => throw GetException(ParsingStatus.Overflow, type);
 
+        [DoesNotReturn]
+        internal static void ThrowOverflowOrFormatExceptionInt128(ParsingStatus status) => throw GetExceptionInt128(status);
+
+        [DoesNotReturn]
+        internal static void ThrowOverflowOrFormatExceptionUInt128(ParsingStatus status) => throw GetExceptionUInt128(status);
+
         private static Exception GetException(ParsingStatus status, TypeCode type)
         {
             if (status == ParsingStatus.Failed)
@@ -2126,6 +2788,22 @@ namespace System
             return new OverflowException(s);
         }
 
+        private static Exception GetExceptionInt128(ParsingStatus status)
+        {
+            if (status == ParsingStatus.Failed)
+                throw new FormatException(SR.Format_InvalidString);
+
+            return new OverflowException(SR.Overflow_Int128);
+        }
+
+        private static Exception GetExceptionUInt128(ParsingStatus status)
+        {
+            if (status == ParsingStatus.Failed)
+                throw new FormatException(SR.Format_InvalidString);
+
+            return new OverflowException(SR.Overflow_UInt128);
+        }
+
         internal static double NumberToDouble(ref NumberBuffer number)
         {
             number.CheckConsistency();
index 57f9b76..cddd18e 100644 (file)
@@ -161,6 +161,18 @@ namespace System.Numerics
 #endif
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static int LeadingZeroCount(Int128 value)
+        {
+            ulong upper = value.Upper;
+
+            if (upper == 0)
+            {
+                return 64 + LeadingZeroCount(value.Lower);
+            }
+            return LeadingZeroCount(upper);
+        }
+
         /// <summary>
         /// Count the number of leading zero bits in a mask.
         /// Similar in behavior to the x86 instruction LZCNT.
@@ -234,6 +246,18 @@ namespace System.Numerics
             return LeadingZeroCount(hi);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static int LeadingZeroCount(UInt128 value)
+        {
+            ulong upper = value.Upper;
+
+            if (upper == 0)
+            {
+                return 64 + LeadingZeroCount(value.Lower);
+            }
+            return LeadingZeroCount(upper);
+        }
+
         /// <summary>
         /// Count the number of leading zero bits in a mask.
         /// Similar in behavior to the x86 instruction LZCNT.
index fd21795..fe07737 100644 (file)
@@ -34,17 +34,17 @@ namespace System
         }
 #pragma warning restore CA1821
 
-        // Returns a String which represents the object instance.  The default
-        // for an object is to return the fully qualified name of the class.
+        /// <summary>Returns a string that represents the current object.</summary>
+        /// <returns>A string that represents the current object.</returns>
         public virtual string? ToString()
         {
+            // The default for an object is to return the fully qualified name of the class.
             return GetType().ToString();
         }
 
-        // Returns a boolean indicating if the passed in object obj is
-        // Equal to this.  Equality is defined as object equality for reference
-        // types and bitwise equality for value types using a loader trick to
-        // replace Equals with EqualsValue for value types).
+        /// <summary>Determines whether the specified object is equal to the current object.</summary>
+        /// <param name="obj">The object to compare with the current object.</param>
+        /// <returns><c>true</c> if the specified object is equal to the current object; otherwise, <c>false</c>.</returns>
         public virtual bool Equals(object? obj)
         {
             return RuntimeHelpers.Equals(this, obj);
@@ -69,16 +69,19 @@ namespace System
             return objA == objB;
         }
 
-        // GetHashCode is intended to serve as a hash function for this object.
-        // Based on the contents of the object, the hash function will return a suitable
-        // value with a relatively random distribution over the various inputs.
-        //
-        // The default implementation returns the sync block index for this instance.
-        // Calling it on the same object multiple times will return the same value, so
-        // it will technically meet the needs of a hash function, but it's less than ideal.
-        // Objects (& especially value classes) should override this method.
+        /// <summary>Serves as the default hash function.</summary>
+        /// <returns>A hash code for the current object.</returns>
         public virtual int GetHashCode()
         {
+            // GetHashCode is intended to serve as a hash function for this object.
+            // Based on the contents of the object, the hash function will return a suitable
+            // value with a relatively random distribution over the various inputs.
+            //
+            // The default implementation returns the sync block index for this instance.
+            // Calling it on the same object multiple times will return the same value, so
+            // it will technically meet the needs of a hash function, but it's less than ideal.
+            // Objects (& especially value classes) should override this method.
+
             return RuntimeHelpers.GetHashCode(this);
         }
     }
index 171f511..e83ecdd 100644 (file)
@@ -1005,6 +1005,10 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (float)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1029,6 +1033,10 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (float)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1073,6 +1081,10 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (float)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1097,6 +1109,10 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (float)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1141,6 +1157,10 @@ namespace System
             {
                 return (long)(object)value;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                return (float)(Int128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 return (nint)(object)value;
@@ -1165,6 +1185,10 @@ namespace System
             {
                 return (ulong)(object)value;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                return (float)(UInt128)(object)value;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 return (nuint)(object)value;
@@ -1231,6 +1255,11 @@ namespace System
                 result = (long)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(Int128))
+            {
+                result = (float)(Int128)(object)value;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nint))
             {
                 result = (nint)(object)value;
@@ -1261,6 +1290,11 @@ namespace System
                 result = (ulong)(object)value;
                 return true;
             }
+            else if (typeof(TOther) == typeof(UInt128))
+            {
+                result = (float)(UInt128)(object)value;
+                return true;
+            }
             else if (typeof(TOther) == typeof(nuint))
             {
                 result = (nuint)(object)value;
index f85a121..3e68ff7 100644 (file)
@@ -197,6 +197,12 @@ namespace System
         }
 
         [DoesNotReturn]
+        internal static void ThrowOverflowException()
+        {
+            throw new OverflowException();
+        }
+
+        [DoesNotReturn]
         internal static void ThrowOverflowException_TimeSpanTooLong()
         {
             throw new OverflowException(SR.Overflow_TimeSpanTooLong);
diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs
new file mode 100644 (file)
index 0000000..785584a
--- /dev/null
@@ -0,0 +1,1838 @@
+// 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;
+
+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>
+    {
+        internal const int Size = 16;
+
+        // Unix System V ABI actually requires this to be little endian
+        // order and not `upper, lower` on big endian systems.
+
+        private readonly ulong _lower;
+        private readonly ulong _upper;
+
+        /// <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);
+        }
+
+        public static UInt128 Parse(string s)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            return Number.ParseUInt128(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo);
+        }
+
+        public static UInt128 Parse(string s, NumberStyles style)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.ParseUInt128(s, style, NumberFormatInfo.CurrentInfo);
+        }
+
+        public static UInt128 Parse(string s, IFormatProvider? provider)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            return Number.ParseUInt128(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider));
+        }
+
+        public static UInt128 Parse(string s, NumberStyles style, IFormatProvider? provider)
+        {
+            ArgumentNullException.ThrowIfNull(s);
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.ParseUInt128(s, style, NumberFormatInfo.GetInstance(provider));
+        }
+
+        public static UInt128 Parse(ReadOnlySpan<char> s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
+        {
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.ParseUInt128(s, style, NumberFormatInfo.GetInstance(provider));
+        }
+
+        public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result)
+        {
+            if (s is not null)
+            {
+                return Number.TryParseUInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
+            }
+            else
+            {
+                result = default;
+                return false;
+            }
+        }
+
+        public static bool TryParse(ReadOnlySpan<char> s, out UInt128 result)
+        {
+            return Number.TryParseUInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
+        }
+
+        public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result)
+        {
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+
+            if (s is not null)
+            {
+                return Number.TryParseUInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+            }
+            else
+            {
+                result = default;
+                return false;
+            }
+        }
+
+        public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out UInt128 result)
+        {
+            NumberFormatInfo.ValidateParseStyleInteger(style);
+            return Number.TryParseUInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == 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(TypeCode.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 >> 24) == 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 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 | (ulong)(value >> 76)) - 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="Half" /> 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(Half value) => (UInt128)(double)(value);
+
+        /// <summary>Explicitly converts a <see cref="Half" /> 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(Half value) => checked((UInt128)(double)(value));
+
+        /// <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);
+
+        private void WriteLittleEndianUnsafe(Span<byte> destination)
+        {
+            Debug.Assert(destination.Length >= Size);
+
+            ulong lower = _lower;
+            ulong upper = _upper;
+
+            if (!BitConverter.IsLittleEndian)
+            {
+                ulong tmp = lower;
+                lower = BinaryPrimitives.ReverseEndianness(upper);
+                upper = BinaryPrimitives.ReverseEndianness(tmp);
+            }
+
+            ref byte address = ref MemoryMarshal.GetReference(destination);
+
+            Unsafe.WriteUnaligned(ref address, lower);
+            Unsafe.WriteUnaligned(ref Unsafe.AddByteOffset(ref address, sizeof(ulong)), upper);
+        }
+
+        //
+        // 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)
+        {
+            UInt128 quotient = left / right;
+            return (quotient, left - (quotient * right));
+        }
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.LeadingZeroCount(TSelf)" />
+        public static UInt128 LeadingZeroCount(UInt128 value)
+        {
+            if (value._upper == 0)
+            {
+                return 64 + ulong.LeadingZeroCount(value._lower);
+            }
+            return ulong.LeadingZeroCount(value._upper);
+        }
+
+        /// <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}.GetShortestBitLength()" />
+        long IBinaryInteger<UInt128>.GetShortestBitLength()
+        {
+            UInt128 value = this;
+            return (Size * 8) - BitOperations.LeadingZeroCount(value);
+        }
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.GetByteCount()" />
+        int IBinaryInteger<UInt128>.GetByteCount() => Size;
+
+        /// <inheritdoc cref="IBinaryInteger{TSelf}.TryWriteLittleEndian(Span{byte}, out int)" />
+        bool IBinaryInteger<UInt128>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten)
+        {
+            if (destination.Length >= Size)
+            {
+                WriteLittleEndianUnsafe(destination);
+                bytesWritten = Size;
+                return true;
+            }
+            else
+            {
+                bytesWritten = 0;
+                return false;
+            }
+        }
+
+        //
+        // IBinaryNumber
+        //
+
+        /// <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}.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}.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}.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}.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)
+        {
+            if ((right._upper == 0) && (left._upper == 0))
+            {
+                // left and right are both uint64
+                return left._lower / right._lower;
+            }
+
+            if (right >= left)
+            {
+                return (right == left) ? One : Zero;
+            }
+
+            return DivideSlow(left, right);
+
+            static uint AddDivisor(Span<uint> left, ReadOnlySpan<uint> right)
+            {
+                Debug.Assert(left.Length >= right.Length);
+
+                // Repairs the dividend, if the last subtract was too much
+
+                ulong carry = 0UL;
+
+                for (int i = 0; i < right.Length; i++)
+                {
+                    ref uint leftElement = ref left[i];
+                    ulong digit = (leftElement + carry) + right[i];
+
+                    leftElement = unchecked((uint)digit);
+                    carry = digit >> 32;
+                }
+
+                return (uint)carry;
+            }
+
+            static bool DivideGuessTooBig(ulong q, ulong valHi, uint valLo, uint divHi, uint divLo)
+            {
+                Debug.Assert(q <= 0xFFFFFFFF);
+
+                // 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 chkHi = divHi * q;
+                ulong chkLo = divLo * q;
+
+                chkHi += (chkLo >> 32);
+                chkLo = (uint)(chkLo);
+
+                return (chkHi > valHi) || ((chkHi == valHi) && (chkLo > valLo));
+            }
+
+            unsafe static UInt128 DivideSlow(UInt128 quotient, UInt128 divisor)
+            {
+                // This is the same algorithm currently used by BigInteger so
+                // we need to get a Span<uint> containing the value represented
+                // in the least number of elements possible.
+
+                uint* pLeft = stackalloc uint[Size / sizeof(uint)];
+                quotient.WriteLittleEndianUnsafe(new Span<byte>(pLeft, Size));
+                Span<uint> left = new Span<uint>(pLeft, (Size / sizeof(uint)) - (BitOperations.LeadingZeroCount(quotient) / 32));
+
+                uint* pRight = stackalloc uint[Size / sizeof(uint)];
+                divisor.WriteLittleEndianUnsafe(new Span<byte>(pRight, Size));
+                Span<uint> right = new Span<uint>(pRight, (Size / sizeof(uint)) - (BitOperations.LeadingZeroCount(divisor) / 32));
+
+                Span<uint> rawBits = stackalloc uint[Size / sizeof(uint)];
+                rawBits.Clear();
+                Span<uint> bits = rawBits.Slice(0, left.Length - right.Length + 1);
+
+                Debug.Assert(left.Length >= 1);
+                Debug.Assert(right.Length >= 1);
+                Debug.Assert(left.Length >= right.Length);
+
+                // 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.
+
+                uint divHi = right[right.Length - 1];
+                uint divLo = right.Length > 1 ? right[right.Length - 2] : 0;
+
+                // We measure the leading zeros of the divisor
+                int shift = BitOperations.LeadingZeroCount(divHi);
+                int backShift = 32 - shift;
+
+                // And, we make sure the most significant bit is set
+                if (shift > 0)
+                {
+                    uint divNx = right.Length > 2 ? right[right.Length - 3] : 0;
+
+                    divHi = (divHi << shift) | (divLo >> backShift);
+                    divLo = (divLo << shift) | (divNx >> backShift);
+                }
+
+                // Then, we divide all of the bits as we would do it using
+                // pen and paper: guessing the next digit, subtracting, ...
+                for (int i = left.Length; i >= right.Length; i--)
+                {
+                    int n = i - right.Length;
+                    uint t = ((uint)(i) < (uint)(left.Length)) ? left[i] : 0;
+
+                    ulong valHi = ((ulong)(t) << 32) | left[i - 1];
+                    uint valLo = (i > 1) ? left[i - 2] : 0;
+
+                    // We shifted the divisor, we shift the dividend too
+                    if (shift > 0)
+                    {
+                        uint valNx = i > 2 ? left[i - 3] : 0;
+
+                        valHi = (valHi << shift) | (valLo >> backShift);
+                        valLo = (valLo << shift) | (valNx >> backShift);
+                    }
+
+                    // First guess for the current digit of the quotient,
+                    // which naturally must have only 32 bits...
+                    ulong digit = valHi / divHi;
+
+                    if (digit > 0xFFFFFFFF)
+                    {
+                        digit = 0xFFFFFFFF;
+                    }
+
+                    // Our first guess may be a little bit to big
+                    while (DivideGuessTooBig(digit, valHi, valLo, divHi, divLo))
+                    {
+                        --digit;
+                    }
+
+                    if (digit > 0)
+                    {
+                        // Now it's time to subtract our current quotient
+                        uint carry = SubtractDivisor(left.Slice(n), right, digit);
+
+                        if (carry != t)
+                        {
+                            Debug.Assert(carry == (t + 1));
+
+                            // Our guess was still exactly one too high
+                            carry = AddDivisor(left.Slice(n), right);
+
+                            --digit;
+                            Debug.Assert(carry == 1);
+                        }
+                    }
+
+                    // We have the digit!
+                    if ((uint)(n) < (uint)(bits.Length))
+                    {
+                        bits[n] = (uint)(digit);
+                    }
+
+                    if ((uint)(i) < (uint)(left.Length))
+                    {
+                        left[i] = 0;
+                    }
+                }
+
+                return new UInt128(
+                    ((ulong)(rawBits[3]) << 32) | rawBits[2],
+                    ((ulong)(rawBits[1]) << 32) | rawBits[0]
+                );
+            }
+
+            static uint SubtractDivisor(Span<uint> left, ReadOnlySpan<uint> right, ulong q)
+            {
+                Debug.Assert(left.Length >= right.Length);
+                Debug.Assert(q <= 0xFFFFFFFF);
+
+                // Combines a subtract and a multiply operation, which is naturally
+                // more efficient than multiplying and then subtracting...
+
+                ulong carry = 0UL;
+
+                for (int i = 0; i < right.Length; i++)
+                {
+                    carry += right[i] * q;
+
+                    uint digit = (uint)(carry);
+                    carry >>= 32;
+
+                    ref uint leftElement = ref left[i];
+
+                    if (leftElement < digit)
+                    {
+                        ++carry;
+                    }
+                    leftElement -= digit;
+                }
+
+                return (uint)(carry);
+            }
+        }
+
+        /// <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}.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}.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)
+        {
+            UInt128 quotient = left / right;
+            return left - (quotient * right);
+        }
+
+        //
+        // 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;
+        }
+
+        internal 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
+
+            UInt128 al = left._lower;
+            UInt128 ah = left._upper;
+
+            UInt128 bl = right._lower;
+            UInt128 bh = right._upper;
+
+            UInt128 mull = al * bl;
+            UInt128 t = ah * bl + mull._upper;
+            UInt128 tl = al * bh + t._lower;
+
+            lower = new UInt128(tl._lower, mull._lower);
+            return ah * bh + t._upper + tl._upper;
+        }
+
+        //
+        // INumber
+        //
+
+        /// <inheritdoc cref="INumber{TSelf}.Abs(TSelf)" />
+        static UInt128 INumber<UInt128>.Abs(UInt128 value) => value;
+
+        /// <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}.CreateChecked{TOther}(TOther)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static UInt128 CreateChecked<TOther>(TOther value)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                return (byte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                return (char)(object)value;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                return checked((UInt128)(decimal)(object)value);
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                return checked((UInt128)(double)(object)value);
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                return checked((UInt128)(Half)(object)value);
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                return checked((UInt128)(short)(object)value);
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                return checked((UInt128)(int)(object)value);
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                return checked((UInt128)(long)(object)value);
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                return checked((UInt128)(nint)(object)value);
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                return checked((UInt128)(sbyte)(object)value);
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                return checked((UInt128)(float)(object)value);
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                return (ushort)(object)value;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                return (uint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                return (ulong)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                return (nuint)(object)value;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                return default;
+            }
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.CreateSaturating{TOther}(TOther)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static UInt128 CreateSaturating<TOther>(TOther value)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                return (byte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                return (char)(object)value;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                var actualValue = (decimal)(object)value;
+                return (actualValue < 0) ? MinValue : (UInt128)actualValue;
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                return (UInt128)(double)(object)value;
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                return (UInt128)(Half)(object)value;
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                var actualValue = (short)(object)value;
+                return (actualValue < 0) ? MinValue : (UInt128)actualValue;
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                var actualValue = (int)(object)value;
+                return (actualValue < 0) ? MinValue : (UInt128)actualValue;
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                var actualValue = (long)(object)value;
+                return (actualValue < 0) ? MinValue : (UInt128)actualValue;
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                var actualValue = (nint)(object)value;
+                return (actualValue < 0) ? MinValue : (UInt128)actualValue;
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                var actualValue = (sbyte)(object)value;
+                return (actualValue < 0) ? MinValue : (UInt128)actualValue;
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                return (UInt128)(float)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                return (ushort)(object)value;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                return (uint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                return (ulong)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                return (nuint)(object)value;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                return default;
+            }
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.CreateTruncating{TOther}(TOther)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static UInt128 CreateTruncating<TOther>(TOther value)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                return (byte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                return (char)(object)value;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                var actualValue = (decimal)(object)value;
+                return (actualValue < 0) ? MinValue : (UInt128)actualValue;
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                return (UInt128)(double)(object)value;
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                return (UInt128)(Half)(object)value;
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                return (UInt128)(short)(object)value;
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                return (UInt128)(int)(object)value;
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                return (UInt128)(long)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                return (UInt128)(nint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                return (UInt128)(sbyte)(object)value;
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                return (UInt128)(float)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                return (ushort)(object)value;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                return (uint)(object)value;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                return (ulong)(object)value;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                return (nuint)(object)value;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                return default;
+            }
+        }
+
+        /// <inheritdoc cref="INumber{TSelf}.IsNegative(TSelf)" />
+        static bool INumber<UInt128>.IsNegative(UInt128 value) => false;
+
+        /// <inheritdoc cref="INumber{TSelf}.Max(TSelf, TSelf)" />
+        public static UInt128 Max(UInt128 x, UInt128 y) => (x >= y) ? x : y;
+
+        /// <inheritdoc cref="INumber{TSelf}.MaxMagnitude(TSelf, TSelf)" />
+        static UInt128 INumber<UInt128>.MaxMagnitude(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}.MinMagnitude(TSelf, TSelf)" />
+        static UInt128 INumber<UInt128>.MinMagnitude(UInt128 x, UInt128 y) => Min(x, y);
+
+        /// <inheritdoc cref="INumber{TSelf}.Sign(TSelf)" />
+        public static int Sign(UInt128 value) => (value == 0U) ? 0 : 1;
+
+        /// <inheritdoc cref="INumber{TSelf}.TryCreate{TOther}(TOther, out TSelf)" />
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool TryCreate<TOther>(TOther value, out UInt128 result)
+            where TOther : INumber<TOther>
+        {
+            if (typeof(TOther) == typeof(byte))
+            {
+                result = (byte)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(char))
+            {
+                result = (char)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(decimal))
+            {
+                var actualValue = (decimal)(object)value;
+
+                if (actualValue < 0.0m)
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(double))
+            {
+                var actualValue = (double)(object)value;
+
+                if ((actualValue < 0.0) || (actualValue >= +340282366920938463463374607431768211456.0) || double.IsNaN(actualValue))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(Half))
+            {
+                var actualValue = (Half)(object)value;
+
+                if ((actualValue < Half.Zero) || (actualValue > Half.MaxValue) || Half.IsNaN(actualValue))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(short))
+            {
+                var actualValue = (short)(object)value;
+
+                if (actualValue < 0)
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(int))
+            {
+                var actualValue = (int)(object)value;
+
+                if (actualValue < 0)
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(long))
+            {
+                var actualValue = (long)(object)value;
+
+                if (actualValue < 0)
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(nint))
+            {
+                var actualValue = (nint)(object)value;
+
+                if (actualValue < 0)
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(sbyte))
+            {
+                var actualValue = (sbyte)(object)value;
+
+                if (actualValue < 0)
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(float))
+            {
+                var actualValue = (float)(object)value;
+
+                if ((actualValue < 0.0f) || (actualValue > float.MaxValue) || float.IsNaN(actualValue))
+                {
+                    result = default;
+                    return false;
+                }
+
+                result = (UInt128)actualValue;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(ushort))
+            {
+                result = (ushort)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(uint))
+            {
+                result = (uint)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(ulong))
+            {
+                result = (ulong)(object)value;
+                return true;
+            }
+            else if (typeof(TOther) == typeof(nuint))
+            {
+                result = (nuint)(object)value;
+                return true;
+            }
+            else
+            {
+                ThrowHelper.ThrowNotSupportedException();
+                result = default;
+                return false;
+            }
+        }
+
+        //
+        // INumberBase
+        //
+
+        /// <inheritdoc cref="INumberBase{TSelf}.One" />
+        public static UInt128 One => new UInt128(0, 1);
+
+        /// <inheritdoc cref="INumberBase{TSelf}.Zero" />
+        public static UInt128 Zero => default;
+
+        //
+        // IParsable
+        //
+
+        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, TResult}.op_LeftShift(TSelf, int)" />
+        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, TResult}.op_RightShift(TSelf, int)" />
+        public static UInt128 operator >>(UInt128 value, int shiftAmount) => value >>> shiftAmount;
+
+        /// <inheritdoc cref="IShiftOperators{TSelf, TResult}.op_UnsignedRightShift(TSelf, int)" />
+        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;
+    }
+}
index e33fe56..55d5800 100644 (file)
@@ -90,6 +90,7 @@ namespace System.Numerics
         public static explicit operator byte (System.Numerics.BigInteger value) { throw null; }
         public static explicit operator decimal (System.Numerics.BigInteger value) { throw null; }
         public static explicit operator double (System.Numerics.BigInteger value) { throw null; }
+        public static explicit operator System.Int128(System.Numerics.BigInteger value) { throw null; }
         public static explicit operator short (System.Numerics.BigInteger value) { throw null; }
         public static explicit operator int (System.Numerics.BigInteger value) { throw null; }
         public static explicit operator long (System.Numerics.BigInteger value) { throw null; }
@@ -98,6 +99,8 @@ namespace System.Numerics
         public static explicit operator sbyte (System.Numerics.BigInteger value) { throw null; }
         public static explicit operator float (System.Numerics.BigInteger value) { throw null; }
         [System.CLSCompliantAttribute(false)]
+        public static explicit operator System.UInt128(System.Numerics.BigInteger value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
         public static explicit operator ushort (System.Numerics.BigInteger value) { throw null; }
         [System.CLSCompliantAttribute(false)]
         public static explicit operator uint (System.Numerics.BigInteger value) { throw null; }
@@ -121,6 +124,7 @@ namespace System.Numerics
         [System.CLSCompliantAttribute(false)]
         public static bool operator >=(ulong left, System.Numerics.BigInteger right) { throw null; }
         public static implicit operator System.Numerics.BigInteger (byte value) { throw null; }
+        public static implicit operator System.Numerics.BigInteger (System.Int128 value) { throw null; }
         public static implicit operator System.Numerics.BigInteger (short value) { throw null; }
         public static implicit operator System.Numerics.BigInteger (int value) { throw null; }
         public static implicit operator System.Numerics.BigInteger (long value) { throw null; }
@@ -128,6 +132,8 @@ namespace System.Numerics
         [System.CLSCompliantAttribute(false)]
         public static implicit operator System.Numerics.BigInteger (sbyte value) { throw null; }
         [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.Numerics.BigInteger (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
         public static implicit operator System.Numerics.BigInteger (ushort value) { throw null; }
         [System.CLSCompliantAttribute(false)]
         public static implicit operator System.Numerics.BigInteger (uint value) { throw null; }
index 8944075..38e6988 100644 (file)
   <data name="Overflow_Int64" xml:space="preserve">
     <value>Value was either too large or too small for an Int64.</value>
   </data>
+  <data name="Overflow_Int128" xml:space="preserve">
+    <value>Value was either too large or too small for an Int128.</value>
+  </data>
   <data name="Overflow_UInt32" xml:space="preserve">
     <value>Value was either too large or too small for a UInt32.</value>
   </data>
   <data name="Overflow_UInt64" xml:space="preserve">
     <value>Value was either too large or too small for a UInt64.</value>
   </data>
+  <data name="Overflow_UInt128" xml:space="preserve">
+    <value>Value was either too large or too small for a UInt128.</value>
+  </data>
   <data name="Overflow_Decimal" xml:space="preserve">
     <value>Value was either too large or too small for a Decimal.</value>
   </data>
index 0dc15c9..e541085 100644 (file)
@@ -1114,7 +1114,7 @@ namespace System.Numerics
             if (cu == 1)
                 return _bits[0] == uu;
 
-            return NumericsHelpers.MakeUlong(_bits[1], _bits[0]) == uu;
+            return NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) == uu;
         }
 
         [CLSCompliant(false)]
@@ -1132,7 +1132,7 @@ namespace System.Numerics
                 return false;
             if (cu == 1)
                 return _bits[0] == other;
-            return NumericsHelpers.MakeUlong(_bits[1], _bits[0]) == other;
+            return NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) == other;
         }
 
         public bool Equals(BigInteger other)
@@ -1165,7 +1165,7 @@ namespace System.Numerics
             if ((_sign ^ other) < 0 || (cu = _bits.Length) > 2)
                 return _sign;
             ulong uu = other < 0 ? (ulong)-other : (ulong)other;
-            ulong uuTmp = cu == 2 ? NumericsHelpers.MakeUlong(_bits[1], _bits[0]) : _bits[0];
+            ulong uuTmp = cu == 2 ? NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) : _bits[0];
             return _sign * uuTmp.CompareTo(uu);
         }
 
@@ -1181,7 +1181,7 @@ namespace System.Numerics
             int cu = _bits.Length;
             if (cu > 2)
                 return +1;
-            ulong uuTmp = cu == 2 ? NumericsHelpers.MakeUlong(_bits[1], _bits[0]) : _bits[0];
+            ulong uuTmp = cu == 2 ? NumericsHelpers.MakeUInt64(_bits[1], _bits[0]) : _bits[0];
             return uuTmp.CompareTo(other);
         }
 
@@ -1811,6 +1811,106 @@ namespace System.Numerics
             }
         }
 
+        public static implicit operator BigInteger(Int128 value)
+        {
+            int sign;
+            uint[]? bits;
+
+            if ((int.MinValue < value) && (value <= int.MaxValue))
+            {
+                sign = (int)value;
+                bits = null;
+            }
+            else if (value == int.MinValue)
+            {
+                return s_bnMinInt;
+            }
+            else
+            {
+                UInt128 x;
+                if (value < 0)
+                {
+                    x = unchecked((UInt128)(-value));
+                    sign = -1;
+                }
+                else
+                {
+                    x = (UInt128)value;
+                    sign = +1;
+                }
+
+                if (x <= uint.MaxValue)
+                {
+                    bits = new uint[1];
+                    bits[0] = (uint)(x >> (kcbitUint * 0));
+                }
+                else if (x <= ulong.MaxValue)
+                {
+                    bits = new uint[2];
+                    bits[0] = (uint)(x >> (kcbitUint * 0));
+                    bits[1] = (uint)(x >> (kcbitUint * 1));
+                }
+                else if (x <= new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))
+                {
+                    bits = new uint[3];
+                    bits[0] = (uint)(x >> (kcbitUint * 0));
+                    bits[1] = (uint)(x >> (kcbitUint * 1));
+                    bits[2] = (uint)(x >> (kcbitUint * 2));
+                }
+                else
+                {
+                    bits = new uint[4];
+                    bits[0] = (uint)(x >> (kcbitUint * 0));
+                    bits[1] = (uint)(x >> (kcbitUint * 1));
+                    bits[2] = (uint)(x >> (kcbitUint * 2));
+                    bits[3] = (uint)(x >> (kcbitUint * 3));
+                }
+            }
+
+            return new BigInteger(sign, bits);
+        }
+
+        [CLSCompliant(false)]
+        public static implicit operator BigInteger(UInt128 value)
+        {
+            int sign = +1;
+            uint[]? bits;
+
+            if (value <= (uint)int.MaxValue)
+            {
+                sign = (int)value;
+                bits = null;
+            }
+            else if (value <= uint.MaxValue)
+            {
+                bits = new uint[1];
+                bits[0] = (uint)(value >> (kcbitUint * 0));
+            }
+            else if (value <= ulong.MaxValue)
+            {
+                bits = new uint[2];
+                bits[0] = (uint)(value >> (kcbitUint * 0));
+                bits[1] = (uint)(value >> (kcbitUint * 1));
+            }
+            else if (value <= new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))
+            {
+                bits = new uint[3];
+                bits[0] = (uint)(value >> (kcbitUint * 0));
+                bits[1] = (uint)(value >> (kcbitUint * 1));
+                bits[2] = (uint)(value >> (kcbitUint * 2));
+            }
+            else
+            {
+                bits = new uint[4];
+                bits[0] = (uint)(value >> (kcbitUint * 0));
+                bits[1] = (uint)(value >> (kcbitUint * 1));
+                bits[2] = (uint)(value >> (kcbitUint * 2));
+                bits[3] = (uint)(value >> (kcbitUint * 3));
+            }
+
+            return new BigInteger(sign, bits);
+        }
+
         public static explicit operator BigInteger(float value)
         {
             return new BigInteger(value);
@@ -1907,7 +2007,7 @@ namespace System.Numerics
             ulong uu;
             if (len > 1)
             {
-                uu = NumericsHelpers.MakeUlong(value._bits[1], value._bits[0]);
+                uu = NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
             }
             else
             {
@@ -1940,7 +2040,82 @@ namespace System.Numerics
 
             if (len > 1)
             {
-                return NumericsHelpers.MakeUlong(value._bits[1], value._bits[0]);
+                return NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
+            }
+            return value._bits[0];
+        }
+
+        public static explicit operator Int128(BigInteger value)
+        {
+            value.AssertValid();
+
+            if (value._bits is null)
+            {
+                return value._sign;
+            }
+
+            int len = value._bits.Length;
+
+            if (len > 4)
+            {
+                throw new OverflowException(SR.Overflow_Int128);
+            }
+
+            UInt128 uu;
+
+            if (len > 2)
+            {
+                uu = new UInt128(
+                    NumericsHelpers.MakeUInt64((len > 3) ? value._bits[3] : 0, value._bits[2]),
+                    NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0])
+                );
+            }
+            else if (len > 1)
+            {
+                uu = NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
+            }
+            else
+            {
+                uu = value._bits[0];
+            }
+
+            Int128 ll = (value._sign > 0) ? unchecked((Int128)uu) : unchecked(-(Int128)uu);
+
+            if (((ll > 0) && (value._sign > 0)) || ((ll < 0) && (value._sign < 0)))
+            {
+                // Signs match, no overflow
+                return ll;
+            }
+            throw new OverflowException(SR.Overflow_Int128);
+        }
+
+        [CLSCompliant(false)]
+        public static explicit operator UInt128(BigInteger value)
+        {
+            value.AssertValid();
+
+            if (value._bits is null)
+            {
+                return checked((UInt128)value._sign);
+            }
+
+            int len = value._bits.Length;
+
+            if ((len > 4) || (value._sign < 0))
+            {
+                throw new OverflowException(SR.Overflow_UInt128);
+            }
+
+            if (len > 2)
+            {
+                return new UInt128(
+                    NumericsHelpers.MakeUInt64((len > 3) ? value._bits[3] : 0, value._bits[2]),
+                    NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0])
+                );
+            }
+            else if (len > 1)
+            {
+                return NumericsHelpers.MakeUInt64(value._bits[1], value._bits[0]);
             }
             return value._bits[0];
         }
index 48fdce9..51643f6 100644 (file)
@@ -1041,7 +1041,7 @@ namespace System.Numerics
                 for (int iuDst = 0; iuDst < cuDst; iuDst++)
                 {
                     Debug.Assert(rguDst[iuDst] < kuBase);
-                    ulong uuRes = NumericsHelpers.MakeUlong(rguDst[iuDst], uCarry);
+                    ulong uuRes = NumericsHelpers.MakeUInt64(rguDst[iuDst], uCarry);
                     rguDst[iuDst] = (uint)(uuRes % kuBase);
                     uCarry = (uint)(uuRes / kuBase);
                 }
index ff9d263..ebee3f5 100644 (file)
@@ -127,7 +127,7 @@ namespace System.Numerics
             }
         }
 
-        public static ulong MakeUlong(uint uHi, uint uLo)
+        public static ulong MakeUInt64(uint uHi, uint uLo)
         {
             return ((ulong)uHi << kcbitUint) | uLo;
         }
index c21459e..8afd049 100644 (file)
@@ -2644,9 +2644,12 @@ namespace System
         public static System.Half MultiplicativeIdentity { get { throw null; } }
         public static System.Half NaN { get { throw null; } }
         public static System.Half NegativeInfinity { get { throw null; } }
+        public static System.Half NegativeOne { get { throw null; } }
         public static System.Half NegativeZero { get { throw null; } }
+        public static System.Half One { get { throw null; } }
         public static System.Half Pi { get { throw null; } }
         public static System.Half PositiveInfinity { get { throw null; } }
+        public static System.Half Zero { get { throw null; } }
         static System.Half System.Numerics.IAdditiveIdentity<System.Half,System.Half>.AdditiveIdentity { get { throw null; } }
         static System.Half System.Numerics.INumberBase<System.Half>.One { get { throw null; } }
         static System.Half System.Numerics.INumberBase<System.Half>.Zero { get { throw null; } }
@@ -2906,6 +2909,157 @@ namespace System
         public InsufficientMemoryException(string? message) { }
         public InsufficientMemoryException(string? message, System.Exception? innerException) { }
     }
+    public readonly partial struct Int128 : System.IComparable, System.IComparable<System.Int128>, System.IEquatable<System.Int128>, System.IFormattable, System.IParsable<System.Int128>, System.ISpanFormattable, System.ISpanParsable<System.Int128>, System.Numerics.IAdditionOperators<System.Int128, System.Int128, System.Int128>, System.Numerics.IAdditiveIdentity<System.Int128, System.Int128>, System.Numerics.IBinaryInteger<System.Int128>, System.Numerics.IBinaryNumber<System.Int128>, System.Numerics.IBitwiseOperators<System.Int128, System.Int128, System.Int128>, System.Numerics.IComparisonOperators<System.Int128, System.Int128>, System.Numerics.IDecrementOperators<System.Int128>, System.Numerics.IDivisionOperators<System.Int128, System.Int128, System.Int128>, System.Numerics.IEqualityOperators<System.Int128, System.Int128>, System.Numerics.IIncrementOperators<System.Int128>, System.Numerics.IMinMaxValue<System.Int128>, System.Numerics.IModulusOperators<System.Int128, System.Int128, System.Int128>, System.Numerics.IMultiplicativeIdentity<System.Int128, System.Int128>, System.Numerics.IMultiplyOperators<System.Int128, System.Int128, System.Int128>, System.Numerics.INumber<System.Int128>, System.Numerics.INumberBase<System.Int128>, System.Numerics.IShiftOperators<System.Int128, System.Int128>, System.Numerics.ISignedNumber<System.Int128>, System.Numerics.ISubtractionOperators<System.Int128, System.Int128, System.Int128>, System.Numerics.IUnaryNegationOperators<System.Int128, System.Int128>, System.Numerics.IUnaryPlusOperators<System.Int128, System.Int128>
+    {
+        private readonly int _dummyPrimitive;
+        [System.CLSCompliantAttribute(false)]
+        public Int128(ulong upper, ulong lower) { throw null; }
+        public static System.Int128 MaxValue { get { throw null; } }
+        public static System.Int128 MinValue { get { throw null; } }
+        public static System.Int128 NegativeOne { get { throw null; } }
+        public static System.Int128 One { get { throw null; } }
+        static System.Int128 System.Numerics.IAdditiveIdentity<System.Int128,System.Int128>.AdditiveIdentity { get { throw null; } }
+        static System.Int128 System.Numerics.IMultiplicativeIdentity<System.Int128,System.Int128>.MultiplicativeIdentity { get { throw null; } }
+        public static System.Int128 Zero { get { throw null; } }
+        public static System.Int128 Abs(System.Int128 value) { throw null; }
+        public static System.Int128 Clamp(System.Int128 value, System.Int128 min, System.Int128 max) { throw null; }
+        public int CompareTo(System.Int128 value) { throw null; }
+        public int CompareTo(object? value) { throw null; }
+        public static System.Int128 CopySign(System.Int128 value, System.Int128 sign) { throw null; }
+        public static System.Int128 CreateChecked<TOther>(TOther value) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public static System.Int128 CreateSaturating<TOther>(TOther value) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public static System.Int128 CreateTruncating<TOther>(TOther value) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public static (System.Int128 Quotient, System.Int128 Remainder) DivRem(System.Int128 left, System.Int128 right) { throw null; }
+        public bool Equals(System.Int128 other) { throw null; }
+        public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; }
+        public override int GetHashCode() { throw null; }
+        public static bool IsNegative(System.Int128 value) { throw null; }
+        public static bool IsPow2(System.Int128 value) { throw null; }
+        public static System.Int128 LeadingZeroCount(System.Int128 value) { throw null; }
+        public static System.Int128 Log2(System.Int128 value) { throw null; }
+        public static System.Int128 Max(System.Int128 x, System.Int128 y) { throw null; }
+        public static System.Int128 MaxMagnitude(System.Int128 x, System.Int128 y) { throw null; }
+        public static System.Int128 Min(System.Int128 x, System.Int128 y) { throw null; }
+        public static System.Int128 MinMagnitude(System.Int128 x, System.Int128 y) { throw null; }
+        public static System.Int128 operator +(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator &(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator |(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator checked +(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator checked --(System.Int128 value) { throw null; }
+        public static System.Int128 operator checked /(System.Int128 left, System.Int128 right) { throw null; }
+        public static explicit operator checked System.Int128 (double value) { throw null; }
+        public static explicit operator checked System.Int128 (System.Half value) { throw null; }
+        public static explicit operator checked System.Int128 (float value) { throw null; }
+        public static explicit operator checked byte (System.Int128 value) { throw null; }
+        public static explicit operator checked char (System.Int128 value) { throw null; }
+        public static explicit operator checked short (System.Int128 value) { throw null; }
+        public static explicit operator checked int (System.Int128 value) { throw null; }
+        public static explicit operator checked long (System.Int128 value) { throw null; }
+        public static explicit operator checked nint (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked sbyte (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked ushort (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked uint (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked ulong (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked System.UInt128 (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked nuint (System.Int128 value) { throw null; }
+        public static System.Int128 operator checked ++(System.Int128 value) { throw null; }
+        public static System.Int128 operator checked *(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator checked -(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator checked -(System.Int128 value) { throw null; }
+        public static System.Int128 operator --(System.Int128 value) { throw null; }
+        public static System.Int128 operator /(System.Int128 left, System.Int128 right) { throw null; }
+        public static bool operator ==(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator ^(System.Int128 left, System.Int128 right) { throw null; }
+        public static explicit operator System.Int128 (decimal value) { throw null; }
+        public static explicit operator System.Int128 (double value) { throw null; }
+        public static explicit operator System.Int128 (System.Half value) { throw null; }
+        public static explicit operator byte (System.Int128 value) { throw null; }
+        public static explicit operator char (System.Int128 value) { throw null; }
+        public static explicit operator decimal (System.Int128 value) { throw null; }
+        public static explicit operator double (System.Int128 value) { throw null; }
+        public static explicit operator System.Half (System.Int128 value) { throw null; }
+        public static explicit operator short (System.Int128 value) { throw null; }
+        public static explicit operator int (System.Int128 value) { throw null; }
+        public static explicit operator long (System.Int128 value) { throw null; }
+        public static explicit operator nint (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator sbyte (System.Int128 value) { throw null; }
+        public static explicit operator float (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator System.UInt128 (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator ushort (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator uint (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator ulong (System.Int128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator nuint (System.Int128 value) { throw null; }
+        public static explicit operator System.Int128 (float value) { throw null; }
+        public static bool operator >(System.Int128 left, System.Int128 right) { throw null; }
+        public static bool operator >=(System.Int128 left, System.Int128 right) { throw null; }
+        public static implicit operator System.Int128 (byte value) { throw null; }
+        public static implicit operator System.Int128 (char value) { throw null; }
+        public static implicit operator System.Int128 (short value) { throw null; }
+        public static implicit operator System.Int128 (int value) { throw null; }
+        public static implicit operator System.Int128 (long value) { throw null; }
+        public static implicit operator System.Int128 (nint value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.Int128 (sbyte value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.Int128 (ushort value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.Int128 (uint value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.Int128 (ulong value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.Int128 (nuint value) { throw null; }
+        public static System.Int128 operator ++(System.Int128 value) { throw null; }
+        public static bool operator !=(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator <<(System.Int128 value, int shiftAmount) { throw null; }
+        public static bool operator <(System.Int128 left, System.Int128 right) { throw null; }
+        public static bool operator <=(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator %(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator *(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator ~(System.Int128 value) { throw null; }
+        public static System.Int128 operator >>(System.Int128 value, int shiftAmount) { throw null; }
+        public static System.Int128 operator -(System.Int128 left, System.Int128 right) { throw null; }
+        public static System.Int128 operator -(System.Int128 value) { throw null; }
+        public static System.Int128 operator +(System.Int128 value) { throw null; }
+        public static System.Int128 operator >>>(System.Int128 value, int shiftAmount) { throw null; }
+        public static System.Int128 Parse(System.ReadOnlySpan<char> s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider? provider = null) { throw null; }
+        public static System.Int128 Parse(System.ReadOnlySpan<char> s, System.IFormatProvider? provider) { throw null; }
+        public static System.Int128 Parse(string s) { throw null; }
+        public static System.Int128 Parse(string s, System.Globalization.NumberStyles style) { throw null; }
+        public static System.Int128 Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; }
+        public static System.Int128 Parse(string s, System.IFormatProvider? provider) { throw null; }
+        public static System.Int128 PopCount(System.Int128 value) { throw null; }
+        public static System.Int128 RotateLeft(System.Int128 value, int rotateAmount) { throw null; }
+        public static System.Int128 RotateRight(System.Int128 value, int rotateAmount) { throw null; }
+        public static int Sign(System.Int128 value) { throw null; }
+        int System.Numerics.IBinaryInteger<System.Int128>.GetByteCount() { throw null; }
+        long System.Numerics.IBinaryInteger<System.Int128>.GetShortestBitLength() { throw null; }
+        bool System.Numerics.IBinaryInteger<System.Int128>.TryWriteLittleEndian(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        public override string ToString() { throw null; }
+        public string ToString(System.IFormatProvider? provider) { throw null; }
+        public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format) { throw null; }
+        public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; }
+        public static System.Int128 TrailingZeroCount(System.Int128 value) { throw null; }
+        public static bool TryCreate<TOther>(TOther value, out System.Int128 result) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public bool TryFormat(System.Span<char> destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan<char> format = default(System.ReadOnlySpan<char>), System.IFormatProvider? provider = null) { throw null; }
+        public static bool TryParse(System.ReadOnlySpan<char> s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result) { throw null; }
+        public static bool TryParse(System.ReadOnlySpan<char> s, System.IFormatProvider? provider, out System.Int128 result) { throw null; }
+        public static bool TryParse(System.ReadOnlySpan<char> s, out System.Int128 result) { throw null; }
+        public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result) { throw null; }
+        public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Int128 result) { throw null; }
+        public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Int128 result) { throw null; }
+    }
     public readonly partial struct Int16 : System.IComparable, System.IComparable<short>, System.IConvertible, System.IEquatable<short>, System.IFormattable, System.IParsable<short>, System.ISpanFormattable, System.ISpanParsable<short>, System.Numerics.IAdditionOperators<short, short, short>, System.Numerics.IAdditiveIdentity<short, short>, System.Numerics.IBinaryInteger<short>, System.Numerics.IBinaryNumber<short>, System.Numerics.IBitwiseOperators<short, short, short>, System.Numerics.IComparisonOperators<short, short>, System.Numerics.IDecrementOperators<short>, System.Numerics.IDivisionOperators<short, short, short>, System.Numerics.IEqualityOperators<short, short>, System.Numerics.IIncrementOperators<short>, System.Numerics.IMinMaxValue<short>, System.Numerics.IModulusOperators<short, short, short>, System.Numerics.IMultiplicativeIdentity<short, short>, System.Numerics.IMultiplyOperators<short, short, short>, System.Numerics.INumber<short>, System.Numerics.INumberBase<short>, System.Numerics.IShiftOperators<short, short>, System.Numerics.ISignedNumber<short>, System.Numerics.ISubtractionOperators<short, short, short>, System.Numerics.IUnaryNegationOperators<short, short>, System.Numerics.IUnaryPlusOperators<short, short>
     {
         private readonly short _dummyPrimitive;
@@ -5580,6 +5734,163 @@ namespace System
         public TypeUnloadedException(string? message, System.Exception? innerException) { }
     }
     [System.CLSCompliantAttribute(false)]
+    public readonly partial struct UInt128 : System.IComparable, System.IComparable<System.UInt128>, System.IEquatable<System.UInt128>, System.IFormattable, System.IParsable<System.UInt128>, System.ISpanFormattable, System.ISpanParsable<System.UInt128>, System.Numerics.IAdditionOperators<System.UInt128, System.UInt128, System.UInt128>, System.Numerics.IAdditiveIdentity<System.UInt128, System.UInt128>, System.Numerics.IBinaryInteger<System.UInt128>, System.Numerics.IBinaryNumber<System.UInt128>, System.Numerics.IBitwiseOperators<System.UInt128, System.UInt128, System.UInt128>, System.Numerics.IComparisonOperators<System.UInt128, System.UInt128>, System.Numerics.IDecrementOperators<System.UInt128>, System.Numerics.IDivisionOperators<System.UInt128, System.UInt128, System.UInt128>, System.Numerics.IEqualityOperators<System.UInt128, System.UInt128>, System.Numerics.IIncrementOperators<System.UInt128>, System.Numerics.IMinMaxValue<System.UInt128>, System.Numerics.IModulusOperators<System.UInt128, System.UInt128, System.UInt128>, System.Numerics.IMultiplicativeIdentity<System.UInt128, System.UInt128>, System.Numerics.IMultiplyOperators<System.UInt128, System.UInt128, System.UInt128>, System.Numerics.INumber<System.UInt128>, System.Numerics.INumberBase<System.UInt128>, System.Numerics.IShiftOperators<System.UInt128, System.UInt128>, System.Numerics.ISubtractionOperators<System.UInt128, System.UInt128, System.UInt128>, System.Numerics.IUnaryNegationOperators<System.UInt128, System.UInt128>, System.Numerics.IUnaryPlusOperators<System.UInt128, System.UInt128>, System.Numerics.IUnsignedNumber<System.UInt128>
+    {
+        private readonly int _dummyPrimitive;
+        [System.CLSCompliantAttribute(false)]
+        public UInt128(ulong upper, ulong lower) { throw null; }
+        public static System.UInt128 MaxValue { get { throw null; } }
+        public static System.UInt128 MinValue { get { throw null; } }
+        public static System.UInt128 One { get { throw null; } }
+        static System.UInt128 System.Numerics.IAdditiveIdentity<System.UInt128,System.UInt128>.AdditiveIdentity { get { throw null; } }
+        static System.UInt128 System.Numerics.IMultiplicativeIdentity<System.UInt128,System.UInt128>.MultiplicativeIdentity { get { throw null; } }
+        public static System.UInt128 Zero { get { throw null; } }
+        public static System.UInt128 Clamp(System.UInt128 value, System.UInt128 min, System.UInt128 max) { throw null; }
+        public int CompareTo(object? value) { throw null; }
+        public int CompareTo(System.UInt128 value) { throw null; }
+        public static System.UInt128 CreateChecked<TOther>(TOther value) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public static System.UInt128 CreateSaturating<TOther>(TOther value) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public static System.UInt128 CreateTruncating<TOther>(TOther value) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public static (System.UInt128 Quotient, System.UInt128 Remainder) DivRem(System.UInt128 left, System.UInt128 right) { throw null; }
+        public override bool Equals([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] object? obj) { throw null; }
+        public bool Equals(System.UInt128 other) { throw null; }
+        public override int GetHashCode() { throw null; }
+        public static bool IsPow2(System.UInt128 value) { throw null; }
+        public static System.UInt128 LeadingZeroCount(System.UInt128 value) { throw null; }
+        public static System.UInt128 Log2(System.UInt128 value) { throw null; }
+        public static System.UInt128 Max(System.UInt128 x, System.UInt128 y) { throw null; }
+        public static System.UInt128 Min(System.UInt128 x, System.UInt128 y) { throw null; }
+        public static System.UInt128 operator +(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator &(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator |(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator checked +(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator checked --(System.UInt128 value) { throw null; }
+        public static System.UInt128 operator checked /(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static explicit operator checked System.UInt128 (double value) { throw null; }
+        public static explicit operator checked System.UInt128 (System.Half value) { throw null; }
+        public static explicit operator checked System.UInt128 (short value) { throw null; }
+        public static explicit operator checked System.UInt128 (int value) { throw null; }
+        public static explicit operator checked System.UInt128 (long value) { throw null; }
+        public static explicit operator checked System.UInt128 (nint value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked System.UInt128 (sbyte value) { throw null; }
+        public static explicit operator checked System.UInt128 (float value) { throw null; }
+        public static explicit operator checked byte (System.UInt128 value) { throw null; }
+        public static explicit operator checked char (System.UInt128 value) { throw null; }
+        public static explicit operator checked short (System.UInt128 value) { throw null; }
+        public static explicit operator checked int (System.UInt128 value) { throw null; }
+        public static explicit operator checked long (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked System.Int128 (System.UInt128 value) { throw null; }
+        public static explicit operator checked nint (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked sbyte (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked ushort (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked uint (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked ulong (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator checked nuint (System.UInt128 value) { throw null; }
+        public static System.UInt128 operator checked ++(System.UInt128 value) { throw null; }
+        public static System.UInt128 operator checked *(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator checked -(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator checked -(System.UInt128 value) { throw null; }
+        public static System.UInt128 operator --(System.UInt128 value) { throw null; }
+        public static System.UInt128 operator /(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static bool operator ==(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator ^(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static explicit operator System.UInt128 (decimal value) { throw null; }
+        public static explicit operator System.UInt128 (double value) { throw null; }
+        public static explicit operator System.UInt128 (System.Half value) { throw null; }
+        public static explicit operator System.UInt128 (short value) { throw null; }
+        public static explicit operator System.UInt128 (int value) { throw null; }
+        public static explicit operator System.UInt128 (long value) { throw null; }
+        public static explicit operator System.UInt128 (nint value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator System.UInt128 (sbyte value) { throw null; }
+        public static explicit operator System.UInt128 (float value) { throw null; }
+        public static explicit operator byte (System.UInt128 value) { throw null; }
+        public static explicit operator char (System.UInt128 value) { throw null; }
+        public static explicit operator decimal (System.UInt128 value) { throw null; }
+        public static explicit operator double (System.UInt128 value) { throw null; }
+        public static explicit operator System.Half (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator System.Int128 (System.UInt128 value) { throw null; }
+        public static explicit operator short (System.UInt128 value) { throw null; }
+        public static explicit operator int (System.UInt128 value) { throw null; }
+        public static explicit operator long (System.UInt128 value) { throw null; }
+        public static explicit operator nint (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator sbyte (System.UInt128 value) { throw null; }
+        public static explicit operator float (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator ushort (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator uint (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator ulong (System.UInt128 value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static explicit operator nuint (System.UInt128 value) { throw null; }
+        public static bool operator >(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static bool operator >=(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static implicit operator System.UInt128 (byte value) { throw null; }
+        public static implicit operator System.UInt128 (char value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.UInt128 (ushort value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.UInt128 (uint value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.UInt128 (ulong value) { throw null; }
+        [System.CLSCompliantAttribute(false)]
+        public static implicit operator System.UInt128 (nuint value) { throw null; }
+        public static System.UInt128 operator ++(System.UInt128 value) { throw null; }
+        public static bool operator !=(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator <<(System.UInt128 value, int shiftAmount) { throw null; }
+        public static bool operator <(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static bool operator <=(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator %(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator *(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator ~(System.UInt128 value) { throw null; }
+        public static System.UInt128 operator >>(System.UInt128 value, int shiftAmount) { throw null; }
+        public static System.UInt128 operator -(System.UInt128 left, System.UInt128 right) { throw null; }
+        public static System.UInt128 operator -(System.UInt128 value) { throw null; }
+        public static System.UInt128 operator +(System.UInt128 value) { throw null; }
+        public static System.UInt128 operator >>>(System.UInt128 value, int shiftAmount) { throw null; }
+        public static System.UInt128 Parse(System.ReadOnlySpan<char> s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider? provider = null) { throw null; }
+        public static System.UInt128 Parse(System.ReadOnlySpan<char> s, System.IFormatProvider? provider) { throw null; }
+        public static System.UInt128 Parse(string s) { throw null; }
+        public static System.UInt128 Parse(string s, System.Globalization.NumberStyles style) { throw null; }
+        public static System.UInt128 Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; }
+        public static System.UInt128 Parse(string s, System.IFormatProvider? provider) { throw null; }
+        public static System.UInt128 PopCount(System.UInt128 value) { throw null; }
+        public static System.UInt128 RotateLeft(System.UInt128 value, int rotateAmount) { throw null; }
+        public static System.UInt128 RotateRight(System.UInt128 value, int rotateAmount) { throw null; }
+        public static int Sign(System.UInt128 value) { throw null; }
+        int System.Numerics.IBinaryInteger<System.UInt128>.GetByteCount() { throw null; }
+        long System.Numerics.IBinaryInteger<System.UInt128>.GetShortestBitLength() { throw null; }
+        bool System.Numerics.IBinaryInteger<System.UInt128>.TryWriteLittleEndian(System.Span<byte> destination, out int bytesWritten) { throw null; }
+        static System.UInt128 System.Numerics.INumber<System.UInt128>.Abs(System.UInt128 value) { throw null; }
+        static System.UInt128 System.Numerics.INumber<System.UInt128>.CopySign(System.UInt128 value, System.UInt128 sign) { throw null; }
+        static bool System.Numerics.INumber<System.UInt128>.IsNegative(System.UInt128 value) { throw null; }
+        static System.UInt128 System.Numerics.INumber<System.UInt128>.MaxMagnitude(System.UInt128 x, System.UInt128 y) { throw null; }
+        static System.UInt128 System.Numerics.INumber<System.UInt128>.MinMagnitude(System.UInt128 x, System.UInt128 y) { throw null; }
+        public override string ToString() { throw null; }
+        public string ToString(System.IFormatProvider? provider) { throw null; }
+        public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format) { throw null; }
+        public string ToString([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] string? format, System.IFormatProvider? provider) { throw null; }
+        public static System.UInt128 TrailingZeroCount(System.UInt128 value) { throw null; }
+        public static bool TryCreate<TOther>(TOther value, out System.UInt128 result) where TOther : System.Numerics.INumber<TOther> { throw null; }
+        public bool TryFormat(System.Span<char> destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan<char> format = default(System.ReadOnlySpan<char>), System.IFormatProvider? provider = null) { throw null; }
+        public static bool TryParse(System.ReadOnlySpan<char> s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result) { throw null; }
+        public static bool TryParse(System.ReadOnlySpan<char> s, System.IFormatProvider? provider, out System.UInt128 result) { throw null; }
+        public static bool TryParse(System.ReadOnlySpan<char> s, out System.UInt128 result) { throw null; }
+        public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result) { throw null; }
+        public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.UInt128 result) { throw null; }
+        public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UInt128 result) { throw null; }
+    }
+    [System.CLSCompliantAttribute(false)]
     public readonly partial struct UInt16 : System.IComparable, System.IComparable<ushort>, System.IConvertible, System.IEquatable<ushort>, System.IFormattable, System.IParsable<ushort>, System.ISpanFormattable, System.ISpanParsable<ushort>, System.Numerics.IAdditionOperators<ushort, ushort, ushort>, System.Numerics.IAdditiveIdentity<ushort, ushort>, System.Numerics.IBinaryInteger<ushort>, System.Numerics.IBinaryNumber<ushort>, System.Numerics.IBitwiseOperators<ushort, ushort, ushort>, System.Numerics.IComparisonOperators<ushort, ushort>, System.Numerics.IDecrementOperators<ushort>, System.Numerics.IDivisionOperators<ushort, ushort, ushort>, System.Numerics.IEqualityOperators<ushort, ushort>, System.Numerics.IIncrementOperators<ushort>, System.Numerics.IMinMaxValue<ushort>, System.Numerics.IModulusOperators<ushort, ushort, ushort>, System.Numerics.IMultiplicativeIdentity<ushort, ushort>, System.Numerics.IMultiplyOperators<ushort, ushort, ushort>, System.Numerics.INumber<ushort>, System.Numerics.INumberBase<ushort>, System.Numerics.IShiftOperators<ushort, ushort>, System.Numerics.ISubtractionOperators<ushort, ushort, ushort>, System.Numerics.IUnaryNegationOperators<ushort, ushort>, System.Numerics.IUnaryPlusOperators<ushort, ushort>, System.Numerics.IUnsignedNumber<ushort>
     {
         private readonly ushort _dummyPrimitive;
index 8959692..035e305 100644 (file)
@@ -85,6 +85,7 @@
     <Compile Include="System\Int16Tests.cs" />
     <Compile Include="System\Int32Tests.cs" />
     <Compile Include="System\Int64Tests.cs" />
+    <Compile Include="System\Int128Tests.cs" />
     <Compile Include="System\IntPtrTests.cs" />
     <Compile Include="System\InvalidCastExceptionTests.cs" />
     <Compile Include="System\InvalidOperationExceptionTests.cs" />
     <Compile Include="System\UInt16Tests.cs" />
     <Compile Include="System\UInt32Tests.cs" />
     <Compile Include="System\UInt64Tests.cs" />
+    <Compile Include="System\UInt128Tests.cs" />
     <Compile Include="System\UIntPtrTests.cs" />
     <Compile Include="System\UnitySerializationHolderTests.cs" />
     <Compile Include="System\Uri.CreateStringTests.cs" />
     <Compile Include="System\Int16Tests.GenericMath.cs" />
     <Compile Include="System\Int32Tests.GenericMath.cs" />
     <Compile Include="System\Int64Tests.GenericMath.cs" />
+    <Compile Include="System\Int128Tests.GenericMath.cs" />
     <Compile Include="System\IntPtrTests.GenericMath.cs" />
     <Compile Include="System\SByteTests.GenericMath.cs" />
     <Compile Include="System\SingleTests.GenericMath.cs" />
     <Compile Include="System\UInt16Tests.GenericMath.cs" />
     <Compile Include="System\UInt32Tests.GenericMath.cs" />
     <Compile Include="System\UInt64Tests.GenericMath.cs" />
+    <Compile Include="System\UInt128Tests.GenericMath.cs" />
     <Compile Include="System\UIntPtrTests.GenericMath.cs" />
   </ItemGroup>
 
index 8bc5a58..77a6ae8 100644 (file)
@@ -318,6 +318,17 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromInt128Test()
+        {
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            Assert.Equal(1.0m, NumberHelper<decimal>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            Assert.Equal(-1.0m, NumberHelper<decimal>.CreateChecked<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<decimal>.CreateChecked<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            Assert.Throws<OverflowException>(() => NumberHelper<decimal>.CreateChecked<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -379,6 +390,17 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromUInt128Test()
+        {
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            Assert.Equal(1.0m, NumberHelper<decimal>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<decimal>.CreateChecked<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            Assert.Throws<OverflowException>(() => NumberHelper<decimal>.CreateChecked<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            Assert.Throws<OverflowException>(() => NumberHelper<decimal>.CreateChecked<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -450,6 +472,18 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromInt128Test()
+        {
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+
+            Assert.Equal(+1.0m, NumberHelper<decimal>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            Assert.Equal(-1.0m, NumberHelper<decimal>.CreateSaturating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+
+            Assert.Equal(decimal.MaxValue, NumberHelper<decimal>.CreateSaturating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            Assert.Equal(decimal.MinValue, NumberHelper<decimal>.CreateSaturating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -511,6 +545,18 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromUInt128Test()
+        {
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+
+            Assert.Equal(+1.0m, NumberHelper<decimal>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            Assert.Equal(decimal.MaxValue, NumberHelper<decimal>.CreateSaturating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+
+            Assert.Equal(decimal.MaxValue, NumberHelper<decimal>.CreateSaturating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            Assert.Equal(decimal.MaxValue, NumberHelper<decimal>.CreateSaturating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -582,6 +628,18 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromInt128Test()
+        {
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+
+            Assert.Equal(+1.0m, NumberHelper<decimal>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            Assert.Equal(-1.0m, NumberHelper<decimal>.CreateTruncating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+
+            Assert.Equal(decimal.MaxValue, NumberHelper<decimal>.CreateTruncating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateTruncating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -643,6 +701,18 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromUInt128Test()
+        {
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+
+            Assert.Equal(+1.0m, NumberHelper<decimal>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            Assert.Equal(decimal.MaxValue, NumberHelper<decimal>.CreateTruncating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+
+            Assert.Equal(decimal.MaxValue, NumberHelper<decimal>.CreateTruncating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            Assert.Equal(0.0m, NumberHelper<decimal>.CreateTruncating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -804,6 +874,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromInt128Test()
+        {
+            decimal result;
+
+            Assert.True(NumberHelper<decimal>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0m, result);
+
+            Assert.True(NumberHelper<decimal>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(1.0m, result);                         
+
+            Assert.True(NumberHelper<decimal>.TryCreate<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(-1.0m, result);
+
+            Assert.False(NumberHelper<decimal>.TryCreate<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(0.0m, result);
+
+            Assert.False(NumberHelper<decimal>.TryCreate<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0m, result);
+        }
+
+        [Fact]
         public static void TryCreateFromIntPtrTest()
         {
             decimal result;
@@ -929,6 +1020,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromUInt128Test()
+        {
+            decimal result;
+
+            Assert.True(NumberHelper<decimal>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0m, result);
+
+            Assert.True(NumberHelper<decimal>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(1.0m, result);
+
+            Assert.False(NumberHelper<decimal>.TryCreate<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(0.0m, result);
+
+            Assert.False(NumberHelper<decimal>.TryCreate<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(0.0m, result);
+
+            Assert.False(NumberHelper<decimal>.TryCreate<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0m, result);
+        }
+
+        [Fact]
         public static void TryCreateFromUIntPtrTest()
         {
             decimal result;
index 677c020..6c9fc81 100644 (file)
@@ -543,6 +543,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromInt128Test()
+        {
+            AssertBitwiseEqual(0.0, NumberHelper<double>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0, NumberHelper<double>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper<double>.CreateChecked<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(-170141183460469231731687303715884105728.0, NumberHelper<double>.CreateChecked<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(-1.0, NumberHelper<double>.CreateChecked<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -604,6 +614,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromUInt128Test()
+        {
+            AssertBitwiseEqual(0.0, NumberHelper<double>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0, NumberHelper<double>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper<double>.CreateChecked<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(170141183460469231731687303715884105728.0, NumberHelper<double>.CreateChecked<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(340282366920938463463374607431768211455.0, NumberHelper<double>.CreateChecked<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -679,6 +699,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromInt128Test()
+        {
+            AssertBitwiseEqual(0.0, NumberHelper<double>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0, NumberHelper<double>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper<double>.CreateSaturating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(-170141183460469231731687303715884105728.0, NumberHelper<double>.CreateSaturating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(-1.0, NumberHelper<double>.CreateSaturating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -740,6 +770,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromUInt128Test()
+        {
+            AssertBitwiseEqual(0.0, NumberHelper<double>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0, NumberHelper<double>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper<double>.CreateSaturating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(170141183460469231731687303715884105728.0, NumberHelper<double>.CreateSaturating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(340282366920938463463374607431768211455.0, NumberHelper<double>.CreateSaturating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -815,6 +855,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromInt128Test()
+        {
+            AssertBitwiseEqual(0.0, NumberHelper<double>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0, NumberHelper<double>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper<double>.CreateTruncating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(-170141183460469231731687303715884105728.0, NumberHelper<double>.CreateTruncating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(-1.0, NumberHelper<double>.CreateTruncating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -876,6 +926,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromUInt128Test()
+        {
+            AssertBitwiseEqual(0.0, NumberHelper<double>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0, NumberHelper<double>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper<double>.CreateTruncating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(170141183460469231731687303715884105728.0, NumberHelper<double>.CreateTruncating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(340282366920938463463374607431768211455.0, NumberHelper<double>.CreateTruncating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -1069,6 +1129,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromInt128Test()
+        {
+            double result;
+
+            Assert.True(NumberHelper<double>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(1.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(170141183460469231731687303715884105727.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(-170141183460469231731687303715884105728.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(-1.0, result);
+        }
+
+        [Fact]
         public static void TryCreateFromIntPtrTest()
         {
             double result;
@@ -1194,6 +1275,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromUInt128Test()
+        {
+            double result;
+
+            Assert.True(NumberHelper<double>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(1.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(170141183460469231731687303715884105727.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(170141183460469231731687303715884105728.0, result);
+
+            Assert.True(NumberHelper<double>.TryCreate<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(340282366920938463463374607431768211456.0, result);
+        }
+
+        [Fact]
         public static void TryCreateFromUIntPtrTest()
         {
             double result;
index c3ca50c..246fe53 100644 (file)
@@ -561,6 +561,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromInt128Test()
+        {
+            AssertBitwiseEqual(PositiveZero, NumberHelper<Half>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(PositiveOne, NumberHelper<Half>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0, NumberHelper<Half>.CreateChecked<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual((Half)(-170141183460469231731687303715884105728.0f), NumberHelper<Half>.CreateChecked<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(NegativeOne, NumberHelper<Half>.CreateChecked<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -622,6 +632,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromUInt128Test()
+        {
+            AssertBitwiseEqual(PositiveZero, NumberHelper<Half>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(PositiveOne, NumberHelper<Half>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0, NumberHelper<Half>.CreateChecked<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105728.0, NumberHelper<Half>.CreateChecked<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual((Half)340282366920938463463374607431768211455.0, NumberHelper<Half>.CreateChecked<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -697,6 +717,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromInt128Test()
+        {
+            AssertBitwiseEqual(PositiveZero, NumberHelper<Half>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(PositiveOne, NumberHelper<Half>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper<Half>.CreateSaturating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual((Half)(-170141183460469231731687303715884105728.0f), NumberHelper<Half>.CreateSaturating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(NegativeOne, NumberHelper<Half>.CreateSaturating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -758,6 +788,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromUInt128Test()
+        {
+            AssertBitwiseEqual(PositiveZero, NumberHelper<Half>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(PositiveOne, NumberHelper<Half>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper<Half>.CreateSaturating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105728.0f, NumberHelper<Half>.CreateSaturating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(Half.PositiveInfinity, NumberHelper<Half>.CreateSaturating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -833,6 +873,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromInt128Test()
+        {
+            AssertBitwiseEqual(PositiveZero, NumberHelper<Half>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(PositiveOne, NumberHelper<Half>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper<Half>.CreateTruncating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual((Half)(-170141183460469231731687303715884105728.0f), NumberHelper<Half>.CreateTruncating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(NegativeOne, NumberHelper<Half>.CreateTruncating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -894,6 +944,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromUInt128Test()
+        {
+            AssertBitwiseEqual(PositiveZero, NumberHelper<Half>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(PositiveOne, NumberHelper<Half>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper<Half>.CreateTruncating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual((Half)170141183460469231731687303715884105728.0f, NumberHelper<Half>.CreateTruncating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(Half.PositiveInfinity, NumberHelper<Half>.CreateTruncating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -1087,6 +1147,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromInt128Test()
+        {
+            Half result;
+
+            Assert.True(NumberHelper<Half>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(PositiveZero, result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(PositiveOne, result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal((Half)170141183460469231731687303715884105727.0, result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal((Half)(-170141183460469231731687303715884105728.0), result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(NegativeOne, result);
+        }
+
+        [Fact]
         public static void TryCreateFromIntPtrTest()
         {
             Half result;
@@ -1212,6 +1293,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromUInt128Test()
+        {
+            Half result;
+
+            Assert.True(NumberHelper<Half>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(PositiveZero, result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(PositiveOne, result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal((Half)170141183460469231731687303715884105727.0f, result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal((Half)170141183460469231731687303715884105728.0f, result);
+
+            Assert.True(NumberHelper<Half>.TryCreate<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(Half.PositiveInfinity, result);
+        }
+
+        [Fact]
         public static void TryCreateFromUIntPtrTest()
         {
             Half result;
diff --git a/src/libraries/System.Runtime/tests/System/Int128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/Int128Tests.GenericMath.cs
new file mode 100644 (file)
index 0000000..1ff3b1e
--- /dev/null
@@ -0,0 +1,1747 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using Xunit;
+
+namespace System.Tests
+{
+    public class Int128Tests_GenericMath
+    {
+        internal static readonly Int128 ByteMaxValue = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_00FF);
+
+        internal static readonly Int128 Int16MaxValue = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_7FFF);
+
+        internal static readonly Int128 Int16MaxValuePlusOne = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_8000);
+
+        internal static readonly Int128 Int16MinValue = new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_8000);
+
+        internal static readonly Int128 Int32MaxValue = new Int128(0x0000_0000_0000_0000, 0x0000_0000_7FFF_FFFF);
+
+        internal static readonly Int128 Int32MaxValuePlusOne = new Int128(0x0000_0000_0000_0000, 0x0000_0000_8000_0000);
+
+        internal static readonly Int128 Int32MinValue = new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_8000_0000);
+
+        internal static readonly Int128 Int64MaxValue = new Int128(0x0000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF);
+
+        internal static readonly Int128 Int64MaxValuePlusOne = new Int128(0x0000_0000_0000_0000, 0x8000_0000_0000_0000);
+
+        internal static readonly Int128 Int64MinValue = new Int128(0xFFFF_FFFF_FFFF_FFFF, 0x8000_0000_0000_0000);
+
+        internal static readonly Int128 MaxValue = new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+        internal static readonly Int128 MaxValueMinusOne = new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE);
+
+        internal static readonly Int128 MinValue = new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+        internal static readonly Int128 MinValuePlusOne = new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001);
+
+        internal static readonly Int128 NegativeOne = new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+        internal static readonly Int128 NegativeTwo = new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE);
+
+        internal static readonly Int128 One = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001);
+
+        internal static readonly Int128 SByteMaxValue = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_007F);
+
+        internal static readonly Int128 SByteMaxValuePlusOne = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0080);
+
+        internal static readonly Int128 SByteMinValue = new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80);
+
+        internal static readonly Int128 Two = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0002);
+
+        internal static readonly Int128 UInt16MaxValue = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_FFFF);
+
+        internal static readonly Int128 UInt32MaxValue = new Int128(0x0000_0000_0000_0000, 0x0000_0000_FFFF_FFFF);
+
+        internal static readonly Int128 UInt64MaxValue = new Int128(0x0000_0000_0000_0000, 0xFFFF_FFFF_FFFF_FFFF);
+
+        internal static readonly Int128 Zero = new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+        //
+        // IAdditionOperators
+        //
+
+        [Fact]
+        public static void op_AdditionTest()
+        {
+            Assert.Equal(One, AdditionOperatorsHelper<Int128, Int128, Int128>.op_Addition(Zero, 1));
+            Assert.Equal(Two, AdditionOperatorsHelper<Int128, Int128, Int128>.op_Addition(One, 1));
+            Assert.Equal(MinValue, AdditionOperatorsHelper<Int128, Int128, Int128>.op_Addition(MaxValue, 1));
+            Assert.Equal(MinValuePlusOne, AdditionOperatorsHelper<Int128, Int128, Int128>.op_Addition(MinValue, 1));
+            Assert.Equal(Zero, AdditionOperatorsHelper<Int128, Int128, Int128>.op_Addition(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_CheckedAdditionTest()
+        {
+            Assert.Equal(One, AdditionOperatorsHelper<Int128, Int128, Int128>.op_CheckedAddition(Zero, 1));
+            Assert.Equal(Two, AdditionOperatorsHelper<Int128, Int128, Int128>.op_CheckedAddition(One, 1));
+            Assert.Equal(MinValuePlusOne, AdditionOperatorsHelper<Int128, Int128, Int128>.op_CheckedAddition(MinValue, 1));
+            Assert.Equal(Zero, AdditionOperatorsHelper<Int128, Int128, Int128>.op_CheckedAddition(NegativeOne, 1));
+
+            Assert.Throws<OverflowException>(() => AdditionOperatorsHelper<Int128, Int128, Int128>.op_CheckedAddition(MaxValue, 1));
+        }
+
+        //
+        // IAdditiveIdentity
+        //
+
+        [Fact]
+        public static void AdditiveIdentityTest()
+        {
+            Assert.Equal(Zero, AdditiveIdentityHelper<Int128, Int128>.AdditiveIdentity);
+        }
+
+        //
+        // IBinaryInteger
+        //
+
+        [Fact]
+        public static void DivRemTest()
+        {
+            Assert.Equal((Zero, Zero), BinaryIntegerHelper<Int128>.DivRem(Zero, 2));
+            Assert.Equal((Zero, One), BinaryIntegerHelper<Int128>.DivRem(One, 2));
+            Assert.Equal((new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), One), BinaryIntegerHelper<Int128>.DivRem(MaxValue, 2));
+            Assert.Equal((new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), Zero), BinaryIntegerHelper<Int128>.DivRem(MinValue, 2));
+            Assert.Equal((Zero, NegativeOne), BinaryIntegerHelper<Int128>.DivRem(NegativeOne, 2));
+        }
+
+        [Fact]
+        public static void LeadingZeroCountTest()
+        {
+            Assert.Equal(0x80, BinaryIntegerHelper<Int128>.LeadingZeroCount(Zero));
+            Assert.Equal(0x7F, BinaryIntegerHelper<Int128>.LeadingZeroCount(One));
+            Assert.Equal(0x01, BinaryIntegerHelper<Int128>.LeadingZeroCount(MaxValue));
+            Assert.Equal(0x00, BinaryIntegerHelper<Int128>.LeadingZeroCount(MinValue));
+            Assert.Equal(0x00, BinaryIntegerHelper<Int128>.LeadingZeroCount(NegativeOne));
+        }
+
+        [Fact]
+        public static void PopCountTest()
+        {
+            Assert.Equal(0x00, BinaryIntegerHelper<Int128>.PopCount(Zero));
+            Assert.Equal(0x01, BinaryIntegerHelper<Int128>.PopCount(One));
+            Assert.Equal(0x7F, BinaryIntegerHelper<Int128>.PopCount(MaxValue));
+            Assert.Equal(0x01, BinaryIntegerHelper<Int128>.PopCount(MinValue));
+            Assert.Equal(0x80, BinaryIntegerHelper<Int128>.PopCount(NegativeOne));
+        }
+
+        [Fact]
+        public static void RotateLeftTest()
+        {
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<Int128>.RotateLeft(Zero, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0002), BinaryIntegerHelper<Int128>.RotateLeft(One, 1));
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BinaryIntegerHelper<Int128>.RotateLeft(MaxValue, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BinaryIntegerHelper<Int128>.RotateLeft(MinValue, 1));
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper<Int128>.RotateLeft(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void RotateRightTest()
+        {
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<Int128>.RotateRight(Zero, 1));
+            Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<Int128>.RotateRight(One, 1));
+            Assert.Equal(new Int128(0xBFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper<Int128>.RotateRight(MaxValue, 1));
+            Assert.Equal(new Int128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<Int128>.RotateRight(MinValue, 1));
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper<Int128>.RotateRight(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void TrailingZeroCountTest()
+        {
+            Assert.Equal(0x80, BinaryIntegerHelper<Int128>.TrailingZeroCount(Zero));
+            Assert.Equal(0x00, BinaryIntegerHelper<Int128>.TrailingZeroCount(One));
+            Assert.Equal(0x00, BinaryIntegerHelper<Int128>.TrailingZeroCount(MaxValue));
+            Assert.Equal(0x7F, BinaryIntegerHelper<Int128>.TrailingZeroCount(MinValue));
+            Assert.Equal(0x00, BinaryIntegerHelper<Int128>.TrailingZeroCount(NegativeOne));
+        }
+
+        [Fact]
+        public static void GetByteCountTest()
+        {
+            Assert.Equal(16, BinaryIntegerHelper<Int128>.GetByteCount(Zero));
+            Assert.Equal(16, BinaryIntegerHelper<Int128>.GetByteCount(One));
+            Assert.Equal(16, BinaryIntegerHelper<Int128>.GetByteCount(MaxValue));
+            Assert.Equal(16, BinaryIntegerHelper<Int128>.GetByteCount(MinValue));
+            Assert.Equal(16, BinaryIntegerHelper<Int128>.GetByteCount(NegativeOne));
+        }
+
+        [Fact]
+        public static void GetShortestBitLengthTest()
+        {
+            Assert.Equal(0x00, BinaryIntegerHelper<Int128>.GetShortestBitLength(Zero));
+            Assert.Equal(0x01, BinaryIntegerHelper<Int128>.GetShortestBitLength(One));
+            Assert.Equal(0x7F, BinaryIntegerHelper<Int128>.GetShortestBitLength(MaxValue));
+            Assert.Equal(0x80, BinaryIntegerHelper<Int128>.GetShortestBitLength(MinValue));
+            Assert.Equal(0x01, BinaryIntegerHelper<Int128>.GetShortestBitLength(NegativeOne));
+        }
+
+        [Fact]
+        public static void TryWriteLittleEndianTest()
+        {
+            Span<byte> destination = stackalloc byte[16];
+            int bytesWritten = 0;
+
+            Assert.True(BinaryIntegerHelper<Int128>.TryWriteLittleEndian(Zero, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<Int128>.TryWriteLittleEndian(One, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<Int128>.TryWriteLittleEndian(MaxValue, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<Int128>.TryWriteLittleEndian(MinValue, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<Int128>.TryWriteLittleEndian(NegativeOne, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, destination.ToArray());
+
+            Assert.False(BinaryIntegerHelper<Int128>.TryWriteLittleEndian(default, Span<byte>.Empty, out bytesWritten));
+            Assert.Equal(0, bytesWritten);
+            Assert.Equal(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, destination.ToArray());
+        }
+
+        //
+        // IBinaryNumber
+        //
+
+        [Fact]
+        public static void IsPow2Test()
+        {
+            Assert.False(BinaryNumberHelper<Int128>.IsPow2(Zero));
+            Assert.True(BinaryNumberHelper<Int128>.IsPow2(One));
+            Assert.False(BinaryNumberHelper<Int128>.IsPow2(MaxValue));
+            Assert.False(BinaryNumberHelper<Int128>.IsPow2(MinValue));
+            Assert.False(BinaryNumberHelper<Int128>.IsPow2(NegativeOne));
+        }
+
+        [Fact]
+        public static void Log2Test()
+        {
+            Assert.Equal(0x00, BinaryNumberHelper<Int128>.Log2(Zero));
+            Assert.Equal(0x00, BinaryNumberHelper<Int128>.Log2(One));
+            Assert.Equal(0x7E, BinaryNumberHelper<Int128>.Log2(MaxValue));
+            Assert.Throws<ArgumentOutOfRangeException>(() => BinaryNumberHelper<Int128>.Log2(MinValue));
+            Assert.Throws<ArgumentOutOfRangeException>(() => BinaryNumberHelper<Int128>.Log2(NegativeOne));
+        }
+
+        //
+        // IBitwiseOperators
+        //
+
+        [Fact]
+        public static void op_BitwiseAndTest()
+        {
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseAnd(Zero, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseAnd(One, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseAnd(MaxValue, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseAnd(MinValue, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseAnd(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_BitwiseOrTest()
+        {
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseOr(Zero, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseOr(One, 1));
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseOr(MaxValue, 1));
+            Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseOr(MinValue, 1));
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_BitwiseOr(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_ExclusiveOrTest()
+        {
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_ExclusiveOr(Zero, 1));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_ExclusiveOr(One, 1));
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_ExclusiveOr(MaxValue, 1));
+            Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_ExclusiveOr(MinValue, 1));
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_ExclusiveOr(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_OnesComplementTest()
+        {
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_OnesComplement(Zero));
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_OnesComplement(One));
+            Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_OnesComplement(MaxValue));
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_OnesComplement(MinValue));
+            Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<Int128, Int128, Int128>.op_OnesComplement(NegativeOne));
+        }
+
+        //
+        // IComparisonOperators
+        //
+
+        [Fact]
+        public static void op_GreaterThanTest()
+        {
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThan(Zero, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThan(One, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThan(MaxValue, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThan(MinValue, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThan(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_GreaterThanOrEqualTest()
+        {
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThanOrEqual(Zero, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThanOrEqual(One, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThanOrEqual(MaxValue, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThanOrEqual(MinValue, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_GreaterThanOrEqual(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_LessThanTest()
+        {
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_LessThan(Zero, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_LessThan(One, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_LessThan(MaxValue, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_LessThan(MinValue, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_LessThan(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_LessThanOrEqualTest()
+        {
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_LessThanOrEqual(Zero, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_LessThanOrEqual(One, 1));
+            Assert.False(ComparisonOperatorsHelper<Int128, Int128>.op_LessThanOrEqual(MaxValue, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_LessThanOrEqual(MinValue, 1));
+            Assert.True(ComparisonOperatorsHelper<Int128, Int128>.op_LessThanOrEqual(NegativeOne, 1));
+        }
+
+        //
+        // IDecrementOperators
+        //
+
+        [Fact]
+        public static void op_DecrementTest()
+        {
+            Assert.Equal(NegativeOne, DecrementOperatorsHelper<Int128>.op_Decrement(Zero));
+            Assert.Equal(Zero, DecrementOperatorsHelper<Int128>.op_Decrement(One));
+            Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper<Int128>.op_Decrement(MaxValue));
+            Assert.Equal(MaxValue, DecrementOperatorsHelper<Int128>.op_Decrement(MinValue));
+            Assert.Equal(NegativeTwo, DecrementOperatorsHelper<Int128>.op_Decrement(NegativeOne));
+        }
+
+        [Fact]
+        public static void op_CheckedDecrementTest()
+        {
+            Assert.Equal(NegativeOne, DecrementOperatorsHelper<Int128>.op_CheckedDecrement(Zero));
+            Assert.Equal(Zero, DecrementOperatorsHelper<Int128>.op_CheckedDecrement(One));
+            Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper<Int128>.op_CheckedDecrement(MaxValue));
+            Assert.Equal(NegativeTwo, DecrementOperatorsHelper<Int128>.op_CheckedDecrement(NegativeOne));
+
+            Assert.Throws<OverflowException>(() => DecrementOperatorsHelper<Int128>.op_CheckedDecrement(MinValue));
+        }
+
+        //
+        // IDivisionOperators
+        //
+
+        [Fact]
+        public static void op_DivisionTest()
+        {
+            Assert.Equal(Zero, DivisionOperatorsHelper<Int128, Int128, Int128>.op_Division(Zero, 2));
+            Assert.Equal(Zero, DivisionOperatorsHelper<Int128, Int128, Int128>.op_Division(One, 2));
+            Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper<Int128, Int128, Int128>.op_Division(MaxValue, 2));
+            Assert.Equal(new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper<Int128, Int128, Int128>.op_Division(MinValue, 2));
+            Assert.Equal(Zero, DivisionOperatorsHelper<Int128, Int128, Int128>.op_Division(NegativeOne, 2));
+
+            Assert.Throws<DivideByZeroException>(() => DivisionOperatorsHelper<Int128, Int128, Int128>.op_Division(One, 0));
+        }
+
+        [Fact]
+        public static void op_CheckedDivisionTest()
+        {
+            Assert.Equal(Zero, DivisionOperatorsHelper<Int128, Int128, Int128>.op_CheckedDivision(Zero, 2));
+            Assert.Equal(Zero, DivisionOperatorsHelper<Int128, Int128, Int128>.op_CheckedDivision(One, 2));
+            Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper<Int128, Int128, Int128>.op_CheckedDivision(MaxValue, 2));
+            Assert.Equal(new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper<Int128, Int128, Int128>.op_CheckedDivision(MinValue, 2));
+            Assert.Equal(Zero, DivisionOperatorsHelper<Int128, Int128, Int128>.op_CheckedDivision(NegativeOne, 2));
+
+            Assert.Throws<DivideByZeroException>(() => DivisionOperatorsHelper<Int128, Int128, Int128>.op_CheckedDivision(One, 0));
+        }
+
+        //
+        // IEqualityOperators
+        //
+
+        [Fact]
+        public static void op_EqualityTest()
+        {
+            Assert.False(EqualityOperatorsHelper<Int128, Int128>.op_Equality(Zero, 1));
+            Assert.True(EqualityOperatorsHelper<Int128, Int128>.op_Equality(One, 1));
+            Assert.False(EqualityOperatorsHelper<Int128, Int128>.op_Equality(MaxValue, 1));
+            Assert.False(EqualityOperatorsHelper<Int128, Int128>.op_Equality(MinValue, 1));
+            Assert.False(EqualityOperatorsHelper<Int128, Int128>.op_Equality(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_InequalityTest()
+        {
+            Assert.True(EqualityOperatorsHelper<Int128, Int128>.op_Inequality(Zero, 1));
+            Assert.False(EqualityOperatorsHelper<Int128, Int128>.op_Inequality(One, 1));
+            Assert.True(EqualityOperatorsHelper<Int128, Int128>.op_Inequality(MaxValue, 1));
+            Assert.True(EqualityOperatorsHelper<Int128, Int128>.op_Inequality(MinValue, 1));
+            Assert.True(EqualityOperatorsHelper<Int128, Int128>.op_Inequality(NegativeOne, 1));
+        }
+
+        //
+        // IIncrementOperators
+        //
+
+        [Fact]
+        public static void op_IncrementTest()
+        {
+            Assert.Equal(One, IncrementOperatorsHelper<Int128>.op_Increment(Zero));
+            Assert.Equal(Two, IncrementOperatorsHelper<Int128>.op_Increment(One));
+            Assert.Equal(MinValue, IncrementOperatorsHelper<Int128>.op_Increment(MaxValue));
+            Assert.Equal(MinValuePlusOne, IncrementOperatorsHelper<Int128>.op_Increment(MinValue));
+            Assert.Equal(Zero, IncrementOperatorsHelper<Int128>.op_Increment(NegativeOne));
+        }
+
+        [Fact]
+        public static void op_CheckedIncrementTest()
+        {
+            Assert.Equal(One, IncrementOperatorsHelper<Int128>.op_CheckedIncrement(Zero));
+            Assert.Equal(Two, IncrementOperatorsHelper<Int128>.op_CheckedIncrement(One));
+            Assert.Equal(MinValuePlusOne, IncrementOperatorsHelper<Int128>.op_CheckedIncrement(MinValue));
+            Assert.Equal(Zero, IncrementOperatorsHelper<Int128>.op_CheckedIncrement(NegativeOne));
+
+            Assert.Throws<OverflowException>(() => IncrementOperatorsHelper<Int128>.op_CheckedIncrement(MaxValue));
+        }
+
+        //
+        // IMinMaxValue
+        //
+
+        [Fact]
+        public static void MaxValueTest()
+        {
+            Assert.Equal(MaxValue, MinMaxValueHelper<Int128>.MaxValue);
+        }
+
+        [Fact]
+        public static void MinValueTest()
+        {
+            Assert.Equal(MinValue, MinMaxValueHelper<Int128>.MinValue);
+        }
+
+        //
+        // IModulusOperators
+        //
+
+        [Fact]
+        public static void op_ModulusTest()
+        {
+            Assert.Equal(Zero, ModulusOperatorsHelper<Int128, Int128, Int128>.op_Modulus(Zero, 2));
+            Assert.Equal(One, ModulusOperatorsHelper<Int128, Int128, Int128>.op_Modulus(One, 2));
+            Assert.Equal(One, ModulusOperatorsHelper<Int128, Int128, Int128>.op_Modulus(MaxValue, 2));
+            Assert.Equal(Zero, ModulusOperatorsHelper<Int128, Int128, Int128>.op_Modulus(MinValue, 2));
+            Assert.Equal(NegativeOne, ModulusOperatorsHelper<Int128, Int128, Int128>.op_Modulus(NegativeOne, 2));
+
+            Assert.Throws<DivideByZeroException>(() => ModulusOperatorsHelper<Int128, Int128, Int128>.op_Modulus(One, 0));
+        }
+
+        //
+        // IMultiplicativeIdentity
+        //
+
+        [Fact]
+        public static void MultiplicativeIdentityTest()
+        {
+            Assert.Equal(One, MultiplicativeIdentityHelper<Int128, Int128>.MultiplicativeIdentity);
+        }
+
+        //
+        // IMultiplyOperators
+        //
+
+        [Fact]
+        public static void op_MultiplyTest()
+        {
+            Assert.Equal(Zero, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_Multiply(Zero, 2));
+            Assert.Equal(Two, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_Multiply(One, 2));
+            Assert.Equal(NegativeTwo, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_Multiply(MaxValue, 2));
+            Assert.Equal(Zero, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_Multiply(MinValue, 2));
+            Assert.Equal(NegativeTwo, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_Multiply(NegativeOne, 2));
+        }
+
+        [Fact]
+        public static void op_CheckedMultiplyTest()
+        {
+            Assert.Equal(Zero, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_CheckedMultiply(Zero, 2));
+            Assert.Equal(Two, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_CheckedMultiply(One, 2));
+            Assert.Equal(NegativeTwo, MultiplyOperatorsHelper<Int128, Int128, Int128>.op_CheckedMultiply(NegativeOne, 2));
+
+            Assert.Throws<OverflowException>(() => MultiplyOperatorsHelper<Int128, Int128, Int128>.op_CheckedMultiply(MaxValue, 2));
+            Assert.Throws<OverflowException>(() => MultiplyOperatorsHelper<Int128, Int128, Int128>.op_CheckedMultiply(MinValue, 2));
+        }
+
+        //
+        // INumber
+        //
+
+        [Fact]
+        public static void ClampTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.Clamp(Zero, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F));
+            Assert.Equal(One, NumberHelper<Int128>.Clamp(One, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F));
+            Assert.Equal(0x007F, NumberHelper<Int128>.Clamp(MaxValue, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F));
+            Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), NumberHelper<Int128>.Clamp(MinValue, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.Clamp(NegativeOne, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F));
+        }
+
+        [Fact]
+        public static void MaxTest()
+        {
+            Assert.Equal(One, NumberHelper<Int128>.Max(Zero, 1));
+            Assert.Equal(One, NumberHelper<Int128>.Max(One, 1));
+            Assert.Equal(MaxValue, NumberHelper<Int128>.Max(MaxValue, 1));
+            Assert.Equal(One, NumberHelper<Int128>.Max(MinValue, 1));
+            Assert.Equal(One, NumberHelper<Int128>.Max(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void MinTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.Min(Zero, 1));
+            Assert.Equal(One, NumberHelper<Int128>.Min(One, 1));
+            Assert.Equal(One, NumberHelper<Int128>.Min(MaxValue, 1));
+            Assert.Equal(MinValue, NumberHelper<Int128>.Min(MinValue, 1));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.Min(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void SignTest()
+        {
+            Assert.Equal(0, NumberHelper<Int128>.Sign(Zero));
+            Assert.Equal(1, NumberHelper<Int128>.Sign(One));
+            Assert.Equal(1, NumberHelper<Int128>.Sign(MaxValue));
+            Assert.Equal(-1, NumberHelper<Int128>.Sign(MinValue));
+            Assert.Equal(-1, NumberHelper<Int128>.Sign(NegativeOne));
+        }
+
+        //
+        // INumberBase
+        //
+
+        [Fact]
+        public static void OneTest()
+        {
+            Assert.Equal(One, NumberBaseHelper<Int128>.One);
+        }
+
+        [Fact]
+        public static void ZeroTest()
+        {
+            Assert.Equal(Zero, NumberBaseHelper<Int128>.Zero);
+        }
+
+        [Fact]
+        public static void AbsTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.Abs(Zero));
+            Assert.Equal(One, NumberHelper<Int128>.Abs(One));
+            Assert.Equal(MaxValue, NumberHelper<Int128>.Abs(MaxValue));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.Abs(MinValue));
+            Assert.Equal(One, NumberHelper<Int128>.Abs(NegativeOne));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<byte>(0x00));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<byte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<Int128>.CreateChecked<byte>(0x7F));
+            Assert.Equal(SByteMaxValuePlusOne, NumberHelper<Int128>.CreateChecked<byte>(0x80));
+            Assert.Equal(ByteMaxValue, NumberHelper<Int128>.CreateChecked<byte>(0xFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromCharTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<char>((char)0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<char>((char)0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateChecked<char>((char)0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<Int128>.CreateChecked<char>((char)0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<Int128>.CreateChecked<char>((char)0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromDecimalTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<decimal>(decimal.Zero));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<decimal>(decimal.One));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<decimal>(decimal.MinusOne));
+
+            Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper<Int128>.CreateChecked<decimal>(decimal.MaxValue));
+            Assert.Equal(new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001), NumberHelper<Int128>.CreateChecked<decimal>(decimal.MinValue));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromDoubleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<double>(+0.0));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<double>(-0.0));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<double>(+double.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<double>(-double.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<double>(+1.0));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<double>(-1.0));
+
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateChecked<double>(+170141183460469212842221372237303250944.0));
+            Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateChecked<double>(-170141183460469212842221372237303250944.0));
+
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateChecked<double>(-170141183460469231731687303715884105728.0));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<double>(+170141183460469231731687303715884105728.0));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<double>(-170141183460469269510619166673045815296.0));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<double>(double.MaxValue));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<double>(double.MinValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<double>(double.PositiveInfinity));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<double>(double.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromHalfTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<Half>((Half)(+0.0)));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<Half>((Half)(-0.0)));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<Half>(+Half.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<Half>(-Half.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<Half>((Half)(+1.0)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<Half>((Half)(-1.0)));
+
+            Assert.Equal(+65504, NumberHelper<Int128>.CreateChecked<Half>(Half.MaxValue));
+            Assert.Equal(-65504, NumberHelper<Int128>.CreateChecked<Half>(Half.MinValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<Half>(Half.PositiveInfinity));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<Half>(Half.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<short>(0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<short>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateChecked<short>(0x7FFF));
+            Assert.Equal(Int16MinValue, NumberHelper<Int128>.CreateChecked<short>(unchecked((short)0x8000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<short>(unchecked((short)0xFFFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<int>(0x00000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<int>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateChecked<int>(0x7FFFFFFF));
+            Assert.Equal(Int32MinValue, NumberHelper<Int128>.CreateChecked<int>(unchecked((int)0x80000000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<int>(unchecked((int)0xFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<long>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<long>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateChecked<long>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MinValue, NumberHelper<Int128>.CreateChecked<long>(unchecked((long)0x8000000000000000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<long>(unchecked((long)0xFFFFFFFFFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<nint>(unchecked((nint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<Int128>.CreateChecked<nint>(unchecked((nint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateChecked<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MinValue, NumberHelper<Int128>.CreateChecked<nint>(unchecked((nint)0x8000000000000000)));
+                Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<nint>((nint)0x00000000));
+                Assert.Equal(One, NumberHelper<Int128>.CreateChecked<nint>((nint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateChecked<nint>((nint)0x7FFFFFFF));
+                Assert.Equal(Int32MinValue, NumberHelper<Int128>.CreateChecked<nint>(unchecked((nint)0x80000000)));
+                Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<nint>(unchecked((nint)0xFFFFFFFF)));
+            }
+        }
+
+        [Fact]
+        public static void CreateCheckedFromSByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<sbyte>(0x00));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<sbyte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<Int128>.CreateChecked<sbyte>(0x7F));
+            Assert.Equal(SByteMinValue, NumberHelper<Int128>.CreateChecked<sbyte>(unchecked((sbyte)0x80)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<sbyte>(unchecked((sbyte)0xFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromSingleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<float>(+0.0f));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<float>(-0.0f));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<float>(+float.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<float>(-float.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<float>(+1.0f));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateChecked<float>(-1.0f));
+
+            Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateChecked<float>(+170141173319264429905852091742258462720.0f));
+            Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateChecked<float>(-170141173319264429905852091742258462720.0f));
+
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateChecked<float>(-170141183460469231731687303715884105728.0f));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<float>(+170141183460469231731687303715884105728.0f));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<float>(-170141203742878835383357727663135391744.0f));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<float>(float.MaxValue));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<float>(float.MinValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<float>(float.PositiveInfinity));
+            Assert.Throws<OverflowException>(() => NumberHelper<Int128>.CreateChecked<float>(float.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<ushort>(0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<ushort>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateChecked<ushort>(0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<Int128>.CreateChecked<ushort>(0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<Int128>.CreateChecked<ushort>(0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<uint>(0x00000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<uint>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateChecked<uint>(0x7FFFFFFF));
+            Assert.Equal(Int32MaxValuePlusOne, NumberHelper<Int128>.CreateChecked<uint>(0x80000000));
+            Assert.Equal(UInt32MaxValue, NumberHelper<Int128>.CreateChecked<uint>(0xFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<ulong>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateChecked<ulong>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateChecked<ulong>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MaxValuePlusOne, NumberHelper<Int128>.CreateChecked<ulong>(0x8000000000000000));
+            Assert.Equal(UInt64MaxValue, NumberHelper<Int128>.CreateChecked<ulong>(0xFFFFFFFFFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<nuint>(unchecked((nuint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<Int128>.CreateChecked<nuint>(unchecked((nuint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateChecked<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MaxValuePlusOne, NumberHelper<Int128>.CreateChecked<nuint>(unchecked((nuint)0x8000000000000000)));
+                Assert.Equal(UInt64MaxValue, NumberHelper<Int128>.CreateChecked<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateChecked<nuint>((nuint)0x00000000));
+                Assert.Equal(One, NumberHelper<Int128>.CreateChecked<nuint>((nuint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateChecked<nuint>((nuint)0x7FFFFFFF));
+                Assert.Equal(Int32MaxValuePlusOne, NumberHelper<Int128>.CreateChecked<nuint>((nuint)0x80000000));
+                Assert.Equal(UInt32MaxValue, NumberHelper<Int128>.CreateChecked<nuint>((nuint)0xFFFFFFFF));
+            }
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<byte>(0x00));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<byte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<Int128>.CreateSaturating<byte>(0x7F));
+            Assert.Equal(SByteMaxValuePlusOne, NumberHelper<Int128>.CreateSaturating<byte>(0x80));
+            Assert.Equal(ByteMaxValue, NumberHelper<Int128>.CreateSaturating<byte>(0xFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromCharTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<char>((char)0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<char>((char)0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateSaturating<char>((char)0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<Int128>.CreateSaturating<char>((char)0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<Int128>.CreateSaturating<char>((char)0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromDecimalTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<decimal>(decimal.Zero));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<decimal>(decimal.One));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<decimal>(decimal.MinusOne));
+
+            Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper<Int128>.CreateSaturating<decimal>(decimal.MaxValue));
+            Assert.Equal(new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001), NumberHelper<Int128>.CreateSaturating<decimal>(decimal.MinValue));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromDoubleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<double>(+0.0));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<double>(-0.0));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<double>(+double.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<double>(-double.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<double>(+1.0));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<double>(-1.0));
+
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateSaturating<double>(+170141183460469212842221372237303250944.0));
+            Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateSaturating<double>(-170141183460469212842221372237303250944.0));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<double>(-170141183460469231731687303715884105728.0));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateSaturating<double>(+170141183460469231731687303715884105728.0));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<double>(-170141183460469269510619166673045815296.0));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateSaturating<double>(double.MaxValue));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<double>(double.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateSaturating<double>(double.PositiveInfinity));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<double>(double.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromHalfTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<Half>((Half)(+0.0)));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<Half>((Half)(-0.0)));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<Half>(+Half.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<Half>(-Half.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<Half>((Half)(+1.0)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<Half>((Half)(-1.0)));
+
+            Assert.Equal(+65504, NumberHelper<Int128>.CreateSaturating<Half>(Half.MaxValue));
+            Assert.Equal(-65504, NumberHelper<Int128>.CreateSaturating<Half>(Half.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateSaturating<Half>(Half.PositiveInfinity));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<Half>(Half.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<short>(0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<short>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateSaturating<short>(0x7FFF));
+            Assert.Equal(Int16MinValue, NumberHelper<Int128>.CreateSaturating<short>(unchecked((short)0x8000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<short>(unchecked((short)0xFFFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<int>(0x00000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<int>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateSaturating<int>(0x7FFFFFFF));
+            Assert.Equal(Int32MinValue, NumberHelper<Int128>.CreateSaturating<int>(unchecked((int)0x80000000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<int>(unchecked((int)0xFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<long>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<long>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateSaturating<long>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MinValue, NumberHelper<Int128>.CreateSaturating<long>(unchecked((long)0x8000000000000000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<long>(unchecked((long)0xFFFFFFFFFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<nint>(unchecked((nint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<nint>(unchecked((nint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateSaturating<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MinValue, NumberHelper<Int128>.CreateSaturating<nint>(unchecked((nint)0x8000000000000000)));
+                Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<nint>((nint)0x00000000));
+                Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<nint>((nint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateSaturating<nint>((nint)0x7FFFFFFF));
+                Assert.Equal(Int32MinValue, NumberHelper<Int128>.CreateSaturating<nint>(unchecked((nint)0x80000000)));
+                Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<nint>(unchecked((nint)0xFFFFFFFF)));
+            }
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromSByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<sbyte>(0x00));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<sbyte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<Int128>.CreateSaturating<sbyte>(0x7F));
+            Assert.Equal(SByteMinValue, NumberHelper<Int128>.CreateSaturating<sbyte>(unchecked((sbyte)0x80)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<sbyte>(unchecked((sbyte)0xFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromSingleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<float>(+0.0f));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<float>(-0.0f));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<float>(+float.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<float>(-float.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<float>(+1.0f));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateSaturating<float>(-1.0f));
+
+            Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateSaturating<float>(+170141173319264429905852091742258462720.0f));
+            Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateSaturating<float>(-170141173319264429905852091742258462720.0f));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<float>(-170141183460469231731687303715884105728.0f));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateSaturating<float>(+170141183460469231731687303715884105728.0f));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<float>(-170141203742878835383357727663135391744.0f));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateSaturating<float>(float.MaxValue));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<float>(float.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateSaturating<float>(float.PositiveInfinity));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateSaturating<float>(float.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<ushort>(0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<ushort>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateSaturating<ushort>(0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<Int128>.CreateSaturating<ushort>(0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<Int128>.CreateSaturating<ushort>(0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<uint>(0x00000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<uint>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateSaturating<uint>(0x7FFFFFFF));
+            Assert.Equal(Int32MaxValuePlusOne, NumberHelper<Int128>.CreateSaturating<uint>(0x80000000));
+            Assert.Equal(UInt32MaxValue, NumberHelper<Int128>.CreateSaturating<uint>(0xFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<ulong>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<ulong>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateSaturating<ulong>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MaxValuePlusOne, NumberHelper<Int128>.CreateSaturating<ulong>(0x8000000000000000));
+            Assert.Equal(UInt64MaxValue, NumberHelper<Int128>.CreateSaturating<ulong>(0xFFFFFFFFFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<nuint>(unchecked((nuint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<nuint>(unchecked((nuint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateSaturating<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MaxValuePlusOne, NumberHelper<Int128>.CreateSaturating<nuint>(unchecked((nuint)0x8000000000000000)));
+                Assert.Equal(UInt64MaxValue, NumberHelper<Int128>.CreateSaturating<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateSaturating<nuint>((nuint)0x00000000));
+                Assert.Equal(One, NumberHelper<Int128>.CreateSaturating<nuint>((nuint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateSaturating<nuint>((nuint)0x7FFFFFFF));
+                Assert.Equal(Int32MaxValuePlusOne, NumberHelper<Int128>.CreateSaturating<nuint>((nuint)0x80000000));
+                Assert.Equal(UInt32MaxValue, NumberHelper<Int128>.CreateSaturating<nuint>((nuint)0xFFFFFFFF));
+            }
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<byte>(0x00));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<byte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<Int128>.CreateTruncating<byte>(0x7F));
+            Assert.Equal(SByteMaxValuePlusOne, NumberHelper<Int128>.CreateTruncating<byte>(0x80));
+            Assert.Equal(ByteMaxValue, NumberHelper<Int128>.CreateTruncating<byte>(0xFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromCharTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<char>((char)0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<char>((char)0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateTruncating<char>((char)0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<Int128>.CreateTruncating<char>((char)0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<Int128>.CreateTruncating<char>((char)0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromDecimalTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<decimal>(decimal.Zero));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<decimal>(decimal.One));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<decimal>(decimal.MinusOne));
+
+            Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper<Int128>.CreateTruncating<decimal>(decimal.MaxValue));
+            Assert.Equal(new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001), NumberHelper<Int128>.CreateTruncating<decimal>(decimal.MinValue));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromDoubleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<double>(+0.0));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<double>(-0.0));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<double>(+double.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<double>(-double.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<double>(+1.0));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<double>(-1.0));
+
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateTruncating<double>(+170141183460469212842221372237303250944.0));
+            Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateTruncating<double>(-170141183460469212842221372237303250944.0));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<double>(-170141183460469231731687303715884105728.0));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateTruncating<double>(+170141183460469231731687303715884105728.0));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<double>(-170141183460469269510619166673045815296.0));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateTruncating<double>(double.MaxValue));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<double>(double.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateTruncating<double>(double.PositiveInfinity));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<double>(double.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromHalfTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<Half>((Half)(+0.0)));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<Half>((Half)(-0.0)));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<Half>(+Half.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<Half>(-Half.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<Half>((Half)(+1.0)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<Half>((Half)(-1.0)));
+
+            Assert.Equal(+65504, NumberHelper<Int128>.CreateTruncating<Half>(Half.MaxValue));
+            Assert.Equal(-65504, NumberHelper<Int128>.CreateTruncating<Half>(Half.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateTruncating<Half>(Half.PositiveInfinity));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<Half>(Half.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<short>(0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<short>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateTruncating<short>(0x7FFF));
+            Assert.Equal(Int16MinValue, NumberHelper<Int128>.CreateTruncating<short>(unchecked((short)0x8000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<short>(unchecked((short)0xFFFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<int>(0x00000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<int>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateTruncating<int>(0x7FFFFFFF));
+            Assert.Equal(Int32MinValue, NumberHelper<Int128>.CreateTruncating<int>(unchecked((int)0x80000000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<int>(unchecked((int)0xFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<long>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<long>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateTruncating<long>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MinValue, NumberHelper<Int128>.CreateTruncating<long>(unchecked((long)0x8000000000000000)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<long>(unchecked((long)0xFFFFFFFFFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<nint>(unchecked((nint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<nint>(unchecked((nint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateTruncating<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MinValue, NumberHelper<Int128>.CreateTruncating<nint>(unchecked((nint)0x8000000000000000)));
+                Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<nint>((nint)0x00000000));
+                Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<nint>((nint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateTruncating<nint>((nint)0x7FFFFFFF));
+                Assert.Equal(Int32MinValue, NumberHelper<Int128>.CreateTruncating<nint>(unchecked((nint)0x80000000)));
+                Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<nint>(unchecked((nint)0xFFFFFFFF)));
+            }
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromSByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<sbyte>(0x00));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<sbyte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<Int128>.CreateTruncating<sbyte>(0x7F));
+            Assert.Equal(SByteMinValue, NumberHelper<Int128>.CreateTruncating<sbyte>(unchecked((sbyte)0x80)));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<sbyte>(unchecked((sbyte)0xFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromSingleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<float>(+0.0f));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<float>(-0.0f));
+
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<float>(+float.Epsilon));
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<float>(-float.Epsilon));
+
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<float>(+1.0f));
+            Assert.Equal(NegativeOne, NumberHelper<Int128>.CreateTruncating<float>(-1.0f));
+
+            Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateTruncating<float>(+170141173319264429905852091742258462720.0f));
+            Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), NumberHelper<Int128>.CreateTruncating<float>(-170141173319264429905852091742258462720.0f));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<float>(-170141183460469231731687303715884105728.0f));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateTruncating<float>(+170141183460469231731687303715884105728.0f));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<float>(-170141203742878835383357727663135391744.0f));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateTruncating<float>(float.MaxValue));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<float>(float.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<Int128>.CreateTruncating<float>(float.PositiveInfinity));
+            Assert.Equal(MinValue, NumberHelper<Int128>.CreateTruncating<float>(float.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<ushort>(0x0000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<ushort>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<Int128>.CreateTruncating<ushort>(0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<Int128>.CreateTruncating<ushort>(0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<Int128>.CreateTruncating<ushort>(0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<uint>(0x00000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<uint>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateTruncating<uint>(0x7FFFFFFF));
+            Assert.Equal(Int32MaxValuePlusOne, NumberHelper<Int128>.CreateTruncating<uint>(0x80000000));
+            Assert.Equal(UInt32MaxValue, NumberHelper<Int128>.CreateTruncating<uint>(0xFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<ulong>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<ulong>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateTruncating<ulong>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MaxValuePlusOne, NumberHelper<Int128>.CreateTruncating<ulong>(0x8000000000000000));
+            Assert.Equal(UInt64MaxValue, NumberHelper<Int128>.CreateTruncating<ulong>(0xFFFFFFFFFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<nuint>(unchecked((nuint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<nuint>(unchecked((nuint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<Int128>.CreateTruncating<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MaxValuePlusOne, NumberHelper<Int128>.CreateTruncating<nuint>(unchecked((nuint)0x8000000000000000)));
+                Assert.Equal(UInt64MaxValue, NumberHelper<Int128>.CreateTruncating<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<Int128>.CreateTruncating<nuint>((nuint)0x00000000));
+                Assert.Equal(One, NumberHelper<Int128>.CreateTruncating<nuint>((nuint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<Int128>.CreateTruncating<nuint>((nuint)0x7FFFFFFF));
+                Assert.Equal(Int32MaxValuePlusOne, NumberHelper<Int128>.CreateTruncating<nuint>((nuint)0x80000000));
+                Assert.Equal(UInt32MaxValue, NumberHelper<Int128>.CreateTruncating<nuint>((nuint)0xFFFFFFFF));
+            }
+        }
+
+        [Fact]
+        public static void TryCreateFromByteTest()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<byte>(0x00, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<byte>(0x01, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<byte>(0x7F, out result));
+            Assert.Equal(SByteMaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<byte>(0x80, out result));
+            Assert.Equal(SByteMaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<byte>(0xFF, out result));
+            Assert.Equal(ByteMaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromCharTest()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<char>((char)0x0000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<char>((char)0x0001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<char>((char)0x7FFF, out result));
+            Assert.Equal(Int16MaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<char>((char)0x8000, out result));
+            Assert.Equal(Int16MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<char>((char)0xFFFF, out result));
+            Assert.Equal(UInt16MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromDecimalTest()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<decimal>(decimal.Zero, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<decimal>(decimal.One, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<decimal>(decimal.MinusOne, out result));
+            Assert.Equal(NegativeOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<decimal>(decimal.MaxValue, out result));
+            Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<decimal>(decimal.MinValue, out result));
+            Assert.Equal(new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001), result);
+        }
+
+        [Fact]
+        public static void TryCreateFromDoubleTest()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(+0.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(-0.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(+double.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(-double.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(+1.0, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(-1.0, out result));
+            Assert.Equal(NegativeOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(+170141183460469212842221372237303250944.0, out result));
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(-170141183460469212842221372237303250944.0, out result));
+            Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<double>(-170141183460469231731687303715884105728.0, out result));
+            Assert.Equal(MinValue, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<double>(+170141183460469231731687303715884105728.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<double>(-170141183460469269510619166673045815296.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<double>(double.MaxValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<double>(double.MinValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<double>(double.PositiveInfinity, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<double>(double.NegativeInfinity, out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromHalfTest()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>((Half)(+0.0), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>((Half)(-0.0), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>(+Half.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>(-Half.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>((Half)(+1.0), out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>((Half)(-1.0), out result));
+            Assert.Equal(NegativeOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>(Half.MaxValue, out result));
+            Assert.Equal(+65504, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<Half>(Half.MinValue, out result));
+            Assert.Equal(-65504, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<Half>(Half.PositiveInfinity, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<Half>(Half.NegativeInfinity, out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromInt16Test()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<short>(0x0000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<short>(0x0001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<short>(0x7FFF, out result));
+            Assert.Equal(Int16MaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<short>(unchecked((short)0x8000), out result));
+            Assert.Equal(Int16MinValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<short>(unchecked((short)0xFFFF), out result));
+            Assert.Equal(NegativeOne, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromInt32Test()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<int>(0x00000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<int>(0x00000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<int>(0x7FFFFFFF, out result));
+            Assert.Equal(Int32MaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<int>(unchecked((int)0x80000000), out result));
+            Assert.Equal(Int32MinValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<int>(unchecked((int)0xFFFFFFFF), out result));
+            Assert.Equal(NegativeOne, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromInt64Test()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<long>(0x0000000000000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<long>(0x0000000000000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<long>(0x7FFFFFFFFFFFFFFF, out result));
+            Assert.Equal(Int64MaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<long>(unchecked((long)0x8000000000000000), out result));
+            Assert.Equal(Int64MinValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<long>(unchecked((long)0xFFFFFFFFFFFFFFFF), out result));
+            Assert.Equal(NegativeOne, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromIntPtrTest()
+        {
+            Int128 result;
+
+            if (Environment.Is64BitProcess)
+            {
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>(unchecked((nint)0x0000000000000000), out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>(unchecked((nint)0x0000000000000001), out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result));
+                Assert.Equal(Int64MaxValue, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>(unchecked((nint)0x8000000000000000), out result));
+                Assert.Equal(Int64MinValue, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result));
+                Assert.Equal(NegativeOne, result);
+            }
+            else
+            {
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>((nint)0x00000000, out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>((nint)0x00000001, out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>((nint)0x7FFFFFFF, out result));
+                Assert.Equal(Int32MaxValue, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>(unchecked((nint)0x80000000), out result));
+                Assert.Equal(Int32MinValue, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nint>(unchecked((nint)0xFFFFFFFF), out result));
+                Assert.Equal(NegativeOne, result);
+            }
+        }
+
+        [Fact]
+        public static void TryCreateFromSByteTest()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<sbyte>(0x00, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<sbyte>(0x01, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<sbyte>(0x7F, out result));
+            Assert.Equal(SByteMaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<sbyte>(unchecked((sbyte)0x80), out result));
+            Assert.Equal(SByteMinValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<sbyte>(unchecked((sbyte)0xFF), out result));
+            Assert.Equal(NegativeOne, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromSingleTest()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(+0.0f, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(-0.0f, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(+float.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(-float.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(+1.0f, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(-1.0f, out result));
+            Assert.Equal(NegativeOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(+170141173319264429905852091742258462720.0f, out result));
+            Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(-170141173319264429905852091742258462720.0f, out result));
+            Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<float>(-170141183460469231731687303715884105728.0f, out result));
+            Assert.Equal(MinValue, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<float>(+170141183460469231731687303715884105728.0f, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<float>(-170141203742878835383357727663135391744.0f, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<float>(float.MaxValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<float>(float.MinValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<float>(float.PositiveInfinity, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<Int128>.TryCreate<float>(float.NegativeInfinity, out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUInt16Test()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ushort>(0x0000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ushort>(0x0001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ushort>(0x7FFF, out result));
+            Assert.Equal(Int16MaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ushort>(0x8000, out result));
+            Assert.Equal(Int16MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ushort>(0xFFFF, out result));
+            Assert.Equal(UInt16MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUInt32Test()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<uint>(0x00000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<uint>(0x00000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<uint>(0x7FFFFFFF, out result));
+            Assert.Equal(Int32MaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<uint>(0x80000000, out result));
+            Assert.Equal(Int32MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<uint>(0xFFFFFFFF, out result));
+            Assert.Equal(UInt32MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUInt64Test()
+        {
+            Int128 result;
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ulong>(0x0000000000000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ulong>(0x0000000000000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ulong>(0x7FFFFFFFFFFFFFFF, out result));
+            Assert.Equal(Int64MaxValue, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ulong>(0x8000000000000000, out result));
+            Assert.Equal(Int64MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<Int128>.TryCreate<ulong>(0xFFFFFFFFFFFFFFFF, out result));
+            Assert.Equal(UInt64MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUIntPtrTest()
+        {
+            Int128 result;
+
+            if (Environment.Is64BitProcess)
+            {
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>(unchecked((nuint)0x0000000000000000), out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>(unchecked((nuint)0x0000000000000001), out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result));
+                Assert.Equal(Int64MaxValue, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>(unchecked((nuint)0x8000000000000000), out result));
+                Assert.Equal(Int64MaxValuePlusOne, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result));
+                Assert.Equal(UInt64MaxValue, result);
+            }
+            else
+            {
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>((nuint)0x00000000, out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>((nuint)0x00000001, out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>((nuint)0x7FFFFFFF, out result));
+                Assert.Equal(Int32MaxValue, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>(unchecked((nuint)0x80000000), out result));
+                Assert.Equal(Int32MaxValuePlusOne, result);
+
+                Assert.True(NumberHelper<Int128>.TryCreate<nuint>(unchecked((nuint)0xFFFFFFFF), out result));
+                Assert.Equal(UInt32MaxValue, result);
+            }
+        }
+
+        //
+        // IShiftOperators
+        //
+
+        [Fact]
+        public static void op_LeftShiftTest()
+        {
+            Assert.Equal(Zero, ShiftOperatorsHelper<Int128, Int128>.op_LeftShift(Zero, 1));
+            Assert.Equal(Two, ShiftOperatorsHelper<Int128, Int128>.op_LeftShift(One, 1));
+            Assert.Equal(NegativeTwo, ShiftOperatorsHelper<Int128, Int128>.op_LeftShift(MaxValue, 1));
+            Assert.Equal(Zero, ShiftOperatorsHelper<Int128, Int128>.op_LeftShift(MinValue, 1));
+            Assert.Equal(NegativeTwo, ShiftOperatorsHelper<Int128, Int128>.op_LeftShift(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_RightShiftTest()
+        {
+            Assert.Equal(Zero, ShiftOperatorsHelper<Int128, Int128>.op_RightShift(Zero, 1));
+            Assert.Equal(Zero, ShiftOperatorsHelper<Int128, Int128>.op_RightShift(One, 1));
+            Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper<Int128, Int128>.op_RightShift(MaxValue, 1));
+            Assert.Equal(new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper<Int128, Int128>.op_RightShift(MinValue, 1));
+            Assert.Equal(NegativeOne, ShiftOperatorsHelper<Int128, Int128>.op_RightShift(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_UnsignedRightShiftTest()
+        {
+            Assert.Equal(Zero, ShiftOperatorsHelper<Int128, Int128>.op_UnsignedRightShift(Zero, 1));
+            Assert.Equal(Zero, ShiftOperatorsHelper<Int128, Int128>.op_UnsignedRightShift(One, 1));
+            Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper<Int128, Int128>.op_UnsignedRightShift(MaxValue, 1));
+            Assert.Equal(new Int128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper<Int128, Int128>.op_UnsignedRightShift(MinValue, 1));
+            Assert.Equal(MaxValue, ShiftOperatorsHelper<Int128, Int128>.op_UnsignedRightShift(NegativeOne, 1));
+        }
+
+        //
+        // ISignedNumber
+        //
+
+        [Fact]
+        public static void NegativeOneTest()
+        {
+            Assert.Equal(NegativeOne, SignedNumberHelper<Int128>.NegativeOne);
+        }
+
+        //
+        // ISubtractionOperators
+        //
+
+        [Fact]
+        public static void op_SubtractionTest()
+        {
+            Assert.Equal(NegativeOne, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_Subtraction(Zero, 1));
+            Assert.Equal(Zero, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_Subtraction(One, 1));
+            Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_Subtraction(MaxValue, 1));
+            Assert.Equal(MaxValue, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_Subtraction(MinValue, 1));
+            Assert.Equal(NegativeTwo, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_Subtraction(NegativeOne, 1));
+        }
+
+        [Fact]
+        public static void op_CheckedSubtractionTest()
+        {
+            Assert.Equal(NegativeOne, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_CheckedSubtraction(Zero, 1));
+            Assert.Equal(Zero, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_CheckedSubtraction(One, 1));
+            Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_CheckedSubtraction(MaxValue, 1));
+            Assert.Equal(NegativeTwo, SubtractionOperatorsHelper<Int128, Int128, Int128>.op_CheckedSubtraction(NegativeOne, 1));
+
+            Assert.Throws<OverflowException>(() => SubtractionOperatorsHelper<Int128, Int128, Int128>.op_CheckedSubtraction(MinValue, 1));
+        }
+
+        //
+        // IUnaryNegationOperators
+        //
+
+        [Fact]
+        public static void op_UnaryNegationTest()
+        {
+            Assert.Equal(Zero, UnaryNegationOperatorsHelper<Int128, Int128>.op_UnaryNegation(Zero));
+            Assert.Equal(NegativeOne, UnaryNegationOperatorsHelper<Int128, Int128>.op_UnaryNegation(One));
+            Assert.Equal(MinValuePlusOne, UnaryNegationOperatorsHelper<Int128, Int128>.op_UnaryNegation(MaxValue));
+            Assert.Equal(MinValue, UnaryNegationOperatorsHelper<Int128, Int128>.op_UnaryNegation(MinValue));
+            Assert.Equal(One, UnaryNegationOperatorsHelper<Int128, Int128>.op_UnaryNegation(NegativeOne));
+        }
+
+        [Fact]
+        public static void op_CheckedUnaryNegationTest()
+        {
+            Assert.Equal(Zero, UnaryNegationOperatorsHelper<Int128, Int128>.op_CheckedUnaryNegation(Zero));
+            Assert.Equal(NegativeOne, UnaryNegationOperatorsHelper<Int128, Int128>.op_CheckedUnaryNegation(One));
+            Assert.Equal(MinValuePlusOne, UnaryNegationOperatorsHelper<Int128, Int128>.op_CheckedUnaryNegation(MaxValue));
+            Assert.Equal(One, UnaryNegationOperatorsHelper<Int128, Int128>.op_CheckedUnaryNegation(NegativeOne));
+
+            Assert.Throws<OverflowException>(() => UnaryNegationOperatorsHelper<Int128, Int128>.op_CheckedUnaryNegation(MinValue));
+        }
+
+        //
+        // IUnaryPlusOperators
+        //
+
+        [Fact]
+        public static void op_UnaryPlusTest()
+        {
+            Assert.Equal(Zero, UnaryPlusOperatorsHelper<Int128, Int128>.op_UnaryPlus(Zero));
+            Assert.Equal(One, UnaryPlusOperatorsHelper<Int128, Int128>.op_UnaryPlus(One));
+            Assert.Equal(MaxValue, UnaryPlusOperatorsHelper<Int128, Int128>.op_UnaryPlus(MaxValue));
+            Assert.Equal(MinValue, UnaryPlusOperatorsHelper<Int128, Int128>.op_UnaryPlus(MinValue));
+            Assert.Equal(NegativeOne, UnaryPlusOperatorsHelper<Int128, Int128>.op_UnaryPlus(NegativeOne));
+        }
+    }
+}
diff --git a/src/libraries/System.Runtime/tests/System/Int128Tests.cs b/src/libraries/System.Runtime/tests/System/Int128Tests.cs
new file mode 100644 (file)
index 0000000..15f1a39
--- /dev/null
@@ -0,0 +1,456 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.Numerics;
+using Xunit;
+
+namespace System.Tests
+{
+    public class Int128Tests
+    {
+        [Fact]
+        public static void Ctor_Empty()
+        {
+            var i = new Int128();
+            Assert.Equal(0, i);
+        }
+
+        [Fact]
+        public static void Ctor_Value()
+        {
+            Int128 i = 41;
+            Assert.Equal(41, i);
+        }
+
+        [Fact]
+        public static void MaxValue()
+        {
+            Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), Int128.MaxValue);
+        }
+
+        [Fact]
+        public static void MinValue()
+        {
+            Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), Int128.MinValue);
+        }
+
+        public static IEnumerable<object[]> CompareTo_Other_ReturnsExpected_TestData()
+        {
+            yield return new object[] { (Int128)234, (Int128)234, 0 };
+            yield return new object[] { (Int128)234, Int128.MinValue, 1 };
+            yield return new object[] { (Int128)(-234), Int128.MinValue, 1 };
+            yield return new object[] { Int128.MinValue, Int128.MinValue, 0 };
+            yield return new object[] { (Int128)234, (Int128)(-123), 1 };
+            yield return new object[] { (Int128)234, (Int128)0, 1 };
+            yield return new object[] { (Int128)234, (Int128)123, 1 };
+            yield return new object[] { (Int128)234, (Int128)456, -1 };
+            yield return new object[] { (Int128)234, Int128.MaxValue, -1 };
+            yield return new object[] { (Int128)(-234), Int128.MaxValue, -1 };
+            yield return new object[] { Int128.MaxValue, Int128.MaxValue, 0 };
+            yield return new object[] { (Int128)(-234), (Int128)(-234), 0 };
+            yield return new object[] { (Int128)(-234), (Int128)234, -1 };
+            yield return new object[] { (Int128)(-234), (Int128)(-432), 1 };
+            yield return new object[] { (Int128)234, null, 1 };
+        }
+
+        [Theory]
+        [MemberData(nameof(CompareTo_Other_ReturnsExpected_TestData))]
+        public void CompareTo_Other_ReturnsExpected(Int128 i, object value, int expected)
+        {
+            if (value is Int128 int128Value)
+            {
+                Assert.Equal(expected, Int128.Sign(i.CompareTo(int128Value)));
+                Assert.Equal(-expected, Int128.Sign(int128Value.CompareTo(i)));
+            }
+
+            Assert.Equal(expected, Int128.Sign(i.CompareTo(value)));
+        }
+
+        public static IEnumerable<object[]> CompareTo_ObjectNotInt128_ThrowsArgumentException_TestData()
+        {
+            yield return new object[] { "a" };
+            yield return new object[] { 234 };
+        }
+
+        [Theory]
+        [MemberData(nameof(CompareTo_ObjectNotInt128_ThrowsArgumentException_TestData))]
+        public void CompareTo_ObjectNotInt128_ThrowsArgumentException(object value)
+        {
+            AssertExtensions.Throws<ArgumentException>(null, () => ((Int128)123).CompareTo(value));
+        }
+
+        public static IEnumerable<object[]> EqualsTest_TestData()
+        {
+            yield return new object[] { (Int128)789, (Int128)789, true };
+            yield return new object[] { (Int128)789, (Int128)(-789), false };
+            yield return new object[] { (Int128)789, (Int128)0, false };
+            yield return new object[] { (Int128)0, (Int128)0, true };
+            yield return new object[] { (Int128)(-789), (Int128)(-789), true };
+            yield return new object[] { (Int128)(-789), (Int128)789, false };
+            yield return new object[] { (Int128)789, null, false };
+            yield return new object[] { (Int128)789, "789", false };
+            yield return new object[] { (Int128)789, 789, false };
+        }
+
+        [Theory]
+        [MemberData(nameof(EqualsTest_TestData))]
+        public static void EqualsTest(Int128 i1, object obj, bool expected)
+        {
+            if (obj is Int128 i2)
+            {
+                Assert.Equal(expected, i1.Equals(i2));
+                Assert.Equal(expected, i1.GetHashCode().Equals(i2.GetHashCode()));
+            }
+            Assert.Equal(expected, i1.Equals(obj));
+        }
+
+        public static IEnumerable<object[]> ToString_TestData()
+        {
+            foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo })
+            {
+                foreach (string defaultSpecifier in new[] { "G", "G\0", "\0N222", "\0", "", "R" })
+                {
+                    yield return new object[] { Int128.MinValue, defaultSpecifier, defaultFormat, "-170141183460469231731687303715884105728" };
+                    yield return new object[] { (Int128)(-4567), defaultSpecifier, defaultFormat, "-4567" };
+                    yield return new object[] { (Int128)0, defaultSpecifier, defaultFormat, "0" };
+                    yield return new object[] { (Int128)4567, defaultSpecifier, defaultFormat, "4567" };
+                    yield return new object[] { Int128.MaxValue, defaultSpecifier, defaultFormat, "170141183460469231731687303715884105727" };
+                }
+
+                yield return new object[] { (Int128)4567, "D", defaultFormat, "4567" };
+                yield return new object[] { (Int128)4567, "D99", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" };
+                yield return new object[] { (Int128)4567, "D99\09", defaultFormat, "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" };
+                yield return new object[] { (Int128)(-4567), "D99", defaultFormat, "-000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004567" };
+
+                yield return new object[] { (Int128)0x2468, "x", defaultFormat, "2468" };
+                yield return new object[] { (Int128)(-0x2468), "x", defaultFormat, "ffffffffffffffffffffffffffffdb98" };
+                yield return new object[] { (Int128)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) };
+            }
+
+            NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
+            yield return new object[] { (Int128)32, "C100", invariantFormat, "¤32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
+            yield return new object[] { (Int128)32, "P100", invariantFormat, "3,200.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
+            yield return new object[] { (Int128)32, "D100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" };
+            yield return new object[] { (Int128)32, "E100", invariantFormat, "3.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
+            yield return new object[] { (Int128)32, "F100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
+            yield return new object[] { (Int128)32, "N100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
+            yield return new object[] { (Int128)32, "X100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020" };
+
+            var customFormat = new NumberFormatInfo()
+            {
+                NegativeSign = "#",
+                NumberDecimalSeparator = "~",
+                NumberGroupSeparator = "*",
+                PositiveSign = "&",
+                NumberDecimalDigits = 2,
+                PercentSymbol = "@",
+                PercentGroupSeparator = ",",
+                PercentDecimalSeparator = ".",
+                PercentDecimalDigits = 5
+            };
+            yield return new object[] { (Int128)(-2468), "N", customFormat, "#2*468~00" };
+            yield return new object[] { (Int128)2468, "N", customFormat, "2*468~00" };
+            yield return new object[] { (Int128)123, "E", customFormat, "1~230000E&002" };
+            yield return new object[] { (Int128)123, "F", customFormat, "123~00" };
+            yield return new object[] { (Int128)123, "P", customFormat, "12,300.00000 @" };
+        }
+
+        [Theory]
+        [MemberData(nameof(ToString_TestData))]
+        public static void ToStringTest(Int128 i, string format, IFormatProvider provider, string expected)
+        {
+            // Format is case insensitive
+            string upperFormat = format.ToUpperInvariant();
+            string lowerFormat = format.ToLowerInvariant();
+
+            string upperExpected = expected.ToUpperInvariant();
+            string lowerExpected = expected.ToLowerInvariant();
+
+            bool isDefaultProvider = (provider is null) || (provider == NumberFormatInfo.CurrentInfo);
+
+            if (string.IsNullOrEmpty(format) || (format.ToUpperInvariant() is "G" or "R"))
+            {
+                if (isDefaultProvider)
+                {
+                    Assert.Equal(upperExpected, i.ToString());
+                    Assert.Equal(upperExpected, i.ToString((IFormatProvider)null));
+                }
+                Assert.Equal(upperExpected, i.ToString(provider));
+            }
+
+            if (isDefaultProvider)
+            {
+                Assert.Equal(upperExpected, i.ToString(upperFormat));
+                Assert.Equal(lowerExpected, i.ToString(lowerFormat));
+                Assert.Equal(upperExpected, i.ToString(upperFormat, null));
+                Assert.Equal(lowerExpected, i.ToString(lowerFormat, null));
+            }
+
+            Assert.Equal(upperExpected, i.ToString(upperFormat, provider));
+            Assert.Equal(lowerExpected, i.ToString(lowerFormat, provider));
+        }
+
+        [Fact]
+        public static void ToString_InvalidFormat_ThrowsFormatException()
+        {
+            Int128 i = 123;
+            Assert.Throws<FormatException>(() => i.ToString("Y")); // Invalid format
+            Assert.Throws<FormatException>(() => i.ToString("Y", null)); // Invalid format
+        }
+
+        public static IEnumerable<object[]> Parse_Valid_TestData()
+        {
+            // Reuse all Int64 test data
+            foreach (object[] objs in Int64Tests.Parse_Valid_TestData())
+            {
+                bool unsigned = (((NumberStyles)objs[1]) & NumberStyles.HexNumber) == NumberStyles.HexNumber;
+                yield return new object[] { objs[0], objs[1], objs[2], unsigned ? (Int128)(ulong)(long)objs[3] : (Int128)(long)objs[3] };
+            }
+
+            // All lengths decimal
+            foreach (bool neg in new[] { false, true })
+            {
+                string s = neg ? "-" : "";
+                Int128 result = 0;
+                for (int i = 1; i <= 19; i++)
+                {
+                    result = (result * 10) + (i % 10);
+                    s += (i % 10).ToString();
+                    yield return new object[] { s, NumberStyles.Integer, null, neg ? result * -1 : result };
+                }
+            }
+
+            // All lengths hexadecimal
+            {
+                string s = "";
+                Int128 result = 0;
+                for (int i = 1; i <= 16; i++)
+                {
+                    result = (result * 16) + (i % 16);
+                    s += (i % 16).ToString("X");
+                    yield return new object[] { s, NumberStyles.HexNumber, null, result };
+                }
+            }
+
+            // And test boundary conditions for Int128
+            yield return new object[] { "-170141183460469231731687303715884105728", NumberStyles.Integer, null, Int128.MinValue };
+            yield return new object[] { "170141183460469231731687303715884105727", NumberStyles.Integer, null, Int128.MaxValue };
+            yield return new object[] { "   -170141183460469231731687303715884105728   ", NumberStyles.Integer, null, Int128.MinValue };
+            yield return new object[] { "   +170141183460469231731687303715884105727   ", NumberStyles.Integer, null, Int128.MaxValue };
+            yield return new object[] { "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber, null, Int128.MaxValue };
+            yield return new object[] { "80000000000000000000000000000000", NumberStyles.HexNumber, null, Int128.MinValue };
+            yield return new object[] { "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber, null, (Int128)(-1) };
+            yield return new object[] { "   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF  ", NumberStyles.HexNumber, null, (Int128)(-1) };
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_Valid_TestData))]
+        public static void Parse_Valid(string value, NumberStyles style, IFormatProvider provider, Int128 expected)
+        {
+            Int128 result;
+
+            // Default style and provider
+            if ((style == NumberStyles.Integer) && (provider is null))
+            {
+                Assert.True(Int128.TryParse(value, out result));
+                Assert.Equal(expected, result);
+                Assert.Equal(expected, Int128.Parse(value));
+            }
+
+            // Default provider
+            if (provider is null)
+            {
+                Assert.Equal(expected, Int128.Parse(value, style));
+
+                // Substitute default NumberFormatInfo
+                Assert.True(Int128.TryParse(value, style, new NumberFormatInfo(), out result));
+                Assert.Equal(expected, result);
+                Assert.Equal(expected, Int128.Parse(value, style, new NumberFormatInfo()));
+            }
+
+            // Default style
+            if (style == NumberStyles.Integer)
+            {
+                Assert.Equal(expected, Int128.Parse(value, provider));
+            }
+
+            // Full overloads
+            Assert.True(Int128.TryParse(value, style, provider, out result));
+            Assert.Equal(expected, result);
+            Assert.Equal(expected, Int128.Parse(value, style, provider));
+        }
+
+        public static IEnumerable<object[]> Parse_Invalid_TestData()
+        {
+            // Reuse all int test data, except for those that wouldn't overflow Int128.
+            foreach (object[] objs in Int32Tests.Parse_Invalid_TestData())
+            {
+                if ((Type)objs[3] == typeof(OverflowException) &&
+                    (!BigInteger.TryParse((string)objs[0], out BigInteger bi) || (bi >= Int128.MinValue && bi <= Int128.MaxValue)))
+                {
+                    continue;
+                }
+                yield return objs;
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_Invalid_TestData))]
+        public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType)
+        {
+            Int128 result;
+
+            // Default style and provider
+            if ((style == NumberStyles.Integer) && (provider is null))
+            {
+                Assert.False(Int128.TryParse(value, out result));
+                Assert.Equal(default, result);
+                Assert.Throws(exceptionType, () => Int128.Parse(value));
+            }
+
+            // Default provider
+            if (provider is null)
+            {
+                Assert.Throws(exceptionType, () => Int128.Parse(value, style));
+
+                // Substitute default NumberFormatInfo
+                Assert.False(Int128.TryParse(value, style, new NumberFormatInfo(), out result));
+                Assert.Equal(default, result);
+                Assert.Throws(exceptionType, () => Int128.Parse(value, style, new NumberFormatInfo()));
+            }
+
+            // Default style
+            if (style == NumberStyles.Integer)
+            {
+                Assert.Throws(exceptionType, () => Int128.Parse(value, provider));
+            }
+
+            // Full overloads
+            Assert.False(Int128.TryParse(value, style, provider, out result));
+            Assert.Equal(default, result);
+            Assert.Throws(exceptionType, () => Int128.Parse(value, style, provider));
+        }
+
+        [Theory]
+        [InlineData(NumberStyles.HexNumber | NumberStyles.AllowParentheses, null)]
+        [InlineData(unchecked((NumberStyles)0xFFFFFC00), "style")]
+        public static void TryParse_InvalidNumberStyle_ThrowsArgumentException(NumberStyles style, string paramName)
+        {
+            Int128 result = 0;
+            AssertExtensions.Throws<ArgumentException>(paramName, () => Int128.TryParse("1", style, null, out result));
+            Assert.Equal(default(Int128), result);
+
+            AssertExtensions.Throws<ArgumentException>(paramName, () => Int128.Parse("1", style));
+            AssertExtensions.Throws<ArgumentException>(paramName, () => Int128.Parse("1", style, null));
+        }
+
+        public static IEnumerable<object[]> Parse_ValidWithOffsetCount_TestData()
+        {
+            foreach (object[] inputs in Parse_Valid_TestData())
+            {
+                yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] };
+            }
+
+            yield return new object[] { "-170141183460469231731687303715884105728", 0, 39, NumberStyles.Integer, null, new Int128(0xF333_3333_3333_3333, 0x3333_3333_3333_3334) };
+            yield return new object[] { "0170141183460469231731687303715884105727", 1, 39, NumberStyles.Integer, null, new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF) };
+            yield return new object[] { "170141183460469231731687303715884105727", 0, 1, NumberStyles.Integer, null, 1 };
+            yield return new object[] { "ABC", 0, 2, NumberStyles.HexNumber, null, (Int128)0xAB };
+            yield return new object[] { "(123)", 1, 3, NumberStyles.AllowParentheses, null, (Int128)123 };
+            yield return new object[] { "$1,000", 0, 2, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$" }, (Int128)1 };
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))]
+        public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, Int128 expected)
+        {
+            Int128 result;
+
+            // Default style and provider
+            if ((style == NumberStyles.Integer) && (provider is null))
+            {
+                Assert.True(Int128.TryParse(value.AsSpan(offset, count), out result));
+                Assert.Equal(expected, result);
+            }
+
+            Assert.Equal(expected, Int128.Parse(value.AsSpan(offset, count), style, provider));
+
+            Assert.True(Int128.TryParse(value.AsSpan(offset, count), style, provider, out result));
+            Assert.Equal(expected, result);
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_Invalid_TestData))]
+        public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType)
+        {
+            if (value is not null)
+            {
+                Int128 result;
+
+                // Default style and provider
+                if ((style == NumberStyles.Integer) && (provider is null))
+                {
+                    Assert.False(Int128.TryParse(value.AsSpan(), out result));
+                    Assert.Equal(0, result);
+                }
+
+                Assert.Throws(exceptionType, () => Int128.Parse(value.AsSpan(), style, provider));
+
+                Assert.False(Int128.TryParse(value.AsSpan(), style, provider, out result));
+                Assert.Equal(0, result);
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(ToString_TestData))]
+        public static void TryFormat(Int128 i, string format, IFormatProvider provider, string expected)
+        {
+            char[] actual;
+            int charsWritten;
+
+            // Just right
+            actual = new char[expected.Length];
+            Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider));
+            Assert.Equal(expected.Length, charsWritten);
+            Assert.Equal(expected, new string(actual));
+
+            // Longer than needed
+            actual = new char[expected.Length + 1];
+            Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider));
+            Assert.Equal(expected.Length, charsWritten);
+            Assert.Equal(expected, new string(actual, 0, charsWritten));
+
+            // Too short
+            if (expected.Length > 0)
+            {
+                actual = new char[expected.Length - 1];
+                Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider));
+                Assert.Equal(0, charsWritten);
+            }
+
+            if (format is not null)
+            {
+                // Upper format
+                actual = new char[expected.Length];
+                Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider));
+                Assert.Equal(expected.Length, charsWritten);
+                Assert.Equal(expected.ToUpperInvariant(), new string(actual));
+
+                // Lower format
+                actual = new char[expected.Length];
+                Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider));
+                Assert.Equal(expected.Length, charsWritten);
+                Assert.Equal(expected.ToLowerInvariant(), new string(actual));
+            }
+        }
+
+        [Fact]
+        public static void TestNegativeNumberParsingWithHyphen()
+        {
+            // CLDR data for Swedish culture has negative sign U+2212. This test ensure parsing with the hyphen with such cultures will succeed.
+            CultureInfo ci = CultureInfo.GetCultureInfo("sv-SE");
+            Assert.Equal(-15868, Int128.Parse("-15868", NumberStyles.Number, ci));
+        }
+    }
+}
index 7bc92dd..a5cf58f 100644 (file)
@@ -543,6 +543,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromInt128Test()
+        {
+            AssertBitwiseEqual(0.0f, NumberHelper<float>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0f, NumberHelper<float>.CreateChecked<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper<float>.CreateChecked<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(-170141183460469231731687303715884105728.0f, NumberHelper<float>.CreateChecked<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(-1.0f, NumberHelper<float>.CreateChecked<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -604,6 +614,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateCheckedFromUInt128Test()
+        {
+            AssertBitwiseEqual(0.0f, NumberHelper<float>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0f, NumberHelper<float>.CreateChecked<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper<float>.CreateChecked<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(170141183460469231731687303715884105728.0f, NumberHelper<float>.CreateChecked<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(float.PositiveInfinity, NumberHelper<float>.CreateChecked<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateCheckedFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -679,6 +699,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromInt128Test()
+        {
+            AssertBitwiseEqual(0.0f, NumberHelper<float>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0f, NumberHelper<float>.CreateSaturating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper<float>.CreateSaturating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(-170141183460469231731687303715884105728.0f, NumberHelper<float>.CreateSaturating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(-1.0f, NumberHelper<float>.CreateSaturating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -740,6 +770,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateSaturatingFromUInt128Test()
+        {
+            AssertBitwiseEqual(0.0f, NumberHelper<float>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0f, NumberHelper<float>.CreateSaturating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper<float>.CreateSaturating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(170141183460469231731687303715884105728.0f, NumberHelper<float>.CreateSaturating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(float.PositiveInfinity, NumberHelper<float>.CreateSaturating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateSaturatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -815,6 +855,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromInt128Test()
+        {
+            AssertBitwiseEqual(0.0f, NumberHelper<float>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0f, NumberHelper<float>.CreateTruncating<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper<float>.CreateTruncating<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(-170141183460469231731687303715884105728.0f, NumberHelper<float>.CreateTruncating<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(-1.0f, NumberHelper<float>.CreateTruncating<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -876,6 +926,16 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void CreateTruncatingFromUInt128Test()
+        {
+            AssertBitwiseEqual(0.0f, NumberHelper<float>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(1.0f, NumberHelper<float>.CreateTruncating<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001)));
+            AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper<float>.CreateTruncating<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+            AssertBitwiseEqual(170141183460469231731687303715884105728.0f, NumberHelper<float>.CreateTruncating<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000)));
+            AssertBitwiseEqual(float.PositiveInfinity, NumberHelper<float>.CreateTruncating<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)));
+        }
+
+        [Fact]
         public static void CreateTruncatingFromUIntPtrTest()
         {
             if (Environment.Is64BitProcess)
@@ -1069,6 +1129,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromInt128Test()
+        {
+            float result;
+
+            Assert.True(NumberHelper<float>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<Int128>(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(1.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<Int128>(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(170141183460469231731687303715884105727.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<Int128>(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(-170141183460469231731687303715884105728.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<Int128>(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(-1.0f, result);
+        }
+
+        [Fact]
         public static void TryCreateFromIntPtrTest()
         {
             float result;
@@ -1194,6 +1275,27 @@ namespace System.Tests
         }
 
         [Fact]
+        public static void TryCreateFromUInt128Test()
+        {
+            float result;
+
+            Assert.True(NumberHelper<float>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(0.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<UInt128>(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result));
+            Assert.Equal(1.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<UInt128>(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(170141183460469231731687303715884105727.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<UInt128>(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result));
+            Assert.Equal(170141183460469231731687303715884105728.0f, result);
+
+            Assert.True(NumberHelper<float>.TryCreate<UInt128>(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result));
+            Assert.Equal(float.PositiveInfinity, result);
+        }
+
+        [Fact]
         public static void TryCreateFromUIntPtrTest()
         {
             float result;
diff --git a/src/libraries/System.Runtime/tests/System/UInt128Tests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/UInt128Tests.GenericMath.cs
new file mode 100644 (file)
index 0000000..8177f0f
--- /dev/null
@@ -0,0 +1,1695 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Globalization;
+using Xunit;
+
+namespace System.Tests
+{
+    public class UInt128Tests_GenericMath
+    {
+        internal static readonly UInt128 ByteMaxValue = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_00FF);
+
+        internal static readonly UInt128 Int16MaxValue = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_7FFF);
+
+        internal static readonly UInt128 Int16MaxValuePlusOne = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_8000);
+
+        internal static readonly UInt128 Int16MinValue = new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_8000);
+
+        internal static readonly UInt128 Int32MaxValue = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_7FFF_FFFF);
+
+        internal static readonly UInt128 Int32MaxValuePlusOne = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_8000_0000);
+
+        internal static readonly UInt128 Int32MinValue = new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_8000_0000);
+
+        internal static readonly UInt128 Int64MaxValue = new UInt128(0x0000_0000_0000_0000, 0x7FFF_FFFF_FFFF_FFFF);
+
+        internal static readonly UInt128 Int64MaxValuePlusOne = new UInt128(0x0000_0000_0000_0000, 0x8000_0000_0000_0000);
+
+        internal static readonly UInt128 Int64MinValue = new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0x8000_0000_0000_0000);
+
+        internal static readonly UInt128 Int128MaxValue = new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+        internal static readonly UInt128 Int128MaxValueMinusOne = new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE);
+
+        internal static readonly UInt128 Int128MaxValuePlusOne = new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+        internal static readonly UInt128 Int128MaxValuePlusTwo = new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001);
+
+        internal static readonly UInt128 MaxValue = new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+        internal static readonly UInt128 MaxValueMinusOne = new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE);
+
+        internal static readonly UInt128 One = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001);
+
+        internal static readonly UInt128 SByteMaxValue = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_007F);
+
+        internal static readonly UInt128 SByteMaxValuePlusOne = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0080);
+
+        internal static readonly UInt128 SByteMinValue = new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80);
+
+        internal static readonly UInt128 Two = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0002);
+
+        internal static readonly UInt128 UInt16MaxValue = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_FFFF);
+
+        internal static readonly UInt128 UInt32MaxValue = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_FFFF_FFFF);
+
+        internal static readonly UInt128 UInt64MaxValue = new UInt128(0x0000_0000_0000_0000, 0xFFFF_FFFF_FFFF_FFFF);
+
+        internal static readonly UInt128 Zero = new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+        //
+        // IAdditionOperators
+        //
+
+        [Fact]
+        public static void op_AdditionTest()
+        {
+            Assert.Equal(One, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_Addition(Zero, 1U));
+            Assert.Equal(Two, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_Addition(One, 1U));
+            Assert.Equal(Int128MaxValuePlusOne, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_Addition(Int128MaxValue, 1U));
+            Assert.Equal(Int128MaxValuePlusTwo, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_Addition(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(Zero, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_Addition(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_CheckedAdditionTest()
+        {
+            Assert.Equal(One, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedAddition(Zero, 1U));
+            Assert.Equal(Two, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedAddition(One, 1U));
+            Assert.Equal(Int128MaxValuePlusOne, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedAddition(Int128MaxValue, 1U));
+            Assert.Equal(Int128MaxValuePlusTwo, AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedAddition(Int128MaxValuePlusOne, 1U));
+
+            Assert.Throws<OverflowException>(() => AdditionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedAddition(MaxValue, 1U));
+        }
+
+        //
+        // IAdditiveIdentity
+        //
+
+        [Fact]
+        public static void AdditiveIdentityTest()
+        {
+            Assert.Equal(Zero, AdditiveIdentityHelper<UInt128, UInt128>.AdditiveIdentity);
+        }
+
+        //
+        // IBinaryInteger
+        //
+
+        [Fact]
+        public static void DivRemTest()
+        {
+            Assert.Equal((Zero, Zero), BinaryIntegerHelper<UInt128>.DivRem(Zero, 2U));
+            Assert.Equal((Zero, One), BinaryIntegerHelper<UInt128>.DivRem(One, 2U));
+            Assert.Equal((new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), One), BinaryIntegerHelper<UInt128>.DivRem(Int128MaxValue, 2U));
+            Assert.Equal((new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), Zero), BinaryIntegerHelper<UInt128>.DivRem(Int128MaxValuePlusOne, 2U));
+            Assert.Equal((Int128MaxValue, One), BinaryIntegerHelper<UInt128>.DivRem(MaxValue, 2U));
+        }
+
+        [Fact]
+        public static void LeadingZeroCountTest()
+        {
+            Assert.Equal(0x80U, BinaryIntegerHelper<UInt128>.LeadingZeroCount(Zero));
+            Assert.Equal(0x7FU, BinaryIntegerHelper<UInt128>.LeadingZeroCount(One));
+            Assert.Equal(0x01U, BinaryIntegerHelper<UInt128>.LeadingZeroCount(Int128MaxValue));
+            Assert.Equal(0x00U, BinaryIntegerHelper<UInt128>.LeadingZeroCount(Int128MaxValuePlusOne));
+            Assert.Equal(0x00U, BinaryIntegerHelper<UInt128>.LeadingZeroCount(MaxValue));
+        }
+
+        [Fact]
+        public static void PopCountTest()
+        {
+            Assert.Equal(0x00U, BinaryIntegerHelper<UInt128>.PopCount(Zero));
+            Assert.Equal(0x01U, BinaryIntegerHelper<UInt128>.PopCount(One));
+            Assert.Equal(0x7FU, BinaryIntegerHelper<UInt128>.PopCount(Int128MaxValue));
+            Assert.Equal(0x01U, BinaryIntegerHelper<UInt128>.PopCount(Int128MaxValuePlusOne));
+            Assert.Equal(0x80U, BinaryIntegerHelper<UInt128>.PopCount(MaxValue));
+        }
+
+        [Fact]
+        public static void RotateLeftTest()
+        {
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<UInt128>.RotateLeft(Zero, 1));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0002), BinaryIntegerHelper<UInt128>.RotateLeft(One, 1));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BinaryIntegerHelper<UInt128>.RotateLeft(Int128MaxValue, 1));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BinaryIntegerHelper<UInt128>.RotateLeft(Int128MaxValuePlusOne, 1));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper<UInt128>.RotateLeft(MaxValue, 1));
+        }
+
+        [Fact]
+        public static void RotateRightTest()
+        {
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<UInt128>.RotateRight(Zero, 1));
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<UInt128>.RotateRight(One, 1));
+            Assert.Equal(new UInt128(0xBFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper<UInt128>.RotateRight(Int128MaxValue, 1));
+            Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper<UInt128>.RotateRight(Int128MaxValuePlusOne, 1));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper<UInt128>.RotateRight(MaxValue, 1));
+        }
+
+        [Fact]
+        public static void TrailingZeroCountTest()
+        {
+            Assert.Equal(0x80U, BinaryIntegerHelper<UInt128>.TrailingZeroCount(Zero));
+            Assert.Equal(0x00U, BinaryIntegerHelper<UInt128>.TrailingZeroCount(One));
+            Assert.Equal(0x00U, BinaryIntegerHelper<UInt128>.TrailingZeroCount(Int128MaxValue));
+            Assert.Equal(0x7FU, BinaryIntegerHelper<UInt128>.TrailingZeroCount(Int128MaxValuePlusOne));
+            Assert.Equal(0x00U, BinaryIntegerHelper<UInt128>.TrailingZeroCount(MaxValue));
+        }
+
+        [Fact]
+        public static void GetByteCountTest()
+        {
+            Assert.Equal(16, BinaryIntegerHelper<UInt128>.GetByteCount(Zero));
+            Assert.Equal(16, BinaryIntegerHelper<UInt128>.GetByteCount(One));
+            Assert.Equal(16, BinaryIntegerHelper<UInt128>.GetByteCount(Int128MaxValue));
+            Assert.Equal(16, BinaryIntegerHelper<UInt128>.GetByteCount(Int128MaxValuePlusOne));
+            Assert.Equal(16, BinaryIntegerHelper<UInt128>.GetByteCount(MaxValue));
+        }
+
+        [Fact]
+        public static void GetShortestBitLengthTest()
+        {
+            Assert.Equal(0x00, BinaryIntegerHelper<UInt128>.GetShortestBitLength(Zero));
+            Assert.Equal(0x01, BinaryIntegerHelper<UInt128>.GetShortestBitLength(One));
+            Assert.Equal(0x7F, BinaryIntegerHelper<UInt128>.GetShortestBitLength(Int128MaxValue));
+            Assert.Equal(0x80, BinaryIntegerHelper<UInt128>.GetShortestBitLength(Int128MaxValuePlusOne));
+            Assert.Equal(0x80, BinaryIntegerHelper<UInt128>.GetShortestBitLength(MaxValue));
+        }
+
+        [Fact]
+        public static void TryWriteLittleEndianTest()
+        {
+            Span<byte> destination = stackalloc byte[16];
+            int bytesWritten = 0;
+
+            Assert.True(BinaryIntegerHelper<UInt128>.TryWriteLittleEndian(Zero, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<UInt128>.TryWriteLittleEndian(One, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<UInt128>.TryWriteLittleEndian(Int128MaxValue, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<UInt128>.TryWriteLittleEndian(Int128MaxValuePlusOne, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80 }, destination.ToArray());
+
+            Assert.True(BinaryIntegerHelper<UInt128>.TryWriteLittleEndian(MaxValue, destination, out bytesWritten));
+            Assert.Equal(16, bytesWritten);
+            Assert.Equal(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, destination.ToArray());
+
+            Assert.False(BinaryIntegerHelper<UInt128>.TryWriteLittleEndian(default, Span<byte>.Empty, out bytesWritten));
+            Assert.Equal(0, bytesWritten);
+            Assert.Equal(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }, destination.ToArray());
+        }
+
+        //
+        // IBinaryNumber
+        //
+
+        [Fact]
+        public static void IsPow2Test()
+        {
+            Assert.False(BinaryNumberHelper<UInt128>.IsPow2(Zero));
+            Assert.True(BinaryNumberHelper<UInt128>.IsPow2(One));
+            Assert.False(BinaryNumberHelper<UInt128>.IsPow2(Int128MaxValue));
+            Assert.True(BinaryNumberHelper<UInt128>.IsPow2(Int128MaxValuePlusOne));
+            Assert.False(BinaryNumberHelper<UInt128>.IsPow2(MaxValue));
+        }
+
+        [Fact]
+        public static void Log2Test()
+        {
+            Assert.Equal(0x00U, BinaryNumberHelper<UInt128>.Log2(Zero));
+            Assert.Equal(0x00U, BinaryNumberHelper<UInt128>.Log2(One));
+            Assert.Equal(0x7EU, BinaryNumberHelper<UInt128>.Log2(Int128MaxValue));
+            Assert.Equal(0x7FU, BinaryNumberHelper<UInt128>.Log2(Int128MaxValuePlusOne));
+            Assert.Equal(0x7FU, BinaryNumberHelper<UInt128>.Log2(MaxValue));
+        }
+
+        //
+        // IBitwiseOperators
+        //
+
+        [Fact]
+        public static void op_BitwiseAndTest()
+        {
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseAnd(Zero, 1U));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseAnd(One, 1U));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseAnd(Int128MaxValue, 1U));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseAnd(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseAnd(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_BitwiseOrTest()
+        {
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseOr(Zero, 1U));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseOr(One, 1U));
+            Assert.Equal(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseOr(Int128MaxValue, 1U));
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseOr(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_BitwiseOr(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_ExclusiveOrTest()
+        {
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_ExclusiveOr(Zero, 1U));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_ExclusiveOr(One, 1U));
+            Assert.Equal(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_ExclusiveOr(Int128MaxValue, 1U));
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_ExclusiveOr(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_ExclusiveOr(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_OnesComplementTest()
+        {
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_OnesComplement(Zero));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_OnesComplement(One));
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_OnesComplement(Int128MaxValue));
+            Assert.Equal(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_OnesComplement(Int128MaxValuePlusOne));
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper<UInt128, UInt128, UInt128>.op_OnesComplement(MaxValue));
+        }
+
+        //
+        // IComparisonOperators
+        //
+
+        [Fact]
+        public static void op_GreaterThanTest()
+        {
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThan(Zero, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThan(One, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThan(Int128MaxValue, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThan(Int128MaxValuePlusOne, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThan(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_GreaterThanOrEqualTest()
+        {
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThanOrEqual(Zero, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThanOrEqual(One, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThanOrEqual(Int128MaxValue, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThanOrEqual(Int128MaxValuePlusOne, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_GreaterThanOrEqual(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_LessThanTest()
+        {
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThan(Zero, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThan(One, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThan(Int128MaxValue, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThan(Int128MaxValuePlusOne, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThan(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_LessThanOrEqualTest()
+        {
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThanOrEqual(Zero, 1U));
+            Assert.True(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThanOrEqual(One, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThanOrEqual(Int128MaxValue, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThanOrEqual(Int128MaxValuePlusOne, 1U));
+            Assert.False(ComparisonOperatorsHelper<UInt128, UInt128>.op_LessThanOrEqual(MaxValue, 1U));
+        }
+
+        //
+        // IDecrementOperators
+        //
+
+        [Fact]
+        public static void op_DecrementTest()
+        {
+            Assert.Equal(MaxValue, DecrementOperatorsHelper<UInt128>.op_Decrement(Zero));
+            Assert.Equal(Zero, DecrementOperatorsHelper<UInt128>.op_Decrement(One));
+            Assert.Equal(Int128MaxValueMinusOne, DecrementOperatorsHelper<UInt128>.op_Decrement(Int128MaxValue));
+            Assert.Equal(Int128MaxValue, DecrementOperatorsHelper<UInt128>.op_Decrement(Int128MaxValuePlusOne));
+            Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper<UInt128>.op_Decrement(MaxValue));
+        }
+
+        [Fact]
+        public static void op_CheckedDecrementTest()
+        {
+            Assert.Equal(Zero, DecrementOperatorsHelper<UInt128>.op_CheckedDecrement(One));
+            Assert.Equal(Int128MaxValueMinusOne, DecrementOperatorsHelper<UInt128>.op_CheckedDecrement(Int128MaxValue));
+            Assert.Equal(Int128MaxValue, DecrementOperatorsHelper<UInt128>.op_CheckedDecrement(Int128MaxValuePlusOne));
+            Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper<UInt128>.op_CheckedDecrement(MaxValue));
+
+            Assert.Throws<OverflowException>(() => DecrementOperatorsHelper<UInt128>.op_CheckedDecrement(Zero));
+        }
+
+        //
+        // IDivisionOperators
+        //
+
+        [Fact]
+        public static void op_DivisionTest()
+        {
+            Assert.Equal(Zero, DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_Division(Zero, 2U));
+            Assert.Equal(Zero, DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_Division(One, 2U));
+            Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_Division(Int128MaxValue, 2U));
+            Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_Division(Int128MaxValuePlusOne, 2U));
+            Assert.Equal(Int128MaxValue, DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_Division(MaxValue, 2U));
+
+            Assert.Throws<DivideByZeroException>(() => DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_Division(One, 0U));
+        }
+
+        [Fact]
+        public static void op_CheckedDivisionTest()
+        {
+            Assert.Equal(Zero, DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedDivision(Zero, 2U));
+            Assert.Equal(Zero, DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedDivision(One, 2U));
+            Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedDivision(Int128MaxValue, 2U));
+            Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedDivision(Int128MaxValuePlusOne, 2U));
+            Assert.Equal(Int128MaxValue, DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedDivision(MaxValue, 2U));
+
+            Assert.Throws<DivideByZeroException>(() => DivisionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedDivision(One, 0U));
+        }
+
+        //
+        // IEqualityOperators
+        //
+
+        [Fact]
+        public static void op_EqualityTest()
+        {
+            Assert.False(EqualityOperatorsHelper<UInt128, UInt128>.op_Equality(Zero, 1U));
+            Assert.True(EqualityOperatorsHelper<UInt128, UInt128>.op_Equality(One, 1U));
+            Assert.False(EqualityOperatorsHelper<UInt128, UInt128>.op_Equality(Int128MaxValue, 1U));
+            Assert.False(EqualityOperatorsHelper<UInt128, UInt128>.op_Equality(Int128MaxValuePlusOne, 1U));
+            Assert.False(EqualityOperatorsHelper<UInt128, UInt128>.op_Equality(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_InequalityTest()
+        {
+            Assert.True(EqualityOperatorsHelper<UInt128, UInt128>.op_Inequality(Zero, 1U));
+            Assert.False(EqualityOperatorsHelper<UInt128, UInt128>.op_Inequality(One, 1U));
+            Assert.True(EqualityOperatorsHelper<UInt128, UInt128>.op_Inequality(Int128MaxValue, 1U));
+            Assert.True(EqualityOperatorsHelper<UInt128, UInt128>.op_Inequality(Int128MaxValuePlusOne, 1U));
+            Assert.True(EqualityOperatorsHelper<UInt128, UInt128>.op_Inequality(MaxValue, 1U));
+        }
+
+        //
+        // IIncrementOperators
+        //
+
+        [Fact]
+        public static void op_IncrementTest()
+        {
+            Assert.Equal(One, IncrementOperatorsHelper<UInt128>.op_Increment(Zero));
+            Assert.Equal(Two, IncrementOperatorsHelper<UInt128>.op_Increment(One));
+            Assert.Equal(Int128MaxValuePlusOne, IncrementOperatorsHelper<UInt128>.op_Increment(Int128MaxValue));
+            Assert.Equal(Int128MaxValuePlusTwo, IncrementOperatorsHelper<UInt128>.op_Increment(Int128MaxValuePlusOne));
+            Assert.Equal(Zero, IncrementOperatorsHelper<UInt128>.op_Increment(MaxValue));
+        }
+
+        [Fact]
+        public static void op_CheckedIncrementTest()
+        {
+            Assert.Equal(One, IncrementOperatorsHelper<UInt128>.op_CheckedIncrement(Zero));
+            Assert.Equal(Two, IncrementOperatorsHelper<UInt128>.op_CheckedIncrement(One));
+            Assert.Equal(Int128MaxValuePlusOne, IncrementOperatorsHelper<UInt128>.op_CheckedIncrement(Int128MaxValue));
+            Assert.Equal(Int128MaxValuePlusTwo, IncrementOperatorsHelper<UInt128>.op_CheckedIncrement(Int128MaxValuePlusOne));
+
+            Assert.Throws<OverflowException>(() => IncrementOperatorsHelper<UInt128>.op_CheckedIncrement(MaxValue));
+        }
+
+        //
+        // IMinMaxValue
+        //
+
+        [Fact]
+        public static void MaxValueTest()
+        {
+            Assert.Equal(MaxValue, MinMaxValueHelper<UInt128>.MaxValue);
+        }
+
+        [Fact]
+        public static void MinValueTest()
+        {
+            Assert.Equal(Zero, MinMaxValueHelper<UInt128>.MinValue);
+        }
+
+        //
+        // IModulusOperators
+        //
+
+        [Fact]
+        public static void op_ModulusTest()
+        {
+            Assert.Equal(Zero, ModulusOperatorsHelper<UInt128, UInt128, UInt128>.op_Modulus(Zero, 2U));
+            Assert.Equal(One, ModulusOperatorsHelper<UInt128, UInt128, UInt128>.op_Modulus(One, 2U));
+            Assert.Equal(One, ModulusOperatorsHelper<UInt128, UInt128, UInt128>.op_Modulus(Int128MaxValue, 2U));
+            Assert.Equal(Zero, ModulusOperatorsHelper<UInt128, UInt128, UInt128>.op_Modulus(Int128MaxValuePlusOne, 2U));
+            Assert.Equal(One, ModulusOperatorsHelper<UInt128, UInt128, UInt128>.op_Modulus(MaxValue, 2U));
+
+            Assert.Throws<DivideByZeroException>(() => ModulusOperatorsHelper<UInt128, UInt128, UInt128>.op_Modulus(One, 0U));
+        }
+
+        //
+        // IMultiplicativeIdentity
+        //
+
+        [Fact]
+        public static void MultiplicativeIdentityTest()
+        {
+            Assert.Equal(One, MultiplicativeIdentityHelper<UInt128, UInt128>.MultiplicativeIdentity);
+        }
+
+        //
+        // IMultiplyOperators
+        //
+
+        [Fact]
+        public static void op_MultiplyTest()
+        {
+            Assert.Equal(Zero, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_Multiply(Zero, 2U));
+            Assert.Equal(Two, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_Multiply(One, 2U));
+            Assert.Equal(MaxValueMinusOne, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_Multiply(Int128MaxValue, 2U));
+            Assert.Equal(Zero, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_Multiply(Int128MaxValuePlusOne, 2U));
+            Assert.Equal(MaxValueMinusOne, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_Multiply(MaxValue, 2U));
+        }
+        [Fact]
+        public static void op_CheckedMultiplyTest()
+        {
+            Assert.Equal(Zero, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedMultiply(Zero, 2U));
+            Assert.Equal(Two, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedMultiply(One, 2U));
+            Assert.Equal(MaxValueMinusOne, MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedMultiply(Int128MaxValue, 2U));
+
+            Assert.Throws<OverflowException>(() => MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedMultiply(Int128MaxValuePlusOne, 2U));
+            Assert.Throws<OverflowException>(() => MultiplyOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedMultiply(MaxValue, 2U));
+        }
+
+        //
+        // INumber
+        //
+
+        [Fact]
+        public static void ClampTest()
+        {
+            Assert.Equal(One, NumberHelper<UInt128>.Clamp(Zero, 0x0001U, 0x007FU));
+            Assert.Equal(One, NumberHelper<UInt128>.Clamp(One, 0x0001U, 0x007FU));
+            Assert.Equal(0x007FU, NumberHelper<UInt128>.Clamp(Int128MaxValue, 0x0001U, 0x007FU));
+            Assert.Equal(0x007FU, NumberHelper<UInt128>.Clamp(Int128MaxValuePlusOne, 0x0001U, 0x007FU));
+            Assert.Equal(0x007FU, NumberHelper<UInt128>.Clamp(MaxValue, 0x0001U, 0x007FU));
+        }
+
+        [Fact]
+        public static void MaxTest()
+        {
+            Assert.Equal(One, NumberHelper<UInt128>.Max(Zero, 1U));
+            Assert.Equal(One, NumberHelper<UInt128>.Max(One, 1U));
+            Assert.Equal(Int128MaxValue, NumberHelper<UInt128>.Max(Int128MaxValue, 1U));
+            Assert.Equal(Int128MaxValuePlusOne, NumberHelper<UInt128>.Max(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.Max(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void MinTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.Min(Zero, 1U));
+            Assert.Equal(One, NumberHelper<UInt128>.Min(One, 1U));
+            Assert.Equal(One, NumberHelper<UInt128>.Min(Int128MaxValue, 1U));
+            Assert.Equal(One, NumberHelper<UInt128>.Min(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(One, NumberHelper<UInt128>.Min(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void SignTest()
+        {
+            Assert.Equal(0, NumberHelper<UInt128>.Sign(Zero));
+            Assert.Equal(1, NumberHelper<UInt128>.Sign(One));
+            Assert.Equal(1, NumberHelper<UInt128>.Sign(Int128MaxValue));
+            Assert.Equal(1, NumberHelper<UInt128>.Sign(Int128MaxValuePlusOne));
+            Assert.Equal(1, NumberHelper<UInt128>.Sign(MaxValue));
+        }
+
+        //
+        // INumberBase
+        //
+
+        [Fact]
+        public static void OneTest()
+        {
+            Assert.Equal(One, NumberBaseHelper<UInt128>.One);
+        }
+
+        [Fact]
+        public static void ZeroTest()
+        {
+            Assert.Equal(Zero, NumberBaseHelper<UInt128>.Zero);
+        }
+
+        [Fact]
+        public static void AbsTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.Abs(Zero));
+            Assert.Equal(One, NumberHelper<UInt128>.Abs(One));
+            Assert.Equal(Int128MaxValue, NumberHelper<UInt128>.Abs(Int128MaxValue));
+            Assert.Equal(Int128MaxValuePlusOne, NumberHelper<UInt128>.Abs(Int128MaxValuePlusOne));
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.Abs(MaxValue));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<byte>(0x00));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<byte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<UInt128>.CreateChecked<byte>(0x7F));
+            Assert.Equal(SByteMaxValuePlusOne, NumberHelper<UInt128>.CreateChecked<byte>(0x80));
+            Assert.Equal(ByteMaxValue, NumberHelper<UInt128>.CreateChecked<byte>(0xFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromCharTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<char>((char)0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<char>((char)0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateChecked<char>((char)0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<UInt128>.CreateChecked<char>((char)0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<UInt128>.CreateChecked<char>((char)0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromDecimalTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<decimal>(decimal.Zero));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<decimal>(decimal.One));
+
+            Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper<UInt128>.CreateChecked<decimal>(decimal.MaxValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<decimal>(decimal.MinValue));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<decimal>(decimal.MinusOne));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromDoubleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<double>(+0.0));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<double>(-0.0));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<double>(+double.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<double>(+1.0));
+
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateChecked<double>(+170141183460469231731687303715884105728.0));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateChecked<double>(+340282366920938425684442744474606501888.0));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(-double.Epsilon));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(-1.0));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(+340282366920938463463374607431768211456.0));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(-340282366920938425684442744474606501888.0));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(double.MaxValue));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(double.MinValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(double.PositiveInfinity));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<double>(double.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromHalfTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<Half>((Half)(+0.0)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<Half>((Half)(-0.0)));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<Half>(+Half.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<Half>((Half)(+1.0)));
+            Assert.Equal(+65504U, NumberHelper<UInt128>.CreateChecked<Half>(Half.MaxValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<Half>(-Half.Epsilon));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<Half>((Half)(-1.0)));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<Half>(Half.MinValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<Half>(Half.PositiveInfinity));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<Half>(Half.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<short>(0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<short>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateChecked<short>(0x7FFF));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<short>(unchecked((short)0x8000)));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<short>(unchecked((short)0xFFFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<int>(0x00000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<int>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateChecked<int>(0x7FFFFFFF));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<int>(unchecked((int)0x80000000)));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<int>(unchecked((int)0xFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<long>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<long>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateChecked<long>(0x7FFFFFFFFFFFFFFF));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<long>(unchecked((long)0x8000000000000000)));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<long>(unchecked((long)0xFFFFFFFFFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<nint>(unchecked((nint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<nint>(unchecked((nint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateChecked<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<nint>(unchecked((nint)0x8000000000000000)));
+                Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<nint>((nint)0x00000000));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<nint>((nint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateChecked<nint>((nint)0x7FFFFFFF));
+                Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<nint>(unchecked((nint)0x80000000)));
+                Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<nint>(unchecked((nint)0xFFFFFFFF)));
+            }
+        }
+
+        [Fact]
+        public static void CreateCheckedFromSByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<sbyte>(0x00));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<sbyte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<UInt128>.CreateChecked<sbyte>(0x7F));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<sbyte>(unchecked((sbyte)0x80)));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<sbyte>(unchecked((sbyte)0xFF)));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromSingleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<float>(+0.0f));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<float>(-0.0f));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<float>(+float.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<float>(+1.0f));
+
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateChecked<float>(+170141183460469231731687303715884105728.0f));
+            Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateChecked<float>(float.MaxValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<float>(-float.Epsilon));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<float>(-1.0f));            
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<float>(float.MinValue));
+
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<float>(float.PositiveInfinity));
+            Assert.Throws<OverflowException>(() => NumberHelper<UInt128>.CreateChecked<float>(float.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<ushort>(0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<ushort>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateChecked<ushort>(0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<UInt128>.CreateChecked<ushort>(0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<UInt128>.CreateChecked<ushort>(0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<uint>(0x00000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<uint>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateChecked<uint>(0x7FFFFFFF));
+            Assert.Equal(Int32MaxValuePlusOne, NumberHelper<UInt128>.CreateChecked<uint>(0x80000000));
+            Assert.Equal(UInt32MaxValue, NumberHelper<UInt128>.CreateChecked<uint>(0xFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<ulong>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<ulong>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateChecked<ulong>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MaxValuePlusOne, NumberHelper<UInt128>.CreateChecked<ulong>(0x8000000000000000));
+            Assert.Equal(UInt64MaxValue, NumberHelper<UInt128>.CreateChecked<ulong>(0xFFFFFFFFFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateCheckedFromUIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<nuint>(unchecked((nuint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<nuint>(unchecked((nuint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateChecked<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MaxValuePlusOne, NumberHelper<UInt128>.CreateChecked<nuint>(unchecked((nuint)0x8000000000000000)));
+                Assert.Equal(UInt64MaxValue, NumberHelper<UInt128>.CreateChecked<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateChecked<nuint>((nuint)0x00000000));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateChecked<nuint>((nuint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateChecked<nuint>((nuint)0x7FFFFFFF));
+                Assert.Equal(Int32MaxValuePlusOne, NumberHelper<UInt128>.CreateChecked<nuint>((nuint)0x80000000));
+                Assert.Equal(UInt32MaxValue, NumberHelper<UInt128>.CreateChecked<nuint>((nuint)0xFFFFFFFF));
+            }
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<byte>(0x00));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<byte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<UInt128>.CreateSaturating<byte>(0x7F));
+            Assert.Equal(SByteMaxValuePlusOne, NumberHelper<UInt128>.CreateSaturating<byte>(0x80));
+            Assert.Equal(ByteMaxValue, NumberHelper<UInt128>.CreateSaturating<byte>(0xFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromCharTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<char>((char)0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<char>((char)0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateSaturating<char>((char)0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<UInt128>.CreateSaturating<char>((char)0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<UInt128>.CreateSaturating<char>((char)0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromDecimalTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<decimal>(decimal.Zero));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<decimal>(decimal.One));
+
+            Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper<UInt128>.CreateSaturating<decimal>(decimal.MaxValue));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<decimal>(decimal.MinValue));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<decimal>(decimal.MinusOne));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromDoubleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(+0.0));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(-0.0));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(+double.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<double>(+1.0));
+
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateSaturating<double>(+170141183460469231731687303715884105728.0));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateSaturating<double>(+340282366920938425684442744474606501888.0));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(-double.Epsilon));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(-1.0));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateSaturating<double>(+340282366920938463463374607431768211456.0));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(-340282366920938425684442744474606501888.0));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateSaturating<double>(double.MaxValue));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(double.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateSaturating<double>(double.PositiveInfinity));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<double>(double.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromHalfTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<Half>((Half)(+0.0)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<Half>((Half)(-0.0)));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<Half>(+Half.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<Half>((Half)(+1.0)));
+            Assert.Equal(+65504U, NumberHelper<UInt128>.CreateSaturating<Half>(Half.MaxValue));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<Half>(-Half.Epsilon));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<Half>((Half)(-1.0)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<Half>(Half.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateSaturating<Half>(Half.PositiveInfinity));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<Half>(Half.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<short>(0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<short>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateSaturating<short>(0x7FFF));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<short>(unchecked((short)0x8000)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<short>(unchecked((short)0xFFFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<int>(0x00000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<int>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateSaturating<int>(0x7FFFFFFF));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<int>(unchecked((int)0x80000000)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<int>(unchecked((int)0xFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<long>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<long>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateSaturating<long>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<long>(unchecked((long)0x8000000000000000)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<long>(unchecked((long)0xFFFFFFFFFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nint>(unchecked((nint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<nint>(unchecked((nint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateSaturating<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nint>(unchecked((nint)0x8000000000000000)));
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nint>((nint)0x00000000));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<nint>((nint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateSaturating<nint>((nint)0x7FFFFFFF));
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nint>(unchecked((nint)0x80000000)));
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nint>(unchecked((nint)0xFFFFFFFF)));
+            }
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromSByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<sbyte>(0x00));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<sbyte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<UInt128>.CreateSaturating<sbyte>(0x7F));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<sbyte>(unchecked((sbyte)0x80)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<sbyte>(unchecked((sbyte)0xFF)));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromSingleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<float>(+0.0f));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<float>(-0.0f));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<float>(+float.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<float>(+1.0f));
+
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateSaturating<float>(+170141183460469231731687303715884105728.0f));
+            Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateSaturating<float>(float.MaxValue));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<float>(-float.Epsilon));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<float>(-1.0f));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<float>(float.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateSaturating<float>(float.PositiveInfinity));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<float>(float.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<ushort>(0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<ushort>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateSaturating<ushort>(0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<UInt128>.CreateSaturating<ushort>(0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<UInt128>.CreateSaturating<ushort>(0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<uint>(0x00000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<uint>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateSaturating<uint>(0x7FFFFFFF));
+            Assert.Equal(Int32MaxValuePlusOne, NumberHelper<UInt128>.CreateSaturating<uint>(0x80000000));
+            Assert.Equal(UInt32MaxValue, NumberHelper<UInt128>.CreateSaturating<uint>(0xFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<ulong>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<ulong>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateSaturating<ulong>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MaxValuePlusOne, NumberHelper<UInt128>.CreateSaturating<ulong>(0x8000000000000000));
+            Assert.Equal(UInt64MaxValue, NumberHelper<UInt128>.CreateSaturating<ulong>(0xFFFFFFFFFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateSaturatingFromUIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nuint>(unchecked((nuint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<nuint>(unchecked((nuint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateSaturating<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MaxValuePlusOne, NumberHelper<UInt128>.CreateSaturating<nuint>(unchecked((nuint)0x8000000000000000)));
+                Assert.Equal(UInt64MaxValue, NumberHelper<UInt128>.CreateSaturating<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateSaturating<nuint>((nuint)0x00000000));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateSaturating<nuint>((nuint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateSaturating<nuint>((nuint)0x7FFFFFFF));
+                Assert.Equal(Int32MaxValuePlusOne, NumberHelper<UInt128>.CreateSaturating<nuint>((nuint)0x80000000));
+                Assert.Equal(UInt32MaxValue, NumberHelper<UInt128>.CreateSaturating<nuint>((nuint)0xFFFFFFFF));
+            }
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<byte>(0x00));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<byte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<UInt128>.CreateTruncating<byte>(0x7F));
+            Assert.Equal(SByteMaxValuePlusOne, NumberHelper<UInt128>.CreateTruncating<byte>(0x80));
+            Assert.Equal(ByteMaxValue, NumberHelper<UInt128>.CreateTruncating<byte>(0xFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromCharTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<char>((char)0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<char>((char)0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateTruncating<char>((char)0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<UInt128>.CreateTruncating<char>((char)0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<UInt128>.CreateTruncating<char>((char)0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromDecimalTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<decimal>(decimal.Zero));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<decimal>(decimal.One));
+
+            Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper<UInt128>.CreateTruncating<decimal>(decimal.MaxValue));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<decimal>(decimal.MinValue));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<decimal>(decimal.MinusOne));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromDoubleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(+0.0));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(-0.0));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(+double.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<double>(+1.0));
+
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateTruncating<double>(+170141183460469231731687303715884105728.0));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateTruncating<double>(+340282366920938425684442744474606501888.0));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(-double.Epsilon));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(-1.0));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<double>(+340282366920938463463374607431768211456.0));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(-340282366920938425684442744474606501888.0));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<double>(double.MaxValue));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(double.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<double>(double.PositiveInfinity));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<double>(double.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromHalfTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<Half>((Half)(+0.0)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<Half>((Half)(-0.0)));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<Half>(+Half.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<Half>((Half)(+1.0)));
+            Assert.Equal(+65504U, NumberHelper<UInt128>.CreateTruncating<Half>(Half.MaxValue));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<Half>(-Half.Epsilon));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<Half>((Half)(-1.0)));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<Half>(Half.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<Half>(Half.PositiveInfinity));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<Half>(Half.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<short>(0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<short>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateTruncating<short>(0x7FFF));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_8000), NumberHelper<UInt128>.CreateTruncating<short>(unchecked((short)0x8000)));
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<short>(unchecked((short)0xFFFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<int>(0x00000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<int>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateTruncating<int>(0x7FFFFFFF));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_8000_0000), NumberHelper<UInt128>.CreateTruncating<int>(unchecked((int)0x80000000)));
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<int>(unchecked((int)0xFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<long>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<long>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateTruncating<long>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0x8000_0000_0000_0000), NumberHelper<UInt128>.CreateTruncating<long>(unchecked((long)0x8000000000000000)));
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<long>(unchecked((long)0xFFFFFFFFFFFFFFFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<nint>(unchecked((nint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<nint>(unchecked((nint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateTruncating<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0x8000_0000_0000_0000), NumberHelper<UInt128>.CreateTruncating<nint>(unchecked((nint)0x8000000000000000)));
+                Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<nint>((nint)0x00000000));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<nint>((nint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateTruncating<nint>((nint)0x7FFFFFFF));
+                Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_8000_0000), NumberHelper<UInt128>.CreateTruncating<nint>(unchecked((nint)0x80000000)));
+                Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<nint>(unchecked((nint)0xFFFFFFFF)));
+            }
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromSByteTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<sbyte>(0x00));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<sbyte>(0x01));
+            Assert.Equal(SByteMaxValue, NumberHelper<UInt128>.CreateTruncating<sbyte>(0x7F));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), NumberHelper<UInt128>.CreateTruncating<sbyte>(unchecked((sbyte)0x80)));
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<sbyte>(unchecked((sbyte)0xFF)));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromSingleTest()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<float>(+0.0f));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<float>(-0.0f));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<float>(+float.Epsilon));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<float>(+1.0f));
+
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateTruncating<float>(+170141183460469231731687303715884105728.0f));
+            Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), NumberHelper<UInt128>.CreateTruncating<float>(float.MaxValue));
+
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<float>(-float.Epsilon));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<float>(-1.0f));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<float>(float.MinValue));
+
+            Assert.Equal(MaxValue, NumberHelper<UInt128>.CreateTruncating<float>(float.PositiveInfinity));
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<float>(float.NegativeInfinity));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUInt16Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<ushort>(0x0000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<ushort>(0x0001));
+            Assert.Equal(Int16MaxValue, NumberHelper<UInt128>.CreateTruncating<ushort>(0x7FFF));
+            Assert.Equal(Int16MaxValuePlusOne, NumberHelper<UInt128>.CreateTruncating<ushort>(0x8000));
+            Assert.Equal(UInt16MaxValue, NumberHelper<UInt128>.CreateTruncating<ushort>(0xFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUInt32Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<uint>(0x00000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<uint>(0x00000001));
+            Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateTruncating<uint>(0x7FFFFFFF));
+            Assert.Equal(Int32MaxValuePlusOne, NumberHelper<UInt128>.CreateTruncating<uint>(0x80000000));
+            Assert.Equal(UInt32MaxValue, NumberHelper<UInt128>.CreateTruncating<uint>(0xFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUInt64Test()
+        {
+            Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<ulong>(0x0000000000000000));
+            Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<ulong>(0x0000000000000001));
+            Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateTruncating<ulong>(0x7FFFFFFFFFFFFFFF));
+            Assert.Equal(Int64MaxValuePlusOne, NumberHelper<UInt128>.CreateTruncating<ulong>(0x8000000000000000));
+            Assert.Equal(UInt64MaxValue, NumberHelper<UInt128>.CreateTruncating<ulong>(0xFFFFFFFFFFFFFFFF));
+        }
+
+        [Fact]
+        public static void CreateTruncatingFromUIntPtrTest()
+        {
+            if (Environment.Is64BitProcess)
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<nuint>(unchecked((nuint)0x0000000000000000)));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<nuint>(unchecked((nuint)0x0000000000000001)));
+                Assert.Equal(Int64MaxValue, NumberHelper<UInt128>.CreateTruncating<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF)));
+                Assert.Equal(Int64MaxValuePlusOne, NumberHelper<UInt128>.CreateTruncating<nuint>(unchecked((nuint)0x8000000000000000)));
+                Assert.Equal(UInt64MaxValue, NumberHelper<UInt128>.CreateTruncating<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF)));
+            }
+            else
+            {
+                Assert.Equal(Zero, NumberHelper<UInt128>.CreateTruncating<nuint>((nuint)0x00000000));
+                Assert.Equal(One, NumberHelper<UInt128>.CreateTruncating<nuint>((nuint)0x00000001));
+                Assert.Equal(Int32MaxValue, NumberHelper<UInt128>.CreateTruncating<nuint>((nuint)0x7FFFFFFF));
+                Assert.Equal(Int32MaxValuePlusOne, NumberHelper<UInt128>.CreateTruncating<nuint>((nuint)0x80000000));
+                Assert.Equal(UInt32MaxValue, NumberHelper<UInt128>.CreateTruncating<nuint>((nuint)0xFFFFFFFF));
+            }
+        }
+
+        [Fact]
+        public static void TryCreateFromByteTest()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<byte>(0x00, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<byte>(0x01, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<byte>(0x7F, out result));
+            Assert.Equal(SByteMaxValue, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<byte>(0x80, out result));
+            Assert.Equal(SByteMaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<byte>(0xFF, out result));
+            Assert.Equal(ByteMaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromCharTest()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<char>((char)0x0000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<char>((char)0x0001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<char>((char)0x7FFF, out result));
+            Assert.Equal(Int16MaxValue, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<char>((char)0x8000, out result));
+            Assert.Equal(Int16MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<char>((char)0xFFFF, out result));
+            Assert.Equal(UInt16MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromDecimalTest()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<decimal>(decimal.Zero, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<decimal>(decimal.One, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<decimal>(decimal.MaxValue, out result));
+            Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<decimal>(decimal.MinValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<decimal>(decimal.MinusOne, out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromDoubleTest()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<double>(+0.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<double>(-0.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<double>(+double.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<double>(+1.0, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<double>(+170141183460469231731687303715884105728.0, out result));
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<double>(+340282366920938425684442744474606501888.0, out result));
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(-double.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(-1.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(+340282366920938463463374607431768211456.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(-340282366920938425684442744474606501888.0, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(double.MaxValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(double.MinValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(double.PositiveInfinity, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<double>(double.NegativeInfinity, out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromHalfTest()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<Half>((Half)(+0.0), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<Half>((Half)(-0.0), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<Half>(+Half.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<Half>((Half)(+1.0), out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<Half>(Half.MaxValue, out result));
+            Assert.Equal(+65504U, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<Half>(-Half.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<Half>((Half)(-1.0), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<Half>(Half.MinValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<Half>(Half.PositiveInfinity, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<Half>(Half.NegativeInfinity, out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromInt16Test()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<short>(0x0000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<short>(0x0001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<short>(0x7FFF, out result));
+            Assert.Equal(Int16MaxValue, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<short>(unchecked((short)0x8000), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<short>(unchecked((short)0xFFFF), out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromInt32Test()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<int>(0x00000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<int>(0x00000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<int>(0x7FFFFFFF, out result));
+            Assert.Equal(Int32MaxValue, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<int>(unchecked((int)0x80000000), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<int>(unchecked((int)0xFFFFFFFF), out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromInt64Test()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<long>(0x0000000000000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<long>(0x0000000000000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<long>(0x7FFFFFFFFFFFFFFF, out result));
+            Assert.Equal(Int64MaxValue, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<long>(unchecked((long)0x8000000000000000), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<long>(unchecked((long)0xFFFFFFFFFFFFFFFF), out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromIntPtrTest()
+        {
+            UInt128 result;
+
+            if (Environment.Is64BitProcess)
+            {
+                Assert.True(NumberHelper<UInt128>.TryCreate<nint>(unchecked((nint)0x0000000000000000), out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nint>(unchecked((nint)0x0000000000000001), out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nint>(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result));
+                Assert.Equal(Int64MaxValue, result);
+
+                Assert.False(NumberHelper<UInt128>.TryCreate<nint>(unchecked((nint)0x8000000000000000), out result));
+                Assert.Equal(Zero, result);
+
+                Assert.False(NumberHelper<UInt128>.TryCreate<nint>(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result));
+                Assert.Equal(Zero, result);
+            }
+            else
+            {
+                Assert.True(NumberHelper<UInt128>.TryCreate<nint>((nint)0x00000000, out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nint>((nint)0x00000001, out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nint>((nint)0x7FFFFFFF, out result));
+                Assert.Equal(Int32MaxValue, result);
+
+                Assert.False(NumberHelper<UInt128>.TryCreate<nint>(unchecked((nint)0x80000000), out result));
+                Assert.Equal(Zero, result);
+
+                Assert.False(NumberHelper<UInt128>.TryCreate<nint>(unchecked((nint)0xFFFFFFFF), out result));
+                Assert.Equal(Zero, result);
+            }
+        }
+
+        [Fact]
+        public static void TryCreateFromSByteTest()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<sbyte>(0x00, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<sbyte>(0x01, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<sbyte>(0x7F, out result));
+            Assert.Equal(SByteMaxValue, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<sbyte>(unchecked((sbyte)0x80), out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<sbyte>(unchecked((sbyte)0xFF), out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromSingleTest()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<float>(+0.0f, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<float>(-0.0f, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<float>(+float.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<float>(+1.0f, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<float>(+170141183460469231731687303715884105728.0f, out result));
+            Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<float>(float.MaxValue, out result));
+            Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<float>(-float.Epsilon, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<float>(-1.0f, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<float>(float.MinValue, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<float>(float.PositiveInfinity, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.False(NumberHelper<UInt128>.TryCreate<float>(float.NegativeInfinity, out result));
+            Assert.Equal(Zero, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUInt16Test()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ushort>(0x0000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ushort>(0x0001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ushort>(0x7FFF, out result));
+            Assert.Equal(Int16MaxValue, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ushort>(0x8000, out result));
+            Assert.Equal(Int16MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ushort>(0xFFFF, out result));
+            Assert.Equal(UInt16MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUInt32Test()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<uint>(0x00000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<uint>(0x00000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<uint>(0x7FFFFFFF, out result));
+            Assert.Equal(Int32MaxValue, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<uint>(0x80000000, out result));
+            Assert.Equal(Int32MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<uint>(0xFFFFFFFF, out result));
+            Assert.Equal(UInt32MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUInt64Test()
+        {
+            UInt128 result;
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ulong>(0x0000000000000000, out result));
+            Assert.Equal(Zero, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ulong>(0x0000000000000001, out result));
+            Assert.Equal(One, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ulong>(0x7FFFFFFFFFFFFFFF, out result));
+            Assert.Equal(Int64MaxValue, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ulong>(0x8000000000000000, out result));
+            Assert.Equal(Int64MaxValuePlusOne, result);
+
+            Assert.True(NumberHelper<UInt128>.TryCreate<ulong>(0xFFFFFFFFFFFFFFFF, out result));
+            Assert.Equal(UInt64MaxValue, result);
+        }
+
+        [Fact]
+        public static void TryCreateFromUIntPtrTest()
+        {
+            UInt128 result;
+
+            if (Environment.Is64BitProcess)
+            {
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>(unchecked((nuint)0x0000000000000000), out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>(unchecked((nuint)0x0000000000000001), out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result));
+                Assert.Equal(Int64MaxValue, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>(unchecked((nuint)0x8000000000000000), out result));
+                Assert.Equal(Int64MaxValuePlusOne, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result));
+                Assert.Equal(UInt64MaxValue, result);
+            }
+            else
+            {
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>((nuint)0x00000000, out result));
+                Assert.Equal(Zero, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>((nuint)0x00000001, out result));
+                Assert.Equal(One, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>((nuint)0x7FFFFFFF, out result));
+                Assert.Equal(Int32MaxValue, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>(unchecked((nuint)0x80000000), out result));
+                Assert.Equal(Int32MaxValuePlusOne, result);
+
+                Assert.True(NumberHelper<UInt128>.TryCreate<nuint>(unchecked((nuint)0xFFFFFFFF), out result));
+                Assert.Equal(UInt32MaxValue, result);
+            }
+        }
+
+        //
+        // IShiftOperators
+        //
+
+        [Fact]
+        public static void op_LeftShiftTest()
+        {
+            Assert.Equal(Zero, ShiftOperatorsHelper<UInt128, UInt128>.op_LeftShift(Zero, 1));
+            Assert.Equal(Two, ShiftOperatorsHelper<UInt128, UInt128>.op_LeftShift(One, 1));
+            Assert.Equal(MaxValueMinusOne, ShiftOperatorsHelper<UInt128, UInt128>.op_LeftShift(Int128MaxValue, 1));
+            Assert.Equal(Zero, ShiftOperatorsHelper<UInt128, UInt128>.op_LeftShift(Int128MaxValuePlusOne, 1));
+            Assert.Equal(MaxValueMinusOne, ShiftOperatorsHelper<UInt128, UInt128>.op_LeftShift(MaxValue, 1));
+        }
+
+        [Fact]
+        public static void op_RightShiftTest()
+        {
+            Assert.Equal(Zero, ShiftOperatorsHelper<UInt128, UInt128>.op_RightShift(Zero, 1));
+            Assert.Equal(Zero, ShiftOperatorsHelper<UInt128, UInt128>.op_RightShift(One, 1));
+            Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper<UInt128, UInt128>.op_RightShift(Int128MaxValue, 1));
+            Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper<UInt128, UInt128>.op_RightShift(Int128MaxValuePlusOne, 1));
+            Assert.Equal(Int128MaxValue, ShiftOperatorsHelper<UInt128, UInt128>.op_RightShift(MaxValue, 1));
+        }
+
+        [Fact]
+        public static void op_UnsignedRightShiftTest()
+        {
+            Assert.Equal(Zero, ShiftOperatorsHelper<UInt128, UInt128>.op_UnsignedRightShift(Zero, 1));
+            Assert.Equal(Zero, ShiftOperatorsHelper<UInt128, UInt128>.op_UnsignedRightShift(One, 1));
+            Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper<UInt128, UInt128>.op_UnsignedRightShift(Int128MaxValue, 1));
+            Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper<UInt128, UInt128>.op_UnsignedRightShift(Int128MaxValuePlusOne, 1));
+            Assert.Equal(Int128MaxValue, ShiftOperatorsHelper<UInt128, UInt128>.op_UnsignedRightShift(MaxValue, 1));
+        }
+
+        //
+        // ISubtractionOperators
+        //
+
+        [Fact]
+        public static void op_SubtractionTest()
+        {
+            Assert.Equal(MaxValue, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_Subtraction(Zero, 1U));
+            Assert.Equal(Zero, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_Subtraction(One, 1U));
+            Assert.Equal(Int128MaxValueMinusOne, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_Subtraction(Int128MaxValue, 1U));
+            Assert.Equal(Int128MaxValue, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_Subtraction(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_Subtraction(MaxValue, 1U));
+        }
+
+        [Fact]
+        public static void op_CheckedSubtractionTest()
+        {
+            Assert.Equal(Zero, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedSubtraction(One, 1U));
+            Assert.Equal(Int128MaxValueMinusOne, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedSubtraction(Int128MaxValue, 1U));
+            Assert.Equal(Int128MaxValue, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedSubtraction(Int128MaxValuePlusOne, 1U));
+            Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedSubtraction(MaxValue, 1U));
+
+            Assert.Throws<OverflowException>(() => SubtractionOperatorsHelper<UInt128, UInt128, UInt128>.op_CheckedSubtraction(Zero, 1U));
+        }
+
+        //
+        // IUnaryNegationOperators
+        //
+
+        [Fact]
+        public static void op_UnaryNegationTest()
+        {
+            Assert.Equal(Zero, UnaryNegationOperatorsHelper<UInt128, UInt128>.op_UnaryNegation(Zero));
+            Assert.Equal(MaxValue, UnaryNegationOperatorsHelper<UInt128, UInt128>.op_UnaryNegation(One));
+            Assert.Equal(Int128MaxValuePlusTwo, UnaryNegationOperatorsHelper<UInt128, UInt128>.op_UnaryNegation(Int128MaxValue));
+            Assert.Equal(Int128MaxValuePlusOne, UnaryNegationOperatorsHelper<UInt128, UInt128>.op_UnaryNegation(Int128MaxValuePlusOne));
+            Assert.Equal(One, UnaryNegationOperatorsHelper<UInt128, UInt128>.op_UnaryNegation(MaxValue));
+        }
+
+        [Fact]
+        public static void op_CheckedUnaryNegationTest()
+        {
+            Assert.Equal(Zero, UnaryNegationOperatorsHelper<UInt128, UInt128>.op_CheckedUnaryNegation(Zero));
+
+            Assert.Throws<OverflowException>(() => UnaryNegationOperatorsHelper<UInt128, UInt128>.op_CheckedUnaryNegation(One));
+            Assert.Throws<OverflowException>(() => UnaryNegationOperatorsHelper<UInt128, UInt128>.op_CheckedUnaryNegation(Int128MaxValue));
+            Assert.Throws<OverflowException>(() => UnaryNegationOperatorsHelper<UInt128, UInt128>.op_CheckedUnaryNegation(Int128MaxValuePlusOne));
+            Assert.Throws<OverflowException>(() => UnaryNegationOperatorsHelper<UInt128, UInt128>.op_CheckedUnaryNegation(MaxValue));
+        }
+
+        //
+        // IUnaryPlusOperators
+        //
+
+        [Fact]
+        public static void op_UnaryPlusTest()
+        {
+            Assert.Equal(Zero, UnaryPlusOperatorsHelper<UInt128, UInt128>.op_UnaryPlus(Zero));
+            Assert.Equal(One, UnaryPlusOperatorsHelper<UInt128, UInt128>.op_UnaryPlus(One));
+            Assert.Equal(Int128MaxValue, UnaryPlusOperatorsHelper<UInt128, UInt128>.op_UnaryPlus(Int128MaxValue));
+            Assert.Equal(Int128MaxValuePlusOne, UnaryPlusOperatorsHelper<UInt128, UInt128>.op_UnaryPlus(Int128MaxValuePlusOne));
+            Assert.Equal(MaxValue, UnaryPlusOperatorsHelper<UInt128, UInt128>.op_UnaryPlus(MaxValue));
+        }
+    }
+}
diff --git a/src/libraries/System.Runtime/tests/System/UInt128Tests.cs b/src/libraries/System.Runtime/tests/System/UInt128Tests.cs
new file mode 100644 (file)
index 0000000..a02f205
--- /dev/null
@@ -0,0 +1,437 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using System.Globalization;
+using System.Numerics;
+using Xunit;
+
+namespace System.Tests
+{
+    public class UInt128Tests
+    {
+        [Fact]
+        public static void Ctor_Empty()
+        {
+            var i = new UInt128();
+            Assert.Equal(0U, i);
+        }
+
+        [Fact]
+        public static void Ctor_Value()
+        {
+            UInt128 i = 41U;
+            Assert.Equal(41U, i);
+        }
+
+        [Fact]
+        public static void MaxValue()
+        {
+            Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), UInt128.MaxValue);
+        }
+
+        [Fact]
+        public static void MinValue()
+        {
+            Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), UInt128.MinValue);
+        }
+
+        public static IEnumerable<object[]> CompareTo_Other_ReturnsExpected_TestData()
+        {
+            yield return new object[] { (UInt128)234, (UInt128)234, 0 };
+            yield return new object[] { (UInt128)234, UInt128.MinValue, 1 };
+            yield return new object[] { (UInt128)234, (UInt128)123, 1 };
+            yield return new object[] { (UInt128)234, (UInt128)456, -1 };
+            yield return new object[] { (UInt128)234, UInt128.MaxValue, -1 };
+            yield return new object[] { (UInt128)234, null, 1 };
+        }
+
+        [Theory]
+        [MemberData(nameof(CompareTo_Other_ReturnsExpected_TestData))]
+        public void CompareTo_Other_ReturnsExpected(UInt128 i, object value, int expected)
+        {
+            if (value is UInt128 UInt128Value)
+            {
+                Assert.Equal(expected, Math.Sign(i.CompareTo(UInt128Value)));
+            }
+
+            Assert.Equal(expected, Math.Sign(i.CompareTo(value)));
+        }
+
+        public static IEnumerable<object[]> CompareTo_ObjectNotUInt128_ThrowsArgumentException_TestData()
+        {
+            yield return new object[] { "a" };
+            yield return new object[] { 234 };
+        }
+
+        [Theory]
+        [MemberData(nameof(CompareTo_ObjectNotUInt128_ThrowsArgumentException_TestData))]
+        public void CompareTo_ObjectNotUInt128_ThrowsArgumentException(object value)
+        {
+            AssertExtensions.Throws<ArgumentException>(null, () => ((UInt128)123).CompareTo(value));
+        }
+
+        public static IEnumerable<object[]> EqualsTest_TestData()
+        {
+            yield return new object[] { (UInt128)789, (UInt128)789, true };
+            yield return new object[] { (UInt128)788, (UInt128)0, false };
+            yield return new object[] { (UInt128)0, (UInt128)0, true };
+            yield return new object[] { (UInt128)789, null, false };
+            yield return new object[] { (UInt128)789, "789", false };
+            yield return new object[] { (UInt128)789, 789, false };
+        }
+
+        [Theory]
+        [MemberData(nameof(EqualsTest_TestData))]
+        public static void EqualsTest(UInt128 i1, object obj, bool expected)
+        {
+            if (obj is UInt128 i2)
+            {
+                Assert.Equal(expected, i1.Equals(i2));
+                Assert.Equal(expected, i1.GetHashCode().Equals(i2.GetHashCode()));
+            }
+            Assert.Equal(expected, i1.Equals(obj));
+        }
+
+        public static IEnumerable<object[]> ToString_TestData()
+        {
+            foreach (NumberFormatInfo defaultFormat in new[] { null, NumberFormatInfo.CurrentInfo })
+            {
+                foreach (string defaultSpecifier in new[] { "G", "G\0", "\0N222", "\0", "", "R" })
+                {
+                    yield return new object[] { (UInt128)0, defaultSpecifier, defaultFormat, "0" };
+                    yield return new object[] { (UInt128)4567, defaultSpecifier, defaultFormat, "4567" };
+                    yield return new object[] { UInt128.MaxValue, defaultSpecifier, defaultFormat, "340282366920938463463374607431768211455" };
+                }
+
+                yield return new object[] { (UInt128)4567, "D", defaultFormat, "4567" };
+                yield return new object[] { (UInt128)4567, "D18", defaultFormat, "000000000000004567" };
+
+                yield return new object[] { (UInt128)0x2468, "x", defaultFormat, "2468" };
+                yield return new object[] { (UInt128)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) };
+
+
+            }
+
+            NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
+            yield return new object[] { (UInt128)32, "C100", invariantFormat, "¤32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
+            yield return new object[] { (UInt128)32, "P100", invariantFormat, "3,200.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
+            yield return new object[] { (UInt128)32, "D100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" };
+            yield return new object[] { (UInt128)32, "E100", invariantFormat, "3.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
+            yield return new object[] { (UInt128)32, "F100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
+            yield return new object[] { (UInt128)32, "N100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
+            yield return new object[] { (UInt128)32, "X100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020" };
+
+            var customFormat = new NumberFormatInfo()
+            {
+                NegativeSign = "#",
+                NumberDecimalSeparator = "~",
+                NumberGroupSeparator = "*",
+                PositiveSign = "&",
+                NumberDecimalDigits = 2,
+                PercentSymbol = "@",
+                PercentGroupSeparator = ",",
+                PercentDecimalSeparator = ".",
+                PercentDecimalDigits = 5
+            };
+            yield return new object[] { (UInt128)2468, "N", customFormat, "2*468~00" };
+            yield return new object[] { (UInt128)123, "E", customFormat, "1~230000E&002" };
+            yield return new object[] { (UInt128)123, "F", customFormat, "123~00" };
+            yield return new object[] { (UInt128)123, "P", customFormat, "12,300.00000 @" };
+            yield return new object[] { UInt128.MaxValue, "n5", customFormat, "340*282*366*920*938*463*463*374*607*431*768*211*455~00000" };
+        }
+
+        [Theory]
+        [MemberData(nameof(ToString_TestData))]
+        public static void ToStringTest(UInt128 i, string format, IFormatProvider provider, string expected)
+        {
+            // Format is case insensitive
+            string upperFormat = format.ToUpperInvariant();
+            string lowerFormat = format.ToLowerInvariant();
+
+            string upperExpected = expected.ToUpperInvariant();
+            string lowerExpected = expected.ToLowerInvariant();
+
+            bool isDefaultProvider = (provider == null || provider == NumberFormatInfo.CurrentInfo);
+            if (string.IsNullOrEmpty(format) || format.ToUpperInvariant() is "G" or "R")
+            {
+                if (isDefaultProvider)
+                {
+                    Assert.Equal(upperExpected, i.ToString());
+                    Assert.Equal(upperExpected, i.ToString((IFormatProvider)null));
+                }
+                Assert.Equal(upperExpected, i.ToString(provider));
+            }
+            if (isDefaultProvider)
+            {
+                Assert.Equal(upperExpected, i.ToString(upperFormat));
+                Assert.Equal(lowerExpected, i.ToString(lowerFormat));
+                Assert.Equal(upperExpected, i.ToString(upperFormat, null));
+                Assert.Equal(lowerExpected, i.ToString(lowerFormat, null));
+            }
+            Assert.Equal(upperExpected, i.ToString(upperFormat, provider));
+            Assert.Equal(lowerExpected, i.ToString(lowerFormat, provider));
+        }
+
+        [Fact]
+        public static void ToString_InvalidFormat_ThrowsFormatException()
+        {
+            UInt128 i = 123U;
+            Assert.Throws<FormatException>(() => i.ToString("Y")); // Invalid format
+            Assert.Throws<FormatException>(() => i.ToString("Y", null)); // Invalid format
+        }
+
+        public static IEnumerable<object[]> Parse_Valid_TestData()
+        {
+            // Reuse all Int128 test data that's relevant
+            foreach (object[] objs in Int128Tests.Parse_Valid_TestData())
+            {
+                if ((Int128)objs[3] < 0) continue;
+                yield return new object[] { objs[0], objs[1], objs[2], (UInt128)(Int128)objs[3] };
+            }
+
+            // All lengths decimal
+            {
+                string s = "";
+                UInt128 result = 0U;
+                for (int i = 1; i <= 20; i++)
+                {
+                    result = (result * 10U) + (UInt128)(i % 10);
+                    s += (i % 10).ToString();
+                    yield return new object[] { s, NumberStyles.Integer, null, result };
+                }
+            }
+
+            // All lengths hexadecimal
+            {
+                string s = "";
+                UInt128 result = 0U;
+                for (int i = 1; i <= 16; i++)
+                {
+                    result = (result * 16U) + (UInt128)(i % 16);
+                    s += (i % 16).ToString("X");
+                    yield return new object[] { s, NumberStyles.HexNumber, null, result };
+                }
+            }
+
+            // And test boundary conditions for UInt128
+            yield return new object[] { "340282366920938463463374607431768211455", NumberStyles.Integer, null, UInt128.MaxValue };
+            yield return new object[] { "+340282366920938463463374607431768211455", NumberStyles.Integer, null, UInt128.MaxValue };
+            yield return new object[] { "    +340282366920938463463374607431768211455  ", NumberStyles.Integer, null, UInt128.MaxValue };
+            yield return new object[] { "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", NumberStyles.HexNumber, null, UInt128.MaxValue };
+            yield return new object[] { "   FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF   ", NumberStyles.HexNumber, null, UInt128.MaxValue };
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_Valid_TestData))]
+        public static void Parse_Valid(string value, NumberStyles style, IFormatProvider provider, UInt128 expected)
+        {
+            UInt128 result;
+
+            // Default style and provider
+            if (style == NumberStyles.Integer && provider == null)
+            {
+                Assert.True(UInt128.TryParse(value, out result));
+                Assert.Equal(expected, result);
+                Assert.Equal(expected, UInt128.Parse(value));
+            }
+
+            // Default provider
+            if (provider == null)
+            {
+                Assert.Equal(expected, UInt128.Parse(value, style));
+
+                // Substitute default NumberFormatInfo
+                Assert.True(UInt128.TryParse(value, style, new NumberFormatInfo(), out result));
+                Assert.Equal(expected, result);
+                Assert.Equal(expected, UInt128.Parse(value, style, new NumberFormatInfo()));
+            }
+
+            // Default style
+            if (style == NumberStyles.Integer)
+            {
+                Assert.Equal(expected, UInt128.Parse(value, provider));
+            }
+
+            // Full overloads
+            Assert.True(UInt128.TryParse(value, style, provider, out result));
+            Assert.Equal(expected, result);
+            Assert.Equal(expected, UInt128.Parse(value, style, provider));
+        }
+
+        public static IEnumerable<object[]> Parse_Invalid_TestData()
+        {
+            // Reuse all Int128 test data, except for those that wouldn't overflow UInt128.
+            foreach (object[] objs in Int128Tests.Parse_Invalid_TestData())
+            {
+                if ((Type)objs[3] == typeof(OverflowException) &&
+                    (!BigInteger.TryParse((string)objs[0], out BigInteger bi) || bi <= UInt128.MaxValue))
+                {
+                    continue;
+                }
+
+                yield return objs;
+            }
+
+            // < min value
+            foreach (string ws in new[] { "", "    " })
+            {
+                yield return new object[] { ws + "-1" + ws, NumberStyles.Integer, null, typeof(OverflowException) };
+                yield return new object[] { ws + "abc123" + ws, NumberStyles.Integer, new NumberFormatInfo { NegativeSign = "abc" }, typeof(OverflowException) };
+            }
+
+            // > max value
+            yield return new object[] { "340282366920938463463374607431768211456", NumberStyles.Integer, null, typeof(OverflowException) };
+            yield return new object[] { "100000000000000000000000000000000", NumberStyles.HexNumber, null, typeof(OverflowException) };
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_Invalid_TestData))]
+        public static void Parse_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType)
+        {
+            UInt128 result;
+
+            // Default style and provider
+            if (style == NumberStyles.Integer && provider == null)
+            {
+                Assert.False(UInt128.TryParse(value, out result));
+                Assert.Equal(default, result);
+                Assert.Throws(exceptionType, () => UInt128.Parse(value));
+            }
+
+            // Default provider
+            if (provider == null)
+            {
+                Assert.Throws(exceptionType, () => UInt128.Parse(value, style));
+
+                // Substitute default NumberFormatInfo
+                Assert.False(UInt128.TryParse(value, style, new NumberFormatInfo(), out result));
+                Assert.Equal(default, result);
+                Assert.Throws(exceptionType, () => UInt128.Parse(value, style, new NumberFormatInfo()));
+            }
+
+            // Default style
+            if (style == NumberStyles.Integer)
+            {
+                Assert.Throws(exceptionType, () => UInt128.Parse(value, provider));
+            }
+
+            // Full overloads
+            Assert.False(UInt128.TryParse(value, style, provider, out result));
+            Assert.Equal(default, result);
+            Assert.Throws(exceptionType, () => UInt128.Parse(value, style, provider));
+        }
+
+        [Theory]
+        [InlineData(NumberStyles.HexNumber | NumberStyles.AllowParentheses, null)]
+        [InlineData(unchecked((NumberStyles)0xFFFFFC00), "style")]
+        public static void TryParse_InvalidNumberStyle_ThrowsArgumentException(NumberStyles style, string paramName)
+        {
+            UInt128 result = 0U;
+            AssertExtensions.Throws<ArgumentException>(paramName, () => UInt128.TryParse("1", style, null, out result));
+            Assert.Equal(default(UInt128), result);
+
+            AssertExtensions.Throws<ArgumentException>(paramName, () => UInt128.Parse("1", style));
+            AssertExtensions.Throws<ArgumentException>(paramName, () => UInt128.Parse("1", style, null));
+        }
+
+        public static IEnumerable<object[]> Parse_ValidWithOffsetCount_TestData()
+        {
+            foreach (object[] inputs in Parse_Valid_TestData())
+            {
+                yield return new object[] { inputs[0], 0, ((string)inputs[0]).Length, inputs[1], inputs[2], inputs[3] };
+            }
+
+            yield return new object[] { "+123", 1, 3, NumberStyles.Integer, null, (UInt128)123 };
+            yield return new object[] { "+123", 0, 3, NumberStyles.Integer, null, (UInt128)12 };
+            yield return new object[] { "  123  ", 1, 2, NumberStyles.Integer, null, (UInt128)1 };
+            yield return new object[] { "12", 0, 1, NumberStyles.HexNumber, null, (UInt128)0x1 };
+            yield return new object[] { "ABC", 1, 1, NumberStyles.HexNumber, null, (UInt128)0xb };
+            yield return new object[] { "$1,000", 1, 3, NumberStyles.Currency, new NumberFormatInfo() { CurrencySymbol = "$" }, (UInt128)10 };
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_ValidWithOffsetCount_TestData))]
+        public static void Parse_Span_Valid(string value, int offset, int count, NumberStyles style, IFormatProvider provider, UInt128 expected)
+        {
+            UInt128 result;
+
+            // Default style and provider
+            if (style == NumberStyles.Integer && provider == null)
+            {
+                Assert.True(UInt128.TryParse(value.AsSpan(offset, count), out result));
+                Assert.Equal(expected, result);
+            }
+
+            Assert.Equal(expected, UInt128.Parse(value.AsSpan(offset, count), style, provider));
+
+            Assert.True(UInt128.TryParse(value.AsSpan(offset, count), style, provider, out result));
+            Assert.Equal(expected, result);
+        }
+
+        [Theory]
+        [MemberData(nameof(Parse_Invalid_TestData))]
+        public static void Parse_Span_Invalid(string value, NumberStyles style, IFormatProvider provider, Type exceptionType)
+        {
+            if (value != null)
+            {
+                UInt128 result;
+
+                // Default style and provider
+                if (style == NumberStyles.Integer && provider == null)
+                {
+                    Assert.False(UInt128.TryParse(value.AsSpan(), out result));
+                    Assert.Equal(0u, result);
+                }
+
+                Assert.Throws(exceptionType, () => UInt128.Parse(value.AsSpan(), style, provider));
+
+                Assert.False(UInt128.TryParse(value.AsSpan(), style, provider, out result));
+                Assert.Equal(0u, result);
+            }
+        }
+
+        [Theory]
+        [MemberData(nameof(ToString_TestData))]
+        public static void TryFormat(UInt128 i, string format, IFormatProvider provider, string expected)
+        {
+            char[] actual;
+            int charsWritten;
+
+            // Just right
+            actual = new char[expected.Length];
+            Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider));
+            Assert.Equal(expected.Length, charsWritten);
+            Assert.Equal(expected, new string(actual));
+
+            // Longer than needed
+            actual = new char[expected.Length + 1];
+            Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider));
+            Assert.Equal(expected.Length, charsWritten);
+            Assert.Equal(expected, new string(actual, 0, charsWritten));
+
+            // Too short
+            if (expected.Length > 0)
+            {
+                actual = new char[expected.Length - 1];
+                Assert.False(i.TryFormat(actual.AsSpan(), out charsWritten, format, provider));
+                Assert.Equal(0, charsWritten);
+            }
+
+            if (format != null)
+            {
+                // Upper format
+                actual = new char[expected.Length];
+                Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToUpperInvariant(), provider));
+                Assert.Equal(expected.Length, charsWritten);
+                Assert.Equal(expected.ToUpperInvariant(), new string(actual));
+
+                // Lower format
+                actual = new char[expected.Length];
+                Assert.True(i.TryFormat(actual.AsSpan(), out charsWritten, format.ToLowerInvariant(), provider));
+                Assert.Equal(expected.Length, charsWritten);
+                Assert.Equal(expected.ToLowerInvariant(), new string(actual));
+            }
+        }
+    }
+}
index f727473..2d9e842 100644 (file)
@@ -29,6 +29,7 @@ add_subdirectory(PInvoke/Miscellaneous/HandleRef)
 add_subdirectory(PInvoke/Miscellaneous/MultipleAssembliesWithSamePInvoke)
 add_subdirectory(PInvoke/CriticalHandles)
 add_subdirectory(PInvoke/Generics)
+add_subdirectory(PInvoke/Int128)
 add_subdirectory(PInvoke/AsAny)
 add_subdirectory(PInvoke/SafeHandles)
 add_subdirectory(PInvoke/Vector2_3_4)
diff --git a/src/tests/Interop/PInvoke/Int128/CMakeLists.txt b/src/tests/Interop/PInvoke/Int128/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9474de8
--- /dev/null
@@ -0,0 +1,21 @@
+project (Int128Native)
+include ("${CLR_INTEROP_TEST_ROOT}/Interop.cmake")
+if(CLR_CMAKE_TARGET_ARCH_I386)
+  add_definitions(-DTARGET_X86)
+  add_definitions(-DTARGET_XARCH)
+elseif(CLR_CMAKE_TARGET_ARCH_AMD64)
+  add_definitions(-DTARGET_AMD64)
+  add_definitions(-DTARGET_XARCH)
+elseif(CLR_CMAKE_TARGET_ARCH_ARM)
+  add_definitions(-DTARGET_ARM)
+  add_definitions(-DTARGET_ARMARCH)
+elseif(CLR_CMAKE_TARGET_ARCH_ARM64)
+  add_definitions(-DTARGET_ARM64)
+  add_definitions(-DTARGET_ARMARCH)
+endif()
+set(SOURCES
+  Int128Native.cpp
+  UInt128Native.cpp
+)
+add_library (Int128Native SHARED ${SOURCES})
+install (TARGETS Int128Native DESTINATION bin)
diff --git a/src/tests/Interop/PInvoke/Int128/Int128Native.cpp b/src/tests/Interop/PInvoke/Int128/Int128Native.cpp
new file mode 100644 (file)
index 0000000..28f70bc
--- /dev/null
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include <stdio.h>
+#include <stdint.h>
+#include <xplatform.h>
+#include <platformdefines.h>
+
+#if (INT128_WIDTH == 128)
+    typedef int128_t Int128;
+#elif defined(__SIZEOF_INT128__)
+    typedef __int128 Int128;
+#else
+    typedef struct {
+        uint64_t lower;
+        uint64_t upper;
+    } Int128;
+#endif
+
+static Int128 Int128Value = { };
+
+extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE GetInt128(uint64_t upper, uint64_t lower)
+{
+    Int128 result;
+
+#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
+    result = upper;
+    result = result << 64;
+    result = result | lower;
+#else
+    result.lower = lower;
+    result.upper = upper;
+#endif
+
+    return result;
+}
+
+extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetInt128Out(uint64_t upper, uint64_t lower, Int128* pValue)
+{
+    Int128 value = GetInt128(upper, lower);
+    *pValue = value;
+}
+
+extern "C" DLL_EXPORT const Int128* STDMETHODCALLTYPE GetInt128Ptr(uint64_t upper, uint64_t lower)
+{
+    GetInt128Out(upper, lower, &Int128Value);
+    return &Int128Value;
+}
+
+extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128(Int128 lhs, Int128 rhs)
+{
+    Int128 result;
+
+#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
+    result = lhs + rhs;
+#else
+    result.lower = lhs.lower + rhs.lower;
+    uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
+    result.upper = lhs.upper + rhs.upper + carry;
+#endif
+
+    return result;
+}
+
+extern "C" DLL_EXPORT Int128 STDMETHODCALLTYPE AddInt128s(const Int128* pValues, uint32_t count)
+{
+    Int128 result = {};
+
+    for (uint32_t i = 0; i < count; i++)
+    {
+        result = AddInt128(result, pValues[i]);
+    }
+
+    return result;
+}
diff --git a/src/tests/Interop/PInvoke/Int128/Int128Test.cs b/src/tests/Interop/PInvoke/Int128/Int128Test.cs
new file mode 100644 (file)
index 0000000..5a9ddd5
--- /dev/null
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+using Xunit;
+
+unsafe partial class Int128Native
+{
+    [DllImport(nameof(Int128Native))]
+    public static extern Int128 GetInt128(ulong upper, ulong lower);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern void GetInt128Out(ulong upper, ulong lower, Int128* value);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern void GetInt128Out(ulong upper, ulong lower, out Int128 value);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern Int128* GetInt128Ptr(ulong upper, ulong lower);
+
+    [DllImport(nameof(Int128Native), EntryPoint = "GetInt128Ptr")]
+    public static extern ref readonly Int128 GetInt128Ref(ulong upper, ulong lower);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern Int128 AddInt128(Int128 lhs, Int128 rhs);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern Int128 AddInt128s(Int128* pValues, int count);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern Int128 AddInt128s([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] Int128[] pValues, int count);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern Int128 AddInt128s(in Int128 pValues, int count);
+}
+
+unsafe partial class Int128Native
+{
+    private static void TestInt128()
+    {
+        Int128 value1 = Int128Native.GetInt128(1, 2);
+        Assert.Equal(new Int128(1, 2), value1);
+
+        Int128 value2;
+        Int128Native.GetInt128Out(3, 4, &value2);
+        Assert.Equal(new Int128(3, 4), value2);
+
+        Int128Native.GetInt128Out(5, 6, out Int128 value3);
+        Assert.Equal(new Int128(5, 6), value3);
+
+        Int128* value4 = Int128Native.GetInt128Ptr(7, 8);
+        Assert.Equal(new Int128(7, 8), *value4);
+
+        ref readonly Int128 value5 = ref Int128Native.GetInt128Ref(9, 10);
+        Assert.Equal(new Int128(9, 10), value5);
+
+        Int128 value6 = Int128Native.AddInt128(new Int128(11, 12), new Int128(13, 14));
+        Assert.Equal(new Int128(24, 26), value6);
+
+        Int128[] values = new Int128[] {
+            new Int128(15, 16),
+            new Int128(17, 18),
+            new Int128(19, 20),
+            new Int128(21, 22),
+            new Int128(23, 24),
+        };
+
+        fixed (Int128* pValues = &values[0])
+        {
+            Int128 value7 = Int128Native.AddInt128s(pValues, values.Length);
+            Assert.Equal(new Int128(95, 100), value7);
+        }
+
+        Int128 value8 = Int128Native.AddInt128s(values, values.Length);
+        Assert.Equal(new Int128(95, 100), value8);
+
+        Int128 value9 = Int128Native.AddInt128s(in values[0], values.Length);
+        Assert.Equal(new Int128(95, 100), value9);
+    }
+}
diff --git a/src/tests/Interop/PInvoke/Int128/Int128Test.csproj b/src/tests/Interop/PInvoke/Int128/Int128Test.csproj
new file mode 100644 (file)
index 0000000..42fc09c
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
+    <DebugType>embedded</DebugType>
+    <OutputType>exe</OutputType>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="*.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <CMakeProjectReference Include="CMakeLists.txt" />
+  </ItemGroup>
+</Project>
diff --git a/src/tests/Interop/PInvoke/Int128/Program.cs b/src/tests/Interop/PInvoke/Int128/Program.cs
new file mode 100644 (file)
index 0000000..effd0b8
--- /dev/null
@@ -0,0 +1,26 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+unsafe partial class Int128Native
+{
+    public static int Main(string[] args)
+    {
+        try
+        {
+            Console.WriteLine("Testing Int128");
+            TestInt128();
+
+            Console.WriteLine("Testing UInt128");
+            TestUInt128();
+        }
+        catch (System.Exception ex)
+        {
+            Console.WriteLine(ex);
+            return 0;
+        }
+        return 100;
+    }
+}
diff --git a/src/tests/Interop/PInvoke/Int128/UInt128Native.cpp b/src/tests/Interop/PInvoke/Int128/UInt128Native.cpp
new file mode 100644 (file)
index 0000000..6d561cf
--- /dev/null
@@ -0,0 +1,75 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+#include <stdio.h>
+#include <stdint.h>
+#include <xplatform.h>
+#include <platformdefines.h>
+
+#if (UINT128_WIDTH == 128)
+    typedef uint128_t UInt128;
+#elif defined(__SIZEOF_INT128__)
+    typedef unsigned __int128 UInt128;
+#else
+    typedef struct {
+        uint64_t lower;
+        uint64_t upper;
+    } UInt128;
+#endif
+
+static UInt128 UInt128Value = { };
+
+extern "C" DLL_EXPORT UInt128 STDMETHODCALLTYPE GetUInt128(uint64_t upper, uint64_t lower)
+{
+    UInt128 result;
+
+#if (INT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
+    result = upper;
+    result = result << 64;
+    result = result | lower;
+#else
+    result.lower = lower;
+    result.upper = upper;
+#endif
+
+    return result;
+}
+
+extern "C" DLL_EXPORT void STDMETHODCALLTYPE GetUInt128Out(uint64_t upper, uint64_t lower, UInt128* pValue)
+{
+    UInt128 value = GetUInt128(upper, lower);
+    *pValue = value;
+}
+
+extern "C" DLL_EXPORT const UInt128* STDMETHODCALLTYPE GetUInt128Ptr(uint64_t upper, uint64_t lower)
+{
+    GetUInt128Out(upper, lower, &UInt128Value);
+    return &UInt128Value;
+}
+
+extern "C" DLL_EXPORT UInt128 STDMETHODCALLTYPE AddUInt128(UInt128 lhs, UInt128 rhs)
+{
+    UInt128 result;
+
+#if (UINT128_WIDTH == 128) || defined(__SIZEOF_INT128__)
+    result = lhs + rhs;
+#else
+    result.lower = lhs.lower + rhs.lower;
+    uint64_t carry = (result.lower < lhs.lower) ? 1 : 0;
+    result.upper = lhs.upper + rhs.upper + carry;
+#endif
+
+    return result;
+}
+
+extern "C" DLL_EXPORT UInt128 STDMETHODCALLTYPE AddUInt128s(const UInt128* pValues, uint32_t count)
+{
+    UInt128 result = {};
+
+    for (uint32_t i = 0; i < count; i++)
+    {
+        result = AddUInt128(result, pValues[i]);
+    }
+
+    return result;
+}
diff --git a/src/tests/Interop/PInvoke/Int128/UInt128Test.cs b/src/tests/Interop/PInvoke/Int128/UInt128Test.cs
new file mode 100644 (file)
index 0000000..6398a77
--- /dev/null
@@ -0,0 +1,81 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+using Xunit;
+
+unsafe partial class Int128Native
+{
+    [DllImport(nameof(Int128Native))]
+    public static extern UInt128 GetUInt128(ulong upper, ulong lower);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern void GetUInt128Out(ulong upper, ulong lower, UInt128* value);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern void GetUInt128Out(ulong upper, ulong lower, out UInt128 value);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern UInt128* GetUInt128Ptr(ulong upper, ulong lower);
+
+    [DllImport(nameof(Int128Native), EntryPoint = "GetUInt128Ptr")]
+    public static extern ref readonly UInt128 GetUInt128Ref(ulong upper, ulong lower);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern UInt128 AddUInt128(UInt128 lhs, UInt128 rhs);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern UInt128 AddUInt128s(UInt128* pValues, int count);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern UInt128 AddUInt128s([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] UInt128[] pValues, int count);
+
+    [DllImport(nameof(Int128Native))]
+    public static extern UInt128 AddUInt128s(in UInt128 pValues, int count);
+}
+
+unsafe partial class Int128Native
+{
+    private static void TestUInt128()
+    {
+        UInt128 value1 = Int128Native.GetUInt128(1, 2);
+        Assert.Equal(new UInt128(1, 2), value1);
+
+        UInt128 value2;
+        Int128Native.GetUInt128Out(3, 4, &value2);
+        Assert.Equal(new UInt128(3, 4), value2);
+
+        Int128Native.GetUInt128Out(5, 6, out UInt128 value3);
+        Assert.Equal(new UInt128(5, 6), value3);
+
+        UInt128* value4 = Int128Native.GetUInt128Ptr(7, 8);
+        Assert.Equal(new UInt128(7, 8), *value4);
+
+        ref readonly UInt128 value5 = ref Int128Native.GetUInt128Ref(9, 10);
+        Assert.Equal(new UInt128(9, 10), value5);
+
+        UInt128 value6 = Int128Native.AddUInt128(new UInt128(11, 12), new UInt128(13, 14));
+        Assert.Equal(new UInt128(24, 26), value6);
+
+        UInt128[] values = new UInt128[] {
+            new UInt128(15, 16),
+            new UInt128(17, 18),
+            new UInt128(19, 20),
+            new UInt128(21, 22),
+            new UInt128(23, 24),
+        };
+
+        fixed (UInt128* pValues = &values[0])
+        {
+            UInt128 value7 = Int128Native.AddUInt128s(pValues, values.Length);
+            Assert.Equal(new UInt128(95, 100), value7);
+        }
+
+        UInt128 value8 = Int128Native.AddUInt128s(values, values.Length);
+        Assert.Equal(new UInt128(95, 100), value8);
+
+        UInt128 value9 = Int128Native.AddUInt128s(in values[0], values.Length);
+        Assert.Equal(new UInt128(95, 100), value9);
+    }
+}
index 91a5063..79446ba 100644 (file)
         <ExcludeList Include="$(XunitTestBinBase)/Interop/PInvoke/Generics/GenericsTest/GenericsTest/**">
             <Issue>needs triage</Issue>
         </ExcludeList>
+        <ExcludeList Include="$(XunitTestBinBase)/Interop/PInvoke/Int128/Int128Test/Int128Test/**">
+            <Issue>needs triage</Issue>
+        </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/Interop/PInvoke/SafeHandles/**">
             <Issue>https://github.com/dotnet/runtime/issues/48084</Issue>
         </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/Interop/ICustomMarshaler/ConflictingNames/SameNameDifferentAssembly/**">
             <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
         </ExcludeList>
+        <ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Int128/**">
+            <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
+        </ExcludeList>
         <ExcludeList Include = "$(XunitTestBinBase)/Interop/PInvoke/Miscellaneous/HandleRef/HandleRefTest/**">
             <Issue>https://github.com/dotnet/runtime/issues/41519</Issue>
         </ExcludeList>