/* * Copyright (c) 2021, Cesar Torres * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #if __has_include() # define AKCOMPLEX_CAN_USE_MATH_H # include #endif #ifdef __cplusplus # if __cplusplus >= 201103L # define COMPLEX_NOEXCEPT noexcept # endif namespace AK { template class [[gnu::packed]] Complex { public: constexpr Complex() : m_real(0) , m_imag(0) { } constexpr Complex(T real) : m_real(real) , m_imag((T)0) { } constexpr Complex(T real, T imaginary) : m_real(real) , m_imag(imaginary) { } constexpr T real() const COMPLEX_NOEXCEPT { return m_real; } constexpr T imag() const COMPLEX_NOEXCEPT { return m_imag; } constexpr T magnitude_squared() const COMPLEX_NOEXCEPT { return m_real * m_real + m_imag * m_imag; } # ifdef AKCOMPLEX_CAN_USE_MATH_H constexpr T magnitude() const COMPLEX_NOEXCEPT { // for numbers 32 or under bit long we don't need the extra precision of sqrt // although it may return values with a considerable error if real and imag are too big? if constexpr (sizeof(T) <= sizeof(float)) { return sqrtf(m_real * m_real + m_imag * m_imag); } else if constexpr (sizeof(T) <= sizeof(double)) { return sqrt(m_real * m_real + m_imag * m_imag); } else { return sqrtl(m_real * m_real + m_imag * m_imag); } } constexpr T phase() const COMPLEX_NOEXCEPT { return atan2(m_imag, m_real); } template static constexpr Complex from_polar(U magnitude, V phase) { if constexpr (sizeof(T) <= sizeof(float)) { return Complex(magnitude * cosf(phase), magnitude * sinf(phase)); } else if constexpr (sizeof(T) <= sizeof(double)) { return Complex(magnitude * cos(phase), magnitude * sin(phase)); } else { return Complex(magnitude * cosl(phase), magnitude * sinl(phase)); } } # endif template constexpr Complex& operator=(const Complex& other) { m_real = other.real(); m_imag = other.imag(); return *this; } template constexpr Complex& operator=(const U& x) { m_real = x; m_imag = 0; return *this; } template constexpr Complex operator+=(const Complex& x) { m_real += x.real(); m_imag += x.imag(); return *this; } template constexpr Complex operator+=(const U& x) { m_real += x.real(); return *this; } template constexpr Complex operator-=(const Complex& x) { m_real -= x.real(); m_imag -= x.imag(); return *this; } template constexpr Complex operator-=(const U& x) { m_real -= x.real(); return *this; } template constexpr Complex operator*=(const Complex& x) { const T real = m_real; m_real = real * x.real() - m_imag * x.imag(); m_imag = real * x.imag() + m_imag * x.real(); return *this; } template constexpr Complex operator*=(const U& x) { m_real *= x; m_imag *= x; return *this; } template constexpr Complex operator/=(const Complex& x) { const T real = m_real; const T divisor = x.real() * x.real() + x.imag() * x.imag(); m_real = (real * x.real() + m_imag * x.imag()) / divisor; m_imag = (m_imag * x.real() - x.real() * x.imag()) / divisor; return *this; } template constexpr Complex operator/=(const U& x) { m_real /= x; m_imag /= x; return *this; } template constexpr Complex operator+(const Complex& a) { Complex x = *this; x += a; return x; } template constexpr Complex operator+(const U& a) { Complex x = *this; x += a; return x; } template constexpr Complex operator-(const Complex& a) { Complex x = *this; x -= a; return x; } template constexpr Complex operator-(const U& a) { Complex x = *this; x -= a; return x; } template constexpr Complex operator*(const Complex& a) { Complex x = *this; x *= a; return x; } template constexpr Complex operator*(const U& a) { Complex x = *this; x *= a; return x; } template constexpr Complex operator/(const Complex& a) { Complex x = *this; x /= a; return x; } template constexpr Complex operator/(const U& a) { Complex x = *this; x /= a; return x; } template constexpr bool operator==(const Complex& a) const { return (this->real() == a.real()) && (this->imag() == a.imag()); } template constexpr bool operator!=(const Complex& a) const { return !(*this == a); } constexpr Complex operator+() { return *this; } constexpr Complex operator-() { return Complex(-m_real, -m_imag); } private: T m_real; T m_imag; }; // reverse associativity operators for scalars template constexpr Complex operator+(const U& b, const Complex& a) { Complex x = a; x += b; return x; } template constexpr Complex operator-(const U& b, const Complex& a) { Complex x = a; x -= b; return x; } template constexpr Complex operator*(const U& b, const Complex& a) { Complex x = a; x *= b; return x; } template constexpr Complex operator/(const U& b, const Complex& a) { Complex x = a; x /= b; return x; } // some identities template static constinit Complex complex_real_unit = Complex((T)1, (T)0); template static constinit Complex complex_imag_unit = Complex((T)0, (T)1); # ifdef AKCOMPLEX_CAN_USE_MATH_H template static constexpr bool approx_eq(const Complex& a, const Complex& b, const double margin = 0.000001) { const auto x = const_cast&>(a) - const_cast&>(b); return x.magnitude() <= margin; } // complex version of exp() template static constexpr Complex cexp(const Complex& a) { // FIXME: this can probably be faster and not use so many expensive trigonometric functions if constexpr (sizeof(T) <= sizeof(float)) { return expf(a.real()) * Complex(cosf(a.imag()), sinf(a.imag())); } else if constexpr (sizeof(T) <= sizeof(double)) { return exp(a.real()) * Complex(cos(a.imag()), sin(a.imag())); } else { return expl(a.real()) * Complex(cosl(a.imag()), sinl(a.imag())); } } } # endif using AK::Complex; using AK::complex_imag_unit; using AK::complex_real_unit; # ifdef AKCOMPLEX_CAN_USE_MATH_H using AK::approx_eq; using AK::cexp; # endif #endif