Skip to content

The LINQ compiler gives a constant, a nullable operand and sgn(0) the value evaluation gives - #2242

Merged
Rafael-SOWNet merged 1 commit into
masterfrom
linq-constants-nullables-and-sgn
Oct 30, 2026
Merged

Rafael-SOWNet merged 1 commit into
masterfrom
linq-constants-nullables-and-sgn

Conversation

@Rafael-SOWNet

Copy link
Copy Markdown
Member

Fixes #2235. Fixes #2237. Fixes #2238. Three defects in the LINQ compiler, all found by work/compilesweep.

A constant over Complex (#2235). A constant is compiled as the narrowest type that holds it, -1 as a long, and a function widens a long only to double. So in an expression compiled over Complex, sqrt(-1), ln(-1) and arcsin(2) came out as Math's NaN, widened to (NaN, 0). And 1/0 was +oo, where evaluation has no value. FastExpression, whose constants are Complex, was right. Where any argument or the result is Complex, BuildTree now converts each constant to Complex. Over double, a value that is not real still has none.

A nullable operand (#2237). An Iverson bracket compiles to an int?, and a provided integer constant to a long?. No function has a definition for either, and the widening table has neither. So tan(iverson(pi > 0)) threw KeyNotFoundException, and abs, floor and sgn of one threw AngouriBugException, which asks for a bug report. The unary and binary converters now take a nullable number as a double, NaN for none, before anything else. A statement's bool? stays as it is.

sgn(0) over Complex (#2238). The definition was z / |z|, which is 0/0 at 0. It now uses the Signum extension FastExpression already used, which is 0 there.

2.6.0 and master now
"x + sqrt(-1)".ToEntity().Compile<Complex, Complex>("x")(0) (NaN, 0) (6.1e-17, 1)
"x + 1/0".ToEntity().Compile<Complex, Complex>("x")(0) (Infinity, 0) (NaN, NaN)
"x + sgn(-1 provided pi > 3)".ToEntity().Compile<Complex, Complex>("x")(1) throws AngouriBugException (0, 0)
"sgn(x)".ToEntity().Compile<Complex, Complex>("x")(0) (NaN, NaN) (0, 0)

Tests. LinqCompilationValueTest compares LINQ over Complex and FastExpression with evaluation on 16 expressions:

  • six constants whose values are not real;
  • 1/0;
  • five nullable operands;
  • sgn at 0.

It also checks the double compiler on both kinds. 17 of its 21 cases fail on master. Full suite 18134/0, interactive trio, AOT smoke 73/0, all frameworks build.

sgn(-1 provided false) is left out of the test: evaluation gives 1 there, which is wrong. That is filed as #2241, with its fix to follow.

🤖 Generated with Claude Code

… value evaluation gives

Three defects in the LINQ compiler, found by work/compilesweep.

A constant over Complex (#2235). A constant is compiled as the narrowest
type that holds it, -1 as a long, and a function widens a long only to
double. So in an expression compiled over Complex, sqrt(-1), ln(-1) and
arcsin(2) were Math's NaN, widened to (NaN, 0), and 1/0 was +oo, where
evaluation has no value. FastExpression, whose constants are Complex,
was right. Where any argument or the result is Complex, BuildTree now
converts each constant to Complex. Over double, a value that is not real
still has none.

A nullable operand (#2237). An Iverson bracket compiles to an int?, and a
provided integer constant to a long?. No function has a definition for
either, and the widening table has neither. So tan(iverson(pi > 0))
threw KeyNotFoundException, and abs, floor and sgn of one threw
AngouriBugException, which asks for a bug report. The unary and binary
converters now take a nullable number as a double, NaN for none, before
anything else. A statement's bool? stays as it is.

sgn(0) over Complex (#2238). The definition was z / |z|, which is 0/0 at
0. It is the Signum extension FastExpression already used, which is 0
there.

LinqCompilationValueTest compares LINQ over Complex and FastExpression
with evaluation on 16 expressions, among them six constants whose values
are not real, 1/0, five nullable operands and sgn at 0. It also checks
the double compiler on both kinds. 17 of its 21 cases fail on master.

sgn(-1 provided false) is left out: evaluation gives 1 there, which is
wrong, and is #2241.

Fixes #2235. Fixes #2237. Fixes #2238.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Rafael-SOWNet Rafael-SOWNet moved this from Todo to In review in AngouriMath Oct 30, 2026
@codecov

codecov Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 86.70%. Comparing base (b8a6a6f) to head (2e229d3).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #2242      +/-   ##
==========================================
+ Coverage   86.68%   86.70%   +0.02%     
==========================================
  Files         310      310              
  Lines       67766    67775       +9     
  Branches    19660    19663       +3     
==========================================
+ Hits        58744    58767      +23     
+ Misses       4909     4896      -13     
+ Partials     4113     4112       -1     
Flag Coverage Δ
cpp 37.77% <ø> (ø)
csharp 87.25% <100.00%> (+0.02%) ⬆️
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 Δ
...ctions/Compilation/IntoLinq/CompilationProtocol.cs 92.21% <100.00%> (+0.09%) ⬆️
...Functions/Compilation/IntoLinq/IntoLinqCompiler.cs 97.05% <100.00%> (+0.18%) ⬆️
...h/Functions/Compilation/IntoLinq/MathAllMethods.cs 73.72% <100.00%> (ø)

... 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 5f1e85a into master Oct 30, 2026
36 checks passed
@github-project-automation github-project-automation Bot moved this from In review to Done in AngouriMath Oct 30, 2026
@Rafael-SOWNet
Rafael-SOWNet deleted the linq-constants-nullables-and-sgn branch October 30, 2026 12:42
Rafael-SOWNet added a commit that referenced this pull request Oct 30, 2026
Signumf's rule took a real number's sign from its EDecimal, and
EDecimal's Sign of NaN is 1. So the signum of anything without a value
was 1: sgn(NaN), sgn(0/0), sgn(-1 provided false), and
1 + sgn(-1 provided pi < 3) was 2. That is a value where there is none,
on 2.6.0 as on master. Number.Signum already gave NaN for NaN; only this
rule did not.

A NaN real is now its own signum. abs, sin and the rest already passed a
NaN through.

Found by comparing the compiled sgn with evaluation for #2242. The
compiled value was NaN there, and evaluation's was 1.

SignumOfNaNTest: five spellings of a missing value, by Evaled and by
Simplify, all failing on master; five signs that stay as they were; and
the compiled value.

canoncheck-baseline.tsv loses 52 findings, and gains none. Each was an
InnerSimplified order disagreement on a sum with sgn(a / 0) in it. a / 0
has no value, and its signum was 1, so the two orders of the sum came
out as different expressions. Now that the signum is NaN, the sum is
NaN in either order. The baseline goes from 2091 to 2039.

Fixes #2241.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rafael-SOWNet added a commit that referenced this pull request Oct 30, 2026
#2242 widened an Iverson bracket, an int?, and a provided integer, a
long?, to double before a function took it. A piecewise's cases did not
pass through that widening, so a piecewise with such a case still threw
KeyNotFoundException, alone, under a function or nested in another
piecewise: MaxType, which picks the type the cases are taken in, has no
int? or long?. sin(piecewise(iverson(x > 0) provided x > 0, sin(2)))
was one. HandlePiecewise now widens each numeric case
as a function's operand is widened.

On 2.6.0 an Iverson bracket did not compile at all, and a provided
integer in a piecewise over double threw KeyNotFoundException too:
piecewise(piecewise(-1 provided x > 0, 3) provided x > 0, x) + x at 1.
The #2237 line in BREAKING-CHANGES.md now says which of the two threw,
and takes in the piecewise.

PiecewiseOfNullableCompilationTest compares seven such expressions,
under both compilers, with evaluation. All seven fail on master.

Part of #2237.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Rafael-SOWNet added a commit that referenced this pull request Oct 30, 2026
#2246)

#2242 widened an Iverson bracket, an int?, and a provided integer, a
long?, to double before a function took it. A piecewise's cases did not
pass through that widening, so a piecewise with such a case still threw
KeyNotFoundException, alone, under a function or nested in another
piecewise: MaxType, which picks the type the cases are taken in, has no
int? or long?. sin(piecewise(iverson(x > 0) provided x > 0, sin(2)))
was one. HandlePiecewise now widens each numeric case
as a function's operand is widened.

On 2.6.0 an Iverson bracket did not compile at all, and a provided
integer in a piecewise over double threw KeyNotFoundException too:
piecewise(piecewise(-1 provided x > 0, 3) provided x > 0, x) + x at 1.
The #2237 line in BREAKING-CHANGES.md now says which of the two threw,
and takes in the piecewise.

PiecewiseOfNullableCompilationTest compares seven such expressions,
under both compilers, with evaluation. All seven fail on master.

Part of #2237.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

1 participant