///////////////////////////////////////////////////////////////////////////////
//
//                          IMPORTANT NOTICE
//
// The following open source license statement does not apply to any
// entity in the Exception List published by FMSoft.
//
// For more information, please visit:
//
// https://www.fmsoft.cn/exception-list
//
//////////////////////////////////////////////////////////////////////////////
/**
 * \file fixedmath.h
 * \author Wei Yongming <vincent@minigui.org>
 * \date 2002/01/12
 *
 * \brief This file includes fixed point and three-dimension math routines.
 *
 \verbatim

    This file is part of MiniGUI, a mature cross-platform windowing
    and Graphics User Interface (GUI) support system for embedded systems
    and smart IoT devices.

    Copyright (C) 2002~2018, Beijing FMSoft Technologies Co., Ltd.
    Copyright (C) 1998~2002, WEI Yongming

    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 3 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, see <http://www.gnu.org/licenses/>.

    Or,

    As this program is a library, any link to this program must follow
    GNU General Public License version 3 (GPLv3). If you cannot accept
    GPLv3, you need to be licensed from FMSoft.

    If you have got a commercial license of this program, please use it
    under the terms and conditions of the commercial license.

    For more information about the commercial license, please refer to
    <http://www.minigui.com/en/about/licensing-policy/>.

 \endverbatim
 */

/*
 * $Id: fixedmath.h 12154 2009-10-15 07:21:34Z weiym $
 *
 *      MiniGUI for Linux/uClinux, eCos, uC/OS-II, VxWorks,
 *      pSOS, ThreadX, NuCleus, OSE, and Win32.
 *
 *      Fixed-point math routins come from Allegro (a gift software)
 *      by Shawn Hargreaves and others.
 */

#ifndef _MGUI_MGHAVE_FIXED_MATH_H
#define _MGUI_MGHAVE_FIXED_MATH_H


#include <errno.h>
#include <math.h>
#include <stdlib.h>

