2012-01-20

Klasa liczby zespolonej

Moja implementacja klasy liczby zespolonej, nie jest może wszystko-mająca, ale do FFT wystarczy. Do tego testy. Publikuje to dlatego, że przechodzę na implementację klasy Complex z System.Numerics. Jedyną różnicę jaką zauważyłem to zachowanie się w przypadku liczenia przesunięcia fazowego z (0,0). Zgodnie z definicją z wikipedii i nazwijmy to zdrowym rozsądkiem wartość ta powinna być nieokreślona, tymczasem w System.Numerics jest to zero.

public struct Complex
{
    public static readonly Complex ZERO = new Complex(0, 0);
    public static readonly Complex ONE = new Complex(1, 0);
    public static readonly Complex i = new Complex(0, 1);
    public static readonly Complex UNDEFINED = new Complex(Double.NaN, Double.NaN);

    public readonly double Real;
    public readonly double Image;

    public Complex(double a_real, double a_image = 0)
    {
        Real = a_real;
        Image = a_image;
    }

    public static bool operator ==(Complex a_a, Complex a_b)
    {
        return (a_a.Real == a_b.Real) & (a_a.Image == a_b.Image);
    }

    public static bool operator !=(Complex a_a, Complex a_b)
    {
        return (a_a.Real != a_b.Real) | (a_a.Image != a_b.Image);
    }

    public static Complex operator +(Complex a_a)
    {
        return a_a;
    }

    public static Complex FromPolar(double a_magnitude, double a_phase)
    {
        return new Complex(a_magnitude * Math.Cos(a_phase), 
            a_magnitude * Math.Sin(a_phase));
    }

    public static Complex FromPolar(double a_phase)
    {
        return new Complex(Math.Cos(a_phase), Math.Sin(a_phase));
    }

    public static Complex operator -(Complex a_a)
    {
        return new Complex(-a_a.Real, -a_a.Image);
    }

    public static Complex operator +(Complex a_a, Complex a_b)
    {
        return new Complex(
            a_a.Real + a_b.Real, 
            a_a.Image + a_b.Image);
    }

    public static Complex operator -(Complex a_a, Complex a_b)
    {
        return new Complex(
            a_a.Real - a_b.Real, 
            a_a.Image - a_b.Image);
    }

    public static Complex operator *(Complex a_a, Complex a_b)
    {
        return new Complex(
            a_a.Real * a_b.Real - a_a.Image * a_b.Image, 
            a_a.Real * a_b.Image + a_a.Image * a_b.Real);
    }

    public static Complex operator *(Complex a_a, double a_b)
    {
        return new Complex(
            a_a.Real * a_b,
            a_a.Image * a_b);
    }

    public static Complex operator *(double a_a, Complex a_b)
    {
        return new Complex(
            a_b.Real * a_a,
            a_b.Image * a_a);
    }

    public static Complex operator /(Complex a_a, Complex a_b)
    {
        double den = a_b.Real * a_b.Real + a_b.Image * a_b.Image;

        return new Complex(
            (a_a.Real * a_b.Real + a_a.Image * a_b.Image) / den,
            (a_a.Image * a_b.Real - a_a.Real * a_b.Image) / den);
    }

    public Complex Scale(double a_scale)
    {
        return new Complex(
            Real * a_scale,
            Image * a_scale);
    }

    public bool IsAlmostEquals(Complex a_a, 
        double a_precision = Constants.DOUBLE_PRECISION)
    {
        return Real.IsAlmostEquals(a_a.Real, a_precision) &&
            Image.IsAlmostEquals(a_a.Image, a_precision);
    }

    public bool IsAlmostRelativeEquals(Complex a_a, 
        double a_precision = Constants.DOUBLE_PRECISION)
    {
        return Real.IsAlmostRelativeEquals(a_a.Real, a_precision) &&
            Image.IsAlmostRelativeEquals(a_a.Image, a_precision);
    }

