diff --git a/exercises/diffie-hellman/DiffieHellmanTest.cs b/exercises/diffie-hellman/DiffieHellmanTest.cs index dd66ac78eb..3cb9ca503c 100644 --- a/exercises/diffie-hellman/DiffieHellmanTest.cs +++ b/exercises/diffie-hellman/DiffieHellmanTest.cs @@ -1,78 +1,59 @@ -using System.Linq; +// This file was auto-generated based on version 1.0.0 of the canonical data. + +using System.Linq; using System.Numerics; using Xunit; public class DiffieHellmanTest { [Fact] - public void Private_key_in_range() + public void Private_key_is_in_range_1_p() { - var primeP = new BigInteger(23); - var privateKeys = Enumerable.Range(0, 10).Select(_ => DiffieHellman.PrivateKey(primeP)).ToList(); - Assert.All(privateKeys, privateKey => + var p = new BigInteger(7919); + var privateKeys = Enumerable.Range(0, 10).Select(_ => DiffieHellman.PrivateKey(p)).ToArray(); + foreach (var privateKey in privateKeys) { - Assert.InRange(privateKey, new BigInteger(1), primeP - new BigInteger(1)); - }); - } - - // Note: due to the nature of randomness, there is always a chance that this test fails - // Be sure to check the actual generated values - [Fact(Skip = "Remove to run test")] - public void Private_key_randomly_generated() - { - var primeP = new BigInteger(7919); - var privateKeys = Enumerable.Range(0, 5).Select(_ => DiffieHellman.PrivateKey(primeP)).ToList(); - Assert.Equal(privateKeys.Distinct().Count(), privateKeys.Count); + Assert.InRange(privateKey, new BigInteger(1), p); + } } [Fact(Skip = "Remove to run test")] - public void Public_key_correctly_calculated() + public void Private_key_is_random() { - var primeP = new BigInteger(23); - var primeG = new BigInteger(5); - var privateKey = new BigInteger(6); - - var actual = DiffieHellman.PublicKey(primeP, primeG, privateKey); - Assert.Equal(new BigInteger(8), actual); + var p = new BigInteger(7919); + var privateKeys = Enumerable.Range(0, 10).Select(_ => DiffieHellman.PrivateKey(p)).ToArray(); + Assert.Equal(privateKeys.Distinct().Count(), privateKeys.Length); } [Fact(Skip = "Remove to run test")] - public void Secret_key_correctly_calculated() + public void Can_calculate_public_key_using_private_key() { - var primeP = new BigInteger(23); - var publicKey = new BigInteger(19); + var p = new BigInteger(23); + var g = new BigInteger(5); var privateKey = new BigInteger(6); - - var actual = DiffieHellman.Secret(primeP, publicKey, privateKey); - Assert.Equal(new BigInteger(2), actual); + Assert.Equal(new BigInteger(8), DiffieHellman.PublicKey(p, g, privateKey)); } [Fact(Skip = "Remove to run test")] - public void Secret_key_correctly_calculated_when_using_large_primes() + public void Can_calculate_secret_using_other_partys_public_key() { - var primeP = BigInteger.Parse("120227323036150778550155526710966921740030662694578947298423549235265759593711587341037426347114541533006628856300552706996143592240453345642869233562886752930249953227657883929905072620233073626594386072962776144691433658814261874113232461749035425712805067202910389407991986070558964461330091797026762932543"); - var publicKey = BigInteger.Parse("75205441154357919442925546169208711235485855904969178206313309299205868312399046149367516336607966149689640419216591714331722664409474612463910928128055994157922930443733535659848264364106037925315974095321112757711756912144137705613776063541350548911512715512539186192176020596861210448363099541947258202188"); - var privateKey = BigInteger.Parse("2483479393625932939911081304356888505153797135447327501792696199190469015215177630758617902200417377685436170904594686456961202706692908603181062371925882"); - var expected = BigInteger.Parse("70900735223964890815905879227737819348808518698920446491346508980461201746567735331455825644429877946556431095820785835497384849778344216981228226252639932672153547963980483673419756271345828771971984887453014488572245819864454136618980914729839523581263886740821363010486083940557620831348661126601106717071"); - var actual = DiffieHellman.Secret(primeP, publicKey, privateKey); - Assert.Equal(expected, actual); + var p = new BigInteger(23); + var theirPublicKey = new BigInteger(19); + var myPrivateKey = new BigInteger(6); + Assert.Equal(new BigInteger(2), DiffieHellman.Secret(p, theirPublicKey, myPrivateKey)); } [Fact(Skip = "Remove to run test")] - public void Test_exchange() + public void Key_exchange() { - var primeP = new BigInteger(23); - var primeG = new BigInteger(5); - - var privateKeyA = DiffieHellman.PrivateKey(primeP); - var privateKeyB = DiffieHellman.PrivateKey(primeP); - - var publicKeyA = DiffieHellman.PublicKey(primeP, primeG, privateKeyA); - var publicKeyB = DiffieHellman.PublicKey(primeP, primeG, privateKeyB); - - var secretA = DiffieHellman.Secret(primeP, publicKeyB, privateKeyA); - var secretB = DiffieHellman.Secret(primeP, publicKeyA, privateKeyB); - - Assert.Equal(secretB, secretA); + var p = new BigInteger(23); + var g = new BigInteger(5); + var alicePrivateKey = DiffieHellman.PrivateKey(p); + var bobPrivateKey = DiffieHellman.PrivateKey(p); + var alicePublicKey = DiffieHellman.PublicKey(p, g, alicePrivateKey); + var bobPublicKey = DiffieHellman.PublicKey(p, g, bobPrivateKey); + var secretA = DiffieHellman.Secret(p, bobPublicKey, alicePrivateKey); + var secretB = DiffieHellman.Secret(p, alicePublicKey, bobPrivateKey); + Assert.Equal(secretA, secretB); } -} +} \ No newline at end of file diff --git a/generators/Exercises/Generators/DiffieHellman.cs b/generators/Exercises/Generators/DiffieHellman.cs new file mode 100644 index 0000000000..8e5d52e2af --- /dev/null +++ b/generators/Exercises/Generators/DiffieHellman.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using Exercism.CSharp.Output; +using Exercism.CSharp.Output.Rendering; +using Humanizer; + +namespace Exercism.CSharp.Exercises.Generators +{ + public class DiffieHellman : GeneratorExercise + { + protected override void UpdateTestMethod(TestMethod testMethod) + { + switch (testMethod.Property) + { + case "privateKeyIsInRange": + UpdateTestMethodForPrivateKeyIsInRangeProperty(testMethod); + break; + case "privateKeyIsRandom": + UpdateTestMethodForPrivateKeyIsRandomProperty(testMethod); + break; + case "keyExchange": + UpdateTestMethodForKeyExchangeProperty(testMethod); + break; + default: + UpdateTestMethodForNormalProperty(testMethod); + break; + } + } + + private void UpdateTestMethodForPrivateKeyIsInRangeProperty(TestMethod testMethod) + { + testMethod.TestedMethod = "PrivateKey"; + testMethod.Expected["greaterThan"] = new BigInteger(testMethod.Expected["greaterThan"]); + + testMethod.Arrange = RenderArrangeForPrivateKeyIsInRangeProperty(testMethod); + testMethod.Assert = RenderAssertForPrivateKeyIsInRangeProperty(testMethod); + } + + private string RenderArrangeForPrivateKeyIsInRangeProperty(TestMethod testMethod) + => RenderRandomPrivateKeysArrange(testMethod); + + private string RenderAssertForPrivateKeyIsInRangeProperty(TestMethod testMethod) + { + var arrange = new StringBuilder(); + + arrange.AppendLine("foreach (var privateKey in privateKeys)"); + arrange.AppendLine("{"); + arrange.AppendLine(((string)Render.AssertInRange("privateKey", Render.Object(testMethod.Expected["greaterThan"]), "p")).Indent()); + arrange.AppendLine("}"); + + return arrange.ToString(); + } + + private void UpdateTestMethodForPrivateKeyIsRandomProperty(TestMethod testMethod) + { + testMethod.TestedMethod = "PrivateKey"; + testMethod.Arrange = RenderArrangeForPrivateKeyIsRandomProperty(testMethod); + testMethod.Assert = RenderAssertForPrivateKeyIsRandomProperty(); + } + + private string RenderArrangeForPrivateKeyIsRandomProperty(TestMethod testMethod) + => RenderRandomPrivateKeysArrange(testMethod); + + private string RenderAssertForPrivateKeyIsRandomProperty() + => Render.AssertEqual("privateKeys.Distinct().Count()", "privateKeys.Length"); + + private string RenderRandomPrivateKeysArrange(TestMethod testMethod) + { + var arrange = new StringBuilder(); + + arrange.AppendLine(Render.Variable("p", Render.BigInteger(new BigInteger(7919)))); + arrange.AppendLine(Render.Variable("privateKeys", $"Enumerable.Range(0, 10).Select(_ => {testMethod.TestedClass}.{testMethod.TestedMethod}(p)).ToArray()")); + + return arrange.ToString(); + } + + private void UpdateTestMethodForKeyExchangeProperty(TestMethod testMethod) + { + testMethod.UseVariablesForInput = true; + + var keys = testMethod.Input.Keys.ToArray(); + + foreach (var key in keys) + testMethod.Input[key] = ConvertKeyExchangeInput(testMethod.Input[key], testMethod); + + testMethod.Assert = RenderAssertForKeyExchangeProperty(); + } + + private static dynamic ConvertKeyExchangeInput(dynamic input, TestMethod testMethod) + { + switch (input) + { + case long l: + return new BigInteger(l); + case string str: + return new UnescapedValue($"{testMethod.TestedClass}.{str.Pascalize()}"); + default: + return input; + } + } + + private string RenderAssertForKeyExchangeProperty() => Render.AssertEqual("secretA", "secretB"); + + private static void UpdateTestMethodForNormalProperty(TestMethod testMethod) + { + testMethod.UseVariablesForInput = true; + testMethod.Expected = new BigInteger(testMethod.Expected); + + var keys = testMethod.Input.Keys.ToArray(); + + foreach (var key in keys) + testMethod.Input[key] = new BigInteger(testMethod.Input[key]); + } + + protected override void UpdateNamespaces(ISet namespaces) + { + namespaces.Add(typeof(BigInteger).Namespace); + namespaces.Add(typeof(Enumerable).Namespace); + } + } +} \ No newline at end of file diff --git a/generators/Output/Rendering/Render.cs b/generators/Output/Rendering/Render.cs index 85249f2a51..00235f89f7 100644 --- a/generators/Output/Rendering/Render.cs +++ b/generators/Output/Rendering/Render.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Numerics; using System.Text.RegularExpressions; namespace Exercism.CSharp.Output.Rendering @@ -23,6 +24,7 @@ public string Object(object val) case char c: return Char(c); case DateTime dateTime: return DateTime(dateTime); case Regex regex: return Regex(regex); + case BigInteger bigInt: return BigInteger(bigInt); default: if (IsList(val)) return List((dynamic)val); diff --git a/generators/Output/Rendering/RenderAssert.cs b/generators/Output/Rendering/RenderAssert.cs index 4f857fd52b..6efb0ce516 100644 --- a/generators/Output/Rendering/RenderAssert.cs +++ b/generators/Output/Rendering/RenderAssert.cs @@ -25,6 +25,9 @@ public string AssertBoolean(bool expected, string actual) public string AssertMatches(string expected, string actual) => RenderTemplate("AssertMatches", new { expected, actual }); + public string AssertInRange(string expected, string lower, string upper) + => RenderTemplate("AssertInRange", new { expected, lower, upper }); + public string AssertThrows(Type expectedException, string actual) => RenderTemplate("AssertThrows", new { expected = expectedException.Name, actual }); diff --git a/generators/Output/Rendering/RenderNumber.cs b/generators/Output/Rendering/RenderNumber.cs index e6a17eab31..1b3996a6d7 100644 --- a/generators/Output/Rendering/RenderNumber.cs +++ b/generators/Output/Rendering/RenderNumber.cs @@ -1,4 +1,5 @@ using System.Globalization; +using System.Numerics; namespace Exercism.CSharp.Output.Rendering { @@ -13,5 +14,7 @@ public partial class Render public string Ulong(ulong ulng) => $"{ulng}UL"; public string Uint(uint ui) => string.Format("0x{0:X}u", ui); + + public string BigInteger(BigInteger bigInt) => $"new BigInteger({bigInt.ToString()})"; } } \ No newline at end of file diff --git a/generators/Output/Rendering/Templates/_AssertInRange.liquid b/generators/Output/Rendering/Templates/_AssertInRange.liquid new file mode 100644 index 0000000000..b425bdae89 --- /dev/null +++ b/generators/Output/Rendering/Templates/_AssertInRange.liquid @@ -0,0 +1 @@ +Assert.InRange({{ expected }}, {{ lower }}, {{ upper }}); \ No newline at end of file