/* Fixed point math code V1.67 17/2/08
   Uses the ARM code routines in fp_math.s, and the C code in fixmath.c
   Copyright 2008 Jeffrey Lee
   This file is part of WOUM.
   WOUM 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.
   WOUM 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 WOUM.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef _FIXMATH_H
#define _FIXMATH_H

#include <float.h>

typedef int f1616;
typedef int f248;
typedef int f2012;
typedef long long f4816;

#ifdef __cplusplus
extern "C" {
#endif

/* 16.16 code */

/* Contains code to manipulate fixed point numbers in 16.16 format (i.e. 1 bit
   sign, 15 bit whole part, 16 bit decimal part)
   Addition, subtraction, comparison, etc. are not included here since they can
   be performed as normal */

/* Multiply one f1616 by another
   Will round off to +&7FFF.FFFF or -&8000.0000 if an overflow occurs */
extern const f1616 f1616_mul(f1616,f1616);

/* Divide one f1616 by another
   Will round off to +&7FFF.FFFF or -&8000.0000 if an overflow occurs (i.e.
   division by zero) */
extern const f1616 f1616_div(f1616,f1616);

/* Divide three f1616's by the same number, overwriting the originals
   This routine is faster than doing three divisions seperately
   Will round off to +&7FFF.FFFF or -&8000.0000 if an overflow occurs (i.e.
   division by zero) */
extern void f1616_tridiv(f1616 *,f1616 *,f1616 *,f1616);

/* Calculate the square root of an f1616
   Returns zero if a negative number is passed */
extern const f1616 f1616_sqrt(f1616);

/* Calculate the square of a number (i.e. x*x)
   Is a bit faster than f1616_mul(x,x)
   Will round off to +&7FFF.FFFF if an overflow occurs */
extern const f1616 f1616_sqr(f1616);

/* Return the sine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f1616 f1616_sin(f1616);

/* Return the cosine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f1616 f1616_cos(f1616);

/* Convert an integer to an f1616
   Does not round the output if an overflow occurs */
extern const f1616 f1616_FromInt(int i);

/* Convert a float to an f1616
   Will round the output to +&7FFF.FFFF or -&8000.0000 if an overflow occurs */ 
extern const f1616 f1616_FromFloat(float f);

/* Convert an f1616 to an int
   Will round the result down (i.e. 0.8 => 0, -0.8 => -1) */
extern const int f1616_ToInt(f1616 f);

/* Convert an f1616 to a float */
extern const float f1616_ToFloat(f1616 f);

/* Return arc tangent thingy
   Assumes that x=sin(a), y=cos(a) */
extern const f1616 f1616_atan2(f1616 x,f1616 y);


/* 24.8 code */

/* Contains code to manipulate fixed point numbers in 24.8 format (i.e. 1 bit
   sign, 23 bit whole part, 8 bit decimal part)
   Addition, subtraction, comparison, etc. are not included here since they can
   be performed as normal */

/* Multiply one f248 by another
   Will round off to +&7FFFFF.FF or -&800000.00 if an overflow occurs */
extern const f248 f248_mul(f248,f248);

/* Divide one f248 by another
   Will round off to +&7FFFFF.FF or -&800000.00 if an overflow occurs (i.e.
   division by zero) */
extern const f248 f248_div(f248,f248);

/* Divide three f248's by the same number, overwriting the originals
   This routine is faster than doing three divisions seperately
   Will round off to +&7FFFFF.FF or -&800000.00 if an overflow occurs (i.e.
   division by zero) */
extern void f248_tridiv(f248 *,f248 *,f248 *,f248);

/* Calculate the square root of an f248
   Returns zero if a negative number is passed */
extern const f248 f248_sqrt(f248);

/* Calculate the square of a number (i.e. x*x)
   Is a bit faster than f248_mul(x,x)
   Will round off to +&7FFFFF.FF if an overflow occurs */
extern const f248 f248_sqr(f248);

/* Return the sine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f248 f248_sin(f248);

/* Return the cosine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f248 f248_cos(f248);

/* Convert an integer to an f248
   Does not round the output if an overflow occurs */
extern const f248 f248_FromInt(int i);

/* Convert a float to an f248
   Will round the output to +&7FFFFF.FF or -&800000.00 if an overflow occurs */
extern const f248 f248_FromFloat(float f);

/* Convert an f248 to an int
   Will round the result down (i.e. 0.8 => 0, -0.8 => -1) */
extern const int f248_ToInt(f248 f);

