////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 1996-2026 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// This file is part of Octave.
//
// Octave 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 3 of the License, or
// (at your option) any later version.
//
// Octave 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 Octave; see the file COPYING.  If not, see
// <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////

#if ! defined (octave_oct_mappers_h)
#define octave_oct_mappers_h 1

#include "octave-config.h"

#include <cmath>

#include <limits>

#include "lo-ieee.h"
#include "oct-cmplx.h"
#include "oct-inttypes-fwd.h"

// Provides some commonly repeated, basic loop templates.

// Specialization for octave_int types
template <typename T>
inline auto mappers_abs (const octave_int<T>& x)
{
    if constexpr (std::is_unsigned_v<T>)
        return x;  // abs doesn't make sense for unsigned types
    else
        return std::abs (x.value ());
}


OCTAVE_BEGIN_NAMESPACE(octave)
OCTAVE_BEGIN_NAMESPACE(math)

extern OCTAVE_API bool isna (double x);
extern OCTAVE_API bool isna (float x);
extern OCTAVE_API bool isna (const Complex& x);
extern OCTAVE_API bool isna (const FloatComplex& x);

extern OCTAVE_API bool is_NaN_or_NA (const Complex& x);
extern OCTAVE_API bool is_NaN_or_NA (const FloatComplex& x);

inline double copysign (double x, double y) { return std::copysign (x, y); }
inline float copysign (float x, float y) { return std::copysignf (x, y); }

inline double signbit (double x) { return std::signbit (x); }
inline float signbit (float x) { return std::signbit (x); }

// Test for negative sign.
extern OCTAVE_API bool negative_sign (double x);
extern OCTAVE_API bool negative_sign (float x);

// Test for positive sign.
inline bool positive_sign (double x) { return ! negative_sign (x); }
inline bool positive_sign (float x) { return ! negative_sign (x); }

extern OCTAVE_API Complex acos (const Complex& x);
extern OCTAVE_API FloatComplex acos (const FloatComplex& x);

extern OCTAVE_API Complex asin (const Complex& x);
extern OCTAVE_API FloatComplex asin (const FloatComplex& x);

inline Complex atan (const Complex& x) { return std::atan (x); }
inline FloatComplex atan (const FloatComplex& x) { return std::atan (x); }

// The C++ standard would normally return a std::complex value for conj
// even when the input is fully real.  Octave overrides this.
inline double conj (double x) { return x; }
inline float conj (float x) { return x; }

template <typename T>
std::complex<T>
conj (const std::complex<T>& x)
{
  return std::conj (x);
}

inline double log2 (double x) { return std::log2 (x); }
inline float log2 (float x) { return std::log2f (x); }

extern OCTAVE_API Complex log2 (const Complex& x);
extern OCTAVE_API FloatComplex log2 (const FloatComplex& x);

extern OCTAVE_API double log2 (double x, int& exp);
extern OCTAVE_API float log2 (float x, int& exp);

extern OCTAVE_API Complex log2 (const Complex& x, int& exp);
extern OCTAVE_API FloatComplex log2 (const FloatComplex& x, int& exp);

inline double exp2 (double x) { return std::exp2 (x); }
inline float exp2 (float x) { return std::exp2f (x); }

template <typename T>
std::complex<T>
ceil (const std::complex<T>& x)
{
  return std::complex<T> (std::ceil (std::real (x)),
                          std::ceil (std::imag (x)));
}

template <typename T>
std::complex<T>
trunc (const std::complex<T>& x)
{
  return std::complex<T> (std::trunc (std::real (x)),
                          std::trunc (std::imag (x)));
}

// Provide alias for trunc under the more familiar name of fix.
inline double fix (double x) { return std::trunc (x); }
inline float fix (float x) { return std::trunc (x); }

template <typename T>
std::complex<T>
fix (const std::complex<T>& x)
{
  return trunc (x);
}

template <typename T>
std::complex<T>
floor (const std::complex<T>& x)
{
  return std::complex<T> (std::floor (std::real (x)),
                          std::floor (std::imag (x)));
}

inline double round (double x) { return std::round (x); }
inline float round (float x) { return std::roundf (x); }

template <typename T>
std::complex<T>
round (const std::complex<T>& x)
{
  return std::complex<T> (round (std::real (x)), round (std::imag (x)));
}

inline double
roundb (double x)
{
  double t = round (x);

  if (fabs (x - t) == 0.5)
    t = 2 * std::trunc (0.5 * t);

  return t;
}

inline float
roundb (float x)
{
  float t = round (x);

  if (fabsf (x - t) == 0.5f)
    t = 2 * std::trunc (0.5f * t);

  return t;
}

