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
11 changes: 11 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,17 @@ real roots, so those are now the kinks, and each piece is solved exactly
| `"x^2/abs(1-x^2) = x".Solve("x")` | `{ 0, 0.618…, 1.618…, 1.618… + 1.7E-9i, … }` | `{ 0, (-1 + sqrt(5)) / 2, (1 + sqrt(5)) / 2 }` |
| `"abs(x^2 - 4) = 3*x".Solve("x")` | `{ 1, 4, 4.0000000000000062… + 1.6E-7i }` | `{ 1, 4 }` |

### A piecewise's condition under a reading asks only the case it takes

**Silent: `DomainConditionIn` of a `piecewise` is where the case it takes is defined, as `DomainCondition` already
reads it (#1817), where it asked every case to be defined.** `piecewise(sqrt(x) provided x > 0, 1)` is 1 at `-4`, and its
condition over the reals failed there. The library asks for this condition under the ambient reading, so the change
reaches whatever consults it there ([#2256](https://github.com/asc-community/AngouriMath/issues/2256)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"piecewise(sqrt(x) provided x > 0, 1)".ToEntity().DomainConditionIn(Domain.Real)` | `x >= 0 and x in RR` | `x > 0 and x >= 0 or not x > 0 or not x in RR` |

### A system with more or fewer equations than unknowns is solved or declined

**Not silent: what was an exception is an answer or `null`.** `EquationSystem.Solve` threw
Expand Down
3 changes: 3 additions & 0 deletions Sources/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ file_header_template=\nCopyright (c) 2019-2026 Angouri.\nAngouriMath is licensed
[Tests/UnitTests/Convenience/IversonTest.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/Convenience/PiecewiseConditionInAReadingTest.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/Convenience/MatrixFunctionsInAnExpressionTest.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
16 changes: 14 additions & 2 deletions Sources/AngouriMath/Functions/Evaluation/Evaluation.Definition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ partial record Entity
/// https://github.com/asc-community/AngouriMath/issues/1817
/// </remarks>
private static Entity WhereTheCaseItTakesIsDefined(Piecewise piecewise)
=> WhereTheCaseItTakesIsDefined(piecewise, static entity => entity.DomainCondition);

/// <summary>
/// As above, with <paramref name="conditionOf"/> for where a predicate and a case's expression are defined:
/// <see cref="DomainCondition"/>, or <see cref="DomainConditionIn(Domain)"/> under a reading.
/// </summary>
private static Entity WhereTheCaseItTakesIsDefined(Piecewise piecewise, System.Func<Entity, Entity> conditionOf)
{
Entity defined = Boolean.False;
Entity noEarlierCase = Boolean.True;
Expand All @@ -74,8 +81,8 @@ private static Entity WhereTheCaseItTakesIsDefined(Piecewise piecewise)
// Never taken, and passed over by the cases after it.
if (@case.Predicate.Evaled is var decided && (decided.IsNaN || decided == Boolean.False))
continue;
var predicateDomain = @case.Predicate.DomainCondition;
defined |= @case.Predicate & @case.Expression.DomainCondition & noEarlierCase;
var predicateDomain = conditionOf(@case.Predicate);
defined |= @case.Predicate & conditionOf(@case.Expression) & noEarlierCase;
noEarlierCase &= predicateDomain == Boolean.True ? !@case.Predicate : !@case.Predicate | !predicateDomain;
}
return defined;
Expand Down Expand Up @@ -371,6 +378,11 @@ private static IEnumerable<Entity> Conjuncts(Entity condition)
/// </remarks>
public Entity DomainConditionIn(Domain reading)
{
// A piecewise is defined where the case it takes is, as DomainCondition reads it, and not where every case
// is: tan(piecewise(pi / x provided x > 0, x / 2)) is 0 at 0, and its condition over the reals asked the
// first case to be defined there. https://github.com/asc-community/AngouriMath/issues/2256
if (this is Piecewise piecewise)
return WhereTheCaseItTakesIsDefined(piecewise, entity => entity.DomainConditionIn(reading)).InnerSimplified;
var read = Codomain > reading ? WithCodomain(reading) : this;
var condition = read.IntrinsicCondition;
foreach (var child in DirectChildren)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// 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.Core;
using AngouriMath.Extensions;
using Xunit;

namespace AngouriMath.Tests.Convenience
{
/// <summary>
/// A piecewise is defined under a reading where the case it takes is, as <see cref="Entity.DomainCondition"/> already
/// reads it, where <see cref="Entity.DomainConditionIn(Domain)"/> asked every case to be defined:
/// <c>tan(piecewise(pi / x provided x &gt; 0, x / 2))</c> is 0 at 0, and its condition over the reals failed there.
/// <a href="https://github.com/asc-community/AngouriMath/issues/2256">#2256</a>
/// </summary>
[Trait("Area", "Convenience")]
public sealed class PiecewiseConditionInAReadingTest
{
[Theory]
[InlineData("tan(piecewise(pi / x provided x > 0, x / 2))", "0", true)]
[InlineData("tan(piecewise(pi / x provided x > 0, x / 2))", "-2", true)]
[InlineData("tan(piecewise(pi / x provided x > 0, x / 2))", "2", false)]
[InlineData("piecewise(arccos(1 / x) provided x > 0, sec(sgn(x)))", "-1/2", true)]
[InlineData("piecewise(arccos(1 / x) provided x > 0, sec(sgn(x)))", "1/2", false)]
[InlineData("piecewise(sqrt(x) provided x > 0, 1)", "-4", true)]
[InlineData("piecewise(sqrt(x - 1) provided x > 0, 1)", "1/2", false)]
public void OverTheRealsItIsWhereTheCaseItTakesIs(string expression, string at, bool defined)
{
var condition = expression.ToEntity().DomainConditionIn(Domain.Real).Substitute("x", at.ToEntity()).Evaled;
Assert.Equal(defined ? Entity.Boolean.True : Entity.Boolean.False, condition);
}

/// <summary>Over the complex numbers it is what <see cref="Entity.DomainCondition"/> says.</summary>
[Theory]
[InlineData("tan(piecewise(pi / x provided x > 0, x / 2))", "0")]
[InlineData("tan(piecewise(pi / x provided x > 0, x / 2))", "2")]
[InlineData("piecewise(sqrt(x - 1) provided x > 0, 1)", "1/2")]
public void OverTheComplexNumbersItIsTheDomainCondition(string expression, string at)
=> Assert.Equal(
expression.ToEntity().DomainCondition.Substitute("x", at.ToEntity()).Evaled,
expression.ToEntity().DomainConditionIn(Domain.Complex).Substitute("x", at.ToEntity()).Evaled);
}
}
Loading