/**
 *	@FR_math.h - header definition file for fixed radix math routines
 *
 *	@copy Copyright (C) <2001-2026>  <M. A. Chatterjee>
 *  @author M A Chatterjee <deftio [at] deftio [dot] com>
 *
 *  This file contains integer math settable fixed point radix math routines for
 *  use on systems in which floating point is not desired or unavailable.
 *
 *  @license:
 *	This software is provided 'as-is', without any express or implied
 *	warranty. In no event will the authors be held liable for any damages
 *	arising from the use of this software.
 *
 *	Permission is granted to anyone to use this software for any purpose,
 *	including commercial applications, and to alter it and redistribute it
 *	freely, subject to the following restrictions:
 *
 *	1. The origin of this software must not be misrepresented; you must not
 *	claim that you wrote the original software. If you use this software
 *	in a product, an acknowledgment in the product documentation would be
 *	appreciated but is not required.
 *
 *	2. Altered source versions must be plainly marked as such, and must not be
 *	misrepresented as being the original software.
 *
 *	3. This notice may not be removed or altered from any source
 *	distribution.
 *
 */

#ifndef __FR_Math_h__
#define __FR_Math_h__

#define FR_MATH_VERSION     "2.0.8"
#define FR_MATH_VERSION_HEX  0x020008  /* major << 16 | minor << 8 | patch */

#ifdef FR_CORE_ONLY
#define FR_NO_PRINT
#define FR_NO_WAVES
#endif

#ifdef FR_LEAN
#define FR_NO_WAVES
#endif

#ifdef __cplusplus
extern "C"
{
#endif

#ifndef __FR_Platform_Defs_H__
#include "FR_defs.h"
#endif

/* Quick note on macro parameter wrapping:
 * Arguments are parenthesized in expansions, e.g.
 *   #define MACRO_X_SQUARED(x)  ((x)*(x))   // inner parens around each x
 * Macros substitute text as-is. If a parameter is an expression like 3+4*5
 * and the body mixes operators without extra parentheses, precedence errors
 * follow. Parenthesize parameters (and fragile subexpressions) in the macro body.
 * Example: MACRO_X_SQUARED_BAD(x)  (x*x)  ->  3+4*5*3+4*5  == 83 (wrong).
 *          MACRO_X_SQUARED(x)  ((x)*(x))  ->  (3+4*5)*(3+4*5)  == 529 (right).
 */

/*absolute value for integer and fixed radix types*/
#define FR_ABS(x) (((x) < 0) ? (-(x)) : (x))

/*sign of x.  Not as good as The Sign of Four, but I digress           */
#define FR_SGN(x) ((x) >> ((((signed)sizeof(x)) << 3) - 1))

/*===============================================
 * Simple Fixed Point Math Conversions
 * r is radix precision in bits, converts to/from integer w truncation */
#define I2FR(x, r) ((x) << (r))
#define FR2I(x, r) ((x) >> (r))

/*===============================================
 * Make a fixed radix number from integer + base-10 fractional parts.
 * r  = output radix (fractional bits)
 * i  = integer part (signed)
 * f  = decimal-fraction digits as written, e.g. for "12.34" pass f=34
 * d  = number of decimal digits in f, e.g. for "12.34" pass d=2
 *
 * FR_NUM(12, 34, 2, 10)  ≈ 12.34 in s.10  = (12<<10) + (34<<10)/100 = 12636
 * FR_NUM(-3, 5,  1, 16)  ≈ -3.5  in s.16  = (-3<<16) - (5<<16)/10
 *
 * The fraction is rounded toward zero. For round-to-nearest, add half an LSB
 * before scaling at the call site. Sign of the fractional part follows the
 * sign of i (for i==0 the result is positive, matching "+0.5" intuition).
 */
#define FR_NUM_POW10(d) (                                              \
    ((d) == 0) ? 1L :                                                  \
    ((d) == 1) ? 10L :                                                 \
    ((d) == 2) ? 100L :                                                \
    ((d) == 3) ? 1000L :                                               \
    ((d) == 4) ? 10000L :                                              \
    ((d) == 5) ? 100000L :                                             \
    ((d) == 6) ? 1000000L :                                            \
    ((d) == 7) ? 10000000L :                                           \
    ((d) == 8) ? 100000000L : 1000000000L)
#define FR_NUM(i, f, d, r) (                                           \
    ((s32)(i) << (r)) +                                                \
    (((i) < 0)                                                         \
        ? -((s32)(((s32)(f) << (r)) / FR_NUM_POW10(d)))                \
        :  ((s32)(((s32)(f) << (r)) / FR_NUM_POW10(d)))))