    public override int GetHashCode()
    {
        return Real.GetHashCode() ^ Image.GetHashCode();
    }

    public override bool Equals(object a_obj)
    {
        if (a_obj == null)
            return false;

        if (!typeof(Complex).Equals(a_obj.GetType()))
            return false;
        Complex complex = (Complex)a_obj;

        return (complex.Real == Real) && (complex.Image == Image);
    }

    public bool Equals(Complex a_complex)
    {
        return (a_complex.Real == Real) && (a_complex.Image == Image);
    }

    [DebuggerHidden]
    public Complex Conjugate
    {
        get
        {
            return new Complex(Real, -Image);
        }
    }

    public double Abs
    {
        get
        {
            return MathExtensions.Hypot(Real, Image);
        }
    }

    public double Phase
    {
        get
        {
            if (IsAlmostRelativeEquals(ZERO))
                return Double.NaN;

            return Math.Atan2(Image, Real);
        }
    }

    public override string ToString()
    {
        if ((Real != 0) && (Image != 0))
            return String.Format("({0} + {1}i)", Real, Image);
        else if (Real != 0)
            return Real.ToString();
        else if (Image != 0)
            return String.Format("{1}i", Image);
        else
            return "0";
    }
}

Testy:

[TestMethod]
public void Complex_Test()
{
    // Constructor
    {
        Assert.AreEqual(4, new Complex(4).Real);
        Assert.AreEqual(0, new Complex(4).Image);
        Assert.AreEqual(5, new Complex(5, 6).Real);
        Assert.AreEqual(6, new Complex(5, 6).Image);
    }

    // ==, !=
    {
        Assert.IsTrue(new Complex(5, 4) == new Complex(5, 4));
        Assert.IsTrue(new Complex(5.3, 4.7) == new Complex(5.3, 4.7));
        Assert.IsFalse(new Complex(5, 4) == new Complex(5, 5));
        Assert.IsFalse(new Complex(5, 4) == new Complex(4, 4));
        Assert.IsFalse(new Complex(5, 5) == new Complex(5, 4));
        Assert.IsFalse(new Complex(4, 4) == new Complex(5, 4));

        Assert.IsFalse(new Complex(5, 4) != new Complex(5, 4));
        Assert.IsTrue(new Complex(5, 4) != new Complex(5, 5));
        Assert.IsTrue(new Complex(5, 4) != new Complex(4, 4));
        Assert.IsTrue(new Complex(5, 5) != new Complex(5, 4));
        Assert.IsTrue(new Complex(4, 4) != new Complex(5, 4));
    }

    // unary -
    {
        Assert.IsTrue(new Complex(-1, 1) == -new Complex(1, -1));
        Assert.IsTrue(new Complex(-1.6, 1.5) == -new Complex(1.6, -1.5));
        Assert.IsTrue(new Complex(-1, -1) == -new Complex(1, 1));
        Assert.IsTrue(new Complex(1, -1) == -new Complex(-1, 1));
        Assert.IsTrue(new Complex(1, 0) == -new Complex(-1, 0));
        Assert.IsTrue(new Complex(0, 0) == -new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 1) == -new Complex(0, -1));

        Assert.IsTrue(-new Complex(-1, 1) == - -new Complex(1, -1));
        Assert.IsTrue(-new Complex(-1, -1) == - -new Complex(1, 1));
        Assert.IsTrue(-new Complex(1, -1) == - -new Complex(-1, 1));
        Assert.IsTrue(-new Complex(1, 0) == - -new Complex(-1, 0));
        Assert.IsTrue(-new Complex(0, 0) == - -new Complex(0, 0));
        Assert.IsTrue(-new Complex(0, 1) == - -new Complex(0, -1));
    }

    // unary +
    {
        Assert.IsTrue(-new Complex(-1, 1) == +new Complex(1, -1));
        Assert.IsTrue(-new Complex(-1, -1) == +new Complex(1, 1));
        Assert.IsTrue(-new Complex(-1.2, -1.5) == +new Complex(1.2, 1.5));
        Assert.IsTrue(-new Complex(1, -1) == +new Complex(-1, 1));
        Assert.IsTrue(-new Complex(1, 0) == +new Complex(-1, 0));
        Assert.IsTrue(-new Complex(0, 0) == +new Complex(0, 0));
        Assert.IsTrue(-new Complex(0, 1) == +new Complex(0, -1));
    }

    // +
    {
        Assert.IsTrue(new Complex(0, 0) == new Complex(0, 0) + new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 1) == new Complex(0, 1) + new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 1) == new Complex(0, 0) + new Complex(0, 1));
        Assert.IsTrue(new Complex(1, 0) == new Complex(1, 0) + new Complex(0, 0));
        Assert.IsTrue(new Complex(1, 0) == new Complex(0, 0) + new Complex(1, 0));
        Assert.IsTrue(new Complex(1, 1) == new Complex(1, 0) + new Complex(0, 1));
        Assert.IsTrue(new Complex(1, 1) == new Complex(0, 1) + new Complex(1, 0));
        Assert.IsTrue(new Complex(3, 3) == new Complex(1, 2) + new Complex(2, 1));
        Assert.IsTrue(new Complex(4.1, 3.7) == new Complex(1.4, 2.6) + 
            new Complex(2.7, 1.1));
    }

    // -
    {
        Assert.IsTrue(new Complex(0, 0) == new Complex(0, 0) - -new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 1) == new Complex(0, 1) - -new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 1) == new Complex(0, 0) - -new Complex(0, 1));
        Assert.IsTrue(new Complex(1, 0) == new Complex(1, 0) - -new Complex(0, 0));
        Assert.IsTrue(new Complex(1, 0) == new Complex(0, 0) - -new Complex(1, 0));
        Assert.IsTrue(new Complex(1, 1) == new Complex(1, 0) - -new Complex(0, 1));
        Assert.IsTrue(new Complex(1, 1) == new Complex(0, 1) - -new Complex(1, 0));
        Assert.IsTrue(new Complex(3, 3) == new Complex(1, 2) - -new Complex(2, 1));
        Assert.IsTrue(new Complex(3.8, 3.8) == new Complex(1.7, 2.6) - 
            -new Complex(2.1, 1.2));
    }

    // * Complex
    {
        Assert.IsTrue(new Complex(0, 0) == new Complex(0, 0) * new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 0) == new Complex(0, 1) * new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 0) == new Complex(0, 0) * new Complex(0, 1));
        Assert.IsTrue(new Complex(0, 0) == new Complex(1, 0) * new Complex(0, 0));
        Assert.IsTrue(new Complex(0, 0) == new Complex(0, 0) * new Complex(1, 0));
        Assert.IsTrue(new Complex(0, 1) == new Complex(1, 0) * new Complex(0, 1));
        Assert.IsTrue(new Complex(0, 1) == new Complex(0, 1) * new Complex(1, 0));
        Assert.IsTrue(new Complex(0, 5) == new Complex(1, 2) * new Complex(2, 1));
        Assert.IsTrue(new Complex(-14, 31) == new Complex(2, 3) * 
            new Complex(5, 8));
        AreAlmostRelativeEquals(new Complex(-14.32, 37.47), 
            new Complex(2.3, 3.2) * new Complex(5.6, 8.5));
    }

    // Complex * double, double * Complex, Scale
    {
        Action<Complex, Complex, double> scale_test = (c1, c2, s) =>
        {
            Assert.IsTrue(c1 == c2 * s);
            Assert.IsTrue(c1 == s * c2);
            Assert.IsTrue(c1 == c2.Scale(s));
        };

        scale_test(new Complex(0, 0), new Complex(0, 0), 0);
        scale_test(new Complex(0, 0), new Complex(4, 4), 0);
        scale_test(new Complex(0, 0), new Complex(4, 0), 0);
        scale_test(new Complex(0, 0), new Complex(0, 4), 0);
        scale_test(new Complex(0, 0), new Complex(0, 0), 4);
        scale_test(new Complex(16, 16), new Complex(4, 4), 4);
        scale_test(new Complex(16, 0), new Complex(4, 0), 4);
        scale_test(new Complex(0, 16), new Complex(0, 4), 4);
        scale_test(new Complex(0, 0), new Complex(0, 0), -4);
        scale_test(new Complex(-16, -16), new Complex(4, 4), -4);
        scale_test(new Complex(-16, 0), new Complex(4, 0), -4);
        scale_test(new Complex(0, -16), new Complex(0, 4), -4);
        scale_test(new Complex(-16, -12), new Complex(4, 3), -4);
        scale_test(new Complex(-16, 12), new Complex(4, -3), -4);
        scale_test(new Complex(16, -12), new Complex(-4, 3), -4);
        scale_test(new Complex(17.63, -13.12), new Complex(-4.3, 3.2), -4.1);
    }

    // /
    {
        AreAlmostRelativeEquals(new Complex(23.0 / 41.0, 2.0 / 41.0), 
            new Complex(2, 3) / new Complex(4, 5));
        AreAlmostRelativeEquals(new Complex(23.0 / 13.0, -2.0 / 13.0), 
            new Complex(4, 5) / new Complex(2, 3));
        AreAlmostRelativeEquals(
            new Complex(1.4896226415094338, -0.13867924528301892), 
            new Complex(4.4, 5.3) / new Complex(2.6, 3.8));
    }

    // IsAlmostEquals
    {
        Complex c = new Complex(0, 0);

        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, -0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, -0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(0.5, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(0.5, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-0.5, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-0.5, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(0.5, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(0.5, -0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(-0.5, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(-0.5, -0.5) * 
            Constants.DOUBLE_PRECISION));

        c = new Complex(40, -40);

        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(2, -0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-2, -0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(0.5, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(0.5, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-0.5, 2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsFalse(c.IsAlmostEquals(c + new Complex(-0.5, -2) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(0.5, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(0.5, -0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(-0.5, 0.5) * 
            Constants.DOUBLE_PRECISION));
        Assert.IsTrue(c.IsAlmostEquals(c + new Complex(-0.5, -0.5) * 
            Constants.DOUBLE_PRECISION));
    }

    // IsAlmostRelativeEquals
    {
        Complex c = new Complex(1 / Constants.DOUBLE_PRECISION, 
            1 / Constants.DOUBLE_PRECISION);

        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(2, 2)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(2, -2)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(-2, 2)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(-2, -2)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(2, 0.5)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(-2, 0.5)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(2, -0.5)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(-2, -0.5)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(0.5, 2)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(0.5, -2)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(-0.5, 2)));
        Assert.IsFalse(c.IsAlmostRelativeEquals(c + new Complex(-0.5, -2)));
        Assert.IsTrue(c.IsAlmostRelativeEquals(c + new Complex(0.5, 0.5)));
        Assert.IsTrue(c.IsAlmostRelativeEquals(c + new Complex(0.5, -0.5)));
        Assert.IsTrue(c.IsAlmostRelativeEquals(c + new Complex(-0.5, 0.5)));
        Assert.IsTrue(c.IsAlmostRelativeEquals(c + new Complex(-0.5, -0.5)));
    }

    // Equals(obj), Equals(complex)
    {
        Assert.AreEqual(new Complex(5, 4), new Complex(5, 4));
        Assert.AreEqual(new Complex(5.4, 4.4), new Complex(5.4, 4.4));
        Assert.AreNotEqual(new Complex(5, 4), new Complex(5, 5));
        Assert.AreNotEqual(new Complex(5, 4), new Complex(4, 4));
        Assert.AreNotEqual(new Complex(5, 5), new Complex(5, 4));
        Assert.AreNotEqual(new Complex(4, 4), new Complex(5, 4));

        Assert.IsTrue(new Complex(5, 4).Equals(new Complex(5, 4)));
        Assert.IsTrue(new Complex(5.3, 4.3).Equals(new Complex(5.3, 4.3)));
        Assert.IsFalse(new Complex(5, 4).Equals(new Complex(5, 5)));
        Assert.IsFalse(new Complex(5, 4).Equals(new Complex(4, 4)));
        Assert.IsFalse(new Complex(5, 5).Equals(new Complex(5, 4)));
        Assert.IsFalse(new Complex(4, 4).Equals(new Complex(5, 4)));
    }

    // Conjugate
    {
        Assert.IsTrue(new Complex(0, 0) == new Complex(0, 0).Conjugate);
        Assert.IsTrue(new Complex(1, 0) == new Complex(1, 0).Conjugate);
        Assert.IsTrue(new Complex(0, -1) == new Complex(0, 1).Conjugate);
        Assert.IsTrue(new Complex(0, 1) == new Complex(0, -1).Conjugate);
        Assert.IsTrue(new Complex(2, -3) == new Complex(2, 3).Conjugate);
        Assert.IsTrue(new Complex(2, 3) == new Complex(2, -3).Conjugate);
        Assert.IsTrue(new Complex(-2, -3) == new Complex(-2, 3).Conjugate);
        Assert.IsTrue(new Complex(-2, 3) == new Complex(-2, -3).Conjugate);
        Assert.IsTrue(new Complex(-2.2, 3.2) == new Complex(-2.2, -3.2).Conjugate);
    }

    // Abs
    {
        Assert.IsTrue(0 == new Complex(0, 0).Abs);
        Assert.IsTrue(1 == new Complex(0, 1).Abs);
        Assert.IsTrue(1 == new Complex(0, -1).Abs);
        Assert.IsTrue(1 == new Complex(1, 0).Abs);
        Assert.IsTrue(1 == new Complex(-1, 0).Abs);
        Assert.IsTrue(5 == new Complex(3, 4).Abs);
        Assert.IsTrue(5 == new Complex(-3, 4).Abs);
        Assert.IsTrue(5 == new Complex(3, -4).Abs);
        Assert.IsTrue(5 == new Complex(-3, -4).Abs);
        Assert.IsTrue(2*MathExtensions.SQRT2 == new Complex(2, 2).Abs);
    }

    // Phase
    {
        Assert.AreEqual(Double.NaN,
            MathExtensions.ToDeg(new Complex(0, 0).Phase));
        Assert.AreEqual(Double.NaN,
            MathExtensions.ToDeg(new Complex(Constants.DOUBLE_PRECISION / 2, 
            Constants.DOUBLE_PRECISION / 2).Phase));
        Assert.AreEqual(0,
            MathExtensions.ToDeg(new Complex(1, 0).Phase));
        Assert.AreEqual(45,
            MathExtensions.ToDeg(new Complex(1, 1).Phase));
        Assert.AreEqual(90,
            MathExtensions.ToDeg(new Complex(0, 1).Phase));
        Assert.AreEqual(135,
            MathExtensions.ToDeg(new Complex(-1, 1).Phase));
        Assert.AreEqual(180,
            MathExtensions.ToDeg(new Complex(-1, 0).Phase));
        Assert.AreEqual(225 - 360,
            MathExtensions.ToDeg(new Complex(-1, -1).Phase));
        Assert.AreEqual(270 - 360,
            MathExtensions.ToDeg(new Complex(0, -1).Phase));
        Assert.AreEqual(315 - 360,
            MathExtensions.ToDeg(new Complex(1, -1).Phase));
    }
}

Brak komentarzy:

Prześlij komentarz