complex.hh 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. // -*- mode: c++; coding: utf-8 -*-
  2. // ra-ra - Definitions related to complex types.
  3. // (c) Daniel Llorens - 2005, 2023
  4. // This library is free software; you can redistribute it and/or modify it under
  5. // the terms of the GNU Lesser General Public License as published by the Free
  6. // Software Foundation; either version 3 of the License, or (at your option) any
  7. // later version.
  8. #pragma once
  9. #include <cmath>
  10. #include <limits>
  11. #include <complex>
  12. #include <algorithm> // for clamp()
  13. // abs() needs no qualifying for ra:: types (ADL), shouldn't need it on pods either. FIXME maybe let user decide.
  14. // std::max/min are special, see DEF_NAME in ra.hh.
  15. using std::max, std::min, std::abs, std::fma, std::sqrt, std::pow, std::exp, std::swap,
  16. std::isfinite, std::isinf, std::isnan, std::clamp, std::lerp, std::conj, std::expm1;
  17. #define FOR_FLOAT(T) \
  18. constexpr T conj(T x) { return x; } \
  19. FOR_EACH(FOR_FLOAT, float, double)
  20. #undef FOR_FLOAT
  21. #define FOR_FLOAT(R, C) \
  22. constexpr C \
  23. fma(C const & a, C const & b, C const & c) \
  24. { \
  25. return C(fma(a.real(), b.real(), fma(-a.imag(), b.imag(), c.real())), \
  26. fma(a.real(), b.imag(), fma(a.imag(), b.real(), c.imag()))); \
  27. } \
  28. constexpr bool isfinite(C z) { return isfinite(z.real()) && isfinite(z.imag()); } \
  29. constexpr bool isnan(C z) { return isnan(z.real()) || isnan(z.imag()); } \
  30. constexpr bool isinf(C z) { return (isinf(z.real()) || isinf(z.imag())) && !isnan(z); }
  31. FOR_FLOAT(float, std::complex<float>)
  32. FOR_FLOAT(double, std::complex<double>)
  33. #undef FOR_FLOAT
  34. namespace ra {
  35. // As an array op; special definitions for rank 0.
  36. template <class T> constexpr bool ra_is_real = std::numeric_limits<T>::is_integer || std::is_floating_point_v<T>;
  37. template <class T> requires (ra_is_real<T>) constexpr T amax(T const & x) { return x; }
  38. template <class T> requires (ra_is_real<T>) constexpr T amin(T const & x) { return x; }
  39. template <class T> requires (ra_is_real<T>) constexpr T sqr(T const & x) { return x*x; }
  40. #define FOR_FLOAT(T) \
  41. constexpr T arg(T x) { return T(0); } \
  42. constexpr T conj(T x) { return x; } \
  43. constexpr T mul_conj(T x, T y) { return x*y; } \
  44. constexpr T sqrm(T x) { return sqr(x); } \
  45. constexpr T sqrm(T x, T y) { return sqr(x-y); } \
  46. constexpr T dot(T x, T y) { return x*y; } \
  47. constexpr T fma_conj(T a, T b, T c) { return fma(a, b, c); } \
  48. constexpr T norm2(T x) { return std::abs(x); } \
  49. constexpr T norm2(T x, T y) { return std::abs(x-y); } \
  50. constexpr T rel_error(T a, T b) { auto den = (abs(a)+abs(b)); return den==0 ? 0. : 2.*norm2(a, b)/den; } \
  51. constexpr T const & real_part(T const & x) { return x; } \
  52. constexpr T & real_part(T & x) { return x; } \
  53. constexpr T imag_part(T x) { return T(0); }
  54. FOR_EACH(FOR_FLOAT, float, double)
  55. #undef FOR_FLOAT
  56. // FIXME few still inline should eventually be constexpr.
  57. #define FOR_FLOAT(R, C) \
  58. inline R arg(C x) { return std::arg(x); } \
  59. constexpr C sqr(C x) { return x*x; } \
  60. constexpr C dot(C x, C y) { return x*y; } \
  61. constexpr C xI(R x) { return C(0, x); } \
  62. constexpr C xI(C z) { return C(-z.imag(), z.real()); } \
  63. constexpr R real_part(C const & z) { return z.real(); } \
  64. constexpr R imag_part(C const & z) { return z.imag(); } \
  65. inline R & real_part(C & z) { return reinterpret_cast<R *>(&z)[0]; } \
  66. inline R & imag_part(C & z) { return reinterpret_cast<R *>(&z)[1]; } \
  67. constexpr R sqrm(C x) { return sqr(x.real())+sqr(x.imag()); } \
  68. constexpr R sqrm(C x, C y) { return sqr(x.real()-y.real())+sqr(x.imag()-y.imag()); } \
  69. constexpr R norm2(C x) { return hypot(x.real(), x.imag()); } \
  70. constexpr R norm2(C x, C y) { return sqrt(sqrm(x, y)); } \
  71. inline R rel_error(C a, C b) { auto den = (abs(a)+abs(b)); return den==0 ? 0. : 2.*norm2(a, b)/den; } \
  72. /* conj(a) * b + c */ \
  73. constexpr C \
  74. fma_conj(C const & a, C const & b, C const & c) \
  75. { \
  76. return C(fma(a.real(), b.real(), fma(a.imag(), b.imag(), c.real())), \
  77. fma(a.real(), b.imag(), fma(-a.imag(), b.real(), c.imag()))); \
  78. } \
  79. /* conj(a) * b */ \
  80. constexpr C \
  81. mul_conj(C const & a, C const & b) \
  82. { \
  83. return C(a.real()*b.real()+a.imag()*b.imag(), \
  84. a.real()*b.imag()-a.imag()*b.real()); \
  85. }
  86. FOR_FLOAT(float, std::complex<float>)
  87. FOR_FLOAT(double, std::complex<double>)
  88. #undef FOR_FLOAT
  89. } // namespace ra