/*
FR_INT(x,r) convert a fixed radix variable x of radix r to an integer
*/
#define FR_INT(x, r) (((x) < 0) ? -((-(x)) >> (r)) : ((x) >> (r)))

/* Change Radix (x,current_radix, new_radix)
 * change number from its current fixed radix (can be 0 or integer) to a new fixed radix
 * Useful when dealing with numbers with mixed radixes.  This is a MACRO so
 * this code expands in place and x is modified.
 */
#define FR_CHRDX(x, r_cur, r_new) (((r_cur) - (r_new)) >= 0 ? ((x) >> ((r_cur) - (r_new))) : ((x) << ((r_new) - (r_cur))))

/* return only the fractional part of x */
#define FR_FRAC(x, r) ((FR_ABS(x)) & (((1 << (r)) - 1)))

/* return the fractional part of number x with radix xr scaled to radix nr bits */
#define FR_FRACS(x, xr, nr) (FR_CHRDX(FR_FRAC((x), (xr)), (xr), (nr)))

/******************************************************
  Add (sub) to fixed point numbers by converting the second number to the
  same radix as the first. If yr < xr then possibility of overflow is increased.
  Note: for two vars, i, j, of prec ir, jr, it is not necessarily true that
  FR_ADD(i,ir,j,jr) == FR_ADD(j,jr,i,ir)
*/
#define FR_ADD(x, xr, y, yr) ((x) += FR_CHRDX(y, yr, xr))
#define FR_SUB(x, xr, y, yr) ((x) -= FR_CHRDX(y, yr, xr))

/* Fixed-radix division with round-to-nearest: x (at radix xr) / y (at radix yr),
 * result at radix xr. Uses a 64-bit intermediate so the full Q16.16 range works
 * correctly. Adds half the divisor before truncation to achieve ≤ 0.5 LSB error. */
static inline s32 FR_div_rnd(s64 num, s32 den) {
    if ((num ^ den) >= 0)                   /* same sign: positive quotient */
        return (s32)((num + den / 2) / den);
    else                                     /* negative quotient */
        return (s32)((num - den / 2) / den);
}
#define FR_DIV(x, xr, y, yr) FR_div_rnd((s64)(x) << (yr), (s32)(y))

/* FR_DIV_TRUNC: truncating division (old FR_DIV behavior). Useful when the
 * caller knows the quotient is exact or truncation is acceptable. */
#define FR_DIV_TRUNC(x, xr, y, yr) ((s32)(((s64)(x) << (yr)) / (s32)(y)))

/* FR_DIV32: 32-bit-only division. Requires |x| < 2^(31-yr) to avoid
 * overflow in the intermediate (x << yr). Use FR_DIV for full-range
 * division with 64-bit intermediate. */
#define FR_DIV32(x, xr, y, yr) (((s32)(x) << (yr)) / (s32)(y))

/* Remainder: both operands should be at the same radix. */
#define FR_MOD(x, y) ((x) % (y))

/* min, max, clamp */
#define FR_MIN(a, b)         (((a) < (b)) ? (a) : (b))
#define FR_MAX(a, b)         (((a) > (b)) ? (a) : (b))
#define FR_CLAMP(x, lo, hi)  (FR_MIN(FR_MAX((x), (lo)), (hi)))

/* Check if x is a power of 2. */
#define FR_ISPOW2(x) (!((x) & ((x) - 1)))

/* floor and ceiling in current radix, leaving current radix intact
 * this means the lower radix number of bits are set to 0
 */
#define FR_FLOOR(x, r) ((x) & (~((1 << r) - 1)))
#define FR_CEIL(x, r) (FR_FLOOR(x, r) + ((FR_FRAC(x, r) ? (1 << r) : 0)))

/*******************************************************
  Interpolate between 2 fixed point values (x0,x1) of any radix,
  with a fractional delta, of a supplied precision.
  If delta is outside 0<= delta<=1 then extrapolation
  does work but programmer should watch for possible overflow.
  x0,x1 need not have same radix as delta
*/
#define FR_INTERP(x0, x1, delta, prec) ((x0) + ((((x1) - (x0)) * (delta)) >> (prec)))