template <typename T>
std::complex<T>
roundb (const std::complex<T>& x)
{
  return std::complex<T> (roundb (std::real (x)), roundb (std::imag (x)));
}

extern OCTAVE_API double frexp (double x, int *expptr);
extern OCTAVE_API float frexp (float x, int *expptr);

// Functions testing for NaN, Inf, and finite elements

inline bool isnan (bool) { return false; }
inline bool isnan (char) { return false; }

inline bool isnan (double x) { return std::isnan (x); }
inline bool isnan (float x) { return std::isnan (x); }

template <typename T>
bool
isnan (const octave_int<T>&)
{
  return false;
}

template <typename T>
bool
isnan (const std::complex<T>& x)
{
  return (std::isnan (std::real (x)) || std::isnan (std::imag (x)));
}

inline bool isinf (double x) { return std::isinf (x); }
inline bool isinf (float x) { return std::isinf (x); }

template <typename T>
bool
isinf (const octave_int<T>&)
{
  return false;
}

template <typename T>
bool
isinf (const std::complex<T>& x)
{
  return (std::isinf (std::real (x)) || std::isinf (std::imag (x)));
}

inline bool isfinite (double x) { return std::isfinite (x); }
inline bool isfinite (float x) { return std::isfinite (x); }

template <typename T>
bool
isfinite (const std::complex<T>& x)
{
  return (isfinite (std::real (x)) && isfinite (std::imag (x)));
}

// Some useful tests, that are commonly repeated.
// Test for a finite integer.

// A large benchmark of std::round vs std::trunc showed that trunc was some
// 3 times faster than round, taking some 1.4 nanoseconds for trunc and
// some 4.1 nanoseconds for round.
inline bool is_integer (double x) { return isfinite (x) && x == std::trunc (x); }
inline bool is_integer (float x) { return isfinite (x) && x == std::trunc (x); }

OCTAVE_DEPRECATED (11, "use octave::math::is_integer instead")
inline bool isinteger (double x) { return is_integer (x); }

OCTAVE_DEPRECATED (11, "use octave::math::is_integer instead")
inline bool isinteger (float x) { return is_integer (x); }

inline double
signum (double x)
{
  double tmp = 0.0;

  if (x < 0.0)
    tmp = -1.0;
  else if (x > 0.0)
    tmp = 1.0;

  return isnan (x) ? numeric_limits<double>::NaN () : tmp;
}

inline float
signum (float x)
{
  float tmp = 0.0f;

  if (x < 0.0f)
    tmp = -1.0f;
  else if (x > 0.0f)
    tmp = 1.0f;

  return isnan (x) ? numeric_limits<float>::NaN () : tmp;
}

template <typename T>
std::complex<T>
signum (const std::complex<T>& x)
{
  T tmp = std::abs (x);

  return tmp == 0 ? 0.0 : x / tmp;
}

// FIXME: Deprecated in Octave 11.  Remove in Octave 13.
// Convert X to the nearest integer value.  Should not pass NaN to
// this function.

// For integer types?  Hmm.  Need to be sure T is an integer type...
template <typename T>
OCTAVE_DEPRECATED (11, "use octave::math::round instead")
T
x_nint (T x)
{
  return x;
}

template <>
OCTAVE_DEPRECATED (11, "use octave::math::round instead")
inline double x_nint (double x)
{
  return round (x);
}

template <>
OCTAVE_DEPRECATED (11, "use octave::math::round instead")
inline float x_nint (float x)
{
  return round (x);
}

extern OCTAVE_API octave_idx_type nint_big (double x);
extern OCTAVE_API octave_idx_type nint_big (float x);

extern OCTAVE_API int nint (double x);
extern OCTAVE_API int nint (float x);

template <typename T>
T
mod (T x, T y)
{
  T retval;

  if (y == 0)
    retval = x;
  else
    {
      T q = x / y;

      if (round (y) != y
          && (std::abs ((q - round (q)) / round (q))
              < std::numeric_limits<T>::epsilon ()))
        retval = 0;
      else
        {
          T n = std::floor (q);

          // Prevent use of extra precision.
          T tmp = y * n;

          retval = x - tmp;
        }
    }

  if (x != y && y != 0)
    retval = copysign (retval, y);

  return retval;
}

template <typename T>
T
rem (T x, T y)
{
  T retval;

  if (y == 0)
    retval = numeric_limits<T>::NaN ();
  else
    {
      T q = x / y;

      if (round (y) != y
          && (std::abs ((q - round (q)) / round (q))
              < std::numeric_limits<T>::epsilon ()))
        retval = 0;
      else
        {
          T n = std::trunc (q);

          // Prevent use of extra precision.
          T tmp = y * n;

          retval = x - tmp;
        }
    }

  if (x != y && y != 0)
    retval = copysign (retval, x);

  return retval;
}

