// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2011-2024 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// ----------------------------------------------------------------------------

/*
 * This module implements a variety of mathematical data types and library
 * functions used by the codec.
 */

#ifndef ASTC_MATHLIB_H_INCLUDED
#define ASTC_MATHLIB_H_INCLUDED

#include <cassert>
#include <cstdint>
#include <cmath>

#ifndef ASTCENC_POPCNT
  #if defined(__POPCNT__)
    #define ASTCENC_POPCNT 1
  #else
    #define ASTCENC_POPCNT 0
  #endif
#endif

#ifndef ASTCENC_F16C
  #if defined(__F16C__)
    #define ASTCENC_F16C 1
  #else
    #define ASTCENC_F16C 0
  #endif
#endif

#ifndef ASTCENC_SSE
  #if defined(__SSE4_2__)
    #define ASTCENC_SSE 42
  #elif defined(__SSE4_1__)
    #define ASTCENC_SSE 41
  #elif defined(__SSE2__)
    #define ASTCENC_SSE 20
  #else
    #define ASTCENC_SSE 0
  #endif
#endif

#ifndef ASTCENC_AVX
  #if defined(__AVX2__)
    #define ASTCENC_AVX 2
  #elif defined(__AVX__)
    #define ASTCENC_AVX 1
  #else
    #define ASTCENC_AVX 0
  #endif
#endif

#ifndef ASTCENC_NEON
  #if defined(__aarch64__)
    #define ASTCENC_NEON 1
  #else
    #define ASTCENC_NEON 0
  #endif
#endif

// Force vector-sized SIMD alignment
#if ASTCENC_AVX
  #define ASTCENC_VECALIGN 32
#elif ASTCENC_SSE || ASTCENC_NEON
  #define ASTCENC_VECALIGN 16
// Use default alignment for non-SIMD builds
#else
  #define ASTCENC_VECALIGN 0
#endif

// C++11 states that alignas(0) should be ignored but GCC doesn't do
// this on some versions, so workaround and avoid emitting alignas(0)
#if ASTCENC_VECALIGN > 0
	#define ASTCENC_ALIGNAS alignas(ASTCENC_VECALIGN)
#else
	#define ASTCENC_ALIGNAS
#endif

#if ASTCENC_SSE != 0 || ASTCENC_AVX != 0 || ASTCENC_POPCNT != 0
	#include <immintrin.h>
#endif

/* ============================================================================
  Fast math library; note that many of the higher-order functions in this set
  use approximations which are less accurate, but faster, than <cmath> standard
  library equivalents.

  Note: Many of these are not necessarily faster than simple C versions when
  used on a single scalar value, but are included for testing purposes as most
  have an option based on SSE intrinsics and therefore provide an obvious route
  to future vectorization.
============================================================================ */

// Union for manipulation of float bit patterns
typedef union
{
	uint32_t u;
	int32_t s;
	float f;
} if32;