/******************************************************
   FR_INTERPI is the same as FR_INTERP except that  insures the
   range is btw [0<= delta < 1] --> ((0 ... (1<<prec)-1)
   Note: delta should be >= 0
*/
#define FR_INTERPI(x0, x1, delta, prec) ((x0) + ((((x1) - (x0)) * ((delta) & ((1 << (prec)) - 1))) >> (prec)))

/******************************************************
  Convert to double, this is for debug only and WILL NOT compile under many embedded systems.
  Fixed Radix to Floating Point Double Conversion
  since this is a MACRO it will not be compiled or instantiated unless it is actually called in code.
*/
#define FR2D(x, r) ((double)(((double)(x)) / ((double)(1 << (r)))))
#define D2FR(d, r) ((s32)(d * (1 << r)))

/******************************************************
  Useful Constants

  FR_kXXXX "k" denotes constant to help when reading macros used in source code
  FR_krXXX "r" == reciprocal e.g. 1/XXX
  As these are MACROS, they take only compiled code space if actually used.
  Consts here calculated by multiply the natural base10 value by (1<<FR_kPREC)
 */
#define FR_kPREC (16)         /* bits of precision in constants listed below */
#define FR_kE (178145)        /* 2.718281828459 */
#define FR_krE (24109)        /* 0.367879441171 */
#define FR_kPI (205887)       /* 3.141592653589 */
#define FR_krPI (20861)       /* 0.318309886183 */
#define FR_kDEG2RAD (1144)    /* 0.017453292519 */
#define FR_kRAD2DEG (3754936) /*57.295779513082 */

#define FR_kQ2RAD (102944) /* 1.570796326794 */
#define FR_kRAD2Q (41722)  /* 0.636619772367 */

/*log2 to ln conversions (see MACROS) */
#define FR_kLOG2E (94548)  /* 1.442695040890 */
#define FR_krLOG2E (45426) /* 0.693147180560 */

/*log2 to log10 conversions (see MACROS) */
#define FR_kLOG2_10 (217706) /* 3.32192809489 */
#define FR_krLOG2_10 (19728) /* 0.30102999566 */

/* High-precision scaling constants at radix 28.
 * Used by FR_EXP, FR_ln, FR_log10 for base conversion.
 * At radix 28 these have ~9 decimal digits of precision, far exceeding
 * the ~4.8 digits of Q16.16.
 */
#define FR_kLOG2E_28    (387270501)   /* log2(e)   = 1.4426950408889634  */
#define FR_krLOG2E_28   (186065279)   /* ln(2)     = 0.6931471805599453  */
#define FR_kLOG2_10_28  (891723283)   /* log2(10)  = 3.3219280948873622  */
#define FR_krLOG2_10_28  (80807124)   /* log10(2)  = 0.3010299956639812  */

/* Multiply fixed-point value x (any radix) by a radix-28 constant k.
 * Result stays at x's radix. Uses 64-bit intermediate.
 * Rounds to nearest (adds 0.5 LSB before shift).
 */
#define FR_MULK28(x, k) ((s32)((((int64_t)(x) * (int64_t)(k)) + (1 << 27)) >> 28))

// common sqrts
#define FR_kSQRT2 (92682)   /* 1.414213562373 */
#define FR_krSQRT2 (46341)  /* 0.707106781186 */
#define FR_kSQRT3 (113512)  /* 1.732050807568 */
#define FR_krSQRT3 (37837)  /* 0.577350269189 */
#define FR_kSQRT5 (146543)  /* 2.236067977599 */
#define FR_krSQRT5 (29309)  /* 0.447213595499 */
#define FR_kSQRT10 (207243) /* 3.162277660168 */
#define FR_krSQRT10 (20724) /* 0.316227766016 */

  /*===============================================
   * Arithmetic operations
   */
  s32 FR_FixMuls(s32 x, s32 y);   // mul signed, round-to-nearest, NOT saturated
  s32 FR_FixMulSat(s32 x, s32 y); // mul signed, round-to-nearest, saturated
  s32 FR_FixAddSat(s32 x, s32 y); // add signed, saturated

/*================================================
 * Constants used in Trig tables, definitions
 *
 * FR_TRIG_PREC     — internal table precision (u0.15, sine table)
 * FR_TRIG_OUT_PREC — output precision of sin/cos/tan (s15.16 since v2.0.1)
 * FR_TRIG_ONE      — exact 1.0 in output format (1 << 16 = 65536)
 *
 * sin/cos return s32 at radix 16 (s15.16). This matches libfixmath Q16.16
 * precision and allows exact representation of 1.0 at the poles.
 * tan returns s32 at radix 16 (s15.16). Saturates at ±FR_TRIG_MAXVAL.
 */