/* Convert an f248 to a float */
extern const float f248_ToFloat(f248 f);


/* 20.12 code */

/* Contains code to manipulate fixed point numbers in 20.12 format (i.e. 1 bit
   sign, 19 bit whole part, 12 bit decimal part)
   Addition, subtraction, comparison, etc. are not included here since they can
   be performed as normal */

/* Multiply one f2012 by another
   Will round off to +&7FFFF.FFF or -&80000.000 if an overflow occurs */
extern const f2012 f2012_mul(f2012,f2012);

/* Divide one f2012 by another
   Will round off to +&7FFFF.FFF or -&80000.000 if an overflow occurs (i.e.
   division by zero) */
extern const f2012 f2012_div(f2012,f2012);

/* Divide three f2012's by the same number, overwriting the originals
   This routine is faster than doing three divisions seperately
   Will round off to +&7FFFF.FFF or -&80000.000 if an overflow occurs (i.e.
   division by zero) */
extern void f2012_tridiv(f2012 *,f2012 *,f2012 *,f2012);

/* Calculate the square root of an f2012
   Returns zero if a negative number is passed */
extern const f2012 f2012_sqrt(f2012);

/* Calculate the square of a number (i.e. x*x)
   Is a bit faster than f2012_mul(x,x)
   Will round off to +&7FFFF.FFF if an overflow occurs */
extern const f2012 f2012_sqr(f2012);

/* Return the sine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f2012 f2012_sin(f2012);

/* Return the cosine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f2012 f2012_cos(f2012);

/* Convert an integer to an f2012
   Does not round the output if an overflow occurs */
extern const f2012 f2012_FromInt(int i);

/* Convert a float to an f2012
   Will round the output to +&7FFFF.FFF or -&80000.000 if an overflow occurs */
extern const f2012 f2012_FromFloat(float f);

/* Convert an f2012 to an int
   Will round the result down (i.e. 0.8 => 0, -0.8 => -1) */
extern const int f2012_ToInt(f2012 f);

/* Convert an f2012 to a float */
extern const float f2012_ToFloat(f2012 f);


/* 48.16 code */

/* Contains code to manipulate fixed point numbers in 48.16 format (i.e. 1 bit
   sign, 47 bit whole part, 16 bit decimal part)
   Addition, subtraction, comparison, etc. are not included here since they can
   be performed as normal */

/* Multiply one f4816 by another
   Will round off to +&7FFFFFFFFFFF.FFFF or -&800000000000.0000 if an overflow
   occurs */
extern const f4816 f4816_mul(f4816,f4816);

/* Divide one f4816 by another
   Will round off to +&7FFFFFFFFFFF.FFFF or -&800000000000.0000 if an overflow
   occurs (i.e. division by zero) */
extern const f4816 f4816_div(f4816,f4816);

/* Divide three f4816's by the same number, overwriting the originals
   This routine is faster than doing three divisions seperately
   Will round off to +&7FFFFFFFFFFF.FFFF or -&800000000000.0000 if an overflow
   occurs (i.e. division by zero) */
extern void f4816_tridiv(f4816 *,f4816 *,f4816 *,f4816);

/* Calculate the square root of an f4816
   Returns zero if a negative number is passed */
extern const f4816 f4816_sqrt(f4816);

/* Calculate the square of a number (i.e. x*x)
   Is a bit faster than f4816_mul(x,x)
   Will round off to +&7FFFFFFFFFFF.FFFF if an overflow occurs */
extern const f4816 f4816_sqr(f4816);

/* Return the sine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f4816 f4816_sin(f4816);

/* Return the cosine of an angle (input in degrees)
   360 is the optimal input range; the angle is rounded down to that range by
   a simple subtraction loop and so will be very slow if input is very large */
extern const f4816 f4816_cos(f4816);

/* Convert an integer to an f4816 */
extern const f4816 f4816_FromInt(int i);

/* Convert a float to an f4816
   Will round the output to +&7FFFFFFFFFFF.FFFF or -&800000000000.0000 if an
   overflow occurs */
extern const f4816 f4816_FromFloat(float f);

/* Convert an f4816 to an int
   Will round the result down (i.e. 0.8 => 0, -0.8 => -1) */
extern const int f4816_ToInt(f4816 f);

/* Convert an f4816 to a float */
extern const float f4816_ToFloat(f4816 f);


/* General fixed point code */

