asc-community / asc-community/AngouriMath
ln and exp are spelled with e, so every logarithm contains a constant it does not mention
- Dominant language
- C#
- Stars
- 831
- Forks
- 79
- Avg merge
- 3h 23m
- Merged PRs (30d)
- 309
Description
`MathS.Ln(a)` is `new Logf(e, a)` and the grammar builds `exp(x)` as `MathS.Pow(MathS.e, x)`, so **every logarithm and every exponential in the library contains Euler's number as part of its structure**, whether or not the writer mentioned it.
Filing this as a proposal rather than a bug: after [#991](https://github.com/asc-community/AngouriMath/pull/991) nothing here is *wrong*, and the observable oddity is defensible. It is a representation choice worth making deliberately rather than by inheritance.
## What it looks like today
```csharp
MathS.Ln("x").Vars // x — the name is not among the variables
MathS.Ln("x").VarsAndConsts // e, x — but the constant is in there
MathS.Ln("x").Substitute("e", 3) // log(3, x) — so substituting for it reaches the base
"exp(x)".ToEntity().Substitute("e", 3) // 3 ^ x
```
Each of those is consistent with the other two once you know the base is Euler's number by value: a substitution replaces a value, and the base *is* that value. It is only surprising if you read `Vars` as the whole answer to "what is in this expression".
Before #991 it was worse than surprising — a binder over `e` reached inside, so `sum(ln(x), e, 1, 2)` was `log(1, x) + log(2, x)` instead of `2 * ln(x)`, and `sum(ln(e), e, 1, 1)` was `NaN` (it is `log(1, 1)`). That is fixed there by giving the operator's own base a distinct occurrence, which is a smaller change than this one and does not decide it.
## The proposal
Make the natural logarithm and the exponential **primitives**, so neither mentions `e`:
```
ln(x) a unary node rather than Logf(e, x)
exp(x) a unary node rather than Powf(e, x)
log(x, b) ln(x) / ln(b) rather than a node of its own
```
This is SymPy's shape, measured against 1.14.0 rather than recalled:
```python
log(x).args # (x,) — no base argument at all
log(x, 10) # log(x)/log(10) — binary log is not a node
log(x).subs(E, 3) # log(x) — nothing to reach
E**x # exp(x)
type(pi).__mro__ # Pi -> NumberSymbol -> AtomicExpr
```
## What it would cost
`Logf` is pattern-matched at **45 sites across 26 files** — printing, LaTeX, evaluation, differentiation, integration, the equation solvers, Gruntz, the SymPy export, compilation. Every one has to learn the new node or be rewritten against it, and a rule that quietly stops matching is the failure mode to fear: `Logf(var b, var a)` no longer sees a natural logarithm.
That is why I did not fold it into #991, and why it wants its own decision. If it is worth doing, the safest order is one node at a time with `canoncheck` and the corpus run between them.
## What it would buy
- `Vars` and `VarsAndConsts` agree about a logarithm, and substituting for `e` cannot change one.
- The base of `log(x, b)` stops being a place where a constant can hide, which is the class of defect #984 was.
- `log(x, b)` gains a definition rather than a representation, so the rules about it are about `ln`.
Contributor guide
Research direction
Start by examining the existing Logf handling at its 45 pattern-match sites across 26 files, including printing, LaTeX, evaluation, calculus, solvers, Gruntz, SymPy export, and compilation. Introduce the proposed nodes one at a time, checking canoncheck and the corpus run between changes. Done means natural logarithms and exponentials no longer expose e, binary logarithms follow the stated definition, and all existing handling remains covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- backend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100