#define FR_TRIG_PREC     (15)
#define FR_TRIG_OUT_PREC (16)
#define FR_TRIG_MASK     ((1 << (FR_TRIG_PREC)) - 1)
#define FR_TRIG_ONE      (1L << FR_TRIG_OUT_PREC)         /* 65536 = 1.0 */
#define FR_TRIG_MAXVAL   ((s32)0x7fffffff)                 /* tan saturation max */
#define FR_TRIG_MINVAL   (-FR_TRIG_MAXVAL)                  /* tan saturation min */

/* Bit Shift Scaling macros.  Useful on some platforms with poor MUL performance.
 * Also can be useful if you need to scale numbers with
 * large portions of bits_in_use and a larger register size is not available.
 * For example, suppose you need to scale a large 32bit number say z=0xa4239323
 * from degrees to radians.  Ideally this number would be multiplied by 0.017
 * but using a (z*FR_kDEG2RAD) >> FR_kPREC type operation is likely to overflow
 * due the MUL result being large.  The FR_RAD2DEG MACRO below doesn't require
 * accumulator headroom bits and so has no chance for loss of precision.
 * Another benefit is  some low end micros (8051, 68xx, MSP430(low end),
 * PIC-8 family) have no multiplier.  So these allow the programmer an option
 * to see if performance or precision are better expressed as shifts rather than
 * scaled multiplies.  As always, mileage may vary depending on architecture,
 * compiler and other considerations.
 */

/* scale by 10s */
#define FR_SMUL10(x) (((x) << 3) + (((x) << 1)))
#define FR_SDIV10(x) (((x) >> 3) - ((x) >> 5) + ((x) >> 7) - ((x) >> 9) + ((x) >> 11))

/* scale by 1/log2(e)  0.693147180560 used for converting log2() to ln()  */
#define FR_SrLOG2E(x) (((x) >> 1) + ((x) >> 2) - ((x) >> 3) + ((x) >> 4) + ((x) >> 7) - ((x) >> 9) - ((x) >> 12) + ((x) >> 15))

/* scale by log2(e)    1.442695040889 used for converting pow2() to exp() */
#define FR_SLOG2E(x) ((x) + ((x) >> 1) - ((x) >> 4) + ((x) >> 8) + ((x) >> 10) + ((x) >> 12) + ((x) >> 14))

/* scale by 1/log2(10) 0.30102999566 used for converting log2() to log10  */
#define FR_SrLOG2_10(x) (((x) >> 2) + ((x) >> 4) - ((x) >> 6) + ((x) >> 7) - ((x) >> 8) + ((x) >> 12))

/* scale by log2(10)   3.32192809489 used for converting pow2() to pow10 */
#define FR_SLOG2_10(x) (((x) << 1) + (x) + ((x) >> 2) + ((x) >> 4) + ((x) >> 7) + ((x) >> 10) + ((x) >> 11) + ((x) >> 13))

/* Shift-only angular conversion macros
 *
 * All are pure constant multipliers expressed as shifts — no multiply, no
 * divide, no 64-bit intermediates, no accumulators. Work at any radix: if
 * your input is degrees at radix 8, the output is the target unit at radix 8.
 * The caller shifts as needed.
 *
 * Angular units:
 *   degrees   = 360  per revolution
 *   radians   = 2*pi per revolution
 *   BAM       = 65536 per revolution (Binary Angular Measure, u16)
 *   quadrants = 4 per revolution (= BAM >> 14)
 *
 * Side-effect note: x is referenced multiple times in each macro — do not
 * pass expressions with side effects.
 */

/* FR_DEG2RAD(x): multiply by pi/180 ≈ 0.017453 (5 terms, ~17 bits) */
#define FR_DEG2RAD(x) (((x) >> 6) + ((x) >> 9) - ((x) >> 13) - ((x) >> 19) - ((x) >> 20))

/* FR_RAD2DEG(x): multiply by 180/pi ≈ 57.29578 (7 terms, ~19 bits) */
#define FR_RAD2DEG(x) (((x) << 6) - ((x) << 3) + (x) + ((x) >> 2) + (((x) >> 4) - ((x) >> 6)) - ((x) >> 10))

/* FR_DEG2BAM(x): multiply by 65536/360 ≈ 182.0449 (7 terms, ~18 bits).
 * Intermediate terms overflow s32 when |x| > ~256 deg at s15.16 (x<<7 term),
 * but the overflow is harmless when the result is truncated to u16 BAM
 * (two's complement wrapping preserves modular correctness).
 * For full-precision s32 BAM (sub-BAM interpolation), use fr_deg_to_bam(). */
