diff --git a/src/Numerics.Tests/PolynomialTests.cs b/src/Numerics.Tests/PolynomialTests.cs index 1385d26a..0c696f66 100644 --- a/src/Numerics.Tests/PolynomialTests.cs +++ b/src/Numerics.Tests/PolynomialTests.cs @@ -42,58 +42,57 @@ namespace MathNet.Numerics.UnitTests [TestFixture, Category("Calculus")] public class PolynomialTests { - [TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x^1 + 3x^2 + 0x^3 + 2x^4")] - [TestCase(new double[0], "")] - [TestCase(new double[] { 0, 4, 3, 0, 0 }, "0 + 4x^1 + 3x^2 + 0x^3 + 0x^4")] + [TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x + 3x^2 + 2x^4")] + [TestCase(new double[0], "0")] + [TestCase(new double[] { 0, 4, 3, 0, 0 }, "4x + 3x^2")] + [TestCase(new double[] { 0, -4, 3 }, "-4x + 3x^2")] + [TestCase(new double[] { 0, -4, -3 }, "-4x - 3x^2")] + [TestCase(new double[] { 0, 4, -3 }, "4x - 3x^2")] public void ToStringTest(double[] x, string expected) { var p = new Polynomial(x); Assert.AreEqual(expected, p.ToString()); } + [TestCase(new double[] { 5, 4, 3, 0, 2 }, "2x^4 + 3x^2 + 4x + 5")] + [TestCase(new double[0], "0")] + [TestCase(new double[] { 0, 4, 3, 0, 0 }, "3x^2 + 4x")] + [TestCase(new double[] { 0, -4, 3 }, "3x^2 - 4x")] + [TestCase(new double[] { 0, -4, -3 }, "-3x^2 - 4x")] + [TestCase(new double[] { 0, 4, -3 }, "-3x^2 + 4x")] + public void ToStringTestReverse(double[] x, string expected) + { + var p = new Polynomial(x); + Assert.AreEqual(expected, p.ToString(highestFirst:true)); + } + [TestCase(new double[] { 5, 4, 3, 0, 2 }, new double[] { 4*1, 3*2, 0*3, 2*4 })] - [TestCase(new double[0], null)] + [TestCase(new double[0], new double[0])] [TestCase(new double[] { 0, 4, 3, 0, 0 }, new double[] { 4*1, 3*2 })] public void DifferentiateTest(double[] x, double[] expected) { var p = new Polynomial(x); var p_res = p.Differentiate(); - if (expected == null) - { - Assert.IsNull(p_res); - return; - } - else + Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch"); + for (int k = 0; k < p_res.Coefficients.Length; k++) { - Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch"); - for (int k = 0; k < p_res.Coefficients.Length; k++) - { - Assert.AreEqual(expected[k], p_res.Coefficients[k], "idx: " + k + " mismatch"); - } + Assert.AreEqual(expected[k], p_res.Coefficients[k], "idx: " + k + " mismatch"); } } [TestCase(new double[] { 5, 4, 3, 0, 2 }, new double[] { 0, 5.0/1.0, 4.0/2.0, 3.0/3.0, 0.0/4.0, 2.0/5.0 })] - [TestCase(new double[0], new double[1] { 0 })] + [TestCase(new double[0], new double[0])] [TestCase(new double[] { 0, 1, 6, 8 }, new double[] {0, 0.0/1.0, 1.0/2.0, 6.0/3.0, 8.0/4.0})] public void IntegrateTest(double[] x, double[] expected) { var p = new Polynomial(x); var p_res = p.Integrate(); - if (expected == null) - { - Assert.IsNull(p_res); - return; - } - else + Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch"); + for (int k = 0; k < p_res.Coefficients.Length; k++) { - Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch"); - for (int k = 0; k < p_res.Coefficients.Length; k++) - { - Assert.AreEqual(expected[k], p_res.Coefficients[k], "idx: " + k + " mismatch"); - } + Assert.AreEqual(expected[k], p_res.Coefficients[k], "idx: " + k + " mismatch"); } } @@ -122,9 +121,6 @@ namespace MathNet.Numerics.UnitTests var p_res = Polynomial.Add(p1, p2); var p_tar = new Polynomial(tgt); - p_res.Trim(); - p_tar.Trim(); - Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch"); for (int k = 0; k < p_res.Coefficients.Length; k++) { @@ -159,9 +155,6 @@ namespace MathNet.Numerics.UnitTests var p_res = Polynomial.Subtract(p1, p2); var p_tar = new Polynomial(tgt); - p_res.Trim(); - p_tar.Trim(); - Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch"); for (int k = 0; k < p_res.Coefficients.Length; k++) { @@ -195,9 +188,6 @@ namespace MathNet.Numerics.UnitTests var p_res = p1 * p2; var p_tar = new Polynomial(tgt); - p_res.Trim(); - p_tar.Trim(); - Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch"); for (int k = 0; k < p_res.Coefficients.Length; k++) { @@ -208,15 +198,14 @@ namespace MathNet.Numerics.UnitTests } - [TestCase(new double[] { 5, 4, 0 }, "5 + 4x^1")] + [TestCase(new double[] { 5, 4, 0 }, "5 + 4x")] [TestCase(new double[] { 0, 0, 0 }, "0")] - [TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x^1 + 3x^2 + 0x^3 + 2x^4")] - [TestCase(new double[] { 0, 0, 8, 0, 0 }, "0 + 0x^1 + 8x^2")] - [TestCase(new double[] { 0, 4, 3, 0, 0 }, "0 + 4x^1 + 3x^2")] + [TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x + 3x^2 + 2x^4")] + [TestCase(new double[] { 0, 0, 8, 0, 0 }, "8x^2")] + [TestCase(new double[] { 0, 4, 3, 0, 0 }, "4x + 3x^2")] public void TrimTest(double[] x, string expected) { var p = new Polynomial(x); - p.Trim(); Assert.AreEqual(expected, p.ToString()); } @@ -224,29 +213,29 @@ namespace MathNet.Numerics.UnitTests [Test] public void DivideLongTestWrongInputs() { - Assert.Throws(typeof(ArgumentOutOfRangeException), () => + Assert.Throws(typeof(DivideByZeroException), () => { var p1 = new Polynomial(1.0d); var p2 = new Polynomial(new double[0]); - var tpl = Polynomial.DivideRemainder(p1, p2); + GC.KeepAlive(Polynomial.DivideRemainder(p1, p2)); }); - Assert.Throws(typeof(ArgumentOutOfRangeException), () => + Assert.DoesNotThrow(() => { - var p1 = new Polynomial(1.0d); - var p2 = new Polynomial(new double[0]); - var tpl = Polynomial.DivideRemainder(p2, p1); + var p1 = new Polynomial(new double[0]); + var p2 = new Polynomial(1.0d); + GC.KeepAlive(Polynomial.DivideRemainder(p1, p2)); }); - Assert.Throws(typeof(ArgumentOutOfRangeException), () => + Assert.Throws(typeof(DivideByZeroException), () => { var p1 = new Polynomial(new double[0]); var p2 = new Polynomial(new double[0]); - var tpl = Polynomial.DivideRemainder(p2, p1); + GC.KeepAlive(Polynomial.DivideRemainder(p1, p2)); }); Assert.Throws(typeof(DivideByZeroException), () => { var p1 = new Polynomial(1.0d); var p2 = new Polynomial(0.0d); - var tpl = Polynomial.DivideRemainder(p1, p2); + GC.KeepAlive(Polynomial.DivideRemainder(p1, p2)); }); } @@ -257,13 +246,13 @@ namespace MathNet.Numerics.UnitTests var p21 = new Polynomial(2.0d); var tpl1 = Polynomial.DivideRemainder(p11, p21); TestEqual(new double[] { 1.0 }, tpl1.Item1); - TestEqual(new double[] { 0.0 }, tpl1.Item2); + TestEqual(new double[0], tpl1.Item2); var p12 = new Polynomial(new double[] { 2.0d, 2.0d }); var p22 = new Polynomial(2.0d); var tpl2 = Polynomial.DivideRemainder(p12, p22); TestEqual(new double[] { 1.0, 1.0 }, tpl2.Item1); - TestEqual(new double[] { 0.0 }, tpl2.Item2); + TestEqual(new double[0], tpl2.Item2); for (int i = 0; i < 5; i++) { @@ -284,7 +273,6 @@ namespace MathNet.Numerics.UnitTests var pquo = tpl3.Item1; var prem = tpl3.Item2; var pres = (pquo * pi) + prem; - pres.Trim(); TestEqual(pres, tgt, msg); } diff --git a/src/Numerics/Polynomial.cs b/src/Numerics/Polynomial.cs index 17d01237..d3f0952b 100644 --- a/src/Numerics/Polynomial.cs +++ b/src/Numerics/Polynomial.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; +using System.Text; using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra.Double; using MathNet.Numerics.LinearRegression; @@ -22,7 +23,7 @@ namespace MathNet.Numerics /// /// Only needed for the ToString method /// - public string VarName = "x^"; + public string VarName = "x"; /// /// Degree of the polynomial, i.e. the largest monomial exponent. For example, the degree of y=x^2+x^5 is 5, for y=3 it is 0. @@ -194,37 +195,41 @@ namespace MathNet.Numerics #region Calculus public Polynomial Differentiate() { - if (Coefficients.Length == 0) + int n = Degree; + if (n < 0) { - return null; + return this; + } + if (n == 0) + { + // Zero + return new Polynomial(); } - var t = Clone() as Polynomial; - t.Trim(); - var cNew = new double[t.Coefficients.Length - 1]; - for (int i = 1; i < t.Coefficients.Length; i++) + var c = new double[n]; + for (int i = 0; i < c.Length; i++) { - cNew[i - 1] = t.Coefficients[i] * i; + c[i] = Coefficients[i + 1] * (i + 1); } - var p = new Polynomial(cNew); - p.Trim(); - return p; + return new Polynomial(c); } public Polynomial Integrate() { - var t = Clone() as Polynomial; - t.Trim(); - var cNew = new double[t.Coefficients.Length + 1]; - for (int i = 1; i < cNew.Length; i++) + int n = Degree; + if (n < 0) + { + return this; + } + + var c = new double[n + 2]; + for (int i = 1; i < c.Length; i++) { - cNew[i] = t.Coefficients[i - 1] / i; + c[i] = Coefficients[i - 1] / i; } - var p = new Polynomial(cNew); - p.Trim(); - return p; + return new Polynomial(c); } #endregion @@ -496,19 +501,15 @@ namespace MathNet.Numerics /// A tuple holding quotient in first and remainder in second public static Tuple DivideRemainder(Polynomial a, Polynomial b) { - if (a == null) - throw new ArgumentNullException(nameof(a)); - if (b == null) - throw new ArgumentNullException(nameof(b)); - - if (a.Coefficients.Length <= 0) - throw new ArgumentOutOfRangeException("a Degree must be greater than zero"); - if (b.Coefficients.Length <= 0) - throw new ArgumentOutOfRangeException("b Degree must be greater than zero"); - - if (b.Coefficients[b.Coefficients.Length - 1] == 0) + if (b.Degree < 0) throw new DivideByZeroException("b polynomial ends with zero"); + if (a.Degree < 0) + { + // zero divided by non-zero is zero without remainder + return Tuple.Create(a, a); + } + var c1 = a.Coefficients.ToArray(); var c2 = b.Coefficients.ToArray(); @@ -581,8 +582,6 @@ namespace MathNet.Numerics var pQuo = new Polynomial(quo); var pRem = new Polynomial(rem); - pRem.Trim(); - pQuo.Trim(); return new Tuple(pQuo, pRem); } #endregion @@ -788,43 +787,113 @@ namespace MathNet.Numerics /// string in displayed format public string ToString(bool highestFirst) { - if (Coefficients == null) + if (Degree < 0) { - return "null"; - } - if (Coefficients.Length == 0) - { - return ""; + return "0"; } - string result = ""; - if (!highestFirst) + var sb = new StringBuilder(); + bool first = true; + if (highestFirst) { - for (int ii = 0; ii < Coefficients.Length; ii++) + for (int i = Coefficients.Length - 1; i >= 0; i--) { - - if (ii == 0 && Coefficients.Length == 1) - result += String.Format("{0}", Coefficients[ii], VarName, ii); - else if (ii == 0) - result += String.Format("{0} + ", Coefficients[ii], VarName, ii); - else if (ii == Coefficients.Length - 1) - result += String.Format("{0}{1}{2}", Coefficients[ii], VarName, ii); + double c = Coefficients[i]; + if (c == 0.0) + { + continue; + } + + if (first) + { + sb.Append(c); + if (i > 0) + { + sb.Append(VarName); + } + if (i > 1) + { + sb.Append("^"); + sb.Append(i); + } + + first = false; + } else - result += String.Format("{0}{1}{2} + ", Coefficients[ii], VarName, ii); + { + if (c < 0.0) + { + sb.Append(" - "); + sb.Append(-c); + } + else + { + sb.Append(" + "); + sb.Append(c); + } + if (i > 0) + { + sb.Append(VarName); + } + if (i > 1) + { + sb.Append("^"); + sb.Append(i); + } + } } } else { - for (int ii = Coefficients.Length - 1; ii >= 0; ii--) + for (int i = 0; i < Coefficients.Length; i++) { - if (ii == 0) - result += Coefficients[ii].ToString(); + double c = Coefficients[i]; + if (c == 0.0) + { + continue; + } + + if (first) + { + sb.Append(c); + if (i > 0) + { + sb.Append(VarName); + } + if (i > 1) + { + sb.Append("^"); + sb.Append(i); + } + + first = false; + } else - result += String.Format("{0}{1}{2} + ", Coefficients[ii], VarName, ii); + { + if (c < 0.0) + { + sb.Append(" - "); + sb.Append(-c); + } + else + { + sb.Append(" + "); + sb.Append(c); + } + if (i > 0) + { + sb.Append(VarName); + } + if (i > 1) + { + sb.Append("^"); + sb.Append(i); + } + } } } - return result; + return sb.ToString(); } #endregion }