/* Calculate the length of the vector (a,b,c)
   Uses 64bit math internally, so full ranges of values are possible
   Works with ints, f1616's, f248's, and f2012's */
extern const int gen_vlen(int a,int b,int c);
/* Macros to make the above easier to use */
#define int_vlen(a,b,c) gen_vlen(a,b,c)
#define f1616_vlen(a,b,c) gen_vlen(a,b,c)
#define f248_vlen(a,b,c) gen_vlen(a,b,c)
#define f2012_vlen(a,b,c) gen_vlen(a,b,c)
#define float_vlen(a,b,c) ((float) sqrt((a)*(a) + (b)*(b) + (c)*(c)))

/* Version for f4816, too gnarly to do in assembler :( */
#define f4816_vlen(a,b,c) f4816_FromFloat((float) sqrt(((float) (a))*((float) (a)) + ((float) (b))*((float) (b)) + ((float) (c))*((float) (c)))/65536)

/* moveto* code
   a and b should be pointers to verticies; dest should be the desired x/y/z
   coordinate
   The functions will fill vector c (which can be a, b, or something else) with
   the point on the line ab such that a.x (or .y, .z)=dest
   dest must lie between a and b
   This is here more as a place holder for the vector code, where the functions
   are defined properly
*/
extern void gen_movetox(const void *a,const void *b,int dest,void *c);
extern void gen_movetoy(const void *a,const void *b,int dest,void *c);
extern void gen_movetoz(const void *a,const void *b,int dest,void *c);
/* Long long (implemented in C) */
extern void genll_movetox(const void *a,const void *b,long long dest,void *c);
extern void genll_movetoy(const void *a,const void *b,long long dest,void *c);
extern void genll_movetoz(const void *a,const void *b,long long dest,void *c);

/* Perspective moveto* code
   Fills c with a vector such that c.x == c.z (or c.y == c.z)
   If negation flag is used it matches -c.x (or -c.y)
   The desired point must lie between the two vectors
*/
extern void gen_pmovetox(const void *a,const void *b,int neg,void *c);
extern void gen_pmovetoy(const void *a,const void *b,int neg,void *c);
/* Long long (implemented in C) */
extern void genll_pmovetox(const void *a,const void *b,int neg,void *c);
extern void genll_pmovetoy(const void *a,const void *b,int neg,void *c);

/* 2d moveto code
   For the line (x1,y1) to (x2,y2), this function returns the y coordinate of
   the point which has an x coordinate of targx
   targx mustn't lie outside x1 or x2
*/
extern const int gen_move2d(int x1,int y1,int x2,int y2,int targx);
extern const long long genll_move2d(long long x1,long long y1,long long x2,long long y2,long long targx);

extern int calc_izgrad(int a,int b,const long long *c,const long long *d); /* Return -(a*d)/(b*c) */
extern const int gen_inverse(int prec,int val); /* Return (2^prec)/val */

#ifdef __cplusplus
}
#endif

/* Floating point code */

/* Define some macros to make switching between floats and fixed point easy */
#define float_mul(a,b) ((a)*(b))
#define float_div(a,b) ((b)==0?((a)<0?FLT_MIN:FLT_MAX):(a)/(b))
#define float_tridiv(a,b,c,d) (*(a)=*(a)/((d)?:1),*(b)=*(b)/((d)?:1),*c=*(c)/((d)?:1))
#define float_sqrt(a) ((float) sqrt((double) (a)))
#define float_sqr(a) ((a)*(a))
#define float_sin(a) ((float) sin((double) (a)*0.0174532925))
#define float_cos(a) ((float) cos((double) (a)*0.0174532925))
#define float_FromInt(a) ((float) (a))
#define float_FromFloat(a) ((float) (a))
#define float_ToInt(a) ((int) (a))
#define float_ToFloat(a) ((float) (a))

/* Integer code */
#define int_mul(a,b) ((a)*(b))
#define int_div(a,b) ((a)/(b)) 
#define int_tridiv(a,b,c,d) (*(a)=*(a)/(d),*(b)=*(b)/(d),*c=*(c)/(d))
extern const int int_sqrt(int a);
#define int_sqr(a) ((a)*(a))
#define int_sin(a) ((int) sin((double) (a)*0.0174532925))
#define int_cos(a) ((int) cos((double) (a)*0.0174532925))
#define int_FromInt(a) ((int) (a))
#define int_FromFloat(a) ((int) (a))
#define int_ToInt(a) ((int) (a))
#define int_ToFloat(a) ((float) (a))

#endif