// Generic min, max definitions
template <typename T>
T
min (T x, T y)
{
  return x <= y ? x : y;
}

template <typename T>
T
max (T x, T y)
{
  return x >= y ? x : y;
}

// This form is favorable.  GCC will translate (x <= y ? x : y) without a
// jump, hence the only conditional jump involved will be the first
// (isnan), infrequent and hence friendly to branch prediction.

inline char
min (char x, char y)
{
  return x <= y ? x : y;
}

inline char
max (char x, char y)
{
  return x >= y ? x : y;
}

inline double
min (double x, double y)
{
  return isnan (y) ? x : (x <= y ? x : y);
}

inline double
max (double x, double y)
{
  return isnan (y) ? x : (x >= y ? x : y);
}

inline double
min (double x, double y, const bool nanflag)
{
  double out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (x <= y ? x : y);
  return out;
}

inline double
max (double x, double y, const bool nanflag)
{
  double out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (x >= y ? x : y);
  return out;
}

inline double
min (double x, double y, const bool nanflag, const bool realabs)
{
  double out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (x <= y ? x : y);
  else
    out = isnan (y) ? x :
          (std::abs (x) < std::abs (y) ? x : (std::abs (x) == std::abs (y) && x <= y ? x : y));
  return out;
}

inline double
max (double x, double y, const bool nanflag, const bool realabs)
{
  double out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (x >= y ? x : y);
  else
    out = isnan (y) ? x :
          (std::abs (x) > std::abs (y) ? x : (std::abs (x) == std::abs (y) && x >= y ? x : y));
  return out;
}

inline float
min (float x, float y)
{
  return isnan (y) ? x : (x <= y ? x : y);
}

inline float
max (float x, float y)
{
  return isnan (y) ? x : (x >= y ? x : y);
}

inline float
min (float x, float y, const bool nanflag)
{
  float out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (x <= y ? x : y);
  return out;
}

inline float
max (float x, float y, const bool nanflag)
{
  float out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (x >= y ? x : y);
  return out;
}

inline float
min (float x, float y, const bool nanflag, const bool realabs)
{
  float out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (x <= y ? x : y);
  else
    out = isnan (y) ? x :
          (std::abs (x) < std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && x <= y ? x : y));
  return out;
}

inline float
max (float x, float y, const bool nanflag, const bool realabs)
{
  float out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (x >= y ? x : y);
  else
    out = isnan (y) ? x :
          (std::abs (x) > std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && x >= y ? x : y));
  return out;
}

inline std::complex<double>
min (const std::complex<double>& x, const std::complex<double>& y)
{
  return isnan (y) ? x : (std::abs (x) < std::abs (y) ? x :
         (std::abs (x) == std::abs (y) && std::arg (x) <= std::arg (y) ? x : y));
}

inline std::complex<double>
max (const std::complex<double>& x, const std::complex<double>& y)
{
  return isnan (y) ? x : (std::abs (x) > std::abs (y) ? x :
         (std::abs (x) == std::abs (y) && std::arg (x) >= std::arg (y) ? x : y));
}

inline std::complex<double>
min (const std::complex<double>& x, const std::complex<double>& y,
     const bool nanflag)
{
  std::complex<double> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (std::abs (x) < std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) <= std::arg (y) ? x : y));
  return out;
}

inline std::complex<double>
max (const std::complex<double>& x, const std::complex<double>& y,
     const bool nanflag)
{
  std::complex<double> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (std::abs (x) > std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) >= std::arg (y) ? x : y));
  return out;
}

inline std::complex<double>
min (const std::complex<double>& x, const std::complex<double>& y,
     const bool nanflag, const bool realabs)
{
  std::complex<double> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (std::real (x) < std::real (y) ? x :
          (std::real (x) == std::real (y) &&
           std::imag (x) <= std::imag (y) ? x : y));
  else
    out = isnan (y) ? x : (std::abs (x) < std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) <= std::arg (y) ? x : y));
  return out;
}

inline std::complex<double>
max (const std::complex<double>& x, const std::complex<double>& y,
     const bool nanflag, const bool realabs)
{
  std::complex<double> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (std::real (x) > std::real (y) ? x :
          (std::real (x) == std::real (y) &&
           std::imag (x) >= std::imag (y) ? x : y));
  else
    out = isnan (y) ? x : (std::abs (x) > std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) >= std::arg (y) ? x : y));
  return out;
}

inline std::complex<float>
min (const std::complex<float>& x, const std::complex<float>& y)
{
  return isnan (y) ? x : (abs (x) < abs (y) ? x :
         (std::abs (x) == std::abs (y) && std::arg (x) <= std::arg (y) ? x : y));
}

