/* enoki/complex.h -- Complex number data structure Enoki is a C++ template library that enables transparent vectorization of numerical kernels using SIMD instruction sets available on current processor architectures. Copyright (c) 2019 Wenzel Jakob All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. */ #pragma once #include NAMESPACE_BEGIN(enoki) /// SFINAE helper for complex numbers template using is_complex_helper = enable_if_t::IsComplex>; template constexpr bool is_complex_v = is_detected_v; template using enable_if_complex_t = enable_if_t>; template using enable_if_not_complex_t = enable_if_t>; template struct Complex : StaticArrayImpl> { using Base = StaticArrayImpl>; ENOKI_ARRAY_IMPORT_BASIC(Base, Complex); using Base::operator=; static constexpr bool IsComplex = true; static constexpr bool IsVector = false; using ArrayType = Complex; using MaskType = Mask; template using ReplaceValue = Complex; Complex() = default; template = 0> ENOKI_INLINE Complex(T&& z) : Base(z) { } template < Base::Depth && (is_scalar_v || is_array_v))> = 0, enable_if_not_complex_t = 0> ENOKI_INLINE Complex(T &&v) : Base(v, zero()) { } template == Base::Depth || !(is_scalar_v || is_array_v))> = 0, enable_if_not_complex_t = 0> ENOKI_INLINE Complex(T &&v) : Base(std::forward(v)) { } ENOKI_INLINE Complex(const Value_ &v1, const Value_ &v2) : Base(v1, v2) { } template ENOKI_INLINE static Complex full_(const T &value, size_t size) { return Array::full_(value, size); } }; template = 0> ENOKI_INLINE T identity(size_t size = 1) { using Value = value_t; return T(full(1.f, size), zero(size)); } template ENOKI_INLINE expr_t real(const Complex &z) { return z.x(); } template ENOKI_INLINE expr_t imag(const Complex &z) { return z.y(); } template ENOKI_INLINE expr_t squared_norm(const Complex &z) { return squared_norm(Array, 2>(z)); } template ENOKI_INLINE expr_t norm(const Complex &z) { return norm(Array, 2>(z)); } template ENOKI_INLINE Complex> normalize(const Complex &q) { return enoki::normalize(Array, 2>(q)); } template ENOKI_INLINE Complex> rcp(const Complex &z) { auto scale = rcp(squared_norm(z)); return Complex>( real(z) * scale, -imag(z) * scale ); } template , typename Result = Complex> ENOKI_INLINE Result operator*(const Complex &z0, const Complex &z1) { using Base = Array; Base z1_perm = shuffle<1, 0>(z1), z0_im = shuffle<1, 1>(z0), z0_re = shuffle<0, 0>(z0); return fmaddsub(z0_re, z1, z0_im * z1_perm); } template , typename Result = Complex> ENOKI_INLINE Result operator*(const Complex &z0, const T1 &v1) { return Array, 2>(z0) * v1; } template , typename Result = Complex> ENOKI_INLINE Result operator*(const T0 &v0, const Complex &z1) { return v0 * Array, 2>(z1); } template , typename Result = Complex> ENOKI_INLINE Result operator/(const Complex &z0, const Complex &z1) { return z0 * rcp(z1); } template , typename Result = Complex> ENOKI_INLINE Result operator/(const Complex &z0, const T1 &v1) { return Array, 2>(z0) / v1; } template ENOKI_INLINE Complex> conj(const Complex &z) { const Complex> mask(0.f, -0.f); return z ^ mask; } template ENOKI_INLINE expr_t abs(const Complex &z) { return norm(z); } template ENOKI_INLINE Complex> exp(const Complex &z) { auto exp_r = exp(real(z)); auto [s, c] = sincos(imag(z)); return { exp_r * c, exp_r * s }; } template ENOKI_INLINE Complex> log(const Complex &z) { return { .5f * log(squared_norm(z)), arg(z) }; } template ENOKI_INLINE expr_t arg(const Complex &z) { return atan2(imag(z), real(z)); } template > std::pair sincos_arg_diff(const Complex &z1, const Complex &z2) { Expr normalization = rsqrt(squared_norm(z1) * squared_norm(z2)); Complex value = z1 * conj(z2) * normalization; return { imag(value), real(value) }; } template ENOKI_INLINE auto pow(const Complex &z0, const Complex &z1) { return exp(log(z0) * z1); } template ENOKI_INLINE Complex> sqrt(const Complex &z) { auto [s, c] = sincos(arg(z) * .5f); auto r = sqrt(abs(z)); return Complex>(c * r, s * r); } template ENOKI_INLINE Complex> sqrtz(const T &x) { auto r = sqrt(abs(x)), z = zero(); auto is_real = x >= 0; return { select(is_real, r, z), select(is_real, z, r) }; } template ENOKI_INLINE Complex> sin(const Complex &z) { auto [s, c] = sincos(real(z)); auto [sh, ch] = sincosh(imag(z)); return Complex>(s * ch, c * sh); } template ENOKI_INLINE Complex> cos(const Complex &z) { auto [s, c] = sincos(real(z)); auto [sh, ch] = sincosh(imag(z)); return Complex>(c * ch, -s * sh); } template >> ENOKI_INLINE std::pair sincos(const Complex &z) { auto [s, c] = sincos(real(z)); auto [sh, ch] = sincosh(imag(z)); return std::make_pair( R(s * ch, c * sh), R(c * ch, -s * sh) ); } template ENOKI_INLINE Complex> tan(const Complex &z) { auto [s, c] = sincos(z); return s / c; } template >> ENOKI_INLINE R asin(const Complex &z) { auto tmp = log(R(-imag(z), real(z)) + sqrt(1.f - z*z)); return R(imag(tmp), -real(tmp)); } template >> ENOKI_INLINE R acos(const Complex &z) { auto tmp = sqrt(1.f - z*z); tmp = log(z + R(-imag(tmp), real(tmp))); return R(imag(tmp), -real(tmp)); } template >> ENOKI_INLINE R atan(const Complex &z) { const R I(0.f, 1.f); auto tmp = log((I-z) / (I+z)); return R(imag(tmp) * .5f, -real(tmp) * .5f); } template ENOKI_INLINE Complex> sinh(const Complex &z) { auto [s, c] = sincos(imag(z)); auto [sh, ch] = sincosh(real(z)); return { sh * c, ch * s }; } template ENOKI_INLINE Complex> cosh(const Complex &z) { auto [s, c] = sincos(imag(z)); auto [sh, ch] = sincosh(real(z)); return { ch * c, sh * s }; } template >> ENOKI_INLINE std::pair sincosh(const Complex &z) { auto [s, c] = sincos(imag(z)); auto [sh, ch] = sincosh(real(z)); return std::make_pair( R(sh * c, ch * s), R(ch * c, sh * s) ); } template ENOKI_INLINE Complex> tanh(const Complex &z) { auto [sh, ch] = sincosh(z); return sh / ch; } template ENOKI_INLINE Complex> asinh(const Complex &z) { return log(z + sqrt(z*z + 1.f)); } template ENOKI_INLINE Complex> acosh(const Complex &z) { return log(z + sqrt(z*z - 1.f)); } template >> ENOKI_INLINE R atanh(const Complex &z) { return log((R(1.f) + z) / (R(1.f) - z)) * R(.5f); } template = 0> ENOKI_NOINLINE std::ostream &operator<<(std::ostream &os, const Complex &z) { os << z.x(); os << (z.y() < 0 ? " - " : " + ") << abs(z.y()) << "i"; return os; } template = 0, enable_if_not_array_t> = 0> ENOKI_NOINLINE std::ostream &operator<<(std::ostream &os, const Complex &z) { os << "["; size_t size = z.x().size(); for (size_t i = 0; i < size; ++i) { os << z.x().coeff(i); os << (z.y().coeff(i) < 0 ? " - " : " + ") << abs(z.y().coeff(i)) << "i"; if (i + 1 < size) os << ",\n "; } os << "]"; return os; } NAMESPACE_END(enoki)