diff --git a/src/Cassowary/Expression.cs b/src/Cassowary/Expression.cs index bbad307..0c5e795 100644 --- a/src/Cassowary/Expression.cs +++ b/src/Cassowary/Expression.cs @@ -1,5 +1,4 @@ using System.Collections.Immutable; -using Cassowary.Extensions; namespace Cassowary; @@ -7,8 +6,69 @@ namespace Cassowary; /// An expression that can be the left hand or right hand side of a constraint equation. /// It is a linear combination of variables, i.e. a sum of variables weighted by coefficients, plus an optional constant. /// -public readonly record struct Expression(ImmutableArray Terms, double Constant) +public readonly struct Expression { + /// + /// Gets the terms of the expression. + /// + public ImmutableArray Terms { get; init; } + + /// + /// Gets the constant value of the expression. + /// + public double Constant { get; init; } + + /// + /// Initializes a new instance of the struct. + /// + /// Terms of the expression. + /// Constant value. + public Expression(ImmutableArray terms, double constant) + { + Terms = terms; + Constant = constant; + } + + /// + /// Initializes a new instance of the struct. + /// + public Expression() + { + Terms = ImmutableArray.Empty; + } + + /// + /// Deconstructs the expression into terms and constant. + /// + /// + /// + public void Deconstruct(out ImmutableArray terms, out double constant) + { + terms = this.Terms; + constant = this.Constant; + } + + /// + public override bool Equals(object? obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + + var other = (Expression)obj; + return other.Constant == Constant && Terms.SequenceEqual(other.Terms); + } + + /// + public override int GetHashCode() + { + unchecked + { + return Constant.GetHashCode() + Terms.Aggregate(19, (h, i) => (h * 19) + i.GetHashCode()); + } + } + /// /// Constructs an expression of the form _n_, where n is a constant real number, not a variable. /// @@ -22,7 +82,7 @@ public readonly record struct Expression(ImmutableArray Terms, double Cons /// /// The . /// New instance of . - public static Expression From(Term term) => new(ImmutableArray.Empty.Add(term), 0); + public static Expression From(Term term) => new(ImmutableArray.Create(term), 0); /// /// Constructs an expression from a single variable. Forms an expression of the form _x_ @@ -88,7 +148,7 @@ public readonly record struct Expression(ImmutableArray Terms, double Cons /// The . /// New instance of with negative and constant. public static Expression operator -(Expression expression) - => new(expression.Terms.ToImmutableArray(term => -term), -expression.Constant); + => new(ImmutableArray.CreateRange(expression.Terms, term => -term), -expression.Constant); /// /// Subtract value from . @@ -192,7 +252,7 @@ public readonly record struct Expression(ImmutableArray Terms, double Cons /// The value /// New instance with and multiply by . public static Expression operator *(Expression expression, double value) - => new(expression.Terms.ToImmutableArray(x => x * value), + => new(ImmutableArray.CreateRange(expression.Terms, x => x * value), expression.Constant * value); /// @@ -233,7 +293,27 @@ public readonly record struct Expression(ImmutableArray Terms, double Cons /// The value /// New instance with and dividing by . public static Expression operator /(Expression expression, double value) - => new(expression.Terms.ToImmutableArray(term => term / value), expression.Constant / value); + => new(ImmutableArray.CreateRange(expression.Terms, term => term / value), expression.Constant / value); + + #endregion + + #region operator = + + /// + /// Checks if two are equal. + /// + /// Left . + /// Right . + public static bool operator ==(Expression left, Expression right) + => left.Equals(right); + + /// + /// Checks if two are not equal. + /// + /// Left . + /// Right . + public static bool operator !=(Expression left, Expression right) + => !left.Equals(right); #endregion } diff --git a/src/Cassowary/Extensions/ImmutableArrayExtensions.cs b/src/Cassowary/Extensions/ImmutableArrayExtensions.cs deleted file mode 100644 index 1b982a3..0000000 --- a/src/Cassowary/Extensions/ImmutableArrayExtensions.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Collections.Immutable; - -namespace Cassowary.Extensions; - -internal static class ImmutableArrayExtensions -{ - public static ImmutableArray ToImmutableArray(this ImmutableArray source, Func map) - { - var builder = ImmutableArray.Empty.ToBuilder(); - for (var index = 0; index < source.Length; index++) - { - builder.Add(map(source[index])); - } - - return builder.ToImmutable(); - } -} diff --git a/src/Cassowary/Term.cs b/src/Cassowary/Term.cs index b864a42..5662238 100644 --- a/src/Cassowary/Term.cs +++ b/src/Cassowary/Term.cs @@ -37,7 +37,7 @@ public readonly record struct Term(Variable Variable, double Coefficient) /// The value. /// Add to and return new . public static Expression operator +(Term term, double value) - => new(ImmutableArray.Empty.Add(term), value); + => new(ImmutableArray.Create(term), value); /// /// Add to and return new . @@ -64,7 +64,7 @@ public readonly record struct Term(Variable Variable, double Coefficient) /// The . /// Create new instance of with both . public static Expression operator +(Term term, Term other) - => new(ImmutableArray.Empty.AddRange(term, other), 0); + => new(ImmutableArray.Create(term, other), 0); #endregion @@ -93,7 +93,7 @@ public readonly record struct Term(Variable Variable, double Coefficient) /// The value /// New with the and negate . public static Expression operator -(Term term, double value) - => new(ImmutableArray.Empty.Add(term), -value); + => new(ImmutableArray.Create(term), -value); /// /// Subtract from and return new . @@ -111,7 +111,7 @@ public readonly record struct Term(Variable Variable, double Coefficient) /// The value /// New with the and negate . public static Expression operator -(double value, Term term) - => new(ImmutableArray.Empty.Add(-term), value); + => new(ImmutableArray.Create(-term), value); /// /// Subtract from and return new . diff --git a/src/Cassowary/Variable.cs b/src/Cassowary/Variable.cs index 5f7c83f..dc1885c 100644 --- a/src/Cassowary/Variable.cs +++ b/src/Cassowary/Variable.cs @@ -45,7 +45,7 @@ public Variable() /// The value. /// New instance with value as . public static Expression operator +(Variable variable, double value) - => new(ImmutableArray.Empty.Add(new(variable, 1)), value); + => new(ImmutableArray.Create(new Term(variable, 1)), value); /// /// Add to and return new . @@ -63,8 +63,7 @@ public Variable() /// The . /// New instance with and and as 0. public static Expression operator +(Variable variable, Term term) - => new(ImmutableArray.Empty - .AddRange(term, new(variable, 1)), 0); + => new(ImmutableArray.Create(term, new(variable, 1)), 0); /// /// Add to and return new . @@ -82,8 +81,7 @@ public Variable() /// The . /// New instance with both variables and constant as 0. public static Expression operator +(Variable variable, Variable other) - => new(ImmutableArray.Empty - .AddRange(new Term(variable, 1), new Term(other, 1)), 0); + => new(ImmutableArray.Create(new Term(variable, 1), new Term(other, 1)), 0); #endregion @@ -113,7 +111,7 @@ public Variable() /// The value. /// New instance with and negate as . public static Expression operator -(Variable variable, double value) - => new(ImmutableArray.Empty.Add(new(variable, 1)), -value); + => new(ImmutableArray.Create(new Term(variable, 1)), -value); /// /// Subtract from and return new . @@ -131,7 +129,7 @@ public Variable() /// The value. /// New instance with and negate as . public static Expression operator -(double value, Variable variable) - => new(ImmutableArray.Empty.Add(new(variable, -1)), value); + => new(ImmutableArray.Create(new Term(variable, -1)), value); /// /// Subtract from and return new . @@ -140,8 +138,7 @@ public Variable() /// The . /// New instance. public static Expression operator -(Variable variable, Term term) - => new(ImmutableArray.Empty - .AddRange(new Term(variable, 1), -term), 0); + => new(ImmutableArray.Create(new Term(variable, 1), -term), 0); /// /// Subtract from and return new . diff --git a/tests/Cassowary.Tests/ExpressionTest.cs b/tests/Cassowary.Tests/ExpressionTest.cs index 626f74d..d608a63 100644 --- a/tests/Cassowary.Tests/ExpressionTest.cs +++ b/tests/Cassowary.Tests/ExpressionTest.cs @@ -1,6 +1,5 @@ using System.Collections.Immutable; using AutoFixture; -using Cassowary.Extensions; using FluentAssertions; namespace Cassowary.Tests; @@ -8,6 +7,15 @@ namespace Cassowary.Tests; public class ExpressionTest { private readonly Fixture _fixture = new(); + + [Fact] + public void ExpressionEqualsExpression() + { + var guid = Guid.NewGuid(); + var variable = new Variable(guid); + (variable | WeightedRelation.Eq(1) | 5) + .Should().Be(variable | WeightedRelation.Eq(1) | 5); + } [Fact] public void FromConstant() @@ -85,7 +93,7 @@ public void NegateExpression() var negated = -expression; negated.Constant.Should().Be(-expression.Constant); - negated.Terms.Should().BeEquivalentTo(expression.Terms.ToImmutableArray(term => -term)); + negated.Terms.Should().BeEquivalentTo(ImmutableArray.CreateRange(expression.Terms, term => -term)); } [Fact] @@ -147,7 +155,7 @@ public void SubExpressionWithExpression() var sub = expression - otherExpression; sub.Constant.Should().Be(expression.Constant - otherExpression.Constant); sub.Terms.Should() - .BeEquivalentTo(expression.Terms.AddRange(otherExpression.Terms.ToImmutableArray(term => -term))); + .BeEquivalentTo(expression.Terms.AddRange(ImmutableArray.CreateRange(otherExpression.Terms, term => -term))); } [Fact] @@ -171,19 +179,19 @@ public void Multiply() var product = expression * multiplier; product.Constant.Should().Be(expression.Constant * multiplier); - product.Terms.Should().BeEquivalentTo(expression.Terms.ToImmutableArray(term => term * multiplier)); + product.Terms.Should().BeEquivalentTo(ImmutableArray.CreateRange(expression.Terms, term => term * multiplier)); product = expression * (float)multiplier; product.Constant.Should().Be(expression.Constant * multiplier); - product.Terms.Should().BeEquivalentTo(expression.Terms.ToImmutableArray(term => term * multiplier)); + product.Terms.Should().BeEquivalentTo(ImmutableArray.CreateRange(expression.Terms, term => term * multiplier)); product = multiplier * expression; product.Constant.Should().Be(expression.Constant * multiplier); - product.Terms.Should().BeEquivalentTo(expression.Terms.ToImmutableArray(term => term * multiplier)); + product.Terms.Should().BeEquivalentTo(ImmutableArray.CreateRange(expression.Terms, term => term * multiplier)); product = (float)multiplier * expression; product.Constant.Should().Be(expression.Constant * multiplier); - product.Terms.Should().BeEquivalentTo(expression.Terms.ToImmutableArray(term => term * multiplier)); + product.Terms.Should().BeEquivalentTo(ImmutableArray.CreateRange(expression.Terms, term => term * multiplier)); } [Fact] @@ -196,10 +204,10 @@ public void Div() var remaning = expression / divider; remaning.Constant.Should().Be(expression.Constant / divider); - remaning.Terms.Should().BeEquivalentTo(expression.Terms.ToImmutableArray(term => term / divider)); + remaning.Terms.Should().BeEquivalentTo(ImmutableArray.CreateRange(expression.Terms, term => term / divider)); remaning = expression / (double)divider; remaning.Constant.Should().Be(expression.Constant / divider); - remaning.Terms.Should().BeEquivalentTo(expression.Terms.ToImmutableArray(term => term / divider)); + remaning.Terms.Should().BeEquivalentTo(ImmutableArray.CreateRange(expression.Terms, term => term / divider)); } } diff --git a/tests/Cassowary.Tests/TermTest.cs b/tests/Cassowary.Tests/TermTest.cs index bdb0f71..e61b109 100644 --- a/tests/Cassowary.Tests/TermTest.cs +++ b/tests/Cassowary.Tests/TermTest.cs @@ -8,6 +8,13 @@ public class TermTest { private readonly Fixture _fixture = new(); + [Fact(DisplayName = "Term equals term")] + public void TermEqualsTerm() + { + var guid = Guid.NewGuid(); + new Term(new Variable(guid), 0.5).Should().Be(new Term(new Variable(guid), 0.5)); + } + [Fact] public void Or() { diff --git a/tests/Cassowary.Tests/VariableTest.cs b/tests/Cassowary.Tests/VariableTest.cs index 0462613..cb8a520 100644 --- a/tests/Cassowary.Tests/VariableTest.cs +++ b/tests/Cassowary.Tests/VariableTest.cs @@ -7,6 +7,13 @@ namespace Cassowary.Tests; public class VariableTest { private readonly Fixture _fixture = new(); + + [Fact] + public void VariableEqualsVariable() + { + var guid = Guid.NewGuid(); + new Variable(guid).Should().Be(new Variable(guid)); + } [Fact] public void AddWithValue()