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
15 changes: 15 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,21 @@ A not-equal written `<>` is read as `not (a = b)`, and printed that way.

## Unreleased — since 2.6.0

### An equation through a negative whole power is not answered with no roots

**Silent: an empty answer becomes the roots.** Inverting `x^n = v` for a negative whole `n` multiplied by the roots
of unity of order `n`, and there are none of order `-2`, so the inversion found no roots, and an equation that reached
one inside another was answered with the empty set, a claim that it has no solution. `x^n = v` has the roots of
`x^|n| = 1/v`, and they are found now. `x^(-2) = 4` on its own was already answered by another route
([#2144](https://github.com/asc-community/AngouriMath/issues/2144)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"1/(1 + x^(-2)) = 1/2".Solve("x")` | `{ }` | `{ 1, -1 }` |
| `"2/(1 + x^(-2)) = 1".Solve("x")` | `{ }` | `{ 1, -1 }` |
| `"1/(2 + p^(-3)) = 1/3".Solve("p")` | `{ }` | `{ 1, -1/2 + i * 1/2 * sqrt(3), -1/2 + i * -1/2 * sqrt(3) }` |
| `"1/(1 + x^(-2)) = y".Solve("x")` | `{ }` | `{ ((-1) / (-y) - 1) ^ (-1/2), -((-1) / (-y) - 1) ^ (-1/2) }` |

### A system with a product equation is solved branch by branch

**Silent: solutions that were missing are answered.** `EquationSystem.Solve` eliminated `x * y = 0` for `y` as `0`,
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Convenience/DocCommentPlacementTest.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/Algebra/SolveTest/NegativePowerInversionTest.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/LambdaArrowSyntaxTest.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
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,19 @@ partial record Powf
if (Base.ContainsNode(x))
{
if (Exponent is Integer { EInteger: var pow })
return Together(Number.GetAllRootsOf1(pow).Select(root => Base.Invert(root * MathS.Pow(value, 1 / Exponent), x)));
{
// x^n = v has |n| roots for a negative n as well, those of x^|n| = 1/v:
// each n-th root of v times a root of unity of order |n|, whose n-th
// power is 1. GetAllRootsOf1 counts up to its argument, so given -2 it
// gave no roots at all, and 1/(1 + x^(-2)) = 1/2 was answered { }. A
// negative power is never zero, so x^(-2) = 0 is the one that has none.
// https://github.com/asc-community/AngouriMath/issues/2144
if (pow.IsZero)
return null;
if (pow.Sign < 0 && value.Evaled is Complex { IsZero: true })
return Enumerable.Empty<Entity>();
return Together(Number.GetAllRootsOf1(pow.Abs()).Select(root => Base.Invert(root * MathS.Pow(value, 1 / Exponent), x)));
}
else
return Base.Invert(MathS.Pow(value, 1 / Exponent), x);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
// Website: https://am.angouri.org.
//

using AngouriMath;
using AngouriMath.Extensions;
using System.Linq;
using Xunit;
using static AngouriMath.Entity;
using static AngouriMath.Entity.Set;

namespace AngouriMath.Tests.Algebra.SolveTest
{
/// <summary>
/// Inverting a negative whole power found no roots: the roots of unity it multiplies by were
/// counted up to the exponent, and up to -2 is none. So an equation that reaches one inside
/// another, as <c>1/(1 + x^(-2)) = 1/2</c> does, was answered with the empty set, a claim that
/// it has no roots, where it has 1 and -1.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2144">#2144</a>
/// </summary>
[Trait("Area", "Algebra")]
public sealed class NegativePowerInversionTest
{
private static Set Solve(string statement, string x = "x") => statement.ToEntity().Solve(x);

private static Entity IsIn(string value, Set set) => value.ToEntity().In(set).Simplify();

[Theory]
[InlineData("1/(1 + x^(-2)) = 1/2", "1", "-1", "2")]
[InlineData("1/(2 + x^(-3)) = 1/3", "1", "-1/2 + 1/2 * sqrt(3) * i", "-1")]
[InlineData("2/(1 + x^(-2)) = 1", "1", "-1", "0")]
[InlineData("1/(3 + (x + 1)^(-2)) = 1/4", "0", "-2", "1")]
public void EveryRootIsFound(string equation, string root, string otherRoot, string nonRoot)
{
var solved = Solve(equation);
Assert.Equal(Entity.Boolean.True, IsIn(root, solved));
Assert.Equal(Entity.Boolean.True, IsIn(otherRoot, solved));
Assert.Equal(Entity.Boolean.False, IsIn(nonRoot, solved));
}

/// <summary>
/// With a symbol on the right the answer is the two roots in terms of it, which at
/// y = 1/2 are the roots above.
/// </summary>
[Fact]
public void TheRootsInASymbolAreTheRootsAtEachValue()
{
var solved = Assert.IsType<FiniteSet>(Solve("1/(1 + x^(-2)) = y"));
Assert.Equal(2, solved.Count);
var atAHalf = solved.Select(root => root.Substitute("y", "1/2".ToEntity()).Simplify()).ToList();
Assert.Contains(atAHalf, root => root == 1);
Assert.Contains(atAHalf, root => root == -1);
}

/// <summary>A negative power is never zero, so this one does have none.</summary>
[Fact]
public void ANegativePowerIsNeverZero()
=> Assert.Equal(Set.Empty, Solve("1/(1 + x^(-2)) = 1"));
}
}
Loading