// These are namespaced to avoid colliding with C standard library functions.
namespace astc
{

static const float PI          = 3.14159265358979323846f;
static const float PI_OVER_TWO = 1.57079632679489661923f;

/**
 * @brief SP float absolute value.
 *
 * @param v   The value to make absolute.
 *
 * @return The absolute value.
 */
static inline float fabs(float v)
{
	return std::fabs(v);
}

/**
 * @brief Test if a float value is a nan.
 *
 * @param v    The value test.
 *
 * @return Zero is not a NaN, non-zero otherwise.
 */
static inline bool isnan(float v)
{
	return v != v;
}

/**
 * @brief Return the minimum of two values.
 *
 * For floats, NaNs are turned into @c q.
 *
 * @param p   The first value to compare.
 * @param q   The second value to compare.
 *
 * @return The smallest value.
 */
template<typename T>
static inline T min(T p, T q)
{
	return p < q ? p : q;
}

/**
 * @brief Return the minimum of three values.
 *
 * For floats, NaNs are turned into @c r.
 *
 * @param p   The first value to compare.
 * @param q   The second value to compare.
 * @param r   The third value to compare.
 *
 * @return The smallest value.
 */
template<typename T>
static inline T min(T p, T q, T r)
{
	return min(min(p, q), r);
}

/**
 * @brief Return the minimum of four values.
 *
 * For floats, NaNs are turned into @c s.
 *
 * @param p   The first value to compare.
 * @param q   The second value to compare.
 * @param r   The third value to compare.
 * @param s   The fourth value to compare.
 *
 * @return The smallest value.
 */
template<typename T>
static inline T min(T p, T q, T r, T s)
{
	return min(min(p, q), min(r, s));
}

/**
 * @brief Return the maximum of two values.
 *
 * For floats, NaNs are turned into @c q.
 *
 * @param p   The first value to compare.
 * @param q   The second value to compare.
 *
 * @return The largest value.
 */
template<typename T>
static inline T max(T p, T q)
{
	return p > q ? p : q;
}

/**
 * @brief Return the maximum of three values.
 *
 * For floats, NaNs are turned into @c r.
 *
 * @param p   The first value to compare.
 * @param q   The second value to compare.
 * @param r   The third value to compare.
 *
 * @return The largest value.
 */
template<typename T>
static inline T max(T p, T q, T r)
{
	return max(max(p, q), r);
}

/**
 * @brief Return the maximum of four values.
 *
 * For floats, NaNs are turned into @c s.
 *
 * @param p   The first value to compare.
 * @param q   The second value to compare.
 * @param r   The third value to compare.
 * @param s   The fourth value to compare.
 *
 * @return The largest value.
 */
template<typename T>
static inline T max(T p, T q, T r, T s)
{
	return max(max(p, q), max(r, s));
}

/**
 * @brief Clamp a value value between @c mn and @c mx.
 *
 * For floats, NaNs are turned into @c mn.
 *
 * @param v      The value to clamp.
 * @param mn     The min value (inclusive).
 * @param mx     The max value (inclusive).
 *
 * @return The clamped value.
 */
template<typename T>
inline T clamp(T v, T mn, T mx)
{
	// Do not reorder; correct NaN handling relies on the fact that comparison
	// with NaN returns false and will fall-though to the "min" value.
	if (v > mx) return mx;
	if (v > mn) return v;
	return mn;
}

/**
 * @brief Clamp a float value between 0.0f and 1.0f.
 *
 * NaNs are turned into 0.0f.
 *
 * @param v   The value to clamp.
 *
 * @return The clamped value.
 */
static inline float clamp1f(float v)
{
	return astc::clamp(v, 0.0f, 1.0f);
}

/**
 * @brief Clamp a float value between 0.0f and 255.0f.
 *
 * NaNs are turned into 0.0f.
 *
 * @param v  The value to clamp.
 *
 * @return The clamped value.
 */
static inline float clamp255f(float v)
{
	return astc::clamp(v, 0.0f, 255.0f);
}

/**
 * @brief SP float round-down.
 *
 * @param v   The value to round.
 *
 * @return The rounded value.
 */
static inline float flt_rd(float v)
{
	return std::floor(v);
}

/**
 * @brief SP float round-to-nearest and convert to integer.
 *
 * @param v   The value to round.
 *
 * @return The rounded value.
 */
static inline int flt2int_rtn(float v)
{

	return static_cast<int>(v + 0.5f);
}

/**
 * @brief SP float round down and convert to integer.
 *
 * @param v   The value to round.
 *
 * @return The rounded value.
 */
static inline int flt2int_rd(float v)
{
	return static_cast<int>(v);
}

/**
 * @brief SP float bit-interpreted as an integer.
 *
 * @param v   The value to bitcast.
 *
 * @return The converted value.
 */
static inline int float_as_int(float v)
{
	union { int a; float b; } u;
	u.b = v;
	return u.a;
}

/**
 * @brief Integer bit-interpreted as an SP float.
 *
 * @param v   The value to bitcast.
 *
 * @return The converted value.
 */
static inline float int_as_float(int v)
{
	union { int a; float b; } u;
	u.a = v;
	return u.b;
}

/**
 * @brief Fast approximation of 1.0 / sqrt(val).
 *
 * @param v   The input value.
 *
 * @return The approximated result.
 */
static inline float rsqrt(float v)
{
	return 1.0f / std::sqrt(v);
}

/**
 * @brief Fast approximation of sqrt(val).
 *
 * @param v   The input value.
 *
 * @return The approximated result.
 */
static inline float sqrt(float v)
{
	return std::sqrt(v);
}

/**
 * @brief Extract mantissa and exponent of a float value.
 *
 * @param      v      The input value.
 * @param[out] expo   The output exponent.
 *
 * @return The mantissa.
 */
static inline float frexp(float v, int* expo)
{
	if32 p;
	p.f = v;
	*expo = ((p.u >> 23) & 0xFF) - 126;
	p.u = (p.u & 0x807fffff) | 0x3f000000;
	return p.f;
}

/**
 * @brief Initialize the seed structure for a random number generator.
 *
 * Important note: For the purposes of ASTC we want sets of random numbers to
 * use the codec, but we want the same seed value across instances and threads
 * to ensure that image output is stable across compressor runs and across
 * platforms. Every PRNG created by this call will therefore return the same
 * sequence of values ...
 *
 * @param state The state structure to initialize.
 */
void rand_init(uint64_t state[2]);

/**
 * @brief Return the next random number from the generator.
 *
 * This RNG is an implementation of the "xoroshoro-128+ 1.0" PRNG, based on the
 * public-domain implementation given by David Blackman & Sebastiano Vigna at
 * http://vigna.di.unimi.it/xorshift/xoroshiro128plus.c
 *
 * @param state The state structure to use/update.
 */
uint64_t rand(uint64_t state[2]);

}

/* ============================================================================
  Softfloat library with fp32 and fp16 conversion functionality.
============================================================================ */
#if (ASTCENC_F16C == 0) && (ASTCENC_NEON == 0)
	/* narrowing float->float conversions */
	uint16_t float_to_sf16(float val);
	float sf16_to_float(uint16_t val);
#endif

/*********************************
  Vector library
*********************************/
#include "astcenc_vecmathlib.h"

/*********************************
  Declaration of line types
*********************************/
// parametric line, 2D: The line is given by line = a + b * t.

struct line2
{
	vfloat4 a;
	vfloat4 b;
};

// parametric line, 3D
struct line3
{
	vfloat4 a;
	vfloat4 b;
};

struct line4
{
	vfloat4 a;
	vfloat4 b;
};


struct processed_line2
{
	vfloat4 amod;
	vfloat4 bs;
};

struct processed_line3
{
	vfloat4 amod;
	vfloat4 bs;
};

struct processed_line4
{
	vfloat4 amod;
	vfloat4 bs;
};

#endif
