Skip to content

File util_core.h

File List > doppler > util > util_core.h

Go to the documentation of this file

#ifndef DP_UTIL_CORE_H
#define DP_UTIL_CORE_H

#include "doppler/clib_common.h"
#include "doppler/jm_perf.h"
#include <math.h>

#ifdef __cplusplus
extern "C"
{
#endif

  JM_FORCEINLINE float _Complex
  dp_square_clip (float _Complex y, float lin)
  {
    float r = fminf (fmaxf (crealf (y), -lin), lin);
    float i = fminf (fmaxf (cimagf (y), -lin), lin);
    return r + i * I;
  }

  JM_FORCEINLINE size_t
  dp_next_pow_two (size_t n)
  {
    size_t c = 1u;
    while (c < n)
      {
        if (c > ((size_t)-1) / 2u)
          return 0u;
        c <<= 1;
      }
    return c;
  }

  JM_FORCEINLINE double
  dp_saturate (double v, double lo, double hi, double nan_to)
  {
    if (v >= lo && v <= hi)
      return v; /* the common case; false for NaN, which falls through */
    if (v < lo)
      return lo;
    if (v > hi)
      return hi;
    return nan_to; /* nothing else can reach here */
  }

  JM_FORCEINLINE double
  dp_ema_step (double state, double x, double alpha)
  {
    /* Loop-invariant, and folded away entirely when alpha is a
       compile-time constant, so the common path pays nothing. */
    if (alpha >= 1.0)
      return x;
    return state + alpha * (x - state);
  }

  JM_FORCEINLINE double
  dp_complement_power (double p, double x)
  {
    if (x == 1.0)
      return p; /* exact by construction, not by luck */
    if (x == 0.0 || p <= 0.0)
      return 0.0;
    if (p >= 1.0)
      return 1.0; /* log1p(-1) is -inf; answer it directly */
    return -expm1 (x * log1p (-p));
  }

  JM_FORCEINLINE double
  dp_ema_alpha_decim (double alpha, size_t d)
  {
    if (d <= 1)
      return alpha; /* exact by construction, not by luck */
    return dp_complement_power (alpha, (double)d);
  }

  JM_FORCEINLINE double
  dp_sinc (double u)
  {
    return (u == 0.0) ? 1.0 : sin (M_PI * u) / (M_PI * u);
  }

  JM_FORCEINLINE int
  dp_simpson_weights (double *w, size_t w_len)
  {
    if (w_len < 3 || (w_len & 1u) == 0)
      return DP_ERR_INVALID;
    const double s = 3.0 * (double)(w_len - 1);
    for (size_t i = 0; i < w_len; i++)
      w[i] = (i == 0 || i + 1 == w_len ? 1.0 : (i & 1u) ? 4.0 : 2.0) / s;
    return DP_OK;
  }

  JM_FORCEINLINE double
  dp_mean_sinc (double umax)
  {
    if (umax <= 0.0)
      return 1.0;
    /* 64-interval Simpson over segments of at most half a bin, so the error
       is one bound at any umax rather than growing with it. */
    double w[65];
    (void)dp_simpson_weights (w, 65);
    const size_t segs = umax > 0.5 ? (size_t)ceil (2.0 * umax) : 1u;
    const double len  = umax / (double)segs;
    double       m    = 0.0;
    for (size_t s = 0; s < segs; s++)
      for (size_t i = 0; i < 65; i++)
        m += w[i] * dp_sinc (len * ((double)s + (double)i / 64.0));
    return m / (double)segs;
  }

  JM_FORCEINLINE void
  dp_midpoint_nodes (double *u, size_t u_len)
  {
    for (size_t k = 0; k < u_len; k++)
      u[k] = ((double)k + 0.5) / (double)u_len;
  }

  JM_FORCEINLINE int
  dp_gauss_hermite (double *z, size_t z_len, double *p, size_t p_len)
  {
    if (z_len == 0 || z_len != p_len)
      return DP_ERR_INVALID;
    const size_t n = z_len;
    double       x = 0.0; /* the guess, in physicists' units: z = x sqrt 2 */
    for (size_t i = 0; i < (n + 1) / 2; i++)
      {
        /* The i-th largest root. Guesses from the roots already found,
           which sit at z[n-1], z[n-2], ... */
        if (i == 0)
          x = sqrt (2.0 * (double)n + 1.0)
              - 1.85575 * pow (2.0 * (double)n + 1.0, -1.0 / 6.0);
        else if (i == 1)
          x -= 1.14 * pow ((double)n, 0.426) / x;
        else if (i == 2)
          x = 1.86 * x - 0.86 * z[n - 1] / M_SQRT2;
        else if (i == 3)
          x = 1.91 * x - 0.91 * z[n - 2] / M_SQRT2;
        else
          x = 2.0 * x - z[n - 1 - (i - 2)] / M_SQRT2;
        double r = M_SQRT2 * x, hm;
        for (int it = 0; it < 100; it++)
          {
            double h0 = 1.0, h1 = r; /* h[k-1], h[k] from k = 1 */
            for (size_t k = 1; k < n; k++)
              {
                const double h2
                    = (r * h1 - sqrt ((double)k) * h0) / sqrt ((double)k + 1.0);
                h0 = h1;
                h1 = h2;
              }
            /* h1 is h[n]; its derivative is sqrt(n) h[n-1] = sqrt(n) h0. */
            const double dz = h1 / (sqrt ((double)n) * h0);
            r -= dz;
            if (fabs (dz) <= 1e-15 * fmax (1.0, fabs (r)))
              break;
          }
        /* h[n-1] at the converged root, for its weight. */
        {
          double h0 = 1.0, h1 = r;
          for (size_t k = 1; k + 1 < n; k++)
            {
              const double h2
                  = (r * h1 - sqrt ((double)k) * h0) / sqrt ((double)k + 1.0);
              h0 = h1;
              h1 = h2;
            }
          hm = n == 1 ? 1.0 : h1;
        }
        x              = r / M_SQRT2;
        const double w = 1.0 / ((double)n * hm * hm);
        z[n - 1 - i]   = r;
        p[n - 1 - i]   = w;
        z[i]           = -r;
        p[i]           = w;
      }
    if (n & 1u)
      z[n / 2] = 0.0; /* the middle root, exactly */
    return DP_OK;
  }

#ifdef __cplusplus
}
#endif

#endif /* UTIL_CORE_H */