Skip to content

The double factorial is the postfix n!! - #2187

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
the-double-factorial-is-the-postfix-double-exclamation-mark
Oct 28, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
the-double-factorial-is-the-postfix-double-exclamation-mark

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

On #2182 Happypig375 pointed out that a doublefactorial function is dubious "when the factorial is the postfix ! operator and we likely want the postfix !!". So the double factorial is the postfix n!!, as Wikipedia, MathWorld and Mathematica write it. 5!! was a parse error, so nothing changes meaning. The node is Entity.DoubleFactorialf, and also MathS.DoubleFactorial. It exports as SymPy's factorial2.

What it is, as Wikipedia defines it:

  • the product of the positive integers up to n with its parity: 7!! is 105, and 8!! is 384;
  • 0!! and (-1)!! are the empty product, 1;
  • below them, n!! = (n + 2)!!/(n + 2), so (-3)!! is -1 and (-5)!! is 1/3;
  • no value at a negative even integer;
  • no value off the integers. The extension through the gamma function gives 0!! = sqrt(2/pi), not 1, so no extension is the double factorial's.

n! = n!! (n - 1)!! is a test. An integer argument past 10000 keeps the node, as a huge whole power does (#1448).

The spellings around it

  • (n!)! keeps its parentheses.
  • n!!! is the triple factorial in the literature, which the library does not have. So it is refused, rather than read as (n!!)!, with a message that says how to write the factorial of a double factorial.
  • The names of functions to come are refused by name #2184's refusals of doublefactorial and factorial2 now point to n!!.
Input Was (2.6.0) Now
7!!, simplified throws UnhandledParseException 105
(-5)!!, simplified throws UnhandledParseException 1/3
n!! throws UnhandledParseException n!!
n!!! throws UnhandledParseException throws UnrecognizedFunctionParseException

Measured

  • Suite: 17,438 passed, 0 failed, 13 skipped, on the code as pushed. The chain's own run had 1 failure, FromStringTest.TestFormula21, which asserted x!! is a parse error; it now asserts the double factorial.
  • McpUnitTests 419, TerminalUnitTests 69, InteractiveWrapperUnitTests 19 passed. NativeAOT smoke test 50 of 50, published and run locally, with a x!! case.
  • Intbench chain against master 3dbb55b: identical on every family, 0 moves. Performance gate passed on all 19 gated benchmarks.
  • Harnesses: BoundCheck 1208 shapes with 0 disagreements, RootCheck 596/596, CasBench 120/121 with 0 wrong, SimpSweep 10463/10463, CrashCheck 2310 cases with 0 crashed, PropCheck and RuleCheck clean, CanonCheck (2091) and Confluence (3) matching their baselines.

Part of #2182.

🤖 Generated with Claude Code

https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura

Mathematics writes the double factorial n!!, as it writes the factorial n!, and 5!! was a parse
error. It is Entity.DoubleFactorialf, MathS.DoubleFactorial, written n!! and exported as SymPy's
factorial2: the product of the positive integers up to n with its parity, 0!! and (-1)!! being
the empty product 1, and below them n!! = (n + 2)!!/(n + 2), so (-3)!! is -1 and (-5)!! is 1/3.
A negative even integer has none, and nothing off the integers has one: the extension through
the gamma function gives 0!! = sqrt(2/pi), so no extension is the double factorial's.

(n!)! keeps its parentheses. Three marks are the triple factorial in the literature, which the
library does not have, so n!!! is refused rather than read as (n!!)!. The refusals of
doublefactorial and factorial2 now point to n!!, and the native smoke test evaluates one.

Part of #2182.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet Rafael-SOWNet moved this from Todo to In review in AngouriMath Oct 28, 2026
@codecov

codecov Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.60870% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.51%. Comparing base (3dbb55b) to head (1bdbcb7).

Files with missing lines Patch % Lines
...nuous/Evaluation.Continuous.Arithmetics.Classes.cs 77.27% 3 Missing and 2 partials ⚠️
.../Continuous/Entity.Continuous.Factorial.Classes.cs 83.33% 1 Missing ⚠️
Sources/AngouriMath/Functions/Substitute.cs 0.00% 0 Missing and 1 partial ⚠️
...AngouriMath/Functions/TreeAnalyzer/Sort.Classes.cs 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2187      +/-   ##
==========================================
- Coverage   86.53%   86.51%   -0.03%     
==========================================
  Files         297      297              
  Lines       66096    66161      +65     
  Branches    19227    19237      +10     
==========================================
+ Hits        57199    57240      +41     
- Misses       4834     4854      +20     
- Partials     4063     4067       +4     
Flag Coverage Δ
cpp 37.77% <ø> (ø)
csharp 87.07% <82.60%> (-0.03%) ⬇️
fsharp 32.97% <ø> (ø)
interactive 40.72% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
Sources/AngouriMath/Convenience/MathS.cs 90.06% <100.00%> (+0.03%) ⬆️
Sources/AngouriMath/Core/Antlr/AngouriMathLexer.cs 99.21% <ø> (+<0.01%) ⬆️
...ources/AngouriMath/Core/Antlr/AngouriMathParser.cs 87.33% <ø> (+0.05%) ⬆️
Sources/AngouriMath/Core/Domains.Classes.cs 100.00% <100.00%> (ø)
...Math/Core/Transformations/Matching/MatchPattern.cs 86.06% <100.00%> (+0.03%) ⬆️
...nuous/Solvers/EquationSolver/InvertNode.Classes.cs 60.40% <100.00%> (+0.16%) ⬆️
...unctions/Output/Latex/Latex.Arithmetics.Classes.cs 97.47% <100.00%> (+0.11%) ⬆️
...ns/Output/ToString/ToString.Arithmetics.Classes.cs 100.00% <100.00%> (ø)
...ions/Output/ToSympy/ToSympy.Arithmetics.Classes.cs 72.72% <100.00%> (+0.50%) ⬆️
.../Continuous/Entity.Continuous.Factorial.Classes.cs 94.44% <83.33%> (-5.56%) ⬇️
... and 3 more

... and 7 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Rafael-SOWNet
Rafael-SOWNet merged commit 5e7731f into master Oct 28, 2026
36 of 37 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in AngouriMath Oct 28, 2026
Rafael-SOWNet added a commit that referenced this pull request Oct 28, 2026
The Kernel Benchmark gate failed twice today on unchanged library code: on #2187's first run, and
on #2188 after its rebase. Both times it was SimplifyEasy, SolveEasy and EvalTrigPrecise, at
3-12x their time, and SimplifyEasy at 3% more allocation. Their re-runs matched the baseline to the
byte.

BenchmarkDotNet times one call before it decides how to measure, and where that call takes more
than two thirds of its 500 ms iteration, it times one call per iteration from then on. The first
call of those three took 349-362 ms on the failing runs and 252-300 ms on the passing ones. For a
call of tens of microseconds, one call per iteration is another measurement. That first call paid
for parsing every static expression of the class and for the library's own set-up: EvalEasy, a
cache lookup, took 133 ms the first time on one machine.

A [GlobalSetup] now touches the class and runs one simplification, one solve and one evaluation
before the timed call. Measured on one machine, master against this, the first call drops from
172 to 8 ms (SimplifyEasy), 164 to 5 ms (SolveEasy) and 158 to 45 ms (EvalTrigPrecise). What the
gated benchmarks allocate is unchanged: SimplifyEasy is 79,682 B/op on both, and the largest move is
ParseEasy's +0.11% (21 B/op). The two compilation benchmarks, which the gate leaves out for their
own spread, moved -0.5% and +2.0%. SolveHard, a call of 70 ms, is now timed one call per iteration rather than
piloted, and allocates 9,832,912 B/op against 9,831,370.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012sonx8iAspMiwRwokT1Ura
@Rafael-SOWNet
Rafael-SOWNet deleted the the-double-factorial-is-the-postfix-double-exclamation-mark branch October 29, 2026 00:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant