#pragma once
/*
 *  $Id: math.h 29741 2026-04-06 08:37:30Z yeti-dn $
 *  Copyright (C) 2003-2026 David Necas (Yeti), Petr Klapetek.
 *  E-mail: yeti@gwyddion.net, klapetek@gwyddion.net.
 *
 *  This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any
 *  later version.
 *
 *  This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 *  details.
 *
 *  You should have received a copy of the GNU General Public License along with this program; if not, write to the
 *  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 */

#ifndef __GWY_MATH_H__
#define __GWY_MATH_H__

#include <math.h>
#include <glib.h>
#include <glib-object.h>

#include <libgwyddion/macros.h>
#include <libgwyddion/xy.h>
#include <libgwyddion/xyz.h>

G_BEGIN_DECLS

#define GWY_ROUND(x) ((gint)floor((x) + 0.5))
#define gwy_round(x) ((gint)floor((x) + 0.5))

#define gwy_clamp(x, lower, upper) fmin(fmax((x), (lower)), (upper))

#define GWY_SQRT3 1.73205080756887729352744634150587236694280525381038
#define GWY_SQRT_PI 1.77245385090551602729816748334114518279754945612237

typedef enum {
    GWY_PERCENTILE_INTERPOLATION_LINEAR   = 0,
    GWY_PERCENTILE_INTERPOLATION_LOWER    = 1,
    GWY_PERCENTILE_INTERPOLATION_HIGHER   = 2,
    GWY_PERCENTILE_INTERPOLATION_NEAREST  = 3,
    GWY_PERCENTILE_INTERPOLATION_MIDPOINT = 4,
} GwyPercentileInterpolationType;

typedef gdouble (*GwyRealFunc)(gdouble x, gpointer user_data);

gdouble  gwy_math_humanize_numbers     (gdouble unit,
                                        gdouble maximum,
                                        gint *precision);
gboolean gwy_math_is_in_polygon        (gdouble x,
                                        gdouble y,
                                        const gdouble* poly,
                                        guint n)                               G_GNUC_PURE;
gint     gwy_math_find_nearest_line    (gdouble x,
                                        gdouble y,
                                        gdouble *d2min,
                                        const gdouble *coords,
                                        gint n,
                                        const gdouble *metric);
gint     gwy_math_find_nearest_point   (gdouble x,
                                        gdouble y,
                                        gdouble *d2min,
                                        const gdouble *coords,
                                        gint n,
                                        const gdouble *metric);
gint     gwy_math_find_nearest_corner  (gdouble x,
                                        gdouble y,
                                        gdouble *d2min,
                                        const gdouble *coords,
                                        gint n,
                                        const gdouble *metric);
gint     gwy_math_cut_line_by_rectangle(gdouble x,
                                        gdouble y,
                                        gdouble vx,
                                        gdouble vy,
                                        const gdouble *rect,
                                        gdouble *t);
gdouble* gwy_math_lin_solve            (gint n,
                                        const gdouble *matrix,
                                        const gdouble *rhs,
                                        gdouble *result);
gdouble* gwy_math_lin_solve_rewrite    (gint n,
                                        gdouble *matrix,
                                        gdouble *rhs,
                                        gdouble *result);
gboolean gwy_math_tridiag_solve_rewrite(gint n,
                                        gdouble *d,
                                        const gdouble *a,
                                        const gdouble *b,
                                        gdouble *rhs);
gdouble* gwy_math_fit_poly             (const gdouble *xdata,
                                        const gdouble *ydata,
                                        gint ndata,
                                        gint degree,
                                        gdouble *coeffs);
gdouble* gwy_math_fit_poly_equidist    (const gdouble *ydata,
                                        gint ndata,
                                        gdouble x0,
                                        gdouble dx,
                                        gint degree,
                                        gdouble *coeffs);
gboolean gwy_math_choleski_decompose   (gint n,
                                        gdouble *matrix);
void     gwy_math_choleski_solve       (gint n,
                                        const gdouble *decomp,
                                        gdouble *rhs);
gboolean gwy_math_choleski_invert      (gint n,
                                        gdouble *matrix);
guint    gwy_math_curvature_at_apex    (const gdouble *coeffs,
                                        gdouble *kappa1,
                                        gdouble *kappa2,
                                        gdouble *phi1,
                                        gdouble *phi2,
                                        gdouble *xc,
                                        gdouble *yc,
                                        gdouble *zc);
guint    gwy_math_curvature_at_origin  (const gdouble *coeffs,
                                        gdouble *kappa1,
                                        gdouble *kappa2,
                                        gdouble *phi1,
                                        gdouble *phi2);
gint     gwy_math_plane_coefficients   (gdouble sumx2,
                                        gdouble sumxy,
                                        gdouble sumy2,
                                        gdouble sumxz,
                                        gdouble sumyz,
                                        gsize n,
                                        gdouble *bx,
                                        gdouble *by);