#define FR_DEG2BAM(x) (((x)<<7)+((x)<<6)-((x)<<3)-((x)<<1)+((x)>>5)+((x)>>6)-((x)>>9))

/* FR_BAM2DEG(x): multiply by 360/65536 = 0.00549316 (4 terms, exact) */
#define FR_BAM2DEG(x) (((x)>>8)+((x)>>9)-((x)>>12)-((x)>>13))

/* FR_RAD2BAM(x): multiply by 65536/(2*pi) ≈ 10430.378 (7 terms, ~21 bits).
 * CAUTION: overflows s32 when |x| > ~4 rad at s15.16 (x<<13 term).
 * For safe conversion at any radix, use fr_rad_to_bam() instead. 
 * #define FR_RAD2BAM(x) (((x)<<13)+((x)<<11)+((x)<<7)+((x)<<6)-((x)<<1)+((x)>>1)-((x)>>3)) */
#define FR_RAD2BAM(x) (((x)<<13)+((x)<<11)+((x)<<7)+((x)<<6)-((x)<<1)+((x)>>1)-((x)>>3)+((x)>>8)-((x)>>11)-((x)>>14))
/* ── Overflow-safe rad/deg to BAM conversion functions ─────────────
 *
 * These replace the FR_RAD2BAM / FR_DEG2BAM macros for callers that
 * need the full ±2*pi or ±360° range at any radix.
 *
 * Strategy: normalize input to radix 16, conditionally reduce into
 * a safe zone, apply the full-precision shift-only multiply, then
 * extract the u16 BAM.  No precision loss from halving/quartering.
 *
 * fr_rad_to_bam: reduce to [-pi, pi], reordered terms.  ±2*pi safe.
 * fr_deg_to_bam: reduce to [-90, 90) + quadrant offset.  ±360° safe.
 */

/* Pi constants at any radix: FR_PI(r) = round(pi * 2^r), etc.
 * Compiler evaluates at compile time when r is a constant.
 * Max safe radix: FR_PI r<=29, FR_TWO_PI r<=28, FR_HALF_PI r<=30. */
#define FR_PI(r)             ((s32)(3.14159265358979323846  * (1LL << (r)) + 0.5))
#define FR_TWO_PI(r)         ((s32)(6.28318530717958647692  * (1LL << (r)) + 0.5))
#define FR_HALF_PI(r)        ((s32)(1.57079632679489661923  * (1LL << (r)) + 0.5))
#define FR_THREE_HALF_PI(r)  ((s32)(4.71238898038468985769  * (1LL << (r)) + 0.5))

/* Convenience aliases at radix 16 */
#define FR_PI_R16       FR_PI(16)
#define FR_TWO_PI_R16   FR_TWO_PI(16)

/* Degree constants at radix 16 (exact — no truncation) */
#define FR_D90_R16      ((s32)90  << 16)
#define FR_D180_R16     ((s32)180 << 16)
#define FR_D360_R16     ((s32)360 << 16)

  u16 fr_rad_to_bam(s32 rad, u16 radix);
#ifndef FR_LEAN
  u16 fr_deg_to_bam(s32 deg, u16 radix);
#endif

/* FR_BAM2RAD(x): multiply by 2*pi/65536 ≈ 0.0000959 (5 terms, ~18 bits) */
#define FR_BAM2RAD(x) (((x)>>13)-((x)>>15)+((x)>>18)+((x)>>21)+((x)>>25))

/* Legacy quadrant macros (quadrants = BAM >> 14) */
#define FR_RAD2Q(x) (((x) >> 1) + ((x) >> 3) + ((x) >> 7) + ((x) >> 8) - ((x) >> 14))
#define FR_Q2RAD(x) ((x) + ((x) >> 1) + ((x) >> 4) + ((x) >> 7) + ((x) >> 11))
#define FR_DEG2Q(x) (((x) >> 6) - ((x) >> 8) - ((x) >> 11) - ((x) >> 13))
#define FR_Q2DEG(x) (((x) << 6) + ((x) << 4) + ((x) << 3) + ((x) << 1))

