/*
 * math_util.h
 *
 *  Created on: 14/02/2019
 *      Author: alexrayne <alexraynepe196@gmail.com>
  ------------------------------------------------------------------------
    Copyright (c) alexrayne

   All rights reserved.
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions are met:
   - Redistributions of source code must retain the above copyright
     notice, this list of conditions and the following disclaimer.
   - Redistributions in binary form must reproduce the above copyright
     notice, this list of conditions and the following disclaimer in the
     documentation and/or other materials provided with the distribution.
   - Neither the name of ARM nor the names of its contributors may be used
     to endorse or promote products derived from this software without
     specific prior written permission.
   *
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS AND CONTRIBUTORS BE
   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   POSSIBILITY OF SUCH DAMAGE. *
 * --------------------------------------------------------------------------
 * здесь положу полезные мат функции
 */

#ifndef LIB_MATH_UTIL_H_
#define LIB_MATH_UTIL_H_

#include <stdbool.h>
#include <stdint.h>
#include <math.h>
#include <memory.h>

#include <c_compat.h>



INLINE
int ensure_range(int x, const int l, const int h){
    if (x < l)
        x = l;
    if (x > h)
        x = h;
    return x;
}

INLINE
bool in_range(const int x, const int l, const int h){
    if (x < l)
        return false;
    if (x > h)
        return false;
    return true;
}

bool in_rangef(const float x, const float l, const float h);


/// @brief compares a == b with tolerance
/// @return true if  a +-tol == b+-tol
bool is_samef(float a, float b, float tol);




unsigned        sqrt32( uint32_t x);
uint32_t        sqrt64( uint64_t x);

unsigned        sqrt32_ceil( uint32_t x);
uint32_t        sqrt64_ceil( uint64_t x);
unsigned        sqrt32_floor( uint32_t x);
uint32_t        sqrt64_floor( uint64_t x);



/// @brief отбрасывает dot младших разрядов с округлением до ближайшего
/// @brief shifts right dot LSBits , rounding to nearest
static inline
uint32_t        qround64(uint64_t x, unsigned dot){
    unsigned half = (x >> (dot-1) ) & 1;
    return (x >> dot) + half;
}

static inline
uint32_t        qround32(uint32_t x, unsigned dot){
    unsigned half = (x >> (dot-1) ) & 1;
    return (x >> dot) + half;
}


/// @brief provides rounded  (x*m)>>32 - mutiplication on m, where m < 1.0 = N/2^32
#define MULL_M(m)  ((1llu<<32) * m)
long mul_ml( unsigned long m, long x);
/// @brief provides rounded  (x*m)>>16 - mutiplication on m, where m < 1.0 = N/2^16
#define MULS_M(m)  ((1lu<<16) * m)
long mul_ms( unsigned short m, short x);



/// @brief provides rounded  (x*m)>>32 - division via mutiplication on m = 2^32/C
#define DIVL_M(m)  ((1llu<<32) / m)
#define div_ml(m, x)    mul_ml(m, x)



/// @brief provides rounded  (x*m)>>16 - division via mutiplication on m = 2^32/C
#define DIVS_M(m)  ((unsigned short)((1lu<<16) / m))
#define div_ms(m, x)    mul_ms(m, x)



//*****************************************************************
#ifndef MAX

#define MAX(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a > _b ? _a : _b;       \
})

#endif

#ifndef MIN
#define MIN(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a < _b ? _a : _b;       \
})
#endif




//*****************************************************************

static inline
unsigned ensure_rangeu(unsigned x, const unsigned min, const unsigned max){
    if (x < min)
        x = min;
    if (x >= max)
        x = max;
    return x;
}

static inline
int ensure_rangei(int x, const int min, const int max){
    if (x < min)
        x = min;
    if (x >= max)
        x = max;
    return x;
}


//*****************************************************************
/// @return x aligned down on page
uintptr_t   align_dn(uintptr_t x, unsigned page);

/// @return x aligned up on page
uintptr_t   align_up(uintptr_t x, unsigned page);

/// @return x aligned to top of page = page(+1)-1
uintptr_t   align_top(uintptr_t x, unsigned page);

/// @return amount of pages over range x
unsigned   align_full_pages(unsigned x, unsigned page);


#define IS_ALIGNED(x, page) ( ( (x) & ((page)-1)  ) == 0)
#define ALIGN_UP(x, page)   (((x) + (page)-1 ) & ~((page)-1))


#ifdef __cplusplus

template<typename T>
INLINE
bool is_same(const T a, const T b){
    return a == b;
}

template<typename T>
INLINE
bool is_same(const T a, const T b, const T tol){
    return std::abs(a - b) < tol;
}

INLINE
bool is_same(const double& a, const double& b){
    return memcmp( &a, &b, sizeof(double) ) == 0;
}

template<typename T1, typename T2>
INLINE
T1 ensure_range(T1 x, const T2 min, const T2 max){
    if (x < min)
        x = min;
    if (x >= max)
        x = max;
    return x;
}

template<typename T1, typename T2>
INLINE
bool in_range(const T1 x, const T2 l, const T2 h){
    if (x < l)
        return false;
    if (x > h)
        return false;
    return true;
}

#endif //__cplusplus


#endif /* LIB_MATH_UTIL_H_ */
