Skip to content

Commit

Permalink
- new units - tipographical points and picas, troy oz, metric, uk and…
Browse files Browse the repository at this point in the history
… us tonne. new method of class creation unit.New(v) and conversion to and from tuples
  • Loading branch information
nikolaygekht committed Mar 23, 2021
1 parent f4481b6 commit 05ea59f
Show file tree
Hide file tree
Showing 13 changed files with 270 additions and 14 deletions.
2 changes: 2 additions & 0 deletions Gehtsoft.Measurements.Test/AreaTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public class AreaTest
[Theory]
[InlineData(120, AreaUnit.Acre, 48.56227, AreaUnit.Hectare, 1e-5)]
[InlineData(120, AreaUnit.Acre, 0.48562300014476, AreaUnit.SquareKilometer, 1e-5)]
[InlineData(1, AreaUnit.Ar, 0.01, AreaUnit.Hectare, 1e-5)]
[InlineData(1, AreaUnit.Ar, 100, AreaUnit.SquareMeter, 1e-5)]
public void Conversion(double value, AreaUnit unit, double expected, AreaUnit targetUnit, double accurracy = 1e-10)
{
var v = new Measurement<AreaUnit>(value, unit);
Expand Down
38 changes: 38 additions & 0 deletions Gehtsoft.Measurements.Test/CoreClassesTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,44 @@ public void Formattable()
$"{v:N2}".Should().Be("1.23m");
$"{v:N4}".Should().Be("1.2346m");
}

[Fact]
public void NewUnitExtension()
{
var u1 = AngularUnit.MOA.New(25);
u1.Should().Be(new Measurement<AngularUnit>(25, AngularUnit.MOA));

var u2 = DistanceUnit.Point.New(1.23456);
u2.Value.Should().Be(1.23456);
u2.Unit.Should().Be(DistanceUnit.Point);
}

[Fact]
public void TupleTest()
{
var t1 = new Tuple<double, AngularUnit>(15, AngularUnit.MOA);

var u1 = new Measurement<AngularUnit>(t1);
u1.Should().Be(AngularUnit.MOA.New(15));

var u2 = (Measurement<AngularUnit>)t1;
u2.Should().Be(AngularUnit.MOA.New(15));

var t2 = (Tuple<double, AngularUnit>)u1;
t2.Should().Be(new Tuple<double, AngularUnit>(15, AngularUnit.MOA));

(double a, AngularUnit b) t3 = (10.0, AngularUnit.MOA);

var u3 = new Measurement<AngularUnit>(t3);
u3.Value.Should().Be(t3.a);
u3.Unit.Should().Be(t3.b);

var t4 = ((double x, AngularUnit y))u3;
t4.x.Should().Be(10);
t4.y.Should().Be(AngularUnit.MOA);


}
}
}

Expand Down
2 changes: 2 additions & 0 deletions Gehtsoft.Measurements.Test/DistanceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ public class DistanceTest
[InlineData(1, DistanceUnit.Millimeter, 0.001, DistanceUnit.Meter)]
[InlineData(1760, DistanceUnit.Yard, 1, DistanceUnit.Mile)]
[InlineData(1, DistanceUnit.NauticalMile, 1852, DistanceUnit.Meter)]
[InlineData(11, DistanceUnit.Point, 3.88055555556, DistanceUnit.Millimeter)]
[InlineData(11, DistanceUnit.Pica, 46.56666666667, DistanceUnit.Millimeter)]
public void Conversion(double value, DistanceUnit unit, double expected, DistanceUnit targetUnit)
{
var v = new Measurement<DistanceUnit>(value, unit);
Expand Down
55 changes: 55 additions & 0 deletions Gehtsoft.Measurements.Test/MeasurementMathTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public void Sin(double value, AngularUnit unit, double expected)
{
var v = new Measurement<AngularUnit>(value, unit);
MeasurementMath.Sin(v).Should().BeApproximately(expected, 1e-10);
v.Sin().Should().BeApproximately(expected, 1e-10);
MeasurementMath.Asin(expected).In(unit).Should().BeApproximately(value, 1e-7);
}

Expand Down Expand Up @@ -61,6 +62,11 @@ public void Abs(double value, AngularUnit unit, double expected)
var v1 = MeasurementMath.Abs(v);
v1.Value.Should().BeApproximately(expected, 1e-10);
v1.Unit.Should().Be(unit);

var v2 = v.Abs();
v2.Value.Should().BeApproximately(expected, 1e-10);
v2.Unit.Should().Be(unit);

}