/*===============================================
 * BAM (Binary Angular Measure) — internal angle representation
 *
 * One full circle = 2^16 BAM units. So:
 *   0       = 0 deg     = 0 rad
 *   16384   = 90 deg    = pi/2 rad   (FR_BAM_QUADRANT)
 *   32768   = 180 deg   = pi rad
 *   49152   = 270 deg   = 3pi/2 rad
 *   65536   wraps to 0  (because BAM is u16)
 *
 * BAM is the natural representation for fixed-point trig because:
 *   - The top 2 bits select the quadrant (no `% 360` modulo needed).
 *   - The next 7 bits index the 128-entry quadrant table directly.
 *   - The bottom 7 bits give linear-interpolation precision.
 */
#define FR_BAM_BITS         (16)
#define FR_BAM_FULL         (1L << FR_BAM_BITS)         /* 65536 */
#define FR_BAM_QUADRANT     (FR_BAM_FULL >> 2)          /* 16384 */
#define FR_BAM_HALF         (FR_BAM_FULL >> 1)          /* 32768 */

/*===============================================
 * Radian-native and BAM-native trig (recommended)
 *
 * All sin/cos functions return s32 at radix 16 (s15.16).
 * 1.0 is represented exactly as FR_TRIG_ONE (65536).
 * Poles (0, 90, 180, 270 deg) produce exact ±FR_TRIG_ONE or 0.
 *
 *   fr_cos_bam(bam)         — cos of a BAM angle,        s15.16 result
 *   fr_sin_bam(bam)         — sin of a BAM angle,        s15.16 result
 *   fr_cos(rad, radix)      — cos of radians at radix,   s15.16 result
 *   fr_sin(rad, radix)      — sin of radians at radix,   s15.16 result
 *   fr_tan(rad, radix)      — tan of radians at radix,   s15.16 result
 *   fr_cos_deg(deg, radix)  — cos of fixed-radix degrees, s15.16 result
 *   fr_sin_deg(deg, radix)  — sin of fixed-radix degrees, s15.16 result
 *   fr_tan_deg(deg, radix)  — tan of fixed-radix degrees, s15.16 result
 *
 * All go through the same 129-entry quadrant table with linear interpolation.
 * Worst-case error: ~2 LSB in s15.16 (~3e-5 absolute), except at the four
 * cardinal angles where the result is exact.
 *
 * The radian and degree wrappers (fr_sin, fr_cos, fr_tan, etc.) range-reduce
 * their input, convert to u16 BAM, and call the BAM-native functions. Small-
 * angle bypasses at the zero crossings (sin≈0, cos≈0, tan≈0) use the linear
 * approximation sin(δ)≈δ to avoid BAM quantization error where it matters most.
 */
  s32 fr_cos_bam(u16 bam);
  s32 fr_sin_bam(u16 bam);
#ifndef FR_LEAN
  s32 fr_tan_bam(u16 bam);
#endif
  s32 fr_cos(s32 rad, u16 radix);
  s32 fr_sin(s32 rad, u16 radix);
  s32 fr_tan(s32 rad, u16 radix);

/* Integer degrees -> BAM using division (exact at all multiples of 45 deg). */
#define FR_DEG2BAM_I(deg) ((u16)((((s32)(deg) << 16) + ((deg) >= 0 ? 180 : -180)) / 360))

/* Legacy single-arg integer-degree macros — use FR_CosI / FR_SinI instead */
/* #define fr_cos_deg(deg)  fr_cos_bam(FR_DEG2BAM_I(deg)) — removed, name reused for 2-arg function */
/* #define fr_sin_deg(deg)  fr_sin_bam(FR_DEG2BAM_I(deg)) — removed, name reused for 2-arg function */

#ifndef FR_LEAN
/*===============================================
 * Degree-input trig API
 *
 *   FR_CosI(deg)              — cos of integer degrees,       s15.16 result
 *   FR_SinI(deg)              — sin of integer degrees,       s15.16 result
 *   FR_TanI(deg)              — tan of integer degrees,       s15.16 result
 *   fr_cos_deg(deg, radix)    — cos of fixed-radix degrees,   s15.16 result
 *   fr_sin_deg(deg, radix)    — sin of fixed-radix degrees,   s15.16 result
 *   fr_tan_deg(deg, radix)    — tan of fixed-radix degrees,   s15.16 result
 */
#define FR_CosI(deg)  fr_cos_bam(FR_DEG2BAM_I(deg))
#define FR_SinI(deg)  fr_sin_bam(FR_DEG2BAM_I(deg))

  s32 fr_cos_deg(s32 deg, u16 radix);
  s32 fr_sin_deg(s32 deg, u16 radix);
  s32 FR_TanI(s32 deg);
  s32 fr_tan_deg(s32 deg, u16 radix);

  /* Legacy macros — use fr_sin_deg/fr_cos_deg/fr_tan_deg in new code */
  #define FR_Sin  fr_sin_deg
  #define FR_Cos  fr_cos_deg
  #define FR_Tan  fr_tan_deg
