diff --git a/THIRD-PARTY-NOTICES.TXT b/THIRD-PARTY-NOTICES.TXT index fc250aa043ea9..307da15465cec 100644 --- a/THIRD-PARTY-NOTICES.TXT +++ b/THIRD-PARTY-NOTICES.TXT @@ -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 +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 index 0000000000000..60be29fdce95c --- /dev/null +++ b/src/coreclr/tools/Common/Compiler/Int128FieldLayoutAlgorithm.cs @@ -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 +{ + /// + /// Represents an algorithm that computes field layout for intrinsic integer types (Int128/UInt128). + /// + 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")); + } + } +} diff --git a/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs b/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs index c4105d1ce9173..0c331f9017dbc 100644 --- a/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs +++ b/src/coreclr/tools/Common/JitInterface/SystemVStructClassificator.cs @@ -30,7 +30,7 @@ internal SystemVStructRegisterPassingHelper(int totalStructSize) 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 @@ public static void GetSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDe { 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 @@ public static void GetSystemVAmd64PassStructInRegisterDescriptor(TypeDesc typeDe 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 @@ static SystemVClassificationType ReClassifyField(SystemVClassificationType origi /// /// Returns 'true' if the struct is passed in registers, 'false' otherwise. /// - private static bool ClassifyEightBytes(TypeDesc typeDesc, + private static bool ClassifyEightBytes(TypeDesc typeDesc, ref SystemVStructRegisterPassingHelper helper, int startOffsetOfStruct) { @@ -239,14 +239,15 @@ private static bool ClassifyEightBytes(TypeDesc typeDesc, 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 @@ private static bool ClassifyEightBytes(TypeDesc typeDesc, bool structRet = false; structRet = ClassifyEightBytes(field.FieldType, ref helper, normalizedFieldOffset); - + helper.InEmbeddedStruct = inEmbeddedStructPrev; if (!structRet) @@ -482,7 +483,7 @@ private static void AssignClassifiedEightByteTypes(ref SystemVStructRegisterPass else if ((helper.EightByteClassifications[currentFieldEightByte] == SystemVClassificationTypeInteger) || (fieldClassificationType == SystemVClassificationTypeInteger)) { - Debug.Assert((fieldClassificationType != SystemVClassificationTypeIntegerReference) && + Debug.Assert((fieldClassificationType != SystemVClassificationTypeIntegerReference) && (fieldClassificationType != SystemVClassificationTypeIntegerByRef)); helper.EightByteClassifications[currentFieldEightByte] = SystemVClassificationTypeInteger; diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs index 66e6044d418a1..4bdb57bb3d9ad 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/CompilerTypeSystemContext.Aot.cs @@ -33,6 +33,7 @@ public SharedGenericsConfiguration GenericsConfig 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 @@ public CompilerTypeSystemContext(TargetDetails details, SharedGenericsMode gener _vectorOfTFieldLayoutAlgorithm = new VectorOfTFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); _vectorFieldLayoutAlgorithm = new VectorFieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); + _int128FieldLayoutAlgorithm = new Int128FieldLayoutAlgorithm(_metadataFieldLayoutAlgorithm); _delegateInfoHashtable = new DelegateInfoHashtable(delegateFeatures); @@ -72,6 +74,8 @@ public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) return _vectorOfTFieldLayoutAlgorithm; else if (VectorFieldLayoutAlgorithm.IsVectorType(type)) return _vectorFieldLayoutAlgorithm; + else if (Int128FieldLayoutAlgorithm.IsIntegerType(type)) + return _int128FieldLayoutAlgorithm; else return _metadataFieldLayoutAlgorithm; } @@ -220,8 +224,8 @@ public class SharedGenericsConfiguration // 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() diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj index dee4e194df86c..60fe6d2394840 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj @@ -303,6 +303,7 @@ + diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs index fdb6fab3e91de..bd9eb74423074 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/Compiler/ReadyToRunCompilerContext.cs @@ -36,6 +36,7 @@ public partial class ReadyToRunCompilerContext : CompilerTypeSystemContext 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 @@ public ReadyToRunCompilerContext(TargetDetails details, SharedGenericsMode gener // No architecture has completely stable handling of Vector 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 @@ public override FieldLayoutAlgorithm GetLayoutAlgorithmForType(DefType type) { return _vectorFieldLayoutAlgorithm; } + else if (Int128FieldLayoutAlgorithm.IsIntegerType(type)) + { + return _int128FieldLayoutAlgorithm; + } else { Debug.Assert(_r2rFieldLayoutAlgorithm != null); diff --git a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj index f779f573e766b..e45ee891ad888 100644 --- a/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj +++ b/src/coreclr/tools/aot/ILCompiler.ReadyToRun/ILCompiler.ReadyToRun.csproj @@ -91,6 +91,7 @@ + diff --git a/src/coreclr/vm/classlayoutinfo.cpp b/src/coreclr/vm/classlayoutinfo.cpp index 2461214cfaf60..c89488a2427f1 100644 --- a/src/coreclr/vm/classlayoutinfo.cpp +++ b/src/coreclr/vm/classlayoutinfo.cpp @@ -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))) { diff --git a/src/coreclr/vm/classnames.h b/src/coreclr/vm/classnames.h index 7d71ce2c7d891..dee85ad23b160 100644 --- a/src/coreclr/vm/classnames.h +++ b/src/coreclr/vm/classnames.h @@ -38,6 +38,12 @@ #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" diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index 5d02b0881d563..8a1583a434e6c 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -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) diff --git a/src/coreclr/vm/methodtablebuilder.cpp b/src/coreclr/vm/methodtablebuilder.cpp index bb330ee8654cd..47563cbe66ef4 100644 --- a/src/coreclr/vm/methodtablebuilder.cpp +++ b/src/coreclr/vm/methodtablebuilder.cpp @@ -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: __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) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index eaf02291e3f97..6664593163714 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -522,6 +522,9 @@ Object must be of type Int64. + + Object must be of type Int128. + Object must be of type IntPtr. @@ -567,6 +570,9 @@ Object must be of type UInt64. + + Object must be of type UInt128. + Object must be of type UIntPtr. @@ -3022,6 +3028,9 @@ Value was either too large or too small for an Int64. + + Value was either too large or too small for an Int128. + The current thread attempted to reacquire a mutex that has reached its maximum acquire count. @@ -3049,6 +3058,9 @@ Value was either too large or too small for a UInt64. + + Value was either too large or too small for a UInt128. + ArgIterator is not supported on this platform. diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index db7442f1cd244..7464a11763580 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -411,6 +411,7 @@ + @@ -1164,6 +1165,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/FormattingHelpers.CountDigits.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/FormattingHelpers.CountDigits.cs index bf4de4f19a193..fd6ce30c9e14b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/FormattingHelpers.CountDigits.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/FormattingHelpers.CountDigits.cs @@ -9,6 +9,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) { @@ -100,6 +126,13 @@ public static int CountDigits(uint value) return digits; } + [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) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs index cde5b26719205..1a5834c08d42f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs @@ -17,7 +17,7 @@ public partial struct Decimal 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(ref d); diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs index 721b7c0e60f36..0427d1842fa58 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs @@ -1296,6 +1296,10 @@ public static decimal CreateChecked(TOther value) { 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 @@ public static decimal CreateChecked(TOther value) { 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 @@ public static decimal CreateSaturating(TOther value) { 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 @@ public static decimal CreateSaturating(TOther value) { 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 @@ public static decimal CreateTruncating(TOther value) { 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 @@ public static decimal CreateTruncating(TOther value) { 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 @@ public static bool TryCreate(TOther value, out decimal result) 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 @@ public static bool TryCreate(TOther value, out decimal result) 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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index cae158eab29c3..98fed8885b55b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -1016,6 +1016,10 @@ public static double CreateChecked(TOther value) { 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 @@ public static double CreateChecked(TOther value) { 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 @@ public static double CreateSaturating(TOther value) { 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 @@ public static double CreateSaturating(TOther value) { 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 @@ public static double CreateTruncating(TOther value) { 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 @@ public static double CreateTruncating(TOther value) { 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 @@ public static bool TryCreate(TOther value, out double result) 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 @@ public static bool TryCreate(TOther value, out double result) 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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index 219a409300a56..c538d15878f5e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -1214,6 +1214,10 @@ public static Half CreateChecked(TOther value) { 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 @@ public static Half CreateChecked(TOther value) { 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 @@ public static Half CreateSaturating(TOther value) { 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 @@ public static Half CreateSaturating(TOther value) { 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 @@ public static Half CreateTruncating(TOther value) { 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 @@ public static Half CreateTruncating(TOther value) { 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 @@ public static bool TryCreate(TOther value, out Half result) 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 @@ public static bool TryCreate(TOther value, out Half result) 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 @@ public static bool TryCreate(TOther value, out Half result) // /// - static Half INumberBase.One => new Half(PositiveOneBits); + public static Half One => new Half(PositiveOneBits); /// - static Half INumberBase.Zero => new Half(PositiveZeroBits); + public static Half Zero => new Half(PositiveZeroBits); // // IParsable @@ -1527,7 +1561,7 @@ public static bool TryCreate(TOther value, out Half result) // /// - static Half ISignedNumber.NegativeOne => new Half(NegativeOneBits); + public static Half NegativeOne => new Half(NegativeOneBits); // // ISpanParsable diff --git a/src/libraries/System.Private.CoreLib/src/System/IComparable.cs b/src/libraries/System.Private.CoreLib/src/System/IComparable.cs index 3599bbdf9ded6..0532833ac97ab 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IComparable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IComparable.cs @@ -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. - // + + /// 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. + /// An object to compare with this instance. + /// + /// A value that indicates the relative order of the objects being compared. The return value has these meanings: + /// + /// + /// Value + /// Meaning + /// + /// + /// Less than zero + /// This instance precedes in the sort order. + /// + /// + /// Zero + /// This instance occurs in the same position in the sort order as . + /// + /// + /// Greater than zero + /// This instance follows in the sort order. + /// + /// + /// + /// is not the same type as this instance. int CompareTo(object? obj); } @@ -27,12 +44,30 @@ public interface IComparable 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. - // + + /// 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. + /// An object to compare with this instance. + /// + /// A value that indicates the relative order of the objects being compared. The return value has these meanings: + /// + /// + /// Value + /// Meaning + /// + /// + /// Less than zero + /// This instance precedes in the sort order. + /// + /// + /// Zero + /// This instance occurs in the same position in the sort order as . + /// + /// + /// Greater than zero + /// This instance follows in the sort order. + /// + /// + /// int CompareTo(T? other); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/IEquatable.cs b/src/libraries/System.Private.CoreLib/src/System/IEquatable.cs index 89f0771802a1b..815f41666816a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IEquatable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IEquatable.cs @@ -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 // invariant due to questionable semantics around equality and inheritance { + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + /// true if the current object is equal to the parameter; otherwise, false. 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 index 0000000000000..a5ab201d6dcfd --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -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 +{ + /// Represents a 128-bit signed integer. + [Intrinsic] + [StructLayout(LayoutKind.Sequential)] + public readonly struct Int128 + : IBinaryInteger, + IMinMaxValue, + ISignedNumber + { + 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; + + /// Initializes a new instance of the struct. + /// The upper 64-bits of the 128-bit value. + /// The lower 64-bits of the 128-bit value. + [CLSCompliant(false)] + public Int128(ulong upper, ulong lower) + { + _lower = lower; + _upper = upper; + } + + internal ulong Lower => _lower; + + internal ulong Upper => _upper; + + /// + 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); + } + } + + /// + public int CompareTo(Int128 value) + { + if (this < value) + { + return -1; + } + else if (this > value) + { + return 1; + } + else + { + return 0; + } + } + + /// + public override bool Equals([NotNullWhen(true)] object? obj) + { + return (obj is Int128 other) && Equals(other); + } + + /// + public bool Equals(Int128 other) + { + return this == other; + } + + /// + public override int GetHashCode() => HashCode.Combine(_lower, _upper); + + /// + 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 destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan 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 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 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 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 + // + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator byte(Int128 value) => (byte)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked byte(Int128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((byte)value._lower); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator char(Int128 value) => (char)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked char(Int128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((char)value._lower); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator decimal(Int128 value) + { + if (IsNegative(value)) + { + value = -value; + return -(decimal)(UInt128)(value); + } + return (decimal)(UInt128)(value); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator double(Int128 value) + { + if (IsNegative(value)) + { + value = -value; + return -(double)(UInt128)(value); + } + return (double)(UInt128)(value); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator Half(Int128 value) + { + if (IsNegative(value)) + { + value = -value; + return -(Half)(UInt128)(value); + } + return (Half)(UInt128)(value); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator short(Int128 value) => (short)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + 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); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator int(Int128 value) => (int)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + 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); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator long(Int128 value) => (long)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + 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); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator nint(Int128 value) => (nint)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + 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); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator sbyte(Int128 value) => (sbyte)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [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); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator float(Int128 value) + { + if (IsNegative(value)) + { + value = -value; + return -(float)(UInt128)(value); + } + return (float)(UInt128)(value); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator ushort(Int128 value) => (ushort)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked ushort(Int128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((ushort)value._lower); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator uint(Int128 value) => (uint)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked uint(Int128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((uint)value._lower); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator ulong(Int128 value) => value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked ulong(Int128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return value._lower; + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator UInt128(Int128 value) => new UInt128(value._upper, value._lower); + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked UInt128(Int128 value) + { + if ((long)value._upper < 0) + { + ThrowHelper.ThrowOverflowException(); + } + return new UInt128(value._upper, value._lower); + } + + /// Explicitly converts a 128-bit signed integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator nuint(Int128 value) => (nuint)value._lower; + + /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [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 + // + + /// Explicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + 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; + } + + /// Explicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + 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); + } + + /// Explicitly converts a value to a 128-bit signed integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit signed integer. + /// is not representable by . + 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 . 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; + } + } + + /// Explicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static explicit operator Int128(Half value) => (Int128)(double)(value); + + /// Explicitly converts a value to a 128-bit signed integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit signed integer. + /// is not representable by . + public static explicit operator checked Int128(Half value) => checked((Int128)(double)(value)); + + /// Explicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static explicit operator Int128(float value) => (Int128)(double)(value); + + /// Explicitly converts a value to a 128-bit signed integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit signed integer. + /// is not representable by . + public static explicit operator checked Int128(float value) => checked((Int128)(double)(value)); + + // + // Implicit Conversions To Int128 + // + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static implicit operator Int128(byte value) => new Int128(0, value); + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static implicit operator Int128(char value) => new Int128(0, value); + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static implicit operator Int128(short value) + { + long lower = value; + return new Int128((ulong)(lower >> 63), (ulong)lower); + } + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static implicit operator Int128(int value) + { + long lower = value; + return new Int128((ulong)(lower >> 63), (ulong)lower); + } + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static implicit operator Int128(long value) + { + long lower = value; + return new Int128((ulong)(lower >> 63), (ulong)lower); + } + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + public static implicit operator Int128(nint value) + { + long lower = value; + return new Int128((ulong)(lower >> 63), (ulong)lower); + } + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + [CLSCompliant(false)] + public static implicit operator Int128(sbyte value) + { + long lower = value; + return new Int128((ulong)(lower >> 63), (ulong)lower); + } + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + [CLSCompliant(false)] + public static implicit operator Int128(ushort value) => new Int128(0, value); + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + [CLSCompliant(false)] + public static implicit operator Int128(uint value) => new Int128(0, value); + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + [CLSCompliant(false)] + public static implicit operator Int128(ulong value) => new Int128(0, value); + + /// Implicitly converts a value to a 128-bit signed integer. + /// The value to convert. + /// converted to a 128-bit signed integer. + [CLSCompliant(false)] + public static implicit operator Int128(nuint value) => new Int128(0, value); + + // + // IAdditionOperators + // + + /// + 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); + } + + /// + 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 + // + + /// + static Int128 IAdditiveIdentity.AdditiveIdentity => default; + + // + // IBinaryInteger + // + + /// + public static (Int128 Quotient, Int128 Remainder) DivRem(Int128 left, Int128 right) + { + Int128 quotient = left / right; + return (quotient, left - (quotient * right)); + } + + /// + public static Int128 LeadingZeroCount(Int128 value) + { + if (value._upper == 0) + { + return 64 + ulong.LeadingZeroCount(value._lower); + } + return ulong.LeadingZeroCount(value._upper); + } + + /// + public static Int128 PopCount(Int128 value) + => ulong.PopCount(value._lower) + ulong.PopCount(value._upper); + + /// + public static Int128 RotateLeft(Int128 value, int rotateAmount) + => (value << rotateAmount) | (value >>> (128 - rotateAmount)); + + /// + public static Int128 RotateRight(Int128 value, int rotateAmount) + => (value >>> rotateAmount) | (value << (128 - rotateAmount)); + + /// + public static Int128 TrailingZeroCount(Int128 value) + { + if (value._lower == 0) + { + return 64 + ulong.TrailingZeroCount(value._upper); + } + return ulong.TrailingZeroCount(value._lower); + } + + /// + long IBinaryInteger.GetShortestBitLength() + { + Int128 value = this; + + if (IsPositive(value)) + { + return (Size * 8) - BitOperations.LeadingZeroCount(value); + } + else + { + return (Size * 8) + 1 - BitOperations.LeadingZeroCount(~value); + } + } + + /// + int IBinaryInteger.GetByteCount() => Size; + + /// + bool IBinaryInteger.TryWriteLittleEndian(Span 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 + // + + /// + public static bool IsPow2(Int128 value) => (PopCount(value) == 1U) && IsPositive(value); + + /// + 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 + // + + /// + public static Int128 operator &(Int128 left, Int128 right) => new Int128(left._upper & right._upper, left._lower & right._lower); + + /// + public static Int128 operator |(Int128 left, Int128 right) => new Int128(left._upper | right._upper, left._lower | right._lower); + + /// + public static Int128 operator ^(Int128 left, Int128 right) => new Int128(left._upper ^ right._upper, left._lower ^ right._lower); + + /// + public static Int128 operator ~(Int128 value) => new Int128(~value._upper, ~value._lower); + + // + // IComparisonOperators + // + + /// + 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); + } + } + + /// + 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); + } + } + + /// + 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); + } + } + + /// + 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 + // + + /// + public static Int128 operator --(Int128 value) => value - One; + + /// + public static Int128 operator checked --(Int128 value) => checked(value - One); + + // + // IDivisionOperators + // + + /// + 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 + ); + } + + /// + public static Int128 operator checked /(Int128 left, Int128 right) => left / right; + + // + // IEqualityOperators + // + + /// + public static bool operator ==(Int128 left, Int128 right) => (left._lower == right._lower) && (left._upper == right._upper); + + /// + public static bool operator !=(Int128 left, Int128 right) => (left._lower != right._lower) || (left._upper != right._upper); + + // + // IIncrementOperators + // + + /// + public static Int128 operator ++(Int128 value) => value + One; + + /// + public static Int128 operator checked ++(Int128 value) => checked(value + One); + + // + // IMinMaxValue + // + + /// + public static Int128 MinValue => new Int128(0x8000_0000_0000_0000, 0); + + /// + public static Int128 MaxValue => new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF); + + // + // IModulusOperators + // + + /// + 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 + // + + /// + static Int128 IMultiplicativeIdentity.MultiplicativeIdentity => One; + + // + // IMultiplyOperators + // + + /// + 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 + ); + } + + /// + 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 + // + + /// + public static Int128 Abs(Int128 value) + { + if (IsNegative(value)) + { + value = -value; + + if (IsNegative(value)) + { + Math.ThrowNegateTwosCompOverflow(); + } + } + return value; + } + + /// + 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; + } + + /// + 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; + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Int128 CreateChecked(TOther value) + where TOther : INumber + { + 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; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Int128 CreateSaturating(TOther value) + where TOther : INumber + { + 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; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Int128 CreateTruncating(TOther value) + where TOther : INumber + { + 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; + } + } + + /// + public static bool IsNegative(Int128 value) => (long)value._upper < 0; + + internal static bool IsPositive(Int128 value) => (long)value._upper >= 0; + + /// + public static Int128 Max(Int128 x, Int128 y) => (x >= y) ? x : y; + + /// + 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; + } + + /// + public static Int128 Min(Int128 x, Int128 y) => (x <= y) ? x : y; + + /// + 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; + } + + /// + public static int Sign(Int128 value) + { + if (IsNegative(value)) + { + return -1; + } + else if (value != default) + { + return 1; + } + else + { + return 0; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryCreate(TOther value, out Int128 result) + where TOther : INumber + { + 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 + // + + /// + public static Int128 One => new Int128(0, 1); + + /// + 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 + // + + /// + 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; + } + } + + /// + 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; + } + } + + /// + 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 + // + + /// + public static Int128 NegativeOne => new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF); + + // + // ISpanParsable + // + + /// + public static Int128 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); + + /// + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + /// + 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); + } + + /// + 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 + // + + /// + public static Int128 operator -(Int128 value) => Zero - value; + + /// + public static Int128 operator checked -(Int128 value) => checked(Zero - value); + + // + // IUnaryPlusOperators + // + + /// + public static Int128 operator +(Int128 value) => value; + } +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Math.cs b/src/libraries/System.Private.CoreLib/src/System/Math.cs index 1c54464cab852..c6951fbafe9c2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Math.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Math.cs @@ -1476,7 +1476,7 @@ public static unsafe double Truncate(double d) } [DoesNotReturn] - private static void ThrowMinMaxException(T min, T max) + internal static void ThrowMinMaxException(T min, T max) { throw new ArgumentException(SR.Format(SR.Argument_MinMaxValue, min, max)); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index 728a680aefd64..d84220ef8889b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -1171,6 +1171,214 @@ static unsafe bool TryFormatUInt64Slow(ulong value, ReadOnlySpan format, I } } + 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 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(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 format, IFormatProvider? provider, Span 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 format, IFormatProvider? provider, Span 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(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 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(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 format, IFormatProvider? provider, Span 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 format, IFormatProvider? provider, Span 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(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 @@ private static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span 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 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 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 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 format, out int digits) { char c = default; diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.NumberBuffer.cs b/src/libraries/System.Private.CoreLib/src/System/Number.NumberBuffer.cs index b68fe37cef6f0..b3f06b04f3f57 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.NumberBuffer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.NumberBuffer.cs @@ -14,10 +14,12 @@ internal static partial class Number 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 { diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index d9c0662e81d25..d0aef03aa3952 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -30,6 +30,8 @@ internal static partial class Number 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 @@ private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long va 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 @@ private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong 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 value, NumberStyles styles, NumberFormatInfo info) { ParsingStatus status = TryParseInt32(value, styles, info, out int result); @@ -219,6 +298,17 @@ internal static long ParseInt64(ReadOnlySpan value, NumberStyles styles, N return result; } + internal static Int128 ParseInt128(ReadOnlySpan 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 value, NumberStyles styles, NumberFormatInfo info) { ParsingStatus status = TryParseUInt32(value, styles, info, out uint result); @@ -241,6 +331,17 @@ internal static ulong ParseUInt64(ReadOnlySpan value, NumberStyles styles, return result; } + internal static UInt128 ParseUInt128(ReadOnlySpan 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 @@ internal static ParsingStatus TryParseInt32(ReadOnlySpan value, NumberStyl return TryParseInt32Number(value, styles, info, out result); } - private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) + private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan 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; + } + + /// Parses int limited to styles that make up NumberStyles.Integer. + internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan 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; + } + + /// Parses long inputs limited to styles that make up NumberStyles.Integer. + internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan 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 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(ref result)); + } + + return TryParseInt64Number(value, styles, info, out result); + } + + private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan 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; + } + + /// Parses Int128 inputs limited to styles that make up NumberStyles.Integer. + internal static ParsingStatus TryParseInt128IntegerStyle(ReadOnlySpan 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 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 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 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 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 @@ private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan value return ParsingStatus.OK; } - /// Parses int limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) + /// Parses uint limited to styles that make up NumberStyles.Integer. + internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) { Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); @@ -562,21 +1311,21 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } // 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 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } else if (info.AllowHyphenDuringParsing && num == '-') { - sign = -1; + overflow = true; index++; if ((uint)index >= (uint)value.Length) goto FalseExit; @@ -605,7 +1354,7 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } 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 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } } - bool overflow = false; int answer = 0; if (IsDigit(num)) @@ -630,7 +1378,7 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value 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 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value 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 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } 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 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value goto OverflowExit; } DoneAtEnd: - result = answer * sign; + result = (uint)answer; ParsingStatus status = ParsingStatus.OK; Exit: return status; @@ -695,6 +1442,8 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value 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 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value goto DoneAtEndButPotentialOverflow; } - /// Parses long inputs limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) + /// Parses uint limited to styles that make up NumberStyles.HexNumber. + internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan 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 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value 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 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value 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 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value goto OverflowExit; } DoneAtEnd: - result = answer * sign; + result = answer; ParsingStatus status = ParsingStatus.OK; Exit: return status; @@ -896,71 +1587,34 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt64(ReadOnlySpan 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(ref result)); - } - - return TryParseInt64Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan 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 value, NumberStyles styles, NumberFormatInfo info, out uint result) + internal static ParsingStatus TryParseUInt64(ReadOnlySpan 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 value, NumberStyles styles, NumberFormatInfo info, out uint result) + private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan 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 @@ private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan valu return ParsingStatus.OK; } - /// Parses uint limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) + /// Parses ulong limited to styles that make up NumberStyles.Integer. + internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) { Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); @@ -1045,7 +1699,7 @@ internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan valu } } - int answer = 0; + long answer = 0; if (IsDigit(num)) { @@ -1063,10 +1717,10 @@ internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan valu 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 @@ internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan valu 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 @@ internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan valu goto OverflowExit; } DoneAtEnd: - result = (uint)answer; + result = (ulong)answer; ParsingStatus status = ParsingStatus.OK; Exit: return status; @@ -1147,8 +1801,8 @@ internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan valu goto DoneAtEndButPotentialOverflow; } - /// Parses uint limited to styles that make up NumberStyles.HexNumber. - internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out uint result) + /// Parses ulong limited to styles that make up NumberStyles.HexNumber. + private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out ulong result) { Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); @@ -1172,7 +1826,7 @@ internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan va } bool overflow = false; - uint answer = 0; + ulong answer = 0; if (HexConverter.IsHexChar(num)) { @@ -1190,10 +1844,10 @@ internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan va 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 @@ internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan va } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) + internal static ParsingStatus TryParseUInt128(ReadOnlySpan 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 value, NumberStyles styles, NumberFormatInfo info, out ulong result) + private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan 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 @@ private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan valu return ParsingStatus.OK; } - /// Parses ulong limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) + /// Parses UInt128 limited to styles that make up NumberStyles.Integer. + internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) { Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); @@ -1381,7 +2035,7 @@ internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan valu } } - long answer = 0; + Int128 answer = 0; if (IsDigit(num)) { @@ -1399,10 +2053,10 @@ internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan valu 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 @@ internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan valu 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 @@ internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan valu 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 @@ internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan valu goto DoneAtEndButPotentialOverflow; } - /// Parses ulong limited to styles that make up NumberStyles.HexNumber. - private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out ulong result) + /// Parses UInt128 limited to styles that make up NumberStyles.HexNumber. + private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out UInt128 result) { Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); @@ -1508,7 +2164,7 @@ private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan val } bool overflow = false; - ulong answer = 0; + UInt128 answer = 0U; if (HexConverter.IsHexChar(num)) { @@ -1526,10 +2182,10 @@ private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan val 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 @@ private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan val 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 @@ private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan val 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 @@ internal enum ParsingStatus [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 @@ private static Exception GetException(ParsingStatus status, TypeCode type) 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(); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs index 57f9b763d041e..cddd18e4f54cf 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs @@ -161,6 +161,18 @@ public static nuint RoundUpToPowerOf2(nuint value) #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); + } + /// /// Count the number of leading zero bits in a mask. /// Similar in behavior to the x86 instruction LZCNT. @@ -234,6 +246,18 @@ public static int LeadingZeroCount(ulong value) 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); + } + /// /// Count the number of leading zero bits in a mask. /// Similar in behavior to the x86 instruction LZCNT. diff --git a/src/libraries/System.Private.CoreLib/src/System/Object.cs b/src/libraries/System.Private.CoreLib/src/System/Object.cs index fd21795401408..fe07737c64561 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Object.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Object.cs @@ -34,17 +34,17 @@ public Object() } #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. + /// Returns a string that represents the current object. + /// A string that represents the current object. 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). + /// Determines whether the specified object is equal to the current object. + /// The object to compare with the current object. + /// true if the specified object is equal to the current object; otherwise, false. public virtual bool Equals(object? obj) { return RuntimeHelpers.Equals(this, obj); @@ -69,16 +69,19 @@ public static bool ReferenceEquals(object? objA, object? objB) 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. + /// Serves as the default hash function. + /// A hash code for the current object. 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); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index 171f51195fcca..e83ecdd3a3e47 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -1005,6 +1005,10 @@ public static float CreateChecked(TOther value) { 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 @@ public static float CreateChecked(TOther value) { 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 @@ public static float CreateSaturating(TOther value) { 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 @@ public static float CreateSaturating(TOther value) { 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 @@ public static float CreateTruncating(TOther value) { 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 @@ public static float CreateTruncating(TOther value) { 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 @@ public static bool TryCreate(TOther value, out float result) 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 @@ public static bool TryCreate(TOther value, out float result) 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; diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index f85a1214e7d22..3e68ff73d8085 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -196,6 +196,12 @@ internal static void ThrowArgumentOutOfRange_TimeSpanTooLong() throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong); } + [DoesNotReturn] + internal static void ThrowOverflowException() + { + throw new OverflowException(); + } + [DoesNotReturn] internal static void ThrowOverflowException_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 index 0000000000000..785584a595be1 --- /dev/null +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -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 +{ + /// Represents a 128-bit unsigned integer. + [CLSCompliant(false)] + [Intrinsic] + [StructLayout(LayoutKind.Sequential)] + public readonly struct UInt128 + : IBinaryInteger, + IMinMaxValue, + IUnsignedNumber + { + 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; + + /// Initializes a new instance of the struct. + /// The upper 64-bits of the 128-bit value. + /// The lower 64-bits of the 128-bit value. + [CLSCompliant(false)] + public UInt128(ulong upper, ulong lower) + { + _lower = lower; + _upper = upper; + } + + internal ulong Lower => _lower; + + internal ulong Upper => _upper; + + /// + 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); + } + } + + /// + public int CompareTo(UInt128 value) + { + if (this < value) + { + return -1; + } + else if (this > value) + { + return 1; + } + else + { + return 0; + } + } + + /// + public override bool Equals([NotNullWhen(true)] object? obj) + { + return (obj is UInt128 other) && Equals(other); + } + + /// + public bool Equals(UInt128 other) + { + return this == other; + } + + /// + public override int GetHashCode() => HashCode.Combine(_lower, _upper); + + /// + 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 destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan 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 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 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 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 + // + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator byte(UInt128 value) => (byte)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked byte(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((byte)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator char(UInt128 value) => (char)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked char(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((char)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + 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); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + 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 . 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; + } + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator Half(UInt128 value) => (Half)(double)(value); + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator short(UInt128 value) => (short)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked short(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((short)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator int(UInt128 value) => (int)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked int(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((int)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator long(UInt128 value) => (long)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked long(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((long)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator Int128(UInt128 value) => new Int128(value._upper, value._lower); + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked Int128(UInt128 value) + { + if ((long)value._upper < 0) + { + ThrowHelper.ThrowOverflowException(); + } + return new Int128(value._upper, value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator nint(UInt128 value) => (nint)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + public static explicit operator checked nint(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((nint)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator sbyte(UInt128 value) => (sbyte)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked sbyte(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((sbyte)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + public static explicit operator float(UInt128 value) => (float)(double)(value); + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator ushort(UInt128 value) => (ushort)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked ushort(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((ushort)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator uint(UInt128 value) => (uint)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked uint(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return checked((uint)value._lower); + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator ulong(UInt128 value) => value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked ulong(UInt128 value) + { + if (value._upper != 0) + { + ThrowHelper.ThrowOverflowException(); + } + return value._lower; + } + + /// Explicitly converts a 128-bit unsigned integer to a value. + /// The value to convert. + /// converted to a . + [CLSCompliant(false)] + public static explicit operator nuint(UInt128 value) => (nuint)value._lower; + + /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a . + /// is not representable by . + [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 + // + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static explicit operator UInt128(decimal value) + { + value = decimal.Truncate(value); + + if (value < 0.0m) + { + ThrowHelper.ThrowOverflowException(); + } + return new UInt128(value.High, value.Low64); + } + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + 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); + } + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + 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 . 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; + } + } + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static explicit operator UInt128(Half value) => (UInt128)(double)(value); + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + public static explicit operator checked UInt128(Half value) => checked((UInt128)(double)(value)); + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static explicit operator UInt128(short value) + { + long lower = value; + return new UInt128((ulong)(lower >> 63), (ulong)lower); + } + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + public static explicit operator checked UInt128(short value) + { + if (value < 0) + { + ThrowHelper.ThrowOverflowException(); + } + return new UInt128(0, (ushort)value); + } + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static explicit operator UInt128(int value) + { + long lower = value; + return new UInt128((ulong)(lower >> 63), (ulong)lower); + } + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + public static explicit operator checked UInt128(int value) + { + if (value < 0) + { + ThrowHelper.ThrowOverflowException(); + } + return new UInt128(0, (uint)value); + } + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static explicit operator UInt128(long value) + { + long lower = value; + return new UInt128((ulong)(lower >> 63), (ulong)lower); + } + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + public static explicit operator checked UInt128(long value) + { + if (value < 0) + { + ThrowHelper.ThrowOverflowException(); + } + return new UInt128(0, (ulong)value); + } + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static explicit operator UInt128(nint value) + { + long lower = value; + return new UInt128((ulong)(lower >> 63), (ulong)lower); + } + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + public static explicit operator checked UInt128(nint value) + { + if (value < 0) + { + ThrowHelper.ThrowOverflowException(); + } + return new UInt128(0, (nuint)value); + } + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + [CLSCompliant(false)] + public static explicit operator UInt128(sbyte value) + { + long lower = value; + return new UInt128((ulong)(lower >> 63), (ulong)lower); + } + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + [CLSCompliant(false)] + public static explicit operator checked UInt128(sbyte value) + { + if (value < 0) + { + ThrowHelper.ThrowOverflowException(); + } + return new UInt128(0, (byte)value); + } + + /// Explicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static explicit operator UInt128(float value) => (UInt128)(double)(value); + + /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + /// is not representable by . + public static explicit operator checked UInt128(float value) => checked((UInt128)(double)(value)); + + // + // Implicit Conversions To UInt128 + // + + /// Implicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static implicit operator UInt128(byte value) => new UInt128(0, value); + + /// Implicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + public static implicit operator UInt128(char value) => new UInt128(0, value); + + /// Implicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + [CLSCompliant(false)] + public static implicit operator UInt128(ushort value) => new UInt128(0, value); + + /// Implicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + [CLSCompliant(false)] + public static implicit operator UInt128(uint value) => new UInt128(0, value); + + /// Implicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + [CLSCompliant(false)] + public static implicit operator UInt128(ulong value) => new UInt128(0, value); + + /// Implicitly converts a value to a 128-bit unsigned integer. + /// The value to convert. + /// converted to a 128-bit unsigned integer. + [CLSCompliant(false)] + public static implicit operator UInt128(nuint value) => new UInt128(0, value); + + private void WriteLittleEndianUnsafe(Span 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 + // + + /// + 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); + } + + /// + 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 + // + + /// + static UInt128 IAdditiveIdentity.AdditiveIdentity => default; + + // + // IBinaryInteger + // + + /// + public static (UInt128 Quotient, UInt128 Remainder) DivRem(UInt128 left, UInt128 right) + { + UInt128 quotient = left / right; + return (quotient, left - (quotient * right)); + } + + /// + public static UInt128 LeadingZeroCount(UInt128 value) + { + if (value._upper == 0) + { + return 64 + ulong.LeadingZeroCount(value._lower); + } + return ulong.LeadingZeroCount(value._upper); + } + + /// + public static UInt128 PopCount(UInt128 value) + => ulong.PopCount(value._lower) + ulong.PopCount(value._upper); + + /// + public static UInt128 RotateLeft(UInt128 value, int rotateAmount) + => (value << rotateAmount) | (value >>> (128 - rotateAmount)); + + /// + public static UInt128 RotateRight(UInt128 value, int rotateAmount) + => (value >>> rotateAmount) | (value << (128 - rotateAmount)); + + /// + public static UInt128 TrailingZeroCount(UInt128 value) + { + if (value._lower == 0) + { + return 64 + ulong.TrailingZeroCount(value._upper); + } + return ulong.TrailingZeroCount(value._lower); + } + + /// + long IBinaryInteger.GetShortestBitLength() + { + UInt128 value = this; + return (Size * 8) - BitOperations.LeadingZeroCount(value); + } + + /// + int IBinaryInteger.GetByteCount() => Size; + + /// + bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int bytesWritten) + { + if (destination.Length >= Size) + { + WriteLittleEndianUnsafe(destination); + bytesWritten = Size; + return true; + } + else + { + bytesWritten = 0; + return false; + } + } + + // + // IBinaryNumber + // + + /// + public static bool IsPow2(UInt128 value) => PopCount(value) == 1U; + + /// + public static UInt128 Log2(UInt128 value) + { + if (value._upper == 0) + { + return ulong.Log2(value._lower); + } + return 64 + ulong.Log2(value._upper); + } + + // + // IBitwiseOperators + // + + /// + public static UInt128 operator &(UInt128 left, UInt128 right) => new UInt128(left._upper & right._upper, left._lower & right._lower); + + /// + public static UInt128 operator |(UInt128 left, UInt128 right) => new UInt128(left._upper | right._upper, left._lower | right._lower); + + /// + public static UInt128 operator ^(UInt128 left, UInt128 right) => new UInt128(left._upper ^ right._upper, left._lower ^ right._lower); + + /// + public static UInt128 operator ~(UInt128 value) => new UInt128(~value._upper, ~value._lower); + + // + // IComparisonOperators + // + + /// + public static bool operator <(UInt128 left, UInt128 right) + { + return (left._upper < right._upper) + || (left._upper == right._upper) && (left._lower < right._lower); + } + + /// + public static bool operator <=(UInt128 left, UInt128 right) + { + return (left._upper < right._upper) + || (left._upper == right._upper) && (left._lower <= right._lower); + } + + /// + public static bool operator >(UInt128 left, UInt128 right) + { + return (left._upper > right._upper) + || (left._upper == right._upper) && (left._lower > right._lower); + } + + /// + public static bool operator >=(UInt128 left, UInt128 right) + { + return (left._upper > right._upper) + || (left._upper == right._upper) && (left._lower >= right._lower); + } + + // + // IDecrementOperators + // + + /// + public static UInt128 operator --(UInt128 value) => value - One; + + /// + public static UInt128 operator checked --(UInt128 value) => checked(value - One); + + // + // IDivisionOperators + // + + /// + 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 left, ReadOnlySpan 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 containing the value represented + // in the least number of elements possible. + + uint* pLeft = stackalloc uint[Size / sizeof(uint)]; + quotient.WriteLittleEndianUnsafe(new Span(pLeft, Size)); + Span left = new Span(pLeft, (Size / sizeof(uint)) - (BitOperations.LeadingZeroCount(quotient) / 32)); + + uint* pRight = stackalloc uint[Size / sizeof(uint)]; + divisor.WriteLittleEndianUnsafe(new Span(pRight, Size)); + Span right = new Span(pRight, (Size / sizeof(uint)) - (BitOperations.LeadingZeroCount(divisor) / 32)); + + Span rawBits = stackalloc uint[Size / sizeof(uint)]; + rawBits.Clear(); + Span 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 left, ReadOnlySpan 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); + } + } + + /// + public static UInt128 operator checked /(UInt128 left, UInt128 right) => left / right; + + // + // IEqualityOperators + // + + /// + public static bool operator ==(UInt128 left, UInt128 right) => (left._lower == right._lower) && (left._upper == right._upper); + + /// + public static bool operator !=(UInt128 left, UInt128 right) => (left._lower != right._lower) || (left._upper != right._upper); + + // + // IIncrementOperators + // + + /// + public static UInt128 operator ++(UInt128 value) => value + One; + + /// + public static UInt128 operator checked ++(UInt128 value) => checked(value + One); + + // + // IMinMaxValue + // + + /// + public static UInt128 MinValue => new UInt128(0, 0); + + /// + public static UInt128 MaxValue => new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF); + + // + // IModulusOperators + // + + /// + public static UInt128 operator %(UInt128 left, UInt128 right) + { + UInt128 quotient = left / right; + return left - (quotient * right); + } + + // + // IMultiplicativeIdentity + // + + /// + static UInt128 IMultiplicativeIdentity.MultiplicativeIdentity => One; + + // + // IMultiplyOperators + // + + /// + 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); + } + + /// + 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 + // + + /// + static UInt128 INumber.Abs(UInt128 value) => value; + + /// + 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; + } + + /// + static UInt128 INumber.CopySign(UInt128 value, UInt128 sign) => value; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt128 CreateChecked(TOther value) + where TOther : INumber + { + 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; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt128 CreateSaturating(TOther value) + where TOther : INumber + { + 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; + } + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static UInt128 CreateTruncating(TOther value) + where TOther : INumber + { + 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; + } + } + + /// + static bool INumber.IsNegative(UInt128 value) => false; + + /// + public static UInt128 Max(UInt128 x, UInt128 y) => (x >= y) ? x : y; + + /// + static UInt128 INumber.MaxMagnitude(UInt128 x, UInt128 y) => Max(x, y); + + /// + public static UInt128 Min(UInt128 x, UInt128 y) => (x <= y) ? x : y; + + /// + static UInt128 INumber.MinMagnitude(UInt128 x, UInt128 y) => Min(x, y); + + /// + public static int Sign(UInt128 value) => (value == 0U) ? 0 : 1; + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryCreate(TOther value, out UInt128 result) + where TOther : INumber + { + 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 + // + + /// + public static UInt128 One => new UInt128(0, 1); + + /// + 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 + // + + /// + 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; + } + } + + /// + public static UInt128 operator >>(UInt128 value, int shiftAmount) => value >>> shiftAmount; + + /// + 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 + // + + /// + public static UInt128 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); + + /// + public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider, out result); + + // + // ISubtractionOperators + // + + /// + 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); + } + + /// + 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 + // + + /// + public static UInt128 operator -(UInt128 value) => Zero - value; + + /// + public static UInt128 operator checked -(UInt128 value) => checked(Zero - value); + + // + // IUnaryPlusOperators + // + + /// + public static UInt128 operator +(UInt128 value) => value; + } +} diff --git a/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs b/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs index e33fe565f5042..55d5800bc004f 100644 --- a/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs +++ b/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs @@ -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; } diff --git a/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx b/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx index 89440751d30ea..38e69886f3fc1 100644 --- a/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx @@ -96,12 +96,18 @@ Value was either too large or too small for an Int64. + + Value was either too large or too small for an Int128. + Value was either too large or too small for a UInt32. Value was either too large or too small for a UInt64. + + Value was either too large or too small for a UInt128. + Value was either too large or too small for a Decimal. diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 0dc15c9e5ca4d..e541085069e71 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -1114,7 +1114,7 @@ public bool Equals(long other) 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 @@ public bool Equals(ulong other) 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 @@ public int CompareTo(long other) 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 @@ public int CompareTo(ulong other) 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 @@ public static implicit operator BigInteger(nuint value) } } + 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 @@ public static explicit operator long(BigInteger value) 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 @@ public static explicit operator ulong(BigInteger value) 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]; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs index 48fdce9235263..51643f6b4e4df 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigNumber.cs @@ -1041,7 +1041,7 @@ internal static bool TryFormatBigInteger(BigInteger value, ReadOnlySpan fo 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); } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs index ff9d263edcb51..ebee3f5bf1602 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/NumericsHelpers.cs @@ -127,7 +127,7 @@ public static void DangerousMakeTwosComplement(Span d) } } - public static ulong MakeUlong(uint uHi, uint uLo) + public static ulong MakeUInt64(uint uHi, uint uLo) { return ((ulong)uHi << kcbitUint) | uLo; } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index c21459ea9a771..8afd04908ba66 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2644,9 +2644,12 @@ public GopherStyleUriParser() { } 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.AdditiveIdentity { get { throw null; } } static System.Half System.Numerics.INumberBase.One { get { throw null; } } static System.Half System.Numerics.INumberBase.Zero { get { throw null; } } @@ -2906,6 +2909,157 @@ public InsufficientMemoryException() { } public InsufficientMemoryException(string? message) { } public InsufficientMemoryException(string? message, System.Exception? innerException) { } } + public readonly partial struct Int128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators + { + 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.AdditiveIdentity { get { throw null; } } + static System.Int128 System.Numerics.IMultiplicativeIdentity.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 value) where TOther : System.Numerics.INumber { throw null; } + public static System.Int128 CreateSaturating(TOther value) where TOther : System.Numerics.INumber { throw null; } + public static System.Int128 CreateTruncating(TOther value) where TOther : System.Numerics.INumber { 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 s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider? provider = null) { throw null; } + public static System.Int128 Parse(System.ReadOnlySpan 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.GetByteCount() { throw null; } + long System.Numerics.IBinaryInteger.GetShortestBitLength() { throw null; } + bool System.Numerics.IBinaryInteger.TryWriteLittleEndian(System.Span 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 value, out System.Int128 result) where TOther : System.Numerics.INumber { throw null; } + public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Int128 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan 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, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { private readonly short _dummyPrimitive; @@ -5580,6 +5734,163 @@ public TypeUnloadedException(string? message) { } public TypeUnloadedException(string? message, System.Exception? innerException) { } } [System.CLSCompliantAttribute(false)] + public readonly partial struct UInt128 : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber + { + 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.AdditiveIdentity { get { throw null; } } + static System.UInt128 System.Numerics.IMultiplicativeIdentity.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 value) where TOther : System.Numerics.INumber { throw null; } + public static System.UInt128 CreateSaturating(TOther value) where TOther : System.Numerics.INumber { throw null; } + public static System.UInt128 CreateTruncating(TOther value) where TOther : System.Numerics.INumber { 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 s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, System.IFormatProvider? provider = null) { throw null; } + public static System.UInt128 Parse(System.ReadOnlySpan 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.GetByteCount() { throw null; } + long System.Numerics.IBinaryInteger.GetShortestBitLength() { throw null; } + bool System.Numerics.IBinaryInteger.TryWriteLittleEndian(System.Span destination, out int bytesWritten) { throw null; } + static System.UInt128 System.Numerics.INumber.Abs(System.UInt128 value) { throw null; } + static System.UInt128 System.Numerics.INumber.CopySign(System.UInt128 value, System.UInt128 sign) { throw null; } + static bool System.Numerics.INumber.IsNegative(System.UInt128 value) { throw null; } + static System.UInt128 System.Numerics.INumber.MaxMagnitude(System.UInt128 x, System.UInt128 y) { throw null; } + static System.UInt128 System.Numerics.INumber.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 value, out System.UInt128 result) where TOther : System.Numerics.INumber { throw null; } + public bool TryFormat(System.Span destination, out int charsWritten, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("NumericFormat")] System.ReadOnlySpan format = default(System.ReadOnlySpan), System.IFormatProvider? provider = null) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.UInt128 result) { throw null; } + public static bool TryParse(System.ReadOnlySpan 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, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber { private readonly ushort _dummyPrimitive; diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj index 89596927bc5af..035e305a55cc2 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests.csproj @@ -85,6 +85,7 @@ + @@ -138,6 +139,7 @@ + @@ -260,12 +262,14 @@ + + diff --git a/src/libraries/System.Runtime/tests/System/DecimalTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/DecimalTests.GenericMath.cs index 8bc5a58c8f4ad..77a6ae8d50c22 100644 --- a/src/libraries/System.Runtime/tests/System/DecimalTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System/DecimalTests.GenericMath.cs @@ -317,6 +317,17 @@ public static void CreateCheckedFromInt64Test() Assert.Equal(-1.0m, NumberHelper.CreateChecked(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateCheckedFromInt128Test() + { + Assert.Equal(0.0m, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + Assert.Equal(1.0m, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + Assert.Equal(-1.0m, NumberHelper.CreateChecked(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + + Assert.Throws(() => NumberHelper.CreateChecked(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + Assert.Throws(() => NumberHelper.CreateChecked(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + } + [Fact] public static void CreateCheckedFromIntPtrTest() { @@ -378,6 +389,17 @@ public static void CreateCheckedFromUInt64Test() Assert.Equal(18446744073709551615.0m, NumberHelper.CreateChecked(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateCheckedFromUInt128Test() + { + Assert.Equal(0.0m, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + Assert.Equal(1.0m, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + + Assert.Throws(() => NumberHelper.CreateChecked(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + Assert.Throws(() => NumberHelper.CreateChecked(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + Assert.Throws(() => NumberHelper.CreateChecked(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + } + [Fact] public static void CreateCheckedFromUIntPtrTest() { @@ -449,6 +471,18 @@ public static void CreateSaturatingFromInt64Test() Assert.Equal(-1.0m, NumberHelper.CreateSaturating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateSaturatingFromInt128Test() + { + Assert.Equal(0.0m, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + + Assert.Equal(+1.0m, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + Assert.Equal(-1.0m, NumberHelper.CreateSaturating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + + Assert.Equal(decimal.MaxValue, NumberHelper.CreateSaturating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + Assert.Equal(decimal.MinValue, NumberHelper.CreateSaturating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + } + [Fact] public static void CreateSaturatingFromIntPtrTest() { @@ -510,6 +544,18 @@ public static void CreateSaturatingFromUInt64Test() Assert.Equal(18446744073709551615.0m, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateSaturatingFromUInt128Test() + { + Assert.Equal(0.0m, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + + Assert.Equal(+1.0m, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + Assert.Equal(decimal.MaxValue, NumberHelper.CreateSaturating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + + Assert.Equal(decimal.MaxValue, NumberHelper.CreateSaturating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + Assert.Equal(decimal.MaxValue, NumberHelper.CreateSaturating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + } + [Fact] public static void CreateSaturatingFromUIntPtrTest() { @@ -581,6 +627,18 @@ public static void CreateTruncatingFromInt64Test() Assert.Equal(-1.0m, NumberHelper.CreateTruncating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateTruncatingFromInt128Test() + { + Assert.Equal(0.0m, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + + Assert.Equal(+1.0m, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + Assert.Equal(-1.0m, NumberHelper.CreateTruncating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + + Assert.Equal(decimal.MaxValue, NumberHelper.CreateTruncating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + Assert.Equal(0.0m, NumberHelper.CreateTruncating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + } + [Fact] public static void CreateTruncatingFromIntPtrTest() { @@ -642,6 +700,18 @@ public static void CreateTruncatingFromUInt64Test() Assert.Equal(18446744073709551615.0m, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateTruncatingFromUInt128Test() + { + Assert.Equal(0.0m, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + + Assert.Equal(+1.0m, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + Assert.Equal(decimal.MaxValue, NumberHelper.CreateTruncating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + + Assert.Equal(decimal.MaxValue, NumberHelper.CreateTruncating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + Assert.Equal(0.0m, NumberHelper.CreateTruncating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + } + [Fact] public static void CreateTruncatingFromUIntPtrTest() { @@ -803,6 +873,27 @@ public static void TryCreateFromInt64Test() Assert.Equal(-1.0m, result); } + [Fact] + public static void TryCreateFromInt128Test() + { + decimal result; + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0m, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(1.0m, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(-1.0m, result); + + Assert.False(NumberHelper.TryCreate(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(0.0m, result); + + Assert.False(NumberHelper.TryCreate(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0m, result); + } + [Fact] public static void TryCreateFromIntPtrTest() { @@ -928,6 +1019,27 @@ public static void TryCreateFromUInt64Test() Assert.Equal(18446744073709551615.0m, result); } + [Fact] + public static void TryCreateFromUInt128Test() + { + decimal result; + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0m, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(1.0m, result); + + Assert.False(NumberHelper.TryCreate(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(0.0m, result); + + Assert.False(NumberHelper.TryCreate(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(0.0m, result); + + Assert.False(NumberHelper.TryCreate(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0m, result); + } + [Fact] public static void TryCreateFromUIntPtrTest() { diff --git a/src/libraries/System.Runtime/tests/System/DoubleTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/DoubleTests.GenericMath.cs index 677c0205b1b5d..6c9fc81275c77 100644 --- a/src/libraries/System.Runtime/tests/System/DoubleTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System/DoubleTests.GenericMath.cs @@ -542,6 +542,16 @@ public static void CreateCheckedFromInt64Test() AssertBitwiseEqual(-1.0, NumberHelper.CreateChecked(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateCheckedFromInt128Test() + { + AssertBitwiseEqual(0.0, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper.CreateChecked(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(-170141183460469231731687303715884105728.0, NumberHelper.CreateChecked(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(-1.0, NumberHelper.CreateChecked(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateCheckedFromIntPtrTest() { @@ -603,6 +613,16 @@ public static void CreateCheckedFromUInt64Test() AssertBitwiseEqual(18446744073709551615.0, NumberHelper.CreateChecked(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateCheckedFromUInt128Test() + { + AssertBitwiseEqual(0.0, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper.CreateChecked(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(170141183460469231731687303715884105728.0, NumberHelper.CreateChecked(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(340282366920938463463374607431768211455.0, NumberHelper.CreateChecked(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateCheckedFromUIntPtrTest() { @@ -678,6 +698,16 @@ public static void CreateSaturatingFromInt64Test() AssertBitwiseEqual(-1.0, NumberHelper.CreateSaturating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateSaturatingFromInt128Test() + { + AssertBitwiseEqual(0.0, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper.CreateSaturating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(-170141183460469231731687303715884105728.0, NumberHelper.CreateSaturating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(-1.0, NumberHelper.CreateSaturating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateSaturatingFromIntPtrTest() { @@ -739,6 +769,16 @@ public static void CreateSaturatingFromUInt64Test() AssertBitwiseEqual(18446744073709551615.0, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateSaturatingFromUInt128Test() + { + AssertBitwiseEqual(0.0, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper.CreateSaturating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(170141183460469231731687303715884105728.0, NumberHelper.CreateSaturating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(340282366920938463463374607431768211455.0, NumberHelper.CreateSaturating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateSaturatingFromUIntPtrTest() { @@ -814,6 +854,16 @@ public static void CreateTruncatingFromInt64Test() AssertBitwiseEqual(-1.0, NumberHelper.CreateTruncating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateTruncatingFromInt128Test() + { + AssertBitwiseEqual(0.0, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper.CreateTruncating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(-170141183460469231731687303715884105728.0, NumberHelper.CreateTruncating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(-1.0, NumberHelper.CreateTruncating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateTruncatingFromIntPtrTest() { @@ -875,6 +925,16 @@ public static void CreateTruncatingFromUInt64Test() AssertBitwiseEqual(18446744073709551615.0, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateTruncatingFromUInt128Test() + { + AssertBitwiseEqual(0.0, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0, NumberHelper.CreateTruncating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(170141183460469231731687303715884105728.0, NumberHelper.CreateTruncating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(340282366920938463463374607431768211455.0, NumberHelper.CreateTruncating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateTruncatingFromUIntPtrTest() { @@ -1068,6 +1128,27 @@ public static void TryCreateFromInt64Test() Assert.Equal(-1.0, result); } + [Fact] + public static void TryCreateFromInt128Test() + { + double result; + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(1.0, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(170141183460469231731687303715884105727.0, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(-170141183460469231731687303715884105728.0, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(-1.0, result); + } + [Fact] public static void TryCreateFromIntPtrTest() { @@ -1193,6 +1274,27 @@ public static void TryCreateFromUInt64Test() Assert.Equal(18446744073709551615.0, result); } + [Fact] + public static void TryCreateFromUInt128Test() + { + double result; + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(1.0, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(170141183460469231731687303715884105727.0, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(170141183460469231731687303715884105728.0, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(340282366920938463463374607431768211456.0, result); + } + [Fact] public static void TryCreateFromUIntPtrTest() { diff --git a/src/libraries/System.Runtime/tests/System/HalfTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/HalfTests.GenericMath.cs index c3ca50c950c48..246fe53b8c840 100644 --- a/src/libraries/System.Runtime/tests/System/HalfTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System/HalfTests.GenericMath.cs @@ -560,6 +560,16 @@ public static void CreateCheckedFromInt64Test() AssertBitwiseEqual(NegativeOne, NumberHelper.CreateChecked(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateCheckedFromInt128Test() + { + AssertBitwiseEqual(PositiveZero, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(PositiveOne, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0, NumberHelper.CreateChecked(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual((Half)(-170141183460469231731687303715884105728.0f), NumberHelper.CreateChecked(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(NegativeOne, NumberHelper.CreateChecked(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateCheckedFromIntPtrTest() { @@ -621,6 +631,16 @@ public static void CreateCheckedFromUInt64Test() AssertBitwiseEqual((Half)18446744073709551615.0f, NumberHelper.CreateChecked(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateCheckedFromUInt128Test() + { + AssertBitwiseEqual(PositiveZero, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(PositiveOne, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0, NumberHelper.CreateChecked(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105728.0, NumberHelper.CreateChecked(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual((Half)340282366920938463463374607431768211455.0, NumberHelper.CreateChecked(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateCheckedFromUIntPtrTest() { @@ -696,6 +716,16 @@ public static void CreateSaturatingFromInt64Test() AssertBitwiseEqual(NegativeOne, NumberHelper.CreateSaturating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateSaturatingFromInt128Test() + { + AssertBitwiseEqual(PositiveZero, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(PositiveOne, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper.CreateSaturating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual((Half)(-170141183460469231731687303715884105728.0f), NumberHelper.CreateSaturating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(NegativeOne, NumberHelper.CreateSaturating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateSaturatingFromIntPtrTest() { @@ -757,6 +787,16 @@ public static void CreateSaturatingFromUInt64Test() AssertBitwiseEqual((Half)18446744073709551615.0f, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateSaturatingFromUInt128Test() + { + AssertBitwiseEqual(PositiveZero, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(PositiveOne, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper.CreateSaturating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105728.0f, NumberHelper.CreateSaturating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(Half.PositiveInfinity, NumberHelper.CreateSaturating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateSaturatingFromUIntPtrTest() { @@ -832,6 +872,16 @@ public static void CreateTruncatingFromInt64Test() AssertBitwiseEqual(NegativeOne, NumberHelper.CreateTruncating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateTruncatingFromInt128Test() + { + AssertBitwiseEqual(PositiveZero, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(PositiveOne, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper.CreateTruncating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual((Half)(-170141183460469231731687303715884105728.0f), NumberHelper.CreateTruncating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(NegativeOne, NumberHelper.CreateTruncating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateTruncatingFromIntPtrTest() { @@ -893,6 +943,16 @@ public static void CreateTruncatingFromUInt64Test() AssertBitwiseEqual((Half)18446744073709551615.0f, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateTruncatingFromUInt128Test() + { + AssertBitwiseEqual(PositiveZero, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(PositiveOne, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105727.0f, NumberHelper.CreateTruncating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual((Half)170141183460469231731687303715884105728.0f, NumberHelper.CreateTruncating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(Half.PositiveInfinity, NumberHelper.CreateTruncating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateTruncatingFromUIntPtrTest() { @@ -1086,6 +1146,27 @@ public static void TryCreateFromInt64Test() Assert.Equal(NegativeOne, result); } + [Fact] + public static void TryCreateFromInt128Test() + { + Half result; + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(PositiveZero, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(PositiveOne, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal((Half)170141183460469231731687303715884105727.0, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal((Half)(-170141183460469231731687303715884105728.0), result); + + Assert.True(NumberHelper.TryCreate(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(NegativeOne, result); + } + [Fact] public static void TryCreateFromIntPtrTest() { @@ -1211,6 +1292,27 @@ public static void TryCreateFromUInt64Test() Assert.Equal((Half)18446744073709551615.0f, result); } + [Fact] + public static void TryCreateFromUInt128Test() + { + Half result; + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(PositiveZero, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(PositiveOne, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal((Half)170141183460469231731687303715884105727.0f, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal((Half)170141183460469231731687303715884105728.0f, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(Half.PositiveInfinity, result); + } + [Fact] public static void TryCreateFromUIntPtrTest() { 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 index 0000000000000..1ff3b1e917a39 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int128Tests.GenericMath.cs @@ -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.op_Addition(Zero, 1)); + Assert.Equal(Two, AdditionOperatorsHelper.op_Addition(One, 1)); + Assert.Equal(MinValue, AdditionOperatorsHelper.op_Addition(MaxValue, 1)); + Assert.Equal(MinValuePlusOne, AdditionOperatorsHelper.op_Addition(MinValue, 1)); + Assert.Equal(Zero, AdditionOperatorsHelper.op_Addition(NegativeOne, 1)); + } + + [Fact] + public static void op_CheckedAdditionTest() + { + Assert.Equal(One, AdditionOperatorsHelper.op_CheckedAddition(Zero, 1)); + Assert.Equal(Two, AdditionOperatorsHelper.op_CheckedAddition(One, 1)); + Assert.Equal(MinValuePlusOne, AdditionOperatorsHelper.op_CheckedAddition(MinValue, 1)); + Assert.Equal(Zero, AdditionOperatorsHelper.op_CheckedAddition(NegativeOne, 1)); + + Assert.Throws(() => AdditionOperatorsHelper.op_CheckedAddition(MaxValue, 1)); + } + + // + // IAdditiveIdentity + // + + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal(Zero, AdditiveIdentityHelper.AdditiveIdentity); + } + + // + // IBinaryInteger + // + + [Fact] + public static void DivRemTest() + { + Assert.Equal((Zero, Zero), BinaryIntegerHelper.DivRem(Zero, 2)); + Assert.Equal((Zero, One), BinaryIntegerHelper.DivRem(One, 2)); + Assert.Equal((new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), One), BinaryIntegerHelper.DivRem(MaxValue, 2)); + Assert.Equal((new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), Zero), BinaryIntegerHelper.DivRem(MinValue, 2)); + Assert.Equal((Zero, NegativeOne), BinaryIntegerHelper.DivRem(NegativeOne, 2)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal(0x80, BinaryIntegerHelper.LeadingZeroCount(Zero)); + Assert.Equal(0x7F, BinaryIntegerHelper.LeadingZeroCount(One)); + Assert.Equal(0x01, BinaryIntegerHelper.LeadingZeroCount(MaxValue)); + Assert.Equal(0x00, BinaryIntegerHelper.LeadingZeroCount(MinValue)); + Assert.Equal(0x00, BinaryIntegerHelper.LeadingZeroCount(NegativeOne)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal(0x00, BinaryIntegerHelper.PopCount(Zero)); + Assert.Equal(0x01, BinaryIntegerHelper.PopCount(One)); + Assert.Equal(0x7F, BinaryIntegerHelper.PopCount(MaxValue)); + Assert.Equal(0x01, BinaryIntegerHelper.PopCount(MinValue)); + Assert.Equal(0x80, BinaryIntegerHelper.PopCount(NegativeOne)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateLeft(Zero, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0002), BinaryIntegerHelper.RotateLeft(One, 1)); + Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BinaryIntegerHelper.RotateLeft(MaxValue, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BinaryIntegerHelper.RotateLeft(MinValue, 1)); + Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper.RotateLeft(NegativeOne, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateRight(Zero, 1)); + Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateRight(One, 1)); + Assert.Equal(new Int128(0xBFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper.RotateRight(MaxValue, 1)); + Assert.Equal(new Int128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateRight(MinValue, 1)); + Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper.RotateRight(NegativeOne, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal(0x80, BinaryIntegerHelper.TrailingZeroCount(Zero)); + Assert.Equal(0x00, BinaryIntegerHelper.TrailingZeroCount(One)); + Assert.Equal(0x00, BinaryIntegerHelper.TrailingZeroCount(MaxValue)); + Assert.Equal(0x7F, BinaryIntegerHelper.TrailingZeroCount(MinValue)); + Assert.Equal(0x00, BinaryIntegerHelper.TrailingZeroCount(NegativeOne)); + } + + [Fact] + public static void GetByteCountTest() + { + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(Zero)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(One)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(MaxValue)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(MinValue)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(NegativeOne)); + } + + [Fact] + public static void GetShortestBitLengthTest() + { + Assert.Equal(0x00, BinaryIntegerHelper.GetShortestBitLength(Zero)); + Assert.Equal(0x01, BinaryIntegerHelper.GetShortestBitLength(One)); + Assert.Equal(0x7F, BinaryIntegerHelper.GetShortestBitLength(MaxValue)); + Assert.Equal(0x80, BinaryIntegerHelper.GetShortestBitLength(MinValue)); + Assert.Equal(0x01, BinaryIntegerHelper.GetShortestBitLength(NegativeOne)); + } + + [Fact] + public static void TryWriteLittleEndianTest() + { + Span destination = stackalloc byte[16]; + int bytesWritten = 0; + + Assert.True(BinaryIntegerHelper.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.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.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.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.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.TryWriteLittleEndian(default, Span.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.IsPow2(Zero)); + Assert.True(BinaryNumberHelper.IsPow2(One)); + Assert.False(BinaryNumberHelper.IsPow2(MaxValue)); + Assert.False(BinaryNumberHelper.IsPow2(MinValue)); + Assert.False(BinaryNumberHelper.IsPow2(NegativeOne)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal(0x00, BinaryNumberHelper.Log2(Zero)); + Assert.Equal(0x00, BinaryNumberHelper.Log2(One)); + Assert.Equal(0x7E, BinaryNumberHelper.Log2(MaxValue)); + Assert.Throws(() => BinaryNumberHelper.Log2(MinValue)); + Assert.Throws(() => BinaryNumberHelper.Log2(NegativeOne)); + } + + // + // IBitwiseOperators + // + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_BitwiseAnd(Zero, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseAnd(One, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseAnd(MaxValue, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_BitwiseAnd(MinValue, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseAnd(NegativeOne, 1)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseOr(Zero, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseOr(One, 1)); + Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_BitwiseOr(MaxValue, 1)); + Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseOr(MinValue, 1)); + Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_BitwiseOr(NegativeOne, 1)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_ExclusiveOr(Zero, 1)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_ExclusiveOr(One, 1)); + Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper.op_ExclusiveOr(MaxValue, 1)); + Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_ExclusiveOr(MinValue, 1)); + Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper.op_ExclusiveOr(NegativeOne, 1)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_OnesComplement(Zero)); + Assert.Equal(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper.op_OnesComplement(One)); + Assert.Equal(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_OnesComplement(MaxValue)); + Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_OnesComplement(MinValue)); + Assert.Equal(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_OnesComplement(NegativeOne)); + } + + // + // IComparisonOperators + // + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(Zero, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(One, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(MaxValue, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(MinValue, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(NegativeOne, 1)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(Zero, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(One, 1)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(MaxValue, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(MinValue, 1)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(NegativeOne, 1)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan(Zero, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(One, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(MaxValue, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(MinValue, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThan(NegativeOne, 1)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(Zero, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(One, 1)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(MaxValue, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(MinValue, 1)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(NegativeOne, 1)); + } + + // + // IDecrementOperators + // + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(NegativeOne, DecrementOperatorsHelper.op_Decrement(Zero)); + Assert.Equal(Zero, DecrementOperatorsHelper.op_Decrement(One)); + Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper.op_Decrement(MaxValue)); + Assert.Equal(MaxValue, DecrementOperatorsHelper.op_Decrement(MinValue)); + Assert.Equal(NegativeTwo, DecrementOperatorsHelper.op_Decrement(NegativeOne)); + } + + [Fact] + public static void op_CheckedDecrementTest() + { + Assert.Equal(NegativeOne, DecrementOperatorsHelper.op_CheckedDecrement(Zero)); + Assert.Equal(Zero, DecrementOperatorsHelper.op_CheckedDecrement(One)); + Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper.op_CheckedDecrement(MaxValue)); + Assert.Equal(NegativeTwo, DecrementOperatorsHelper.op_CheckedDecrement(NegativeOne)); + + Assert.Throws(() => DecrementOperatorsHelper.op_CheckedDecrement(MinValue)); + } + + // + // IDivisionOperators + // + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal(Zero, DivisionOperatorsHelper.op_Division(Zero, 2)); + Assert.Equal(Zero, DivisionOperatorsHelper.op_Division(One, 2)); + Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper.op_Division(MaxValue, 2)); + Assert.Equal(new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper.op_Division(MinValue, 2)); + Assert.Equal(Zero, DivisionOperatorsHelper.op_Division(NegativeOne, 2)); + + Assert.Throws(() => DivisionOperatorsHelper.op_Division(One, 0)); + } + + [Fact] + public static void op_CheckedDivisionTest() + { + Assert.Equal(Zero, DivisionOperatorsHelper.op_CheckedDivision(Zero, 2)); + Assert.Equal(Zero, DivisionOperatorsHelper.op_CheckedDivision(One, 2)); + Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper.op_CheckedDivision(MaxValue, 2)); + Assert.Equal(new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper.op_CheckedDivision(MinValue, 2)); + Assert.Equal(Zero, DivisionOperatorsHelper.op_CheckedDivision(NegativeOne, 2)); + + Assert.Throws(() => DivisionOperatorsHelper.op_CheckedDivision(One, 0)); + } + + // + // IEqualityOperators + // + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality(Zero, 1)); + Assert.True(EqualityOperatorsHelper.op_Equality(One, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(MaxValue, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(MinValue, 1)); + Assert.False(EqualityOperatorsHelper.op_Equality(NegativeOne, 1)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality(Zero, 1)); + Assert.False(EqualityOperatorsHelper.op_Inequality(One, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(MaxValue, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(MinValue, 1)); + Assert.True(EqualityOperatorsHelper.op_Inequality(NegativeOne, 1)); + } + + // + // IIncrementOperators + // + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal(One, IncrementOperatorsHelper.op_Increment(Zero)); + Assert.Equal(Two, IncrementOperatorsHelper.op_Increment(One)); + Assert.Equal(MinValue, IncrementOperatorsHelper.op_Increment(MaxValue)); + Assert.Equal(MinValuePlusOne, IncrementOperatorsHelper.op_Increment(MinValue)); + Assert.Equal(Zero, IncrementOperatorsHelper.op_Increment(NegativeOne)); + } + + [Fact] + public static void op_CheckedIncrementTest() + { + Assert.Equal(One, IncrementOperatorsHelper.op_CheckedIncrement(Zero)); + Assert.Equal(Two, IncrementOperatorsHelper.op_CheckedIncrement(One)); + Assert.Equal(MinValuePlusOne, IncrementOperatorsHelper.op_CheckedIncrement(MinValue)); + Assert.Equal(Zero, IncrementOperatorsHelper.op_CheckedIncrement(NegativeOne)); + + Assert.Throws(() => IncrementOperatorsHelper.op_CheckedIncrement(MaxValue)); + } + + // + // IMinMaxValue + // + + [Fact] + public static void MaxValueTest() + { + Assert.Equal(MaxValue, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(MinValue, MinMaxValueHelper.MinValue); + } + + // + // IModulusOperators + // + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal(Zero, ModulusOperatorsHelper.op_Modulus(Zero, 2)); + Assert.Equal(One, ModulusOperatorsHelper.op_Modulus(One, 2)); + Assert.Equal(One, ModulusOperatorsHelper.op_Modulus(MaxValue, 2)); + Assert.Equal(Zero, ModulusOperatorsHelper.op_Modulus(MinValue, 2)); + Assert.Equal(NegativeOne, ModulusOperatorsHelper.op_Modulus(NegativeOne, 2)); + + Assert.Throws(() => ModulusOperatorsHelper.op_Modulus(One, 0)); + } + + // + // IMultiplicativeIdentity + // + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal(One, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + // + // IMultiplyOperators + // + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal(Zero, MultiplyOperatorsHelper.op_Multiply(Zero, 2)); + Assert.Equal(Two, MultiplyOperatorsHelper.op_Multiply(One, 2)); + Assert.Equal(NegativeTwo, MultiplyOperatorsHelper.op_Multiply(MaxValue, 2)); + Assert.Equal(Zero, MultiplyOperatorsHelper.op_Multiply(MinValue, 2)); + Assert.Equal(NegativeTwo, MultiplyOperatorsHelper.op_Multiply(NegativeOne, 2)); + } + + [Fact] + public static void op_CheckedMultiplyTest() + { + Assert.Equal(Zero, MultiplyOperatorsHelper.op_CheckedMultiply(Zero, 2)); + Assert.Equal(Two, MultiplyOperatorsHelper.op_CheckedMultiply(One, 2)); + Assert.Equal(NegativeTwo, MultiplyOperatorsHelper.op_CheckedMultiply(NegativeOne, 2)); + + Assert.Throws(() => MultiplyOperatorsHelper.op_CheckedMultiply(MaxValue, 2)); + Assert.Throws(() => MultiplyOperatorsHelper.op_CheckedMultiply(MinValue, 2)); + } + + // + // INumber + // + + [Fact] + public static void ClampTest() + { + Assert.Equal(Zero, NumberHelper.Clamp(Zero, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F)); + Assert.Equal(One, NumberHelper.Clamp(One, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F)); + Assert.Equal(0x007F, NumberHelper.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.Clamp(MinValue, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F)); + Assert.Equal(NegativeOne, NumberHelper.Clamp(NegativeOne, new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), 0x007F)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal(One, NumberHelper.Max(Zero, 1)); + Assert.Equal(One, NumberHelper.Max(One, 1)); + Assert.Equal(MaxValue, NumberHelper.Max(MaxValue, 1)); + Assert.Equal(One, NumberHelper.Max(MinValue, 1)); + Assert.Equal(One, NumberHelper.Max(NegativeOne, 1)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal(Zero, NumberHelper.Min(Zero, 1)); + Assert.Equal(One, NumberHelper.Min(One, 1)); + Assert.Equal(One, NumberHelper.Min(MaxValue, 1)); + Assert.Equal(MinValue, NumberHelper.Min(MinValue, 1)); + Assert.Equal(NegativeOne, NumberHelper.Min(NegativeOne, 1)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal(0, NumberHelper.Sign(Zero)); + Assert.Equal(1, NumberHelper.Sign(One)); + Assert.Equal(1, NumberHelper.Sign(MaxValue)); + Assert.Equal(-1, NumberHelper.Sign(MinValue)); + Assert.Equal(-1, NumberHelper.Sign(NegativeOne)); + } + + // + // INumberBase + // + + [Fact] + public static void OneTest() + { + Assert.Equal(One, NumberBaseHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal(Zero, NumberBaseHelper.Zero); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal(Zero, NumberHelper.Abs(Zero)); + Assert.Equal(One, NumberHelper.Abs(One)); + Assert.Equal(MaxValue, NumberHelper.Abs(MaxValue)); + Assert.Throws(() => NumberHelper.Abs(MinValue)); + Assert.Equal(One, NumberHelper.Abs(NegativeOne)); + } + + [Fact] + public static void CreateCheckedFromByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00)); + Assert.Equal(One, NumberHelper.CreateChecked(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateChecked(0x7F)); + Assert.Equal(SByteMaxValuePlusOne, NumberHelper.CreateChecked(0x80)); + Assert.Equal(ByteMaxValue, NumberHelper.CreateChecked(0xFF)); + } + + [Fact] + public static void CreateCheckedFromCharTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked((char)0x0000)); + Assert.Equal(One, NumberHelper.CreateChecked((char)0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateChecked((char)0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateChecked((char)0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateChecked((char)0xFFFF)); + } + + [Fact] + public static void CreateCheckedFromDecimalTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(decimal.Zero)); + + Assert.Equal(One, NumberHelper.CreateChecked(decimal.One)); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(decimal.MinusOne)); + + Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper.CreateChecked(decimal.MaxValue)); + Assert.Equal(new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001), NumberHelper.CreateChecked(decimal.MinValue)); + } + + [Fact] + public static void CreateCheckedFromDoubleTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(+0.0)); + Assert.Equal(Zero, NumberHelper.CreateChecked(-0.0)); + + Assert.Equal(Zero, NumberHelper.CreateChecked(+double.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateChecked(-double.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateChecked(+1.0)); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(-1.0)); + + Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(+170141183460469212842221372237303250944.0)); + Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(-170141183460469212842221372237303250944.0)); + + Assert.Equal(MinValue, NumberHelper.CreateChecked(-170141183460469231731687303715884105728.0)); + + Assert.Throws(() => NumberHelper.CreateChecked(+170141183460469231731687303715884105728.0)); + Assert.Throws(() => NumberHelper.CreateChecked(-170141183460469269510619166673045815296.0)); + + Assert.Throws(() => NumberHelper.CreateChecked(double.MaxValue)); + Assert.Throws(() => NumberHelper.CreateChecked(double.MinValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(double.PositiveInfinity)); + Assert.Throws(() => NumberHelper.CreateChecked(double.NegativeInfinity)); + } + + [Fact] + public static void CreateCheckedFromHalfTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked((Half)(+0.0))); + Assert.Equal(Zero, NumberHelper.CreateChecked((Half)(-0.0))); + + Assert.Equal(Zero, NumberHelper.CreateChecked(+Half.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateChecked(-Half.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateChecked((Half)(+1.0))); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked((Half)(-1.0))); + + Assert.Equal(+65504, NumberHelper.CreateChecked(Half.MaxValue)); + Assert.Equal(-65504, NumberHelper.CreateChecked(Half.MinValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(Half.PositiveInfinity)); + Assert.Throws(() => NumberHelper.CreateChecked(Half.NegativeInfinity)); + } + + [Fact] + public static void CreateCheckedFromInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateChecked(0x7FFF)); + Assert.Equal(Int16MinValue, NumberHelper.CreateChecked(unchecked((short)0x8000))); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateCheckedFromInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked(0x7FFFFFFF)); + Assert.Equal(Int32MinValue, NumberHelper.CreateChecked(unchecked((int)0x80000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateCheckedFromInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MinValue, NumberHelper.CreateChecked(unchecked((long)0x8000000000000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateCheckedFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateChecked(unchecked((nint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateChecked(unchecked((nint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MinValue, NumberHelper.CreateChecked(unchecked((nint)0x8000000000000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateChecked((nint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked((nint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked((nint)0x7FFFFFFF)); + Assert.Equal(Int32MinValue, NumberHelper.CreateChecked(unchecked((nint)0x80000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateCheckedFromSByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00)); + Assert.Equal(One, NumberHelper.CreateChecked(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateChecked(0x7F)); + Assert.Equal(SByteMinValue, NumberHelper.CreateChecked(unchecked((sbyte)0x80))); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateCheckedFromSingleTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(+0.0f)); + Assert.Equal(Zero, NumberHelper.CreateChecked(-0.0f)); + + Assert.Equal(Zero, NumberHelper.CreateChecked(+float.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateChecked(-float.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateChecked(+1.0f)); + Assert.Equal(NegativeOne, NumberHelper.CreateChecked(-1.0f)); + + Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(+170141173319264429905852091742258462720.0f)); + Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(-170141173319264429905852091742258462720.0f)); + + Assert.Equal(MinValue, NumberHelper.CreateChecked(-170141183460469231731687303715884105728.0f)); + + Assert.Throws(() => NumberHelper.CreateChecked(+170141183460469231731687303715884105728.0f)); + Assert.Throws(() => NumberHelper.CreateChecked(-170141203742878835383357727663135391744.0f)); + + Assert.Throws(() => NumberHelper.CreateChecked(float.MaxValue)); + Assert.Throws(() => NumberHelper.CreateChecked(float.MinValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(float.PositiveInfinity)); + Assert.Throws(() => NumberHelper.CreateChecked(float.NegativeInfinity)); + } + + [Fact] + public static void CreateCheckedFromUInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateChecked(0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateChecked(0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateChecked(0xFFFF)); + } + + [Fact] + public static void CreateCheckedFromUInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked(0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateChecked(0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateChecked(0xFFFFFFFF)); + } + + [Fact] + public static void CreateCheckedFromUInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateChecked(0x8000000000000000)); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateChecked(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateCheckedFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateChecked(unchecked((nuint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateChecked(unchecked((nuint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateChecked(unchecked((nuint)0x8000000000000000))); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateChecked(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateChecked((nuint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked((nuint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked((nuint)0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateChecked((nuint)0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateChecked((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(SByteMaxValuePlusOne, NumberHelper.CreateSaturating(0x80)); + Assert.Equal(ByteMaxValue, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal(One, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromDecimalTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(decimal.Zero)); + + Assert.Equal(One, NumberHelper.CreateSaturating(decimal.One)); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(decimal.MinusOne)); + + Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper.CreateSaturating(decimal.MaxValue)); + Assert.Equal(new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001), NumberHelper.CreateSaturating(decimal.MinValue)); + } + + [Fact] + public static void CreateSaturatingFromDoubleTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(+0.0)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-0.0)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(+double.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-double.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateSaturating(+1.0)); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(-1.0)); + + Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(+170141183460469212842221372237303250944.0)); + Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(-170141183460469212842221372237303250944.0)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(-170141183460469231731687303715884105728.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(+170141183460469231731687303715884105728.0)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(-170141183460469269510619166673045815296.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(double.MaxValue)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(double.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(double.PositiveInfinity)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(double.NegativeInfinity)); + } + + [Fact] + public static void CreateSaturatingFromHalfTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((Half)(+0.0))); + Assert.Equal(Zero, NumberHelper.CreateSaturating((Half)(-0.0))); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(+Half.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-Half.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateSaturating((Half)(+1.0))); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating((Half)(-1.0))); + + Assert.Equal(+65504, NumberHelper.CreateSaturating(Half.MaxValue)); + Assert.Equal(-65504, NumberHelper.CreateSaturating(Half.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(Half.PositiveInfinity)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(Half.NegativeInfinity)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(Int16MinValue, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(Int32MinValue, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MinValue, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MinValue, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(Int32MinValue, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(SByteMinValue, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromSingleTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(+0.0f)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-0.0f)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(+float.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-float.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateSaturating(+1.0f)); + Assert.Equal(NegativeOne, NumberHelper.CreateSaturating(-1.0f)); + + Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(+170141173319264429905852091742258462720.0f)); + Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(-170141173319264429905852091742258462720.0f)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(-170141183460469231731687303715884105728.0f)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(+170141183460469231731687303715884105728.0f)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(-170141203742878835383357727663135391744.0f)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(float.MaxValue)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(float.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(float.PositiveInfinity)); + Assert.Equal(MinValue, NumberHelper.CreateSaturating(float.NegativeInfinity)); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(SByteMaxValuePlusOne, NumberHelper.CreateTruncating(0x80)); + Assert.Equal(ByteMaxValue, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal(One, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromDecimalTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(decimal.Zero)); + + Assert.Equal(One, NumberHelper.CreateTruncating(decimal.One)); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(decimal.MinusOne)); + + Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper.CreateTruncating(decimal.MaxValue)); + Assert.Equal(new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001), NumberHelper.CreateTruncating(decimal.MinValue)); + } + + [Fact] + public static void CreateTruncatingFromDoubleTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(+0.0)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-0.0)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(+double.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-double.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateTruncating(+1.0)); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(-1.0)); + + Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(+170141183460469212842221372237303250944.0)); + Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(-170141183460469212842221372237303250944.0)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(-170141183460469231731687303715884105728.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(+170141183460469231731687303715884105728.0)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(-170141183460469269510619166673045815296.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(double.MaxValue)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(double.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(double.PositiveInfinity)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(double.NegativeInfinity)); + } + + [Fact] + public static void CreateTruncatingFromHalfTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((Half)(+0.0))); + Assert.Equal(Zero, NumberHelper.CreateTruncating((Half)(-0.0))); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(+Half.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-Half.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateTruncating((Half)(+1.0))); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating((Half)(-1.0))); + + Assert.Equal(+65504, NumberHelper.CreateTruncating(Half.MaxValue)); + Assert.Equal(-65504, NumberHelper.CreateTruncating(Half.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(Half.PositiveInfinity)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(Half.NegativeInfinity)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(Int16MinValue, NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(Int32MinValue, NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MinValue, NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MinValue, NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(Int32MinValue, NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(SByteMinValue, NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromSingleTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(+0.0f)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-0.0f)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(+float.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-float.Epsilon)); + + Assert.Equal(One, NumberHelper.CreateTruncating(+1.0f)); + Assert.Equal(NegativeOne, NumberHelper.CreateTruncating(-1.0f)); + + Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(+170141173319264429905852091742258462720.0f)); + Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(-170141173319264429905852091742258462720.0f)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(-170141183460469231731687303715884105728.0f)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(+170141183460469231731687303715884105728.0f)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(-170141203742878835383357727663135391744.0f)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(float.MaxValue)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(float.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(float.PositiveInfinity)); + Assert.Equal(MinValue, NumberHelper.CreateTruncating(float.NegativeInfinity)); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void TryCreateFromByteTest() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal(SByteMaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal(SByteMaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal(ByteMaxValue, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal(Int16MaxValue, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal(Int16MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal(UInt16MaxValue, result); + } + + [Fact] + public static void TryCreateFromDecimalTest() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(decimal.Zero, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(decimal.One, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(decimal.MinusOne, out result)); + Assert.Equal(NegativeOne, result); + + Assert.True(NumberHelper.TryCreate(decimal.MaxValue, out result)); + Assert.Equal(new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), result); + + Assert.True(NumberHelper.TryCreate(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.TryCreate(+0.0, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(-0.0, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+double.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(-double.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+1.0, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(-1.0, out result)); + Assert.Equal(NegativeOne, result); + + Assert.True(NumberHelper.TryCreate(+170141183460469212842221372237303250944.0, out result)); + Assert.Equal(new Int128(0x7FFF_FFFF_FFFF_FC00, 0x0000_0000_0000_0000), result); + + Assert.True(NumberHelper.TryCreate(-170141183460469212842221372237303250944.0, out result)); + Assert.Equal(new Int128(0x8000_0000_0000_0400, 0x0000_0000_0000_0000), result); + + Assert.True(NumberHelper.TryCreate(-170141183460469231731687303715884105728.0, out result)); + Assert.Equal(MinValue, result); + + Assert.False(NumberHelper.TryCreate(+170141183460469231731687303715884105728.0, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(-170141183460469269510619166673045815296.0, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.MaxValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.MinValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.PositiveInfinity, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.NegativeInfinity, out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromHalfTest() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate((Half)(+0.0), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((Half)(-0.0), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+Half.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(-Half.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((Half)(+1.0), out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate((Half)(-1.0), out result)); + Assert.Equal(NegativeOne, result); + + Assert.True(NumberHelper.TryCreate(Half.MaxValue, out result)); + Assert.Equal(+65504, result); + + Assert.True(NumberHelper.TryCreate(Half.MinValue, out result)); + Assert.Equal(-65504, result); + + Assert.False(NumberHelper.TryCreate(Half.PositiveInfinity, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(Half.NegativeInfinity, out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal(Int16MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(Int16MinValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(NegativeOne, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal(Int32MinValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(NegativeOne, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal(Int64MinValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(NegativeOne, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + Int128 result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(Int64MinValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(NegativeOne, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal(Int32MinValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(NegativeOne, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal(SByteMaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(SByteMinValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(NegativeOne, result); + } + + [Fact] + public static void TryCreateFromSingleTest() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(+0.0f, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(-0.0f, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+float.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(-float.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+1.0f, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(-1.0f, out result)); + Assert.Equal(NegativeOne, result); + + Assert.True(NumberHelper.TryCreate(+170141173319264429905852091742258462720.0f, out result)); + Assert.Equal(new Int128(0x7FFF_FF80_0000_0000, 0x0000_0000_0000_0000), result); + + Assert.True(NumberHelper.TryCreate(-170141173319264429905852091742258462720.0f, out result)); + Assert.Equal(new Int128(0x8000_0080_0000_0000, 0x0000_0000_0000_0000), result); + + Assert.True(NumberHelper.TryCreate(-170141183460469231731687303715884105728.0f, out result)); + Assert.Equal(MinValue, result); + + Assert.False(NumberHelper.TryCreate(+170141183460469231731687303715884105728.0f, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(-170141203742878835383357727663135391744.0f, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(float.MaxValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(float.MinValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(float.PositiveInfinity, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(float.NegativeInfinity, out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal(Int16MaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal(Int16MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal(UInt16MaxValue, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal(Int32MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal(UInt32MaxValue, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + Int128 result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal(Int64MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal(UInt64MaxValue, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + Int128 result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal(Int64MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(UInt64MaxValue, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal(Int32MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal(UInt32MaxValue, result); + } + } + + // + // IShiftOperators + // + + [Fact] + public static void op_LeftShiftTest() + { + Assert.Equal(Zero, ShiftOperatorsHelper.op_LeftShift(Zero, 1)); + Assert.Equal(Two, ShiftOperatorsHelper.op_LeftShift(One, 1)); + Assert.Equal(NegativeTwo, ShiftOperatorsHelper.op_LeftShift(MaxValue, 1)); + Assert.Equal(Zero, ShiftOperatorsHelper.op_LeftShift(MinValue, 1)); + Assert.Equal(NegativeTwo, ShiftOperatorsHelper.op_LeftShift(NegativeOne, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal(Zero, ShiftOperatorsHelper.op_RightShift(Zero, 1)); + Assert.Equal(Zero, ShiftOperatorsHelper.op_RightShift(One, 1)); + Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper.op_RightShift(MaxValue, 1)); + Assert.Equal(new Int128(0xC000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper.op_RightShift(MinValue, 1)); + Assert.Equal(NegativeOne, ShiftOperatorsHelper.op_RightShift(NegativeOne, 1)); + } + + [Fact] + public static void op_UnsignedRightShiftTest() + { + Assert.Equal(Zero, ShiftOperatorsHelper.op_UnsignedRightShift(Zero, 1)); + Assert.Equal(Zero, ShiftOperatorsHelper.op_UnsignedRightShift(One, 1)); + Assert.Equal(new Int128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper.op_UnsignedRightShift(MaxValue, 1)); + Assert.Equal(new Int128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper.op_UnsignedRightShift(MinValue, 1)); + Assert.Equal(MaxValue, ShiftOperatorsHelper.op_UnsignedRightShift(NegativeOne, 1)); + } + + // + // ISignedNumber + // + + [Fact] + public static void NegativeOneTest() + { + Assert.Equal(NegativeOne, SignedNumberHelper.NegativeOne); + } + + // + // ISubtractionOperators + // + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(NegativeOne, SubtractionOperatorsHelper.op_Subtraction(Zero, 1)); + Assert.Equal(Zero, SubtractionOperatorsHelper.op_Subtraction(One, 1)); + Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper.op_Subtraction(MaxValue, 1)); + Assert.Equal(MaxValue, SubtractionOperatorsHelper.op_Subtraction(MinValue, 1)); + Assert.Equal(NegativeTwo, SubtractionOperatorsHelper.op_Subtraction(NegativeOne, 1)); + } + + [Fact] + public static void op_CheckedSubtractionTest() + { + Assert.Equal(NegativeOne, SubtractionOperatorsHelper.op_CheckedSubtraction(Zero, 1)); + Assert.Equal(Zero, SubtractionOperatorsHelper.op_CheckedSubtraction(One, 1)); + Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper.op_CheckedSubtraction(MaxValue, 1)); + Assert.Equal(NegativeTwo, SubtractionOperatorsHelper.op_CheckedSubtraction(NegativeOne, 1)); + + Assert.Throws(() => SubtractionOperatorsHelper.op_CheckedSubtraction(MinValue, 1)); + } + + // + // IUnaryNegationOperators + // + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal(Zero, UnaryNegationOperatorsHelper.op_UnaryNegation(Zero)); + Assert.Equal(NegativeOne, UnaryNegationOperatorsHelper.op_UnaryNegation(One)); + Assert.Equal(MinValuePlusOne, UnaryNegationOperatorsHelper.op_UnaryNegation(MaxValue)); + Assert.Equal(MinValue, UnaryNegationOperatorsHelper.op_UnaryNegation(MinValue)); + Assert.Equal(One, UnaryNegationOperatorsHelper.op_UnaryNegation(NegativeOne)); + } + + [Fact] + public static void op_CheckedUnaryNegationTest() + { + Assert.Equal(Zero, UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(Zero)); + Assert.Equal(NegativeOne, UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(One)); + Assert.Equal(MinValuePlusOne, UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(MaxValue)); + Assert.Equal(One, UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(NegativeOne)); + + Assert.Throws(() => UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(MinValue)); + } + + // + // IUnaryPlusOperators + // + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal(Zero, UnaryPlusOperatorsHelper.op_UnaryPlus(Zero)); + Assert.Equal(One, UnaryPlusOperatorsHelper.op_UnaryPlus(One)); + Assert.Equal(MaxValue, UnaryPlusOperatorsHelper.op_UnaryPlus(MaxValue)); + Assert.Equal(MinValue, UnaryPlusOperatorsHelper.op_UnaryPlus(MinValue)); + Assert.Equal(NegativeOne, UnaryPlusOperatorsHelper.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 index 0000000000000..15f1a39a02396 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/Int128Tests.cs @@ -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 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 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(null, () => ((Int128)123).CompareTo(value)); + } + + public static IEnumerable 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 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(() => i.ToString("Y")); // Invalid format + Assert.Throws(() => i.ToString("Y", null)); // Invalid format + } + + public static IEnumerable 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 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(paramName, () => Int128.TryParse("1", style, null, out result)); + Assert.Equal(default(Int128), result); + + AssertExtensions.Throws(paramName, () => Int128.Parse("1", style)); + AssertExtensions.Throws(paramName, () => Int128.Parse("1", style, null)); + } + + public static IEnumerable 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)); + } + } +} diff --git a/src/libraries/System.Runtime/tests/System/SingleTests.GenericMath.cs b/src/libraries/System.Runtime/tests/System/SingleTests.GenericMath.cs index 7bc92ddc57cd2..a5cf58f14ccaa 100644 --- a/src/libraries/System.Runtime/tests/System/SingleTests.GenericMath.cs +++ b/src/libraries/System.Runtime/tests/System/SingleTests.GenericMath.cs @@ -542,6 +542,16 @@ public static void CreateCheckedFromInt64Test() AssertBitwiseEqual(-1.0f, NumberHelper.CreateChecked(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateCheckedFromInt128Test() + { + AssertBitwiseEqual(0.0f, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0f, NumberHelper.CreateChecked(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper.CreateChecked(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(-170141183460469231731687303715884105728.0f, NumberHelper.CreateChecked(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(-1.0f, NumberHelper.CreateChecked(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateCheckedFromIntPtrTest() { @@ -603,6 +613,16 @@ public static void CreateCheckedFromUInt64Test() AssertBitwiseEqual(18446744073709551615.0f, NumberHelper.CreateChecked(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateCheckedFromUInt128Test() + { + AssertBitwiseEqual(0.0f, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0f, NumberHelper.CreateChecked(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper.CreateChecked(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(170141183460469231731687303715884105728.0f, NumberHelper.CreateChecked(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(float.PositiveInfinity, NumberHelper.CreateChecked(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateCheckedFromUIntPtrTest() { @@ -678,6 +698,16 @@ public static void CreateSaturatingFromInt64Test() AssertBitwiseEqual(-1.0f, NumberHelper.CreateSaturating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateSaturatingFromInt128Test() + { + AssertBitwiseEqual(0.0f, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0f, NumberHelper.CreateSaturating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper.CreateSaturating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(-170141183460469231731687303715884105728.0f, NumberHelper.CreateSaturating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(-1.0f, NumberHelper.CreateSaturating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateSaturatingFromIntPtrTest() { @@ -739,6 +769,16 @@ public static void CreateSaturatingFromUInt64Test() AssertBitwiseEqual(18446744073709551615.0f, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateSaturatingFromUInt128Test() + { + AssertBitwiseEqual(0.0f, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0f, NumberHelper.CreateSaturating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper.CreateSaturating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(170141183460469231731687303715884105728.0f, NumberHelper.CreateSaturating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(float.PositiveInfinity, NumberHelper.CreateSaturating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateSaturatingFromUIntPtrTest() { @@ -814,6 +854,16 @@ public static void CreateTruncatingFromInt64Test() AssertBitwiseEqual(-1.0f, NumberHelper.CreateTruncating(unchecked(unchecked((long)0xFFFFFFFFFFFFFFFF)))); } + [Fact] + public static void CreateTruncatingFromInt128Test() + { + AssertBitwiseEqual(0.0f, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0f, NumberHelper.CreateTruncating(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper.CreateTruncating(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(-170141183460469231731687303715884105728.0f, NumberHelper.CreateTruncating(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(-1.0f, NumberHelper.CreateTruncating(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateTruncatingFromIntPtrTest() { @@ -875,6 +925,16 @@ public static void CreateTruncatingFromUInt64Test() AssertBitwiseEqual(18446744073709551615.0f, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); } + [Fact] + public static void CreateTruncatingFromUInt128Test() + { + AssertBitwiseEqual(0.0f, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(1.0f, NumberHelper.CreateTruncating(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001))); + AssertBitwiseEqual(170141183460469231731687303715884105727.0f, NumberHelper.CreateTruncating(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + AssertBitwiseEqual(170141183460469231731687303715884105728.0f, NumberHelper.CreateTruncating(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000))); + AssertBitwiseEqual(float.PositiveInfinity, NumberHelper.CreateTruncating(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF))); + } + [Fact] public static void CreateTruncatingFromUIntPtrTest() { @@ -1068,6 +1128,27 @@ public static void TryCreateFromInt64Test() Assert.Equal(-1.0f, result); } + [Fact] + public static void TryCreateFromInt128Test() + { + float result; + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0f, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(1.0f, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(170141183460469231731687303715884105727.0f, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(-170141183460469231731687303715884105728.0f, result); + + Assert.True(NumberHelper.TryCreate(new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(-1.0f, result); + } + [Fact] public static void TryCreateFromIntPtrTest() { @@ -1193,6 +1274,27 @@ public static void TryCreateFromUInt64Test() Assert.Equal(18446744073709551615.0f, result); } + [Fact] + public static void TryCreateFromUInt128Test() + { + float result; + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(0.0f, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), out result)); + Assert.Equal(1.0f, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(170141183460469231731687303715884105727.0f, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), out result)); + Assert.Equal(170141183460469231731687303715884105728.0f, result); + + Assert.True(NumberHelper.TryCreate(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), out result)); + Assert.Equal(float.PositiveInfinity, result); + } + [Fact] public static void TryCreateFromUIntPtrTest() { 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 index 0000000000000..8177f0fe9d696 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt128Tests.GenericMath.cs @@ -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.op_Addition(Zero, 1U)); + Assert.Equal(Two, AdditionOperatorsHelper.op_Addition(One, 1U)); + Assert.Equal(Int128MaxValuePlusOne, AdditionOperatorsHelper.op_Addition(Int128MaxValue, 1U)); + Assert.Equal(Int128MaxValuePlusTwo, AdditionOperatorsHelper.op_Addition(Int128MaxValuePlusOne, 1U)); + Assert.Equal(Zero, AdditionOperatorsHelper.op_Addition(MaxValue, 1U)); + } + + [Fact] + public static void op_CheckedAdditionTest() + { + Assert.Equal(One, AdditionOperatorsHelper.op_CheckedAddition(Zero, 1U)); + Assert.Equal(Two, AdditionOperatorsHelper.op_CheckedAddition(One, 1U)); + Assert.Equal(Int128MaxValuePlusOne, AdditionOperatorsHelper.op_CheckedAddition(Int128MaxValue, 1U)); + Assert.Equal(Int128MaxValuePlusTwo, AdditionOperatorsHelper.op_CheckedAddition(Int128MaxValuePlusOne, 1U)); + + Assert.Throws(() => AdditionOperatorsHelper.op_CheckedAddition(MaxValue, 1U)); + } + + // + // IAdditiveIdentity + // + + [Fact] + public static void AdditiveIdentityTest() + { + Assert.Equal(Zero, AdditiveIdentityHelper.AdditiveIdentity); + } + + // + // IBinaryInteger + // + + [Fact] + public static void DivRemTest() + { + Assert.Equal((Zero, Zero), BinaryIntegerHelper.DivRem(Zero, 2U)); + Assert.Equal((Zero, One), BinaryIntegerHelper.DivRem(One, 2U)); + Assert.Equal((new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), One), BinaryIntegerHelper.DivRem(Int128MaxValue, 2U)); + Assert.Equal((new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), Zero), BinaryIntegerHelper.DivRem(Int128MaxValuePlusOne, 2U)); + Assert.Equal((Int128MaxValue, One), BinaryIntegerHelper.DivRem(MaxValue, 2U)); + } + + [Fact] + public static void LeadingZeroCountTest() + { + Assert.Equal(0x80U, BinaryIntegerHelper.LeadingZeroCount(Zero)); + Assert.Equal(0x7FU, BinaryIntegerHelper.LeadingZeroCount(One)); + Assert.Equal(0x01U, BinaryIntegerHelper.LeadingZeroCount(Int128MaxValue)); + Assert.Equal(0x00U, BinaryIntegerHelper.LeadingZeroCount(Int128MaxValuePlusOne)); + Assert.Equal(0x00U, BinaryIntegerHelper.LeadingZeroCount(MaxValue)); + } + + [Fact] + public static void PopCountTest() + { + Assert.Equal(0x00U, BinaryIntegerHelper.PopCount(Zero)); + Assert.Equal(0x01U, BinaryIntegerHelper.PopCount(One)); + Assert.Equal(0x7FU, BinaryIntegerHelper.PopCount(Int128MaxValue)); + Assert.Equal(0x01U, BinaryIntegerHelper.PopCount(Int128MaxValuePlusOne)); + Assert.Equal(0x80U, BinaryIntegerHelper.PopCount(MaxValue)); + } + + [Fact] + public static void RotateLeftTest() + { + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateLeft(Zero, 1)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0002), BinaryIntegerHelper.RotateLeft(One, 1)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BinaryIntegerHelper.RotateLeft(Int128MaxValue, 1)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BinaryIntegerHelper.RotateLeft(Int128MaxValuePlusOne, 1)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper.RotateLeft(MaxValue, 1)); + } + + [Fact] + public static void RotateRightTest() + { + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateRight(Zero, 1)); + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateRight(One, 1)); + Assert.Equal(new UInt128(0xBFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper.RotateRight(Int128MaxValue, 1)); + Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), BinaryIntegerHelper.RotateRight(Int128MaxValuePlusOne, 1)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BinaryIntegerHelper.RotateRight(MaxValue, 1)); + } + + [Fact] + public static void TrailingZeroCountTest() + { + Assert.Equal(0x80U, BinaryIntegerHelper.TrailingZeroCount(Zero)); + Assert.Equal(0x00U, BinaryIntegerHelper.TrailingZeroCount(One)); + Assert.Equal(0x00U, BinaryIntegerHelper.TrailingZeroCount(Int128MaxValue)); + Assert.Equal(0x7FU, BinaryIntegerHelper.TrailingZeroCount(Int128MaxValuePlusOne)); + Assert.Equal(0x00U, BinaryIntegerHelper.TrailingZeroCount(MaxValue)); + } + + [Fact] + public static void GetByteCountTest() + { + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(Zero)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(One)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(Int128MaxValue)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(Int128MaxValuePlusOne)); + Assert.Equal(16, BinaryIntegerHelper.GetByteCount(MaxValue)); + } + + [Fact] + public static void GetShortestBitLengthTest() + { + Assert.Equal(0x00, BinaryIntegerHelper.GetShortestBitLength(Zero)); + Assert.Equal(0x01, BinaryIntegerHelper.GetShortestBitLength(One)); + Assert.Equal(0x7F, BinaryIntegerHelper.GetShortestBitLength(Int128MaxValue)); + Assert.Equal(0x80, BinaryIntegerHelper.GetShortestBitLength(Int128MaxValuePlusOne)); + Assert.Equal(0x80, BinaryIntegerHelper.GetShortestBitLength(MaxValue)); + } + + [Fact] + public static void TryWriteLittleEndianTest() + { + Span destination = stackalloc byte[16]; + int bytesWritten = 0; + + Assert.True(BinaryIntegerHelper.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.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.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.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.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.TryWriteLittleEndian(default, Span.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.IsPow2(Zero)); + Assert.True(BinaryNumberHelper.IsPow2(One)); + Assert.False(BinaryNumberHelper.IsPow2(Int128MaxValue)); + Assert.True(BinaryNumberHelper.IsPow2(Int128MaxValuePlusOne)); + Assert.False(BinaryNumberHelper.IsPow2(MaxValue)); + } + + [Fact] + public static void Log2Test() + { + Assert.Equal(0x00U, BinaryNumberHelper.Log2(Zero)); + Assert.Equal(0x00U, BinaryNumberHelper.Log2(One)); + Assert.Equal(0x7EU, BinaryNumberHelper.Log2(Int128MaxValue)); + Assert.Equal(0x7FU, BinaryNumberHelper.Log2(Int128MaxValuePlusOne)); + Assert.Equal(0x7FU, BinaryNumberHelper.Log2(MaxValue)); + } + + // + // IBitwiseOperators + // + + [Fact] + public static void op_BitwiseAndTest() + { + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_BitwiseAnd(Zero, 1U)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseAnd(One, 1U)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseAnd(Int128MaxValue, 1U)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_BitwiseAnd(Int128MaxValuePlusOne, 1U)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseAnd(MaxValue, 1U)); + } + + [Fact] + public static void op_BitwiseOrTest() + { + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseOr(Zero, 1U)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseOr(One, 1U)); + Assert.Equal(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_BitwiseOr(Int128MaxValue, 1U)); + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_BitwiseOr(Int128MaxValuePlusOne, 1U)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_BitwiseOr(MaxValue, 1U)); + } + + [Fact] + public static void op_ExclusiveOrTest() + { + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_ExclusiveOr(Zero, 1U)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_ExclusiveOr(One, 1U)); + Assert.Equal(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper.op_ExclusiveOr(Int128MaxValue, 1U)); + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0001), BitwiseOperatorsHelper.op_ExclusiveOr(Int128MaxValuePlusOne, 1U)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper.op_ExclusiveOr(MaxValue, 1U)); + } + + [Fact] + public static void op_OnesComplementTest() + { + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_OnesComplement(Zero)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFE), BitwiseOperatorsHelper.op_OnesComplement(One)); + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_OnesComplement(Int128MaxValue)); + Assert.Equal(new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), BitwiseOperatorsHelper.op_OnesComplement(Int128MaxValuePlusOne)); + Assert.Equal(new UInt128(0x0000_0000_0000_0000, 0x0000_0000_0000_0000), BitwiseOperatorsHelper.op_OnesComplement(MaxValue)); + } + + // + // IComparisonOperators + // + + [Fact] + public static void op_GreaterThanTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(Zero, 1U)); + Assert.False(ComparisonOperatorsHelper.op_GreaterThan(One, 1U)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(Int128MaxValue, 1U)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(Int128MaxValuePlusOne, 1U)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThan(MaxValue, 1U)); + } + + [Fact] + public static void op_GreaterThanOrEqualTest() + { + Assert.False(ComparisonOperatorsHelper.op_GreaterThanOrEqual(Zero, 1U)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(One, 1U)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(Int128MaxValue, 1U)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(Int128MaxValuePlusOne, 1U)); + Assert.True(ComparisonOperatorsHelper.op_GreaterThanOrEqual(MaxValue, 1U)); + } + + [Fact] + public static void op_LessThanTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThan(Zero, 1U)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(One, 1U)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(Int128MaxValue, 1U)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(Int128MaxValuePlusOne, 1U)); + Assert.False(ComparisonOperatorsHelper.op_LessThan(MaxValue, 1U)); + } + + [Fact] + public static void op_LessThanOrEqualTest() + { + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(Zero, 1U)); + Assert.True(ComparisonOperatorsHelper.op_LessThanOrEqual(One, 1U)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(Int128MaxValue, 1U)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(Int128MaxValuePlusOne, 1U)); + Assert.False(ComparisonOperatorsHelper.op_LessThanOrEqual(MaxValue, 1U)); + } + + // + // IDecrementOperators + // + + [Fact] + public static void op_DecrementTest() + { + Assert.Equal(MaxValue, DecrementOperatorsHelper.op_Decrement(Zero)); + Assert.Equal(Zero, DecrementOperatorsHelper.op_Decrement(One)); + Assert.Equal(Int128MaxValueMinusOne, DecrementOperatorsHelper.op_Decrement(Int128MaxValue)); + Assert.Equal(Int128MaxValue, DecrementOperatorsHelper.op_Decrement(Int128MaxValuePlusOne)); + Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper.op_Decrement(MaxValue)); + } + + [Fact] + public static void op_CheckedDecrementTest() + { + Assert.Equal(Zero, DecrementOperatorsHelper.op_CheckedDecrement(One)); + Assert.Equal(Int128MaxValueMinusOne, DecrementOperatorsHelper.op_CheckedDecrement(Int128MaxValue)); + Assert.Equal(Int128MaxValue, DecrementOperatorsHelper.op_CheckedDecrement(Int128MaxValuePlusOne)); + Assert.Equal(MaxValueMinusOne, DecrementOperatorsHelper.op_CheckedDecrement(MaxValue)); + + Assert.Throws(() => DecrementOperatorsHelper.op_CheckedDecrement(Zero)); + } + + // + // IDivisionOperators + // + + [Fact] + public static void op_DivisionTest() + { + Assert.Equal(Zero, DivisionOperatorsHelper.op_Division(Zero, 2U)); + Assert.Equal(Zero, DivisionOperatorsHelper.op_Division(One, 2U)); + Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper.op_Division(Int128MaxValue, 2U)); + Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper.op_Division(Int128MaxValuePlusOne, 2U)); + Assert.Equal(Int128MaxValue, DivisionOperatorsHelper.op_Division(MaxValue, 2U)); + + Assert.Throws(() => DivisionOperatorsHelper.op_Division(One, 0U)); + } + + [Fact] + public static void op_CheckedDivisionTest() + { + Assert.Equal(Zero, DivisionOperatorsHelper.op_CheckedDivision(Zero, 2U)); + Assert.Equal(Zero, DivisionOperatorsHelper.op_CheckedDivision(One, 2U)); + Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), DivisionOperatorsHelper.op_CheckedDivision(Int128MaxValue, 2U)); + Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), DivisionOperatorsHelper.op_CheckedDivision(Int128MaxValuePlusOne, 2U)); + Assert.Equal(Int128MaxValue, DivisionOperatorsHelper.op_CheckedDivision(MaxValue, 2U)); + + Assert.Throws(() => DivisionOperatorsHelper.op_CheckedDivision(One, 0U)); + } + + // + // IEqualityOperators + // + + [Fact] + public static void op_EqualityTest() + { + Assert.False(EqualityOperatorsHelper.op_Equality(Zero, 1U)); + Assert.True(EqualityOperatorsHelper.op_Equality(One, 1U)); + Assert.False(EqualityOperatorsHelper.op_Equality(Int128MaxValue, 1U)); + Assert.False(EqualityOperatorsHelper.op_Equality(Int128MaxValuePlusOne, 1U)); + Assert.False(EqualityOperatorsHelper.op_Equality(MaxValue, 1U)); + } + + [Fact] + public static void op_InequalityTest() + { + Assert.True(EqualityOperatorsHelper.op_Inequality(Zero, 1U)); + Assert.False(EqualityOperatorsHelper.op_Inequality(One, 1U)); + Assert.True(EqualityOperatorsHelper.op_Inequality(Int128MaxValue, 1U)); + Assert.True(EqualityOperatorsHelper.op_Inequality(Int128MaxValuePlusOne, 1U)); + Assert.True(EqualityOperatorsHelper.op_Inequality(MaxValue, 1U)); + } + + // + // IIncrementOperators + // + + [Fact] + public static void op_IncrementTest() + { + Assert.Equal(One, IncrementOperatorsHelper.op_Increment(Zero)); + Assert.Equal(Two, IncrementOperatorsHelper.op_Increment(One)); + Assert.Equal(Int128MaxValuePlusOne, IncrementOperatorsHelper.op_Increment(Int128MaxValue)); + Assert.Equal(Int128MaxValuePlusTwo, IncrementOperatorsHelper.op_Increment(Int128MaxValuePlusOne)); + Assert.Equal(Zero, IncrementOperatorsHelper.op_Increment(MaxValue)); + } + + [Fact] + public static void op_CheckedIncrementTest() + { + Assert.Equal(One, IncrementOperatorsHelper.op_CheckedIncrement(Zero)); + Assert.Equal(Two, IncrementOperatorsHelper.op_CheckedIncrement(One)); + Assert.Equal(Int128MaxValuePlusOne, IncrementOperatorsHelper.op_CheckedIncrement(Int128MaxValue)); + Assert.Equal(Int128MaxValuePlusTwo, IncrementOperatorsHelper.op_CheckedIncrement(Int128MaxValuePlusOne)); + + Assert.Throws(() => IncrementOperatorsHelper.op_CheckedIncrement(MaxValue)); + } + + // + // IMinMaxValue + // + + [Fact] + public static void MaxValueTest() + { + Assert.Equal(MaxValue, MinMaxValueHelper.MaxValue); + } + + [Fact] + public static void MinValueTest() + { + Assert.Equal(Zero, MinMaxValueHelper.MinValue); + } + + // + // IModulusOperators + // + + [Fact] + public static void op_ModulusTest() + { + Assert.Equal(Zero, ModulusOperatorsHelper.op_Modulus(Zero, 2U)); + Assert.Equal(One, ModulusOperatorsHelper.op_Modulus(One, 2U)); + Assert.Equal(One, ModulusOperatorsHelper.op_Modulus(Int128MaxValue, 2U)); + Assert.Equal(Zero, ModulusOperatorsHelper.op_Modulus(Int128MaxValuePlusOne, 2U)); + Assert.Equal(One, ModulusOperatorsHelper.op_Modulus(MaxValue, 2U)); + + Assert.Throws(() => ModulusOperatorsHelper.op_Modulus(One, 0U)); + } + + // + // IMultiplicativeIdentity + // + + [Fact] + public static void MultiplicativeIdentityTest() + { + Assert.Equal(One, MultiplicativeIdentityHelper.MultiplicativeIdentity); + } + + // + // IMultiplyOperators + // + + [Fact] + public static void op_MultiplyTest() + { + Assert.Equal(Zero, MultiplyOperatorsHelper.op_Multiply(Zero, 2U)); + Assert.Equal(Two, MultiplyOperatorsHelper.op_Multiply(One, 2U)); + Assert.Equal(MaxValueMinusOne, MultiplyOperatorsHelper.op_Multiply(Int128MaxValue, 2U)); + Assert.Equal(Zero, MultiplyOperatorsHelper.op_Multiply(Int128MaxValuePlusOne, 2U)); + Assert.Equal(MaxValueMinusOne, MultiplyOperatorsHelper.op_Multiply(MaxValue, 2U)); + } + [Fact] + public static void op_CheckedMultiplyTest() + { + Assert.Equal(Zero, MultiplyOperatorsHelper.op_CheckedMultiply(Zero, 2U)); + Assert.Equal(Two, MultiplyOperatorsHelper.op_CheckedMultiply(One, 2U)); + Assert.Equal(MaxValueMinusOne, MultiplyOperatorsHelper.op_CheckedMultiply(Int128MaxValue, 2U)); + + Assert.Throws(() => MultiplyOperatorsHelper.op_CheckedMultiply(Int128MaxValuePlusOne, 2U)); + Assert.Throws(() => MultiplyOperatorsHelper.op_CheckedMultiply(MaxValue, 2U)); + } + + // + // INumber + // + + [Fact] + public static void ClampTest() + { + Assert.Equal(One, NumberHelper.Clamp(Zero, 0x0001U, 0x007FU)); + Assert.Equal(One, NumberHelper.Clamp(One, 0x0001U, 0x007FU)); + Assert.Equal(0x007FU, NumberHelper.Clamp(Int128MaxValue, 0x0001U, 0x007FU)); + Assert.Equal(0x007FU, NumberHelper.Clamp(Int128MaxValuePlusOne, 0x0001U, 0x007FU)); + Assert.Equal(0x007FU, NumberHelper.Clamp(MaxValue, 0x0001U, 0x007FU)); + } + + [Fact] + public static void MaxTest() + { + Assert.Equal(One, NumberHelper.Max(Zero, 1U)); + Assert.Equal(One, NumberHelper.Max(One, 1U)); + Assert.Equal(Int128MaxValue, NumberHelper.Max(Int128MaxValue, 1U)); + Assert.Equal(Int128MaxValuePlusOne, NumberHelper.Max(Int128MaxValuePlusOne, 1U)); + Assert.Equal(MaxValue, NumberHelper.Max(MaxValue, 1U)); + } + + [Fact] + public static void MinTest() + { + Assert.Equal(Zero, NumberHelper.Min(Zero, 1U)); + Assert.Equal(One, NumberHelper.Min(One, 1U)); + Assert.Equal(One, NumberHelper.Min(Int128MaxValue, 1U)); + Assert.Equal(One, NumberHelper.Min(Int128MaxValuePlusOne, 1U)); + Assert.Equal(One, NumberHelper.Min(MaxValue, 1U)); + } + + [Fact] + public static void SignTest() + { + Assert.Equal(0, NumberHelper.Sign(Zero)); + Assert.Equal(1, NumberHelper.Sign(One)); + Assert.Equal(1, NumberHelper.Sign(Int128MaxValue)); + Assert.Equal(1, NumberHelper.Sign(Int128MaxValuePlusOne)); + Assert.Equal(1, NumberHelper.Sign(MaxValue)); + } + + // + // INumberBase + // + + [Fact] + public static void OneTest() + { + Assert.Equal(One, NumberBaseHelper.One); + } + + [Fact] + public static void ZeroTest() + { + Assert.Equal(Zero, NumberBaseHelper.Zero); + } + + [Fact] + public static void AbsTest() + { + Assert.Equal(Zero, NumberHelper.Abs(Zero)); + Assert.Equal(One, NumberHelper.Abs(One)); + Assert.Equal(Int128MaxValue, NumberHelper.Abs(Int128MaxValue)); + Assert.Equal(Int128MaxValuePlusOne, NumberHelper.Abs(Int128MaxValuePlusOne)); + Assert.Equal(MaxValue, NumberHelper.Abs(MaxValue)); + } + + [Fact] + public static void CreateCheckedFromByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00)); + Assert.Equal(One, NumberHelper.CreateChecked(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateChecked(0x7F)); + Assert.Equal(SByteMaxValuePlusOne, NumberHelper.CreateChecked(0x80)); + Assert.Equal(ByteMaxValue, NumberHelper.CreateChecked(0xFF)); + } + + [Fact] + public static void CreateCheckedFromCharTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked((char)0x0000)); + Assert.Equal(One, NumberHelper.CreateChecked((char)0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateChecked((char)0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateChecked((char)0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateChecked((char)0xFFFF)); + } + + [Fact] + public static void CreateCheckedFromDecimalTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(decimal.Zero)); + Assert.Equal(One, NumberHelper.CreateChecked(decimal.One)); + + Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper.CreateChecked(decimal.MaxValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(decimal.MinValue)); + Assert.Throws(() => NumberHelper.CreateChecked(decimal.MinusOne)); + } + + [Fact] + public static void CreateCheckedFromDoubleTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(+0.0)); + Assert.Equal(Zero, NumberHelper.CreateChecked(-0.0)); + + Assert.Equal(Zero, NumberHelper.CreateChecked(+double.Epsilon)); + Assert.Equal(One, NumberHelper.CreateChecked(+1.0)); + + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(+170141183460469231731687303715884105728.0)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(+340282366920938425684442744474606501888.0)); + + Assert.Throws(() => NumberHelper.CreateChecked(-double.Epsilon)); + Assert.Throws(() => NumberHelper.CreateChecked(-1.0)); + + Assert.Throws(() => NumberHelper.CreateChecked(+340282366920938463463374607431768211456.0)); + Assert.Throws(() => NumberHelper.CreateChecked(-340282366920938425684442744474606501888.0)); + + Assert.Throws(() => NumberHelper.CreateChecked(double.MaxValue)); + Assert.Throws(() => NumberHelper.CreateChecked(double.MinValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(double.PositiveInfinity)); + Assert.Throws(() => NumberHelper.CreateChecked(double.NegativeInfinity)); + } + + [Fact] + public static void CreateCheckedFromHalfTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked((Half)(+0.0))); + Assert.Equal(Zero, NumberHelper.CreateChecked((Half)(-0.0))); + + Assert.Equal(Zero, NumberHelper.CreateChecked(+Half.Epsilon)); + Assert.Equal(One, NumberHelper.CreateChecked((Half)(+1.0))); + Assert.Equal(+65504U, NumberHelper.CreateChecked(Half.MaxValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(-Half.Epsilon)); + Assert.Throws(() => NumberHelper.CreateChecked((Half)(-1.0))); + Assert.Throws(() => NumberHelper.CreateChecked(Half.MinValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(Half.PositiveInfinity)); + Assert.Throws(() => NumberHelper.CreateChecked(Half.NegativeInfinity)); + } + + [Fact] + public static void CreateCheckedFromInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateChecked(0x7FFF)); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((short)0x8000))); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateCheckedFromInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked(0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((int)0x80000000))); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateCheckedFromInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(0x7FFFFFFFFFFFFFFF)); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((long)0x8000000000000000))); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateCheckedFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateChecked(unchecked((nint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateChecked(unchecked((nint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((nint)0x8000000000000000))); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateChecked((nint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked((nint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked((nint)0x7FFFFFFF)); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((nint)0x80000000))); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateCheckedFromSByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00)); + Assert.Equal(One, NumberHelper.CreateChecked(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateChecked(0x7F)); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((sbyte)0x80))); + Assert.Throws(() => NumberHelper.CreateChecked(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateCheckedFromSingleTest() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(+0.0f)); + Assert.Equal(Zero, NumberHelper.CreateChecked(-0.0f)); + + Assert.Equal(Zero, NumberHelper.CreateChecked(+float.Epsilon)); + Assert.Equal(One, NumberHelper.CreateChecked(+1.0f)); + + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(+170141183460469231731687303715884105728.0f)); + Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateChecked(float.MaxValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(-float.Epsilon)); + Assert.Throws(() => NumberHelper.CreateChecked(-1.0f)); + Assert.Throws(() => NumberHelper.CreateChecked(float.MinValue)); + + Assert.Throws(() => NumberHelper.CreateChecked(float.PositiveInfinity)); + Assert.Throws(() => NumberHelper.CreateChecked(float.NegativeInfinity)); + } + + [Fact] + public static void CreateCheckedFromUInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateChecked(0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateChecked(0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateChecked(0xFFFF)); + } + + [Fact] + public static void CreateCheckedFromUInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked(0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateChecked(0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateChecked(0xFFFFFFFF)); + } + + [Fact] + public static void CreateCheckedFromUInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateChecked(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateChecked(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateChecked(0x8000000000000000)); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateChecked(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateCheckedFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateChecked(unchecked((nuint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateChecked(unchecked((nuint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateChecked(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateChecked(unchecked((nuint)0x8000000000000000))); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateChecked(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateChecked((nuint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateChecked((nuint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateChecked((nuint)0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateChecked((nuint)0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateChecked((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateSaturatingFromByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(SByteMaxValuePlusOne, NumberHelper.CreateSaturating(0x80)); + Assert.Equal(ByteMaxValue, NumberHelper.CreateSaturating(0xFF)); + } + + [Fact] + public static void CreateSaturatingFromCharTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((char)0x0000)); + Assert.Equal(One, NumberHelper.CreateSaturating((char)0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateSaturating((char)0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateSaturating((char)0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateSaturating((char)0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromDecimalTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(decimal.Zero)); + Assert.Equal(One, NumberHelper.CreateSaturating(decimal.One)); + + Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper.CreateSaturating(decimal.MaxValue)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(decimal.MinValue)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(decimal.MinusOne)); + } + + [Fact] + public static void CreateSaturatingFromDoubleTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(+0.0)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-0.0)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(+double.Epsilon)); + Assert.Equal(One, NumberHelper.CreateSaturating(+1.0)); + + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(+170141183460469231731687303715884105728.0)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(+340282366920938425684442744474606501888.0)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(-double.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-1.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(+340282366920938463463374607431768211456.0)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-340282366920938425684442744474606501888.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(double.MaxValue)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(double.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(double.PositiveInfinity)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(double.NegativeInfinity)); + } + + [Fact] + public static void CreateSaturatingFromHalfTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((Half)(+0.0))); + Assert.Equal(Zero, NumberHelper.CreateSaturating((Half)(-0.0))); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(+Half.Epsilon)); + Assert.Equal(One, NumberHelper.CreateSaturating((Half)(+1.0))); + Assert.Equal(+65504U, NumberHelper.CreateSaturating(Half.MaxValue)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(-Half.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateSaturating((Half)(-1.0))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(Half.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(Half.PositiveInfinity)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(Half.NegativeInfinity)); + } + + [Fact] + public static void CreateSaturatingFromInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((short)0x8000))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((int)0x80000000))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((long)0x8000000000000000))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateSaturatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateSaturating(unchecked((nint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nint)0x8000000000000000))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((nint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating((nint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating((nint)0x7FFFFFFF)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nint)0x80000000))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateSaturatingFromSByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateSaturating(0x7F)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((sbyte)0x80))); + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateSaturatingFromSingleTest() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(+0.0f)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-0.0f)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(+float.Epsilon)); + Assert.Equal(One, NumberHelper.CreateSaturating(+1.0f)); + + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(+170141183460469231731687303715884105728.0f)); + Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateSaturating(float.MaxValue)); + + Assert.Equal(Zero, NumberHelper.CreateSaturating(-float.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(-1.0f)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(float.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateSaturating(float.PositiveInfinity)); + Assert.Equal(Zero, NumberHelper.CreateSaturating(float.NegativeInfinity)); + } + + [Fact] + public static void CreateSaturatingFromUInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateSaturating(0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateSaturating(0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateSaturating(0xFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateSaturating(0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateSaturating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateSaturating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateSaturating(0x8000000000000000)); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateSaturating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateSaturatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateSaturating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateSaturating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateSaturating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateSaturating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateSaturating((nuint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateSaturating((nuint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateSaturating((nuint)0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateSaturating((nuint)0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateSaturating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void CreateTruncatingFromByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(SByteMaxValuePlusOne, NumberHelper.CreateTruncating(0x80)); + Assert.Equal(ByteMaxValue, NumberHelper.CreateTruncating(0xFF)); + } + + [Fact] + public static void CreateTruncatingFromCharTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((char)0x0000)); + Assert.Equal(One, NumberHelper.CreateTruncating((char)0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateTruncating((char)0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateTruncating((char)0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateTruncating((char)0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromDecimalTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(decimal.Zero)); + Assert.Equal(One, NumberHelper.CreateTruncating(decimal.One)); + + Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), NumberHelper.CreateTruncating(decimal.MaxValue)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(decimal.MinValue)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(decimal.MinusOne)); + } + + [Fact] + public static void CreateTruncatingFromDoubleTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(+0.0)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-0.0)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(+double.Epsilon)); + Assert.Equal(One, NumberHelper.CreateTruncating(+1.0)); + + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(+170141183460469231731687303715884105728.0)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(+340282366920938425684442744474606501888.0)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(-double.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-1.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(+340282366920938463463374607431768211456.0)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-340282366920938425684442744474606501888.0)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(double.MaxValue)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(double.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(double.PositiveInfinity)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(double.NegativeInfinity)); + } + + [Fact] + public static void CreateTruncatingFromHalfTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((Half)(+0.0))); + Assert.Equal(Zero, NumberHelper.CreateTruncating((Half)(-0.0))); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(+Half.Epsilon)); + Assert.Equal(One, NumberHelper.CreateTruncating((Half)(+1.0))); + Assert.Equal(+65504U, NumberHelper.CreateTruncating(Half.MaxValue)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(-Half.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateTruncating((Half)(-1.0))); + Assert.Equal(Zero, NumberHelper.CreateTruncating(Half.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(Half.PositiveInfinity)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(Half.NegativeInfinity)); + } + + [Fact] + public static void CreateTruncatingFromInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_8000), NumberHelper.CreateTruncating(unchecked((short)0x8000))); + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(unchecked((short)0xFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_8000_0000), NumberHelper.CreateTruncating(unchecked((int)0x80000000))); + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(unchecked((int)0xFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0x8000_0000_0000_0000), NumberHelper.CreateTruncating(unchecked((long)0x8000000000000000))); + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(unchecked((long)0xFFFFFFFFFFFFFFFF))); + } + + [Fact] + public static void CreateTruncatingFromIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateTruncating(unchecked((nint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(unchecked((nint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0x8000_0000_0000_0000), NumberHelper.CreateTruncating(unchecked((nint)0x8000000000000000))); + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((nint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating((nint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating((nint)0x7FFFFFFF)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_8000_0000), NumberHelper.CreateTruncating(unchecked((nint)0x80000000))); + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(unchecked((nint)0xFFFFFFFF))); + } + } + + [Fact] + public static void CreateTruncatingFromSByteTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x01)); + Assert.Equal(SByteMaxValue, NumberHelper.CreateTruncating(0x7F)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FF80), NumberHelper.CreateTruncating(unchecked((sbyte)0x80))); + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(unchecked((sbyte)0xFF))); + } + + [Fact] + public static void CreateTruncatingFromSingleTest() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(+0.0f)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-0.0f)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(+float.Epsilon)); + Assert.Equal(One, NumberHelper.CreateTruncating(+1.0f)); + + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(+170141183460469231731687303715884105728.0f)); + Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), NumberHelper.CreateTruncating(float.MaxValue)); + + Assert.Equal(Zero, NumberHelper.CreateTruncating(-float.Epsilon)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(-1.0f)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(float.MinValue)); + + Assert.Equal(MaxValue, NumberHelper.CreateTruncating(float.PositiveInfinity)); + Assert.Equal(Zero, NumberHelper.CreateTruncating(float.NegativeInfinity)); + } + + [Fact] + public static void CreateTruncatingFromUInt16Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0001)); + Assert.Equal(Int16MaxValue, NumberHelper.CreateTruncating(0x7FFF)); + Assert.Equal(Int16MaxValuePlusOne, NumberHelper.CreateTruncating(0x8000)); + Assert.Equal(UInt16MaxValue, NumberHelper.CreateTruncating(0xFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt32Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateTruncating(0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateTruncating(0xFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUInt64Test() + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(0x0000000000000000)); + Assert.Equal(One, NumberHelper.CreateTruncating(0x0000000000000001)); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(0x7FFFFFFFFFFFFFFF)); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateTruncating(0x8000000000000000)); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateTruncating(0xFFFFFFFFFFFFFFFF)); + } + + [Fact] + public static void CreateTruncatingFromUIntPtrTest() + { + if (Environment.Is64BitProcess) + { + Assert.Equal(Zero, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000000))); + Assert.Equal(One, NumberHelper.CreateTruncating(unchecked((nuint)0x0000000000000001))); + Assert.Equal(Int64MaxValue, NumberHelper.CreateTruncating(unchecked((nuint)0x7FFFFFFFFFFFFFFF))); + Assert.Equal(Int64MaxValuePlusOne, NumberHelper.CreateTruncating(unchecked((nuint)0x8000000000000000))); + Assert.Equal(UInt64MaxValue, NumberHelper.CreateTruncating(unchecked((nuint)0xFFFFFFFFFFFFFFFF))); + } + else + { + Assert.Equal(Zero, NumberHelper.CreateTruncating((nuint)0x00000000)); + Assert.Equal(One, NumberHelper.CreateTruncating((nuint)0x00000001)); + Assert.Equal(Int32MaxValue, NumberHelper.CreateTruncating((nuint)0x7FFFFFFF)); + Assert.Equal(Int32MaxValuePlusOne, NumberHelper.CreateTruncating((nuint)0x80000000)); + Assert.Equal(UInt32MaxValue, NumberHelper.CreateTruncating((nuint)0xFFFFFFFF)); + } + } + + [Fact] + public static void TryCreateFromByteTest() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal(SByteMaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x80, out result)); + Assert.Equal(SByteMaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFF, out result)); + Assert.Equal(ByteMaxValue, result); + } + + [Fact] + public static void TryCreateFromCharTest() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate((char)0x0000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((char)0x0001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate((char)0x7FFF, out result)); + Assert.Equal(Int16MaxValue, result); + + Assert.True(NumberHelper.TryCreate((char)0x8000, out result)); + Assert.Equal(Int16MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate((char)0xFFFF, out result)); + Assert.Equal(UInt16MaxValue, result); + } + + [Fact] + public static void TryCreateFromDecimalTest() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(decimal.Zero, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(decimal.One, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(decimal.MaxValue, out result)); + Assert.Equal(new UInt128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), result); + + Assert.False(NumberHelper.TryCreate(decimal.MinValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(decimal.MinusOne, out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromDoubleTest() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(+0.0, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(-0.0, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+double.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+1.0, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(+170141183460469231731687303715884105728.0, out result)); + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), result); + + Assert.True(NumberHelper.TryCreate(+340282366920938425684442744474606501888.0, out result)); + Assert.Equal(new UInt128(0xFFFF_FFFF_FFFF_F800, 0x0000_0000_0000_0000), result); + + Assert.False(NumberHelper.TryCreate(-double.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(-1.0, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(+340282366920938463463374607431768211456.0, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(-340282366920938425684442744474606501888.0, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.MaxValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.MinValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.PositiveInfinity, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(double.NegativeInfinity, out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromHalfTest() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate((Half)(+0.0), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((Half)(-0.0), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+Half.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((Half)(+1.0), out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(Half.MaxValue, out result)); + Assert.Equal(+65504U, result); + + Assert.False(NumberHelper.TryCreate(-Half.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate((Half)(-1.0), out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(Half.MinValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(Half.PositiveInfinity, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(Half.NegativeInfinity, out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromInt16Test() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal(Int16MaxValue, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0x8000), out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(unchecked((short)0xFFFF), out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromInt32Test() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0x80000000), out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(unchecked((int)0xFFFFFFFF), out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromInt64Test() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0x8000000000000000), out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(unchecked((long)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromIntPtrTest() + { + UInt128 result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000000), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x0000000000000001), out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x8000000000000000), out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(Zero, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nint)0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((nint)0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate((nint)0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0x80000000), out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(unchecked((nint)0xFFFFFFFF), out result)); + Assert.Equal(Zero, result); + } + } + + [Fact] + public static void TryCreateFromSByteTest() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x00, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x01, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7F, out result)); + Assert.Equal(SByteMaxValue, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0x80), out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(unchecked((sbyte)0xFF), out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromSingleTest() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(+0.0f, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(-0.0f, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+float.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(+1.0f, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(+170141183460469231731687303715884105728.0f, out result)); + Assert.Equal(new UInt128(0x8000_0000_0000_0000, 0x0000_0000_0000_0000), result); + + Assert.True(NumberHelper.TryCreate(float.MaxValue, out result)); + Assert.Equal(new UInt128(0xFFFF_FF00_0000_0000, 0x0000_0000_0000_0000), result); + + Assert.False(NumberHelper.TryCreate(-float.Epsilon, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(-1.0f, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(float.MinValue, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(float.PositiveInfinity, out result)); + Assert.Equal(Zero, result); + + Assert.False(NumberHelper.TryCreate(float.NegativeInfinity, out result)); + Assert.Equal(Zero, result); + } + + [Fact] + public static void TryCreateFromUInt16Test() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x0000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFF, out result)); + Assert.Equal(Int16MaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x8000, out result)); + Assert.Equal(Int16MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFFFF, out result)); + Assert.Equal(UInt16MaxValue, result); + } + + [Fact] + public static void TryCreateFromUInt32Test() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x80000000, out result)); + Assert.Equal(Int32MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFF, out result)); + Assert.Equal(UInt32MaxValue, result); + } + + [Fact] + public static void TryCreateFromUInt64Test() + { + UInt128 result; + + Assert.True(NumberHelper.TryCreate(0x0000000000000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(0x0000000000000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(0x7FFFFFFFFFFFFFFF, out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.True(NumberHelper.TryCreate(0x8000000000000000, out result)); + Assert.Equal(Int64MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(0xFFFFFFFFFFFFFFFF, out result)); + Assert.Equal(UInt64MaxValue, result); + } + + [Fact] + public static void TryCreateFromUIntPtrTest() + { + UInt128 result; + + if (Environment.Is64BitProcess) + { + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000000), out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x0000000000000001), out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x7FFFFFFFFFFFFFFF), out result)); + Assert.Equal(Int64MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x8000000000000000), out result)); + Assert.Equal(Int64MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFFFFFFFFFF), out result)); + Assert.Equal(UInt64MaxValue, result); + } + else + { + Assert.True(NumberHelper.TryCreate((nuint)0x00000000, out result)); + Assert.Equal(Zero, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x00000001, out result)); + Assert.Equal(One, result); + + Assert.True(NumberHelper.TryCreate((nuint)0x7FFFFFFF, out result)); + Assert.Equal(Int32MaxValue, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0x80000000), out result)); + Assert.Equal(Int32MaxValuePlusOne, result); + + Assert.True(NumberHelper.TryCreate(unchecked((nuint)0xFFFFFFFF), out result)); + Assert.Equal(UInt32MaxValue, result); + } + } + + // + // IShiftOperators + // + + [Fact] + public static void op_LeftShiftTest() + { + Assert.Equal(Zero, ShiftOperatorsHelper.op_LeftShift(Zero, 1)); + Assert.Equal(Two, ShiftOperatorsHelper.op_LeftShift(One, 1)); + Assert.Equal(MaxValueMinusOne, ShiftOperatorsHelper.op_LeftShift(Int128MaxValue, 1)); + Assert.Equal(Zero, ShiftOperatorsHelper.op_LeftShift(Int128MaxValuePlusOne, 1)); + Assert.Equal(MaxValueMinusOne, ShiftOperatorsHelper.op_LeftShift(MaxValue, 1)); + } + + [Fact] + public static void op_RightShiftTest() + { + Assert.Equal(Zero, ShiftOperatorsHelper.op_RightShift(Zero, 1)); + Assert.Equal(Zero, ShiftOperatorsHelper.op_RightShift(One, 1)); + Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper.op_RightShift(Int128MaxValue, 1)); + Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper.op_RightShift(Int128MaxValuePlusOne, 1)); + Assert.Equal(Int128MaxValue, ShiftOperatorsHelper.op_RightShift(MaxValue, 1)); + } + + [Fact] + public static void op_UnsignedRightShiftTest() + { + Assert.Equal(Zero, ShiftOperatorsHelper.op_UnsignedRightShift(Zero, 1)); + Assert.Equal(Zero, ShiftOperatorsHelper.op_UnsignedRightShift(One, 1)); + Assert.Equal(new UInt128(0x3FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF), ShiftOperatorsHelper.op_UnsignedRightShift(Int128MaxValue, 1)); + Assert.Equal(new UInt128(0x4000_0000_0000_0000, 0x0000_0000_0000_0000), ShiftOperatorsHelper.op_UnsignedRightShift(Int128MaxValuePlusOne, 1)); + Assert.Equal(Int128MaxValue, ShiftOperatorsHelper.op_UnsignedRightShift(MaxValue, 1)); + } + + // + // ISubtractionOperators + // + + [Fact] + public static void op_SubtractionTest() + { + Assert.Equal(MaxValue, SubtractionOperatorsHelper.op_Subtraction(Zero, 1U)); + Assert.Equal(Zero, SubtractionOperatorsHelper.op_Subtraction(One, 1U)); + Assert.Equal(Int128MaxValueMinusOne, SubtractionOperatorsHelper.op_Subtraction(Int128MaxValue, 1U)); + Assert.Equal(Int128MaxValue, SubtractionOperatorsHelper.op_Subtraction(Int128MaxValuePlusOne, 1U)); + Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper.op_Subtraction(MaxValue, 1U)); + } + + [Fact] + public static void op_CheckedSubtractionTest() + { + Assert.Equal(Zero, SubtractionOperatorsHelper.op_CheckedSubtraction(One, 1U)); + Assert.Equal(Int128MaxValueMinusOne, SubtractionOperatorsHelper.op_CheckedSubtraction(Int128MaxValue, 1U)); + Assert.Equal(Int128MaxValue, SubtractionOperatorsHelper.op_CheckedSubtraction(Int128MaxValuePlusOne, 1U)); + Assert.Equal(MaxValueMinusOne, SubtractionOperatorsHelper.op_CheckedSubtraction(MaxValue, 1U)); + + Assert.Throws(() => SubtractionOperatorsHelper.op_CheckedSubtraction(Zero, 1U)); + } + + // + // IUnaryNegationOperators + // + + [Fact] + public static void op_UnaryNegationTest() + { + Assert.Equal(Zero, UnaryNegationOperatorsHelper.op_UnaryNegation(Zero)); + Assert.Equal(MaxValue, UnaryNegationOperatorsHelper.op_UnaryNegation(One)); + Assert.Equal(Int128MaxValuePlusTwo, UnaryNegationOperatorsHelper.op_UnaryNegation(Int128MaxValue)); + Assert.Equal(Int128MaxValuePlusOne, UnaryNegationOperatorsHelper.op_UnaryNegation(Int128MaxValuePlusOne)); + Assert.Equal(One, UnaryNegationOperatorsHelper.op_UnaryNegation(MaxValue)); + } + + [Fact] + public static void op_CheckedUnaryNegationTest() + { + Assert.Equal(Zero, UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(Zero)); + + Assert.Throws(() => UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(One)); + Assert.Throws(() => UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(Int128MaxValue)); + Assert.Throws(() => UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(Int128MaxValuePlusOne)); + Assert.Throws(() => UnaryNegationOperatorsHelper.op_CheckedUnaryNegation(MaxValue)); + } + + // + // IUnaryPlusOperators + // + + [Fact] + public static void op_UnaryPlusTest() + { + Assert.Equal(Zero, UnaryPlusOperatorsHelper.op_UnaryPlus(Zero)); + Assert.Equal(One, UnaryPlusOperatorsHelper.op_UnaryPlus(One)); + Assert.Equal(Int128MaxValue, UnaryPlusOperatorsHelper.op_UnaryPlus(Int128MaxValue)); + Assert.Equal(Int128MaxValuePlusOne, UnaryPlusOperatorsHelper.op_UnaryPlus(Int128MaxValuePlusOne)); + Assert.Equal(MaxValue, UnaryPlusOperatorsHelper.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 index 0000000000000..a02f205e72fa8 --- /dev/null +++ b/src/libraries/System.Runtime/tests/System/UInt128Tests.cs @@ -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 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 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(null, () => ((UInt128)123).CompareTo(value)); + } + + public static IEnumerable 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 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(() => i.ToString("Y")); // Invalid format + Assert.Throws(() => i.ToString("Y", null)); // Invalid format + } + + public static IEnumerable 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 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(paramName, () => UInt128.TryParse("1", style, null, out result)); + Assert.Equal(default(UInt128), result); + + AssertExtensions.Throws(paramName, () => UInt128.Parse("1", style)); + AssertExtensions.Throws(paramName, () => UInt128.Parse("1", style, null)); + } + + public static IEnumerable 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)); + } + } + } +} diff --git a/src/tests/Interop/CMakeLists.txt b/src/tests/Interop/CMakeLists.txt index f727473ffcf0b..2d9e84209b2cf 100644 --- a/src/tests/Interop/CMakeLists.txt +++ b/src/tests/Interop/CMakeLists.txt @@ -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 index 0000000000000..9474de8c3dcd6 --- /dev/null +++ b/src/tests/Interop/PInvoke/Int128/CMakeLists.txt @@ -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 index 0000000000000..28f70bca06fab --- /dev/null +++ b/src/tests/Interop/PInvoke/Int128/Int128Native.cpp @@ -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 +#include +#include +#include + +#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 index 0000000000000..5a9ddd5bb2135 --- /dev/null +++ b/src/tests/Interop/PInvoke/Int128/Int128Test.cs @@ -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 index 0000000000000..42fc09c10b718 --- /dev/null +++ b/src/tests/Interop/PInvoke/Int128/Int128Test.csproj @@ -0,0 +1,14 @@ + + + + true + embedded + exe + + + + + + + + diff --git a/src/tests/Interop/PInvoke/Int128/Program.cs b/src/tests/Interop/PInvoke/Int128/Program.cs new file mode 100644 index 0000000000000..effd0b8b09d27 --- /dev/null +++ b/src/tests/Interop/PInvoke/Int128/Program.cs @@ -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 index 0000000000000..6d561cf424343 --- /dev/null +++ b/src/tests/Interop/PInvoke/Int128/UInt128Native.cpp @@ -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 +#include +#include +#include + +#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 index 0000000000000..6398a77ea5790 --- /dev/null +++ b/src/tests/Interop/PInvoke/Int128/UInt128Test.cs @@ -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); + } +} diff --git a/src/tests/issues.targets b/src/tests/issues.targets index 91a50630e042b..79446ba1ad3f2 100644 --- a/src/tests/issues.targets +++ b/src/tests/issues.targets @@ -1758,6 +1758,9 @@ needs triage + + needs triage + https://github.com/dotnet/runtime/issues/48084 @@ -3299,6 +3302,9 @@ https://github.com/dotnet/runtime/issues/41519 + + https://github.com/dotnet/runtime/issues/41519 + https://github.com/dotnet/runtime/issues/41519