[Theory]
Expand Down Expand Up @@ -126,5 +132,54 @@ public void Velocity(double distance, DistanceUnit distanceUnit, double velocity
var velocity2 = MeasurementMath.Velocity(distance1, ts1);
velocity2.In(velocityUnit).Should().BeApproximately(velocity, 1e-5);
}

[Fact]
public void CompareStatements()
{
(DistanceUnit.Centimeter.New(10) == DistanceUnit.Meter.New(0.1)).Should().BeTrue();
(DistanceUnit.Centimeter.New(10) != DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(10) > DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(10) >= DistanceUnit.Meter.New(0.1)).Should().BeTrue();
(DistanceUnit.Centimeter.New(10) < DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(10) <= DistanceUnit.Meter.New(0.1)).Should().BeTrue();

(DistanceUnit.Centimeter.New(20) == DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(20) != DistanceUnit.Meter.New(0.1)).Should().BeTrue();
(DistanceUnit.Centimeter.New(20) > DistanceUnit.Meter.New(0.1)).Should().BeTrue();
(DistanceUnit.Centimeter.New(20) >= DistanceUnit.Meter.New(0.1)).Should().BeTrue();
(DistanceUnit.Centimeter.New(20) < DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(20) <= DistanceUnit.Meter.New(0.1)).Should().BeFalse();

(DistanceUnit.Centimeter.New(5) == DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(5) != DistanceUnit.Meter.New(0.1)).Should().BeTrue();
(DistanceUnit.Centimeter.New(5) > DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(5) >= DistanceUnit.Meter.New(0.1)).Should().BeFalse();
(DistanceUnit.Centimeter.New(5) < DistanceUnit.Meter.New(0.1)).Should().BeTrue();
(DistanceUnit.Centimeter.New(5) <= DistanceUnit.Meter.New(0.1)).Should().BeTrue();
}

[Fact]
public void MathStatements()
{
(DistanceUnit.Centimeter.New(5) + DistanceUnit.Millimeter.New(5)).Should().Be(DistanceUnit.Centimeter.New(5.5));
(DistanceUnit.Centimeter.New(5) - DistanceUnit.Millimeter.New(5)).Should().Be(DistanceUnit.Centimeter.New(4.5));
(DistanceUnit.Centimeter.New(5) * 2).Should().Be(DistanceUnit.Centimeter.New(10));
(2 * DistanceUnit.Centimeter.New(5)).Should().Be(DistanceUnit.Centimeter.New(10));
(DistanceUnit.Centimeter.New(5) / 2).Should().Be(DistanceUnit.Centimeter.New(2.5));
(DistanceUnit.Centimeter.New(5) / DistanceUnit.Centimeter.New(2.5)).Should().Be(2.0);

(+DistanceUnit.Centimeter.New(5)).Should().Be(DistanceUnit.Centimeter.New(5));
(-DistanceUnit.Centimeter.New(5)).Should().Be(DistanceUnit.Centimeter.New(-5));

(WeightUnit.UKTonne.New(1) / WeightUnit.USTonne.New(1)).Should().BeApproximately(1.1201764057331863285556780595369, 1e-10);
}

