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")]
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);
}

177
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
/// <summary>
/// Only needed for the ToString method
/// </summary>
public string VarName = "x^";
public string VarName = "x";
/// <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.
@ -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
/// <returns>A tuple holding quotient in first and remainder in second</returns>
public static Tuple<Polynomial, Polynomial> 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<Polynomial, Polynomial>(pQuo, pRem);
}
#endregion
@ -788,43 +787,113 @@ namespace MathNet.Numerics
/// <returns>string in displayed format</returns>
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
}

Loading…
Cancel
Save