diff --git a/BREAKING-CHANGES.md b/BREAKING-CHANGES.md
index 7a602ae02..eef54d33e 100644
--- a/BREAKING-CHANGES.md
+++ b/BREAKING-CHANGES.md
@@ -911,6 +911,20 @@ no value at `i`, where it is 1 ([#2256](https://github.com/asc-community/Angouri
| `"x! = 0".ToEntity().Simplify()` | `False provided x in RR and (x >= 0 or not x in ZZ)` | `False provided x in ZZ implies x >= 0` |
| `"x!".ToEntity().DomainCondition` | `x in RR and (x >= 0 or not x in ZZ)` | `x in ZZ implies x >= 0` |
+### `am_series` keeps the power it is asked for
+
+**Silent: `am_series` and `Catalogue.Series` keep the powers up to the degree given, where they kept one fewer.**
+The degree, documented as the highest power kept, was passed to `MathS.Series.Maclaurin` and `Taylor` as their
+number of terms. So degree 1 gave the constant term alone, and linearising a model gave its value at the point.
+`MathS.Series` is unchanged, and its documentation now says that its argument counts terms
+([#2299](https://github.com/asc-community/AngouriMath/issues/2299)).
+
+| Input | Was (2.6.0) | Now |
+|---|---|---|
+| `am_series`, `sin(x)`, degree 7 | `x ^ 5 / 120 - x ^ 3 / 6 + x` | `x ^ 5 / 120 - x ^ 7 / 5040 - x ^ 3 / 6 + x` |
+| `am_series`, `sin(x)`, degree 1 | `0` | `x` |
+| `am_series`, `e^x`, degree 1 | `1` | `1 + x` |
+
### A number's conjugate is made under the setting in force
**Silent: `Complex.Conjugate` is made under the downcasting setting of the caller, where the first caller's setting was
diff --git a/Sources/AngouriMath/Convenience/MathS.cs b/Sources/AngouriMath/Convenience/MathS.cs
index 2cfee6d72..72a9eb5a8 100644
--- a/Sources/AngouriMath/Convenience/MathS.cs
+++ b/Sources/AngouriMath/Convenience/MathS.cs
@@ -2624,7 +2624,8 @@ public static class Series
/// The function to find the Maclaurin expansion of
///
///
- /// The degree of the resulting Maclaurin polynomial (and the variable in the resulting series)
+ /// The number of terms, one more than the highest power kept: 4 keeps the powers up to the
+ /// cube, as the example shows. https://github.com/asc-community/AngouriMath/issues/2299
///
///
/// The variable/s to take the series over (and the variable the series will be over)
@@ -2711,7 +2712,8 @@ public static Entity Maclaurin(Entity expr, int degree, params Variable[] exprVa
/// The function to find the Taylor expansion of
///
///
- /// The degree of the resulting taylor polynomial
+ /// The number of terms, one more than the highest power kept: 4 keeps the powers up to the
+ /// cube, as the example shows. https://github.com/asc-community/AngouriMath/issues/2299
///
///
/// The variable/s to take the series over (and the variable in the resulting series),
@@ -2799,7 +2801,8 @@ public static Entity Taylor(Entity expr, int degree, params (Variable exprVariab
/// The function to find the Taylor expansion of
///
///
- /// The degree of the resulting taylor polynomial
+ /// The number of terms, one more than the highest power kept: 4 keeps the powers up to the
+ /// cube, as the example shows. https://github.com/asc-community/AngouriMath/issues/2299
///
///
/// The variable/s to take the series over, the variable the series will be over,
diff --git a/Sources/AngouriMath/Core/Operations/Catalogue.cs b/Sources/AngouriMath/Core/Operations/Catalogue.cs
index 6dfb2e3a3..4390c8465 100644
--- a/Sources/AngouriMath/Core/Operations/Catalogue.cs
+++ b/Sources/AngouriMath/Core/Operations/Catalogue.cs
@@ -146,13 +146,15 @@ internal static class Catalogue
///
/// , or
- /// where there is no point.
+ /// where there is no point,
+ /// with one term more than the degree, since those count terms: sin(x) to degree 1 is
+ /// x, and was 0. https://github.com/asc-community/AngouriMath/issues/2299
///
public static Query> Series { get; } = new(
"series", "Expands as a Taylor series in a variable, up to a degree, around a point or around zero.",
static (e, variable, degree, around) => around is null
- ? MathS.Series.Maclaurin(e, degree, variable)
- : MathS.Series.Taylor(e, degree, (variable, around)),
+ ? MathS.Series.Maclaurin(e, degree + 1, variable)
+ : MathS.Series.Taylor(e, degree + 1, (variable, around)),
TheVariable, new Parameter("degree", "The highest power kept."), new Parameter("around", "The point, or none for zero."));
// ---------------------------------------------------------------- queries on a number
diff --git a/Sources/Tests/McpUnitTests/ToolsTest.cs b/Sources/Tests/McpUnitTests/ToolsTest.cs
index c8fb608a5..7c57762ee 100644
--- a/Sources/Tests/McpUnitTests/ToolsTest.cs
+++ b/Sources/Tests/McpUnitTests/ToolsTest.cs
@@ -175,9 +175,14 @@ public void EigenvaluesHoldAtZeroToo()
Assert.DoesNotContain("provided", answer.Field("eigenvalues"));
}
- [Fact]
- public void Series() =>
- Assert.Equal("x ^ 5 / 120 - x ^ 3 / 6 + x", Mcp.Call("am_series", """{"expression":"sin(x)","variable":"x","degree":7}""").Field("result"));
+ // The degree is the highest power kept, x ^ 7 at 7. It was passed on as the number of terms, so
+ // degree 7 stopped at x ^ 5 and degree 1 was the constant. https://github.com/asc-community/AngouriMath/issues/2299
+ [Theory]
+ [InlineData("sin(x)", 7, "x ^ 5 / 120 - x ^ 7 / 5040 - x ^ 3 / 6 + x")]
+ [InlineData("sin(x)", 1, "x")]
+ [InlineData("e^x", 1, "1 + x")]
+ public void Series(string expression, int degree, string expected) =>
+ Assert.Equal(expected, Mcp.Call("am_series", $$"""{"expression":"{{expression}}","variable":"x","degree":{{degree}}}""").Field("result"));
[Fact]
public void Factorize() =>
diff --git a/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs b/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs
index 288aa6743..996d6094f 100644
--- a/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs
+++ b/Sources/Tests/UnitTests/Core/Operations/CatalogueTest.cs
@@ -171,6 +171,23 @@ public void AnEntryClaimsWhatItsTransformationClaims(Transformation transformati
Assert.Equal(transformation.Relation, relation);
}
+ ///
+ /// The series is taken to the degree asked for, which is the highest power it keeps. It
+ /// was passed on as the number of terms, one fewer: sin(x) to degree 1 was 0.
+ /// https://github.com/asc-community/AngouriMath/issues/2299
+ ///
+ [Theory]
+ [InlineData("sin(x)", 1, null, "x")]
+ [InlineData("e^x", 1, null, "1 + x")]
+ [InlineData("sin(x)", 7, null, "x - x^3/6 + x^5/120 - x^7/5040")]
+ [InlineData("e^x", 2, "1", "e + e (x - 1) + e (x - 1)^2 / 2")]
+ [InlineData("cos(x)", 0, null, "1")]
+ public void ASeriesKeepsTheHighestPowerItIsAskedFor(string expression, int degree, string? around, string expected)
+ {
+ var series = Catalogue.Series.Ask(expression, MathS.Var("x"), degree, around is null ? null : MathS.FromString(around));
+ Assert.Equal(Entity.Number.Integer.Zero, (series - MathS.FromString(expected)).Simplify());
+ }
+
///
/// One answer from each entry, against the method the entry names, on an input where
/// that method and its neighbours answer differently -- so that an entry holding the
@@ -203,8 +220,8 @@ public void EachEntryAnswersAsTheMethodItNames()
Assert.Equal(MathS.Boolean.BuildTruthTable("a and not b", "a", "b"), Catalogue.TruthTable.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")]));
Assert.Equal(MathS.SolveBooleanTable("a and not b", "a", "b"), Catalogue.SatisfyingAssignments.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")]));
Assert.NotEqual(Catalogue.TruthTable.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")]), Catalogue.SatisfyingAssignments.Ask("a and not b", [MathS.Var("a"), MathS.Var("b")]));
- Assert.Equal(MathS.Series.Maclaurin("e^x", 3, x), Catalogue.Series.Ask("e^x", x, 3, null));
- Assert.Equal(MathS.Series.Taylor("e^x", 3, (x, 1)), Catalogue.Series.Ask("e^x", x, 3, 1));
+ Assert.Equal(MathS.Series.Maclaurin("e^x", 4, x), Catalogue.Series.Ask("e^x", x, 3, null));
+ Assert.Equal(MathS.Series.Taylor("e^x", 4, (x, 1)), Catalogue.Series.Ask("e^x", x, 3, 1));
Assert.NotEqual(Catalogue.Series.Ask("e^x", x, 3, null), Catalogue.Series.Ask("e^x", x, 3, 1));
Assert.Equal(MathS.ToBaseN(10.5m, 2), Catalogue.ToBase.Ask(10.5m, 2));