Browse Source

Polynomial: more human-friendly string formatting

ridge-regression
Christoph Ruegg 8 years ago
parent
commit
938688c646
  1. 94
      src/Numerics.Tests/PolynomialTests.cs
  2. 177
      src/Numerics/Polynomial.cs

94
src/Numerics.Tests/PolynomialTests.cs

@ -42,58 +42,57 @@ namespace MathNet.Numerics.UnitTests
[TestFixture, Category("Calculus")] [TestFixture, Category("Calculus")]
public class PolynomialTests public class PolynomialTests
{ {
[TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x^1 + 3x^2 + 0x^3 + 2x^4")] [TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x + 3x^2 + 2x^4")]
[TestCase(new double[0], "")] [TestCase(new double[0], "0")]
[TestCase(new double[] { 0, 4, 3, 0, 0 }, "0 + 4x^1 + 3x^2 + 0x^3 + 0x^4")] [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) public void ToStringTest(double[] x, string expected)
{ {
var p = new Polynomial(x); var p = new Polynomial(x);
Assert.AreEqual(expected, p.ToString()); 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[] { 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 })] [TestCase(new double[] { 0, 4, 3, 0, 0 }, new double[] { 4*1, 3*2 })]
public void DifferentiateTest(double[] x, double[] expected) public void DifferentiateTest(double[] x, double[] expected)
{ {
var p = new Polynomial(x); var p = new Polynomial(x);
var p_res = p.Differentiate(); var p_res = p.Differentiate();
if (expected == null) Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch");
{ for (int k = 0; k < p_res.Coefficients.Length; k++)
Assert.IsNull(p_res);
return;
}
else
{ {
Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch"); Assert.AreEqual(expected[k], p_res.Coefficients[k], "idx: " + k + " mismatch");
for (int k = 0; k < p_res.Coefficients.Length; k++)
{
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[] { 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})] [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) public void IntegrateTest(double[] x, double[] expected)
{ {
var p = new Polynomial(x); var p = new Polynomial(x);
var p_res = p.Integrate(); var p_res = p.Integrate();
if (expected == null) Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch");
{ for (int k = 0; k < p_res.Coefficients.Length; k++)
Assert.IsNull(p_res);
return;
}
else
{ {
Assert.AreEqual(expected.Length, p_res.Coefficients.Length, "length mismatch"); Assert.AreEqual(expected[k], p_res.Coefficients[k], "idx: " + k + " mismatch");
for (int k = 0; k < p_res.Coefficients.Length; k++)
{
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_res = Polynomial.Add(p1, p2);
var p_tar = new Polynomial(tgt); var p_tar = new Polynomial(tgt);
p_res.Trim();
p_tar.Trim();
Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch"); Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch");
for (int k = 0; k < p_res.Coefficients.Length; k++) 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_res = Polynomial.Subtract(p1, p2);
var p_tar = new Polynomial(tgt); var p_tar = new Polynomial(tgt);
p_res.Trim();
p_tar.Trim();
Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch"); Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch");
for (int k = 0; k < p_res.Coefficients.Length; k++) 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_res = p1 * p2;
var p_tar = new Polynomial(tgt); var p_tar = new Polynomial(tgt);
p_res.Trim();
p_tar.Trim();
Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch"); Assert.AreEqual(p_tar.Coefficients.Length, p_res.Coefficients.Length, "length mismatch");
for (int k = 0; k < p_res.Coefficients.Length; k++) 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[] { 0, 0, 0 }, "0")]
[TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x^1 + 3x^2 + 0x^3 + 2x^4")] [TestCase(new double[] { 5, 4, 3, 0, 2 }, "5 + 4x + 3x^2 + 2x^4")]
[TestCase(new double[] { 0, 0, 8, 0, 0 }, "0 + 0x^1 + 8x^2")] [TestCase(new double[] { 0, 0, 8, 0, 0 }, "8x^2")]
[TestCase(new double[] { 0, 4, 3, 0, 0 }, "0 + 4x^1 + 3x^2")] [TestCase(new double[] { 0, 4, 3, 0, 0 }, "4x + 3x^2")]
public void TrimTest(double[] x, string expected) public void TrimTest(double[] x, string expected)
{ {
var p = new Polynomial(x); var p = new Polynomial(x);
p.Trim();
Assert.AreEqual(expected, p.ToString()); Assert.AreEqual(expected, p.ToString());
} }
@ -224,29 +213,29 @@ namespace MathNet.Numerics.UnitTests
[Test] [Test]
public void DivideLongTestWrongInputs() public void DivideLongTestWrongInputs()
{ {
Assert.Throws(typeof(ArgumentOutOfRangeException), () => Assert.Throws(typeof(DivideByZeroException), () =>
{ {
var p1 = new Polynomial(1.0d); var p1 = new Polynomial(1.0d);
var p2 = new Polynomial(new double[0]); 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 p1 = new Polynomial(new double[0]);
var p2 = new Polynomial(new double[0]); var p2 = new Polynomial(1.0d);
var tpl = Polynomial.DivideRemainder(p2, p1); GC.KeepAlive(Polynomial.DivideRemainder(p1, p2));
}); });
Assert.Throws(typeof(ArgumentOutOfRangeException), () => Assert.Throws(typeof(DivideByZeroException), () =>
{ {
var p1 = new Polynomial(new double[0]); var p1 = new Polynomial(new double[0]);
var p2 = 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), () => Assert.Throws(typeof(DivideByZeroException), () =>
{ {
var p1 = new Polynomial(1.0d); var p1 = new Polynomial(1.0d);
var p2 = new Polynomial(0.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 p21 = new Polynomial(2.0d);
var tpl1 = Polynomial.DivideRemainder(p11, p21); var tpl1 = Polynomial.DivideRemainder(p11, p21);
TestEqual(new double[] { 1.0 }, tpl1.Item1); 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 p12 = new Polynomial(new double[] { 2.0d, 2.0d });
var p22 = new Polynomial(2.0d); var p22 = new Polynomial(2.0d);
var tpl2 = Polynomial.DivideRemainder(p12, p22); var tpl2 = Polynomial.DivideRemainder(p12, p22);
TestEqual(new double[] { 1.0, 1.0 }, tpl2.Item1); 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++) for (int i = 0; i < 5; i++)
{ {
@ -284,7 +273,6 @@ namespace MathNet.Numerics.UnitTests
var pquo = tpl3.Item1; var pquo = tpl3.Item1;
var prem = tpl3.Item2; var prem = tpl3.Item2;
var pres = (pquo * pi) + prem; var pres = (pquo * pi) + prem;
pres.Trim();
TestEqual(pres, tgt, msg); TestEqual(pres, tgt, msg);
} }

177
src/Numerics/Polynomial.cs

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using System.Text;
using MathNet.Numerics.LinearAlgebra; using MathNet.Numerics.LinearAlgebra;
using MathNet.Numerics.LinearAlgebra.Double; using MathNet.Numerics.LinearAlgebra.Double;
using MathNet.Numerics.LinearRegression; using MathNet.Numerics.LinearRegression;
@ -22,7 +23,7 @@ namespace MathNet.Numerics
/// <summary> /// <summary>
/// Only needed for the ToString method /// Only needed for the ToString method
/// </summary> /// </summary>
public string VarName = "x^"; public string VarName = "x";
/// <summary> /// <summary>
/// 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. /// 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 #region Calculus
public Polynomial Differentiate() 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; var c = new double[n];
t.Trim(); for (int i = 0; i < c.Length; i++)
var cNew = new double[t.Coefficients.Length - 1];
for (int i = 1; i < t.Coefficients.Length; i++)
{ {
cNew[i - 1] = t.Coefficients[i] * i; c[i] = Coefficients[i + 1] * (i + 1);
} }
var p = new Polynomial(cNew); return new Polynomial(c);
p.Trim();
return p;
} }
public Polynomial Integrate() public Polynomial Integrate()
{ {
var t = Clone() as Polynomial; int n = Degree;
t.Trim(); if (n < 0)
var cNew = new double[t.Coefficients.Length + 1]; {
for (int i = 1; i < cNew.Length; i++) 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); return new Polynomial(c);
p.Trim();
return p;
} }
#endregion #endregion
@ -496,19 +501,15 @@ namespace MathNet.Numerics
/// <returns>A tuple holding quotient in first and remainder in second</returns> /// <returns>A tuple holding quotient in first and remainder in second</returns>
public static Tuple<Polynomial, Polynomial> DivideRemainder(Polynomial a, Polynomial b) public static Tuple<Polynomial, Polynomial> DivideRemainder(Polynomial a, Polynomial b)
{ {
if (a == null) if (b.Degree < 0)
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)
throw new DivideByZeroException("b polynomial ends with zero"); 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 c1 = a.Coefficients.ToArray();
var c2 = b.Coefficients.ToArray(); var c2 = b.Coefficients.ToArray();
@ -581,8 +582,6 @@ namespace MathNet.Numerics
var pQuo = new Polynomial(quo); var pQuo = new Polynomial(quo);
var pRem = new Polynomial(rem); var pRem = new Polynomial(rem);
pRem.Trim();
pQuo.Trim();
return new Tuple<Polynomial, Polynomial>(pQuo, pRem); return new Tuple<Polynomial, Polynomial>(pQuo, pRem);
} }
#endregion #endregion
@ -788,43 +787,113 @@ namespace MathNet.Numerics
/// <returns>string in displayed format</returns> /// <returns>string in displayed format</returns>
public string ToString(bool highestFirst) public string ToString(bool highestFirst)
{ {
if (Coefficients == null) if (Degree < 0)
{ {
return "null"; return "0";
}
if (Coefficients.Length == 0)
{
return "";
} }
string result = ""; var sb = new StringBuilder();
if (!highestFirst) bool first = true;
if (highestFirst)
{ {
for (int ii = 0; ii < Coefficients.Length; ii++) for (int i = Coefficients.Length - 1; i >= 0; i--)
{ {
double c = Coefficients[i];
if (ii == 0 && Coefficients.Length == 1) if (c == 0.0)
result += String.Format("{0}", Coefficients[ii], VarName, ii); {
else if (ii == 0) continue;
result += String.Format("{0} + ", Coefficients[ii], VarName, ii); }
else if (ii == Coefficients.Length - 1)
result += String.Format("{0}{1}{2}", Coefficients[ii], VarName, ii); if (first)
{
sb.Append(c);
if (i > 0)
{
sb.Append(VarName);
}
if (i > 1)
{
sb.Append("^");
sb.Append(i);
}
first = false;
}
else 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 else
{ {
for (int ii = Coefficients.Length - 1; ii >= 0; ii--) for (int i = 0; i < Coefficients.Length; i++)
{ {
if (ii == 0) double c = Coefficients[i];
result += Coefficients[ii].ToString(); 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 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 #endregion
} }

Loading…
Cancel
Save