Skip to content

[WIP] Automatic overload generator #275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions UnitsNet.OperatorOverloads.Tests/OverloadGeneratorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using Xunit;

namespace UnitsNet.OperatorOverloads.Tests
{
public static class OverloadGeneratorTests
{
[Fact]
public static void ShouldReturnDurationAndSpeedWhenGivenLength()
{
var lengthQuantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions\Length.json"));
string neobj = JsonConvert.SerializeObject(lengthQuantity, new JsonSerializerSettings()
{
Formatting = Formatting.Indented
});
var timeQuantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions\Duration.json"));
var speedQuantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions\Speed.json"));

var overloadGenerator = new OverloadGenerator(new[] {lengthQuantity, timeQuantity, speedQuantity});
Overload[] actualQuantities = overloadGenerator.GetDivisionOverloads(lengthQuantity).ToArray();
string[] actualQuantityNames = actualQuantities.Select(x => x.Result.Name).ToArray();
Assert.Equal(2,actualQuantities.Length);

Assert.Contains(speedQuantity.Name, actualQuantityNames);
Assert.Contains(timeQuantity.Name, actualQuantityNames);
}

[Fact]
public static void ShouldReturnLengthWhenGivenSpeed()
{
var lengthQuantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions\Length.json"));
lengthQuantity.SiArray = new[] { 1, 0, 0, 0, 0, 0, 0 };
var timeQuantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions\Duration.json"));
timeQuantity.SiArray = new[] { 0, 0, 1, 0, 0, 0, 0 };
var speedQuantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions\Speed.json"));
speedQuantity.SiArray = new[] { 1, 0, -1, 0, 0, 0, 0 };

var overloadGenerator = new OverloadGenerator(new[] { lengthQuantity, timeQuantity, speedQuantity });
Overload[] actualQuantities = overloadGenerator.GetMultiplicationOverloads(speedQuantity).ToArray();
var actualQuantityNames = actualQuantities.Select(x => x.Result.Name).ToArray();
Assert.Equal(1, actualQuantities.Length);
Assert.Contains(lengthQuantity.Name, actualQuantityNames);
}

[Fact]
public static void RunCodeINSAmeWayAsPowerShell()
{
var overloadGenerator = new OverloadGenerator(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions\");
foreach (var quantity in overloadGenerator.Quantities)
{
var overloads = overloadGenerator.GetOverloads(quantity);
Assert.NotNull(overloads);
foreach (Overload overload in overloads)
{
Assert.NotNull(overload);
}
}
}
}
}
28 changes: 28 additions & 0 deletions UnitsNet.OperatorOverloads.Tests/QuantityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.IO;
using Newtonsoft.Json;
using Xunit;

namespace UnitsNet.OperatorOverloads.Tests
{
public class QuantityTests
{
[Theory]
[MemberData(nameof(GetJsonFiles))]
public static void ShouldNotThrowWhenDeserializingUnits(string filename)
{
var quantity = JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(filename));
Assert.NotNull(quantity);
}

public static IEnumerable<object[]> GetJsonFiles()
{
var files = Directory.EnumerateFiles(@"C:\dev\UnitsNet\UnitsNet\UnitDefinitions", "*.json");
foreach (string file in files)
{
yield return new object[] {file};
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project DefaultTargets="Build;NCrunchOutputDataQuery" Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Remove="UnitsNet.OperatorOverloads.Tests.v3.ncrunchproject" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="newtonsoft.json" Version="10.0.3" />
<PackageReference Include="xunit" Version="2.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UnitsNet.OperatorOverloads\UnitsNet.OperatorOverloads.csproj" />
</ItemGroup>
</Project>
64 changes: 64 additions & 0 deletions UnitsNet.OperatorOverloads/ArrayExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitsNet.OperatorOverloads
{
public static class ArrayExtensions
{
public static int[] ElementwiseSubtract(this int[] left, int[] right)
{
if (left.Length != right.Length)
{
throw new ArgumentException("Arrays must have the same length");
}
var result = new int[left.Length];
for (int i = 0; i < result.Length; i++)
{
result[i] = left[i] - right[i];
}
return result;
}

public static int[] ElementwiseAdd(this int[] left, int[] right)
{
if (left.Length != right.Length)
{
throw new ArgumentException("Arrays must have the same length");
}
var result = new int[left.Length];
for (int i = 0; i < result.Length; i++)
{
result[i] = left[i] + right[i];
}
return result;
}


public static bool EqualContent<T>(this T[] left, T[] right)
{
if (left == default(T[]))
{
return false;
}
if (right == default(T[]))
{
return false;
}
if (left.Length != right.Length)
{
return false;
}
for (int i = 0; i < left.Length; i++)
{
if (!left[i].Equals(right[i]))
{
return false;
}
}
return true;
}
}
}
27 changes: 27 additions & 0 deletions UnitsNet.OperatorOverloads/Overload.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnitsNet.OperatorOverloads
{
public class Overload
{
public Quantity Result { get; }
public Quantity Left { get; }
public Quantity Right { get; }

public Overload(Quantity result, Quantity left, Quantity right)
{
Result = result;
Left = left;
Right = right;
}

public override string ToString()
{
return $"{nameof(Result)}: {Result}, {nameof(Left)}: {Left}, {nameof(Right)}: {Right}";
}
}
}
98 changes: 98 additions & 0 deletions UnitsNet.OperatorOverloads/OverloadGenerator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace UnitsNet.OperatorOverloads
{
public class OverloadGenerator
{
private readonly Quantity[] _quantities;

public OverloadGenerator(string directory)
{
var files = Directory.EnumerateFiles(directory, "*.json");
_quantities = files.Select(f => JsonConvert.DeserializeObject<Quantity>(File.ReadAllText(f))).ToArray();
if (_quantities.Length == 0)
{
throw new InvalidOperationException($"{files}");
}
}

public OverloadGenerator(Quantity[] quantities)
{
_quantities = quantities;
}

public IEnumerable<Overload> GetDivisionOverloads(Quantity quantity)
{
if (quantity.SiArray == null)
yield break;
foreach (Quantity q in _quantities)
{
if (q.SiArray == null)
continue;
if (!quantity.Name.Equals(q.Name,StringComparison.OrdinalIgnoreCase))
{
var newSiArray = quantity.SiArray.ElementwiseSubtract(q.SiArray);

Quantity matchingQuantity = _quantities.SingleOrDefault(x => x.SiArray.EqualContent(newSiArray));
if (matchingQuantity != null)
{
yield return new Overload(matchingQuantity, quantity, q);
}
}
}
}

public IEnumerable<Overload> GetMultiplicationOverloads(Quantity quantity)
{
if (quantity.SiArray == null)
yield break;
foreach (Quantity q in _quantities)
{
if (!quantity.Name.Equals(q.Name, StringComparison.OrdinalIgnoreCase))
{
if (q.SiArray == null)
continue;
var newSiArray = quantity.SiArray.ElementwiseAdd(q.SiArray);

Quantity matchingQuantity = _quantities.SingleOrDefault(x => x.SiArray.EqualContent(newSiArray));
if (matchingQuantity != null)
{
yield return new Overload(matchingQuantity, q, quantity);
}

}
}
}

public IReadOnlyList<Quantity> Quantities => _quantities;

public int NumberOfQuantities => _quantities.Length;

public Quantity GetQuantityByName(string name)
{
return _quantities.Single(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
}


public IEnumerable<Overload> GetOverloads(Quantity quantity)
{
var multiplications = GetMultiplicationOverloads(quantity);
foreach (var multiplication in multiplications)
{
yield return multiplication;
yield return new Overload(multiplication.Result, multiplication.Right, multiplication.Left);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this an intended duplicate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was intended, but is wrong. My thought was that multiplication is communicative, but the other multiplication will end up in the other type.

}
var divisions = GetDivisionOverloads(quantity);
foreach (var division in divisions)
{
yield return division;
}
}
}
}
39 changes: 39 additions & 0 deletions UnitsNet.OperatorOverloads/Quantity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;


namespace UnitsNet.OperatorOverloads
{
public class Localization
{
public string Culture { get; set; }
public List<string> Abbreviations { get; set; }
}

public class Unit
{
public string SingularName { get; set; }
public string PluralName { get; set; }
public string FromUnitToBaseFunc { get; set; }
public string FromBaseToUnitFunc { get; set; }
public List<Localization> Localization { get; set; }
}

public class Quantity
{
public string Name { get; set; }
public string BaseUnit { get; set; }
public string XmlDoc { get; set; }
public List<Unit> Units { get; set; }

/// <summary>
/// m kg s A K mol cd
/// </summary>
public int[] SiArray { get; set; }

public override string ToString()
{
return Name;
}
}
}
11 changes: 11 additions & 0 deletions UnitsNet.OperatorOverloads/UnitsNet.OperatorOverloads.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project DefaultTargets="Build;NCrunchOutputDataQuery" Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Remove="UnitsNet.OperatorOverloads.v3.ncrunchproject" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="newtonsoft.json" Version="10.0.3" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ProjectConfiguration>
<Settings>
<PreviouslyBuiltSuccessfully>True</PreviouslyBuiltSuccessfully>
</Settings>
</ProjectConfiguration>
Loading