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
14 changes: 14 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,20 @@ negative even integer or off the integers, since the extension through the gamma
| `"n!!".ToEntity()` | throws `UnhandledParseException` | `n!!` |
| `"n!!!".ToEntity()` | throws `UnhandledParseException` | throws `UnrecognizedFunctionParseException` |

### The binomial coefficient, the factorial powers and the double factorial compile

**Not silent: what was refused is compiled.** `binomial` was refused by both compilers with
`UncompilableNodeException`, and `fallingfactorial`, `risingfactorial` and `n!!`, new since 2.6.0, compile from the
start. Each compiles in double precision and on the interpreter's terms: a whole order is a product, and any other goes
through the gamma function, with `NaN` at a pole of the numerator and `0` at one of the denominator. The double
factorial has no value off the whole numbers or at a negative even one
([#2195](https://github.com/asc-community/AngouriMath/issues/2195)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"binomial(x, 2)".ToEntity().Compile("x").Call(5)` | `UncompilableNodeException` | `(10, 0)` |
| `"binomial(x, 1/2)".ToEntity().Compile<double, double>("x")(3)` | `UncompilableNodeException` | `2.037183271576257` |

### `mod` compiles over `Complex`

**Not silent: what was refused is compiled.** `Compile<Complex, ...>` refused `mod` with `UncompilableNodeException`,
Expand Down
6 changes: 6 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Common/FastExpressionStatementTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Common/CombinatorialCompilationTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[AngouriMath/Numerics/CombinatorialFunctions.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

[Tests/UnitTests/Common/CompileMatrixTest.cs]
file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed under MIT.\nDetails: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.\nWebsite: https://am.angouri.org.\n

Expand Down
49 changes: 49 additions & 0 deletions Sources/AngouriMath/Functions/Compilation/IntoFE/Compiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,55 @@ private protected override void CompileNode(Compiler compiler)
}
}

// The binomial coefficient, the factorial powers and the double factorial, on the
// interpreter's terms. https://github.com/asc-community/AngouriMath/issues/2195
public partial record DoubleFactorialf
{
private protected override bool CompilesItself => true;

private protected override void CompileNode(Compiler compiler)
{
Argument.InnerCompile(compiler);
compiler.Instructions.Add(new(InstructionType.CALL_DOUBLE_FACTORIAL));
}
}

public partial record Binomialf
{
private protected override bool CompilesItself => true;

private protected override void CompileNode(Compiler compiler)
{
Lower.InnerCompile(compiler);
Upper.InnerCompile(compiler);
compiler.Instructions.Add(new(InstructionType.CALL_BINOMIAL));
}
}

public partial record FallingFactorialf
{
private protected override bool CompilesItself => true;

private protected override void CompileNode(Compiler compiler)
{
Order.InnerCompile(compiler);
Argument.InnerCompile(compiler);
compiler.Instructions.Add(new(InstructionType.CALL_FALLING_FACTORIAL));
}
}

public partial record RisingFactorialf
{
private protected override bool CompilesItself => true;

private protected override void CompileNode(Compiler compiler)
{
Order.InnerCompile(compiler);
Argument.InnerCompile(compiler);
compiler.Instructions.Add(new(InstructionType.CALL_RISING_FACTORIAL));
}
}

public partial record Maxf
{
private protected override bool CompilesItself => true;
Expand Down
18 changes: 18 additions & 0 deletions Sources/AngouriMath/Functions/Compilation/IntoFE/FastExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ internal enum InstructionType
CALL_RE,
CALL_IM,
CALL_NOT,
CALL_DOUBLE_FACTORIAL,

// 2-arg functions
CALL_SUM = 100,
Expand All @@ -77,6 +78,9 @@ internal enum InstructionType
CALL_IMPLIES,
// A value where its predicate holds, and NaN elsewhere.
CALL_PROVIDED,
CALL_BINOMIAL,
CALL_FALLING_FACTORIAL,
CALL_RISING_FACTORIAL,

// 3-arg: a value where its predicate holds, and the one below it elsewhere.
CALL_SELECT = 150,
Expand Down Expand Up @@ -340,6 +344,20 @@ public System.Numerics.Complex Substitute(params System.Numerics.Complex[] value
stack.Push(Truth(stack.Pop()) == true ? value : double.NaN);
break;
}
// The binomial coefficient, the factorial powers and the double factorial.
// https://github.com/asc-community/AngouriMath/issues/2195
case InstructionType.CALL_BINOMIAL:
stack.Push(AngouriMath.Numerics.CombinatorialFunctions.Binomial(stack.Pop(), stack.Pop()));
break;
case InstructionType.CALL_FALLING_FACTORIAL:
stack.Push(AngouriMath.Numerics.CombinatorialFunctions.FallingFactorial(stack.Pop(), stack.Pop()));
break;
case InstructionType.CALL_RISING_FACTORIAL:
stack.Push(AngouriMath.Numerics.CombinatorialFunctions.RisingFactorial(stack.Pop(), stack.Pop()));
break;
case InstructionType.CALL_DOUBLE_FACTORIAL:
stack.Push(AngouriMath.Numerics.CombinatorialFunctions.DoubleFactorial(stack.Pop()));
break;
case InstructionType.CALL_SELECT:
{
var value = stack.Pop();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ Cif when ShouldBeAtLeastDouble(e) is var newE => Expression.Call(GetDef("Ci",
Shif when ShouldBeAtLeastDouble(e) is var newE => Expression.Call(GetDef("Shi", 1, newE.Type), newE),
Chif when ShouldBeAtLeastDouble(e) is var newE => Expression.Call(GetDef("Chi", 1, newE.Type), newE),
Factorialf when ShouldBeAtLeastDouble(e) is var newE => Expression.Call(GetDef("Factorial", 1, newE.Type), newE),
DoubleFactorialf when ShouldBeAtLeastDouble(e) is var newE => Expression.Call(GetDef("DoubleFactorial", 1, newE.Type), newE),
Phif => Expression.Call(GetDef("Phi", 1, e.Type), e),

// The conjugate, the argument and the parts, widened to double as the special
Expand Down Expand Up @@ -233,6 +234,14 @@ Logf when
ShouldBeAtLeastDouble(left) is var newLeft
&& ShouldBeAtLeastDouble(right) is var newRight
=> Expression.Call(GetDef("Log", 2, newRight.Type), newLeft, newRight),
// The binomial coefficient and the factorial powers, widened to double first as the
// special functions are. https://github.com/asc-community/AngouriMath/issues/2195
Binomialf when ShouldBeAtLeastDouble(left) is var newLeft && ShouldBeAtLeastDouble(right) is var newRight
=> Expression.Call(GetDef("Binomial", 2, newLeft.Type), newLeft, newRight),
FallingFactorialf when ShouldBeAtLeastDouble(left) is var newLeft && ShouldBeAtLeastDouble(right) is var newRight
=> Expression.Call(GetDef("FallingFactorial", 2, newLeft.Type), newLeft, newRight),
RisingFactorialf when ShouldBeAtLeastDouble(left) is var newLeft && ShouldBeAtLeastDouble(right) is var newRight
=> Expression.Call(GetDef("RisingFactorial", 2, newLeft.Type), newLeft, newRight),
Maxf => Expression.Call(GetDef("Max", 2, left.Type), left, right),
Minf => Expression.Call(GetDef("Min", 2, left.Type), left, right),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,17 @@ public static System.Numerics.Complex Round(System.Numerics.Complex a)
public static System.Numerics.Complex Im(System.Numerics.Complex a) => AngouriMath.Numerics.ComplexParts.Im(a);
public static double Im(double a) => AngouriMath.Numerics.ComplexParts.Im(a);

// The binomial coefficient, the factorial powers and the double factorial, on the interpreter's
// terms. https://github.com/asc-community/AngouriMath/issues/2195
public static System.Numerics.Complex DoubleFactorial(System.Numerics.Complex a) => AngouriMath.Numerics.CombinatorialFunctions.DoubleFactorial(a);
public static double DoubleFactorial(double a) => AngouriMath.Numerics.CombinatorialFunctions.DoubleFactorial(a);
public static System.Numerics.Complex Binomial(System.Numerics.Complex a, System.Numerics.Complex b) => AngouriMath.Numerics.CombinatorialFunctions.Binomial(a, b);
public static double Binomial(double a, double b) => AngouriMath.Numerics.CombinatorialFunctions.Binomial(a, b);
public static System.Numerics.Complex FallingFactorial(System.Numerics.Complex a, System.Numerics.Complex b) => AngouriMath.Numerics.CombinatorialFunctions.FallingFactorial(a, b);
public static double FallingFactorial(double a, double b) => AngouriMath.Numerics.CombinatorialFunctions.FallingFactorial(a, b);
public static System.Numerics.Complex RisingFactorial(System.Numerics.Complex a, System.Numerics.Complex b) => AngouriMath.Numerics.CombinatorialFunctions.RisingFactorial(a, b);
public static double RisingFactorial(double a, double b) => AngouriMath.Numerics.CombinatorialFunctions.RisingFactorial(a, b);

/// <summary>
/// True where <c>System.Numerics.Complex.IsNaN</c> is, spelled out because that overload is
/// .NET 7 and later and this assembly also targets netstandard2.0. A complex number is NaN
Expand Down Expand Up @@ -515,6 +526,14 @@ public static bool IsNaN(System.Numerics.Complex a)
{ ("Re", 1, typeof(double)), Def<double>(a => Re(a)) },
{ ("Im", 1, typeof(System.Numerics.Complex)), Def<System.Numerics.Complex>(a => Im(a)) },
{ ("Im", 1, typeof(double)), Def<double>(a => Im(a)) },
{ ("DoubleFactorial", 1, typeof(System.Numerics.Complex)), Def<System.Numerics.Complex>(a => DoubleFactorial(a)) },
{ ("DoubleFactorial", 1, typeof(double)), Def<double>(a => DoubleFactorial(a)) },
{ ("Binomial", 2, typeof(System.Numerics.Complex)), Def<System.Numerics.Complex>((a, b) => Binomial(a, b)) },
{ ("Binomial", 2, typeof(double)), Def<double>((a, b) => Binomial(a, b)) },
{ ("FallingFactorial", 2, typeof(System.Numerics.Complex)), Def<System.Numerics.Complex>((a, b) => FallingFactorial(a, b)) },
{ ("FallingFactorial", 2, typeof(double)), Def<double>((a, b) => FallingFactorial(a, b)) },
{ ("RisingFactorial", 2, typeof(System.Numerics.Complex)), Def<System.Numerics.Complex>((a, b) => RisingFactorial(a, b)) },
{ ("RisingFactorial", 2, typeof(double)), Def<double>((a, b) => RisingFactorial(a, b)) },
};

// An expression tree is how C# spells "the MethodInfo of this method" without a string:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
// The conjugate, the argument and the parts, for Complex and double alone in the same way.
// https://github.com/asc-community/AngouriMath/issues/2195
var parts = new[]{ "Conj", "Arg", "Re", "Im" };
// The combinatorial functions, for Complex and double alone in the same way: one argument, and two.
// https://github.com/asc-community/AngouriMath/issues/2195
var combinatorial = new[]{ "DoubleFactorial" };
var combinatorialBinary = new[]{ "Binomial", "FallingFactorial", "RisingFactorial" };
var types = new[]{ "System.Numerics.Complex", "double", "float", "long", "int", "BigInteger" };
#>
//
Expand Down Expand Up @@ -217,6 +221,17 @@ namespace AngouriMath.Core.Compilation.IntoLinq
public static double <#= name #>(double a) => AngouriMath.Numerics.ComplexParts.<#= name #>(a);
<# } #>

// The binomial coefficient, the factorial powers and the double factorial, on the interpreter's
// terms. https://github.com/asc-community/AngouriMath/issues/2195
<# foreach (var name in combinatorial) { #>
public static System.Numerics.Complex <#= name #>(System.Numerics.Complex a) => AngouriMath.Numerics.CombinatorialFunctions.<#= name #>(a);
public static double <#= name #>(double a) => AngouriMath.Numerics.CombinatorialFunctions.<#= name #>(a);
<# } #>
<# foreach (var name in combinatorialBinary) { #>
public static System.Numerics.Complex <#= name #>(System.Numerics.Complex a, System.Numerics.Complex b) => AngouriMath.Numerics.CombinatorialFunctions.<#= name #>(a, b);
public static double <#= name #>(double a, double b) => AngouriMath.Numerics.CombinatorialFunctions.<#= name #>(a, b);
<# } #>

/// <summary>
/// True where <c>System.Numerics.Complex.IsNaN</c> is, spelled out because that overload is
/// .NET 7 and later and this assembly also targets netstandard2.0. A complex number is NaN
Expand Down Expand Up @@ -260,6 +275,16 @@ namespace AngouriMath.Core.Compilation.IntoLinq
<# foreach (var type in new[]{ "System.Numerics.Complex", "double" }) { #>
{ ("<#= name #>", 1, typeof(<#= type #>)), Def<<#= type #>>(a => <#= name #>(a)) },
<# } #>
<# } #>
<# foreach (var name in combinatorial) { #>
<# foreach (var type in new[]{ "System.Numerics.Complex", "double" }) { #>
{ ("<#= name #>", 1, typeof(<#= type #>)), Def<<#= type #>>(a => <#= name #>(a)) },
<# } #>
<# } #>
<# foreach (var name in combinatorialBinary) { #>
<# foreach (var type in new[]{ "System.Numerics.Complex", "double" }) { #>
{ ("<#= name #>", 2, typeof(<#= type #>)), Def<<#= type #>>((a, b) => <#= name #>(a, b)) },
<# } #>
<# } #>
};

Expand Down
Loading
Loading