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
27 changes: 18 additions & 9 deletions Sources/AngouriMath/Core/Transformations/Matching/MatchedRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2120,14 +2120,16 @@ is var (divided, remainder)
MatchPattern.Any<Sumf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Sumf.LinearChildren(node), level, (a, b) => a + b),
Soundness.SoundUnderAssumptions),
Soundness.SoundUnderAssumptions,
description: "the terms of a sum are put in canonical order and like terms are grouped"),

new MatchedRule(
"a-difference-chain-is-sorted-and-grouped",
MatchPattern.Any<Minusf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Sumf.LinearChildren(node), level, (a, b) => a + b),
Soundness.SoundUnderAssumptions),
Soundness.SoundUnderAssumptions,
description: "a difference is read as a sum, whose terms are put in canonical order and grouped"),

new MatchedRule(
"a-product-chain-is-sorted-and-grouped",
Expand All @@ -2136,49 +2138,56 @@ is var (divided, remainder)
node, Mulf.LinearChildren(node), level, (a, b) => a * b),
// Regrouping reads a quotient as a product with a negative power, which is the
// same value wherever the divisor is not zero.
Soundness.SoundUnderAssumptions),
Soundness.SoundUnderAssumptions,
description: "the factors of a product are put in canonical order and like factors are grouped"),

new MatchedRule(
"a-quotient-chain-is-sorted-and-grouped",
MatchPattern.Any<Divf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Mulf.LinearChildren(node), level, (a, b) => a * b),
Soundness.SoundUnderAssumptions),
Soundness.SoundUnderAssumptions,
description: "a quotient is read as a product, whose factors are put in canonical order and grouped"),

new MatchedRule(
"a-conjunction-chain-is-sorted-and-grouped",
MatchPattern.Any<Andf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Andf.LinearChildren(node), level, (a, b) => a & b),
Soundness.Sound),
Soundness.Sound,
description: "the operands of a conjunction are put in canonical order and repeats are grouped"),

new MatchedRule(
"a-disjunction-chain-is-sorted-and-grouped",
MatchPattern.Any<Orf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Orf.LinearChildren(node), level, (a, b) => a | b),
Soundness.Sound),
Soundness.Sound,
description: "the operands of a disjunction are put in canonical order and repeats are grouped"),

new MatchedRule(
"a-union-chain-is-sorted-and-grouped",
MatchPattern.Any<Set.Unionf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Set.Unionf.LinearChildren(node), level, (a, b) => a.Unite(b)),
Soundness.Sound),
Soundness.Sound,
description: "the operands of a union are put in canonical order and repeats are grouped"),

new MatchedRule(
"an-intersection-chain-is-sorted-and-grouped",
MatchPattern.Any<Set.Intersectionf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Set.Intersectionf.LinearChildren(node), level, (a, b) => a.Intersect(b)),
Soundness.Sound),
Soundness.Sound,
description: "the operands of an intersection are put in canonical order and repeats are grouped"),

new MatchedRule(
"an-exclusive-disjunction-chain-is-sorted-and-grouped",
MatchPattern.Any<Xorf>("x"),
(node, _) => Functions.Patterns.SortAndGroup(
node, Xorf.LinearChildren(node), level, (a, b) => a ^ b),
Soundness.Sound));
Soundness.Sound,
description: "the operands of an exclusive disjunction are put in canonical order and repeats are grouped"));

/// <summary>
/// <see cref="Functions.Patterns.FractionCommonDenominatorRules"/>, as data — again one
Expand Down
9 changes: 3 additions & 6 deletions Sources/AngouriMath/Core/Transformations/RewriteRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,7 @@ public static class RewriteRules
// Regrouping reads a quotient as a product with a negative power, which is the
// same value wherever the divisor is not zero.
Soundness.SoundUnderAssumptions,
Patterns.SortRules(TreeAnalyzer.SortLevel.HIGH_LEVEL),
Patterns.SortRulesArms(TreeAnalyzer.SortLevel.HIGH_LEVEL),
Matching.MatchedRules.Sort(TreeAnalyzer.SortLevel.HIGH_LEVEL),
isNormalization: true);

/// <summary>
Expand All @@ -63,8 +62,7 @@ public static class RewriteRules
"Sorts and groups commutative operands, distinguishing terms by their constants too.",
TransformationRelation.Equivalence,
Soundness.SoundUnderAssumptions,
Patterns.SortRules(TreeAnalyzer.SortLevel.MIDDLE_LEVEL),
Patterns.SortRulesArms(TreeAnalyzer.SortLevel.MIDDLE_LEVEL),
Matching.MatchedRules.Sort(TreeAnalyzer.SortLevel.MIDDLE_LEVEL),
isNormalization: true);

