diff --git a/xFunc.Maths/Analyzers/Analyzer{TResult,TContext}.cs b/xFunc.Maths/Analyzers/Analyzer{TResult,TContext}.cs index 9a19d642e..154023673 100644 --- a/xFunc.Maths/Analyzers/Analyzer{TResult,TContext}.cs +++ b/xFunc.Maths/Analyzers/Analyzer{TResult,TContext}.cs @@ -117,6 +117,10 @@ public virtual TResult Analyze(Power exp, TContext context) public virtual TResult Analyze(Temperature exp, TContext context) => Analyze(exp as IExpression, context); + /// + public virtual TResult Analyze(Mass exp, TContext context) + => Analyze(exp as IExpression, context); + /// public virtual TResult Analyze(ToDegree exp, TContext context) => Analyze(exp as IExpression, context); diff --git a/xFunc.Maths/Analyzers/Analyzer{TResult}.cs b/xFunc.Maths/Analyzers/Analyzer{TResult}.cs index eb0e84a7b..e7db78fd6 100644 --- a/xFunc.Maths/Analyzers/Analyzer{TResult}.cs +++ b/xFunc.Maths/Analyzers/Analyzer{TResult}.cs @@ -116,6 +116,10 @@ public virtual TResult Analyze(Power exp) public virtual TResult Analyze(Temperature exp) => Analyze(exp as IExpression); + /// + public virtual TResult Analyze(Mass exp) + => Analyze(exp as IExpression); + /// public virtual TResult Analyze(ToDegree exp) => Analyze(exp as IExpression); diff --git a/xFunc.Maths/Analyzers/Differentiator.cs b/xFunc.Maths/Analyzers/Differentiator.cs index 92fb3bca2..9a2e4e8a4 100644 --- a/xFunc.Maths/Analyzers/Differentiator.cs +++ b/xFunc.Maths/Analyzers/Differentiator.cs @@ -234,6 +234,14 @@ public override IExpression Analyze(Temperature exp, DifferentiatorContext conte return Number.Zero; } + /// + public override IExpression Analyze(Mass exp, DifferentiatorContext context) + { + ValidateArguments(exp, context); + + return Number.Zero; + } + /// public override IExpression Analyze(Pow exp, DifferentiatorContext context) { diff --git a/xFunc.Maths/Analyzers/Formatters/CommonFormatter.cs b/xFunc.Maths/Analyzers/Formatters/CommonFormatter.cs index d2c45fb94..7f830d159 100644 --- a/xFunc.Maths/Analyzers/Formatters/CommonFormatter.cs +++ b/xFunc.Maths/Analyzers/Formatters/CommonFormatter.cs @@ -161,6 +161,10 @@ public virtual string Analyze(Power exp) public virtual string Analyze(Temperature exp) => exp.Value.ToString(); + /// + public virtual string Analyze(Mass exp) + => exp.Value.ToString(); + /// public virtual string Analyze(ToDegree exp) => ToString(exp, "todegree({0})"); diff --git a/xFunc.Maths/Analyzers/IAnalyzer{TResult,TContext}.cs b/xFunc.Maths/Analyzers/IAnalyzer{TResult,TContext}.cs index 3679eb663..b6de4ce67 100644 --- a/xFunc.Maths/Analyzers/IAnalyzer{TResult,TContext}.cs +++ b/xFunc.Maths/Analyzers/IAnalyzer{TResult,TContext}.cs @@ -212,6 +212,14 @@ public interface IAnalyzer /// The result of analysis. TResult Analyze(Temperature exp, TContext context); + /// + /// Analyzes the specified expression. + /// + /// The expression. + /// The context. + /// The result of analysis. + TResult Analyze(Mass exp, TContext context); + /// /// Analyzes the specified expression. /// diff --git a/xFunc.Maths/Analyzers/IAnalyzer{TResult}.cs b/xFunc.Maths/Analyzers/IAnalyzer{TResult}.cs index e7bb120e0..053c3ce21 100644 --- a/xFunc.Maths/Analyzers/IAnalyzer{TResult}.cs +++ b/xFunc.Maths/Analyzers/IAnalyzer{TResult}.cs @@ -186,6 +186,13 @@ public interface IAnalyzer /// The result of analysis. TResult Analyze(Temperature exp); + /// + /// Analyzes the specified expression. + /// + /// The expression. + /// The result of analysis. + TResult Analyze(Mass exp); + /// /// Analyzes the specified expression. /// diff --git a/xFunc.Maths/Analyzers/TypeAnalyzers/ResultTypes.cs b/xFunc.Maths/Analyzers/TypeAnalyzers/ResultTypes.cs index 38b0cb6d7..93a565e45 100644 --- a/xFunc.Maths/Analyzers/TypeAnalyzers/ResultTypes.cs +++ b/xFunc.Maths/Analyzers/TypeAnalyzers/ResultTypes.cs @@ -71,6 +71,11 @@ public enum ResultTypes /// TemperatureNumber = 1 << 10, + /// + /// The expression returns a mass number. + /// + MassNumber = 1 << 11, + /// /// The expression returns a number or a complex number. /// @@ -96,7 +101,7 @@ public enum ResultTypes /// /// The expression returns any type of number. /// - Numbers = Number | AngleNumber | PowerNumber | TemperatureNumber, + Numbers = Number | AngleNumber | PowerNumber | TemperatureNumber | MassNumber, /// /// The expression returns a number or a angle number or a complex number or a vector. diff --git a/xFunc.Maths/Analyzers/TypeAnalyzers/TypeAnalyzer.cs b/xFunc.Maths/Analyzers/TypeAnalyzers/TypeAnalyzer.cs index 93425bc67..f2c26e7a5 100644 --- a/xFunc.Maths/Analyzers/TypeAnalyzers/TypeAnalyzer.cs +++ b/xFunc.Maths/Analyzers/TypeAnalyzers/TypeAnalyzer.cs @@ -309,6 +309,7 @@ ResultTypes.Number or ResultTypes.ComplexNumber or ResultTypes.Vector ResultTypes.AngleNumber => ResultTypes.AngleNumber, ResultTypes.PowerNumber => ResultTypes.PowerNumber, ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, _ => ResultTypes.NumbersOrComplexOrVector.ThrowFor(result), }; } @@ -345,6 +346,11 @@ public virtual ResultTypes Analyze(Add exp) (ResultTypes.TemperatureNumber, ResultTypes.TemperatureNumber) => ResultTypes.TemperatureNumber, + (ResultTypes.Number, ResultTypes.MassNumber) or + (ResultTypes.MassNumber, ResultTypes.Number) or + (ResultTypes.MassNumber, ResultTypes.MassNumber) + => ResultTypes.MassNumber, + (ResultTypes.Number, ResultTypes.ComplexNumber) or (ResultTypes.ComplexNumber, ResultTypes.Number) or (ResultTypes.ComplexNumber, ResultTypes.ComplexNumber) @@ -388,6 +394,7 @@ public virtual ResultTypes Analyze(Ceil exp) ResultTypes.AngleNumber => ResultTypes.AngleNumber, ResultTypes.PowerNumber => ResultTypes.PowerNumber, ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, _ => ResultTypes.Numbers.ThrowFor(result), }; } @@ -444,6 +451,9 @@ public virtual ResultTypes Analyze(Div exp) (ResultTypes.TemperatureNumber, ResultTypes.Number) => ResultTypes.TemperatureNumber, + (ResultTypes.MassNumber, ResultTypes.Number) + => ResultTypes.MassNumber, + (ResultTypes.Number, ResultTypes.ComplexNumber) or (ResultTypes.ComplexNumber, ResultTypes.Number) or (ResultTypes.ComplexNumber, ResultTypes.ComplexNumber) @@ -504,6 +514,7 @@ public virtual ResultTypes Analyze(Floor exp) ResultTypes.AngleNumber => ResultTypes.AngleNumber, ResultTypes.PowerNumber => ResultTypes.PowerNumber, ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, _ => ResultTypes.NumberOrAngle.ThrowFor(result), }; } @@ -523,6 +534,7 @@ public virtual ResultTypes Analyze(Trunc exp) ResultTypes.AngleNumber => ResultTypes.AngleNumber, ResultTypes.PowerNumber => ResultTypes.PowerNumber, ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, _ => ResultTypes.NumberOrAngle.ThrowFor(result), }; } @@ -542,6 +554,7 @@ public virtual ResultTypes Analyze(Frac exp) ResultTypes.AngleNumber => ResultTypes.AngleNumber, ResultTypes.PowerNumber => ResultTypes.PowerNumber, ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, _ => ResultTypes.NumberOrAngle.ThrowFor(result), }; } @@ -710,6 +723,10 @@ public virtual ResultTypes Analyze(Mul exp) (ResultTypes.TemperatureNumber, ResultTypes.Number) => ResultTypes.TemperatureNumber, + (ResultTypes.Number, ResultTypes.MassNumber) or + (ResultTypes.MassNumber, ResultTypes.Number) + => ResultTypes.MassNumber, + (ResultTypes.Number, ResultTypes.ComplexNumber) or (ResultTypes.ComplexNumber, ResultTypes.Number) or (ResultTypes.ComplexNumber, ResultTypes.ComplexNumber) @@ -759,6 +776,10 @@ public virtual ResultTypes Analyze(Power exp) public virtual ResultTypes Analyze(Temperature exp) => CheckArgument(exp, ResultTypes.TemperatureNumber); + /// + public virtual ResultTypes Analyze(Mass exp) + => CheckArgument(exp, ResultTypes.MassNumber); + /// public virtual ResultTypes Analyze(ToDegree exp) => AngleConversion(exp); @@ -849,15 +870,30 @@ public virtual ResultTypes Analyze(Round exp) if (exp is null) ArgNull(ExceptionArgument.exp); - var enumerator = exp.Arguments.GetEnumerator(); - for (var i = 0; enumerator.MoveNext(); i++) + var number = exp.Arguments[0].Analyze(this); + var digits = exp.Arguments[1]?.Analyze(this) ?? ResultTypes.None; + + if (digits is ResultTypes.None or ResultTypes.Undefined or ResultTypes.Number) { - var item = enumerator.Current.Analyze(this); - if (item != ResultTypes.Undefined && item != ResultTypes.Number) - throw new DifferentParameterTypeMismatchException(ResultTypes.Number, item, i); + return number switch + { + ResultTypes.Undefined => ResultTypes.Undefined, + ResultTypes.Number => ResultTypes.Number, + ResultTypes.AngleNumber => ResultTypes.AngleNumber, + ResultTypes.PowerNumber => ResultTypes.PowerNumber, + ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, + _ => throw new DifferentParameterTypeMismatchException( + ResultTypes.Undefined | ResultTypes.Numbers, + number, + 0), + }; } - return ResultTypes.Number; + throw new DifferentParameterTypeMismatchException( + ResultTypes.None | ResultTypes.Undefined | ResultTypes.Number, + digits, + 1); } /// @@ -900,6 +936,11 @@ public virtual ResultTypes Analyze(Sub exp) (ResultTypes.TemperatureNumber, ResultTypes.TemperatureNumber) => ResultTypes.TemperatureNumber, + (ResultTypes.Number, ResultTypes.MassNumber) or + (ResultTypes.MassNumber, ResultTypes.Number) or + (ResultTypes.MassNumber, ResultTypes.MassNumber) + => ResultTypes.MassNumber, + (ResultTypes.Number, ResultTypes.ComplexNumber) or (ResultTypes.ComplexNumber, ResultTypes.Number) or (ResultTypes.ComplexNumber, ResultTypes.ComplexNumber) @@ -939,6 +980,7 @@ public virtual ResultTypes Analyze(UnaryMinus exp) ResultTypes.AngleNumber => ResultTypes.AngleNumber, ResultTypes.PowerNumber => ResultTypes.PowerNumber, ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, ResultTypes.ComplexNumber => ResultTypes.ComplexNumber, _ => ResultTypes.NumberOrComplex.ThrowFor(result), }; @@ -971,10 +1013,12 @@ public virtual ResultTypes Analyze(Sign exp) return result switch { ResultTypes.Undefined => ResultTypes.Undefined, - ResultTypes.Number => ResultTypes.Number, - ResultTypes.AngleNumber => ResultTypes.Number, - ResultTypes.PowerNumber => ResultTypes.Number, - ResultTypes.TemperatureNumber => ResultTypes.Number, + ResultTypes.Number or + ResultTypes.AngleNumber or + ResultTypes.PowerNumber or + ResultTypes.TemperatureNumber or + ResultTypes.MassNumber => ResultTypes.Number, + _ => ResultTypes.Numbers.ThrowFor(result), }; } @@ -1010,6 +1054,7 @@ ResultTypes.Undefined or ResultTypes.AngleNumber => ResultTypes.AngleNumber, ResultTypes.PowerNumber => ResultTypes.PowerNumber, ResultTypes.TemperatureNumber => ResultTypes.TemperatureNumber, + ResultTypes.MassNumber => ResultTypes.MassNumber, _ => ResultTypes.Numbers.ThrowFor(valueResult), }; diff --git a/xFunc.Maths/Expressions/Abs.cs b/xFunc.Maths/Expressions/Abs.cs index 9cbe86225..93a7ddff8 100644 --- a/xFunc.Maths/Expressions/Abs.cs +++ b/xFunc.Maths/Expressions/Abs.cs @@ -43,6 +43,7 @@ public override object Execute(ExpressionParameters? parameters) AngleValue angle => AngleValue.Abs(angle), PowerValue power => PowerValue.Abs(power), TemperatureValue temperature => TemperatureValue.Abs(temperature), + MassValue mass => MassValue.Abs(mass), Complex complex => Complex.Abs(complex), Vector vector => vector.Abs(parameters), _ => throw new ResultIsNotSupportedException(this, result), diff --git a/xFunc.Maths/Expressions/Add.cs b/xFunc.Maths/Expressions/Add.cs index 3b97e8f81..5a86539a2 100644 --- a/xFunc.Maths/Expressions/Add.cs +++ b/xFunc.Maths/Expressions/Add.cs @@ -55,6 +55,10 @@ public override object Execute(ExpressionParameters? parameters) (TemperatureValue left, NumberValue right) => left + right, (TemperatureValue left, TemperatureValue right) => left + right, + (NumberValue left, MassValue right) => left + right, + (MassValue left, NumberValue right) => left + right, + (MassValue left, MassValue right) => left + right, + (NumberValue left, Complex right) => left + right, (Complex left, NumberValue right) => left + right, (Complex left, Complex right) => left + right, diff --git a/xFunc.Maths/Expressions/Ceil.cs b/xFunc.Maths/Expressions/Ceil.cs index 2c5842af2..d86490e54 100644 --- a/xFunc.Maths/Expressions/Ceil.cs +++ b/xFunc.Maths/Expressions/Ceil.cs @@ -41,6 +41,7 @@ public override object Execute(ExpressionParameters? parameters) AngleValue angle => AngleValue.Ceiling(angle), PowerValue power => PowerValue.Ceiling(power), TemperatureValue temperature => TemperatureValue.Ceiling(temperature), + MassValue mass => MassValue.Ceiling(mass), _ => throw new ResultIsNotSupportedException(this, result), }; } diff --git a/xFunc.Maths/Expressions/Collections/ParameterValue.cs b/xFunc.Maths/Expressions/Collections/ParameterValue.cs index 0ee1165b2..a0192f389 100644 --- a/xFunc.Maths/Expressions/Collections/ParameterValue.cs +++ b/xFunc.Maths/Expressions/Collections/ParameterValue.cs @@ -42,6 +42,12 @@ public ParameterValue(TemperatureValue value) { } + /// + public ParameterValue(MassValue value) + : this(value as object) + { + } + /// public ParameterValue(Complex value) : this(value as object) @@ -86,6 +92,7 @@ value is NumberValue or AngleValue or PowerValue or TemperatureValue + or MassValue or Complex or bool or Vector @@ -120,6 +127,10 @@ public static implicit operator ParameterValue(PowerValue value) public static implicit operator ParameterValue(TemperatureValue value) => new ParameterValue(value); + /// + public static implicit operator ParameterValue(MassValue value) + => new ParameterValue(value); + /// public static implicit operator ParameterValue(Complex value) => new ParameterValue(value); diff --git a/xFunc.Maths/Expressions/Div.cs b/xFunc.Maths/Expressions/Div.cs index cf6e48bb9..e906b8997 100644 --- a/xFunc.Maths/Expressions/Div.cs +++ b/xFunc.Maths/Expressions/Div.cs @@ -44,6 +44,7 @@ public override object Execute(ExpressionParameters? parameters) (AngleValue left, NumberValue right) => left / right, (PowerValue left, NumberValue right) => left / right, (TemperatureValue left, NumberValue right) => left / right, + (MassValue left, NumberValue right) => left / right, (NumberValue left, Complex right) => left / right, (Complex left, NumberValue right) => left / right, diff --git a/xFunc.Maths/Expressions/Floor.cs b/xFunc.Maths/Expressions/Floor.cs index 144b80054..cb102de0b 100644 --- a/xFunc.Maths/Expressions/Floor.cs +++ b/xFunc.Maths/Expressions/Floor.cs @@ -41,6 +41,7 @@ public override object Execute(ExpressionParameters? parameters) AngleValue angle => AngleValue.Floor(angle), PowerValue power => PowerValue.Floor(power), TemperatureValue temperature => TemperatureValue.Floor(temperature), + MassValue mass => MassValue.Floor(mass), _ => throw new ResultIsNotSupportedException(this, result), }; } diff --git a/xFunc.Maths/Expressions/Frac.cs b/xFunc.Maths/Expressions/Frac.cs index 6e8c6176a..e2dcc4e99 100644 --- a/xFunc.Maths/Expressions/Frac.cs +++ b/xFunc.Maths/Expressions/Frac.cs @@ -41,6 +41,7 @@ public override object Execute(ExpressionParameters? parameters) AngleValue angle => AngleValue.Frac(angle), PowerValue power => PowerValue.Frac(power), TemperatureValue temperature => TemperatureValue.Frac(temperature), + MassValue mass => MassValue.Frac(mass), _ => throw new ResultIsNotSupportedException(this, result), }; } diff --git a/xFunc.Maths/Expressions/Mul.cs b/xFunc.Maths/Expressions/Mul.cs index d38f3471b..4085ae6b0 100644 --- a/xFunc.Maths/Expressions/Mul.cs +++ b/xFunc.Maths/Expressions/Mul.cs @@ -51,6 +51,9 @@ public override object Execute(ExpressionParameters? parameters) (NumberValue left, TemperatureValue right) => left * right, (TemperatureValue left, NumberValue right) => left * right, + (NumberValue left, MassValue right) => left * right, + (MassValue left, NumberValue right) => left * right, + (NumberValue left, Complex right) => left * right, (Complex left, NumberValue right) => left * right, (Complex left, Complex right) => left * right, diff --git a/xFunc.Maths/Expressions/NumberValue.cs b/xFunc.Maths/Expressions/NumberValue.cs index cd5cf7998..b7b58c425 100644 --- a/xFunc.Maths/Expressions/NumberValue.cs +++ b/xFunc.Maths/Expressions/NumberValue.cs @@ -1,6 +1,7 @@ // Copyright (c) Dmytro Kyshchenko. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; using System.Runtime.CompilerServices; @@ -109,6 +110,7 @@ public override bool Equals(object? obj) => obj is NumberValue other && Equals(other); /// + [ExcludeFromCodeCoverage] public override int GetHashCode() => HashCode.Combine(Number); @@ -426,6 +428,26 @@ public override string ToString() public static TemperatureValue operator +(NumberValue left, TemperatureValue right) => new TemperatureValue(left.Number + right.Value, right.Unit); + /// + /// Adds and . + /// + /// The first object to add. + /// The second object to add. + /// An object that is the sum of and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MassValue operator +(MassValue left, NumberValue right) + => new MassValue(left.Value + right.Number, left.Unit); + + /// + /// Adds and . + /// + /// The first object to add. + /// The second object to add. + /// An object that is the sum of and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MassValue operator +(NumberValue left, MassValue right) + => new MassValue(left.Number + right.Value, right.Unit); + /// /// Adds and . /// @@ -536,6 +558,26 @@ public override string ToString() public static TemperatureValue operator -(TemperatureValue left, NumberValue right) => new TemperatureValue(left.Value - right.Number, left.Unit); + /// + /// Subtracts and . + /// + /// The first object to sub. + /// The second object to sub. + /// An object that is the difference of and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MassValue operator -(NumberValue left, MassValue right) + => new MassValue(left.Number - right.Value, right.Unit); + + /// + /// Subtracts and . + /// + /// The first object to sub. + /// The second object to sub. + /// An object that is the difference of and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MassValue operator -(MassValue left, NumberValue right) + => new MassValue(left.Value - right.Number, left.Unit); + /// /// Subtracts and . /// @@ -646,6 +688,26 @@ public override string ToString() public static TemperatureValue operator *(NumberValue left, TemperatureValue right) => new TemperatureValue(left.Number * right.Value, right.Unit); + /// + /// Multiplies and . + /// + /// The first object to multiply. + /// The second object to multiply. + /// An object that is the product of and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MassValue operator *(MassValue left, NumberValue right) + => new MassValue(left.Value * right.Number, left.Unit); + + /// + /// Multiplies and . + /// + /// The first object to multiply. + /// The second object to multiply. + /// An object that is the product of and . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MassValue operator *(NumberValue left, MassValue right) + => new MassValue(left.Number * right.Value, right.Unit); + /// /// Multiplies and . /// @@ -706,16 +768,6 @@ public override string ToString() public static AngleValue operator /(AngleValue left, NumberValue right) => new AngleValue(left.Angle / right.Number, left.Unit); - /// - /// Divides by . - /// - /// The first object to divide. - /// The second object to divide. - /// An object that is the fraction of and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static AngleValue operator /(NumberValue left, AngleValue right) - => new AngleValue(left.Number / right.Angle, right.Unit); - /// /// Divides by . /// @@ -726,16 +778,6 @@ public override string ToString() public static PowerValue operator /(PowerValue left, NumberValue right) => new PowerValue(left.Value / right.Number, left.Unit); - /// - /// Divides by . - /// - /// The first object to divide. - /// The second object to divide. - /// An object that is the fraction of and . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static PowerValue operator /(NumberValue left, PowerValue right) - => new PowerValue(left.Number / right.Value, right.Unit); - /// /// Divides by . /// @@ -747,14 +789,14 @@ public override string ToString() => new TemperatureValue(left.Value / right.Number, left.Unit); /// - /// Divides by . + /// Divides by . /// /// The first object to divide. /// The second object to divide. /// An object that is the fraction of and . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static TemperatureValue operator /(NumberValue left, TemperatureValue right) - => new TemperatureValue(left.Number / right.Value, right.Unit); + public static MassValue operator /(MassValue left, NumberValue right) + => new MassValue(left.Value / right.Number, left.Unit); /// /// Divides by . diff --git a/xFunc.Maths/Expressions/Round.cs b/xFunc.Maths/Expressions/Round.cs index 91418d1ad..25bd16b71 100644 --- a/xFunc.Maths/Expressions/Round.cs +++ b/xFunc.Maths/Expressions/Round.cs @@ -49,6 +49,10 @@ public override object Execute(ExpressionParameters? parameters) return (result, digits) switch { (NumberValue left, NumberValue right) => NumberValue.Round(left, right), + (AngleValue left, NumberValue right) => AngleValue.Round(left, right), + (PowerValue left, NumberValue right) => PowerValue.Round(left, right), + (TemperatureValue left, NumberValue right) => TemperatureValue.Round(left, right), + (MassValue left, NumberValue right) => MassValue.Round(left, right), _ => throw new ResultIsNotSupportedException(this, result, digits), }; } diff --git a/xFunc.Maths/Expressions/Sign.cs b/xFunc.Maths/Expressions/Sign.cs index 038dc51d2..11d2d20ff 100644 --- a/xFunc.Maths/Expressions/Sign.cs +++ b/xFunc.Maths/Expressions/Sign.cs @@ -42,6 +42,7 @@ public override object Execute(ExpressionParameters? parameters) AngleValue angle => angle.Sign, PowerValue power => power.Sign, TemperatureValue temperature => temperature.Sign, + MassValue mass => mass.Sign, _ => throw new ResultIsNotSupportedException(this, result), }; diff --git a/xFunc.Maths/Expressions/Sub.cs b/xFunc.Maths/Expressions/Sub.cs index 0acdceed3..6884fa9f3 100644 --- a/xFunc.Maths/Expressions/Sub.cs +++ b/xFunc.Maths/Expressions/Sub.cs @@ -53,6 +53,10 @@ public override object Execute(ExpressionParameters? parameters) (TemperatureValue left, NumberValue right) => left - right, (TemperatureValue left, TemperatureValue right) => left - right, + (NumberValue left, MassValue right) => left - right, + (MassValue left, NumberValue right) => left - right, + (MassValue left, MassValue right) => left - right, + (NumberValue left, Complex right) => left - right, (Complex left, NumberValue right) => left - right, (Complex left, Complex right) => left - right, diff --git a/xFunc.Maths/Expressions/Trunc.cs b/xFunc.Maths/Expressions/Trunc.cs index 051b2bfa4..1b676d920 100644 --- a/xFunc.Maths/Expressions/Trunc.cs +++ b/xFunc.Maths/Expressions/Trunc.cs @@ -41,6 +41,7 @@ public override object Execute(ExpressionParameters? parameters) AngleValue angle => AngleValue.Truncate(angle), PowerValue power => PowerValue.Truncate(power), TemperatureValue temperature => TemperatureValue.Truncate(temperature), + MassValue mass => MassValue.Truncate(mass), _ => throw new ResultIsNotSupportedException(this, result), }; } diff --git a/xFunc.Maths/Expressions/UnaryMinus.cs b/xFunc.Maths/Expressions/UnaryMinus.cs index e407780f0..ec43ea747 100644 --- a/xFunc.Maths/Expressions/UnaryMinus.cs +++ b/xFunc.Maths/Expressions/UnaryMinus.cs @@ -30,6 +30,7 @@ public override object Execute(ExpressionParameters? parameters) AngleValue angle => -angle, PowerValue power => -power, TemperatureValue temperature => -temperature, + MassValue mass => -mass, Complex complex => Complex.Negate(complex), _ => throw new ResultIsNotSupportedException(this, result), }; diff --git a/xFunc.Maths/Expressions/Units/AngleUnits/AngleValue.cs b/xFunc.Maths/Expressions/Units/AngleUnits/AngleValue.cs index d36a56d3f..5543c2349 100644 --- a/xFunc.Maths/Expressions/Units/AngleUnits/AngleValue.cs +++ b/xFunc.Maths/Expressions/Units/AngleUnits/AngleValue.cs @@ -198,32 +198,6 @@ public override int GetHashCode() public static AngleValue operator -(AngleValue angleValue) => new AngleValue(-angleValue.Angle, angleValue.Unit); - /// - /// Multiplies two objects of . - /// - /// The first object to multiply. - /// The second object to multiply. - /// An object that is the product of and . - public static AngleValue operator *(AngleValue left, AngleValue right) - { - (left, right) = ToCommonUnits(left, right); - - return new AngleValue(left.Angle * right.Angle, left.Unit); - } - - /// - /// Divides two objects of . - /// - /// The first object to divide. - /// The second object to divide. - /// An object that is the fraction of and . - public static AngleValue operator /(AngleValue left, AngleValue right) - { - (left, right) = ToCommonUnits(left, right); - - return new AngleValue(left.Angle / right.Angle, left.Unit); - } - private static (AngleValue Left, AngleValue Right) ToCommonUnits(AngleValue left, AngleValue right) { var commonUnit = GetCommonUnit(left.Unit, right.Unit); @@ -253,7 +227,7 @@ private static AngleUnit GetCommonUnit(AngleUnit left, AngleUnit right) AngleUnit.Degree => ToDegree(), AngleUnit.Radian => ToRadian(), AngleUnit.Gradian => ToGradian(), - _ => throw new ArgumentOutOfRangeException(nameof(unit)), + _ => throw new ArgumentOutOfRangeException(nameof(unit), unit, null), }; /// @@ -360,6 +334,16 @@ public static AngleValue Truncate(AngleValue angleValue) public static AngleValue Frac(AngleValue angleValue) => new AngleValue(NumberValue.Frac(angleValue.Angle), angleValue.Unit); + /// + /// Rounds a double-precision floating-point value to a specified number of fractional digits, + /// and uses the specified rounding convention for midpoint values. + /// + /// The angle number. + /// The number of fractional digits in the return value. + /// The number nearest to that has a number of fractional digits equal to . If value has fewer fractional digits than , is returned unchanged. + public static AngleValue Round(AngleValue angleValue, NumberValue digits) + => new AngleValue(NumberValue.Round(angleValue.Angle, digits), angleValue.Unit); + /// /// The 'sin' function. /// diff --git a/xFunc.Maths/Expressions/Units/Converters/Converter.cs b/xFunc.Maths/Expressions/Units/Converters/Converter.cs index ff544c53a..0ab5f95d9 100644 --- a/xFunc.Maths/Expressions/Units/Converters/Converter.cs +++ b/xFunc.Maths/Expressions/Units/Converters/Converter.cs @@ -19,6 +19,7 @@ public Converter() new AngleConverter(), new PowerConverter(), new TemperatureConverter(), + new MassConverter(), }; /// diff --git a/xFunc.Maths/Expressions/Units/Converters/MassConverter.cs b/xFunc.Maths/Expressions/Units/Converters/MassConverter.cs new file mode 100644 index 000000000..6c20428e4 --- /dev/null +++ b/xFunc.Maths/Expressions/Units/Converters/MassConverter.cs @@ -0,0 +1,52 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Maths.Expressions.Units.Converters; + +/// +/// The mass unit converter. +/// +public class MassConverter : IConverter, IConverter +{ + private readonly HashSet units = new HashSet + { + "mg", "g", "kg", "t", "oz", "lb", + }; + + /// + public MassValue Convert(object value, string unit) + { + if (value is null) + throw new ArgumentNullException(nameof(value)); + if (string.IsNullOrWhiteSpace(unit)) + throw new ArgumentNullException(nameof(unit)); + + return (value, unit) switch + { + (MassValue massValue, "mg") => massValue.ToMilligram(), + (MassValue massValue, "g") => massValue.ToGram(), + (MassValue massValue, "kg") => massValue.ToKilogram(), + (MassValue massValue, "t") => massValue.ToTonne(), + (MassValue massValue, "oz") => massValue.ToOunce(), + (MassValue massValue, "lb") => massValue.ToPound(), + + (NumberValue numberValue, "mg") => MassValue.Milligram(numberValue), + (NumberValue numberValue, "g") => MassValue.Gram(numberValue), + (NumberValue numberValue, "kg") => MassValue.Kilogram(numberValue), + (NumberValue numberValue, "t") => MassValue.Tonne(numberValue), + (NumberValue numberValue, "oz") => MassValue.Ounce(numberValue), + (NumberValue numberValue, "lb") => MassValue.Pound(numberValue), + + _ when CanConvertTo(unit) => throw new ValueIsNotSupportedException(value), + _ => throw new UnitIsNotSupportedException(unit), + }; + } + + /// + object IConverter.Convert(object value, string unit) + => Convert(value, unit); + + /// + public bool CanConvertTo(string unit) + => units.Contains(unit.ToLower()); +} \ No newline at end of file diff --git a/xFunc.Maths/Expressions/Units/MassUnits/Mass.cs b/xFunc.Maths/Expressions/Units/MassUnits/Mass.cs new file mode 100644 index 000000000..8dba821a0 --- /dev/null +++ b/xFunc.Maths/Expressions/Units/MassUnits/Mass.cs @@ -0,0 +1,29 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Maths.Expressions.Units.MassUnits; + +/// +/// Represents a mass number. +/// +public class Mass : Unit +{ + /// + /// Initializes a new instance of the class. + /// + /// A mass value. + public Mass(MassValue value) + : base(value) + { + } + + /// + protected override TResult AnalyzeInternal(IAnalyzer analyzer) + => analyzer.Analyze(this); + + /// + protected override TResult AnalyzeInternal( + IAnalyzer analyzer, + TContext context) + => analyzer.Analyze(this, context); +} \ No newline at end of file diff --git a/xFunc.Maths/Expressions/Units/MassUnits/MassUnit.cs b/xFunc.Maths/Expressions/Units/MassUnits/MassUnit.cs new file mode 100644 index 000000000..cf4334c96 --- /dev/null +++ b/xFunc.Maths/Expressions/Units/MassUnits/MassUnit.cs @@ -0,0 +1,93 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +namespace xFunc.Maths.Expressions.Units.MassUnits; + +/// +/// Represents a mass unit. +/// +public readonly struct MassUnit : IEquatable +{ + /// + /// The kilogram (kg) unit. + /// + public static readonly MassUnit Kilogram = new MassUnit(1.0, "kg"); + + /// + /// The milligram (mg) unit. + /// + public static readonly MassUnit Milligram = new MassUnit(0.000001, "mg"); + + /// + /// The gram (g) unit. + /// + public static readonly MassUnit Gram = new MassUnit(0.001, "g"); + + /// + /// The tonne (t) unit. + /// + public static readonly MassUnit Tonne = new MassUnit(1000, "t"); + + /// + /// The ounce (oz) unit. + /// + public static readonly MassUnit Ounce = new MassUnit(0.0283495231, "oz"); + + /// + /// The pound (lb) unit. + /// + public static readonly MassUnit Pound = new MassUnit(0.45359237, "lb"); + + private MassUnit(double factor, string unitName) + { + Factor = factor; + UnitName = unitName; + } + + /// + /// Determines whether two specified instances of are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if is equal to ; otherwise, false. + public static bool operator ==(MassUnit left, MassUnit right) + => left.Equals(right); + + /// + /// Determines whether two specified instances of are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if is equal to ; otherwise, false. + public static bool operator !=(MassUnit left, MassUnit right) + => !left.Equals(right); + + /// + public bool Equals(MassUnit other) + => Factor.Equals(other.Factor) && UnitName == other.UnitName; + + /// + public override bool Equals(object? obj) + => obj is MassUnit other && Equals(other); + + /// + [ExcludeFromCodeCoverage] + public override int GetHashCode() + => HashCode.Combine(Factor, UnitName); + + /// + public override string ToString() + => UnitName; + + /// + /// Gets a factor of conversion from this unit to base unit. + /// + public double Factor { get; } + + /// + /// Gets a short name of the unit. + /// + public string UnitName { get; } +} \ No newline at end of file diff --git a/xFunc.Maths/Expressions/Units/MassUnits/MassValue.cs b/xFunc.Maths/Expressions/Units/MassUnits/MassValue.cs new file mode 100644 index 000000000..947e35b12 --- /dev/null +++ b/xFunc.Maths/Expressions/Units/MassUnits/MassValue.cs @@ -0,0 +1,377 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +namespace xFunc.Maths.Expressions.Units.MassUnits; + +/// +/// Represents a number with unit. +/// +public readonly struct MassValue : IEquatable, IComparable, IComparable +{ + /// + /// Initializes a new instance of the struct. + /// + /// The value. + /// The unit of number. + public MassValue(NumberValue value, MassUnit unit) + { + Value = value; + Unit = unit; + } + + /// + /// Creates the struct with Kilogram unit. + /// + /// The value. + /// The mass value. + public static MassValue Kilogram(double value) + => Kilogram(new NumberValue(value)); + + /// + /// Creates the struct with Kilogram unit. + /// + /// The value. + /// The mass value. + public static MassValue Kilogram(NumberValue value) + => new MassValue(value, MassUnit.Kilogram); + + /// + /// Creates the struct with Milligram unit. + /// + /// The value. + /// The mass value. + public static MassValue Milligram(double value) + => Milligram(new NumberValue(value)); + + /// + /// Creates the struct with Milligram unit. + /// + /// The value. + /// The mass value. + public static MassValue Milligram(NumberValue value) + => new MassValue(value, MassUnit.Milligram); + + /// + /// Creates the struct with Gram unit. + /// + /// The value. + /// The mass value. + public static MassValue Gram(double value) + => Gram(new NumberValue(value)); + + /// + /// Creates the struct with Gram unit. + /// + /// The value. + /// The mass value. + public static MassValue Gram(NumberValue value) + => new MassValue(value, MassUnit.Gram); + + /// + /// Creates the struct with Tonne unit. + /// + /// The value. + /// The mass value. + public static MassValue Tonne(double value) + => Tonne(new NumberValue(value)); + + /// + /// Creates the struct with Tonne unit. + /// + /// The value. + /// The mass value. + public static MassValue Tonne(NumberValue value) + => new MassValue(value, MassUnit.Tonne); + + /// + /// Creates the struct with Ounce unit. + /// + /// The value. + /// The mass value. + public static MassValue Ounce(double value) + => Ounce(new NumberValue(value)); + + /// + /// Creates the struct with Ounce unit. + /// + /// The value. + /// The mass value. + public static MassValue Ounce(NumberValue value) + => new MassValue(value, MassUnit.Ounce); + + /// + /// Creates the struct with Pound unit. + /// + /// The value. + /// The mass value. + public static MassValue Pound(double value) + => Pound(new NumberValue(value)); + + /// + /// Creates the struct with Pound unit. + /// + /// The value. + /// The mass value. + public static MassValue Pound(NumberValue value) + => new MassValue(value, MassUnit.Pound); + + /// + public bool Equals(MassValue other) + { + var inBase = ToBase(); + var otherInBase = other.ToBase(); + + return inBase.Value.Equals(otherInBase.Value); + } + + /// + public override bool Equals(object? obj) + => obj is MassValue other && Equals(other); + + /// + public int CompareTo(MassValue other) + { + var inBase = ToBase(); + var otherInBase = other.ToBase(); + + return inBase.Value.CompareTo(otherInBase.Value); + } + + /// + public int CompareTo(object obj) + => obj switch + { + null => 1, + MassValue other => CompareTo(other), + _ => throw new ArgumentException($"Object must be of type {nameof(MassValue)}"), + }; + + /// + [ExcludeFromCodeCoverage] + public override int GetHashCode() + => HashCode.Combine(Value, Unit); + + /// + public override string ToString() + => $"{Value} {Unit.UnitName}"; + + /// + /// Determines whether two specified instances of are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if is equal to ; otherwise, false. + public static bool operator ==(MassValue left, MassValue right) + => left.Equals(right); + + /// + /// Determines whether two specified instances of are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// true if is equal to ; otherwise, false. + public static bool operator !=(MassValue left, MassValue right) + => !(left == right); + + /// + /// Indicates whether parameter is less than the parameter. + /// + /// The left value. + /// The right value. + /// true if the parameter is less than the parameter; otherwise, false. + public static bool operator <(MassValue left, MassValue right) + => left.CompareTo(right) < 0; + + /// + /// Indicates whether parameter is greater than the parameter. + /// + /// The left value. + /// The right value. + /// true if the parameter is greater than the parameter; otherwise, false. + public static bool operator >(MassValue left, MassValue right) + => left.CompareTo(right) > 0; + + /// + /// Indicates whether parameter is less than or equal to the parameter. + /// + /// The left value. + /// The right value. + /// true if the parameter is less than or equal to the parameter; otherwise, false. + public static bool operator <=(MassValue left, MassValue right) + => left.CompareTo(right) <= 0; + + /// + /// Indicates whether parameter is greater than or equal to the parameter. + /// + /// The left value. + /// The right value. + /// true if the parameter is greater than or equal to the parameter; otherwise, false. + public static bool operator >=(MassValue left, MassValue right) + => left.CompareTo(right) >= 0; + + /// + /// Adds two objects of . + /// + /// The first object to add. + /// The second object to add. + /// An object that is the sum of and . + public static MassValue operator +(MassValue left, MassValue right) + { + right = right.To(left.Unit); + + return new MassValue(left.Value + right.Value, left.Unit); + } + + /// + /// Subtracts two objects of . + /// + /// The first object to sub. + /// The second object to sub. + /// An object that is the difference of and . + public static MassValue operator -(MassValue left, MassValue right) + { + right = right.To(left.Unit); + + return new MassValue(left.Value - right.Value, left.Unit); + } + + /// + /// Produces the negative of . + /// + /// The value. + /// The negative of . + public static MassValue operator -(MassValue value) + => new MassValue(-value.Value, value.Unit); + + private MassValue ToBase() + => new MassValue(Value * Unit.Factor, MassUnit.Kilogram); + + /// + /// Convert to the unit. + /// + /// The new unit. + /// The value in the new unit. + public MassValue To(MassUnit newUnit) + { + var inBase = Value * Unit.Factor; + var converted = inBase / newUnit.Factor; + + return new MassValue(converted, newUnit); + } + + /// + /// Converts the value to the kilogram unit. + /// + /// The value in kilogram unit. + public MassValue ToKilogram() + => To(MassUnit.Kilogram); + + /// + /// Converts the value to the milligram unit. + /// + /// The value in milligram unit. + public MassValue ToMilligram() + => To(MassUnit.Milligram); + + /// + /// Converts the value to the gram unit. + /// + /// The value in gram unit. + public MassValue ToGram() + => To(MassUnit.Gram); + + /// + /// Converts the value to the tonne unit. + /// + /// The value in tonne unit. + public MassValue ToTonne() + => To(MassUnit.Tonne); + + /// + /// Converts the value to the ounce unit. + /// + /// The value in ounce unit. + public MassValue ToOunce() + => To(MassUnit.Ounce); + + /// + /// Converts the value to the pound unit. + /// + /// The value in pound unit. + public MassValue ToPound() + => To(MassUnit.Pound); + + /// + /// Returns the absolute value of a specified value. + /// + /// The value. + /// The power value, x, that such that 0 ≤ xMaxValue. + public static MassValue Abs(MassValue value) + => new MassValue(NumberValue.Abs(value.Value), value.Unit); + + /// + /// Returns the smallest integral value that is greater than or equal to the specified value. + /// + /// The value. + /// The smallest integral value. + public static MassValue Ceiling(MassValue value) + => new MassValue(NumberValue.Ceiling(value.Value), value.Unit); + + /// + /// Returns the largest integral value less than or equal to the specified value number. + /// + /// The value. + /// The largest integral value. + public static MassValue Floor(MassValue value) + => new MassValue(NumberValue.Floor(value.Value), value.Unit); + + /// + /// Calculates the integral part of a specified value number. + /// + /// An value to truncate. + /// The integral part of value number. + public static MassValue Truncate(MassValue value) + => new MassValue(NumberValue.Truncate(value.Value), value.Unit); + + /// + /// Returns the fractional part of the value number. + /// + /// The value number. + /// The fractional part. + public static MassValue Frac(MassValue value) + => new MassValue(NumberValue.Frac(value.Value), value.Unit); + + /// + /// Rounds a double-precision floating-point value to a specified number of fractional digits, + /// and uses the specified rounding convention for midpoint values. + /// + /// The number. + /// The number of fractional digits in the return value. + /// The number nearest to that has a number of fractional digits equal to . If value has fewer fractional digits than , is returned unchanged. + public static MassValue Round(MassValue massValue, NumberValue digits) + => new MassValue(NumberValue.Round(massValue.Value, digits), massValue.Unit); + + /// + /// Converts to . + /// + /// The mass number. + public Mass AsExpression() + => new Mass(this); + + /// + /// Gets a value. + /// + public NumberValue Value { get; } + + /// + /// Gets a unit. + /// + public MassUnit Unit { get; } + + /// + /// Gets an integer that indicates the sign of a double-precision floating-point number. + /// + public double Sign => Value.Sign; +} \ No newline at end of file diff --git a/xFunc.Maths/Expressions/Units/PowerUnits/Power.cs b/xFunc.Maths/Expressions/Units/PowerUnits/Power.cs index 0deb1873d..f11865ffa 100644 --- a/xFunc.Maths/Expressions/Units/PowerUnits/Power.cs +++ b/xFunc.Maths/Expressions/Units/PowerUnits/Power.cs @@ -4,7 +4,7 @@ namespace xFunc.Maths.Expressions.Units.PowerUnits; /// -/// Represents an power number. +/// Represents a power number. /// public class Power : Unit { diff --git a/xFunc.Maths/Expressions/Units/PowerUnits/PowerValue.cs b/xFunc.Maths/Expressions/Units/PowerUnits/PowerValue.cs index 7f22626dc..66a890ed0 100644 --- a/xFunc.Maths/Expressions/Units/PowerUnits/PowerValue.cs +++ b/xFunc.Maths/Expressions/Units/PowerUnits/PowerValue.cs @@ -198,32 +198,6 @@ public override int GetHashCode() public static PowerValue operator -(PowerValue value) => new PowerValue(-value.Value, value.Unit); - /// - /// Multiplies two objects of . - /// - /// The first object to multiply. - /// The second object to multiply. - /// An object that is the product of and . - public static PowerValue operator *(PowerValue left, PowerValue right) - { - (left, right) = ToCommonUnits(left, right); - - return new PowerValue(left.Value * right.Value, left.Unit); - } - - /// - /// Divides two objects of . - /// - /// The first object to divide. - /// The second object to divide. - /// An object that is the fraction of and . - public static PowerValue operator /(PowerValue left, PowerValue right) - { - (left, right) = ToCommonUnits(left, right); - - return new PowerValue(left.Value / right.Value, left.Unit); - } - private static (PowerValue Left, PowerValue Right) ToCommonUnits(PowerValue left, PowerValue right) { var commonUnit = GetCommonUnit(left.Unit, right.Unit); @@ -253,7 +227,7 @@ private static PowerUnit GetCommonUnit(PowerUnit left, PowerUnit right) PowerUnit.Watt => ToWatt(), PowerUnit.Kilowatt => ToKilowatt(), PowerUnit.Horsepower => ToHorsepower(), - _ => throw new ArgumentOutOfRangeException(nameof(unit)), + _ => throw new ArgumentOutOfRangeException(nameof(unit), unit, null), }; /// @@ -332,6 +306,16 @@ public static PowerValue Truncate(PowerValue value) public static PowerValue Frac(PowerValue value) => new PowerValue(NumberValue.Frac(value.Value), value.Unit); + /// + /// Rounds a double-precision floating-point value to a specified number of fractional digits, + /// and uses the specified rounding convention for midpoint values. + /// + /// The power number. + /// The number of fractional digits in the return value. + /// The number nearest to that has a number of fractional digits equal to . If value has fewer fractional digits than , is returned unchanged. + public static PowerValue Round(PowerValue powerValue, NumberValue digits) + => new PowerValue(NumberValue.Round(powerValue.Value, digits), powerValue.Unit); + /// /// Converts to . /// diff --git a/xFunc.Maths/Expressions/Units/TemperatureUnits/TemperatureValue.cs b/xFunc.Maths/Expressions/Units/TemperatureUnits/TemperatureValue.cs index f535193b1..d9b1e4aaa 100644 --- a/xFunc.Maths/Expressions/Units/TemperatureUnits/TemperatureValue.cs +++ b/xFunc.Maths/Expressions/Units/TemperatureUnits/TemperatureValue.cs @@ -201,32 +201,6 @@ public override int GetHashCode() public static TemperatureValue operator -(TemperatureValue value) => new TemperatureValue(-value.Value, value.Unit); - /// - /// Multiplies two objects of . - /// - /// The first object to multiply. - /// The second object to multiply. - /// An object that is the product of and . - public static TemperatureValue operator *(TemperatureValue left, TemperatureValue right) - { - (left, right) = ToCommonUnits(left, right); - - return new TemperatureValue(left.Value * right.Value, left.Unit); - } - - /// - /// Divides two objects of . - /// - /// The first object to divide. - /// The second object to divide. - /// An object that is the fraction of and . - public static TemperatureValue operator /(TemperatureValue left, TemperatureValue right) - { - (left, right) = ToCommonUnits(left, right); - - return new TemperatureValue(left.Value / right.Value, left.Unit); - } - private static (TemperatureValue Left, TemperatureValue Right) ToCommonUnits(TemperatureValue left, TemperatureValue right) { var commonUnit = GetCommonUnit(left.Unit, right.Unit); @@ -256,7 +230,7 @@ private static TemperatureUnit GetCommonUnit(TemperatureUnit left, TemperatureUn TemperatureUnit.Celsius => ToCelsius(), TemperatureUnit.Fahrenheit => ToFahrenheit(), TemperatureUnit.Kelvin => ToKelvin(), - _ => throw new ArgumentOutOfRangeException(nameof(unit)), + _ => throw new ArgumentOutOfRangeException(nameof(unit), unit, null), }; /// @@ -335,6 +309,16 @@ public static TemperatureValue Truncate(TemperatureValue value) public static TemperatureValue Frac(TemperatureValue value) => new TemperatureValue(NumberValue.Frac(value.Value), value.Unit); + /// + /// Rounds a double-precision floating-point value to a specified number of fractional digits, + /// and uses the specified rounding convention for midpoint values. + /// + /// The temperature number. + /// The number of fractional digits in the return value. + /// The number nearest to that has a number of fractional digits equal to . If value has fewer fractional digits than , is returned unchanged. + public static TemperatureValue Round(TemperatureValue temperatureValue, NumberValue digits) + => new TemperatureValue(NumberValue.Round(temperatureValue.Value, digits), temperatureValue.Unit); + /// /// Converts to . /// diff --git a/xFunc.Maths/Parser.cs b/xFunc.Maths/Parser.cs index 61cc39d49..723ab73ed 100644 --- a/xFunc.Maths/Parser.cs +++ b/xFunc.Maths/Parser.cs @@ -723,10 +723,15 @@ private ImmutableArray ParseParameterList(ref TokenReader tokenRead private IExpression? ParseNumber(ref TokenReader tokenReader) { + var mass = ParseMassUnit(ref tokenReader); + if (mass is not null) + return mass; + var number = tokenReader.GetCurrent(TokenKind.Number); if (number.IsEmpty()) return null; + // TODO: ! return ParseAngleUnit(ref tokenReader, ref number) ?? ParsePowerUnit(ref tokenReader, ref number) ?? ParseTemperatureUnit(ref tokenReader, ref number) ?? @@ -775,6 +780,28 @@ private ImmutableArray ParseParameterList(ref TokenReader tokenRead return null; } + private IExpression? ParseMassUnit(ref TokenReader tokenReader) + => tokenReader.Scoped(this, static (Parser _, ref TokenReader reader) => + { + // TODO: + var number = reader.GetCurrent(TokenKind.Number); + if (number.IsEmpty()) + return null; + + var id = reader.GetCurrent(Id); + + return id.StringValue switch + { + "mg" => MassValue.Milligram(number.NumberValue).AsExpression(), + "g" => MassValue.Gram(number.NumberValue).AsExpression(), + "kg" => MassValue.Kilogram(number.NumberValue).AsExpression(), + "t" => MassValue.Tonne(number.NumberValue).AsExpression(), + "oz" => MassValue.Ounce(number.NumberValue).AsExpression(), + "lb" => MassValue.Pound(number.NumberValue).AsExpression(), + _ => null, + }; + }); + private IExpression? ParsePolarComplexNumber(ref TokenReader tokenReader) => tokenReader.Scoped(this, static (Parser parser, ref TokenReader reader) => { diff --git a/xFunc.Maths/Processor.cs b/xFunc.Maths/Processor.cs index 3a62f54fc..333e81600 100644 --- a/xFunc.Maths/Processor.cs +++ b/xFunc.Maths/Processor.cs @@ -121,6 +121,11 @@ public IResult Solve(string function, bool simplify) return new TemperatureNumberResult(temperature); } + if (result is MassValue mass) + { + return new MassNumberResult(mass); + } + if (result is Complex complex) { return new ComplexNumberResult(complex); diff --git a/xFunc.Maths/Results/MassNumberResult.cs b/xFunc.Maths/Results/MassNumberResult.cs new file mode 100644 index 000000000..ab795df8f --- /dev/null +++ b/xFunc.Maths/Results/MassNumberResult.cs @@ -0,0 +1,25 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Maths.Results; + +/// +/// Represents a mass number result. +/// +public class MassNumberResult : IResult +{ + /// + /// Initializes a new instance of the class. + /// + /// The numerical representation of result. + public MassNumberResult(MassValue value) => Result = value; + + /// + public override string ToString() => Result.ToString(); + + /// + public MassValue Result { get; } + + /// + object IResult.Result => Result; +} \ No newline at end of file diff --git a/xFunc.Maths/xFunc.Maths.csproj b/xFunc.Maths/xFunc.Maths.csproj index 7286a2709..a42f213bb 100644 --- a/xFunc.Maths/xFunc.Maths.csproj +++ b/xFunc.Maths/xFunc.Maths.csproj @@ -55,6 +55,7 @@ https://github.com/sys27/xFunc/wiki/Breaking-Changes + diff --git a/xFunc.Tests/Analyzers/DifferentiatorTests/DifferentiatorTest.cs b/xFunc.Tests/Analyzers/DifferentiatorTests/DifferentiatorTest.cs index e3ecca31f..bfa98bfcc 100644 --- a/xFunc.Tests/Analyzers/DifferentiatorTests/DifferentiatorTest.cs +++ b/xFunc.Tests/Analyzers/DifferentiatorTests/DifferentiatorTest.cs @@ -58,6 +58,14 @@ public void TemperatureNumberTest() Assert.Equal(zero, exp); } + [Fact] + public void MassNumberTest() + { + var exp = Differentiate(MassValue.Kilogram(10).AsExpression()); + + Assert.Equal(zero, exp); + } + [Fact] public void VariableNullTest() { diff --git a/xFunc.Tests/Analyzers/Formatters/CommonFormatterTest.cs b/xFunc.Tests/Analyzers/Formatters/CommonFormatterTest.cs index 8246cff6d..0715ce436 100644 --- a/xFunc.Tests/Analyzers/Formatters/CommonFormatterTest.cs +++ b/xFunc.Tests/Analyzers/Formatters/CommonFormatterTest.cs @@ -284,6 +284,14 @@ public void TemperatureNumberTest() Assert.Equal("10 °C", exp.ToString()); } + [Fact] + public void MassNumberTest() + { + var exp = MassValue.Gram(10).AsExpression(); + + Assert.Equal("10 g", exp.ToString()); + } + [Fact] public void ToDegreeTest() { diff --git a/xFunc.Tests/Analyzers/TypeAnalyzerTests/AddTests.cs b/xFunc.Tests/Analyzers/TypeAnalyzerTests/AddTests.cs index 8ab7d4874..af842ca65 100644 --- a/xFunc.Tests/Analyzers/TypeAnalyzerTests/AddTests.cs +++ b/xFunc.Tests/Analyzers/TypeAnalyzerTests/AddTests.cs @@ -358,6 +358,39 @@ public void TestAddTemperatureTemperature() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestAddNumberAndMass() + { + var exp = new Add( + new Number(10), + MassValue.Gram(10).AsExpression() + ); + + Test(exp, ResultTypes.MassNumber); + } + + [Fact] + public void TestAddMassAndNumber() + { + var exp = new Add( + MassValue.Gram(10).AsExpression(), + new Number(10) + ); + + Test(exp, ResultTypes.MassNumber); + } + + [Fact] + public void TestAddMassAndMass() + { + var exp = new Add( + MassValue.Gram(10).AsExpression(), + MassValue.Gram(10).AsExpression() + ); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestAddStringToString() { diff --git a/xFunc.Tests/Analyzers/TypeAnalyzerTests/DivTests.cs b/xFunc.Tests/Analyzers/TypeAnalyzerTests/DivTests.cs index 50e461cf7..7836b2cce 100644 --- a/xFunc.Tests/Analyzers/TypeAnalyzerTests/DivTests.cs +++ b/xFunc.Tests/Analyzers/TypeAnalyzerTests/DivTests.cs @@ -141,4 +141,15 @@ public void TestDivTemperatureNumber() Test(exp, ResultTypes.TemperatureNumber); } + + [Fact] + public void TestDivMassNumber() + { + var exp = new Div( + MassValue.Gram(10).AsExpression(), + new Number(10) + ); + + Test(exp, ResultTypes.MassNumber); + } } \ No newline at end of file diff --git a/xFunc.Tests/Analyzers/TypeAnalyzerTests/MulTests.cs b/xFunc.Tests/Analyzers/TypeAnalyzerTests/MulTests.cs index b624e6a27..4ebade440 100644 --- a/xFunc.Tests/Analyzers/TypeAnalyzerTests/MulTests.cs +++ b/xFunc.Tests/Analyzers/TypeAnalyzerTests/MulTests.cs @@ -284,4 +284,26 @@ public void TestMulTemperatureNumber() Test(exp, ResultTypes.TemperatureNumber); } + + [Fact] + public void TestMulNumberAndMass() + { + var exp = new Mul( + new Number(10), + MassValue.Gram(10).AsExpression() + ); + + Test(exp, ResultTypes.MassNumber); + } + + [Fact] + public void TestMulMassAndNumber() + { + var exp = new Mul( + MassValue.Gram(10).AsExpression(), + new Number(10) + ); + + Test(exp, ResultTypes.MassNumber); + } } \ No newline at end of file diff --git a/xFunc.Tests/Analyzers/TypeAnalyzerTests/RoundTests.cs b/xFunc.Tests/Analyzers/TypeAnalyzerTests/RoundTests.cs new file mode 100644 index 000000000..2618d21c2 --- /dev/null +++ b/xFunc.Tests/Analyzers/TypeAnalyzerTests/RoundTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Tests.Analyzers.TypeAnalyzerTests; + +public class RoundTests : TypeAnalyzerBaseTests +{ + [Fact] + public void TestRoundVariable() + { + var exp = new Round(Variable.X, new Number(10)); + + Test(exp, ResultTypes.Undefined); + } + + [Fact] + public void TestRoundNumber() + { + var exp = new Round(new Number(10), new Number(10)); + + Test(exp, ResultTypes.Number); + } + + [Fact] + public void TestRoundAngleNumber() + { + var exp = new Round(AngleValue.Degree(10).AsExpression(), new Number(10)); + + Test(exp, ResultTypes.AngleNumber); + } + + [Fact] + public void TestRoundPowerNumber() + { + var exp = new Round(PowerValue.Watt(10).AsExpression(), new Number(10)); + + Test(exp, ResultTypes.PowerNumber); + } + + [Fact] + public void TestRoundTemperatureNumber() + { + var exp = new Round(TemperatureValue.Celsius(10).AsExpression(), new Number(10)); + + Test(exp, ResultTypes.TemperatureNumber); + } + + [Fact] + public void TestRoundMassNumber() + { + var exp = new Round(MassValue.Gram(10).AsExpression(), new Number(10)); + + Test(exp, ResultTypes.MassNumber); + } + + [Fact] + public void TestRoundWithUnsupportedPrecisionException() + { + var exp = new Round(new Number(10), new ComplexNumber(10)); + + TestDiffParamException(exp); + } + + [Fact] + public void TestRoundException() + { + var exp = new Round(new ComplexNumber(10), new Number(10)); + + TestDiffParamException(exp); + } +} \ No newline at end of file diff --git a/xFunc.Tests/Analyzers/TypeAnalyzerTests/StandardTests.cs b/xFunc.Tests/Analyzers/TypeAnalyzerTests/StandardTests.cs index 0eff1e701..d111d2542 100644 --- a/xFunc.Tests/Analyzers/TypeAnalyzerTests/StandardTests.cs +++ b/xFunc.Tests/Analyzers/TypeAnalyzerTests/StandardTests.cs @@ -39,6 +39,14 @@ public void TestAbsTemperatureNumber() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestAbsMassNumber() + { + var exp = new Abs(MassValue.Gram(1).AsExpression()); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestAbsComplexNumber() { @@ -103,6 +111,14 @@ public void TestCeilTemperature() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestCeilMass() + { + var exp = new Ceil(MassValue.Gram(5.5).AsExpression()); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestCeilVariable() { @@ -263,6 +279,14 @@ public void TestFloorTemperature() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestFloorMass() + { + var exp = new Floor(MassValue.Gram(5.5).AsExpression()); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestFloorException() { @@ -311,6 +335,14 @@ public void TestTruncTemperature() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestTruncMass() + { + var exp = new Trunc(MassValue.Gram(5.5).AsExpression()); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestTruncException() { @@ -359,6 +391,14 @@ public void TestFracTemperature() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestFracMass() + { + var exp = new Frac(MassValue.Gram(5.5).AsExpression()); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestFracException() { @@ -635,30 +675,6 @@ public void TestNumber() Test(Number.One, ResultTypes.Number); } - [Fact] - public void TestRoundVariable() - { - var exp = new Round(new Number(10), Variable.X); - - Test(exp, ResultTypes.Number); - } - - [Fact] - public void TestRoundNumber() - { - var exp = new Round(new Number(10), new Number(10)); - - Test(exp, ResultTypes.Number); - } - - [Fact] - public void TestRoundException() - { - var exp = new Round(new ComplexNumber(10), new Number(10)); - - TestDiffParamException(exp); - } - [Fact] public void TestSimplify() { @@ -712,6 +728,14 @@ public void TestUnaryMinusTemperature() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestUnaryMinusMass() + { + var exp = new UnaryMinus(MassValue.Gram(10).AsExpression()); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestUnaryMinusComplexNumber() { @@ -782,6 +806,12 @@ public void TestSignTemperature() Test(new Sign(TemperatureValue.Celsius(10).AsExpression()), ResultTypes.Number); } + [Fact] + public void TestSignMass() + { + Test(new Sign(MassValue.Gram(10).AsExpression()), ResultTypes.Number); + } + [Fact] public void TestSignException() { @@ -946,6 +976,18 @@ public void TestTemperatureConvert() Test(exp, ResultTypes.TemperatureNumber); } + [Fact] + public void TestMassConvert() + { + var exp = new Convert( + new Converter(), + MassValue.Kilogram(10).AsExpression(), + new StringExpression("g") + ); + + Test(exp, ResultTypes.MassNumber); + } + [Fact] public void TestConvertException() { diff --git a/xFunc.Tests/Analyzers/TypeAnalyzerTests/SubTests.cs b/xFunc.Tests/Analyzers/TypeAnalyzerTests/SubTests.cs index 990fdaf72..cfddb34b6 100644 --- a/xFunc.Tests/Analyzers/TypeAnalyzerTests/SubTests.cs +++ b/xFunc.Tests/Analyzers/TypeAnalyzerTests/SubTests.cs @@ -380,4 +380,37 @@ public void TestSubTemperatureTemperature() Test(exp, ResultTypes.TemperatureNumber); } + + [Fact] + public void TestSubNumberAndMass() + { + var exp = new Sub( + new Number(10), + MassValue.Gram(10).AsExpression() + ); + + Test(exp, ResultTypes.MassNumber); + } + + [Fact] + public void TestSubMassAndNumber() + { + var exp = new Sub( + MassValue.Gram(10).AsExpression(), + new Number(10) + ); + + Test(exp, ResultTypes.MassNumber); + } + + [Fact] + public void TestSubMassAndMass() + { + var exp = new Sub( + MassValue.Gram(10).AsExpression(), + MassValue.Gram(10).AsExpression() + ); + + Test(exp, ResultTypes.MassNumber); + } } \ No newline at end of file diff --git a/xFunc.Tests/Converters/MassTest.cs b/xFunc.Tests/Converters/MassTest.cs index 2c4d4d8c0..a958c6072 100644 --- a/xFunc.Tests/Converters/MassTest.cs +++ b/xFunc.Tests/Converters/MassTest.cs @@ -1,7 +1,8 @@ // Copyright (c) Dmytro Kyshchenko. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using xFunc.UnitConverters; +using MassConverter = xFunc.UnitConverters.MassConverter; +using MassUnits = xFunc.UnitConverters.MassUnits; namespace xFunc.Tests.Converters; diff --git a/xFunc.Tests/Expressions/AbsTest.cs b/xFunc.Tests/Expressions/AbsTest.cs index 83ad42498..9b1775971 100644 --- a/xFunc.Tests/Expressions/AbsTest.cs +++ b/xFunc.Tests/Expressions/AbsTest.cs @@ -44,6 +44,15 @@ public void ExecuteTestTemperatureValue() Assert.Equal(expected, exp.Execute()); } + [Fact] + public void ExecuteTestMassValue() + { + var exp = new Abs(MassValue.Gram(-1).AsExpression()); + var expected = MassValue.Gram(1); + + Assert.Equal(expected, exp.Execute()); + } + [Fact] public void ExecuteTestComplexNumber() { diff --git a/xFunc.Tests/Expressions/AddTest.cs b/xFunc.Tests/Expressions/AddTest.cs index 27db581f2..9c19e7ccc 100644 --- a/xFunc.Tests/Expressions/AddTest.cs +++ b/xFunc.Tests/Expressions/AddTest.cs @@ -241,6 +241,45 @@ public void ExecuteTemperatureAndTemperature() Assert.Equal(expected, actual); } + [Fact] + public void ExecuteNumberAndMass() + { + var exp = new Add( + Number.One, + MassValue.Gram(1).AsExpression() + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(2); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ExecuteMassAndNumber() + { + var exp = new Add( + MassValue.Gram(1).AsExpression(), + Number.One + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(2); + + Assert.Equal(expected, actual); + } + + [Fact] + public void ExecuteMassAndMass() + { + var exp = new Add( + MassValue.Gram(1).AsExpression(), + MassValue.Gram(2).AsExpression() + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(3); + + Assert.Equal(expected, actual); + } + [Fact] public void ExecuteStringAndString() { diff --git a/xFunc.Tests/Expressions/CeilTest.cs b/xFunc.Tests/Expressions/CeilTest.cs index bb105640b..c3e16c814 100644 --- a/xFunc.Tests/Expressions/CeilTest.cs +++ b/xFunc.Tests/Expressions/CeilTest.cs @@ -45,6 +45,17 @@ public void ExecuteTestTemperatureNumber() Assert.Equal(expected, result); } + [Fact] + public void ExecuteTestMassNumber() + { + var ceil = new Ceil(MassValue.Gram(5.55555555).AsExpression()); + var result = ceil.Execute(); + var expected = MassValue.Gram(6); + + Assert.Equal(expected, result); + } + + [Fact] public void ExecuteTestException() => TestNotSupported(new Ceil(Bool.False)); diff --git a/xFunc.Tests/Expressions/Collections/ParameterTest.cs b/xFunc.Tests/Expressions/Collections/ParameterTest.cs index 0b98ed7b5..955754f3c 100644 --- a/xFunc.Tests/Expressions/Collections/ParameterTest.cs +++ b/xFunc.Tests/Expressions/Collections/ParameterTest.cs @@ -54,6 +54,15 @@ public void TemperatureValueCtor() Assert.Equal(value, x.Value); } + [Fact] + public void MassValueCtor() + { + var value = MassValue.Gram(10); + var x = new Parameter("x", value); + + Assert.Equal(value, x.Value); + } + [Fact] public void ComplexCtor() { diff --git a/xFunc.Tests/Expressions/DivTest.cs b/xFunc.Tests/Expressions/DivTest.cs index b4488dcd3..0ec40c086 100644 --- a/xFunc.Tests/Expressions/DivTest.cs +++ b/xFunc.Tests/Expressions/DivTest.cs @@ -88,6 +88,19 @@ public void DivTemperatureAndNumber() Assert.Equal(expected, actual); } + [Fact] + public void DivMassAndNumber() + { + var exp = new Div( + MassValue.Gram(10).AsExpression(), + new Number(2) + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(5); + + Assert.Equal(expected, actual); + } + [Fact] public void ExecuteBoolTest() { diff --git a/xFunc.Tests/Expressions/FloorTest.cs b/xFunc.Tests/Expressions/FloorTest.cs index ae9b69ee2..b15091949 100644 --- a/xFunc.Tests/Expressions/FloorTest.cs +++ b/xFunc.Tests/Expressions/FloorTest.cs @@ -45,6 +45,16 @@ public void ExecuteTemperatureTest() Assert.Equal(expected, result); } + [Fact] + public void ExecuteMassTest() + { + var floor = new Floor(MassValue.Gram(5.55555555).AsExpression()); + var result = floor.Execute(); + var expected = MassValue.Gram(5); + + Assert.Equal(expected, result); + } + [Fact] public void ExecuteTestException() => TestNotSupported(new Floor(Bool.False)); diff --git a/xFunc.Tests/Expressions/FracTest.cs b/xFunc.Tests/Expressions/FracTest.cs index 2403b1ca8..7aebe74e9 100644 --- a/xFunc.Tests/Expressions/FracTest.cs +++ b/xFunc.Tests/Expressions/FracTest.cs @@ -85,6 +85,26 @@ public void ExecuteNegativeTemperatureTest() Assert.Equal(expected, result); } + [Fact] + public void ExecuteMassTest() + { + var exp = new Frac(MassValue.Gram(5.5).AsExpression()); + var result = exp.Execute(); + var expected = MassValue.Gram(0.5); + + Assert.Equal(expected, result); + } + + [Fact] + public void ExecuteNegativeMassTest() + { + var exp = new Frac(MassValue.Gram(-5.5).AsExpression()); + var result = exp.Execute(); + var expected = MassValue.Gram(-0.5); + + Assert.Equal(expected, result); + } + [Fact] public void ExecuteTestException() => TestNotSupported(new Frac(Bool.False)); diff --git a/xFunc.Tests/Expressions/MulTest.cs b/xFunc.Tests/Expressions/MulTest.cs index 821b38d11..8d93a9761 100644 --- a/xFunc.Tests/Expressions/MulTest.cs +++ b/xFunc.Tests/Expressions/MulTest.cs @@ -309,6 +309,32 @@ public void MulTemperatureAndNumber() Assert.Equal(expected, actual); } + [Fact] + public void MulNumberAndMass() + { + var exp = new Mul( + Number.Two, + MassValue.Gram(10).AsExpression() + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(20); + + Assert.Equal(expected, actual); + } + + [Fact] + public void MulMassAndNumber() + { + var exp = new Mul( + MassValue.Gram(10).AsExpression(), + Number.Two + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(20); + + Assert.Equal(expected, actual); + } + [Fact] public void ExecuteMulBoolByBoolTest() => TestNotSupported(new Mul(Bool.True, Bool.True)); diff --git a/xFunc.Tests/Expressions/RoundTest.cs b/xFunc.Tests/Expressions/RoundTest.cs index 185359bcc..7f6b892b4 100644 --- a/xFunc.Tests/Expressions/RoundTest.cs +++ b/xFunc.Tests/Expressions/RoundTest.cs @@ -25,6 +25,46 @@ public void CalculateRoundWithDigits() Assert.Equal(expected, result); } + [Fact] + public void RoundAngleWithDigits() + { + var round = new Round(AngleValue.Degree(5.555555).AsExpression(), Number.Two); + var result = round.Execute(); + var expected = AngleValue.Degree(5.56); + + Assert.Equal(expected, result); + } + + [Fact] + public void RoundPowerWithDigits() + { + var round = new Round(PowerValue.Watt(5.555555).AsExpression(), Number.Two); + var result = round.Execute(); + var expected = PowerValue.Watt(5.56); + + Assert.Equal(expected, result); + } + + [Fact] + public void RoundTemperatureWithDigits() + { + var round = new Round(TemperatureValue.Celsius(5.555555).AsExpression(), Number.Two); + var result = round.Execute(); + var expected = TemperatureValue.Celsius(5.56); + + Assert.Equal(expected, result); + } + + [Fact] + public void RoundMassWithDigits() + { + var round = new Round(MassValue.Gram(5.555555).AsExpression(), Number.Two); + var result = round.Execute(); + var expected = MassValue.Gram(5.56); + + Assert.Equal(expected, result); + } + [Fact] public void ExecuteArgumentIsNotNumber() { diff --git a/xFunc.Tests/Expressions/SignTest.cs b/xFunc.Tests/Expressions/SignTest.cs index f7878a07e..f69304ec6 100644 --- a/xFunc.Tests/Expressions/SignTest.cs +++ b/xFunc.Tests/Expressions/SignTest.cs @@ -50,6 +50,15 @@ public void TemperatureSignTest() Assert.Equal(new NumberValue(1.0), result); } + [Fact] + public void MassSignTest() + { + var exp = new Sign(MassValue.Gram(10).AsExpression()); + var result = exp.Execute(); + + Assert.Equal(new NumberValue(1.0), result); + } + [Fact] public void InvalidParameterTest() => TestNotSupported(new Sign(Bool.False)); diff --git a/xFunc.Tests/Expressions/SubTest.cs b/xFunc.Tests/Expressions/SubTest.cs index cd6f21613..f4485fd57 100644 --- a/xFunc.Tests/Expressions/SubTest.cs +++ b/xFunc.Tests/Expressions/SubTest.cs @@ -232,6 +232,45 @@ public void SubTemperatureAndTemperature() Assert.Equal(expected, actual); } + [Fact] + public void SubNumberAndMass() + { + var exp = new Sub( + Number.One, + MassValue.Gram(10).AsExpression() + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(-9); + + Assert.Equal(expected, actual); + } + + [Fact] + public void SubMassAndNumber() + { + var exp = new Sub( + MassValue.Gram(10).AsExpression(), + Number.One + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(9); + + Assert.Equal(expected, actual); + } + + [Fact] + public void SubMassAndMass() + { + var exp = new Sub( + MassValue.Gram(20).AsExpression(), + MassValue.Gram(10).AsExpression() + ); + var actual = exp.Execute(); + var expected = MassValue.Gram(10); + + Assert.Equal(expected, actual); + } + [Fact] public void ExecuteWrongArgumentTypeTest() => TestNotSupported(new Sub(Bool.True, Bool.True)); diff --git a/xFunc.Tests/Expressions/TruncTest.cs b/xFunc.Tests/Expressions/TruncTest.cs index 8fa522e02..856ecce39 100644 --- a/xFunc.Tests/Expressions/TruncTest.cs +++ b/xFunc.Tests/Expressions/TruncTest.cs @@ -45,6 +45,16 @@ public void ExecuteTemperatureTest() Assert.Equal(expected, result); } + [Fact] + public void ExecuteMassTest() + { + var exp = new Trunc(MassValue.Gram(5.55555555).AsExpression()); + var result = exp.Execute(); + var expected = MassValue.Gram(5); + + Assert.Equal(expected, result); + } + [Fact] public void ExecuteTestException() => TestNotSupported(new Trunc(Bool.False)); diff --git a/xFunc.Tests/Expressions/UnaryMinusTest.cs b/xFunc.Tests/Expressions/UnaryMinusTest.cs index 5fbba0711..7f7c0e27f 100644 --- a/xFunc.Tests/Expressions/UnaryMinusTest.cs +++ b/xFunc.Tests/Expressions/UnaryMinusTest.cs @@ -42,6 +42,15 @@ public void ExecuteTemperatureNumberTest() Assert.Equal(expected, exp.Execute()); } + [Fact] + public void ExecuteMassNumberTest() + { + var exp = new UnaryMinus(MassValue.Gram(10).AsExpression()); + var expected = MassValue.Gram(-10); + + Assert.Equal(expected, exp.Execute()); + } + [Fact] public void ExecuteComplexTest() { diff --git a/xFunc.Tests/Expressions/Units/Converters/MassConverterTests.cs b/xFunc.Tests/Expressions/Units/Converters/MassConverterTests.cs new file mode 100644 index 000000000..4147106bf --- /dev/null +++ b/xFunc.Tests/Expressions/Units/Converters/MassConverterTests.cs @@ -0,0 +1,73 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Tests.Expressions.Units.Converters; + +public class MassConverterTests +{ + [Theory] + [InlineData(null, null)] + [InlineData(1, null)] + public void ConvertNull(object value, string unit) + { + var converter = new MassConverter(); + + Assert.Throws(() => converter.Convert(value, unit)); + } + + public static IEnumerable GetConvertTestsData() + { + var mass = MassValue.Gram(10); + + yield return new object[] { mass, "mg", mass.ToMilligram() }; + yield return new object[] { mass, "g", mass.ToGram() }; + yield return new object[] { mass, "kg", mass.ToKilogram() }; + yield return new object[] { mass, "t", mass.ToTonne() }; + yield return new object[] { mass, "oz", mass.ToOunce() }; + yield return new object[] { mass, "lb", mass.ToPound() }; + + var number = new NumberValue(10); + + yield return new object[] { number, "mg", MassValue.Milligram(number) }; + yield return new object[] { number, "g", MassValue.Gram(number) }; + yield return new object[] { number, "kg", MassValue.Kilogram(number) }; + yield return new object[] { number, "t", MassValue.Tonne(number) }; + yield return new object[] { number, "oz", MassValue.Ounce(number) }; + yield return new object[] { number, "lb", MassValue.Pound(number) }; + } + + [Theory] + [MemberData(nameof(GetConvertTestsData))] + public void ConvertTests(object value, string unit, object expected) + { + var converter = new MassConverter(); + var result = converter.Convert(value, unit); + var resultAsObject = ((IConverter)converter).Convert(value, unit); + + Assert.Equal(expected, result); + Assert.Equal(expected, resultAsObject); + } + + public static IEnumerable GetConvertUnsupportedUnitData() + { + yield return new object[] { MassValue.Gram(10), "xxx" }; + yield return new object[] { new NumberValue(10), "xxx" }; + } + + [Theory] + [MemberData(nameof(GetConvertUnsupportedUnitData))] + public void ConvertUnsupportedUnit(object value, string unit) + { + var converter = new MassConverter(); + + Assert.Throws(() => converter.Convert(value, unit)); + } + + [Fact] + public void ConvertUnsupportedValue() + { + var converter = new MassConverter(); + + Assert.Throws(() => converter.Convert(1, "g")); + } +} \ No newline at end of file diff --git a/xFunc.Tests/Expressions/Units/MassUnits/MassTest.cs b/xFunc.Tests/Expressions/Units/MassUnits/MassTest.cs new file mode 100644 index 000000000..88b30c7f5 --- /dev/null +++ b/xFunc.Tests/Expressions/Units/MassUnits/MassTest.cs @@ -0,0 +1,82 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Tests.Expressions.Units.MassUnits; + +public class MassTest +{ + [Fact] + public void EqualNullTest() + { + var exp = MassValue.Gram(10).AsExpression(); + + Assert.False(exp.Equals(null)); + } + + [Fact] + public void EqualNullObjectTest() + { + var exp = MassValue.Gram(10).AsExpression(); + + Assert.False(exp.Equals((object)null)); + } + + [Fact] + public void EqualSameTest() + { + var exp = MassValue.Gram(10).AsExpression(); + + Assert.True(exp.Equals(exp)); + } + + [Fact] + public void EqualSameObjectTest() + { + var exp = MassValue.Gram(10).AsExpression(); + + Assert.True(exp.Equals((object)exp)); + } + + [Fact] + public void EqualDiffTypeTest() + { + var exp = MassValue.Gram(10).AsExpression(); + var number = Number.One; + + Assert.False(exp.Equals(number)); + } + + [Fact] + public void ExecuteTest() + { + var exp = MassValue.Gram(10).AsExpression(); + var expected = MassValue.Gram(10); + + Assert.Equal(expected, exp.Execute()); + } + + [Fact] + public void ExecuteTest2() + { + var exp = MassValue.Gram(10).AsExpression(); + var expected = MassValue.Gram(10); + + Assert.Equal(expected, exp.Execute(null)); + } + + [Fact] + public void NullAnalyzerTest1() + { + var exp = MassValue.Gram(10).AsExpression(); + + Assert.Throws(() => exp.Analyze(null)); + } + + [Fact] + public void NullAnalyzerTest2() + { + var exp = MassValue.Gram(10).AsExpression(); + + Assert.Throws(() => exp.Analyze(null, null)); + } +} \ No newline at end of file diff --git a/xFunc.Tests/Expressions/Units/MassUnits/MassUnitTest.cs b/xFunc.Tests/Expressions/Units/MassUnits/MassUnitTest.cs new file mode 100644 index 000000000..389126841 --- /dev/null +++ b/xFunc.Tests/Expressions/Units/MassUnits/MassUnitTest.cs @@ -0,0 +1,69 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Tests.Expressions.Units.MassUnits; + +public class MassUnitTest +{ + [Fact] + public void EqualsTest() + { + var a = MassUnit.Kilogram; + var b = MassUnit.Kilogram; + + Assert.True(a.Equals(b)); + } + + [Fact] + public void NotEqualsTest() + { + var a = MassUnit.Kilogram; + var b = MassUnit.Gram; + + Assert.False(a.Equals(b)); + } + + [Fact] + public void ObjectEqualsTest() + { + var a = MassUnit.Kilogram; + var b = MassUnit.Kilogram as object; + + Assert.True(a.Equals(b)); + } + + [Fact] + public void ObjectEqualsWithDifferentTypesTest() + { + var a = MassUnit.Kilogram; + var b = 1 as object; + + Assert.False(a.Equals(b)); + } + + [Fact] + public void EqualsOperatorTest() + { + var a = MassUnit.Kilogram; + var b = MassUnit.Kilogram; + + Assert.True(a == b); + } + + [Fact] + public void NotEqualsOperatorTest() + { + var a = MassUnit.Kilogram; + var b = MassUnit.Gram; + + Assert.True(a != b); + } + + [Fact] + public void ToStringTest() + { + var a = MassUnit.Kilogram; + + Assert.Equal("kg", a.ToString()); + } +} \ No newline at end of file diff --git a/xFunc.Tests/Expressions/Units/MassUnits/MassValueTest.cs b/xFunc.Tests/Expressions/Units/MassUnits/MassValueTest.cs new file mode 100644 index 000000000..6aa04b99e --- /dev/null +++ b/xFunc.Tests/Expressions/Units/MassUnits/MassValueTest.cs @@ -0,0 +1,278 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Tests.Expressions.Units.MassUnits; + +public class MassValueTest +{ + [Fact] + public void EqualTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(10); + + Assert.True(mass1.Equals(mass2)); + } + + [Fact] + public void EqualOperatorTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(10); + + Assert.True(mass1 == mass2); + } + + [Fact] + public void NotEqualTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(12); + + Assert.True(mass1 != mass2); + } + + [Fact] + public void LessTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(12); + + Assert.True(mass1 < mass2); + } + + [Fact] + public void LessFalseTest() + { + var mass1 = MassValue.Gram(20); + var mass2 = MassValue.Gram(12); + + Assert.False(mass1 < mass2); + } + + [Fact] + public void GreaterTest() + { + var mass1 = MassValue.Gram(20); + var mass2 = MassValue.Gram(12); + + Assert.True(mass1 > mass2); + } + + [Fact] + public void GreaterFalseTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(12); + + Assert.False(mass1 > mass2); + } + + [Fact] + public void LessOrEqualTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(10); + + Assert.True(mass1 <= mass2); + } + + [Fact] + public void GreaterOrEqualTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(10); + + Assert.True(mass1 >= mass2); + } + + [Fact] + public void CompareToNull() + { + var mass = MassValue.Gram(10); + + Assert.True(mass.CompareTo(null) > 0); + } + + [Fact] + public void CompareToObject() + { + var mass1 = MassValue.Gram(10); + var mass2 = (object)MassValue.Gram(10); + + Assert.True(mass1.CompareTo(mass2) == 0); + } + + [Fact] + public void CompareToDouble() + { + var mass = MassValue.Gram(10); + + Assert.Throws(() => mass.CompareTo(1)); + } + + [Fact] + public void ValueNotEqualTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(12); + + Assert.False(mass1.Equals(mass2)); + } + + [Fact] + public void UnitNotEqualTest2() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Kilogram(10); + + Assert.False(mass1.Equals(mass2)); + } + + [Fact] + public void EqualDiffTypeTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = 3; + + Assert.False(mass1.Equals(mass2)); + } + + [Fact] + public void EqualObjectTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(10); + + Assert.True(mass1.Equals(mass2 as object)); + } + + [Fact] + public void NotEqualObjectTest() + { + var mass1 = MassValue.Gram(10); + var mass2 = MassValue.Gram(20); + + Assert.False(mass1.Equals(mass2 as object)); + } + + [Fact] + public void ToStringMilligramTest() + { + var mass = MassValue.Milligram(10); + + Assert.Equal("10 mg", mass.ToString()); + } + + [Fact] + public void ToStringGramTest() + { + var mass = MassValue.Gram(10); + + Assert.Equal("10 g", mass.ToString()); + } + + [Fact] + public void ToStringKilogramTest() + { + var mass = MassValue.Kilogram(10); + + Assert.Equal("10 kg", mass.ToString()); + } + + [Fact] + public void ToStringTonneTest() + { + var mass = MassValue.Tonne(10); + + Assert.Equal("10 t", mass.ToString()); + } + + [Fact] + public void ToStringOunceTest() + { + var mass = MassValue.Ounce(10); + + Assert.Equal("10 oz", mass.ToString()); + } + + [Fact] + public void ToStringPoundTest() + { + var mass = MassValue.Pound(10); + + Assert.Equal("10 lb", mass.ToString()); + } + + [Fact] + public void AddOperatorTest() + { + var mass1 = MassValue.Gram(1); + var mass2 = MassValue.Kilogram(1); + var expected = MassValue.Gram(1001); + var result = mass1 + mass2; + + Assert.Equal(expected, result); + } + + [Fact] + public void SubOperatorTest() + { + var mass1 = MassValue.Kilogram(1); + var mass2 = MassValue.Gram(1); + var expected = MassValue.Kilogram(0.999); + var result = mass1 - mass2; + + Assert.Equal(expected, result); + } + + public static IEnumerable GetConversionTestCases() + { + yield return new object[] { 10.0, MassUnit.Milligram, MassUnit.Milligram, 10.0 }; + yield return new object[] { 1000.0, MassUnit.Milligram, MassUnit.Gram, 1.0 }; + yield return new object[] { 1000000.0, MassUnit.Milligram, MassUnit.Kilogram, 1.0 }; + yield return new object[] { 1000000000.0, MassUnit.Milligram, MassUnit.Tonne, 1.0 }; + yield return new object[] { 1000.0, MassUnit.Milligram, MassUnit.Ounce, 0.0352739619 }; + yield return new object[] { 1000.0, MassUnit.Milligram, MassUnit.Pound, 0.0022046226 }; + yield return new object[] { 10.0, MassUnit.Gram, MassUnit.Gram, 10.0 }; + yield return new object[] { 10.0, MassUnit.Gram, MassUnit.Milligram, 10000.0 }; + yield return new object[] { 10.0, MassUnit.Gram, MassUnit.Kilogram, 0.01 }; + yield return new object[] { 1000.0, MassUnit.Gram, MassUnit.Tonne, 0.001 }; + yield return new object[] { 10.0, MassUnit.Gram, MassUnit.Ounce, 0.35273962 }; + yield return new object[] { 10.0, MassUnit.Gram, MassUnit.Pound, 0.022046226 }; + yield return new object[] { 10.0, MassUnit.Kilogram, MassUnit.Kilogram, 10.0 }; + yield return new object[] { 10.0, MassUnit.Kilogram, MassUnit.Milligram, 10000000.0 }; + yield return new object[] { 10.0, MassUnit.Kilogram, MassUnit.Gram, 10000 }; + yield return new object[] { 10.0, MassUnit.Kilogram, MassUnit.Tonne, 0.01 }; + yield return new object[] { 10.0, MassUnit.Kilogram, MassUnit.Ounce, 352.739619807 }; + yield return new object[] { 10.0, MassUnit.Kilogram, MassUnit.Pound, 22.046226218 }; + yield return new object[] { 10.0, MassUnit.Tonne, MassUnit.Tonne, 10.0 }; + yield return new object[] { 10.0, MassUnit.Tonne, MassUnit.Milligram, 10000000000.0 }; + yield return new object[] { 10.0, MassUnit.Tonne, MassUnit.Gram, 10000000.0 }; + yield return new object[] { 10.0, MassUnit.Tonne, MassUnit.Kilogram, 10000.0 }; + yield return new object[] { 1.0, MassUnit.Tonne, MassUnit.Ounce, 35273.961980687 }; + yield return new object[] { 1.0, MassUnit.Tonne, MassUnit.Pound, 2204.622621849 }; + yield return new object[] { 10.0, MassUnit.Ounce, MassUnit.Ounce, 10.0 }; + yield return new object[] { 10.0, MassUnit.Ounce, MassUnit.Milligram, 283495.231 }; + yield return new object[] { 10.0, MassUnit.Ounce, MassUnit.Gram, 283.495231 }; + yield return new object[] { 10.0, MassUnit.Ounce, MassUnit.Kilogram, 0.283495231 }; + yield return new object[] { 10.0, MassUnit.Ounce, MassUnit.Tonne, 0.000283495 }; + yield return new object[] { 1.0, MassUnit.Ounce, MassUnit.Pound, 0.0625 }; + yield return new object[] { 10.0, MassUnit.Pound, MassUnit.Pound, 10.0 }; + yield return new object[] { 10.0, MassUnit.Pound, MassUnit.Milligram, 4535923.7 }; + yield return new object[] { 10.0, MassUnit.Pound, MassUnit.Gram, 4535.9237 }; + yield return new object[] { 10.0, MassUnit.Pound, MassUnit.Kilogram, 4.5359237 }; + yield return new object[] { 10.0, MassUnit.Pound, MassUnit.Tonne, 0.004535924 }; + yield return new object[] { 10.0, MassUnit.Pound, MassUnit.Ounce, 160.0 }; + } + + [Theory] + [MemberData(nameof(GetConversionTestCases))] + public void ConversionTests(double value, MassUnit unit, MassUnit to, double expected) + { + var mass = new MassValue(new NumberValue(value), unit); + var converted = mass.To(to); + + Assert.Equal(expected, converted.Value.Number, 6); + } +} \ No newline at end of file diff --git a/xFunc.Tests/ParserTests/MassUnitTests.cs b/xFunc.Tests/ParserTests/MassUnitTests.cs new file mode 100644 index 000000000..5e9442601 --- /dev/null +++ b/xFunc.Tests/ParserTests/MassUnitTests.cs @@ -0,0 +1,31 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Tests.ParserTests; + +public class MassUnitTests : BaseParserTests +{ + [Fact] + public void ParseKilogram() + => ParseTest("1 kg", MassValue.Kilogram(1).AsExpression()); + + [Fact] + public void ParseGram() + => ParseTest("1 g", MassValue.Gram(1).AsExpression()); + + [Fact] + public void ParseMilligram() + => ParseTest("1 mg", MassValue.Milligram(1).AsExpression()); + + [Fact] + public void ParseTonne() + => ParseTest("1 t", MassValue.Tonne(1).AsExpression()); + + [Fact] + public void ParseOunce() + => ParseTest("1 oz", MassValue.Ounce(1).AsExpression()); + + [Fact] + public void ParsePound() + => ParseTest("1 lb", MassValue.Pound(1).AsExpression()); +} \ No newline at end of file diff --git a/xFunc.Tests/ProcessorTest.cs b/xFunc.Tests/ProcessorTest.cs index ed0c3714b..71f9db399 100644 --- a/xFunc.Tests/ProcessorTest.cs +++ b/xFunc.Tests/ProcessorTest.cs @@ -148,6 +148,17 @@ public void SolveTemperatureTest() Assert.Equal(expected, result.Result); } + [Fact] + public void SolveMassTest() + { + var processor = new Processor(); + + var result = processor.Solve("10 g"); + var expected = MassValue.Gram(10); + + Assert.Equal(expected, result.Result); + } + [Fact] public void ParseTest() { diff --git a/xFunc.Tests/Results/MassNumberResultTest.cs b/xFunc.Tests/Results/MassNumberResultTest.cs new file mode 100644 index 000000000..41e80653e --- /dev/null +++ b/xFunc.Tests/Results/MassNumberResultTest.cs @@ -0,0 +1,34 @@ +// Copyright (c) Dmytro Kyshchenko. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace xFunc.Tests.Results; + +public class MassNumberResultTest +{ + [Fact] + public void ResultTest() + { + var power = MassValue.Gram(10); + var result = new MassNumberResult(power); + + Assert.Equal(power, result.Result); + } + + [Fact] + public void IResultTest() + { + var power = MassValue.Gram(10); + var result = new MassNumberResult(power) as IResult; + + Assert.Equal(power, result.Result); + } + + [Fact] + public void ToStringTest() + { + var power = MassValue.Gram(10); + var result = new MassNumberResult(power); + + Assert.Equal("10 g", result.ToString()); + } +} \ No newline at end of file diff --git a/xFunc.Tests/xFunc.Tests.csproj b/xFunc.Tests/xFunc.Tests.csproj index db85400a9..3ff587d93 100644 --- a/xFunc.Tests/xFunc.Tests.csproj +++ b/xFunc.Tests/xFunc.Tests.csproj @@ -33,6 +33,7 @@ +