From 0f17aefb31cd8f2e7b47d614f50a44ceef54713a Mon Sep 17 00:00:00 2001 From: Christoph Ruegg Date: Fri, 7 Aug 2009 22:04:23 +0800 Subject: [PATCH] number theory: gcd/lcm for pairs and lists (from Iridium) Signed-off-by: Christoph Ruegg --- .../Managed.UnitTests.csproj | 1 + .../NumberTheoryTests/GcdRelatedTest.cs | 131 ++++++++++++ src/Managed/Managed.csproj | 1 + .../NumberTheory/IntegerTheory.Euclid.cs | 200 ++++++++++++++++++ src/Managed/NumberTheory/IntegerTheory.cs | 2 +- src/Native.UnitTests/Native.UnitTests.csproj | 3 + src/Native/Native.csproj | 3 + 7 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 src/Managed.UnitTests/NumberTheoryTests/GcdRelatedTest.cs create mode 100644 src/Managed/NumberTheory/IntegerTheory.Euclid.cs diff --git a/src/Managed.UnitTests/Managed.UnitTests.csproj b/src/Managed.UnitTests/Managed.UnitTests.csproj index 7201e5eb..2a773d29 100644 --- a/src/Managed.UnitTests/Managed.UnitTests.csproj +++ b/src/Managed.UnitTests/Managed.UnitTests.csproj @@ -67,6 +67,7 @@ + diff --git a/src/Managed.UnitTests/NumberTheoryTests/GcdRelatedTest.cs b/src/Managed.UnitTests/NumberTheoryTests/GcdRelatedTest.cs new file mode 100644 index 00000000..b839bb34 --- /dev/null +++ b/src/Managed.UnitTests/NumberTheoryTests/GcdRelatedTest.cs @@ -0,0 +1,131 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace MathNet.Numerics.UnitTests.NumberTheoryTests +{ + using System; + using MbUnit.Framework; + using NumberTheory; + + [TestFixture] + public class GcdRelatedTest + { + [Test] + public void GcdHandlesNormalInputCorrectly() + { + Assert.AreEqual(0, IntegerTheory.GreatestCommonDivisor(0, 0), "Gcd(0,0)"); + Assert.AreEqual(6, IntegerTheory.GreatestCommonDivisor(0, 6), "Gcd(0,6)"); + Assert.AreEqual(1, IntegerTheory.GreatestCommonDivisor(7, 13), "Gcd(7,13)"); + Assert.AreEqual(7, IntegerTheory.GreatestCommonDivisor(7, 14), "Gcd(7,14)"); + Assert.AreEqual(1, IntegerTheory.GreatestCommonDivisor(7, 15), "Gcd(7,15)"); + Assert.AreEqual(3, IntegerTheory.GreatestCommonDivisor(6, 15), "Gcd(6,15)"); + } + + [Test] + public void GcdHandlesNegativeInputCorrectly() + { + Assert.AreEqual(5, IntegerTheory.GreatestCommonDivisor(-5, 0), "Gcd(-5,0)"); + Assert.AreEqual(5, IntegerTheory.GreatestCommonDivisor(0, -5), "Gcd(0, -5)"); + Assert.AreEqual(1, IntegerTheory.GreatestCommonDivisor(-7, 15), "Gcd(-7,15)"); + Assert.AreEqual(1, IntegerTheory.GreatestCommonDivisor(-7, -15), "Gcd(-7,-15)"); + } + + [Test] + public void GcdSupportsLargeInput() + { + Assert.AreEqual(Int32.MaxValue, IntegerTheory.GreatestCommonDivisor(0, Int32.MaxValue), "Gcd(0,Int32Max)"); + Assert.AreEqual(Int64.MaxValue, IntegerTheory.GreatestCommonDivisor(0, Int64.MaxValue), "Gcd(0,Int64Max)"); + Assert.AreEqual(1, IntegerTheory.GreatestCommonDivisor(Int32.MaxValue, Int64.MaxValue), "Gcd(Int32Max,Int64Max)"); + Assert.AreEqual(1 << 18, IntegerTheory.GreatestCommonDivisor(1 << 18, 1 << 20), "Gcd(1>>18,1<<20)"); + } + + [Test] + public void ListGcdHandlesNormalInputCorrectly() + { + Assert.AreEqual(2, IntegerTheory.GreatestCommonDivisor(-10, 6, -8), "Gcd(-10,6,-8)"); + Assert.AreEqual(1, IntegerTheory.GreatestCommonDivisor(-10, 6, -8, 5, 9, 13), "Gcd(-10,6,-8,5,9,13)"); + Assert.AreEqual(5, IntegerTheory.GreatestCommonDivisor(-10, 20, 120, 60, -15, 1000), "Gcd(-10,20,120,60,-15,1000)"); + Assert.AreEqual(3, IntegerTheory.GreatestCommonDivisor(Int64.MaxValue - 1, Int64.MaxValue - 4, Int64.MaxValue - 7), "Gcd(Int64Max-1,Int64Max-4,Int64Max-7)"); + Assert.AreEqual(123, IntegerTheory.GreatestCommonDivisor(492, -2 * 492, 492 / 4), "Gcd(492, -984, 123)"); + } + + [Test] + public void ListGcdHandlesSpecialInputCorrectly() + { + Assert.AreEqual(0, IntegerTheory.GreatestCommonDivisor(), "Gcd()"); + Assert.AreEqual(100, IntegerTheory.GreatestCommonDivisor(-100), "Gcd(-100)"); + } + + [Test] + public void LcmHandlesNormalInputCorrectly() + { + Assert.AreEqual(10, IntegerTheory.LeastCommonMultiple(10, 10), "Lcm(10,10)"); + + Assert.AreEqual(0, IntegerTheory.LeastCommonMultiple(0, 10), "Lcm(0,10)"); + Assert.AreEqual(0, IntegerTheory.LeastCommonMultiple(10, 0), "Lcm(10,0)"); + + Assert.AreEqual(77, IntegerTheory.LeastCommonMultiple(11, 7), "Lcm(11,7)"); + Assert.AreEqual(33, IntegerTheory.LeastCommonMultiple(11, 33), "Lcm(11,33)"); + Assert.AreEqual(374, IntegerTheory.LeastCommonMultiple(11, 34), "Lcm(11,34)"); + } + + [Test] + public void LcmHandlesNegativeInputCorrectly() + { + Assert.AreEqual(352, IntegerTheory.LeastCommonMultiple(11, -32), "Lcm(11,-32)"); + Assert.AreEqual(352, IntegerTheory.LeastCommonMultiple(-11, 32), "Lcm(-11,32)"); + Assert.AreEqual(352, IntegerTheory.LeastCommonMultiple(-11, -32), "Lcm(-11,-32)"); + } + + [Test] + public void LcmSupportsLargeInput() + { + Assert.AreEqual(Int32.MaxValue, IntegerTheory.LeastCommonMultiple(Int32.MaxValue, Int32.MaxValue), "Lcm(Int32Max,Int32Max)"); + Assert.AreEqual(Int64.MaxValue, IntegerTheory.LeastCommonMultiple(Int64.MaxValue, Int64.MaxValue), "Lcm(Int64Max,Int64Max)"); + Assert.AreEqual(Int64.MaxValue, IntegerTheory.LeastCommonMultiple(-Int64.MaxValue, -Int64.MaxValue), "Lcm(-Int64Max,-Int64Max)"); + Assert.AreEqual(Int64.MaxValue, IntegerTheory.LeastCommonMultiple(-Int64.MaxValue, Int64.MaxValue), "Lcm(-Int64Max,Int64Max)"); + } + + [Test] + public void ListLcmHandlesNormalInputCorrectly() + { + Assert.AreEqual(120, IntegerTheory.LeastCommonMultiple(-10, 6, -8), "Lcm(-10,6,-8)"); + Assert.AreEqual(4680, IntegerTheory.LeastCommonMultiple(-10, 6, -8, 5, 9, 13), "Lcm(-10,6,-8,5,9,13)"); + Assert.AreEqual(3000, IntegerTheory.LeastCommonMultiple(-10, 20, 120, 60, -15, 1000), "Lcm(-10,20,120,60,-15,1000)"); + Assert.AreEqual(984, IntegerTheory.LeastCommonMultiple(492, -2 * 492, 492 / 4), "Lcm(492, -984, 123)"); + Assert.AreEqual(2016, IntegerTheory.LeastCommonMultiple(32, 42, 36, 18), "Lcm(32,42,36,18)"); + } + + [Test] + public void ListLcmHandlesSpecialInputCorrectly() + { + Assert.AreEqual(1, IntegerTheory.LeastCommonMultiple(), "Lcm()"); + Assert.AreEqual(100, IntegerTheory.LeastCommonMultiple(-100), "Lcm(-100)"); + } + } +} diff --git a/src/Managed/Managed.csproj b/src/Managed/Managed.csproj index d392c239..af99fdf5 100644 --- a/src/Managed/Managed.csproj +++ b/src/Managed/Managed.csproj @@ -71,6 +71,7 @@ + diff --git a/src/Managed/NumberTheory/IntegerTheory.Euclid.cs b/src/Managed/NumberTheory/IntegerTheory.Euclid.cs new file mode 100644 index 00000000..c0f8a191 --- /dev/null +++ b/src/Managed/NumberTheory/IntegerTheory.Euclid.cs @@ -0,0 +1,200 @@ +// +// Math.NET Numerics, part of the Math.NET Project +// http://mathnet.opensourcedotnet.info +// +// Copyright (c) 2009 Math.NET +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +// + +namespace MathNet.Numerics.NumberTheory +{ + using System; + using System.Collections.Generic; + + /// + /// Number theory utility functions for integers. + /// + public static partial class IntegerTheory + { + /// + /// Returns the greatest common divisor (gcd) of two integers using Euclid's algorithm. + /// + /// First Integer: a. + /// Second Integer: b. + /// Greatest common divisor gcd(a,b) + public static long GreatestCommonDivisor(long a, long b) + { + while (b != 0) + { + long remainder = a % b; + a = b; + b = remainder; + } + + return Math.Abs(a); + } + + /// + /// Returns the greatest common divisor (gcd) of a set of integers using Euclid's + /// algorithm. + /// + /// List of Integers. + /// Greatest common divisor gcd(list of integers) + public static long GreatestCommonDivisor(IList integers) + { + if (null == integers) + { + throw new ArgumentNullException("integers"); + } + + if (integers.Count == 0) + { + return 0; + } + + long gcd = Math.Abs(integers[0]); + + for (int i = 1; (i < integers.Count) && (gcd > 1); i++) + { + gcd = GreatestCommonDivisor(gcd, integers[i]); + } + + return gcd; + } + + /// + /// Returns the greatest common divisor (gcd) of a set of integers using Euclid's algorithm. + /// + /// List of Integers. + /// Greatest common divisor gcd(list of integers) + public static long GreatestCommonDivisor(params long[] integers) + { + return GreatestCommonDivisor((IList)integers); + } + + /// + /// Computes the extended greatest common divisor, such that a*x + b*y = gcd(a,b). + /// + /// First Integer: a. + /// Second Integer: b. + /// Resulting x, such that a*x + b*y = gcd(a,b). + /// Resulting y, such that a*x + b*y = gcd(a,b) + /// Greatest common divisor gcd(a,b) + /// + /// + /// long x,y,d; + /// d = Fn.GreatestCommonDivisor(45,18,out x, out y); + /// -> d == 9 && x == 1 && y == -2 + /// + /// The gcd of 45 and 18 is 9: 18 = 2*9, 45 = 5*9. 9 = 1*45 -2*18, therefore x=1 and y=-2. + /// + public static long ExtendedGreatestCommonDivisor( + long a, + long b, + out long x, + out long y) + { + long mp = 1, np = 0, m = 0, n = 1; + + while (b != 0) + { + long quot = a / b; + long rem = a % b; + a = b; + b = rem; + + long tmp = m; + m = mp - (quot * m); + mp = tmp; + + tmp = n; + n = np - (quot * n); + np = tmp; + } + + if (a >= 0) + { + x = mp; + y = np; + return a; + } + + x = -mp; + y = -np; + return -a; + } + + /// + /// Returns the least common multiple (lcm) of two integers using Euclid's algorithm. + /// + /// First Integer: a. + /// Second Integer: b. + /// Least common multiple lcm(a,b) + public static long LeastCommonMultiple(long a, long b) + { + if ((a == 0) || (b == 0)) + { + return 0; + } + + return Math.Abs((a / GreatestCommonDivisor(a, b)) * b); + } + + /// + /// Returns the least common multiple (lcm) of a set of integers using Euclid's algorithm. + /// + /// List of Integers. + /// Least common multiple lcm(list of integers) + public static long LeastCommonMultiple(IList integers) + { + if (null == integers) + { + throw new ArgumentNullException("integers"); + } + + if (integers.Count == 0) + { + return 1; + } + + long lcm = Math.Abs(integers[0]); + + for (int i = 1; i < integers.Count; i++) + { + lcm = LeastCommonMultiple(lcm, integers[i]); + } + + return lcm; + } + + /// + /// Returns the least common multiple (lcm) of a set of integers using Euclid's algorithm. + /// + /// List of Integers. + /// Least common multiple lcm(list of integers) + public static long LeastCommonMultiple(params long[] integers) + { + return LeastCommonMultiple((IList)integers); + } + } +} diff --git a/src/Managed/NumberTheory/IntegerTheory.cs b/src/Managed/NumberTheory/IntegerTheory.cs index b14a5eed..d92a8207 100644 --- a/src/Managed/NumberTheory/IntegerTheory.cs +++ b/src/Managed/NumberTheory/IntegerTheory.cs @@ -33,7 +33,7 @@ namespace MathNet.Numerics.NumberTheory /// /// Number theory utility functions for integers. /// - public static class IntegerTheory + public static partial class IntegerTheory { /// /// Find out whether the provided 32 bit integer is an even number. diff --git a/src/Native.UnitTests/Native.UnitTests.csproj b/src/Native.UnitTests/Native.UnitTests.csproj index f8e65dde..5a368662 100644 --- a/src/Native.UnitTests/Native.UnitTests.csproj +++ b/src/Native.UnitTests/Native.UnitTests.csproj @@ -83,6 +83,9 @@ InterpolationTests\InterpolationTest.cs + + NumberTheoryTests\GcdRelatedTest.cs + NumberTheoryTests\IntegerTheoryTest.cs diff --git a/src/Native/Native.csproj b/src/Native/Native.csproj index 81515625..ac5f1b07 100644 --- a/src/Native/Native.csproj +++ b/src/Native/Native.csproj @@ -122,6 +122,9 @@ NumberTheory\IntegerTheory.cs + + NumberTheory\IntegerTheory.Euclid.cs + Precision.cs