/* Set up for C function definitions, even when using C++ */
#ifdef __cplusplus
extern "C" {
#endif

#ifdef _MGHAVE_FIXED_MATH

    /**
     * \addtogroup fns Functions
     * @{
     */

    /**
     * \addtogroup global_fns Global/general functions
     * @{
     */

    /**
     * \defgroup fixed_math_fns Fixed point math functions
     *
     * You know that the float point mathematics routines are very
     * expensive. If you do not want precision mathematics result,
     * you can use fixed point. MiniGUI uses a double word (32-bit)
     * integer to represent a fixed point ranged from -32767.0 to
     * 32767.0, and defines some fixed point mathematics routines for
     * your application. Some GDI functions need fixed point
     * math routines, like \a Arc.
     *
     * Example 1:
     *
     * \include fixed_point.c
     *
     * Example 2:
     *
     * \include fixedpoint.c
     * @{
     */

/**
 * \fn fixed fixsqrt (fixed x)
 * \brief Returns the non-negative square root of a fixed point value.
 *
 * This function returns the non-negative square root of \a x.
 * It fails and sets errno to EDOM, if \a x is negative.
 *
 * \sa fixhypot
 */
MG_EXPORT fixed fixsqrt (fixed x);

/**
 * \fn fixed fixhypot (fixed x, fixed y)
 * \brief Returns the Euclidean distance from the origin.
 *
 * The function returns the \a sqrt(x*x+y*y). This is the length of
 * the hypotenuse of a right-angle triangle with sides of length \a x and \a y,
 * or the distance of the point \a (x,y) from the origin.
 *
 * \sa fixsqrt
 */
MG_EXPORT fixed fixhypot (fixed x, fixed y);

/**
 * \fn fixed fixatan (fixed x)
 * \brief Calculates the arc tangent of a fixed point value.
 *
 * This function calculates the arc tangent of \a x; that is the value
 * whose tangent is \a x.
 *
 * \return Returns the arc tangent in radians and the value is
 *         mathematically defined to be between -PI/2 and PI/2 (inclusive).
 *
 * \sa fixatan2
 */
MG_EXPORT fixed fixatan (fixed x);

/**
 * \fn fixed fixatan2 (fixed y, fixed x)
 * \brief Calculates the arc tangent of two fixed point variables.
 *
 *  This function calculates the arc tangent of the two variables \a x and \a y.
 *  It is similar to calculating the arc tangent of \a y / \a x, except that
 *  the signs of both arguments are used to determine the quadrant of the
 *  result.
 *
 * \return Returns the result in radians, which is between -PI and PI
 *         (inclusive).
 *
 * \sa fixatan
 */
MG_EXPORT fixed fixatan2 (fixed y, fixed x);

extern MG_EXPORT const fixed __mg_cos_tbl[];
extern MG_EXPORT const fixed __mg_tan_tbl[];
extern MG_EXPORT const fixed __mg_acos_tbl[];

/************************** inline fixed point math functions *****************/
/* ftofix and fixtof are used in generic C versions of fixmul and fixdiv */

/**
 * \fn fixed ftofix (double x)
 * \brief Converts a float point value to a fixed point value.
 *
 * This function converts the specified float point value \a x to
 * a fixed point value.
 *
 * \note The float point should be ranged from -32767.0 to 32767.0.
 * If it runs out of the range, this function sets \a errno to \a ERANGE.
 *
 * \sa fixtof
 */
static inline fixed ftofix (double x)
{
   if (x > 32767.0) {
      errno = ERANGE;
      return 0x7FFFFFFF;
   }

   if (x < -32767.0) {
      errno = ERANGE;
      return -0x7FFFFFFF;
   }

   return (long)(x * 65536.0 + (x < 0 ? -0.5 : 0.5));
}

/**
 * \fn double fixtof (fixed x)
 * \brief Converts a fixed point value to a float point value.
 *
 * This function converts the specified fixed point value \a x to
 * a float point value.
 *
 * \sa ftofix
 */
static inline double fixtof (fixed x)
{
   return (double)x / 65536.0;
}


/**
 * \fn fixed fixadd (fixed x, fixed y)
 * \brief Returns the sum of two fixed point values.
 *
 * This function adds two fixed point values \a x and \a y, and
 * returns the sum.
 *
 * \param x x,y: Two addends.
 * \param y x,y: Two addends.
 * \return The sum. If the result runs out of range of fixed point,
 *         this function sets \a errno to \a ERANGE.
 *
 * \sa fixsub
 */
static inline fixed fixadd (fixed x, fixed y)
{
   fixed result = x + y;

   if (result >= 0) {
      if ((x < 0) && (y < 0)) {
         errno = ERANGE;
         return -0x7FFFFFFF;
      }
      else
         return result;
   }
   else {
      if ((x > 0) && (y > 0)) {
         errno = ERANGE;
         return 0x7FFFFFFF;
      }
      else
         return result;
   }
}

/**
 * \fn fixed fixsub (fixed x, fixed y)
 * \brief Subtract a fixed point value from another.
 *
 * This function subtracts the fixed point values \a y from the fixed
 * point value \a x, and returns the difference.
 *
 * \param x The minuend.
 * \param y The subtrahend.
 *
 * \return The difference. If the result runs out of range of fixed point,
 *         this function sets \a errno to \a ERANGE.
 *
 * \sa fixadd
 */
static inline fixed fixsub (fixed x, fixed y)
{
   fixed result = x - y;

   if (result >= 0) {
      if ((x < 0) && (y > 0)) {
         errno = ERANGE;
         return -0x7FFFFFFF;
      }
      else
         return result;
   }
   else {
      if ((x > 0) && (y < 0)) {
         errno = ERANGE;
         return 0x7FFFFFFF;
      }
      else
         return result;
   }
}

/**
 * \fn fixed fixmul (fixed x, fixed y)
 * \brief Returns the product of two fixed point values.
 *
 * This function returns the product of two fixed point values \a x and \a y.
 *
 * \param x The faciend.
 * \param y The multiplicato.
 * \return The prodcut. If the result runs out of range of fixed point,
 *         this function sets \a errno to \a ERANGE.
 *
 * \sa fixdiv
 */
MG_EXPORT fixed fixmul (fixed x, fixed y);

/**
 * \fn fixed fixdiv (fixed x, fixed y)
 * \brief Returns the quotient of two fixed point values.
 *
 * This function returns the quotient of two fixed point values \a x and \a y.
 *
 * \param x The dividend.
 * \param y The divisor.
 * \return The quotient. If the result runs out of range of fixed point,
 *         this function sets \a errno to \a ERANGE.
 *
 * \sa fixmul
 */
MG_EXPORT fixed fixdiv (fixed x, fixed y);

/**
 * \fn int fixceil (fixed x)
 * \brief Rounds a fixed point value to the nearest integer.
 *
 * This function rounds the fixed point value \a x to the nearest integer
 * and returns it.
 *
 * \return The rounded integer value.
 */

static inline int fixceil (fixed x)
{
   if (x > 0x7FFF0000) {
      errno = ERANGE;
      return 0x7FFF;
   }

   x += 0xFFFF;
   if (x > 0)
       return (x >> 16);
   else
       return ~((~x) >> 16);
}

/**
 * \fn fixed itofix (int x)
 * \brief Converts an integer to a fixed point value.
 *
 * This function converts the integer \a x to a fixed point value.
 *
 * \sa fixtoi
 */
static inline fixed itofix (int x)
{
   return x << 16;
}

/**
 * \fn int fixtoi (fixed x)
 * \brief Converts an fixed point value to an integer.
 *
 * This function converts the fixed point \a x to an integer.
 *
 * \sa itofix
 */
static inline int fixtoi (fixed x)
{
    return (x >> 16) + ((x & 0x8000) >> 15);
}

/**
 * \fn fixed fixcos (fixed x)
 * \brief Returns the cosine of a fixed point.
 *
 * This function returns the cosine of the fixed point \a x,
 * where \a x is given in radians.
 *
 * \sa fixacos
 */
static inline fixed fixcos (fixed x)
{
    return __mg_cos_tbl[((x + 0x4000) >> 15) & 0x1FF];
}

/**
 * \fn fixed fixsin (fixed x)
 * \brief Returns the sine of a fixed point.
 *
 * This function returns the sine of the fixed point \a x,
 * where \a x is given in radians.
 *
 * \sa fixasin
 */
static inline fixed fixsin (fixed x)
{
    return __mg_cos_tbl[((x - 0x400000 + 0x4000) >> 15) & 0x1FF];
}

/**
 * \fn fixed fixtan (fixed x)
 * \brief Returns the tangent of a fixed point.
 *
 * This function returns the tangent of the fixed point \a x,
 * where \a x is given in radians.
 *
 * \sa fixcos, fixsin
 */
static inline fixed fixtan (fixed x)
{
    return __mg_tan_tbl[((x + 0x4000) >> 15) & 0xFF];
}

/**
 * \fn fixed fixacos (fixed x)
 * \brief Calculates and returns the arc cosine of a fixed point.
 *
 * This function calculates the arc cosine of the fixed point \a x;
 * that is the value whose cosine is \a x. If \a x falls outside
 * the range -1 to 1, this function fails and \a errno is set to EDOM.
 *
 * \return Returns the arc cosine in radians and the value is mathematically
 *         defined to be between 0 and PI (inclusive).
 *
 * \sa fixcos
 */
static inline fixed fixacos (fixed x)
{
   if ((x < -65536) || (x > 65536)) {
      errno = EDOM;
      return 0;
   }

   return __mg_acos_tbl[(x+65536+127)>>8];
}

/**
 * \fn fixed fixasin (fixed x)
 * \brief Calculates and returns the arc sine of a fixed point.
 *
 * This function calculates the arc sine of the fixed point \a x;
 * that is the value whose sine is \a x. If \a x falls outside
 * the range -1 to 1, this function fails and \a errno is set to EDOM.
 *
 * \return Returns the arc sine in radians and the value is mathematically
 *         defined to be between -PI/2 and PI/2 (inclusive).
 *
 * \sa fixsin
 */
static inline fixed fixasin (fixed x)
{
   if ((x < -65536) || (x > 65536)) {
      errno = EDOM;
      return 0;
   }

   return 0x00400000 - __mg_acos_tbl[(x+65536+127)>>8];
}

    /** @} end of fixed_math_fns */


    /** @} end of global_fns */

    /** @} end of fns */

#endif /* _MGHAVE_FIXED_MATH */

/* Ends C function definitions when using C++ */
#ifdef __cplusplus
}
#endif

#endif /* _MGUI_MGHAVE_FIXED_MATH_H */