/// <summary>
Expand All @@ -76,8 +74,7 @@ public static class RewriteRules
"Sorts and groups commutative operands by the whole subtree.",
TransformationRelation.Equivalence,
Soundness.SoundUnderAssumptions,
Patterns.SortRules(TreeAnalyzer.SortLevel.LOW_LEVEL),
Patterns.SortRulesArms(TreeAnalyzer.SortLevel.LOW_LEVEL),
Matching.MatchedRules.Sort(TreeAnalyzer.SortLevel.LOW_LEVEL),
isNormalization: true);

/// <summary>
Expand Down
12 changes: 7 additions & 5 deletions Sources/AngouriMath/Docs/Contributing/WritingARule.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ effect". This is how to supply those seven things.
`Core/Transformations/Matching/MatchedRules.cs`, as a value in a `MatchedRuleSet`. **33** sets and
**324** rules live there today.

The `switch` statements in `Functions/Simplification/Patterns` are the older form. Twenty-seven of
the thirty registered sets no longer run theirs, and **none of those twenty-seven describes it any
more either**; the three that remain still run theirs, so describing it is honest
([#825](https://github.com/asc-community/AngouriMath/issues/825)).
The `switch` statements in `Functions/Simplification/Patterns` are the older form. **All thirty
registered sets now run as data and describe what they run**; none executes its `switch` any more.
The last three to move were the `CanonicalOrder` family, repointed at `MatchedRules.Sort(level)` —
which had existed as data, held to the `switch` by `MatchedRulesAgreeWithTheSwitchTest` at a hundred
firings per level, and been run by nothing. The `switch` forms stay because that agreement test is
what keeps the two spellings honest ([#825](https://github.com/asc-community/AngouriMath/issues/825)).
**Do not add a rule to a `switch`.** A `switch` arm cannot carry a name, a tier, an identity or a
direction, and everything below is about those.

Expand Down Expand Up @@ -89,7 +91,7 @@ debugged for an afternoon.
| `Left.ToString()` | `Divf(var a, Divf(var b, var c))` — how the matcher spells it |

Write the identity with `=`, not `->`: it is an equality, and the arrow belongs to the direction the
rule happens to be applied in. **294** rules carry one today; a new rule should.
rule happens to be applied in. **321** rules carry one today; a new rule should.

## The pattern language

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ public void TheRegistryIsAddressableAsFarAsItSaysItIs()
// are thirty-three; NumericNeat's sixteen are eleven, six of them being three rules
// written once per side a negative factor can sit on; and the two factorial sets are
// eight arms each written as three. Every other repointed set is one arm for one rule.
Assert.Equal(315, withRules.Sum(set => set.Rules.Count));
Assert.Equal(321, withRules.Sum(set => set.Rules.Count));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,16 @@ public void WhereARuleGoes()
Stated(33, MatchedRules.All.Count, "the number of rule sets written as data");
Stated(324, MatchedRules.All.Sum(set => set.Rules.Count), "the number of rules written as data");
Stated(30, RewriteRules.All.Count, "the number of registered sets");
Stated(27, RewriteRules.All.Count(set =>
// Two families register under one name and run a data set under another --
// CommonDenominator over MatchedRules.CommonDenominator(level), CanonicalOrder over
// MatchedRules.Sort(level) -- so a name match alone undercounts them. This said 27
// while the truth was 30 for exactly as long as the second family was unlisted here.
Stated(30, RewriteRules.All.Count(set =>
MatchedRules.All.Any(data => data.Name == set.Name)
|| set.Name.StartsWith("CommonDenominator", StringComparison.Ordinal)),
|| set.Name.StartsWith("CommonDenominator", StringComparison.Ordinal)
|| set.Name.StartsWith("CanonicalOrder", StringComparison.Ordinal)),
"how many registered sets run the matcher");
Stated(27, RewriteRules.All.Count(set =>
Stated(30, RewriteRules.All.Count(set =>
set.Rules.Count > 0 && set.Rules.All(rule => rule.Soundness is not null)),
"how many registered sets describe what they run");
}
Expand All @@ -68,7 +73,7 @@ public void TheNameIsASentence()

[Fact]
public void TheIdentityIsNotTheName()
=> Stated(294,
=> Stated(321,
MatchedRules.All.SelectMany(set => set.Rules).Count(rule => rule.Description is not null),
"how many rules carry an identity");

Expand Down
18 changes: 12 additions & 6 deletions Sources/Tests/UnitTests/Core/Transformations/RuleMetadataTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,14 @@ namespace AngouriMath.Tests.Core.Transformations
/// </summary>
/// <remarks>
/// <para>
/// <b>The registry runs one thing and describes another.</b> 27 of the 30 sets execute
/// <see cref="MatchedRuleSet.ApplyHere"/>, and 29 of them take their
/// <see cref="RewriteRuleSet.Rules"/> from <c>RuleRegistryGenerator</c> reading the
/// <c>switch</c> those sets no longer run. That is
/// <a href="https://github.com/asc-community/AngouriMath/issues/825">#825</a>'s open half.
/// <b>The registry used to run one thing and describe another.</b> When this was written,
/// 27 of the 30 sets executed <see cref="MatchedRuleSet.ApplyHere"/> while 29 took their
/// <see cref="RewriteRuleSet.Rules"/> from <c>RuleRegistryGenerator</c> reading a
/// <c>switch</c> they no longer ran — <a href="https://github.com/asc-community/AngouriMath/issues/825">#825</a>'s
/// open half. Both numbers are now 30 and 0: every registration passes its
/// <see cref="MatchedRuleSet"/> to the constructor that takes one, which runs it and
/// describes it from the same object. The last three to move were the <c>CanonicalOrder</c>
/// family, and the counts below moved with them.
/// </para>
/// <para>
/// This file is about the three things that had to be true of
Expand Down Expand Up @@ -112,6 +115,9 @@ public void ARuleWrittenAsDataCarriesItsTierIntoTheRegistry()
var fromData = new[]
{
nameof(RewriteRules.Boolean),
nameof(RewriteRules.CanonicalOrder),
nameof(RewriteRules.CanonicalOrderCountingConstants),
nameof(RewriteRules.CanonicalOrderExact),
nameof(RewriteRules.CollapseMultipleFractions),
nameof(RewriteRules.Common),
nameof(RewriteRules.CollapseTrigonometricFunctions),
Expand Down Expand Up @@ -168,7 +174,7 @@ public void EveryRuleOfARepointedSetCarriesItsIdentity()
var repointed = RewriteRules.All
.Where(set => set.Rules.Count > 0 && set.Rules.All(rule => rule.Soundness is not null))
.ToList();
Assert.Equal(294, repointed.Sum(set => set.Rules.Count));
Assert.Equal(321, repointed.Sum(set => set.Rules.Count));
Assert.All(repointed, set => Assert.All(set.Rules, rule => Assert.NotNull(rule.Description)));
}

Expand Down
21 changes: 11 additions & 10 deletions Sources/Tests/UnitTests/Core/Transformations/StepAsASentenceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,19 +63,20 @@ public void EveryRuleWrittenAsDataIsNamedInProse()
Assert.All(names, name => Assert.True(Explanation.IsProse(name),
$"'{name}' is a rule name that does not read as a phrase in English"));

// And a generated one is not mistaken for prose. Which sets those are moves as the
// registry is repointed set by set, so they are found rather than named: a set whose
// rules carry no tier of their own is one still described by `RuleRegistryGenerator`,
// and every name it gives is a rendered pattern. Naming one instead cost a failure the
// day Trigonometric was repointed, which is the argument for asking.
// And no set is described by `RuleRegistryGenerator` any more. A set whose rules
// carry no tier of their own was one still described by the generator, and every name
// it gave was a rendered pattern rather than prose; this used to assert that such
// sets existed and that their names did not pass for English. The last three --
// the CanonicalOrder family -- were repointed at `MatchedRules.Sort`, so the
// population is empty, and that is asserted rather than the assertion being deleted:
// a set falling back to the generator would be a regression this should catch.
var generated = RewriteRules.All
.Where(set => set.Rules.Count > 0 && set.Rules.All(rule => rule.Soundness is null))
.SelectMany(set => set.Rules)
.Select(rule => rule.Name)
.Select(set => set.Name)
.ToList();
Assert.NotEmpty(generated);
Assert.All(generated, name => Assert.False(Explanation.IsProse(name),
$"'{name}' is a rendered pattern and was taken for prose"));
Assert.True(generated.Count == 0,
"these sets are described by the generator again, and their rule names are "
+ "rendered patterns rather than prose: " + string.Join(", ", generated));
}

/// <summary>
Expand Down
Loading