Skip to content

Commit

Permalink
#544 - Add support of operations on different unit types.
Browse files Browse the repository at this point in the history
  • Loading branch information
sys27 committed May 10, 2022
1 parent dfdc7d3 commit 5959360
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 0 deletions.
6 changes: 6 additions & 0 deletions xFunc.Maths/Analyzers/Simplifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,12 @@ public override IExpression Analyze(Mul exp)
=> (left.Value * right.Value).AsExpression(),
(Length left, Number right)
=> (left.Value * right.Value).AsExpression(),
(Length left, Length right)
=> (left.Value * right.Value).AsExpression(),
(Area left, Length right)
=> (left.Value * right.Value).AsExpression(),
(Length left, Area right)
=> (left.Value * right.Value).AsExpression(),

(Number left, Time right)
=> (left.Value * right.Value).AsExpression(),
Expand Down
7 changes: 7 additions & 0 deletions xFunc.Maths/Analyzers/TypeAnalyzers/TypeAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -828,6 +828,13 @@ public virtual ResultTypes Analyze(Mul exp)
(ResultTypes.LengthNumber, ResultTypes.Number)
=> ResultTypes.LengthNumber,

(ResultTypes.LengthNumber, ResultTypes.LengthNumber)
=> ResultTypes.AreaNumber,

(ResultTypes.AreaNumber, ResultTypes.LengthNumber) or
(ResultTypes.LengthNumber, ResultTypes.AreaNumber)
=> ResultTypes.VolumeNumber,

(ResultTypes.Number, ResultTypes.TimeNumber) or
(ResultTypes.TimeNumber, ResultTypes.Number)
=> ResultTypes.TimeNumber,
Expand Down
3 changes: 3 additions & 0 deletions xFunc.Maths/Expressions/Mul.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public override object Execute(ExpressionParameters? parameters)

(NumberValue left, LengthValue right) => left * right,
(LengthValue left, NumberValue right) => left * right,
(LengthValue left, LengthValue right) => left * right,
(AreaValue left, LengthValue right) => left * right,
(LengthValue left, AreaValue right) => left * right,

(NumberValue left, TimeValue right) => left * right,
(TimeValue left, NumberValue right) => left * right,
Expand Down
26 changes: 26 additions & 0 deletions xFunc.Maths/Expressions/Units/AreaUnits/AreaUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,32 @@ public override int GetHashCode()
public override string ToString()
=> UnitName;

/// <summary>
/// Maps the current area unit to volume unit.
/// </summary>
/// <returns>The volume unit.</returns>
/// <seealso cref="AreaUnit" />
/// <exception cref="InvalidOperationException">Cannot convert the current AreaUnit to an VolumeUnit.</exception>
public VolumeUnit ToVolumeUnit()
{
if (this == Meter)
return VolumeUnit.Meter;

if (this == Centimeter)
return VolumeUnit.Centimeter;

if (this == Inch)
return VolumeUnit.Inch;

if (this == Foot)
return VolumeUnit.Foot;

if (this == Yard)
return VolumeUnit.Yard;

throw new InvalidOperationException($"Cannot convert '{UnitName}' to an VolumeUnit.");
}

/// <summary>
/// Gets a factor of conversion from this unit to base unit.
/// </summary>
Expand Down
35 changes: 35 additions & 0 deletions xFunc.Maths/Expressions/Units/LengthUnits/LengthUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,41 @@ public override int GetHashCode()
public override string ToString()
=> UnitName;

/// <summary>
/// Maps the current length unit to area unit.
/// </summary>
/// <returns>The area unit.</returns>
/// <seealso cref="AreaUnit" />
/// <exception cref="InvalidOperationException">Cannot convert the current LengthUnit to an AreaUnit.</exception>
public AreaUnit ToAreaUnit()
{
if (this == Meter)
return AreaUnit.Meter;

if (this == Millimeter)
return AreaUnit.Millimeter;

if (this == Centimeter)
return AreaUnit.Centimeter;

if (this == Kilometer)
return AreaUnit.Kilometer;

if (this == Inch)
return AreaUnit.Inch;

if (this == Foot)
return AreaUnit.Foot;

if (this == Yard)
return AreaUnit.Yard;

if (this == Mile)
return AreaUnit.Mile;

throw new InvalidOperationException($"Cannot convert '{UnitName}' to an AreaUnit.");
}

/// <summary>
/// Gets a factor of conversion from this unit to base unit.
/// </summary>
Expand Down
45 changes: 45 additions & 0 deletions xFunc.Maths/Expressions/Units/LengthUnits/LengthValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,51 @@ public override string ToString()
public static LengthValue operator -(LengthValue value)
=> new LengthValue(-value.Value, value.Unit);

/// <summary>
/// Multiplies two objects of <see cref="LengthValue"/>.
/// </summary>
/// <param name="left">The first object to multiply.</param>
/// <param name="right">The second object to multiply.</param>
/// <returns>An object that is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static AreaValue operator *(LengthValue left, LengthValue right)
{
right = right.To(left.Unit);

var areaUnit = left.Unit.ToAreaUnit();

return new AreaValue(left.Value * right.Value, areaUnit);
}

/// <summary>
/// Multiplies two objects of <see cref="LengthValue"/>.
/// </summary>
/// <param name="left">The first object to multiply.</param>
/// <param name="right">The second object to multiply.</param>
/// <returns>An object that is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static VolumeValue operator *(AreaValue left, LengthValue right)
{
var areaUnit = right.Unit.ToAreaUnit();
var rightArea = new AreaValue(right.Value, areaUnit);
var volumeUnit = left.Unit.ToVolumeUnit();

return new VolumeValue(left.Value * rightArea.Value, volumeUnit);
}

