From 403bc41904b5cacc33d72b27a4fefdba467c3bcc Mon Sep 17 00:00:00 2001 From: bazzilic Date: Fri, 20 Aug 2021 13:58:38 +0800 Subject: [PATCH 1/5] FromDouble(...) : ~1.5% avg improvement --- BigFraction.sln | 7 ++++ .../OptimizationsBenchmark.csproj | 16 ++++++++++ OptimizationsBenchmark/Program.cs | 32 +++++++++++++++++++ src/Aprismatic.BigFraction/BigFraction.cs | 31 +++++++++--------- 4 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 OptimizationsBenchmark/OptimizationsBenchmark.csproj create mode 100644 OptimizationsBenchmark/Program.cs diff --git a/BigFraction.sln b/BigFraction.sln index e65b9b4..a30b7f1 100644 --- a/BigFraction.sln +++ b/BigFraction.sln @@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aprismatic.BigFraction", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BigFractionTest", "test\BigFractionTest\BigFractionTest.csproj", "{164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OptimizationsBenchmark", "OptimizationsBenchmark\OptimizationsBenchmark.csproj", "{40004B57-2480-43B3-91DE-1B443986EF2D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,6 +42,10 @@ Global {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}.Release|Any CPU.Build.0 = Release|Any CPU + {40004B57-2480-43B3-91DE-1B443986EF2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40004B57-2480-43B3-91DE-1B443986EF2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40004B57-2480-43B3-91DE-1B443986EF2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40004B57-2480-43B3-91DE-1B443986EF2D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -47,6 +53,7 @@ Global GlobalSection(NestedProjects) = preSolution {671B62C9-5C1F-45BE-A064-85113E652AFF} = {3303BAA0-773E-4F5D-B699-F3CA3CD5BBB4} {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F} = {9993F7DE-AFDC-4D82-A030-C3C5E373CE77} + {40004B57-2480-43B3-91DE-1B443986EF2D} = {9993F7DE-AFDC-4D82-A030-C3C5E373CE77} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {575666A0-07A3-4BB3-95A8-281FA6583CE7} diff --git a/OptimizationsBenchmark/OptimizationsBenchmark.csproj b/OptimizationsBenchmark/OptimizationsBenchmark.csproj new file mode 100644 index 0000000..dad5e2c --- /dev/null +++ b/OptimizationsBenchmark/OptimizationsBenchmark.csproj @@ -0,0 +1,16 @@ + + + + Exe + net5.0 + + + + + + + + + + + diff --git a/OptimizationsBenchmark/Program.cs b/OptimizationsBenchmark/Program.cs new file mode 100644 index 0000000..f3f251a --- /dev/null +++ b/OptimizationsBenchmark/Program.cs @@ -0,0 +1,32 @@ +using System; +using Aprismatic; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; + +namespace OptimizationsBenchmark +{ + class Program + { + static void Main(string[] args) + { + var summary = BenchmarkRunner.Run(); + } + } + + public class FromDouble + { + private readonly double val; + private readonly Random rnd = new(); + + public FromDouble() + { + val = rnd.NextDouble() * rnd.Next() * (-1); + } + + //[Benchmark] + //public BigFraction Old() => BigFraction.FromDoubleOld(val, 1e-15); + + [Benchmark] + public BigFraction New() => BigFraction.FromDouble(val, 1e-15); + } +} diff --git a/src/Aprismatic.BigFraction/BigFraction.cs b/src/Aprismatic.BigFraction/BigFraction.cs index 73a16da..89dbd54 100644 --- a/src/Aprismatic.BigFraction/BigFraction.cs +++ b/src/Aprismatic.BigFraction/BigFraction.cs @@ -253,41 +253,39 @@ public static BigFraction FromDouble(double value, double accuracy) } var sign = Math.Sign(value); + var signbi = + sign == -1 ? BigInteger.MinusOne : + sign == 0 ? BigInteger.Zero : + BigInteger.One; if (sign == -1) - { value = Math.Abs(value); - } // Accuracy is the maximum relative error; convert to absolute maxError - double maxError = sign == 0 ? accuracy : value * accuracy; + var maxError = sign == 0 ? accuracy : value * accuracy; var n = new BigInteger(value); value -= Math.Floor(value); if (value < maxError) - { - return new BigFraction(sign * n, BigInteger.One); - } + return new BigFraction(signbi * n, BigInteger.One); if (1 - maxError < value) - { - return new BigFraction(sign * (n + 1), BigInteger.One); - } + return new BigFraction(signbi * (n + BigInteger.One), BigInteger.One); // The lower fraction is 0/1 - int lower_n = 0; - int lower_d = 1; + var lower_n = 0; + var lower_d = 1; // The upper fraction is 1/1 - int upper_n = 1; - int upper_d = 1; + var upper_n = 1; + var upper_d = 1; while (true) { // The middle fraction is (lower_n + upper_n) / (lower_d + upper_d) - int middle_n = lower_n + upper_n; - int middle_d = lower_d + upper_d; + var middle_n = lower_n + upper_n; + var middle_d = lower_d + upper_d; if (middle_d * (value + maxError) < middle_n) { @@ -304,7 +302,8 @@ public static BigFraction FromDouble(double value, double accuracy) else { // Middle is our best fraction - return new BigFraction((n * middle_d + middle_n) * sign, middle_d); + var middle_d_bi = new BigInteger(middle_d); + return new BigFraction((n * middle_d_bi + middle_n) * signbi, middle_d_bi); } } } From 52837e946a91c9b8d16a157c99820d8748bccabb Mon Sep 17 00:00:00 2001 From: bazzilic Date: Fri, 20 Aug 2021 14:42:55 +0800 Subject: [PATCH 2/5] ToDecimal() : 15-40 % improvement in certain cases (when |Numerator| or |Denominator| > decimal.MaxValue) --- OptimizationsBenchmark/Program.cs | 25 +++++++++++++------ src/Aprismatic.BigFraction/BigFraction.cs | 30 ++++++++++------------- test/BigFractionTest/BigFractionTest.cs | 28 +++++++++++++-------- 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/OptimizationsBenchmark/Program.cs b/OptimizationsBenchmark/Program.cs index f3f251a..bbb3bf9 100644 --- a/OptimizationsBenchmark/Program.cs +++ b/OptimizationsBenchmark/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Numerics; using Aprismatic; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; @@ -9,24 +10,34 @@ class Program { static void Main(string[] args) { - var summary = BenchmarkRunner.Run(); + var summary = BenchmarkRunner.Run(); } } - public class FromDouble + public class ToDecimal { - private readonly double val; + private readonly BigFraction val_lt1, val_gt1; private readonly Random rnd = new(); - public FromDouble() + public ToDecimal() { - val = rnd.NextDouble() * rnd.Next() * (-1); + val_lt1 = new BigFraction(new BigInteger(decimal.MaxValue), new BigInteger(decimal.MaxValue)) * + new BigFraction(BigInteger.One, new BigInteger(2)); + val_gt1 = new BigFraction(new BigInteger(decimal.MaxValue), new BigInteger(decimal.MaxValue)) * 2; + + Console.WriteLine(val_lt1); } + [Benchmark] + public decimal Old_LT1() => val_lt1.ToDecimal(); + //[Benchmark] - //public BigFraction Old() => BigFraction.FromDoubleOld(val, 1e-15); + //public decimal New_LT1() => val_lt1.ToDecimalNew(); [Benchmark] - public BigFraction New() => BigFraction.FromDouble(val, 1e-15); + public decimal Old_GT1() => val_gt1.ToDecimal(); + + //[Benchmark] + //public decimal New_GT1() => val_gt1.ToDecimalNew(); } } diff --git a/src/Aprismatic.BigFraction/BigFraction.cs b/src/Aprismatic.BigFraction/BigFraction.cs index 89dbd54..f8fc62f 100644 --- a/src/Aprismatic.BigFraction/BigFraction.cs +++ b/src/Aprismatic.BigFraction/BigFraction.cs @@ -221,26 +221,22 @@ public void Simplify() public double ToDouble() => (double)Numerator / (double)Denominator; - public decimal ToDecimal() => DecimalScale(this); - - private static decimal DecimalScale(BigFraction bigFraction) + public decimal ToDecimal() { - if (bigFraction.Numerator <= MAX_DECIMAL && bigFraction.Numerator >= MIN_DECIMAL && - bigFraction.Denominator <= MAX_DECIMAL && bigFraction.Denominator >= MIN_DECIMAL) - return (decimal)bigFraction.Numerator / (decimal)bigFraction.Denominator; + if (Numerator == 0) + return 0; + + if (Numerator <= MAX_DECIMAL && Numerator >= MIN_DECIMAL && + Denominator <= MAX_DECIMAL && Denominator >= MIN_DECIMAL) + return (decimal)Numerator / (decimal)Denominator; + + var intPart = Numerator / Denominator; - var intPart = bigFraction.Numerator / bigFraction.Denominator; if (intPart != 0) - { - return (decimal)intPart + DecimalScale(bigFraction - intPart); - } - else - { - if (bigFraction.Numerator == 0) - return 0; - else - return 1 / DecimalScale(1 / bigFraction); ; - } + return (decimal)intPart + (this - intPart).ToDecimal(); + + var thisinverse = new BigFraction(Denominator, Numerator); // == 1 / this + return 1 / thisinverse.ToDecimal(); } //Conversion from double to fraction diff --git a/test/BigFractionTest/BigFractionTest.cs b/test/BigFractionTest/BigFractionTest.cs index d53c02b..b6bc36c 100644 --- a/test/BigFractionTest/BigFractionTest.cs +++ b/test/BigFractionTest/BigFractionTest.cs @@ -402,14 +402,22 @@ public void doubletofraction() } } -namespace DecimalScaleTest +namespace DecimalTests { public class DecimalScaleTest { + [Fact(DisplayName = "Simple cases")] + public void SimpleCases() + { + Assert.Equal(1m, BigFraction.One.ToDecimal()); + Assert.Equal(0m, BigFraction.Zero.ToDecimal()); + Assert.Equal(-1m, BigFraction.MinusOne.ToDecimal()); + } + [Fact(DisplayName = "( > MAX ) / ( < MAX )")] public void Case1() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MaxValue) * 10, new BigInteger(decimal.MaxValue) - 1); + var bigA = new BigFraction(new BigInteger(decimal.MaxValue) * 10, new BigInteger(decimal.MaxValue) - 1); var a = bigA.ToDecimal(); Assert.Equal(10m, a); } @@ -417,7 +425,7 @@ public void Case1() [Fact(DisplayName = "( < MAX ) / ( > MAX )")] public void Case2() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MaxValue) / 2, new BigInteger(decimal.MaxValue) * 10); + var bigA = new BigFraction(new BigInteger(decimal.MaxValue) / 2, new BigInteger(decimal.MaxValue) * 10); var a = bigA.ToDecimal(); Assert.Equal(0.05m, a); } @@ -425,7 +433,7 @@ public void Case2() [Fact(DisplayName = "( > MAX ) / ( > MAX )")] public void Case3() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MaxValue) * 100, new BigInteger(decimal.MaxValue) * 10); + var bigA = new BigFraction(new BigInteger(decimal.MaxValue) * 100, new BigInteger(decimal.MaxValue) * 10); var a = bigA.ToDecimal(); Assert.Equal(10m, a); } @@ -433,7 +441,7 @@ public void Case3() [Fact(DisplayName = "( < MIN ) / ( < MAX )")] public void Case4() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MinValue) * 10, new BigInteger(decimal.MaxValue) / 2); + var bigA = new BigFraction(new BigInteger(decimal.MinValue) * 10, new BigInteger(decimal.MaxValue) / 2); var a = bigA.ToDecimal(); Assert.Equal(-20m, a); } @@ -441,7 +449,7 @@ public void Case4() [Fact(DisplayName = "( < MIN ) / ( > MAX )")] public void Case5() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MinValue) * 10, new BigInteger(decimal.MaxValue) * 20); + var bigA = new BigFraction(new BigInteger(decimal.MinValue) * 10, new BigInteger(decimal.MaxValue) * 20); var a = bigA.ToDecimal(); Assert.Equal(-0.5m, a); } @@ -449,7 +457,7 @@ public void Case5() [Fact(DisplayName = "( < MIN ) / ( < MIN )")] public void Case6() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MinValue) * 10, new BigInteger(decimal.MinValue) / 2); + var bigA = new BigFraction(new BigInteger(decimal.MinValue) * 10, new BigInteger(decimal.MinValue) / 2); var a = bigA.ToDecimal(); Assert.Equal(20m, a); } @@ -457,7 +465,7 @@ public void Case6() [Fact(DisplayName = "( < MAX ) / ( < MIN )")] public void Case7() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MaxValue) / 2, new BigInteger(decimal.MinValue) * 5); + var bigA = new BigFraction(new BigInteger(decimal.MaxValue) / 2, new BigInteger(decimal.MinValue) * 5); var a = bigA.ToDecimal(); Assert.Equal(-0.1m, a); } @@ -465,7 +473,7 @@ public void Case7() [Fact(DisplayName = "( > MAX ) / ( < MIN )")] public void Case8() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MaxValue) * 10, new BigInteger(decimal.MinValue) * 5); + var bigA = new BigFraction(new BigInteger(decimal.MaxValue) * 10, new BigInteger(decimal.MinValue) * 5); var a = bigA.ToDecimal(); Assert.Equal(-2m, a); } @@ -474,7 +482,7 @@ public void Case8() [Fact(DisplayName = "( = MAX ) / ( = MIN )")] public void Case9() { - BigFraction bigA = new BigFraction(new BigInteger(decimal.MaxValue), new BigInteger(decimal.MinValue)); + var bigA = new BigFraction(new BigInteger(decimal.MaxValue), new BigInteger(decimal.MinValue)); var a = bigA.ToDecimal(); Assert.Equal(-1m, a); } From 0d65e15b47cbb20f1a2ce0d33eae52c47c3218f3 Mon Sep 17 00:00:00 2001 From: bazzilic Date: Fri, 20 Aug 2021 15:14:41 +0800 Subject: [PATCH 3/5] (int,int) and (long,long) constructors + more idiomatic checks for zero --- src/Aprismatic.BigFraction/BigFraction.cs | 16 +++++++-- test/BigFractionTest/BigFractionTest.cs | 42 +++++++++++++++-------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/Aprismatic.BigFraction/BigFraction.cs b/src/Aprismatic.BigFraction/BigFraction.cs index f8fc62f..351a356 100644 --- a/src/Aprismatic.BigFraction/BigFraction.cs +++ b/src/Aprismatic.BigFraction/BigFraction.cs @@ -38,6 +38,18 @@ public BigFraction(BigInteger num, BigInteger den) Denominator = den; } + public BigFraction(int num, int den) + { + Numerator = new BigInteger(num); + Denominator = new BigInteger(den); + } + + public BigFraction(long num, long den) + { + Numerator = new BigInteger(num); + Denominator = new BigInteger(den); + } + //BigInteger constructor public BigFraction(BigInteger num) { @@ -184,7 +196,7 @@ public override bool Equals(object obj) { if (!(obj is BigFraction comparebigfrac)) return false; - if (Numerator == 0 && comparebigfrac.Numerator == 0) return true; // if both values are zero + if (IsZero && comparebigfrac.IsZero) return true; return Numerator * comparebigfrac.Denominator == comparebigfrac.Numerator * Denominator; } @@ -223,7 +235,7 @@ public void Simplify() public decimal ToDecimal() { - if (Numerator == 0) + if (IsZero) return 0; if (Numerator <= MAX_DECIMAL && Numerator >= MIN_DECIMAL && diff --git a/test/BigFractionTest/BigFractionTest.cs b/test/BigFractionTest/BigFractionTest.cs index b6bc36c..c870e0c 100644 --- a/test/BigFractionTest/BigFractionTest.cs +++ b/test/BigFractionTest/BigFractionTest.cs @@ -18,49 +18,63 @@ public ConstructorTest(ITestOutputHelper output) [Fact(DisplayName = "Integer")] public void IntConstructor() { - BigFraction a = new BigFraction(-10); - BigFraction expected = new BigFraction(new BigInteger(-10), BigInteger.One); + var a = new BigFraction(-10); + var expected = new BigFraction(new BigInteger(-10), BigInteger.One); Assert.Equal(expected, a); } [Fact(DisplayName = "Long")] public void LongConstructor() { - BigFraction a = new BigFraction(12147483647); - BigFraction expected = new BigFraction(new BigInteger(12147483647), BigInteger.One); + var a = new BigFraction(12147483647); + var expected = new BigFraction(new BigInteger(12147483647), BigInteger.One); Assert.Equal(expected, a); } [Fact(DisplayName = "Double")] public void DouConstructor() { - BigFraction a = new BigFraction(6545.99); - BigFraction expected = new BigFraction(new BigInteger(654599), new BigInteger(100)); + var a = new BigFraction(6545.99); + var expected = new BigFraction(new BigInteger(654599), new BigInteger(100)); Assert.Equal(expected, a); } [Fact(DisplayName = "Decimal")] public void DecConstructor() { - BigFraction a = new BigFraction(new decimal(8984.65)); - BigFraction expected = new BigFraction(new BigInteger(898465), new BigInteger(100)); + var a = new BigFraction(new decimal(8984.65)); + var expected = new BigFraction(new BigInteger(898465), new BigInteger(100)); Assert.Equal(expected, a); } [Fact(DisplayName = "BigInteger")] public void BigIntConstructor() { - BigFraction a = new BigFraction(new BigInteger(33)); - BigFraction expected = new BigFraction(new BigInteger(33), new BigInteger(1)); + var a = new BigFraction(new BigInteger(33)); + var expected = new BigFraction(new BigInteger(33), new BigInteger(1)); Assert.Equal(expected, a); } - [Fact(DisplayName = "Fractional")] + [Fact(DisplayName = "Fractions")] public void FracConstructor() { - BigFraction a = new BigFraction(new BigInteger(3300), new BigInteger(9900)); - BigFraction expected = new BigFraction(new BigInteger(33), new BigInteger(99)); - Assert.Equal(expected, a); + var bibi = new BigFraction(new BigInteger(3300), new BigInteger(9900)); + var expected = new BigFraction(new BigInteger(33), new BigInteger(99)); + Assert.Equal(new BigInteger(3300), bibi.Numerator); + Assert.Equal(new BigInteger(9900), bibi.Denominator); + Assert.Equal(expected, bibi); + + var inin = new BigFraction(132, 26); + expected = new BigFraction(new BigInteger(66), new BigInteger(13)); + Assert.Equal(new BigInteger(132), inin.Numerator); + Assert.Equal(new BigInteger(26), inin.Denominator); + Assert.Equal(expected, inin); + + var lolo = new BigFraction(222L, 50L); + expected = new BigFraction(new BigInteger(111), new BigInteger(25)); + Assert.Equal(new BigInteger(222), lolo.Numerator); + Assert.Equal(new BigInteger(50), lolo.Denominator); + Assert.Equal(expected, lolo); } } } From ec2807a19ee7ad4a434ca81268809f233b0834bf Mon Sep 17 00:00:00 2001 From: bazzilic Date: Fri, 20 Aug 2021 18:08:14 +0800 Subject: [PATCH 4/5] 2x-2.5x speed up for opertaions where one of the operands in a BigInteger. More tests --- OptimizationsBenchmark/Program.cs | 58 +++++-- src/Aprismatic.BigFraction/BigFraction.cs | 187 ++++++++++++++++++---- test/BigFractionTest/BigFractionTest.cs | 166 ++++++++++++++++--- 3 files changed, 340 insertions(+), 71 deletions(-) diff --git a/OptimizationsBenchmark/Program.cs b/OptimizationsBenchmark/Program.cs index bbb3bf9..77f7a7e 100644 --- a/OptimizationsBenchmark/Program.cs +++ b/OptimizationsBenchmark/Program.cs @@ -10,34 +10,62 @@ class Program { static void Main(string[] args) { - var summary = BenchmarkRunner.Run(); + var summary = BenchmarkRunner.Run(); } } - public class ToDecimal + public class Operators { - private readonly BigFraction val_lt1, val_gt1; + private readonly BigFraction bf; + private readonly BigInteger bi; private readonly Random rnd = new(); - public ToDecimal() + public Operators() { - val_lt1 = new BigFraction(new BigInteger(decimal.MaxValue), new BigInteger(decimal.MaxValue)) * - new BigFraction(BigInteger.One, new BigInteger(2)); - val_gt1 = new BigFraction(new BigInteger(decimal.MaxValue), new BigInteger(decimal.MaxValue)) * 2; - - Console.WriteLine(val_lt1); + bf = new BigFraction(123456.3456); + bi = new BigInteger(43765); } [Benchmark] - public decimal Old_LT1() => val_lt1.ToDecimal(); + public BigFraction AddOld() => bf + new BigFraction(bi); + + [Benchmark] + public BigFraction AddNew() => bf + bi; + + [Benchmark] + public BigFraction SubOld() => bf - new BigFraction(bi); + + [Benchmark] + public BigFraction SubNew() => bf - bi; + + [Benchmark] + public BigFraction DivOld() => bf / new BigFraction(bi); - //[Benchmark] - //public decimal New_LT1() => val_lt1.ToDecimalNew(); + [Benchmark] + public BigFraction DivNew() => bf / bi; + + [Benchmark] + public BigFraction MulOld() => bf * new BigFraction(bi); [Benchmark] - public decimal Old_GT1() => val_gt1.ToDecimal(); + public BigFraction MulNew() => bf * bi; - //[Benchmark] - //public decimal New_GT1() => val_gt1.ToDecimalNew(); + [Benchmark] + public bool EqOld() => bf == new BigFraction(bi); + + [Benchmark] + public bool EqNew() => bf == bi; + + [Benchmark] + public bool NeqOld() => bf != new BigFraction(bi); + + [Benchmark] + public bool NeqNew() => bf != bi; + + [Benchmark] + public bool GtOld() => bf > new BigFraction(bi); + + [Benchmark] + public bool GtNew() => bf > bi; } } diff --git a/src/Aprismatic.BigFraction/BigFraction.cs b/src/Aprismatic.BigFraction/BigFraction.cs index 351a356..5c6e11e 100644 --- a/src/Aprismatic.BigFraction/BigFraction.cs +++ b/src/Aprismatic.BigFraction/BigFraction.cs @@ -110,58 +110,141 @@ public BigFraction(int i) //Operator % public static BigFraction operator %(BigFraction r, BigInteger mod) { - BigInteger modmulden = r.Denominator * mod; - BigInteger remainder = r.Numerator % modmulden; - BigFraction answer = new BigFraction(remainder, r.Denominator); + var modmulden = r.Denominator * mod; + var remainder = r.Numerator % modmulden; + var answer = new BigFraction(remainder, r.Denominator); return answer; } //Operator > - public static Boolean operator >(BigFraction r1, BigFraction r2) + public static bool operator >(BigFraction r1, BigFraction r2) { - BigInteger r1compare = r1.Numerator * r2.Denominator; - BigInteger r2compare = r2.Numerator * r1.Denominator; - if (r1compare.CompareTo(r2compare) == 1) { return true; } - else { return false; } + var r1compare = r1.Numerator * r2.Denominator; + var r2compare = r2.Numerator * r1.Denominator; + return r1compare.CompareTo(r2compare) == 1; + } + + public static bool operator >(BigFraction r1, BigInteger r2) + { + var r1compare = r1.Numerator; + var r2compare = r2 * r1.Denominator; + return r1compare.CompareTo(r2compare) == 1; + } + + public static bool operator >(BigInteger r1, BigFraction r2) + { + var r1compare = r1 * r2.Denominator; + var r2compare = r2.Numerator; + return r1compare.CompareTo(r2compare) == 1; } //Operator < - public static Boolean operator <(BigFraction r1, BigFraction r2) + public static bool operator <(BigFraction r1, BigFraction r2) { - BigInteger r1compare = r1.Numerator * r2.Denominator; - BigInteger r2compare = r2.Numerator * r1.Denominator; + var r1compare = r1.Numerator * r2.Denominator; + var r2compare = r2.Numerator * r1.Denominator; + return r1compare.CompareTo(r2compare) == -1; + } + + public static bool operator <(BigFraction r1, BigInteger r2) + { + var r1compare = r1.Numerator; + var r2compare = r2 * r1.Denominator; + return r1compare.CompareTo(r2compare) == -1; + } + + public static bool operator <(BigInteger r1, BigFraction r2) + { + var r1compare = r1 * r2.Denominator; + var r2compare = r2.Numerator; return r1compare.CompareTo(r2compare) == -1; } //Operator == - public static Boolean operator ==(BigFraction r1, BigFraction r2) + public static bool operator ==(BigFraction r1, BigFraction r2) + { + var r1compare = r1.Numerator * r2.Denominator; + var r2compare = r2.Numerator * r1.Denominator; + return r1compare.CompareTo(r2compare) == 0; + } + + public static bool operator ==(BigFraction r1, BigInteger r2) + { + var r1compare = r1.Numerator; + var r2compare = r2 * r1.Denominator; + return r1compare.CompareTo(r2compare) == 0; + } + + public static bool operator ==(BigInteger r1, BigFraction r2) { - BigInteger r1compare = r1.Numerator * r2.Denominator; - BigInteger r2compare = r2.Numerator * r1.Denominator; + var r1compare = r1 * r2.Denominator; + var r2compare = r2.Numerator; return r1compare.CompareTo(r2compare) == 0; } //Operator != - public static Boolean operator !=(BigFraction r1, BigFraction r2) + public static bool operator !=(BigFraction r1, BigFraction r2) { - BigInteger r1compare = r1.Numerator * r2.Denominator; - BigInteger r2compare = r2.Numerator * r1.Denominator; - return !(r1compare.CompareTo(r2compare) == 0); + var r1compare = r1.Numerator * r2.Denominator; + var r2compare = r2.Numerator * r1.Denominator; + return r1compare.CompareTo(r2compare) != 0; + } + + public static bool operator !=(BigFraction r1, BigInteger r2) + { + var r1compare = r1.Numerator; + var r2compare = r2 * r1.Denominator; + return r1compare.CompareTo(r2compare) != 0; + } + + public static bool operator !=(BigInteger r1, BigFraction r2) + { + var r1compare = r1 * r2.Denominator; + var r2compare = r2.Numerator; + return r1compare.CompareTo(r2compare) != 0; } //Operator <= - public static Boolean operator <=(BigFraction r1, BigFraction r2) + public static bool operator <=(BigFraction r1, BigFraction r2) + { + var r1compare = r1.Numerator * r2.Denominator; + var r2compare = r2.Numerator * r1.Denominator; + return r1compare.CompareTo(r2compare) == -1 || r1compare.CompareTo(r2compare) == 0; + } + + public static bool operator <=(BigFraction r1, BigInteger r2) + { + var r1compare = r1.Numerator; + var r2compare = r2 * r1.Denominator; + return r1compare.CompareTo(r2compare) == -1 || r1compare.CompareTo(r2compare) == 0; + } + + public static bool operator <=(BigInteger r1, BigFraction r2) { - BigInteger r1compare = r1.Numerator * r2.Denominator; - BigInteger r2compare = r2.Numerator * r1.Denominator; + var r1compare = r1 * r2.Denominator; + var r2compare = r2.Numerator; return r1compare.CompareTo(r2compare) == -1 || r1compare.CompareTo(r2compare) == 0; } //Operator >= - public static Boolean operator >=(BigFraction r1, BigFraction r2) + public static bool operator >=(BigFraction r1, BigFraction r2) { - BigInteger r1compare = r1.Numerator * r2.Denominator; - BigInteger r2compare = r2.Numerator * r1.Denominator; + var r1compare = r1.Numerator * r2.Denominator; + var r2compare = r2.Numerator * r1.Denominator; + return r1compare.CompareTo(r2compare) == 1 || r1compare.CompareTo(r2compare) == 0; + } + + public static bool operator >=(BigFraction r1, BigInteger r2) + { + var r1compare = r1.Numerator; + var r2compare = r2 * r1.Denominator; + return r1compare.CompareTo(r2compare) == 1 || r1compare.CompareTo(r2compare) == 0; + } + + public static bool operator >=(BigInteger r1, BigFraction r2) + { + var r1compare = r1 * r2.Denominator; + var r2compare = r2.Numerator; return r1compare.CompareTo(r2compare) == 1 || r1compare.CompareTo(r2compare) == 0; } @@ -172,6 +255,18 @@ public BigFraction(int i) a.Denominator * b.Denominator); } + public static BigFraction operator -(BigFraction a, BigInteger b) + { + return new BigFraction(a.Numerator - b * a.Denominator, + a.Denominator); + } + + public static BigFraction operator -(BigInteger a, BigFraction b) + { + return new BigFraction(a * b.Denominator - b.Numerator, + b.Denominator); + } + //Operator + public static BigFraction operator +(BigFraction a, BigFraction b) { @@ -179,18 +274,50 @@ public BigFraction(int i) a.Denominator * b.Denominator); } + public static BigFraction operator +(BigFraction a, BigInteger b) + { + return new BigFraction(a.Numerator + b * a.Denominator, + a.Denominator); + } + + public static BigFraction operator +(BigInteger a, BigFraction b) + { + return new BigFraction(a * b.Denominator + b.Numerator, + b.Denominator); + } + //Operator * public static BigFraction operator *(BigFraction a, BigFraction b) { return new BigFraction(a.Numerator * b.Numerator, a.Denominator * b.Denominator); } + public static BigFraction operator *(BigFraction a, BigInteger b) + { + return new BigFraction(a.Numerator * b, a.Denominator); + } + + public static BigFraction operator *(BigInteger a, BigFraction b) + { + return new BigFraction(a * b.Numerator, b.Denominator); + } + //Operator / public static BigFraction operator /(BigFraction a, BigFraction b) { return new BigFraction(a.Numerator * b.Denominator, a.Denominator * b.Numerator); } + public static BigFraction operator /(BigFraction a, BigInteger b) + { + return new BigFraction(a.Numerator, a.Denominator * b); + } + + public static BigFraction operator /(BigInteger a, BigFraction b) + { + return new BigFraction(a * b.Denominator, b.Numerator); + } + //Override Equals public override bool Equals(object obj) { @@ -217,13 +344,13 @@ public override string ToString() public void Simplify() { - BigInteger quotient = Numerator / Denominator; //Separate quotient from the number for faster calculation - BigInteger remainder = Numerator % Denominator; - BigInteger gcd = BigInteger.GreatestCommonDivisor(remainder, Denominator); - remainder = remainder / gcd; + var quotient = Numerator / Denominator; //Separate quotient from the number for faster calculation + var remainder = Numerator % Denominator; + var gcd = BigInteger.GreatestCommonDivisor(remainder, Denominator); + remainder /= gcd; - Denominator = Denominator / gcd; - Numerator = (quotient * Denominator) + remainder; + Denominator /= gcd; + Numerator = quotient * Denominator + remainder; } //NOTE: ALWAYS use this method when converting from BigFraction to BigInteger. diff --git a/test/BigFractionTest/BigFractionTest.cs b/test/BigFractionTest/BigFractionTest.cs index c870e0c..ba5696f 100644 --- a/test/BigFractionTest/BigFractionTest.cs +++ b/test/BigFractionTest/BigFractionTest.cs @@ -198,9 +198,14 @@ public void Modulus() [Fact(DisplayName = ">")] public void Greater() { - BigFraction a = new BigFraction(5.25); - BigFraction b = new BigFraction(4.20); + var a = new BigFraction(5.25); + var b = new BigFraction(4.20); Assert.True(a > b); + Assert.False(b > a); + Assert.True(a > -b); + Assert.True(b > -a); + Assert.False(-a > b); + Assert.False(-a > -b); } [Fact(DisplayName = "<")] @@ -248,91 +253,200 @@ public BigFractionOperatorOthers(ITestOutputHelper output) [Fact(DisplayName = "+")] public void Addition() { - BigFraction a = new BigFraction(1000.25); - BigFraction c = a + 1000.25; - BigFraction d = a + new decimal(1000.25); - BigFraction expected = new BigFraction(2000.5); + var a = new BigFraction(1000.25); + var c = a + 1000.25; + var d = a + new decimal(1000.25); + var e = a + BigInteger.One; + var f = BigInteger.One + a; + var expected = new BigFraction(2000.5); + var expected2 = new BigFraction(1001.25); Assert.Equal(expected, c); Assert.Equal(expected, d); + Assert.Equal(expected2, e); + Assert.Equal(expected2, f); + Assert.Equal(a, a + BigFraction.Zero); + Assert.Equal(a, BigFraction.Zero + a); } [Fact(DisplayName = "-")] public void Subtraction() { - BigFraction a = new BigFraction(2000.5); - BigFraction c = a - 1000.25; - BigFraction d = a - new decimal(1000.25); - BigFraction expected = new BigFraction(1000.25); + var a = new BigFraction(2000.5); + var c = a - 1000.25; + var d = a - new decimal(1000.25); + var e = a - BigInteger.One; + var f = BigInteger.One - a; + var expected = new BigFraction(1000.25); + var expected2 = new BigFraction(1999.5); + var expected3 = new BigFraction(-1999.5); Assert.Equal(expected, c); Assert.Equal(expected, d); + Assert.Equal(expected2, e); + Assert.Equal(expected3, f); + Assert.Equal(a, a - BigInteger.Zero); + Assert.Equal(-a, BigInteger.Zero - a); } [Fact(DisplayName = "*")] public void Mulplication() { - BigFraction a = new BigFraction(-5.25); - BigFraction c = a * 10.1; - BigFraction d = a * new decimal(10.1); - BigFraction expected = new BigFraction(-53.025); + var a = new BigFraction(-5.25); + var c = a * 10.1; + var d = a * new decimal(10.1); + var e = a * new BigInteger(2); + var f = new BigInteger(2) * a; + var expected = new BigFraction(-53.025); + var expected2 = new BigFraction(-10.5); Assert.Equal(expected, c); Assert.Equal(expected, d); + Assert.Equal(expected2, e); + Assert.Equal(expected2, f); + Assert.True((a * BigInteger.Zero).IsZero); + Assert.True((BigInteger.Zero * a).IsZero); + Assert.Equal(a, a * BigInteger.One); + Assert.Equal(a, BigInteger.One * a); } [Fact(DisplayName = "/")] public void Division() { - BigFraction a = new BigFraction(-5.25); - BigFraction c = a / -1.25; - BigFraction d = a / new decimal(-1.25); - BigFraction expected = new BigFraction(4.20); + var a = new BigFraction(-5.25); + var c = a / -1.25; + var d = a / new decimal(-1.25); + var e = a / new BigInteger(2); + var f = new BigInteger(21) / a; + var expected = new BigFraction(4.20); + var expected2 = new BigFraction(-2.625); + var expected3 = new BigFraction(-4); Assert.Equal(expected, c); Assert.Equal(expected, d); + Assert.Equal(expected2,e); + Assert.Equal(expected3, f); + Assert.Equal(BigFraction.Zero, BigInteger.Zero / a); + Assert.Equal(a, a / BigInteger.One); } [Fact(DisplayName = "%")] public void Modulus() { - BigFraction a = new BigFraction(5.25); - BigFraction c = a % 5; - BigFraction expected = new BigFraction(0.25); + var a = new BigFraction(5.25); + var c = a % 5; + var expected = new BigFraction(0.25); Assert.Equal(expected, c); } [Fact(DisplayName = ">")] public void Greater() { - BigFraction a = new BigFraction(5.25); + var a = new BigFraction(5.25); Assert.True(a > 4.2); + Assert.False(a > 6.8); + Assert.True(a > BigInteger.One); + Assert.False(-a > BigInteger.One); + Assert.False(BigInteger.MinusOne > a); + Assert.True(BigInteger.MinusOne > -a); } [Fact(DisplayName = "<")] public void Smaller() { - BigFraction a = new BigFraction(-251.15); + var a = new BigFraction(-251.15); Assert.True(a < 4.2); + Assert.False(a < -300); + Assert.False(a > BigInteger.One); + Assert.True(-a > BigInteger.One); + Assert.True(BigInteger.MinusOne > a); + Assert.False(BigInteger.One > -a); } [Fact(DisplayName = ">=")] public void GreaterEqual() { - BigFraction a = new BigFraction(251.15); + var a = new BigFraction(251.15); Assert.True(a >= 251.15); Assert.True(a >= 4.2); + Assert.False(a >= 300.1); + Assert.True(-a >= -251.15); + Assert.False(-a >= 4.2); + Assert.True(-a >= -300.1); + + var b = new BigFraction(100); + Assert.True(b >= new BigInteger(100)); + Assert.True(b >= BigInteger.One); + Assert.True(b >= BigInteger.MinusOne); + Assert.False(b >= new BigInteger(120)); + Assert.True(-b >= new BigInteger(-100)); + Assert.False(-b >= BigInteger.One); + Assert.False(-b >= BigInteger.MinusOne); + Assert.True(-b >= new BigInteger(-120)); + + Assert.True(new BigInteger(100) >= b); + Assert.False(BigInteger.One >= b); + Assert.False(BigInteger.MinusOne >= b); + Assert.True(new BigInteger(120) >= b); + Assert.True(new BigInteger(-100) >= -b); + Assert.True(BigInteger.One >= -b); + Assert.True(BigInteger.MinusOne >= -b); + Assert.False(new BigInteger(-120) >= -b); } [Fact(DisplayName = "<=")] public void SmallerEqual() { - BigFraction a = new BigFraction(-251.15); + var a = new BigFraction(-251.15); Assert.True(a <= 4.2); Assert.True(a <= -251.15); + Assert.False(a <= -300.1); + Assert.True(-a <= 251.15); + Assert.False(-a <= -4.2); + Assert.True(-a <= 300.1); + + var b = new BigFraction(-100); + Assert.True(b <= new BigInteger(-100)); + Assert.True(b <= BigInteger.One); + Assert.True(b <= BigInteger.MinusOne); + Assert.False(b <= new BigInteger(-120)); + Assert.True(-b <= new BigInteger(100)); + Assert.False(-b <= BigInteger.One); + Assert.False(-b <= BigInteger.MinusOne); + Assert.True(-b <= new BigInteger(120)); + + Assert.True(new BigInteger(-100) <= b); + Assert.False(BigInteger.One <= b); + Assert.False(BigInteger.MinusOne <= b); + Assert.True(new BigInteger(-120) <= b); + Assert.True(new BigInteger(100) <= -b); + Assert.True(BigInteger.One <= -b); + Assert.True(BigInteger.MinusOne <= -b); + Assert.False(new BigInteger(120) <= -b); } [Fact(DisplayName = "==")] public void Equal() { - BigFraction a = new BigFraction(-251.15); + var a = new BigFraction(-251.15); Assert.True(a == -251.15); + Assert.False(a == 123); + + var b = new BigFraction(40, 20); + Assert.True(b == new BigInteger(2)); + Assert.True(new BigInteger(2) == b); + Assert.False(b == new BigInteger(3)); + Assert.False(new BigInteger(3) == b); + } + + [Fact(DisplayName = "!=")] + public void NotEqual() + { + var a = new BigFraction(51.45); + Assert.True(a != -25); + Assert.False(a != 51.45); + + var b = new BigFraction(40, 20); + Assert.False(b != new BigInteger(2)); + Assert.False(new BigInteger(2) != b); + Assert.True(b != new BigInteger(3)); + Assert.True(new BigInteger(3) != b); } } } From eff15650123e0f1d8fb4c5e31c4646dd81cdd352 Mon Sep 17 00:00:00 2001 From: bazzilic Date: Fri, 20 Aug 2021 18:09:36 +0800 Subject: [PATCH 5/5] removed the benchmarking subproject --- BigFraction.sln | 7 -- .../OptimizationsBenchmark.csproj | 16 ----- OptimizationsBenchmark/Program.cs | 71 ------------------- 3 files changed, 94 deletions(-) delete mode 100644 OptimizationsBenchmark/OptimizationsBenchmark.csproj delete mode 100644 OptimizationsBenchmark/Program.cs diff --git a/BigFraction.sln b/BigFraction.sln index a30b7f1..e65b9b4 100644 --- a/BigFraction.sln +++ b/BigFraction.sln @@ -26,8 +26,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aprismatic.BigFraction", "s EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BigFractionTest", "test\BigFractionTest\BigFractionTest.csproj", "{164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OptimizationsBenchmark", "OptimizationsBenchmark\OptimizationsBenchmark.csproj", "{40004B57-2480-43B3-91DE-1B443986EF2D}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,10 +40,6 @@ Global {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}.Debug|Any CPU.Build.0 = Debug|Any CPU {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}.Release|Any CPU.ActiveCfg = Release|Any CPU {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F}.Release|Any CPU.Build.0 = Release|Any CPU - {40004B57-2480-43B3-91DE-1B443986EF2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {40004B57-2480-43B3-91DE-1B443986EF2D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {40004B57-2480-43B3-91DE-1B443986EF2D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {40004B57-2480-43B3-91DE-1B443986EF2D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -53,7 +47,6 @@ Global GlobalSection(NestedProjects) = preSolution {671B62C9-5C1F-45BE-A064-85113E652AFF} = {3303BAA0-773E-4F5D-B699-F3CA3CD5BBB4} {164B4DA8-0D9B-47F4-932B-CA1E4DA54A3F} = {9993F7DE-AFDC-4D82-A030-C3C5E373CE77} - {40004B57-2480-43B3-91DE-1B443986EF2D} = {9993F7DE-AFDC-4D82-A030-C3C5E373CE77} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {575666A0-07A3-4BB3-95A8-281FA6583CE7} diff --git a/OptimizationsBenchmark/OptimizationsBenchmark.csproj b/OptimizationsBenchmark/OptimizationsBenchmark.csproj deleted file mode 100644 index dad5e2c..0000000 --- a/OptimizationsBenchmark/OptimizationsBenchmark.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - Exe - net5.0 - - - - - - - - - - - diff --git a/OptimizationsBenchmark/Program.cs b/OptimizationsBenchmark/Program.cs deleted file mode 100644 index 77f7a7e..0000000 --- a/OptimizationsBenchmark/Program.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Numerics; -using Aprismatic; -using BenchmarkDotNet.Attributes; -using BenchmarkDotNet.Running; - -namespace OptimizationsBenchmark -{ - class Program - { - static void Main(string[] args) - { - var summary = BenchmarkRunner.Run(); - } - } - - public class Operators - { - private readonly BigFraction bf; - private readonly BigInteger bi; - private readonly Random rnd = new(); - - public Operators() - { - bf = new BigFraction(123456.3456); - bi = new BigInteger(43765); - } - - [Benchmark] - public BigFraction AddOld() => bf + new BigFraction(bi); - - [Benchmark] - public BigFraction AddNew() => bf + bi; - - [Benchmark] - public BigFraction SubOld() => bf - new BigFraction(bi); - - [Benchmark] - public BigFraction SubNew() => bf - bi; - - [Benchmark] - public BigFraction DivOld() => bf / new BigFraction(bi); - - [Benchmark] - public BigFraction DivNew() => bf / bi; - - [Benchmark] - public BigFraction MulOld() => bf * new BigFraction(bi); - - [Benchmark] - public BigFraction MulNew() => bf * bi; - - [Benchmark] - public bool EqOld() => bf == new BigFraction(bi); - - [Benchmark] - public bool EqNew() => bf == bi; - - [Benchmark] - public bool NeqOld() => bf != new BigFraction(bi); - - [Benchmark] - public bool NeqNew() => bf != bi; - - [Benchmark] - public bool GtOld() => bf > new BigFraction(bi); - - [Benchmark] - public bool GtNew() => bf > bi; - } -}