gboolean gwy_math_refine_maximum_1d    (const gdouble *y,
                                        gdouble *x);
gboolean gwy_math_refine_maximum_2d    (const gdouble *z,
                                        gdouble *x,
                                        gdouble *y);
gdouble  gwy_math_find_minimum_1d      (GwyRealFunc function,
                                        gdouble a,
                                        gdouble b,
                                        gpointer user_data);
guint*   gwy_check_regular_2d_grid     (const gdouble *coords,
                                        guint stride,
                                        guint n,
                                        gdouble tolerance,
                                        guint *xres,
                                        guint *yres,
                                        GwyXY *xymin,
                                        GwyXY *xystep)                         G_GNUC_MALLOC;
guint*   gwy_check_regular_2d_lattice  (const gdouble *coords,
                                        guint stride,
                                        guint n,
                                        guint *xres,
                                        guint *yres,
                                        GwyXY *xymin,
                                        GwyXY *vx,
                                        GwyXY *vy,
                                        gdouble *maxdiff)                      G_GNUC_MALLOC;
void     gwy_accumulate_counts         (guint *array,
                                        gint n,
                                        gboolean do_rewind);
gdouble* gwy_math_fill                 (gdouble *array,
                                        gsize n,
                                        gdouble value);
gdouble* gwy_math_linspace             (gdouble *array,
                                        gsize n,
                                        gdouble first,
                                        gdouble step);
gdouble  gwy_math_median               (gdouble *array,
                                        gsize n);
gdouble  gwy_math_kth_rank             (gdouble *array,
                                        gsize n,
                                        gsize k);
void     gwy_math_kth_ranks            (gdouble *array,
                                        gsize n,
                                        const guint *k,
                                        guint nk,
                                        gdouble *values);
void     gwy_math_percentiles          (gdouble *array,
                                        gsize n,
                                        GwyPercentileInterpolationType interp,
                                        const gdouble *p,
                                        guint np,
                                        gdouble *values);
void     gwy_math_sort                 (gdouble *array,
                                        gsize n);
void     gwy_guint_sort                (guint *array,
                                        gsize n);
void     gwy_math_sort_with_index      (gdouble *array,
                                        gsize n,
                                        guint *index_array);
gdouble  gwy_math_mean                 (const gdouble *array,
                                        gsize n);
gdouble  gwy_math_trimmed_mean         (gdouble *array,
                                        gsize n,
                                        guint nlowest,
                                        guint nhighest);
gint     gwy_compare_double            (gconstpointer a,
                                        gconstpointer b)                       G_GNUC_PURE;
gdouble  gwy_xlnx_int                  (guint x)                               G_GNUC_CONST;
gdouble  gwy_canonicalize_angle        (gdouble phi,
                                        gboolean positive,
                                        gboolean oriented)                     G_GNUC_CONST;
guint    gwy_math_histogram            (const gdouble *values,
                                        guint n,
                                        gdouble min,
                                        gdouble max,
                                        guint nbins,
                                        guint *counts);

/* We want third party library users to simply #include <gwy.h> and get an inlinable function. They must also get
 * a linkable version, which is necessary for instance for gobject-introspection. This is hairy:
 *
 * - A static inline allows third party code to get the function as inline (good).
 *   It prevents providing a neatly linkable version because the compiler cannot ever see both static inline and
 *   extern for the same symbol (bad).
 *   It makes it invisible for introspection. Probably may be worked around adding some introspection-only stuff and
 *   renaming (ugly).
 *
 * - An extern inline also provides a linkable version (good).
 *   Multiple translations unit cannot all see the definition. It must be provided exactly once which we cannot really
 *   ensure for third parties, only for functions used internally in the library (bad).
 *   They can probably be made visible for introspection but again having to work around the one-definition (ugly).
 *
 * - An inline with neither extern nor static is even less helpful than the two others. It cannot be called from other
 *   translation units.
 *
 * There are some compiler- and linker-specific features which can give us what we want. However, portability is an
 * issue then. Macros may be the least bad solution.
 *
 * Header:
 *
 * extern gdouble gwy_foo(void);
 *
 * #define gwy_foo _gwy_foo_implementation
 * G_GNUC_UNUSED
 * static inline gdouble
 * _gwy_foo_implementation(void)
 * {
 *     return 1.25;
 * }
 *
 * Source:
 * #undef gwy_foo
 *
 * gdouble
 * gwy_foo(void)
 * {
 *     return _gwy_foo_implementation();
 * }
 *
 * This achieves:
 * - All code and gi-scanner see the extern symbol gwy_foo() declared since as it comes first.
 * - Any code is redirected to the the inline implementation, which is actually _gwy_foo_implementation().
 * - Since _gwy_foo_implementation() is static inline, nothing can link to it and it never polutes any symbol table.
 * - Any code which really wants to dlopen or link to the function (gi.repository) can do so.
 *
 * The remaning ugly bit is that C code may really want to see the extern version of the function. It then must get
 * rid of the redirection macro. The least contrived reason is being able to identify functions by address across
 * different compilation units. However, since this is still niche and would require *all* relevant code compiled like
 * this, we do not provide a dedicated flag to disable the indirection macros. Do a bunch of #undefs and send a bug
 * report if it makes you sad?
 */