[Fact]
public void Sign()
{
AngularUnit.MOA.New(5).Sign().Should().BeGreaterThan(0);
AngularUnit.MOA.New(0).Sign().Should().Be(0);
AngularUnit.MOA.New(-5).Sign().Should().BeLessThan(0);
}
}
}
4 changes: 4 additions & 0 deletions Gehtsoft.Measurements.Test/WeightTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ public class WeightTest
[InlineData(1, WeightUnit.Pound, 0.453592, WeightUnit.Kilogram, 1e-5)]
[InlineData(1, WeightUnit.Kilogram, 9.80665, WeightUnit.Neuton, 1e-5)]
[InlineData(5, WeightUnit.Ounce, 141.747615, WeightUnit.Gram, 1e-5)]
[InlineData(1, WeightUnit.TroyOz, 31.1034768, WeightUnit.Gram, 1e-5)]
[InlineData(1, WeightUnit.Tonne, 1000, WeightUnit.Kilogram, 1e-5)]
[InlineData(1, WeightUnit.USTonne, 0.907, WeightUnit.Tonne, 1e-5)]
[InlineData(1, WeightUnit.UKTonne, 1.016, WeightUnit.Tonne, 1e-5)]
public void Conversion(double value, WeightUnit unit, double expected, WeightUnit targetUnit, double accurracy = 1e-10)
{
var v = new Measurement<WeightUnit>(value, unit);
Expand Down
7 changes: 7 additions & 0 deletions Gehtsoft.Measurements/AreaUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,12 @@ public enum AreaUnit
[Unit("ha", 0)]
[Conversion(ConversionOperation.Multiply, 1e+10)]
Hectare,

/// <summary>
/// Ar (1/100 of hectare, "sotka")
/// </summary>
[Unit("ar", 0)]
[Conversion(ConversionOperation.Multiply, 1e+8)]
Ar,
}
}
14 changes: 14 additions & 0 deletions Gehtsoft.Measurements/DistanceUnit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,19 @@ public enum DistanceUnit
[Unit("km", 3)]
[Conversion(ConversionOperation.Divide, 25.4, ConversionOperation.Multiply, 1_000_000)]
Kilometer,

/// <summary>
/// Typographical/DTP point (1 pt == 1/72 of inch)
/// </summary>
[Unit("pt", 1)]
[Conversion(ConversionOperation.Divide, 72)]
Point,

/// <summary>
/// Typographical/DTP point (1 pt == 1/72 of inch)
/// </summary>
[Unit("p", 1)]
[Conversion(ConversionOperation.Divide, 6)]
Pica,
}
}
51 changes: 50 additions & 1 deletion Gehtsoft.Measurements/Measurement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ namespace Gehtsoft.Measurements
/// The class supports serialization using `System.Text.Json` serializer and `XmlSerializer` as well as
/// many 3rd party serializers such as `BinaronSerializer`.
/// </para>
/// <param name="T">The measurement unit</param>
/// </summary>
public readonly struct Measurement<T> : IEquatable<Measurement<T>>, IComparable<Measurement<T>>, IFormattable
where T : Enum
Expand Down Expand Up @@ -50,6 +49,26 @@ public Measurement(double value, T unit)
Unit = unit;
}

/// <summary>
/// Constructor that accepts a tuple.
/// </summary>
/// <param name="value"></param>
public Measurement(Tuple<double, T> value)
{
Value = value.Item1;
Unit = value.Item2;
}

/// <summary>
/// Constructor that accepts a anonymous tuple.
/// </summary>
/// <param name="value"></param>
public Measurement((double, T) value)
{
Value = value.Item1;
Unit = value.Item2;
}

/// <summary>
/// Constructor that accepts a text representation of a value
/// </summary>
Expand Down Expand Up @@ -311,6 +330,8 @@ public int CompareTo(Measurement<T> other)
double v1, v2;
v1 = In(BaseUnit);
v2 = other.In(BaseUnit);
if (Math.Abs(v1 - v2) < 1e-10)
return 0;
return v1.CompareTo(v2);
}

Expand Down Expand Up @@ -370,6 +391,15 @@ public int CompareTo(Measurement<T> other)
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Measurement<T> operator -(Measurement<T> v1) => new Measurement<T>(-v1.Value, v1.Unit);

/// <summary>
/// Unary plus value
/// </summary>
/// <param name="v1"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Measurement<T> operator +(Measurement<T> v1) => v1;

/// <summary>
/// Add one measurement to another.
/// </summary>
Expand Down Expand Up @@ -421,5 +451,24 @@ public int CompareTo(Measurement<T> other)
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double operator /(Measurement<T> v1, Measurement<T> v2) => v1.Value / v2.In(v1.Unit);

/// <summary>
/// Implicitly converts the value to a tuple
/// </summary>
/// <param name="value"></param>
public static implicit operator Tuple<double, T>(Measurement<T> value) => new Tuple<double, T>(value.Value, value.Unit);

