From f2aaa0b6793d82517ad3d21d8f27bc4fe8b7bdf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20K=C3=A4gi?= Date: Thu, 5 Oct 2023 12:47:11 +0200 Subject: [PATCH] Introduce factory class to create measures. --- .../ConvertingQuantities.cs | 8 +- .../Quantities.Benchmark/CreateQuantities.cs | 22 +++--- source/Quantities/Core/Measure.cs | 4 +- source/Quantities/Creation/Creators.cs | 66 ++++++++++++++++ source/Quantities/Creation/Factories.cs | 79 ------------------- source/Quantities/Creation/Factory.cs | 37 +++++++++ source/Quantities/Creation/Injectors.cs | 39 --------- source/Quantities/Systems.cs | 16 ++-- 8 files changed, 129 insertions(+), 142 deletions(-) create mode 100644 source/Quantities/Creation/Creators.cs delete mode 100644 source/Quantities/Creation/Factories.cs create mode 100644 source/Quantities/Creation/Factory.cs delete mode 100644 source/Quantities/Creation/Injectors.cs diff --git a/source/Quantities.Benchmark/ConvertingQuantities.cs b/source/Quantities.Benchmark/ConvertingQuantities.cs index 423df2cb..8ce0d2f7 100644 --- a/source/Quantities.Benchmark/ConvertingQuantities.cs +++ b/source/Quantities.Benchmark/ConvertingQuantities.cs @@ -40,8 +40,8 @@ .NET SDK 7.0.111 | Method | Mean | Error | StdDev | Ratio | RatioSD | Allocated | Alloc Ratio | |------------------------ |-----------:|----------:|----------:|------:|--------:|----------:|------------:| -| TrivialImplementation | 0.6493 ns | 0.0171 ns | 0.0152 ns | 1.00 | 0.00 | - | NA | -| QuantityImplementation | 2.9082 ns | 0.0899 ns | 0.0923 ns | 4.45 | 0.14 | - | NA | -| QuantityToSame | 26.5881 ns | 0.1631 ns | 0.1525 ns | 40.96 | 1.02 | - | NA | -| QuantityToVeryDifferent | 27.2572 ns | 0.1387 ns | 0.1083 ns | 42.25 | 0.86 | - | NA | +| TrivialImplementation | 0.5988 ns | 0.0069 ns | 0.0058 ns | 1.00 | 0.00 | - | NA | +| QuantityImplementation | 4.0346 ns | 0.0202 ns | 0.0158 ns | 6.74 | 0.06 | - | NA | +| QuantityToSame | 14.9515 ns | 0.1545 ns | 0.1370 ns | 24.94 | 0.35 | - | NA | +| QuantityToVeryDifferent | 17.1767 ns | 0.0614 ns | 0.0575 ns | 28.67 | 0.28 | - | NA | */ diff --git a/source/Quantities.Benchmark/CreateQuantities.cs b/source/Quantities.Benchmark/CreateQuantities.cs index 2a38d979..5f4c57c0 100644 --- a/source/Quantities.Benchmark/CreateQuantities.cs +++ b/source/Quantities.Benchmark/CreateQuantities.cs @@ -7,7 +7,6 @@ using Quantities.Units.Si.Metric; using Quantities.Dimensions; -using static Quantities.Systems; using static Quantities.AliasOf; namespace Quantities.Benchmark; @@ -39,6 +38,7 @@ public sealed class DummyObject : ICastOperators //[EventPipeProfiler(EventPipeProfile.CpuSampling)] public class CreateQuantities { + private static readonly Creation.Product kwh = Si().Dot(Metric()); private static readonly Random random = new(); private readonly Double value = random.NextDouble(); @@ -62,6 +62,9 @@ public class CreateQuantities [Benchmark] public Energy CreateProductQuantity() => Energy.Of(in this.value, Si().Dot(Metric())); + [Benchmark] + public Energy CreateCachedProductQuantity() => Energy.Of(in this.value, in kwh); + [Benchmark] public Area CreateScalarPowerQuantity() => Area.Of(in this.value, AliasOf.Metric()); @@ -81,12 +84,13 @@ .NET SDK 7.0.111 | Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio | |------------------------------- |----------:|----------:|----------:|------:|--------:|-------:|----------:|------------:| -| CreateObject | 6.780 ns | 0.0843 ns | 0.0788 ns | 1.00 | 0.00 | 0.0057 | 24 B | 1.00 | -| CreateStruct | 1.294 ns | 0.0125 ns | 0.0111 ns | 0.19 | 0.00 | - | - | 0.00 | -| CreateScalarQuantity | 4.461 ns | 0.0295 ns | 0.0276 ns | 0.66 | 0.01 | - | - | 0.00 | -| CreateScalarQuantityWithoutOpt | 3.312 ns | 0.0145 ns | 0.0121 ns | 0.49 | 0.01 | - | - | 0.00 | -| CreateQuotientQuantity | 24.993 ns | 0.1407 ns | 0.1247 ns | 3.69 | 0.05 | - | - | 0.00 | -| CreateProductQuantity | 26.276 ns | 0.5255 ns | 0.5396 ns | 3.88 | 0.11 | - | - | 0.00 | -| CreateScalarPowerQuantity | 16.077 ns | 0.0537 ns | 0.0476 ns | 2.37 | 0.02 | - | - | 0.00 | -| CreateSquarePowerQuantity | 13.729 ns | 0.0552 ns | 0.0461 ns | 2.03 | 0.02 | - | - | 0.00 | +| CreateObject | 5.937 ns | 0.1106 ns | 0.1035 ns | 1.00 | 0.00 | 0.0057 | 24 B | 1.00 | +| CreateStruct | 1.621 ns | 0.0464 ns | 0.0411 ns | 0.27 | 0.01 | - | - | 0.00 | +| CreateScalarQuantity | 5.468 ns | 0.0441 ns | 0.0412 ns | 0.92 | 0.02 | - | - | 0.00 | +| CreateScalarQuantityWithoutOpt | 4.514 ns | 0.0139 ns | 0.0123 ns | 0.76 | 0.01 | - | - | 0.00 | +| CreateQuotientQuantity | 15.357 ns | 0.0940 ns | 0.0879 ns | 2.59 | 0.05 | - | - | 0.00 | +| CreateProductQuantity | 15.606 ns | 0.2638 ns | 0.2467 ns | 2.63 | 0.08 | - | - | 0.00 | +| CreateCachedProductQuantity | 5.546 ns | 0.0397 ns | 0.0371 ns | 0.93 | 0.01 | - | - | 0.00 | +| CreateScalarPowerQuantity | 15.624 ns | 0.1290 ns | 0.1007 ns | 2.64 | 0.05 | - | - | 0.00 | +| CreateSquarePowerQuantity | 5.448 ns | 0.1203 ns | 0.1066 ns | 0.92 | 0.03 | - | - | 0.00 | */ diff --git a/source/Quantities/Core/Measure.cs b/source/Quantities/Core/Measure.cs index e5e7246a..8bbec60b 100644 --- a/source/Quantities/Core/Measure.cs +++ b/source/Quantities/Core/Measure.cs @@ -3,12 +3,11 @@ namespace Quantities.Core; -internal abstract class Measure : IInjector +internal abstract class Measure { private readonly Polynomial conversion; private Measure(in Polynomial conversion) => this.conversion = conversion; public Polynomial Project(Measure other) => this.conversion / other.conversion; - public abstract TResult Inject(IInject inject); public abstract Result Multiply(Measure other); protected abstract Result Multiply() where TMeasure : IMeasure; public abstract Result Divide(Measure other); @@ -26,6 +25,5 @@ public Impl() : base(TMeasure.Poly) { } public override String ToString() => TMeasure.Representation; protected override Result Multiply() => TOtherMeasure.Multiply(); protected override Result Divide() => TOtherMeasure.Divide(); - public override TResult Inject(IInject inject) => inject.Inject(); } } diff --git a/source/Quantities/Creation/Creators.cs b/source/Quantities/Creation/Creators.cs new file mode 100644 index 00000000..f711fc86 --- /dev/null +++ b/source/Quantities/Creation/Creators.cs @@ -0,0 +1,66 @@ +using Quantities.Dimensions; +using Quantities.Units; + +namespace Quantities.Creation; + +public readonly struct Scalar + where TDim : IUnit, IDimension +{ + private readonly Factory factory; + internal Factory Factory => this.factory; + internal Scalar(Factory factory) => this.factory = factory; + public Product Dot(in Scalar rightTerm) + where TRight : IUnit, IDimension => new(rightTerm.factory.Inject(this.factory.Product)); + public Quotient Per(in Scalar denominator) + where TDenominator : IUnit, IDimension => new(denominator.factory.Inject(this.factory.Quotient)); + internal Quantity Create(in Double value) => new(in value, this.factory.Create()); + internal Quantity Transform(in Quantity other) => other.Project(this.factory.Create()); +} + +public readonly ref struct Alias + where TUnit : IAlias, IUnit, IDimension + where TLinear : IDimension +{ + private readonly Measure measure; + internal Alias(Measure measure) => this.measure = measure; + internal Quantity Create(in Double value) => new(in value, in this.measure); + internal Quantity Transform(in Quantity other) => other.Project(in this.measure); +} + +public readonly struct Product + where TLeft : IUnit, IDimension + where TRight : IUnit, IDimension +{ + private readonly Factory factory; + internal Product(Factory factory) => this.factory = factory; + internal Quantity Create(in Double value) => new(in value, this.factory.Create()); + internal Quantity Transform(in Quantity other) => other.Project(this.factory.Create()); +} + +public readonly ref struct Quotient + where TN : IUnit, IDimension + where TD : IUnit, IDimension +{ + private readonly Factory factory; + internal Quotient(Factory factory) => this.factory = factory; + internal Quantity Create(in Double value) => new(in value, this.factory.Create()); + internal Quantity Transform(in Quantity other) => other.Project(this.factory.Create()); +} + +public readonly ref struct Square + where TDim : IUnit, IDimension +{ + private readonly Factory factory; + internal Square(Factory factory) => this.factory = factory; + internal Quantity Create(in Double value) => new(in value, this.factory.Square()); + internal Quantity Transform(in Quantity other) => other.Project(this.factory.Square()); +} + +public readonly ref struct Cubic + where TDim : IUnit, IDimension +{ + private readonly Factory factory; + internal Cubic(Factory factory) => this.factory = factory; + internal Quantity Create(in Double value) => new(in value, this.factory.Cubic()); + internal Quantity Transform(in Quantity other) => other.Project(this.factory.Cubic()); +} diff --git a/source/Quantities/Creation/Factories.cs b/source/Quantities/Creation/Factories.cs deleted file mode 100644 index f7e04a87..00000000 --- a/source/Quantities/Creation/Factories.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Quantities.Dimensions; -using Quantities.Units; - -namespace Quantities.Creation; - -public readonly ref struct Scalar - where TDim : IUnit, IDimension -{ - private readonly Measure measure; - internal Scalar(Measure measure) => this.measure = measure; - - // ToDo: Use injection here - public Product Dot(in Scalar rightTerm) - where TRight : IUnit, IDimension - { - var left = this.measure.Inject(Injectors.Product); - return new(rightTerm.measure.Inject(left)); - } - - public Quotient Per(in Scalar denominator) - where TDenominator : IUnit, IDimension - { - var nominator = this.measure.Inject(Injectors.Quotient); - return new(denominator.measure.Inject(nominator)); - } - - internal Quantity Create(in Double value) => new(in value, in this.measure); - internal Quantity Transform(in Quantity other) => other.Project(in this.measure); - internal Measure Cubic() => this.measure.Inject(Injectors.Cubic); - internal Measure Square() => this.measure.Inject(Injectors.Square); -} - -public readonly ref struct Alias - where TUnit : IAlias, IUnit, IDimension - where TLinear : IDimension -{ - private readonly Measure measure; - internal Alias(Measure measure) => this.measure = measure; - internal Quantity Create(in Double value) => new(in value, in this.measure); - internal Quantity Transform(in Quantity other) => other.Project(in this.measure); -} - -public readonly ref struct Product - where TLeft : IUnit, IDimension - where TRight : IUnit, IDimension -{ - private readonly Measure measure; - internal Product(Measure measure) => this.measure = measure; - internal Quantity Create(in Double value) => new(in value, in this.measure); - internal Quantity Transform(in Quantity other) => other.Project(in this.measure); -} - -public readonly ref struct Quotient - where TN : IUnit, IDimension - where TD : IUnit, IDimension -{ - private readonly Measure measure; - internal Quotient(Measure measure) => this.measure = measure; - internal Quantity Create(in Double value) => new(in value, in this.measure); - internal Quantity Transform(in Quantity other) => other.Project(in this.measure); -} - -public readonly ref struct Square - where TDim : IUnit, IDimension -{ - private readonly Measure measure; - internal Square(Measure measure) => this.measure = measure; - internal Quantity Create(in Double value) => new(in value, in this.measure); - internal Quantity Transform(in Quantity other) => other.Project(in this.measure); -} - -public readonly ref struct Cubic - where TDim : IUnit, IDimension -{ - private readonly Measure measure; - internal Cubic(Measure measure) => this.measure = measure; - internal Quantity Create(in Double value) => new(in value, in this.measure); - internal Quantity Transform(in Quantity other) => other.Project(in this.measure); -} diff --git a/source/Quantities/Creation/Factory.cs b/source/Quantities/Creation/Factory.cs new file mode 100644 index 00000000..cb97f526 --- /dev/null +++ b/source/Quantities/Creation/Factory.cs @@ -0,0 +1,37 @@ +namespace Quantities.Creation; + +internal abstract class Factory : IInjector +{ + public IInject Product { get; } + public IInject Quotient { get; } + private Factory(IInject product, IInject quotient) => (Product, Quotient) = (product, quotient); + public abstract Measure Create(); + public abstract Measure Square(); + public abstract Measure Cubic(); + public abstract TResult Inject(IInject inject); + public static Factory Of() where TMeasure : IMeasure => AllocationFree>.Item; + + private sealed class Impl : Factory + where TMeasure : IMeasure + { + private static readonly IInject product = new ProductInject(); + private static readonly IInject quotient = new QuotientInject(); + public Impl() : base(product, quotient) { } + public override Measure Create() => Measure.Of(); + public override Measure Square() => Measure.Of>(); + public override Measure Cubic() => Measure.Of>(); + public override TResult Inject(IInject inject) => inject.Inject(); + } + + private sealed class ProductInject : IInject + where TLeftTerm : IMeasure + { + public Factory Inject() where TMeasure : IMeasure => Of>(); + } + + private sealed class QuotientInject : IInject + where TLeftTerm : IMeasure + { + public Factory Inject() where TMeasure : IMeasure => Of>(); + } +} diff --git a/source/Quantities/Creation/Injectors.cs b/source/Quantities/Creation/Injectors.cs deleted file mode 100644 index a15e05ce..00000000 --- a/source/Quantities/Creation/Injectors.cs +++ /dev/null @@ -1,39 +0,0 @@ -namespace Quantities.Creation; - -internal static class Injectors -{ - public static IInject Cubic { get; } = new CubicInject(); - public static IInject Square { get; } = new SquareInject(); - public static IInject> Product { get; } = new ProductInject(); - public static IInject> Quotient { get; } = new QuotientInject(); - - private sealed class CubicInject : IInject - { - public Measure Inject() where TMeasure : IMeasure => Measure.Of>(); - } - - private sealed class SquareInject : IInject - { - public Measure Inject() where TMeasure : IMeasure => Measure.Of>(); - } - - private sealed class ProductInject : IInject> - { - public IInject Inject() where TMeasure : IMeasure => AllocationFree>.Item; - private sealed class Product : IInject - where TLeftTerm : IMeasure - { - public Measure Inject() where TMeasure : IMeasure => Measure.Of>(); - } - } - - private sealed class QuotientInject : IInject> - { - public IInject Inject() where TMeasure : IMeasure => AllocationFree>.Item; - private sealed class Quotient : IInject - where TLeftTerm : IMeasure - { - public Measure Inject() where TMeasure : IMeasure => Measure.Of>(); - } - } -} diff --git a/source/Quantities/Systems.cs b/source/Quantities/Systems.cs index 6dee7c1e..3cb2b446 100644 --- a/source/Quantities/Systems.cs +++ b/source/Quantities/Systems.cs @@ -9,21 +9,21 @@ namespace Quantities; public static class Systems { public static Scalar Si() - where TDim : ISiUnit, IDimension => new(Measure.Of>()); + where TDim : ISiUnit, IDimension => new(Factory.Of>()); public static Scalar Si() where TPrefix : IMetricPrefix - where TDim : ISiUnit, IDimension => new(Measure.Of>()); + where TDim : ISiUnit, IDimension => new(Factory.Of>()); public static Scalar Metric() - where TDim : IMetricUnit, IDimension => new(Measure.Of>()); + where TDim : IMetricUnit, IDimension => new(Factory.Of>()); public static Scalar Metric() where TPrefix : IMetricPrefix - where TDim : IMetricUnit, IDimension => new(Measure.Of>()); + where TDim : IMetricUnit, IDimension => new(Factory.Of>()); public static Scalar Imperial() - where TDim : IImperialUnit, IDimension => new(Measure.Of>()); + where TDim : IImperialUnit, IDimension => new(Factory.Of>()); public static Scalar NonStandard() - where TDim : INonStandardUnit, IDimension => new(Measure.Of>()); - public static Cubic Cubic(in Scalar scalar) where TDim : IUnit, IDimension => new(scalar.Cubic()); - public static Square Square(in Scalar scalar) where TDim : IUnit, IDimension => new(scalar.Square()); + where TDim : INonStandardUnit, IDimension => new(Factory.Of>()); + public static Cubic Cubic(in Scalar scalar) where TDim : IUnit, IDimension => new(scalar.Factory); + public static Square Square(in Scalar scalar) where TDim : IUnit, IDimension => new(scalar.Factory); } public static class AliasOf