packages feed

KpqC-0.1.0: cbits/third_party/HAETAE/include/fixpoint.h

// SPDX-License-Identifier: MIT

#ifndef HAETAE_FIXPOINT_H
#define HAETAE_FIXPOINT_H

#include "params.h"

#include <stdint.h>
#include <stdlib.h>

#ifdef __SIZEOF_INT128__
__extension__ typedef __int128 int128;
__extension__ typedef unsigned __int128 uint128;
#endif

typedef struct {
  uint64_t limb48[2];
} fp96_76;

#define fixpoint_square HAETAE_NAMESPACE(fixpoint_square)
void fixpoint_square(fp96_76 *sqx, const fp96_76 *x);

#define fixpoint_newton_invsqrt HAETAE_NAMESPACE(fixpoint_newton_invsqrt)
void fixpoint_newton_invsqrt(fp96_76 *invsqrtx, const fp96_76 *xhalf);

#define fixpoint_mul_rnd13 HAETAE_NAMESPACE(fixpoint_mul_rnd13)
int32_t fixpoint_mul_rnd13(const uint64_t x, const fp96_76 *y,
                           const uint8_t sign);

#define fixpoint_add HAETAE_NAMESPACE(fixpoint_add)
void fixpoint_add(fp96_76 *xy, const fp96_76 *x, const fp96_76 *y);

static inline void renormalize(fp96_76 *x) {
  x->limb48[1] += x->limb48[0] >> 48;
  x->limb48[0] &= (UINT64_C(1) << 48) - 1;
}

static inline int64_t smulh48(int64_t a, uint64_t b) {
#ifdef __SIZEOF_INT128__
  return (((int128)a * (int128)b) + ((int128)1 << 48) - 1) >> 48;
#else
  int64_t ah = a >> 24;
  int64_t al = a - (ah << 24);
  int64_t bl = b & ((1 << 24) - 1);
  int64_t bh = b >> 24;

  int64_t res = (al * bl + ((1 << 24) - 1)) >> 24; // ceiling in low word
  res += al * bh + ah * bl;
  res = (res + ((1 << 24) - 1)) >> 24; // ceiling in high word
  return res + (ah * bh);
#endif
}

static inline void mul64(uint64_t r[2], const uint64_t b, const uint64_t a) {
#ifndef __SIZEOF_INT128__
  uint64_t al = a & ((UINT64_C(1) << 32) - 1),
           bl = b & ((UINT64_C(1) << 32) - 1), ah = a >> 32, bh = b >> 32;
  r[0] = a * b;
  r[1] = ah * bl + al * bh + ((al * bl) >> 32);
  r[1] >>= 32;
  r[1] += ah * bh;
#else
  uint128 res = ((uint128)a * (uint128)b);
  r[0] = res;
  r[1] = res >> 64;
#endif
}

static inline void sq64(uint64_t r[2], const uint64_t a) {
#ifndef __SIZEOF_INT128__
  uint64_t al = a & ((UINT64_C(1) << 32) - 1), ah = a >> 32;
  r[0] = a * a;
  r[1] = ah * al * 2 + ((al * al) >> 32);
  r[1] >>= 32;
  r[1] += ah * ah;
#else
  uint128 res = ((uint128)a * (uint128)a);
  r[0] = res;
  r[1] = res >> 64;
#endif
}

static inline void mul48(uint64_t r[2], const uint64_t b, const uint64_t a) {
  mul64(r, b, a);
  r[1] <<= 16;
  r[1] ^= r[0] >> 48;
  r[0] &= (UINT64_C(1) << 48) - 1;
}

static inline void mulacc48(uint64_t r[2], const uint64_t b, const uint64_t a) {
  uint64_t tmp[2];
  mul48(tmp, b, a);
  r[0] += tmp[0];
  r[1] += tmp[1];
}

// (a0 + a1*2^32)^2 = a0^2 + 2^33*a0*a1 + 2^64*a1^2
static inline void sq48(uint64_t r[2], const uint64_t a) {
  sq64(r, a);

  r[1] <<= 16;
  r[1] ^= r[0] >> 48;
  r[0] &= (UINT64_C(1) << 48) - 1;
}

static inline void fixpoint_mul_high(fp96_76 *xy, const fp96_76 *x,
                                     const uint64_t y) {
  uint64_t tmp[2];
  mul48(&xy->limb48[0], x->limb48[0], y); // implicitly shifted right by 48

  mul48(tmp, x->limb48[1], y);
  xy->limb48[1] += tmp[0];

  // shift right by 28, rounding
  xy->limb48[0] += UINT64_C(1) << 27;
  xy->limb48[0] >>= 28;
  xy->limb48[0] += (xy->limb48[1] << 20) & ((UINT64_C(1) << 48) - 1);
  xy->limb48[1] >>= 28;

  xy->limb48[1] += tmp[1] << 20;

  renormalize(xy);
}

#endif /* !HAETAE_FIXPOINT_H */