/// <summary>
/// Explicitly converts the a tuple to a value
/// </summary>
/// <param name="value"></param>
public static explicit operator Measurement<T>(Tuple<double, T> value) => new Measurement<T>(value.Item1, value.Item2);

/// <summary>
/// Implicitly converts the value to an anonymous tuple
/// </summary>
/// <param name="value"></param>
public static implicit operator (double, T)(Measurement<T> value) => (value.Value, value.Unit);

}
}
28 changes: 22 additions & 6 deletions Gehtsoft.Measurements/MeasurementMath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,41 @@ namespace Gehtsoft.Measurements
/// </summary>
public static class MeasurementMath
{
/// <summary>
/// <para>Returns sign of the value</para>
/// <para>The method return `-1` for negative values, `0` for zero value and `1` for positive values</para>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <returns></returns>
public static int Sign<T>(this Measurement<T> value) where T : Enum
{
if (value.Value < 0)
return -1;
else if (value.Value == 0)
return 0;
return 1;
}

/// <summary>
/// Calculate sine of angular value
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Sin(Measurement<AngularUnit> value) => Math.Sin(value.In(AngularUnit.Radian));
public static double Sin(this Measurement<AngularUnit> value) => Math.Sin(value.In(AngularUnit.Radian));

/// <summary>
/// Calculate cosine of angular value
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Cos(Measurement<AngularUnit> value) => Math.Cos(value.In(AngularUnit.Radian));
public static double Cos(this Measurement<AngularUnit> value) => Math.Cos(value.In(AngularUnit.Radian));

/// <summary>
/// Calculate tangent of angular value
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Tan(Measurement<AngularUnit> value) => Math.Tan(value.In(AngularUnit.Radian));
public static double Tan(this Measurement<AngularUnit> value) => Math.Tan(value.In(AngularUnit.Radian));

/// <summary>
/// Calculate arcsine as angular value
Expand All @@ -52,19 +68,19 @@ public static class MeasurementMath
/// Calculate square root of a value
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Measurement<T> Sqrt<T>(Measurement<T> value) where T : Enum => new Measurement<T>(Math.Sqrt(value.Value), value.Unit);
public static Measurement<T> Sqrt<T>(this Measurement<T> value) where T : Enum => new Measurement<T>(Math.Sqrt(value.Value), value.Unit);

/// <summary>
/// Raise the value in the power specified
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Measurement<T> Pow<T>(Measurement<T> value, double exp) where T : Enum => new Measurement<T>(Math.Pow(value.Value, exp), value.Unit);
public static Measurement<T> Pow<T>(this Measurement<T> value, double exp) where T : Enum => new Measurement<T>(Math.Pow(value.Value, exp), value.Unit);

/// <summary>
/// Calculate the absolute value
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Measurement<T> Abs<T>(Measurement<T> value) where T : Enum => new Measurement<T>(Math.Abs(value.Value), value.Unit);
public static Measurement<T> Abs<T>(this Measurement<T> value) where T : Enum => new Measurement<T>(Math.Abs(value.Value), value.Unit);

/// <summary>
/// Calculate velocity from distance and time
Expand Down
35 changes: 35 additions & 0 deletions Gehtsoft.Measurements/UnitExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Gehtsoft.Measurements
{
/// <summary>
/// The extensions for unit type enumerations
/// </summary>
public static class UnitExtensions
{
/// <summary>
/// <para>Creates a new value of the specified unit.</para>
/// <para>The method is an extension for a `enum` value, i.e. you can use it as `AngularUnit.MOA.New(10)` instead of writing `new Measurement&lt;AngularUnit&gt;(10, AngularUnit.MOA)`</para>
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="unit"></param>
/// <param name="value"></param>
/// <returns></returns>
public static Measurement<T> New<T>(this T unit, double value) where T : Enum => new Measurement<T>(value, unit);

/// <summary>
/// Converts an enumeration of doubles into an enumeration of measures of the specified unit.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="values"></param>
/// <param name="unit"></param>
/// <returns></returns>
public static IEnumerable<Measurement<T>> As<T>(this IEnumerable<double> values, T unit) where T : Enum
{
foreach (var v in values)
yield return new Measurement<T>(v, unit);
}
}
}
Loading

0 comments on commit 05ea59f

Please sign in to comment.