From eb6c0bb376bc456b798c048b76f56ca5a2b51808 Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Thu, 10 Sep 2026 14:15:27 +1000 Subject: [PATCH] Add BigMul across the integer types Ten members: int.BigMul, uint.BigMul, long.BigMul and ulong.BigMul (net9), Int128.BigMul, UInt128.BigMul, nint.BigMul and nuint.BigMul (net11), and the two Math.BigMul out-overloads, which are net5 rather than netcoreapp3.0 as the docs imply, and were missing on every target below that. The three net9 Math.BigMul overloads cannot be added. Math.BigMul(uint, uint), Math.BigMul(long, long) and Math.BigMul(ulong, ulong) have signatures identical to the numeric-type members above, and C# emits both static extension members onto the same class, so declaring both is CS0111. The numeric-type set is the coherent one to keep, and the omission is noted on each of the three. The four overloads returning Int128 or UInt128 are net7.0 and later, since those types do not exist below that. Noted, since the api_list section header does not show it. Verified against net11 by reconstructing every product from the returned halves and comparing to BigInteger: Math.BigMul(long, long, out long) returns a signed upper half over an unsigned lower half, and nint.BigMul splits at 32 or 64 bits depending on IntPtr.Size. The tests use the same oracle and run on every target, so they validate the BCL on net9.0 and later and the polyfill below. API count 1095 -> 1105. --- apiCount.include.md | 44 ++--- api_list.include.md | 15 ++ assemblySize.include.md | 80 ++++----- readme.md | 143 ++++++++------- src/Consume/Consume.cs | 16 ++ src/Polyfill/MathPolyfill.cs | 55 ++++++ src/Polyfill/Numbers/Int128Polyfill.cs | 16 ++ src/Polyfill/Numbers/Int32Polyfill.cs | 11 ++ src/Polyfill/Numbers/Int64Polyfill.cs | 13 ++ src/Polyfill/Numbers/IntPtrPolyfill.cs | 21 +++ src/Polyfill/Numbers/UInt128Polyfill.cs | 28 +++ src/Polyfill/Numbers/UInt32Polyfill.cs | 12 ++ src/Polyfill/Numbers/UInt64Polyfill.cs | 13 ++ src/Polyfill/Numbers/UIntPtrPolyfill.cs | 21 +++ src/Split/net10.0/Numbers/Int128Polyfill.cs | 12 ++ src/Split/net10.0/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net10.0/Numbers/UInt128Polyfill.cs | 20 +++ src/Split/net10.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net461/MathPolyfill.cs | 34 ++++ src/Split/net461/Numbers/Int32Polyfill.cs | 5 + src/Split/net461/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net461/Numbers/UInt32Polyfill.cs | 5 + src/Split/net461/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net462/MathPolyfill.cs | 34 ++++ src/Split/net462/Numbers/Int32Polyfill.cs | 5 + src/Split/net462/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net462/Numbers/UInt32Polyfill.cs | 5 + src/Split/net462/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net47/MathPolyfill.cs | 34 ++++ src/Split/net47/Numbers/Int32Polyfill.cs | 5 + src/Split/net47/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net47/Numbers/UInt32Polyfill.cs | 5 + src/Split/net47/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net471/MathPolyfill.cs | 34 ++++ src/Split/net471/Numbers/Int32Polyfill.cs | 5 + src/Split/net471/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net471/Numbers/UInt32Polyfill.cs | 5 + src/Split/net471/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net472/MathPolyfill.cs | 34 ++++ src/Split/net472/Numbers/Int32Polyfill.cs | 5 + src/Split/net472/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net472/Numbers/UInt32Polyfill.cs | 5 + src/Split/net472/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net48/MathPolyfill.cs | 34 ++++ src/Split/net48/Numbers/Int32Polyfill.cs | 5 + src/Split/net48/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net48/Numbers/UInt32Polyfill.cs | 5 + src/Split/net48/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net481/MathPolyfill.cs | 34 ++++ src/Split/net481/Numbers/Int32Polyfill.cs | 5 + src/Split/net481/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net481/Numbers/UInt32Polyfill.cs | 5 + src/Split/net481/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net5.0/Numbers/Int32Polyfill.cs | 5 + src/Split/net5.0/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net5.0/Numbers/UInt32Polyfill.cs | 5 + src/Split/net5.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net6.0/Numbers/Int32Polyfill.cs | 5 + src/Split/net6.0/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net6.0/Numbers/UInt32Polyfill.cs | 5 + src/Split/net6.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net7.0/Numbers/Int128Polyfill.cs | 12 ++ src/Split/net7.0/Numbers/Int32Polyfill.cs | 5 + src/Split/net7.0/Numbers/Int64Polyfill.cs | 5 + src/Split/net7.0/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net7.0/Numbers/UInt128Polyfill.cs | 20 +++ src/Split/net7.0/Numbers/UInt32Polyfill.cs | 5 + src/Split/net7.0/Numbers/UInt64Polyfill.cs | 5 + src/Split/net7.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net8.0/Numbers/Int128Polyfill.cs | 12 ++ src/Split/net8.0/Numbers/Int32Polyfill.cs | 5 + src/Split/net8.0/Numbers/Int64Polyfill.cs | 5 + src/Split/net8.0/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net8.0/Numbers/UInt128Polyfill.cs | 20 +++ src/Split/net8.0/Numbers/UInt32Polyfill.cs | 5 + src/Split/net8.0/Numbers/UInt64Polyfill.cs | 5 + src/Split/net8.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/net9.0/Numbers/Int128Polyfill.cs | 12 ++ src/Split/net9.0/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/net9.0/Numbers/UInt128Polyfill.cs | 20 +++ src/Split/net9.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/netcoreapp2.0/MathPolyfill.cs | 34 ++++ .../netcoreapp2.0/Numbers/Int32Polyfill.cs | 5 + .../netcoreapp2.0/Numbers/IntPtrPolyfill.cs | 18 ++ .../netcoreapp2.0/Numbers/UInt32Polyfill.cs | 5 + .../netcoreapp2.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/netcoreapp2.1/MathPolyfill.cs | 34 ++++ .../netcoreapp2.1/Numbers/Int32Polyfill.cs | 5 + .../netcoreapp2.1/Numbers/IntPtrPolyfill.cs | 18 ++ .../netcoreapp2.1/Numbers/UInt32Polyfill.cs | 5 + .../netcoreapp2.1/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/netcoreapp2.2/MathPolyfill.cs | 34 ++++ .../netcoreapp2.2/Numbers/Int32Polyfill.cs | 5 + .../netcoreapp2.2/Numbers/IntPtrPolyfill.cs | 18 ++ .../netcoreapp2.2/Numbers/UInt32Polyfill.cs | 5 + .../netcoreapp2.2/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/netcoreapp3.0/MathPolyfill.cs | 34 ++++ .../netcoreapp3.0/Numbers/Int32Polyfill.cs | 5 + .../netcoreapp3.0/Numbers/IntPtrPolyfill.cs | 18 ++ .../netcoreapp3.0/Numbers/UInt32Polyfill.cs | 5 + .../netcoreapp3.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/netcoreapp3.1/MathPolyfill.cs | 34 ++++ .../netcoreapp3.1/Numbers/Int32Polyfill.cs | 5 + .../netcoreapp3.1/Numbers/IntPtrPolyfill.cs | 18 ++ .../netcoreapp3.1/Numbers/UInt32Polyfill.cs | 5 + .../netcoreapp3.1/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/netstandard2.0/MathPolyfill.cs | 34 ++++ .../netstandard2.0/Numbers/Int32Polyfill.cs | 5 + .../netstandard2.0/Numbers/IntPtrPolyfill.cs | 18 ++ .../netstandard2.0/Numbers/UInt32Polyfill.cs | 5 + .../netstandard2.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/netstandard2.1/MathPolyfill.cs | 34 ++++ .../netstandard2.1/Numbers/Int32Polyfill.cs | 5 + .../netstandard2.1/Numbers/IntPtrPolyfill.cs | 18 ++ .../netstandard2.1/Numbers/UInt32Polyfill.cs | 5 + .../netstandard2.1/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Split/uap10.0/MathPolyfill.cs | 34 ++++ src/Split/uap10.0/Numbers/Int32Polyfill.cs | 5 + src/Split/uap10.0/Numbers/IntPtrPolyfill.cs | 18 ++ src/Split/uap10.0/Numbers/UInt32Polyfill.cs | 5 + src/Split/uap10.0/Numbers/UIntPtrPolyfill.cs | 18 ++ src/Tests/BigMulTests.cs | 167 ++++++++++++++++++ 122 files changed, 2133 insertions(+), 126 deletions(-) create mode 100644 src/Tests/BigMulTests.cs diff --git a/apiCount.include.md b/apiCount.include.md index 2272c410..7149901f 100644 --- a/apiCount.include.md +++ b/apiCount.include.md @@ -1,28 +1,28 @@ -**API count: 1095** +**API count: 1105** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1026 | -| `net462` | 1026 | -| `net47` | 1025 | -| `net471` | 1024 | -| `net472` | 1020 | -| `net48` | 1020 | -| `net481` | 1020 | -| `netstandard2.0` | 1022 | -| `netstandard2.1` | 875 | -| `netcoreapp2.0` | 945 | -| `netcoreapp2.1` | 886 | -| `netcoreapp2.2` | 886 | -| `netcoreapp3.0` | 847 | -| `netcoreapp3.1` | 846 | -| `net5.0` | 718 | -| `net6.0` | 619 | -| `net7.0` | 466 | -| `net8.0` | 345 | -| `net9.0` | 232 | -| `net10.0` | 178 | +| `net461` | 1032 | +| `net462` | 1032 | +| `net47` | 1031 | +| `net471` | 1030 | +| `net472` | 1026 | +| `net48` | 1026 | +| `net481` | 1026 | +| `netstandard2.0` | 1028 | +| `netstandard2.1` | 881 | +| `netcoreapp2.0` | 951 | +| `netcoreapp2.1` | 892 | +| `netcoreapp2.2` | 892 | +| `netcoreapp3.0` | 853 | +| `netcoreapp3.1` | 852 | +| `net5.0` | 722 | +| `net6.0` | 623 | +| `net7.0` | 474 | +| `net8.0` | 353 | +| `net9.0` | 236 | +| `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1012 | +| `uap10.0` | 1018 | diff --git a/api_list.include.md b/api_list.include.md index 776189f0..d0ca7ab5 100644 --- a/api_list.include.md +++ b/api_list.include.md @@ -594,6 +594,7 @@ #### Int128 + * `Int128 BigMul(Int128, Int128, Int128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int128.bigmul?view=net-11.0) * `Int128 Log10(Int128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int128.log10?view=net-11.0) @@ -615,6 +616,7 @@ * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryformat?view=net-11.0#system-int32-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryformat?view=net-11.0#system-int32-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `long BigMul(int, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.bigmul?view=net-11.0) * `int Log10(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryparse?view=net-11.0#system-int32-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-int32@)) * `bool TryParse(ReadOnlySpan, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryparse?view=net-11.0#system-int32-tryparse(system-readonlyspan((system-char))-system-globalization-numberstyles-system-iformatprovider-system-int32@)) @@ -629,6 +631,9 @@ * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryformat?view=net-11.0#system-int64-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryformat?view=net-11.0#system-int64-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `Int128 BigMul(long, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.bigmul?view=net-11.0) + * Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. + * Note: Only available on net7.0 and later, since Int128 does not exist below that. * `long Log10(long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryparse?view=net-11.0#system-int64-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-int64@)) * `bool TryParse(ReadOnlySpan, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryparse?view=net-11.0#system-int64-tryparse(system-readonlyspan((system-char))-system-globalization-numberstyles-system-iformatprovider-system-int64@)) @@ -647,6 +652,7 @@ #### IntPtr + * `nint BigMul(nint, nint, nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.bigmul?view=net-11.0) * `nint Log10(nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.tryparse?view=net-11.0#system-intptr-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-intptr@)) * `bool TryParse(ReadOnlySpan, nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.tryparse?view=net-11.0#system-intptr-tryparse(system-readonlyspan((system-byte))-system-intptr@)) @@ -696,6 +702,8 @@ #### Math + * `long BigMul(long, long, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.bigmul?view=net-11.0#system-math-bigmul(system-int64-system-int64-system-int64@)) + * `ulong BigMul(ulong, ulong, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.bigmul?view=net-11.0#system-math-bigmul(system-uint64-system-uint64-system-uint64@)) * `byte Clamp(byte, byte, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp?view=net-11.0#system-math-clamp(system-byte-system-byte-system-byte)) * `decimal Clamp(decimal, decimal, decimal)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp?view=net-11.0#system-math-clamp(system-decimal-system-decimal-system-decimal)) * `double Clamp(double, double, double)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp?view=net-11.0#system-math-clamp(system-double-system-double-system-double)) @@ -1397,6 +1405,7 @@ #### UInt128 + * `UInt128 BigMul(UInt128, UInt128, UInt128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint128.bigmul?view=net-11.0) * `UInt128 Log10(UInt128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint128.log10?view=net-11.0) @@ -1418,6 +1427,8 @@ * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryformat?view=net-11.0#system-uint32-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryformat?view=net-11.0#system-uint32-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `ulong BigMul(uint, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.bigmul?view=net-11.0) + * Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. * `uint Log10(uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryparse?view=net-11.0#system-uint32-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-uint32@)) * `bool TryParse(ReadOnlySpan, NumberStyles, IFormatProvider?, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryparse?view=net-11.0#system-uint32-tryparse(system-readonlyspan((system-byte))-system-globalization-numberstyles-system-iformatprovider-system-uint32@)) @@ -1432,6 +1443,9 @@ * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryformat?view=net-11.0#system-uint64-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryformat?view=net-11.0#system-uint64-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `UInt128 BigMul(ulong, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.bigmul?view=net-11.0) + * Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. + * Note: Only available on net7.0 and later, since UInt128 does not exist below that. * `ulong Log10(ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryparse?view=net-11.0#system-uint64-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-uint64@)) * `bool TryParse(ReadOnlySpan, NumberStyles, IFormatProvider?, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryparse?view=net-11.0#system-uint64-tryparse(system-readonlyspan((system-byte))-system-globalization-numberstyles-system-iformatprovider-system-uint64@)) @@ -1444,6 +1458,7 @@ #### UIntPtr + * `nuint BigMul(nuint, nuint, nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.bigmul?view=net-11.0) * `nuint Log10(nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.tryparse?view=net-11.0#system-uintptr-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-uintptr@)) * `bool TryParse(ReadOnlySpan, nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.tryparse?view=net-11.0#system-uintptr-tryparse(system-readonlyspan((system-byte))-system-uintptr@)) diff --git a/assemblySize.include.md b/assemblySize.include.md index ef20e168..e3799e63 100644 --- a/assemblySize.include.md +++ b/assemblySize.include.md @@ -2,26 +2,26 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 369.0KB | +361.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 324.5KB | +316.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 373.0KB | +366.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net47 | 7.0KB | 372.5KB | +365.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net472 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 348.0KB | +339.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.1 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.2 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp3.0 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 322.5KB | +313.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net5.0 | 9.5KB | 286.5KB | +277.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net6.0 | 10.0KB | 228.5KB | +218.5KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 193.5KB | +183.5KB | +9.5KB | +6.0KB | +1.0KB | +3.5KB | -| net8.0 | 9.5KB | 164.0KB | +154.5KB | +8.0KB | | +512bytes | +3.0KB | -| net9.0 | 9.5KB | 114.0KB | +104.5KB | +8.5KB | | +512bytes | +3.5KB | -| net10.0 | 10.0KB | 92.0KB | +82.0KB | +8.5KB | | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 370.5KB | +362.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 326.0KB | +317.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | +| net461 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net47 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 371.5KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net472 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net48 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net481 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| netcoreapp2.0 | 9.0KB | 349.0KB | +340.0KB | +8.0KB | +7.0KB | +9.5KB | +14.0KB | +| netcoreapp2.1 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 325.5KB | +316.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 323.5KB | +314.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net5.0 | 9.5KB | 287.5KB | +278.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net6.0 | 10.0KB | 229.0KB | +219.0KB | +10.0KB | +7.0KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 195.5KB | +185.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 165.5KB | +156.0KB | +8.5KB | | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 115.5KB | +106.0KB | +8.5KB | | +512bytes | +3.0KB | +| net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -29,24 +29,24 @@ | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 541.1KB | +533.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 470.0KB | +461.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 541.1KB | +532.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 546.1KB | +539.1KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net47 | 7.0KB | 545.4KB | +538.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 543.0KB | +534.5KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net472 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 509.5KB | +500.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.1 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.2 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp3.0 | 9.5KB | 466.5KB | +457.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 464.9KB | +455.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net5.0 | 9.5KB | 411.1KB | +401.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net6.0 | 10.0KB | 333.2KB | +323.2KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 279.9KB | +269.9KB | +17.1KB | +7.4KB | +1.6KB | +4.2KB | -| net8.0 | 9.5KB | 236.5KB | +227.0KB | +15.5KB | +299bytes | +1.1KB | +3.7KB | -| net9.0 | 9.5KB | 164.6KB | +155.1KB | +16.0KB | | +1.1KB | +4.2KB | -| net10.0 | 10.0KB | 133.9KB | +123.9KB | +16.0KB | | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 543.3KB | +535.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 472.2KB | +463.7KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | +| net461 | 8.5KB | 542.8KB | +534.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 547.8KB | +540.8KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net47 | 7.0KB | 547.6KB | +540.6KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 544.7KB | +536.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net472 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net48 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net481 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| netcoreapp2.0 | 9.0KB | 511.2KB | +502.2KB | +15.7KB | +8.7KB | +14.4KB | +19.4KB | +| netcoreapp2.1 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 468.7KB | +459.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 466.7KB | +457.2KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net5.0 | 9.5KB | 412.6KB | +403.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net6.0 | 10.0KB | 334.1KB | +324.1KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 282.8KB | +272.8KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 239.0KB | +229.5KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 166.8KB | +157.3KB | +16.0KB | | +1.1KB | +3.7KB | +| net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | diff --git a/readme.md b/readme.md index 6e1774c1..45d6bcde 100644 --- a/readme.md +++ b/readme.md @@ -13,34 +13,34 @@ The package targets `netstandard2.0` and is designed to support the following ru * `uap10` -**API count: 1095** +**API count: 1105** ### Per Target Framework | Target | APIs | | -- | -- | -| `net461` | 1026 | -| `net462` | 1026 | -| `net47` | 1025 | -| `net471` | 1024 | -| `net472` | 1020 | -| `net48` | 1020 | -| `net481` | 1020 | -| `netstandard2.0` | 1022 | -| `netstandard2.1` | 875 | -| `netcoreapp2.0` | 945 | -| `netcoreapp2.1` | 886 | -| `netcoreapp2.2` | 886 | -| `netcoreapp3.0` | 847 | -| `netcoreapp3.1` | 846 | -| `net5.0` | 718 | -| `net6.0` | 619 | -| `net7.0` | 466 | -| `net8.0` | 345 | -| `net9.0` | 232 | -| `net10.0` | 178 | +| `net461` | 1032 | +| `net462` | 1032 | +| `net47` | 1031 | +| `net471` | 1030 | +| `net472` | 1026 | +| `net48` | 1026 | +| `net481` | 1026 | +| `netstandard2.0` | 1028 | +| `netstandard2.1` | 881 | +| `netcoreapp2.0` | 951 | +| `netcoreapp2.1` | 892 | +| `netcoreapp2.2` | 892 | +| `netcoreapp3.0` | 853 | +| `netcoreapp3.1` | 852 | +| `net5.0` | 722 | +| `net6.0` | 623 | +| `net7.0` | 474 | +| `net8.0` | 353 | +| `net9.0` | 236 | +| `net10.0` | 182 | | `net11.0` | 58 | -| `uap10.0` | 1012 | +| `uap10.0` | 1018 | @@ -96,26 +96,26 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 369.0KB | +361.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netstandard2.1 | 8.5KB | 324.5KB | +316.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net461 | 8.5KB | 368.0KB | +359.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net462 | 7.0KB | 373.0KB | +366.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net47 | 7.0KB | 372.5KB | +365.5KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | -| net471 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | -| net472 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net48 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| net481 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp2.0 | 9.0KB | 348.0KB | +339.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.1 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp2.2 | 9.0KB | 328.0KB | +319.0KB | +9.0KB | +6.5KB | +9.0KB | +14.0KB | -| netcoreapp3.0 | 9.5KB | 324.0KB | +314.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | -| netcoreapp3.1 | 9.5KB | 322.5KB | +313.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | -| net5.0 | 9.5KB | 286.5KB | +277.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | -| net6.0 | 10.0KB | 228.5KB | +218.5KB | +9.5KB | +6.5KB | +512bytes | +3.5KB | -| net7.0 | 10.0KB | 193.5KB | +183.5KB | +9.5KB | +6.0KB | +1.0KB | +3.5KB | -| net8.0 | 9.5KB | 164.0KB | +154.5KB | +8.0KB | | +512bytes | +3.0KB | -| net9.0 | 9.5KB | 114.0KB | +104.5KB | +8.5KB | | +512bytes | +3.5KB | -| net10.0 | 10.0KB | 92.0KB | +82.0KB | +8.5KB | | +512bytes | +3.5KB | +| netstandard2.0 | 8.0KB | 370.5KB | +362.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| netstandard2.1 | 8.5KB | 326.0KB | +317.5KB | +8.5KB | +6.5KB | +8.5KB | +13.5KB | +| net461 | 8.5KB | 369.0KB | +360.5KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net462 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net47 | 7.0KB | 374.0KB | +367.0KB | +7.5KB | +6.5KB | +7.5KB | +12.0KB | +| net471 | 8.5KB | 371.5KB | +363.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net472 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net48 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| net481 | 8.5KB | 370.5KB | +362.0KB | +8.5KB | +6.5KB | +9.0KB | +13.0KB | +| netcoreapp2.0 | 9.0KB | 349.0KB | +340.0KB | +8.0KB | +7.0KB | +9.5KB | +14.0KB | +| netcoreapp2.1 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp2.2 | 9.0KB | 329.5KB | +320.5KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.0 | 9.5KB | 325.5KB | +316.0KB | +8.5KB | +6.5KB | +9.0KB | +13.5KB | +| netcoreapp3.1 | 9.5KB | 323.5KB | +314.0KB | +9.0KB | +6.5KB | +9.5KB | +14.0KB | +| net5.0 | 9.5KB | 287.5KB | +278.0KB | +9.0KB | +6.5KB | +9.0KB | +13.5KB | +| net6.0 | 10.0KB | 229.0KB | +219.0KB | +10.0KB | +7.0KB | +512bytes | +3.5KB | +| net7.0 | 10.0KB | 195.5KB | +185.5KB | +9.5KB | +5.5KB | +512bytes | +3.5KB | +| net8.0 | 9.5KB | 165.5KB | +156.0KB | +8.5KB | | +512bytes | +3.5KB | +| net9.0 | 9.5KB | 115.5KB | +106.0KB | +8.5KB | | +512bytes | +3.0KB | +| net10.0 | 10.0KB | 93.5KB | +83.5KB | +8.5KB | | +512bytes | +3.0KB | | net11.0 | 10.0KB | 21.0KB | +11.0KB | +9.0KB | | +512bytes | +3.5KB | @@ -123,26 +123,26 @@ This project uses features from the newest stable SDK and C# language. As such c | | Empty Assembly | With Polyfill | Diff | Ensure | ArgumentExceptions | StringInterpolation | Nullability | |----------------|----------------|---------------|-----------|-----------|--------------------|---------------------|-------------| -| netstandard2.0 | 8.0KB | 541.1KB | +533.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netstandard2.1 | 8.5KB | 470.0KB | +461.5KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net461 | 8.5KB | 541.1KB | +532.6KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net462 | 7.0KB | 546.1KB | +539.1KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net47 | 7.0KB | 545.4KB | +538.4KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | -| net471 | 8.5KB | 543.0KB | +534.5KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | -| net472 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net48 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| net481 | 8.5KB | 540.4KB | +531.9KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp2.0 | 9.0KB | 509.5KB | +500.5KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.1 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp2.2 | 9.0KB | 477.2KB | +468.2KB | +16.7KB | +8.2KB | +13.9KB | +19.4KB | -| netcoreapp3.0 | 9.5KB | 466.5KB | +457.0KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | -| netcoreapp3.1 | 9.5KB | 464.9KB | +455.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | -| net5.0 | 9.5KB | 411.1KB | +401.6KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | -| net6.0 | 10.0KB | 333.2KB | +323.2KB | +17.2KB | +8.2KB | +1.1KB | +4.2KB | -| net7.0 | 10.0KB | 279.9KB | +269.9KB | +17.1KB | +7.4KB | +1.6KB | +4.2KB | -| net8.0 | 9.5KB | 236.5KB | +227.0KB | +15.5KB | +299bytes | +1.1KB | +3.7KB | -| net9.0 | 9.5KB | 164.6KB | +155.1KB | +16.0KB | | +1.1KB | +4.2KB | -| net10.0 | 10.0KB | 133.9KB | +123.9KB | +16.0KB | | +1.1KB | +4.2KB | +| netstandard2.0 | 8.0KB | 543.3KB | +535.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| netstandard2.1 | 8.5KB | 472.2KB | +463.7KB | +16.2KB | +8.2KB | +13.4KB | +18.9KB | +| net461 | 8.5KB | 542.8KB | +534.3KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net462 | 7.0KB | 547.8KB | +540.8KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net47 | 7.0KB | 547.6KB | +540.6KB | +15.2KB | +8.2KB | +12.4KB | +17.4KB | +| net471 | 8.5KB | 544.7KB | +536.2KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net472 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net48 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| net481 | 8.5KB | 542.7KB | +534.2KB | +16.2KB | +8.2KB | +13.9KB | +18.4KB | +| netcoreapp2.0 | 9.0KB | 511.2KB | +502.2KB | +15.7KB | +8.7KB | +14.4KB | +19.4KB | +| netcoreapp2.1 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp2.2 | 9.0KB | 479.4KB | +470.4KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.0 | 9.5KB | 468.7KB | +459.2KB | +16.2KB | +8.2KB | +13.9KB | +18.9KB | +| netcoreapp3.1 | 9.5KB | 466.7KB | +457.2KB | +16.7KB | +8.2KB | +14.4KB | +19.4KB | +| net5.0 | 9.5KB | 412.6KB | +403.1KB | +16.7KB | +8.2KB | +13.9KB | +18.9KB | +| net6.0 | 10.0KB | 334.1KB | +324.1KB | +17.7KB | +8.7KB | +1.1KB | +4.2KB | +| net7.0 | 10.0KB | 282.8KB | +272.8KB | +17.1KB | +6.9KB | +1.1KB | +4.2KB | +| net8.0 | 9.5KB | 239.0KB | +229.5KB | +16.0KB | +299bytes | +1.1KB | +4.2KB | +| net9.0 | 9.5KB | 166.8KB | +157.3KB | +16.0KB | | +1.1KB | +3.7KB | +| net10.0 | 10.0KB | 136.1KB | +126.1KB | +16.0KB | | +1.1KB | +3.7KB | | net11.0 | 10.0KB | 30.9KB | +20.9KB | +16.5KB | | +1.1KB | +4.2KB | @@ -1209,6 +1209,7 @@ The class `Polyfill` includes the following extension methods: #### Int128 + * `Int128 BigMul(Int128, Int128, Int128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int128.bigmul?view=net-11.0) * `Int128 Log10(Int128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int128.log10?view=net-11.0) @@ -1230,6 +1231,7 @@ The class `Polyfill` includes the following extension methods: * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryformat?view=net-11.0#system-int32-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryformat?view=net-11.0#system-int32-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `long BigMul(int, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.bigmul?view=net-11.0) * `int Log10(int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryparse?view=net-11.0#system-int32-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-int32@)) * `bool TryParse(ReadOnlySpan, int)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int32.tryparse?view=net-11.0#system-int32-tryparse(system-readonlyspan((system-char))-system-globalization-numberstyles-system-iformatprovider-system-int32@)) @@ -1244,6 +1246,9 @@ The class `Polyfill` includes the following extension methods: * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryformat?view=net-11.0#system-int64-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryformat?view=net-11.0#system-int64-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `Int128 BigMul(long, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.bigmul?view=net-11.0) + * Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. + * Note: Only available on net7.0 and later, since Int128 does not exist below that. * `long Log10(long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryparse?view=net-11.0#system-int64-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-int64@)) * `bool TryParse(ReadOnlySpan, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.int64.tryparse?view=net-11.0#system-int64-tryparse(system-readonlyspan((system-char))-system-globalization-numberstyles-system-iformatprovider-system-int64@)) @@ -1262,6 +1267,7 @@ The class `Polyfill` includes the following extension methods: #### IntPtr + * `nint BigMul(nint, nint, nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.bigmul?view=net-11.0) * `nint Log10(nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.tryparse?view=net-11.0#system-intptr-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-intptr@)) * `bool TryParse(ReadOnlySpan, nint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.tryparse?view=net-11.0#system-intptr-tryparse(system-readonlyspan((system-byte))-system-intptr@)) @@ -1311,6 +1317,8 @@ The class `Polyfill` includes the following extension methods: #### Math + * `long BigMul(long, long, long)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.bigmul?view=net-11.0#system-math-bigmul(system-int64-system-int64-system-int64@)) + * `ulong BigMul(ulong, ulong, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.bigmul?view=net-11.0#system-math-bigmul(system-uint64-system-uint64-system-uint64@)) * `byte Clamp(byte, byte, byte)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp?view=net-11.0#system-math-clamp(system-byte-system-byte-system-byte)) * `decimal Clamp(decimal, decimal, decimal)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp?view=net-11.0#system-math-clamp(system-decimal-system-decimal-system-decimal)) * `double Clamp(double, double, double)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.math.clamp?view=net-11.0#system-math-clamp(system-double-system-double-system-double)) @@ -2012,6 +2020,7 @@ The class `Polyfill` includes the following extension methods: #### UInt128 + * `UInt128 BigMul(UInt128, UInt128, UInt128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint128.bigmul?view=net-11.0) * `UInt128 Log10(UInt128)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint128.log10?view=net-11.0) @@ -2033,6 +2042,8 @@ The class `Polyfill` includes the following extension methods: * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryformat?view=net-11.0#system-uint32-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryformat?view=net-11.0#system-uint32-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `ulong BigMul(uint, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.bigmul?view=net-11.0) + * Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. * `uint Log10(uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryparse?view=net-11.0#system-uint32-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-uint32@)) * `bool TryParse(ReadOnlySpan, NumberStyles, IFormatProvider?, uint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint32.tryparse?view=net-11.0#system-uint32-tryparse(system-readonlyspan((system-byte))-system-globalization-numberstyles-system-iformatprovider-system-uint32@)) @@ -2047,6 +2058,9 @@ The class `Polyfill` includes the following extension methods: * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryformat?view=net-11.0#system-uint64-tryformat(system-span((system-byte))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) * `bool TryFormat(Span, int, ReadOnlySpan, IFormatProvider?)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryformat?view=net-11.0#system-uint64-tryformat(system-span((system-char))-system-int32@-system-readonlyspan((system-char))-system-iformatprovider)) + * `UInt128 BigMul(ulong, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.bigmul?view=net-11.0) + * Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. + * Note: Only available on net7.0 and later, since UInt128 does not exist below that. * `ulong Log10(ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryparse?view=net-11.0#system-uint64-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-uint64@)) * `bool TryParse(ReadOnlySpan, NumberStyles, IFormatProvider?, ulong)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uint64.tryparse?view=net-11.0#system-uint64-tryparse(system-readonlyspan((system-byte))-system-globalization-numberstyles-system-iformatprovider-system-uint64@)) @@ -2059,6 +2073,7 @@ The class `Polyfill` includes the following extension methods: #### UIntPtr + * `nuint BigMul(nuint, nuint, nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.bigmul?view=net-11.0) * `nuint Log10(nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.log10?view=net-11.0) * `bool TryParse(ReadOnlySpan, IFormatProvider?, nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.tryparse?view=net-11.0#system-uintptr-tryparse(system-readonlyspan((system-byte))-system-iformatprovider-system-uintptr@)) * `bool TryParse(ReadOnlySpan, nuint)` [reference](https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.tryparse?view=net-11.0#system-uintptr-tryparse(system-readonlyspan((system-byte))-system-uintptr@)) @@ -2474,7 +2489,7 @@ void ObjectDisposedExceptionExample(bool isDisposed) ObjectDisposedException.ThrowIf(isDisposed, typeof(Consume)); } ``` -snippet source | anchor +snippet source | anchor @@ -2493,7 +2508,7 @@ void EnsureExample(Order order, Customer customer, string customerId, string ema this.quantity = Ensure.NotNegativeOrZero(quantity); } ``` -snippet source | anchor +snippet source | anchor diff --git a/src/Consume/Consume.cs b/src/Consume/Consume.cs index c32917e2..9a29574e 100644 --- a/src/Consume/Consume.cs +++ b/src/Consume/Consume.cs @@ -611,6 +611,22 @@ void Log10_Methods() #endif } + void BigMul_Methods() + { + long intBigMul = int.BigMul(2, 3); + ulong uintBigMul = uint.BigMul(2, 3); + long mathHigh = Math.BigMul(2L, 3L, out var mathLow); + ulong mathUnsignedHigh = Math.BigMul(2UL, 3UL, out var mathUnsignedLow); + nint nintBigMul = nint.BigMul(2, 3, out var nintLower); + nuint nuintBigMul = nuint.BigMul(2, 3, out var nuintLower); +#if NET7_0_OR_GREATER + Int128 longBigMul = long.BigMul(2, 3); + UInt128 ulongBigMul = ulong.BigMul(2, 3); + Int128 int128BigMul = Int128.BigMul(2, 3, out var int128Lower); + UInt128 uint128BigMul = UInt128.BigMul(2, 3, out var uint128Lower); +#endif + } + void Byte_Methods() { byte.TryParse(s: "1", provider: null, result: out _); diff --git a/src/Polyfill/MathPolyfill.cs b/src/Polyfill/MathPolyfill.cs index fc019c25..f54a85b3 100644 --- a/src/Polyfill/MathPolyfill.cs +++ b/src/Polyfill/MathPolyfill.cs @@ -321,5 +321,60 @@ public static nint Clamp(nint value, nint min, nint max) } #endif + +#if !NET5_0_OR_GREATER + + /// + /// Produces the full product of two 64-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.math.bigmul?view=net-11.0#system-math-bigmul(system-int64-system-int64-system-int64@) + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.math.bigmul?view=net-11.0#system-math-bigmul(system-uint64-system-uint64-system-uint64@) + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + +#endif + } + +#if !NET5_0_OR_GREATER + + // Splits each operand into 32-bit limbs and accumulates the four partial products, + // which is what the runtime falls back to when there is no widening multiply intrinsic. + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + // reinterpreting the unsigned product as signed overshoots by one operand for + // each negative operand, so subtract the other operand for each sign bit set + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } + } + +#endif } diff --git a/src/Polyfill/Numbers/Int128Polyfill.cs b/src/Polyfill/Numbers/Int128Polyfill.cs index 71368ca2..9dd3bfdb 100644 --- a/src/Polyfill/Numbers/Int128Polyfill.cs +++ b/src/Polyfill/Numbers/Int128Polyfill.cs @@ -21,6 +21,22 @@ public static Int128 Log10(Int128 value) return Log10Core((UInt128) value); } + + /// + /// Produces the full product of two 128-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.int128.bigmul?view=net-11.0 + public static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) + { + unchecked + { + var upper = BigMulCore((UInt128) left, (UInt128) right, out var unsignedLower); + lower = (Int128) unsignedLower; + // reinterpreting the unsigned product as signed overshoots by one operand for + // each negative operand, so subtract the other operand for each sign bit set + return (Int128) upper - ((left >> 127) & right) - ((right >> 127) & left); + } + } } static Int128 Log10Core(UInt128 value) diff --git a/src/Polyfill/Numbers/Int32Polyfill.cs b/src/Polyfill/Numbers/Int32Polyfill.cs index cce948ae..fef4ef42 100644 --- a/src/Polyfill/Numbers/Int32Polyfill.cs +++ b/src/Polyfill/Numbers/Int32Polyfill.cs @@ -80,6 +80,17 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out #endif #endif +#endif + +#if !NET9_0_OR_GREATER + + /// + /// Produces the full product of two 32-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.int32.bigmul?view=net-11.0 + public static long BigMul(int left, int right) => + (long) left * right; + #endif /// diff --git a/src/Polyfill/Numbers/Int64Polyfill.cs b/src/Polyfill/Numbers/Int64Polyfill.cs index 5a51b485..c4f4327c 100644 --- a/src/Polyfill/Numbers/Int64Polyfill.cs +++ b/src/Polyfill/Numbers/Int64Polyfill.cs @@ -80,6 +80,19 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out #endif #endif +#endif + +#if NET7_0_OR_GREATER && !NET9_0_OR_GREATER + + /// + /// Produces the full product of two 64-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.int64.bigmul?view=net-11.0 + //Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. + //Note: Only available on net7.0 and later, since Int128 does not exist below that. + public static Int128 BigMul(long left, long right) => + (Int128) left * right; + #endif /// diff --git a/src/Polyfill/Numbers/IntPtrPolyfill.cs b/src/Polyfill/Numbers/IntPtrPolyfill.cs index e93262ef..62ccf7b0 100644 --- a/src/Polyfill/Numbers/IntPtrPolyfill.cs +++ b/src/Polyfill/Numbers/IntPtrPolyfill.cs @@ -131,6 +131,27 @@ public static nint Log10(nint value) return (nint) Log10Core((ulong) value); } + + /// + /// Produces the full product of two native-sized signed numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.intptr.bigmul?view=net-11.0 + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } #endif diff --git a/src/Polyfill/Numbers/UInt128Polyfill.cs b/src/Polyfill/Numbers/UInt128Polyfill.cs index ef8bb84b..fc128c6e 100644 --- a/src/Polyfill/Numbers/UInt128Polyfill.cs +++ b/src/Polyfill/Numbers/UInt128Polyfill.cs @@ -14,6 +14,34 @@ static partial class Polyfill //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uint128.log10?view=net-11.0 public static UInt128 Log10(UInt128 value) => (UInt128) Log10Core(value); + + /// + /// Produces the full product of two unsigned 128-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uint128.bigmul?view=net-11.0 + public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) => + BigMulCore(left, right, out lower); + } + + // Splits each operand into 64-bit limbs and accumulates the four partial products. + // Every partial product of two 64-bit limbs is exact in 128 bits, so no wider + // intermediate type is needed. + static UInt128 BigMulCore(UInt128 left, UInt128 right, out UInt128 lower) + { + unchecked + { + UInt128 leftLower = (ulong) left; + UInt128 leftUpper = (ulong) (left >> 64); + UInt128 rightLower = (ulong) right; + UInt128 rightUpper = (ulong) (right >> 64); + + var lowerLower = leftLower * rightLower; + var middle = leftUpper * rightLower + (lowerLower >> 64); + var middleLower = leftLower * rightUpper + (ulong) middle; + + lower = (middleLower << 64) | (ulong) lowerLower; + return leftUpper * rightUpper + (middle >> 64) + (middleLower >> 64); + } } } diff --git a/src/Polyfill/Numbers/UInt32Polyfill.cs b/src/Polyfill/Numbers/UInt32Polyfill.cs index 72af6885..61d33b18 100644 --- a/src/Polyfill/Numbers/UInt32Polyfill.cs +++ b/src/Polyfill/Numbers/UInt32Polyfill.cs @@ -80,6 +80,18 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out #endif #endif +#endif + +#if !NET9_0_OR_GREATER + + /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uint32.bigmul?view=net-11.0 + //Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + #endif /// diff --git a/src/Polyfill/Numbers/UInt64Polyfill.cs b/src/Polyfill/Numbers/UInt64Polyfill.cs index 8cb61b59..096dd621 100644 --- a/src/Polyfill/Numbers/UInt64Polyfill.cs +++ b/src/Polyfill/Numbers/UInt64Polyfill.cs @@ -80,6 +80,19 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out #endif #endif +#endif + +#if NET7_0_OR_GREATER && !NET9_0_OR_GREATER + + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uint64.bigmul?view=net-11.0 + //Note: The identical Math.BigMul overload is not polyfilled, since C# emits both static extension members onto the same class and they would collide. + //Note: Only available on net7.0 and later, since UInt128 does not exist below that. + public static UInt128 BigMul(ulong left, ulong right) => + (UInt128) left * right; + #endif /// diff --git a/src/Polyfill/Numbers/UIntPtrPolyfill.cs b/src/Polyfill/Numbers/UIntPtrPolyfill.cs index 26849c60..11b8c373 100644 --- a/src/Polyfill/Numbers/UIntPtrPolyfill.cs +++ b/src/Polyfill/Numbers/UIntPtrPolyfill.cs @@ -124,6 +124,27 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.log10?view=net-11.0 public static nuint Log10(nuint value) => (nuint) Log10Core(value); + + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + //Link: https://learn.microsoft.com/en-us/dotnet/api/system.uintptr.bigmul?view=net-11.0 + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } #endif diff --git a/src/Split/net10.0/Numbers/Int128Polyfill.cs b/src/Split/net10.0/Numbers/Int128Polyfill.cs index b18f70a1..79ab62a8 100644 --- a/src/Split/net10.0/Numbers/Int128Polyfill.cs +++ b/src/Split/net10.0/Numbers/Int128Polyfill.cs @@ -17,6 +17,18 @@ public static Int128 Log10(Int128 value) } return Log10Core((UInt128) value); } + /// + /// Produces the full product of two 128-bit numbers. + /// + public static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) + { + unchecked + { + var upper = BigMulCore((UInt128) left, (UInt128) right, out var unsignedLower); + lower = (Int128) unsignedLower; + return (Int128) upper - ((left >> 127) & right) - ((right >> 127) & left); + } + } } static Int128 Log10Core(UInt128 value) { diff --git a/src/Split/net10.0/Numbers/IntPtrPolyfill.cs b/src/Split/net10.0/Numbers/IntPtrPolyfill.cs index 57fe93fc..32959837 100644 --- a/src/Split/net10.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net10.0/Numbers/IntPtrPolyfill.cs @@ -19,5 +19,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net10.0/Numbers/UInt128Polyfill.cs b/src/Split/net10.0/Numbers/UInt128Polyfill.cs index c3c33950..3589ed61 100644 --- a/src/Split/net10.0/Numbers/UInt128Polyfill.cs +++ b/src/Split/net10.0/Numbers/UInt128Polyfill.cs @@ -11,5 +11,25 @@ static partial class Polyfill /// public static UInt128 Log10(UInt128 value) => (UInt128) Log10Core(value); + /// + /// Produces the full product of two unsigned 128-bit numbers. + /// + public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) => + BigMulCore(left, right, out lower); + } + static UInt128 BigMulCore(UInt128 left, UInt128 right, out UInt128 lower) + { + unchecked + { + UInt128 leftLower = (ulong) left; + UInt128 leftUpper = (ulong) (left >> 64); + UInt128 rightLower = (ulong) right; + UInt128 rightUpper = (ulong) (right >> 64); + var lowerLower = leftLower * rightLower; + var middle = leftUpper * rightLower + (lowerLower >> 64); + var middleLower = leftLower * rightUpper + (ulong) middle; + lower = (middleLower << 64) | (ulong) lowerLower; + return leftUpper * rightUpper + (middle >> 64) + (middleLower >> 64); + } } } diff --git a/src/Split/net10.0/Numbers/UIntPtrPolyfill.cs b/src/Split/net10.0/Numbers/UIntPtrPolyfill.cs index 8eb6c210..a134f9c5 100644 --- a/src/Split/net10.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net10.0/Numbers/UIntPtrPolyfill.cs @@ -13,5 +13,23 @@ static partial class Polyfill /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net461/MathPolyfill.cs b/src/Split/net461/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/net461/MathPolyfill.cs +++ b/src/Split/net461/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/net461/Numbers/Int32Polyfill.cs b/src/Split/net461/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/net461/Numbers/Int32Polyfill.cs +++ b/src/Split/net461/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net461/Numbers/IntPtrPolyfill.cs b/src/Split/net461/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/net461/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net461/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net461/Numbers/UInt32Polyfill.cs b/src/Split/net461/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/net461/Numbers/UInt32Polyfill.cs +++ b/src/Split/net461/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net461/Numbers/UIntPtrPolyfill.cs b/src/Split/net461/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/net461/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net461/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net462/MathPolyfill.cs b/src/Split/net462/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/net462/MathPolyfill.cs +++ b/src/Split/net462/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/net462/Numbers/Int32Polyfill.cs b/src/Split/net462/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/net462/Numbers/Int32Polyfill.cs +++ b/src/Split/net462/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net462/Numbers/IntPtrPolyfill.cs b/src/Split/net462/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/net462/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net462/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net462/Numbers/UInt32Polyfill.cs b/src/Split/net462/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/net462/Numbers/UInt32Polyfill.cs +++ b/src/Split/net462/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net462/Numbers/UIntPtrPolyfill.cs b/src/Split/net462/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/net462/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net462/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net47/MathPolyfill.cs b/src/Split/net47/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/net47/MathPolyfill.cs +++ b/src/Split/net47/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/net47/Numbers/Int32Polyfill.cs b/src/Split/net47/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/net47/Numbers/Int32Polyfill.cs +++ b/src/Split/net47/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net47/Numbers/IntPtrPolyfill.cs b/src/Split/net47/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/net47/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net47/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net47/Numbers/UInt32Polyfill.cs b/src/Split/net47/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/net47/Numbers/UInt32Polyfill.cs +++ b/src/Split/net47/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net47/Numbers/UIntPtrPolyfill.cs b/src/Split/net47/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/net47/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net47/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net471/MathPolyfill.cs b/src/Split/net471/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/net471/MathPolyfill.cs +++ b/src/Split/net471/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/net471/Numbers/Int32Polyfill.cs b/src/Split/net471/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/net471/Numbers/Int32Polyfill.cs +++ b/src/Split/net471/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net471/Numbers/IntPtrPolyfill.cs b/src/Split/net471/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/net471/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net471/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net471/Numbers/UInt32Polyfill.cs b/src/Split/net471/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/net471/Numbers/UInt32Polyfill.cs +++ b/src/Split/net471/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net471/Numbers/UIntPtrPolyfill.cs b/src/Split/net471/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/net471/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net471/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net472/MathPolyfill.cs b/src/Split/net472/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/net472/MathPolyfill.cs +++ b/src/Split/net472/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/net472/Numbers/Int32Polyfill.cs b/src/Split/net472/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/net472/Numbers/Int32Polyfill.cs +++ b/src/Split/net472/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net472/Numbers/IntPtrPolyfill.cs b/src/Split/net472/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/net472/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net472/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net472/Numbers/UInt32Polyfill.cs b/src/Split/net472/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/net472/Numbers/UInt32Polyfill.cs +++ b/src/Split/net472/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net472/Numbers/UIntPtrPolyfill.cs b/src/Split/net472/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/net472/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net472/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net48/MathPolyfill.cs b/src/Split/net48/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/net48/MathPolyfill.cs +++ b/src/Split/net48/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/net48/Numbers/Int32Polyfill.cs b/src/Split/net48/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/net48/Numbers/Int32Polyfill.cs +++ b/src/Split/net48/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net48/Numbers/IntPtrPolyfill.cs b/src/Split/net48/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/net48/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net48/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net48/Numbers/UInt32Polyfill.cs b/src/Split/net48/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/net48/Numbers/UInt32Polyfill.cs +++ b/src/Split/net48/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net48/Numbers/UIntPtrPolyfill.cs b/src/Split/net48/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/net48/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net48/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net481/MathPolyfill.cs b/src/Split/net481/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/net481/MathPolyfill.cs +++ b/src/Split/net481/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/net481/Numbers/Int32Polyfill.cs b/src/Split/net481/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/net481/Numbers/Int32Polyfill.cs +++ b/src/Split/net481/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net481/Numbers/IntPtrPolyfill.cs b/src/Split/net481/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/net481/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net481/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net481/Numbers/UInt32Polyfill.cs b/src/Split/net481/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/net481/Numbers/UInt32Polyfill.cs +++ b/src/Split/net481/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net481/Numbers/UIntPtrPolyfill.cs b/src/Split/net481/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/net481/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net481/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net5.0/Numbers/Int32Polyfill.cs b/src/Split/net5.0/Numbers/Int32Polyfill.cs index e455cd66..12bf78cd 100644 --- a/src/Split/net5.0/Numbers/Int32Polyfill.cs +++ b/src/Split/net5.0/Numbers/Int32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net5.0/Numbers/IntPtrPolyfill.cs b/src/Split/net5.0/Numbers/IntPtrPolyfill.cs index 04d77ea3..e0edad6d 100644 --- a/src/Split/net5.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net5.0/Numbers/IntPtrPolyfill.cs @@ -56,5 +56,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net5.0/Numbers/UInt32Polyfill.cs b/src/Split/net5.0/Numbers/UInt32Polyfill.cs index 9592af7a..9c3dfcf0 100644 --- a/src/Split/net5.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/net5.0/Numbers/UInt32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net5.0/Numbers/UIntPtrPolyfill.cs b/src/Split/net5.0/Numbers/UIntPtrPolyfill.cs index 3198624c..c94d99c1 100644 --- a/src/Split/net5.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net5.0/Numbers/UIntPtrPolyfill.cs @@ -50,5 +50,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net6.0/Numbers/Int32Polyfill.cs b/src/Split/net6.0/Numbers/Int32Polyfill.cs index e455cd66..12bf78cd 100644 --- a/src/Split/net6.0/Numbers/Int32Polyfill.cs +++ b/src/Split/net6.0/Numbers/Int32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net6.0/Numbers/IntPtrPolyfill.cs b/src/Split/net6.0/Numbers/IntPtrPolyfill.cs index 04d77ea3..e0edad6d 100644 --- a/src/Split/net6.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net6.0/Numbers/IntPtrPolyfill.cs @@ -56,5 +56,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net6.0/Numbers/UInt32Polyfill.cs b/src/Split/net6.0/Numbers/UInt32Polyfill.cs index 9592af7a..9c3dfcf0 100644 --- a/src/Split/net6.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/net6.0/Numbers/UInt32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net6.0/Numbers/UIntPtrPolyfill.cs b/src/Split/net6.0/Numbers/UIntPtrPolyfill.cs index 3198624c..c94d99c1 100644 --- a/src/Split/net6.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net6.0/Numbers/UIntPtrPolyfill.cs @@ -50,5 +50,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net7.0/Numbers/Int128Polyfill.cs b/src/Split/net7.0/Numbers/Int128Polyfill.cs index b18f70a1..79ab62a8 100644 --- a/src/Split/net7.0/Numbers/Int128Polyfill.cs +++ b/src/Split/net7.0/Numbers/Int128Polyfill.cs @@ -17,6 +17,18 @@ public static Int128 Log10(Int128 value) } return Log10Core((UInt128) value); } + /// + /// Produces the full product of two 128-bit numbers. + /// + public static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) + { + unchecked + { + var upper = BigMulCore((UInt128) left, (UInt128) right, out var unsignedLower); + lower = (Int128) unsignedLower; + return (Int128) upper - ((left >> 127) & right) - ((right >> 127) & left); + } + } } static Int128 Log10Core(UInt128 value) { diff --git a/src/Split/net7.0/Numbers/Int32Polyfill.cs b/src/Split/net7.0/Numbers/Int32Polyfill.cs index b61ed0f2..2ef54e86 100644 --- a/src/Split/net7.0/Numbers/Int32Polyfill.cs +++ b/src/Split/net7.0/Numbers/Int32Polyfill.cs @@ -26,6 +26,11 @@ public static bool TryParse(ReadOnlySpan utf8Text, out int result) => int.TryParse(Encoding.UTF8.GetString(utf8Text), NumberStyles.Integer, null, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net7.0/Numbers/Int64Polyfill.cs b/src/Split/net7.0/Numbers/Int64Polyfill.cs index e1138c11..e3b9c59e 100644 --- a/src/Split/net7.0/Numbers/Int64Polyfill.cs +++ b/src/Split/net7.0/Numbers/Int64Polyfill.cs @@ -26,6 +26,11 @@ public static bool TryParse(ReadOnlySpan utf8Text, out long result) => long.TryParse(Encoding.UTF8.GetString(utf8Text), NumberStyles.Integer, null, out result); #endif /// + /// Produces the full product of two 64-bit numbers. + /// + public static Int128 BigMul(long left, long right) => + (Int128) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static long Log10(long value) diff --git a/src/Split/net7.0/Numbers/IntPtrPolyfill.cs b/src/Split/net7.0/Numbers/IntPtrPolyfill.cs index bdf79435..c6dfcc90 100644 --- a/src/Split/net7.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net7.0/Numbers/IntPtrPolyfill.cs @@ -36,5 +36,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net7.0/Numbers/UInt128Polyfill.cs b/src/Split/net7.0/Numbers/UInt128Polyfill.cs index c3c33950..3589ed61 100644 --- a/src/Split/net7.0/Numbers/UInt128Polyfill.cs +++ b/src/Split/net7.0/Numbers/UInt128Polyfill.cs @@ -11,5 +11,25 @@ static partial class Polyfill /// public static UInt128 Log10(UInt128 value) => (UInt128) Log10Core(value); + /// + /// Produces the full product of two unsigned 128-bit numbers. + /// + public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) => + BigMulCore(left, right, out lower); + } + static UInt128 BigMulCore(UInt128 left, UInt128 right, out UInt128 lower) + { + unchecked + { + UInt128 leftLower = (ulong) left; + UInt128 leftUpper = (ulong) (left >> 64); + UInt128 rightLower = (ulong) right; + UInt128 rightUpper = (ulong) (right >> 64); + var lowerLower = leftLower * rightLower; + var middle = leftUpper * rightLower + (lowerLower >> 64); + var middleLower = leftLower * rightUpper + (ulong) middle; + lower = (middleLower << 64) | (ulong) lowerLower; + return leftUpper * rightUpper + (middle >> 64) + (middleLower >> 64); + } } } diff --git a/src/Split/net7.0/Numbers/UInt32Polyfill.cs b/src/Split/net7.0/Numbers/UInt32Polyfill.cs index 24bdc115..0d116984 100644 --- a/src/Split/net7.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/net7.0/Numbers/UInt32Polyfill.cs @@ -26,6 +26,11 @@ public static bool TryParse(ReadOnlySpan utf8Text, out uint result) => uint.TryParse(Encoding.UTF8.GetString(utf8Text), NumberStyles.Integer, null, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net7.0/Numbers/UInt64Polyfill.cs b/src/Split/net7.0/Numbers/UInt64Polyfill.cs index 688e37c3..78c0dff2 100644 --- a/src/Split/net7.0/Numbers/UInt64Polyfill.cs +++ b/src/Split/net7.0/Numbers/UInt64Polyfill.cs @@ -26,6 +26,11 @@ public static bool TryParse(ReadOnlySpan utf8Text, out ulong result) => ulong.TryParse(Encoding.UTF8.GetString(utf8Text), NumberStyles.Integer, null, out result); #endif /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static UInt128 BigMul(ulong left, ulong right) => + (UInt128) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static ulong Log10(ulong value) => diff --git a/src/Split/net7.0/Numbers/UIntPtrPolyfill.cs b/src/Split/net7.0/Numbers/UIntPtrPolyfill.cs index 894a58eb..fc544005 100644 --- a/src/Split/net7.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net7.0/Numbers/UIntPtrPolyfill.cs @@ -30,5 +30,23 @@ public static bool TryParse(ReadOnlySpan utf8Text, out nuint result) => /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net8.0/Numbers/Int128Polyfill.cs b/src/Split/net8.0/Numbers/Int128Polyfill.cs index b18f70a1..79ab62a8 100644 --- a/src/Split/net8.0/Numbers/Int128Polyfill.cs +++ b/src/Split/net8.0/Numbers/Int128Polyfill.cs @@ -17,6 +17,18 @@ public static Int128 Log10(Int128 value) } return Log10Core((UInt128) value); } + /// + /// Produces the full product of two 128-bit numbers. + /// + public static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) + { + unchecked + { + var upper = BigMulCore((UInt128) left, (UInt128) right, out var unsignedLower); + lower = (Int128) unsignedLower; + return (Int128) upper - ((left >> 127) & right) - ((right >> 127) & left); + } + } } static Int128 Log10Core(UInt128 value) { diff --git a/src/Split/net8.0/Numbers/Int32Polyfill.cs b/src/Split/net8.0/Numbers/Int32Polyfill.cs index 7409a64d..5b048216 100644 --- a/src/Split/net8.0/Numbers/Int32Polyfill.cs +++ b/src/Split/net8.0/Numbers/Int32Polyfill.cs @@ -9,6 +9,11 @@ static partial class Polyfill extension(int) { /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/net8.0/Numbers/Int64Polyfill.cs b/src/Split/net8.0/Numbers/Int64Polyfill.cs index e602de75..4901bae4 100644 --- a/src/Split/net8.0/Numbers/Int64Polyfill.cs +++ b/src/Split/net8.0/Numbers/Int64Polyfill.cs @@ -9,6 +9,11 @@ static partial class Polyfill extension(long) { /// + /// Produces the full product of two 64-bit numbers. + /// + public static Int128 BigMul(long left, long right) => + (Int128) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static long Log10(long value) diff --git a/src/Split/net8.0/Numbers/IntPtrPolyfill.cs b/src/Split/net8.0/Numbers/IntPtrPolyfill.cs index 57fe93fc..32959837 100644 --- a/src/Split/net8.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net8.0/Numbers/IntPtrPolyfill.cs @@ -19,5 +19,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net8.0/Numbers/UInt128Polyfill.cs b/src/Split/net8.0/Numbers/UInt128Polyfill.cs index c3c33950..3589ed61 100644 --- a/src/Split/net8.0/Numbers/UInt128Polyfill.cs +++ b/src/Split/net8.0/Numbers/UInt128Polyfill.cs @@ -11,5 +11,25 @@ static partial class Polyfill /// public static UInt128 Log10(UInt128 value) => (UInt128) Log10Core(value); + /// + /// Produces the full product of two unsigned 128-bit numbers. + /// + public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) => + BigMulCore(left, right, out lower); + } + static UInt128 BigMulCore(UInt128 left, UInt128 right, out UInt128 lower) + { + unchecked + { + UInt128 leftLower = (ulong) left; + UInt128 leftUpper = (ulong) (left >> 64); + UInt128 rightLower = (ulong) right; + UInt128 rightUpper = (ulong) (right >> 64); + var lowerLower = leftLower * rightLower; + var middle = leftUpper * rightLower + (lowerLower >> 64); + var middleLower = leftLower * rightUpper + (ulong) middle; + lower = (middleLower << 64) | (ulong) lowerLower; + return leftUpper * rightUpper + (middle >> 64) + (middleLower >> 64); + } } } diff --git a/src/Split/net8.0/Numbers/UInt32Polyfill.cs b/src/Split/net8.0/Numbers/UInt32Polyfill.cs index 29aab694..b5ff98c8 100644 --- a/src/Split/net8.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/net8.0/Numbers/UInt32Polyfill.cs @@ -9,6 +9,11 @@ static partial class Polyfill extension(uint) { /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/net8.0/Numbers/UInt64Polyfill.cs b/src/Split/net8.0/Numbers/UInt64Polyfill.cs index 4ac98b2c..b488acdd 100644 --- a/src/Split/net8.0/Numbers/UInt64Polyfill.cs +++ b/src/Split/net8.0/Numbers/UInt64Polyfill.cs @@ -9,6 +9,11 @@ static partial class Polyfill extension(ulong) { /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static UInt128 BigMul(ulong left, ulong right) => + (UInt128) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static ulong Log10(ulong value) => diff --git a/src/Split/net8.0/Numbers/UIntPtrPolyfill.cs b/src/Split/net8.0/Numbers/UIntPtrPolyfill.cs index 8eb6c210..a134f9c5 100644 --- a/src/Split/net8.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net8.0/Numbers/UIntPtrPolyfill.cs @@ -13,5 +13,23 @@ static partial class Polyfill /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/net9.0/Numbers/Int128Polyfill.cs b/src/Split/net9.0/Numbers/Int128Polyfill.cs index b18f70a1..79ab62a8 100644 --- a/src/Split/net9.0/Numbers/Int128Polyfill.cs +++ b/src/Split/net9.0/Numbers/Int128Polyfill.cs @@ -17,6 +17,18 @@ public static Int128 Log10(Int128 value) } return Log10Core((UInt128) value); } + /// + /// Produces the full product of two 128-bit numbers. + /// + public static Int128 BigMul(Int128 left, Int128 right, out Int128 lower) + { + unchecked + { + var upper = BigMulCore((UInt128) left, (UInt128) right, out var unsignedLower); + lower = (Int128) unsignedLower; + return (Int128) upper - ((left >> 127) & right) - ((right >> 127) & left); + } + } } static Int128 Log10Core(UInt128 value) { diff --git a/src/Split/net9.0/Numbers/IntPtrPolyfill.cs b/src/Split/net9.0/Numbers/IntPtrPolyfill.cs index 57fe93fc..32959837 100644 --- a/src/Split/net9.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/net9.0/Numbers/IntPtrPolyfill.cs @@ -19,5 +19,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/net9.0/Numbers/UInt128Polyfill.cs b/src/Split/net9.0/Numbers/UInt128Polyfill.cs index c3c33950..3589ed61 100644 --- a/src/Split/net9.0/Numbers/UInt128Polyfill.cs +++ b/src/Split/net9.0/Numbers/UInt128Polyfill.cs @@ -11,5 +11,25 @@ static partial class Polyfill /// public static UInt128 Log10(UInt128 value) => (UInt128) Log10Core(value); + /// + /// Produces the full product of two unsigned 128-bit numbers. + /// + public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower) => + BigMulCore(left, right, out lower); + } + static UInt128 BigMulCore(UInt128 left, UInt128 right, out UInt128 lower) + { + unchecked + { + UInt128 leftLower = (ulong) left; + UInt128 leftUpper = (ulong) (left >> 64); + UInt128 rightLower = (ulong) right; + UInt128 rightUpper = (ulong) (right >> 64); + var lowerLower = leftLower * rightLower; + var middle = leftUpper * rightLower + (lowerLower >> 64); + var middleLower = leftLower * rightUpper + (ulong) middle; + lower = (middleLower << 64) | (ulong) lowerLower; + return leftUpper * rightUpper + (middle >> 64) + (middleLower >> 64); + } } } diff --git a/src/Split/net9.0/Numbers/UIntPtrPolyfill.cs b/src/Split/net9.0/Numbers/UIntPtrPolyfill.cs index 8eb6c210..a134f9c5 100644 --- a/src/Split/net9.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/net9.0/Numbers/UIntPtrPolyfill.cs @@ -13,5 +13,23 @@ static partial class Polyfill /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/netcoreapp2.0/MathPolyfill.cs b/src/Split/netcoreapp2.0/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/netcoreapp2.0/MathPolyfill.cs +++ b/src/Split/netcoreapp2.0/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/netcoreapp2.0/Numbers/Int32Polyfill.cs b/src/Split/netcoreapp2.0/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/netcoreapp2.0/Numbers/Int32Polyfill.cs +++ b/src/Split/netcoreapp2.0/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/netcoreapp2.0/Numbers/IntPtrPolyfill.cs b/src/Split/netcoreapp2.0/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/netcoreapp2.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/netcoreapp2.0/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/netcoreapp2.0/Numbers/UInt32Polyfill.cs b/src/Split/netcoreapp2.0/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/netcoreapp2.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/netcoreapp2.0/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/netcoreapp2.0/Numbers/UIntPtrPolyfill.cs b/src/Split/netcoreapp2.0/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/netcoreapp2.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/netcoreapp2.0/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/netcoreapp2.1/MathPolyfill.cs b/src/Split/netcoreapp2.1/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/netcoreapp2.1/MathPolyfill.cs +++ b/src/Split/netcoreapp2.1/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/netcoreapp2.1/Numbers/Int32Polyfill.cs b/src/Split/netcoreapp2.1/Numbers/Int32Polyfill.cs index e455cd66..12bf78cd 100644 --- a/src/Split/netcoreapp2.1/Numbers/Int32Polyfill.cs +++ b/src/Split/netcoreapp2.1/Numbers/Int32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/netcoreapp2.1/Numbers/IntPtrPolyfill.cs b/src/Split/netcoreapp2.1/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/netcoreapp2.1/Numbers/IntPtrPolyfill.cs +++ b/src/Split/netcoreapp2.1/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/netcoreapp2.1/Numbers/UInt32Polyfill.cs b/src/Split/netcoreapp2.1/Numbers/UInt32Polyfill.cs index 9592af7a..9c3dfcf0 100644 --- a/src/Split/netcoreapp2.1/Numbers/UInt32Polyfill.cs +++ b/src/Split/netcoreapp2.1/Numbers/UInt32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/netcoreapp2.1/Numbers/UIntPtrPolyfill.cs b/src/Split/netcoreapp2.1/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/netcoreapp2.1/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/netcoreapp2.1/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/netcoreapp2.2/MathPolyfill.cs b/src/Split/netcoreapp2.2/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/netcoreapp2.2/MathPolyfill.cs +++ b/src/Split/netcoreapp2.2/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/netcoreapp2.2/Numbers/Int32Polyfill.cs b/src/Split/netcoreapp2.2/Numbers/Int32Polyfill.cs index e455cd66..12bf78cd 100644 --- a/src/Split/netcoreapp2.2/Numbers/Int32Polyfill.cs +++ b/src/Split/netcoreapp2.2/Numbers/Int32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/netcoreapp2.2/Numbers/IntPtrPolyfill.cs b/src/Split/netcoreapp2.2/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/netcoreapp2.2/Numbers/IntPtrPolyfill.cs +++ b/src/Split/netcoreapp2.2/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/netcoreapp2.2/Numbers/UInt32Polyfill.cs b/src/Split/netcoreapp2.2/Numbers/UInt32Polyfill.cs index 9592af7a..9c3dfcf0 100644 --- a/src/Split/netcoreapp2.2/Numbers/UInt32Polyfill.cs +++ b/src/Split/netcoreapp2.2/Numbers/UInt32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/netcoreapp2.2/Numbers/UIntPtrPolyfill.cs b/src/Split/netcoreapp2.2/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/netcoreapp2.2/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/netcoreapp2.2/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/netcoreapp3.0/MathPolyfill.cs b/src/Split/netcoreapp3.0/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/netcoreapp3.0/MathPolyfill.cs +++ b/src/Split/netcoreapp3.0/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/netcoreapp3.0/Numbers/Int32Polyfill.cs b/src/Split/netcoreapp3.0/Numbers/Int32Polyfill.cs index e455cd66..12bf78cd 100644 --- a/src/Split/netcoreapp3.0/Numbers/Int32Polyfill.cs +++ b/src/Split/netcoreapp3.0/Numbers/Int32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/netcoreapp3.0/Numbers/IntPtrPolyfill.cs b/src/Split/netcoreapp3.0/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/netcoreapp3.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/netcoreapp3.0/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/netcoreapp3.0/Numbers/UInt32Polyfill.cs b/src/Split/netcoreapp3.0/Numbers/UInt32Polyfill.cs index 9592af7a..9c3dfcf0 100644 --- a/src/Split/netcoreapp3.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/netcoreapp3.0/Numbers/UInt32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/netcoreapp3.0/Numbers/UIntPtrPolyfill.cs b/src/Split/netcoreapp3.0/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/netcoreapp3.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/netcoreapp3.0/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/netcoreapp3.1/MathPolyfill.cs b/src/Split/netcoreapp3.1/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/netcoreapp3.1/MathPolyfill.cs +++ b/src/Split/netcoreapp3.1/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/netcoreapp3.1/Numbers/Int32Polyfill.cs b/src/Split/netcoreapp3.1/Numbers/Int32Polyfill.cs index e455cd66..12bf78cd 100644 --- a/src/Split/netcoreapp3.1/Numbers/Int32Polyfill.cs +++ b/src/Split/netcoreapp3.1/Numbers/Int32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/netcoreapp3.1/Numbers/IntPtrPolyfill.cs b/src/Split/netcoreapp3.1/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/netcoreapp3.1/Numbers/IntPtrPolyfill.cs +++ b/src/Split/netcoreapp3.1/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/netcoreapp3.1/Numbers/UInt32Polyfill.cs b/src/Split/netcoreapp3.1/Numbers/UInt32Polyfill.cs index 9592af7a..9c3dfcf0 100644 --- a/src/Split/netcoreapp3.1/Numbers/UInt32Polyfill.cs +++ b/src/Split/netcoreapp3.1/Numbers/UInt32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/netcoreapp3.1/Numbers/UIntPtrPolyfill.cs b/src/Split/netcoreapp3.1/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/netcoreapp3.1/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/netcoreapp3.1/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/netstandard2.0/MathPolyfill.cs b/src/Split/netstandard2.0/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/netstandard2.0/MathPolyfill.cs +++ b/src/Split/netstandard2.0/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/netstandard2.0/Numbers/Int32Polyfill.cs b/src/Split/netstandard2.0/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/netstandard2.0/Numbers/Int32Polyfill.cs +++ b/src/Split/netstandard2.0/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/netstandard2.0/Numbers/IntPtrPolyfill.cs b/src/Split/netstandard2.0/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/netstandard2.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/netstandard2.0/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/netstandard2.0/Numbers/UInt32Polyfill.cs b/src/Split/netstandard2.0/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/netstandard2.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/netstandard2.0/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/netstandard2.0/Numbers/UIntPtrPolyfill.cs b/src/Split/netstandard2.0/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/netstandard2.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/netstandard2.0/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/netstandard2.1/MathPolyfill.cs b/src/Split/netstandard2.1/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/netstandard2.1/MathPolyfill.cs +++ b/src/Split/netstandard2.1/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/netstandard2.1/Numbers/Int32Polyfill.cs b/src/Split/netstandard2.1/Numbers/Int32Polyfill.cs index e455cd66..12bf78cd 100644 --- a/src/Split/netstandard2.1/Numbers/Int32Polyfill.cs +++ b/src/Split/netstandard2.1/Numbers/Int32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/netstandard2.1/Numbers/IntPtrPolyfill.cs b/src/Split/netstandard2.1/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/netstandard2.1/Numbers/IntPtrPolyfill.cs +++ b/src/Split/netstandard2.1/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/netstandard2.1/Numbers/UInt32Polyfill.cs b/src/Split/netstandard2.1/Numbers/UInt32Polyfill.cs index 9592af7a..9c3dfcf0 100644 --- a/src/Split/netstandard2.1/Numbers/UInt32Polyfill.cs +++ b/src/Split/netstandard2.1/Numbers/UInt32Polyfill.cs @@ -36,6 +36,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/netstandard2.1/Numbers/UIntPtrPolyfill.cs b/src/Split/netstandard2.1/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/netstandard2.1/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/netstandard2.1/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Split/uap10.0/MathPolyfill.cs b/src/Split/uap10.0/MathPolyfill.cs index 52a7edb0..57fbeb6c 100644 --- a/src/Split/uap10.0/MathPolyfill.cs +++ b/src/Split/uap10.0/MathPolyfill.cs @@ -253,5 +253,39 @@ public static nint Clamp(nint value, nint min, nint max) } return value; } + /// + /// Produces the full product of two 64-bit numbers. + /// + public static long BigMul(long a, long b, out long low) => + BigMulCore(a, b, out low); + /// + /// Produces the full product of two unsigned 64-bit numbers. + /// + public static ulong BigMul(ulong a, ulong b, out ulong low) => + BigMulCore(a, b, out low); + } + static ulong BigMulCore(ulong left, ulong right, out ulong lower) + { + unchecked + { + var leftLower = (uint) left; + var leftUpper = (uint) (left >> 32); + var rightLower = (uint) right; + var rightUpper = (uint) (right >> 32); + var lowerLower = (ulong) leftLower * rightLower; + var middle = (ulong) leftUpper * rightLower + (lowerLower >> 32); + var middleLower = (ulong) leftLower * rightUpper + (uint) middle; + lower = (middleLower << 32) | (uint) lowerLower; + return (ulong) leftUpper * rightUpper + (middle >> 32) + (middleLower >> 32); + } + } + static long BigMulCore(long left, long right, out long lower) + { + unchecked + { + var upper = BigMulCore((ulong) left, (ulong) right, out var unsignedLower); + lower = (long) unsignedLower; + return (long) upper - ((left >> 63) & right) - ((right >> 63) & left); + } } } diff --git a/src/Split/uap10.0/Numbers/Int32Polyfill.cs b/src/Split/uap10.0/Numbers/Int32Polyfill.cs index 6b87843d..d1c04316 100644 --- a/src/Split/uap10.0/Numbers/Int32Polyfill.cs +++ b/src/Split/uap10.0/Numbers/Int32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out int.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two 32-bit numbers. + /// + public static long BigMul(int left, int right) => + (long) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static int Log10(int value) diff --git a/src/Split/uap10.0/Numbers/IntPtrPolyfill.cs b/src/Split/uap10.0/Numbers/IntPtrPolyfill.cs index fafe1b2b..f886461d 100644 --- a/src/Split/uap10.0/Numbers/IntPtrPolyfill.cs +++ b/src/Split/uap10.0/Numbers/IntPtrPolyfill.cs @@ -86,5 +86,23 @@ public static nint Log10(nint value) } return (nint) Log10Core((ulong) value); } + /// + /// Produces the full product of two native-sized signed numbers. + /// + public static nint BigMul(nint left, nint right, out nint lower) + { + unchecked + { + if (IntPtr.Size == 4) + { + var product = (long) left * right; + lower = (nint) (int) product; + return (nint) (int) (product >> 32); + } + var upper = Math.BigMul((long) left, (long) right, out var low); + lower = (nint) low; + return (nint) upper; + } + } } } diff --git a/src/Split/uap10.0/Numbers/UInt32Polyfill.cs b/src/Split/uap10.0/Numbers/UInt32Polyfill.cs index 91159c5e..97b51daa 100644 --- a/src/Split/uap10.0/Numbers/UInt32Polyfill.cs +++ b/src/Split/uap10.0/Numbers/UInt32Polyfill.cs @@ -46,6 +46,11 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out uint.TryParse(s.ToString(), NumberStyles.Integer, provider, out result); #endif /// + /// Produces the full product of two unsigned 32-bit numbers. + /// + public static ulong BigMul(uint left, uint right) => + (ulong) left * right; + /// /// Computes the base-10 logarithm of a value. /// public static uint Log10(uint value) => diff --git a/src/Split/uap10.0/Numbers/UIntPtrPolyfill.cs b/src/Split/uap10.0/Numbers/UIntPtrPolyfill.cs index 4b29454b..58787e89 100644 --- a/src/Split/uap10.0/Numbers/UIntPtrPolyfill.cs +++ b/src/Split/uap10.0/Numbers/UIntPtrPolyfill.cs @@ -80,5 +80,23 @@ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out /// public static nuint Log10(nuint value) => (nuint) Log10Core(value); + /// + /// Produces the full product of two native-sized unsigned numbers. + /// + public static nuint BigMul(nuint left, nuint right, out nuint lower) + { + unchecked + { + if (UIntPtr.Size == 4) + { + var product = (ulong) left * right; + lower = (nuint) (uint) product; + return (nuint) (uint) (product >> 32); + } + var upper = Math.BigMul((ulong) left, (ulong) right, out var low); + lower = (nuint) low; + return (nuint) upper; + } + } } } diff --git a/src/Tests/BigMulTests.cs b/src/Tests/BigMulTests.cs new file mode 100644 index 00000000..957bbba3 --- /dev/null +++ b/src/Tests/BigMulTests.cs @@ -0,0 +1,167 @@ +using System.Numerics; + +// Every case is checked against a BigInteger product, so on net9.0 and later these +// validate the BCL and below it the polyfill. +public class BigMulTests +{ + static int[] ints = [0, 1, -1, 2, -2, 65536, -65537, 123456789, int.MaxValue, int.MinValue]; + static uint[] uints = [0, 1, 2, 65536, 123456789, 0x8000_0000, uint.MaxValue]; + static long[] longs = [0, 1, -1, 2, -2, 0x0123456789ABCDEF, unchecked((long) 0xFEDCBA9876543210), long.MaxValue, long.MinValue]; + static ulong[] ulongs = [0, 1, 2, 0x0123456789ABCDEF, 0x8000_0000_0000_0000, ulong.MaxValue]; + + [Test] + public async Task Int32() + { + foreach (var left in ints) + foreach (var right in ints) + { + var actual = (BigInteger) int.BigMul(left, right); + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + + [Test] + public async Task UInt32() + { + foreach (var left in uints) + foreach (var right in uints) + { + var actual = (BigInteger) uint.BigMul(left, right); + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + + [Test] + public async Task Math_Signed() + { + foreach (var left in longs) + foreach (var right in longs) + { + var upper = Math.BigMul(left, right, out var lower); + var actual = ((BigInteger) upper << 64) + unchecked((ulong) lower); + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + + [Test] + public async Task Math_Unsigned() + { + foreach (var left in ulongs) + foreach (var right in ulongs) + { + var upper = Math.BigMul(left, right, out var lower); + var actual = ((BigInteger) upper << 64) + lower; + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + + [Test] + public async Task IntPtr() + { + var bits = System.IntPtr.Size * 8; + nint[] values = [0, 1, -1, 2, -2, 123456789, (nint) int.MaxValue, (nint) int.MinValue]; + foreach (var left in values) + foreach (var right in values) + { + var upper = nint.BigMul(left, right, out var lower); + var lowerUnsigned = System.IntPtr.Size == 8 + ? (BigInteger) unchecked((ulong) lower) + : (BigInteger) unchecked((uint) lower); + var actual = ((BigInteger) upper << bits) + lowerUnsigned; + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + + [Test] + public async Task UIntPtr() + { + var bits = System.UIntPtr.Size * 8; + nuint[] values = [0, 1, 2, 123456789, (nuint) uint.MaxValue]; + foreach (var left in values) + foreach (var right in values) + { + var upper = nuint.BigMul(left, right, out var lower); + var actual = ((BigInteger) upper << bits) + lower; + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + +#if NET7_0_OR_GREATER + + [Test] + public async Task Int64() + { + foreach (var left in longs) + foreach (var right in longs) + { + var actual = ToBig(long.BigMul(left, right)); + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + + [Test] + public async Task UInt64() + { + foreach (var left in ulongs) + foreach (var right in ulongs) + { + var actual = ToBig(ulong.BigMul(left, right)); + await Assert.That(actual).IsEqualTo((BigInteger) left * right); + } + } + + [Test] + public async Task Int128_() + { + Int128[] values = + [ + 0, + 1, + -1, + (Int128) long.MaxValue * 3, + -((Int128) long.MaxValue * 7), + Int128.MaxValue, + Int128.MinValue + ]; + foreach (var left in values) + foreach (var right in values) + { + var upper = Int128.BigMul(left, right, out var lower); + var actual = (ToBig(upper) << 128) + ToBig((UInt128) lower); + await Assert.That(actual).IsEqualTo(ToBig(left) * ToBig(right)); + } + } + + [Test] + public async Task UInt128_() + { + UInt128[] values = + [ + 0, + 1, + 2, + ulong.MaxValue, + (UInt128) ulong.MaxValue + 1, + UInt128.MaxValue + ]; + foreach (var left in values) + foreach (var right in values) + { + var upper = UInt128.BigMul(left, right, out var lower); + var actual = (ToBig(upper) << 128) + ToBig(lower); + await Assert.That(actual).IsEqualTo(ToBig(left) * ToBig(right)); + } + } + + // built from the two 64 bit halves, so no Int128 to BigInteger conversion is assumed + static BigInteger ToBig(UInt128 value) => + ((BigInteger) (ulong) (value >> 64) << 64) + (ulong) value; + + static BigInteger ToBig(Int128 value) + { + var unsigned = ToBig((UInt128) value); + return value < 0 ? unsigned - (BigInteger.One << 128) : unsigned; + } + +#endif +}