int     gwy_isnan   (double x);
int     gwy_isinf   (double x);
int     gwy_isnormal(double x);
double  gwy_exp10   (double x);
void    gwy_sincos  (double x,
                     double *sinval,
                     double *cosval);
double  gwy_powi    (double x,
                     int i);
gdouble gwy_sinc    (gdouble x);
gdouble gwy_deg2rad (gdouble x);
gdouble gwy_rad2deg (gdouble x);

#ifndef __GI_SCANNER__
#ifndef __GTK_DOC_IGNORE__
#define gwy_isnan    _gwy_isnan_implementation
#define gwy_isinf    _gwy_isinf_implementation
#define gwy_isnormal _gwy_isnormal_implementation
#define gwy_exp10    _gwy_exp10_implementation
#define gwy_sincos   _gwy_sincos_implementation
#define gwy_powi     _gwy_powi_implementation
#define gwy_sinc     _gwy_sinc_implementation
#define gwy_deg2rad  _gwy_deg2rad_implementation
#define gwy_rad2deg  _gwy_rad2deg_implementation

/* GCC's fast math makes isinf & isnan no-op. Must use the fallback function. Gwyddion works with finite arithmetic,
 * but without working isinf and isnan we would have no idea whether we are getting NaNs and Infs upon file import.
 *
 * An alternative may be gathering the detection code to some source compiled in IEEE standard-compliant mode. */

G_GNUC_UNUSED
static inline int
_gwy_isinf_implementation(double x)
{
    GDoubleIEEE754 dbl;
    dbl.v_double = x;
    return (dbl.mpn.biased_exponent == 0x7ff && !dbl.mpn.mantissa_high && !dbl.mpn.mantissa_low);
}

G_GNUC_UNUSED
static inline int
_gwy_isnan_implementation(double x)
{
    GDoubleIEEE754 dbl;
    dbl.v_double = x;
    return (dbl.mpn.biased_exponent == 0x7ff && (dbl.mpn.mantissa_high || dbl.mpn.mantissa_low));
}

G_GNUC_UNUSED
static inline int
_gwy_isnormal_implementation(double x)
{
    GDoubleIEEE754 dbl;
    dbl.v_double = x;
    return dbl.mpn.biased_exponent != 0x7ff;
}

G_GNUC_UNUSED
static inline gdouble
_gwy_sinc_implementation(gdouble x)
{
    if (G_LIKELY(fabs(x) > 3e-4))
        return sin(x)/x;
    return 1.0 - x*x/6.0;
}

G_GNUC_UNUSED
static inline double
_gwy_rad2deg_implementation(double x)
{
    return x/G_PI*180.0;
}

G_GNUC_UNUSED
static inline double
_gwy_deg2rad_implementation(double x)
{
    return x/180.0*G_PI;
}

#ifdef GWY_HAVE_EXP10
#define _gwy_exp10_implementation exp10
#else
G_GNUC_UNUSED
static inline double
_gwy_exp10_implementation(double x)
{
    return exp(G_LN10 * x);
}
#endif

#ifdef GWY_HAVE_SINCOS
#define _gwy_sincos_implementation sincos
#else
G_GNUC_UNUSED
static inline void
_gwy_sincos_implementation(double x, double *sinval, double *cosval)
{
    *sinval = sin(x);
    *cosval = cos(x);
}
#endif

/* This is a compiler thing, not system configuration.  So we cannot even specify in gwyconfig.h whether the function
 * is avilable or not.  All solutions are bad; the cleanest one (do not ever expose the function) does not allow using
 * it in code built on Gwyddion and thus kind of defeats the point... */
#if (defined(__GNUC__) || defined(__clang__))
#define _gwy_powi_implementation __builtin_powi
#else
/* From the precision standpoint this is not nice, but it should be used for small fixed powers where the compiler
 * should reduce it to a couple of multiplications.  Good enough as a fallback. */
G_GNUC_UNUSED
static inline double
_gwy_powi_implementation(double x, int i)
{
    gdouble /* */ r = 1.0;
    gboolean /* */ negative = FALSE;
    if (!i)
        return 1.0;
    if (i < 0) {
        negative = TRUE;
        i = -i;
    }
    while (TRUE) {
        if (i & 1)
            r *= x;
        if (!(i >>= 1))
            break;
        x *= x;
    }
    return negative ? 1.0/r : r;
}
#endif

#endif
#endif

G_END_DECLS

#endif

/* vim: set cin columns=120 tw=118 et ts=4 sw=4 cino=>1s,e0,n0,f0,{0,}0,^0,\:1s,=0,g1s,h0,t0,+1s,c3,(0,u0 : */