#endif /* FR_LEAN */

  /* Inverse trig — output in radians at caller-specified radix (s32).
   * FR_atan2 returns radians at radix 16 (s15.16).
   * Range: acos [0, pi], asin [-pi/2, pi/2],
   *        atan [-pi/2, pi/2], atan2 [-pi, pi].
   */
  s32 FR_acos(s32 input, u16 radix, u16 out_radix);
  s32 FR_asin(s32 input, u16 radix, u16 out_radix);
  s32 FR_atan(s32 input, u16 radix, u16 out_radix);
  s32 FR_atan2(s32 y, s32 x, u16 out_radix);

/* Logarithms */
#define FR_LOG2MIN (-(32767 << 16)) /* returned instead of "negative infinity" */

  s32 FR_log2(s32 input, u16 radix, u16 output_radix);
  s32 FR_ln(s32 input, u16 radix, u16 output_radix);
#ifndef FR_LEAN
  s32 FR_log10(s32 input, u16 radix, u16 output_radix);
#endif

  /* Power */
  s32 FR_pow2(s32 input, u16 radix);
#define FR_EXP(input, radix)   (FR_pow2(FR_MULK28((input), FR_kLOG2E_28), (radix)))
#define FR_POW10(input, radix) (FR_pow2(FR_MULK28((input), FR_kLOG2_10_28), (radix)))

/* Shift-only (multiply-free) base-conversion variants.
 * Lower accuracy (~5-10 LSB at Q16.16) but no multiply instruction.
 * Use these on targets where 32x32->64 multiply is expensive.
 */
#define FR_EXP_FAST(input, radix)   (FR_pow2(FR_SLOG2E(input), radix))
#define FR_POW10_FAST(input, radix) (FR_pow2(FR_SLOG2_10(input), radix))

/*===============================================
 * Formatted output and string parsing
 *
 * Define FR_NO_PRINT before including this header to exclude all
 * print/format functions from compilation. This saves ~1.7 KB of ROM
 * on targets that don't need human-readable output (e.g. headless
 * sensor nodes, DSP-only firmware).
 *
 *   #define FR_NO_PRINT
 *   #include "FR_math.h"
 */
#ifndef FR_NO_PRINT

  /* printing family of functions */
  int FR_printNumF(int (*f)(char), s32 n, int radix, int pad, int prec); /* print fixed radix num as floating point e.g.  -12.34" */
  int FR_printNumD(int (*f)(char), int n, int pad);                      /* print decimal number with optional padding e.g. " 12" */
  int FR_printNumH(int (*f)(char), int n, int showPrefix);               /* print num as a hexidecimal e.g. "0x12ab"              */

  /* string-to-fixed-point parser (inverse of FR_printNumF) */
  s32 FR_numstr(const char *s, u16 radix);

#endif /* FR_NO_PRINT */

/*===============================================
 * Square root and hypot
 *
 * Both take fixed-radix inputs and return a result at the same radix.
 * Algorithm: digit-by-digit isqrt on a 64-bit accumulator (no division,
 * at most 32 iterations). Rounds to nearest.
 *
 * Domain error sentinel: input < 0 (sqrt) returns FR_DOMAIN_ERROR. Caller
 * can check `result == FR_DOMAIN_ERROR` to detect domain errors.
 */
  s32 FR_sqrt(s32 input, u16 radix);
#ifndef FR_LEAN
  s32 FR_hypot(s32 x, s32 y, u16 radix);
#endif

  /* Fast approximate magnitude — shift-only, no multiply, no 64-bit.
   * Based on piecewise-linear approximation of sqrt(x*x + y*y).
   * See US Patent 6,567,777 B1 (Chatterjee, expired).
   *
   *   FR_hypot_fast8(x, y)  8-segment, ~0.10% peak error
   *
   * Inputs are raw signed integers (or fixed-point at any radix — the
   * result is at the same radix as the inputs, just like FR_hypot).
   * No radix parameter needed because the algorithm is scale-invariant.
   */
  s32 FR_hypot_fast8(s32 x, s32 y);

