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 +}