/// <summary>
/// Multiplies two objects of <see cref="LengthValue"/>.
/// </summary>
/// <param name="left">The first object to multiply.</param>
/// <param name="right">The second object to multiply.</param>
/// <returns>An object that is the product of <paramref name="left"/> and <paramref name="right"/>.</returns>
public static VolumeValue operator *(LengthValue left, AreaValue right)
{
var areaUnit = left.Unit.ToAreaUnit();
var leftArea = new AreaValue(left.Value, areaUnit);
var volumeUnit = right.Unit.ToVolumeUnit();

return new VolumeValue(leftArea.Value * right.Value, volumeUnit);
}

private LengthValue ToBase()
=> new LengthValue(Value * Unit.Factor, LengthUnit.Meter);

Expand Down
36 changes: 36 additions & 0 deletions xFunc.Tests/Analyzers/SimplifierTests/MulSimplifierTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,42 @@ public void MulLengthByNumber()
SimplifyTest(mul, expected);
}

[Fact(DisplayName = "2 m * 2 m")]
public void MulLengthByLength()
{
var mul = new Mul(
LengthValue.Meter(2).AsExpression(),
LengthValue.Meter(2).AsExpression()
);
var expected = AreaValue.Meter(4).AsExpression();

SimplifyTest(mul, expected);
}

[Fact(DisplayName = "2 m^2 * 2 m")]
public void MulAreaByLength()
{
var mul = new Mul(
AreaValue.Meter(2).AsExpression(),
LengthValue.Meter(2).AsExpression()
);
var expected = VolumeValue.Meter(4).AsExpression();

SimplifyTest(mul, expected);
}

[Fact(DisplayName = "2 m * 2 m^2")]
public void MulLengthByArea()
{
var mul = new Mul(
LengthValue.Meter(2).AsExpression(),
AreaValue.Meter(2).AsExpression()
);
var expected = VolumeValue.Meter(4).AsExpression();

SimplifyTest(mul, expected);
}

[Fact(DisplayName = "10 * 2 s")]
public void MulNumberByTime()
{
Expand Down
33 changes: 33 additions & 0 deletions xFunc.Tests/Analyzers/TypeAnalyzerTests/MulTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,39 @@ public void TestMulLengthAndNumber()
Test(exp, ResultTypes.LengthNumber);
}

[Fact]
public void TestMulLengthAndLength()
{
var exp = new Mul(
LengthValue.Meter(10).AsExpression(),
LengthValue.Meter(10).AsExpression()
);

Test(exp, ResultTypes.AreaNumber);
}

[Fact]
public void TestMulAreaAndLength()
{
var exp = new Mul(
AreaValue.Meter(10).AsExpression(),
LengthValue.Meter(10).AsExpression()
);

Test(exp, ResultTypes.VolumeNumber);
}

[Fact]
public void TestMulLengthAndArea()
{
var exp = new Mul(
LengthValue.Meter(10).AsExpression(),
AreaValue.Meter(10).AsExpression()
);

Test(exp, ResultTypes.VolumeNumber);
}

[Fact]
public void TestMulNumberAndTime()
{
Expand Down
39 changes: 39 additions & 0 deletions xFunc.Tests/Expressions/MulTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,45 @@ public void MulLengthAndNumber()
Assert.Equal(expected, actual);
}

[Fact]
public void MulLengthAndLength()
{
var exp = new Mul(
LengthValue.Meter(10).AsExpression(),
LengthValue.Meter(2).AsExpression()
);
var actual = exp.Execute();
var expected = AreaValue.Meter(20);

Assert.Equal(expected, actual);
}

[Fact]
public void MulAreaAndLength()
{
var exp = new Mul(
AreaValue.Meter(10).AsExpression(),
LengthValue.Meter(2).AsExpression()
);
var actual = exp.Execute();
var expected = VolumeValue.Meter(20);

Assert.Equal(expected, actual);
}

[Fact]
public void MulLengthAndArea()
{
var exp = new Mul(
LengthValue.Meter(10).AsExpression(),
AreaValue.Meter(2).AsExpression()
);
var actual = exp.Execute();
var expected = VolumeValue.Meter(20);

Assert.Equal(expected, actual);
}

[Fact]
public void MulNumberAndTime()
{
Expand Down
33 changes: 33 additions & 0 deletions xFunc.Tests/Expressions/Units/LengthUnits/LengthValueTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,39 @@ public void SubOperatorTest()
Assert.Equal(expected, result);
}

[Fact]
public void MulOperatorTest()
{
var length1 = LengthValue.Kilometer(2);
var length2 = LengthValue.Meter(2000);
var expected = AreaValue.Kilometer(4);
var result = length1 * length2;

Assert.Equal(expected, result);
}

[Fact]
public void MulOperatorAreaLengthTest()
{
var length1 = AreaValue.Centimeter(20000);
var length2 = LengthValue.Meter(2);
var expected = VolumeValue.Centimeter(40000);
var result = length1 * length2;

Assert.Equal(expected, result);
}

[Fact]
public void MulOperatorLengthAreaTest()
{
var length1 = LengthValue.Meter(2);
var length2 = AreaValue.Centimeter(20000);
var expected = VolumeValue.Centimeter(40000);
var result = length1 * length2;

Assert.Equal(expected, result);
}

public static IEnumerable<object[]> GetConversionTestCases()
{
yield return new object[] { 10.0, LengthUnit.Meter, LengthUnit.Meter, 10.0 };
Expand Down

0 comments on commit 5959360

Please sign in to comment.