/*===============================================
 * Wave generators and ADSR envelope
 *
 * Define FR_NO_WAVES before including this header to exclude all
 * waveform generators (square, pulse, triangle, saw, noise) and the
 * ADSR envelope from compilation. This saves ~400 B of ROM on targets
 * that only need math/trig and don't do audio synthesis.
 *
 *   #define FR_NO_WAVES
 *   #include "FR_math.h"
 */
#ifndef FR_NO_WAVES

/*===============================================
 * Wave generators — synth-style fixed-shape waveforms.
 *
 * All take a u16 BAM phase in [0, 65535] (a full cycle) and return s16
 * in s0.15 in [-32767, +32767]. Use FR_HZ2BAM_INC below to compute a
 * phase increment for a target frequency.
 *
 *   fr_wave_sqr(phase)              50% square
 *   fr_wave_pwm(phase, duty)        variable-duty pulse
 *   fr_wave_tri(phase)              symmetric triangle
 *   fr_wave_saw(phase)              rising sawtooth
 *   fr_wave_tri_morph(phase, brk)   variable-symmetry triangle (morphs to saw)
 *   fr_wave_noise(state*)           LFSR pseudorandom noise
 *
 * fr_wave_tri_morph returns [0, 32767] (unipolar) — caller can re-bias
 * if a bipolar form is desired. The other waves are bipolar [-32767, +32767].
 */
  s16 fr_wave_sqr(u16 phase);
  s16 fr_wave_pwm(u16 phase, u16 duty);
  s16 fr_wave_tri(u16 phase);
  s16 fr_wave_saw(u16 phase);
  s16 fr_wave_tri_morph(u16 phase, u16 break_point);
  s16 fr_wave_noise(u32 *state);

/* FR_HZ2BAM_INC(hz, sample_rate)
 * Compute the per-sample BAM phase increment for a target frequency in Hz
 * given a sample rate in Hz. Result is a u16 to add to the running phase
 * each sample (the running phase wraps mod 2^16 naturally because it's u16).
 *
 *   u16 phase = 0;
 *   u16 inc   = FR_HZ2BAM_INC(440, 48000);
 *   for (...) { sample = fr_sin_bam(phase); phase += inc; }
 *
 * Range: hz must be < sample_rate / 2 (Nyquist) for a meaningful tone;
 * higher hz aliases. The macro does not enforce this.
 *
 * Side-effect note: hz and sample_rate are evaluated once each.
 */
#define FR_HZ2BAM_INC(hz, sample_rate)  ((u16)(((u32)(hz) * 65536UL) / (u32)(sample_rate)))

/*===============================================
 * ADSR envelope generator
 *
 * Linear-segment Attack-Decay-Sustain-Release envelope. Caller-allocated
 * struct, no malloc, no global state. Internal level is held in s1.30 so
 * very long envelopes (e.g. 48000-sample attack at 48 kHz) still get a
 * non-zero per-sample increment. Output is s0.15 in [0, 32767].
 *
 * Lifecycle:
 *   fr_adsr_t env;
 *   fr_adsr_init(&env, atk_samples, dec_samples, sustain_s015, rel_samples);
 *   fr_adsr_trigger(&env);                 // note-on
 *   for (...) sample = fr_adsr_step(&env); // per-audio-sample
 *   fr_adsr_release(&env);                 // note-off
 *   for (...) sample = fr_adsr_step(&env); // until env.state == FR_ADSR_IDLE
 */
#define FR_ADSR_IDLE     (0)
#define FR_ADSR_ATTACK   (1)
#define FR_ADSR_DECAY    (2)
#define FR_ADSR_SUSTAIN  (3)
#define FR_ADSR_RELEASE  (4)

typedef struct fr_adsr_s {
    u8  state;        /* FR_ADSR_* */
    s32 level;        /* current envelope value, s1.30 */
    s32 sustain;      /* sustain target, s1.30 */
    s32 attack_inc;   /* per-sample increment during attack */
    s32 decay_dec;    /* per-sample decrement during decay */
    s32 release_dec;  /* per-sample decrement during release */
} fr_adsr_t;

  void fr_adsr_init(fr_adsr_t *env,
                    u32 attack_samples,
                    u32 decay_samples,
                    s16 sustain_level_s015,
                    u32 release_samples);
  void fr_adsr_trigger(fr_adsr_t *env);
  void fr_adsr_release(fr_adsr_t *env);
  s16  fr_adsr_step(fr_adsr_t *env);

#endif /* FR_NO_WAVES */

#ifdef __cplusplus

} // extern "C"
#endif

#endif /* __FR_Math_h__ */
