/* This file is part of MAUS: http:// micewww.pp.rl.ac.uk:8080/projects/maus
*
* MAUS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MAUS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MAUS. If not, see .
*/
/* Author: Peter Lane
*/
#include
#include
#include
#include
#include
#include "gtest/gtest.h"
#include "Maths/Complex.hh"
using MAUS::imag;
using MAUS::real;
using MAUS::conj;
using MAUS::operator ==;
using MAUS::operator !=;
using MAUS::operator *;
using MAUS::operator *=;
using MAUS::operator /;
using MAUS::operator /=;
using MAUS::operator +;
using MAUS::operator +=;
using MAUS::operator -;
using MAUS::operator -=;
using MAUS::operator <<;
using MAUS::operator >>;
bool equal(const MAUS::complex c1, const MAUS::complex c2) {
return fabs(real(c1) - real(c2)) < 1e-9
&& fabs(imag(c1) - imag(c2) ) < 1e-9;
}
bool equal(double c1, double c2) { return fabs(c1-c2) < 1e-9; }
class ComplexTest : public testing::Test {
public:
ComplexTest() : c1(MAUS::Complex::complex(3, -2)),
c2(MAUS::Complex::complex(3, -2)),
c3(MAUS::Complex::complex(3, 2)),
c4(MAUS::Complex::complex(3, -2)),
cs(MAUS::Complex::complex(6.)),
cc(MAUS::Complex::complex(3, -2))
{ }
protected:
static constexpr double Pi = 3.141592653589793238462643383279502884197169399;
MAUS::complex c1;
MAUS::complex c2;
MAUS::complex c3;
MAUS::complex c4;
MAUS::complex cs;
MAUS::complex ct;
const MAUS::complex cc;
};
TEST_F(ComplexTest, PseudoConstructor) {
// real part explicitly zero, default imaginary part
const MAUS::complex c1 = MAUS::Complex::complex(0.0);
EXPECT_TRUE(equal(real(c1), 0.0));
EXPECT_TRUE(equal(imag(c1), 0.0));
// real and imaginary part explicitly zero
const MAUS::complex c2 = MAUS::Complex::complex(0.0, 0.0);
EXPECT_TRUE(equal(real(c2), 0.0));
EXPECT_TRUE(equal(imag(c2), 0.0));
// arbitrary real and imaginary part
const MAUS::complex c3 = MAUS::Complex::complex(3.14, -15.92);
EXPECT_TRUE(equal(real(c3), 3.14));
EXPECT_TRUE(equal(imag(c3), -15.92));
}
TEST_F(ComplexTest, Equals) {
// operator ==
ASSERT_TRUE(c1 == c2);
ASSERT_FALSE(c1 == c3);
ASSERT_TRUE(c1 == c4);
double real_number = 3.141;
ct = MAUS::Complex::complex(real_number, 0.0);
ASSERT_TRUE(ct == real_number);
ASSERT_TRUE(real_number == ct);
}
TEST_F(ComplexTest, NotEquals) {
// operator !=
ASSERT_FALSE(c1 != c2);
ASSERT_TRUE(c1 != c3);
ASSERT_FALSE(c1 != c4);
double real_number = 3.141;
ct = MAUS::Complex::complex(0.707, 0.0);
ASSERT_TRUE(ct != real_number);
ASSERT_TRUE(real_number != ct);
}
TEST_F(ComplexTest, Components) {
// real and imag functions
EXPECT_TRUE(fabs((real(c1) - 3) ) < 1e-9);
EXPECT_TRUE(fabs((imag(c1) + 2) ) < 1e-9);
EXPECT_TRUE(fabs((real(cc) - 3) ) < 1e-9);
EXPECT_TRUE(fabs((imag(cc) + 2) ) < 1e-9);
}
TEST_F(ComplexTest, Double) {
ASSERT_TRUE(real(cs) == 6.);
ASSERT_TRUE(fabs(imag(cs)) < 1e-9);
}
TEST_F(ComplexTest, Conjugate) {
// complext conjugate
EXPECT_TRUE(conj(MAUS::Complex::complex(3, -2))
== MAUS::Complex::complex(3, 2));
EXPECT_TRUE(conj(MAUS::Complex::complex(3))
== MAUS::Complex::complex(3));
}
TEST_F(ComplexTest, Assignment) {
// operator =
ct = c1;
EXPECT_TRUE(ct == c1);
}
TEST_F(ComplexTest, Multiplication) {
// multiplication
ct = c1*2.;
EXPECT_TRUE(real(ct) == real(c1)*2. && imag(ct) == imag(c1)*2.);
ct *= 2.;
EXPECT_TRUE(real(ct) == real(c1)*4. && imag(ct) == imag(c1)*4.);
ct = 2.*ct;
EXPECT_TRUE(real(ct) == real(c1)*8. && imag(ct) == imag(c1)*8.);
ct = c1;
ct *= c3;
EXPECT_TRUE(real(ct) == (real(c1)*real(c3) - imag(c1)*imag(c3)));
EXPECT_TRUE(imag(ct) == (real(c1)*imag(c3) + imag(c1)*real(c3)));
ct = c1*c3;
EXPECT_TRUE(real(ct) == (real(c1)*real(c3) - imag(c1)*imag(c3)));
EXPECT_TRUE(imag(ct) == (real(c1)*imag(c3) + imag(c1)*real(c3)));
}
TEST_F(ComplexTest, Division) {
ct = c1;
ct = ct/2.;
ct = ct*2.;
EXPECT_TRUE(equal(c1, ct));
ct /= 2.;
ct *= 2.;
EXPECT_TRUE(equal(ct, c1));
ct = 2./c1;
ct *= c1;
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(2, 0)));
ct = c1;
ct /= c3;
ct *= c3;
EXPECT_TRUE(equal(ct, c1));
ct = c1/c3;
ct *= c3;
EXPECT_TRUE(equal(ct, c1));
}
TEST_F(ComplexTest, Addition) {
ct = MAUS::Complex::complex(3, -2);
ct += 2.;
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(5, -2)));
ct = MAUS::Complex::complex(3, -2) + 2.;
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(5, -2)));
ct = 2. + MAUS::Complex::complex(3, -2);
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(5, -2)));
ct = MAUS::Complex::complex(3, -2) + MAUS::Complex::complex(5, -1);
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(8, -3)));
ct = MAUS::Complex::complex(3, -2);
ct += MAUS::Complex::complex(5, -1);
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(8, -3)));
}
TEST_F(ComplexTest, Negation) {
ct = -MAUS::Complex::complex(3, -2);
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(-3, 2)));
}
TEST_F(ComplexTest, Subtraction) {
ct = MAUS::Complex::complex(3, -2);
ct -= 2.;
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(1, -2)));
ct = MAUS::Complex::complex(3, -2) - 2.;
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(1, -2)));
ct = 2. - MAUS::Complex::complex(3, -2);
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(-1, 2)));
ct = MAUS::Complex::complex(3, -2) - MAUS::Complex::complex(5, -1);
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(-2, -1)));
ct = MAUS::Complex::complex(3, -2);
ct -= MAUS::Complex::complex(5, -1);
EXPECT_TRUE(equal(ct, MAUS::Complex::complex(-2, -1)));
}
TEST_F(ComplexTest, Polar) {
const double radius = 2.19;
const double angle = 26 * Pi / 180;
const double x = radius * cos(angle);
const double y = radius * sin(angle);
const MAUS::complex rectangular_value = MAUS::Complex::complex(x, y);
const MAUS::complex polar_value = MAUS::polar(radius, angle);
EXPECT_TRUE(equal(polar_value, rectangular_value));
}
TEST_F(ComplexTest, Magnitude) {
double magnitude_squared = MAUS::abs2(cc);
double norm_squared = MAUS::norm2(cc);
double test_magnitude_squared = real(cc)*real(cc) + imag(cc)*imag(cc);
EXPECT_TRUE(equal(magnitude_squared, test_magnitude_squared));
EXPECT_TRUE(equal(norm_squared, test_magnitude_squared));
double magnitude = MAUS::abs(cc);
double norm = MAUS::norm(cc);
double test_magnitude = sqrt(test_magnitude_squared);
EXPECT_TRUE(equal(magnitude, test_magnitude));
EXPECT_TRUE(equal(norm, test_magnitude));
}
TEST_F(ComplexTest, Arg) {
double argument = MAUS::arg(cc);
EXPECT_TRUE(equal(argument, atan2(imag(cc), real(cc))));
}
TEST_F(ComplexTest, Exponential) {
// e^z = e^x (cos y + i sin y)
MAUS::complex exponential = MAUS::exp(cc);
ct = MAUS::Complex::complex(cos(imag(cc)), sin(imag(cc)));
ct *= exp(real(cc));
EXPECT_TRUE(equal(exponential, ct));
}
TEST_F(ComplexTest, Logarithms) {
MAUS::complex natural_log = MAUS::log(cc);
double x = log(MAUS::abs(cc));
double y = MAUS::arg(cc);
ct = MAUS::Complex::complex(x, y);
EXPECT_TRUE(equal(natural_log, ct));
MAUS::complex log_base_10 = MAUS::log10(cc);
ct = natural_log / log(10);
EXPECT_TRUE(equal(log_base_10, ct));
}
TEST_F(ComplexTest, Pow) {
// z^p = r^p e^{i p theta}
const double radius = 2.19;
const double theta = 26.46 * Pi / 180;
const double real_power = 4.387;
const MAUS::complex cp = MAUS::polar(radius, theta);
MAUS::complex cc_pow_double = MAUS::pow(cp, real_power);
ct = MAUS::Complex::complex(cos(real_power * theta), sin(real_power * theta));
ct *= pow(radius, real_power);
EXPECT_TRUE(equal(cc_pow_double, ct));
// z^w = (r e^{i theta})^(x+iy) = e^{(x lnr-y theta)+i(y lnr + x theta)}
const MAUS::complex complex_power = MAUS::Complex::complex(5.69, 2.81);
MAUS::complex cc_pow_complex = MAUS::pow(cp, complex_power);
MAUS::complex e_power = MAUS::Complex::complex(
real(complex_power) * log(radius) - imag(complex_power) * theta,
imag(complex_power) * log(radius) + real(complex_power) * theta);
ct = MAUS::exp(e_power);
EXPECT_TRUE(equal(cc_pow_complex, ct));
}
TEST_F(ComplexTest, SquareRoot) {
MAUS::complex square_root = MAUS::sqrt(64.0);
ct = MAUS::Complex::complex(sqrt(64.0), 0.0);
EXPECT_TRUE(equal(square_root, ct));
square_root = MAUS::sqrt(cc);
ct = MAUS::polar(sqrt(MAUS::norm(cc)), MAUS::arg(cc)/2);
EXPECT_TRUE(equal(square_root, ct));
}
TEST_F(ComplexTest, TrigonometricFunctions) {
const MAUS::complex sine = MAUS::sin(cc);
const MAUS::complex i_cc = MAUS::Complex::complex(0.0, 1.0) * cc;
ct = (MAUS::exp(i_cc) - MAUS::exp(-i_cc)) / MAUS::Complex::complex(0.0, 2.0);
EXPECT_TRUE(equal(sine, ct));
const MAUS::complex cosine = MAUS::cos(cc);
ct = (MAUS::exp(i_cc) + MAUS::exp(-i_cc)) / MAUS::Complex::complex(2.0, 0.0);
EXPECT_TRUE(equal(cosine, ct));
const MAUS::complex tangent = MAUS::tan(cc);
ct = sine / cosine;
EXPECT_TRUE(equal(tangent, ct));
const MAUS::complex cosecant = MAUS::csc(cc);
ct = 1.0 / sine;
EXPECT_TRUE(equal(cosecant, ct));
const MAUS::complex secant = MAUS::sec(cc);
ct = 1.0 / cosine;
EXPECT_TRUE(equal(secant, ct));
const MAUS::complex cotangent = MAUS::cot(cc);
ct = 1.0 / tangent;
EXPECT_TRUE(equal(cotangent, ct));
}
TEST_F(ComplexTest, HyperbolicTrigonometricFunctions) {
const MAUS::complex sineh = MAUS::sinh(cc);
ct = (MAUS::exp(cc) - MAUS::exp(-cc)) / MAUS::Complex::complex(2.0, 0.0);
EXPECT_TRUE(equal(sineh, ct));
const MAUS::complex cosineh = MAUS::cosh(cc);
ct = (MAUS::exp(cc) + MAUS::exp(-cc)) / MAUS::Complex::complex(2.0, 0.0);
EXPECT_TRUE(equal(cosineh, ct));
const MAUS::complex tangenth = MAUS::tanh(cc);
ct = sineh / cosineh;
EXPECT_TRUE(equal(tangenth, ct));
const MAUS::complex cosecanth = MAUS::csch(cc);
ct = 1.0 / sineh;
EXPECT_TRUE(equal(cosecanth, ct));
const MAUS::complex secanth = MAUS::sech(cc);
ct = 1.0 / cosineh;
EXPECT_TRUE(equal(secanth, ct));
const MAUS::complex cotangenth = MAUS::coth(cc);
ct = 1.0 / tangenth;
EXPECT_TRUE(equal(cotangenth, ct));
}
TEST_F(ComplexTest, Streaming) {
std::stringstream test_stream;
test_stream << c1;
test_stream >> ct;
EXPECT_TRUE(ct == c1);
}