inline std::complex<float>
max (const std::complex<float>& x, const std::complex<float>& y)
{
  return isnan (y) ? x : (abs (x) > abs (y) ? x :
         (std::abs (x) == std::abs (y) && std::arg (x) >= std::arg (y) ? x : y));
}

inline std::complex<float>
min (const std::complex<float>& x, const std::complex<float>& y,
     const bool nanflag)
{
  std::complex<float> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (std::abs (x) < std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) <= std::arg (y) ? x : y));
  return out;
}

inline std::complex<float>
max (const std::complex<float>& x, const std::complex<float>& y,
     const bool nanflag)
{
  std::complex<float> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else
    out = isnan (y) ? x : (std::abs (x) > std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) >= std::arg (y) ? x : y));
  return out;
}

inline std::complex<float>
min (const std::complex<float>& x, const std::complex<float>& y,
     const bool nanflag, const bool realabs)
{
  std::complex<float> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (std::real (x) < std::real (y) ? x :
          (std::real (x) == std::real (y) &&
           std::imag (x) <= std::imag (y) ? x : y));
  else
    out = isnan (y) ? x : (std::abs (x) < std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) <= std::arg (y) ? x : y));
  return out;
}

inline std::complex<float>
max (const std::complex<float>& x, const std::complex<float>& y,
     const bool nanflag, const bool realabs)
{
  std::complex<float> out;
  if (! nanflag && (isnan (x) || isnan (y)))
    out = NAN;
  else if (realabs)
    out = isnan (y) ? x : (std::real (x) > std::real (y) ? x :
          (std::real (x) == std::real (y) &&
           std::imag (x) >= std::imag (y) ? x : y));
  else
    out = isnan (y) ? x : (std::abs (x) > std::abs (y) ? x :
          (std::abs (x) == std::abs (y) && std::arg (x) >= std::arg (y) ? x : y));
  return out;
}

template <typename T>
inline octave_int<T>
min (const octave_int<T>& x, const octave_int<T>& y)
{
  return x.value () <= y.value () ? x : y;
}

template <typename T>
inline octave_int<T>
max (const octave_int<T>& x, const octave_int<T>& y)
{
  return x.value () >= y.value () ? x : y;
}

template <typename T>
inline octave_int<T>
min (const octave_int<T>& x, const octave_int<T>& y,
     [[maybe_unused]] const bool nanflag)
{
  return x.value () <= y.value () ? x : y;
}

template <typename T>
inline octave_int<T>
max (const octave_int<T>& x, const octave_int<T>& y,
     [[maybe_unused]] const bool nanflag)
{
  return x.value () >= y.value () ? x : y;
}

template <typename T>
inline octave_int<T>
min (const octave_int<T>& x, const octave_int<T>& y,
     [[maybe_unused]] const bool nanflag, const bool realabs)
{
  octave_int<T> out;
  if (realabs)
    out = x.value () <= y.value () ? x : y;
  else
    out = mappers_abs (x) < mappers_abs (y) ? x :
          (mappers_abs (x) == mappers_abs (y) &&
           x.value () <= y.value () ? x : y);
  return out;
}

template <typename T>
inline octave_int<T>
max (const octave_int<T>& x, const octave_int<T>& y,
     [[maybe_unused]] const bool nanflag, const bool realabs)
{
  octave_int<T> out;
  if (realabs)
    out = x.value () >= y.value () ? x : y;
  else
    out = mappers_abs (x) > mappers_abs (y) ? x :
          (mappers_abs (x) == mappers_abs (y) &&
           x.value () >= y.value () ? x : y);
  return out;
}

// These map reals to Complex.

extern OCTAVE_API Complex rc_acos (double);
extern OCTAVE_API FloatComplex rc_acos (float);

extern OCTAVE_API Complex rc_acosh (double);
extern OCTAVE_API FloatComplex rc_acosh (float);

extern OCTAVE_API Complex rc_asin (double);
extern OCTAVE_API FloatComplex rc_asin (float);

extern OCTAVE_API Complex rc_atanh (double);
extern OCTAVE_API FloatComplex rc_atanh (float);

extern OCTAVE_API Complex rc_log (double);
extern OCTAVE_API FloatComplex rc_log (float);

extern OCTAVE_API Complex rc_log2 (double);
extern OCTAVE_API FloatComplex rc_log2 (float);

extern OCTAVE_API Complex rc_log10 (double);
extern OCTAVE_API FloatComplex rc_log10 (float);

extern OCTAVE_API Complex rc_sqrt (double);
extern OCTAVE_API FloatComplex rc_sqrt (float);

OCTAVE_END_NAMESPACE(math)
OCTAVE_END_NAMESPACE(octave)

#endif
