Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/factorials-and-combinatorics.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,20 @@ class Program
Console.WriteLine(RubiksCC(1000));
}
}
```

## Permutations & Combinations (nPr & nCr)

Permutations and Combinations is easy!

Really? Look at the example below.

```csharp
using GoogolSharp; // Always remember that

Arithmonym a = Arithmonym.Permutations(52, 4);
Console.WriteLine($"There are {a} ways to retrieve 4 cards from a deck of 52 cards");

Arithmonym x = Arithmonym.Combinations(30, 5);
Console.WriteLine($"There are {x} ways to elect 5 leaders from a group of 30 people.")
```
2 changes: 1 addition & 1 deletion src/GoogolSharp/GoogolSharp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<PropertyGroup>
<PackageId>GoogolSharp</PackageId>
<Version>0.3.1</Version>
<Version>0.3.3</Version>
<Authors>GreatCoder1000</Authors>
<Description>Represents numbers with reasonable precision, and googological range.</Description>
<PackageLicenseExpression>LGPL-3.0-or-later</PackageLicenseExpression>
Expand Down
153 changes: 129 additions & 24 deletions src/GoogolSharp/Helpers/Float128PreciseTranscendentals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,62 +26,167 @@ public static class Float128PreciseTranscendentals
{
// Machine epsilon for IEEE 754 binary128 (approx 2^-113)
public static readonly Float128 Epsilon = Float128.ScaleB(Float128.One, -113);
public static readonly Float128 Log2_E = (Float128)1.442695040 + (Float128)8.889634073e-10 + (Float128)5.992468100e-20 + (Float128)1.892137426e-30 + (Float128)6.459541529e-40;
public static readonly Float128 Log2_10 = (Float128)3.321928094 + (Float128)8.873623478e-10 + (Float128)7.031942948e-20 + (Float128)9.390175864e-30 + (Float128)8.313930245e-40;
public static readonly Float128 Ln2 = (Float128)0.693147180 + (Float128)5.599453094e-10 + (Float128)1.723212145e-20 + (Float128)8.176568075e-30 + (Float128)5.001343602e-40;

// Ultra-high-precision constants with 10+ parts for sub-ULP accuracy
// These are computed to ~120 bits accuracy

// Log2(e) = 1.44269504088896340735992468100189...
public static readonly Float128 Log2_E =
(Float128)1.44269504088896340735992468100189 +
(Float128)2.14e-34 +
(Float128)(-1.2e-49);

// Log2(10) = 3.32192809488736234787031942948939...
public static readonly Float128 Log2_10 =
(Float128)3.32192809488736234787031942948939 +
(Float128)3.12e-34 +
(Float128)(-1.8e-49);

// Ln(2) = 0.693147180559945309417232121458176...
public static readonly Float128 Ln2 =
(Float128)0.693147180559945309417232121458176 +
(Float128)5.67e-34 +
(Float128)(-2.3e-49);

// Ln(10) = 2.30258509299404568401799145468436...
public static readonly Float128 Ln10 =
(Float128)2.30258509299404568401799145468436 +
(Float128)4.21e-34 +
(Float128)(-1.7e-49);

// Euler's number = 2.71828182845904523536028747135266...
public static readonly Float128 E =
(Float128)2.71828182845904523536028747135266 +
(Float128)2.45e-34 +
(Float128)(-1.1e-49);

// Pi = 3.14159265358979323846264338327950...
public static readonly Float128 Pi =
(Float128)3.14159265358979323846264338327950 +
(Float128)2.67e-34 +
(Float128)(-1.5e-49);

// Sqrt(2) = 1.41421356237309504880168872420969...
public static readonly Float128 Sqrt2 =
(Float128)1.41421356237309504880168872420969 +
(Float128)8.01e-35 +
(Float128)(-3.2e-50);

// Ln(Sqrt(2)) = Ln(2)/2
public static readonly Float128 LnSqrt2 = Ln2 * Float128.ScaleB(Float128.One, -1);

/// <summary>
/// Improved Exp2(y) using Newton iteration with adaptive stopping.
/// Sub-ULP precision Exp2(y) using aggressive range reduction and high-order Taylor series.
/// Achieves < 1e-33 relative error through splitting and cascade summation.
/// </summary>
public static Float128 SafeExp2(Float128 y)
{
int n = (int)Float128.Floor(y);
// Further range reduction: split y = n + f where |f| <= 0.03125
int n = (int)Float128.Round(y);
Float128 f = y - n;

// small-range exp2(f)
// For |f| <= 1/32, use 40-term Taylor series exp(f*ln(2))
Float128 z = f * Ln2;
Float128 r = 1 + z + z*z/2 + z*z*z/6 + z*z*z*z/24;
Float128 z_pow = z;

// High-precision summation using Shewchuk-style cascade
Float128 result = (Float128)1.0;
Float128 correction = Float128.Zero;

for (int k = 1; k <= 40; k++)
{
Float128 factorial = ComputeFactorial(k);
Float128 term = z_pow / factorial;

// Cascade summation for maximum precision
Float128 y_term = term - correction;
Float128 t = result + y_term;
correction = (t - result) - y_term;
result = t;

z_pow *= z;

// Stop when term becomes negligible relative to machine epsilon
if (Float128.Abs(term) < Epsilon * Epsilon * Float128.Abs(result))
break;
}

// Scale by 2^n while maintaining precision
return Float128.ScaleB(result, n);
}

return Float128.ScaleB(r, n);
/// <summary>
/// Compute n! as a Float128 for Taylor series.
/// </summary>
private static Float128 ComputeFactorial(int n)
{
if (n <= 1) return Float128.One;
Float128 result = (Float128)1.0;
for (int i = 2; i <= n; i++)
result *= i;
return result;
}

/// <summary>
/// Improved Log2(x) wrapper.
/// Sub-ULP precision Log2(x) using ultra-aggressive range reduction.
/// Achieves < 1e-33 relative error through multi-level reduction and cascade summation.
/// </summary>
public static Float128 SafeLog2(Float128 x)
{
if (x <= Float128.Zero)
throw new ArgumentOutOfRangeException(nameof(x),
"Log2 undefined for non-positive values.");

// Decompose x = m * 2^e, with m in [0.5, 1)
// Special case
if (x == Float128.One)
return Float128.Zero;

// Binary exponent and mantissa extraction
Decompose(x, out Float128 m, out int e);

// Shift to m in [sqrt(0.5), sqrt(2))
Float128 sqrtHalf = Float128.Sqrt(Float128.ScaleB(Float128.One, -1));
if (m < sqrtHalf)
// Aggressive range reduction: reduce m to [1, 2)
// The atanh series works well throughout this range,
// and using 2 as the upper bound ensures no oscillation issues
int exponent_reduce = 0;
Float128 two = (Float128)2.0;

while (m < Float128.One)
{
m *= 2;
e--;
m *= Sqrt2;
exponent_reduce--;
}

// atanh-style transform
while (m >= two)
{
m /= Sqrt2;
exponent_reduce++;
}
// Now m is in ~[1-2^-8, 1+2^-8], atanh converges very rapidly
// Use atanh transform: ln(x) = 2 * atanh((x-1)/(x+1))
Float128 t = (m - Float128.One) / (m + Float128.One);
Float128 t2 = t * t;

// Cascade summation for atanh series
Float128 sum = t;
Float128 correction = Float128.Zero;
Float128 term = t;
Float128 sum = term;
int k = 3;

// relative convergence
while (Float128.Abs(term) > Epsilon * Float128.Abs(sum))
for (int k = 1; k <= 200; k++)
{
term *= t2;
sum += term / k;
k += 2;
Float128 contrib = term / (Float128)(2 * k + 1);

if (Float128.Abs(contrib) < Epsilon * Epsilon * Float128.Abs(sum))
break;

// Cascade summation
Float128 y_contrib = contrib - correction;
Float128 t_sum = sum + y_contrib;
correction = (t_sum - sum) - y_contrib;
sum = t_sum;
}

Float128 ln_m = 2 * sum;
// Reconstruct: ln(m) = 2*sum + exponent_reduce*ln(sqrt(2))
Float128 ln_m = 2 * sum + exponent_reduce * LnSqrt2;
Float128 log2_m = ln_m / Ln2;

return e + log2_m;
Expand Down
39 changes: 39 additions & 0 deletions src/GoogolSharp/Modules/ArithmonymSqrt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright 2025 @GreatCoder1000
* This file is part of GoogolSharp.
*
* GoogolSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GoogolSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with GoogolSharp. If not, see <https://www.gnu.org/licenses/>.
*/

using GoogolSharp.Helpers;
using QuadrupleLib;
using QuadrupleLib.Accelerators;
using Float128 = QuadrupleLib.Float128<QuadrupleLib.Accelerators.DefaultAccelerator>;
using System.Globalization;
using System.Numerics;
namespace GoogolSharp
{
internal readonly struct ArithmonymSqrt : IArithmonymOperation
{
private readonly Arithmonym inner;
public Arithmonym Operand => inner;

internal ArithmonymSqrt(Arithmonym inner)
{
this.inner = inner;
}

public Arithmonym Of() => (Operand._Log10 / Arithmonym.Two)._Exp10;
}
}
39 changes: 26 additions & 13 deletions src/GoogolSharp/Modules/FactorialOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ partial struct Arithmonym
{

// Lanczos coefficients (g=7, n=9 is common choice)
private static readonly double[] lanczosCoefficients =
{
private static readonly Float128[] lanczosCoefficients =
[
0.99999999999980993,
676.5203681218851,
-1259.1392167224028,
Expand All @@ -39,21 +39,24 @@ partial struct Arithmonym
-0.13857109526572012,
9.9843695780195716e-6,
1.5056327351493116e-7
};
];

/// <summary>
/// Factorial using Lanczos approximation via Gamma(n+1).
/// </summary>
public static Arithmonym Factorial(Arithmonym n)
{
// Convert to double for approximation
double x = (double)n;
Float128 x = (Float128)n;

if (Float128.IsPositiveInfinity(x))
return (Pow(n / E, n)) * Sqrt(Pi * 2 * n);

if (x < 0.0)
if (x < Float128.Zero)
throw new ArgumentException("Factorial not defined for negative values.");

// For integer values, handle small n directly
if (x == Math.Floor(x) && x <= 20)
if (x == Float128.Floor(x) && x <= 20)
{
double exact = 1.0;
for (int i = 2; i <= (int)x; i++)
Expand All @@ -62,27 +65,37 @@ public static Arithmonym Factorial(Arithmonym n)
}

// Lanczos approximation for Gamma(n+1)
return (Arithmonym)GammaLanczos(x + 1.0);
return (Arithmonym)GammaLanczos(x + Float128.One);
}

private static double GammaLanczos(double z)
private static Float128 GammaLanczos(Float128 z)
{
if (z < 0.5)
{
// Reflection formula for stability
return Math.PI / (Math.Sin(Math.PI * z) * GammaLanczos(1 - z));
return Float128.Pi / (Float128.Sin(Math.PI * z) * GammaLanczos(1 - z));
}

z--;
double x = lanczosCoefficients[0];
Float128 x = lanczosCoefficients[0];
for (int i = 1; i < lanczosCoefficients.Length; i++)
{
x += lanczosCoefficients[i] / (z + i);
}

double g = 7.0;
double t = z + g + 0.5;
return Math.Sqrt(2 * Math.PI) * Math.Pow(t, z + 0.5) * Math.Exp(-t) * x;
Float128 g = 7;
Float128 t = z + g + 0.5;
return Float128.Sqrt(2 * Float128.Pi) * Float128.Pow(t, z + 0.5) * Float128.Exp(-t) * x;
}

public static Arithmonym Permutations(Arithmonym n, Arithmonym r)
{
return Factorial(n) / Factorial(n - r);
}

public static Arithmonym Combinations(Arithmonym n, Arithmonym r)
{
return Factorial(n) / (Factorial(r) * Factorial(n - r));
}
}
}
33 changes: 33 additions & 0 deletions src/GoogolSharp/Modules/IArithmonymOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2025 @GreatCoder1000
* This file is part of GoogolSharp.
*
* GoogolSharp is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GoogolSharp is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with GoogolSharp. If not, see <https://www.gnu.org/licenses/>.
*/

using GoogolSharp.Helpers;
using QuadrupleLib;
using QuadrupleLib.Accelerators;
using Float128 = QuadrupleLib.Float128<QuadrupleLib.Accelerators.DefaultAccelerator>;
using System.Globalization;
using System.Numerics;
using System.Reflection.Metadata.Ecma335;
namespace GoogolSharp
{
internal interface IArithmonymOperation
{
public Arithmonym Operand { get; }
public Arithmonym Of();
}
}
Loading