#include "IQmathLib.h"


// Преобразование числа с плавающей точкой в число с фиксированной точкой
#define float_to_fixed(A)   (long)((A)*(1 << (GLOBAL_Q)) + (A > 0 ? 0.5: -0.5))
// Преобразование числа с плавающей точкой в число с фиксированной точкой с выбором числа бит, отдаваемых под дробную часть
#define float_to_fixed_base_select(A, F_BITS) (long)((A)*(1 << (F_BITS)) + (A > 0 ? 0.5: -0.5))
// Преобразование целого числа в число с фиксированной точкой
#define int_to_fixed(A) (long)((A) << (GLOBAL_Q))
// Преобразование целого числа в число с фиксированной точкой  с выбором числа бит, отдаваемых под дробную часть
#define int_to_fixed_base_select(A, F_BITS) (long)((A) << (F_BITS))
//Преобразование числа с фиксированной точкой в число с плавающей точкой
#define fixed_to_float(A) ((double)A / (1 << GLOBAL_Q))
//Перобразование числа с фиксированной точкой в целое число
#define fixed_to_int(A) ((int)(A >> GLOBAL_Q) )


long multiply(long x, long y)
{
	long long z = (long long)x * (long long)y;
	return (long)(z >> GLOBAL_Q);
}
//служебная функция. Умножает числа с 27 битами, отданными под дробную часть
static inline long multiply_27(long x, long y)
{
    long long z = (long long)x * (long long)y;
    return z & 0x4000000 ? (long)(z >> 27) + 1 : (long)(z >> 27);
}

long long multiply_fixed_base_select(long long x, long long y, int base)
{
    long long z = (long long)x * (long long)y;
    return z & (1 << base) ? (z >> base) + 1 : (z >> base);
}

long divide(long num, long den)
{
	long long numLong = (long long)num;
    long long quotient = (numLong << GLOBAL_Q) / den;
	return (long)quotient;
}
//
static inline long long divide_fixed_base_select(long long num, long long den, int base)
{
    long long quotient = ((long long)num << base) / den;
    return quotient;
}

#define div_def(A,B) (long)(((long long)(A) << 24)/(B))
#define div_mod(A,B) (A)%(B)
#define mult_def(A,B) (long)((((long long)(A))*((long long)(B))) >> 24)
#define abs_def(A) ((A) > 0 ? (A): -(A))

long sin_fixed(long x)
{
    //Константы сделал ститическими, что бы они вычислялись во время запуска программы, а не исполнения
    static long FIXED_2PI = float_to_fixed(TWO_PI);
    static long FIXED_PI = float_to_fixed(PI);
    static long FIXED_PIna2 = float_to_fixed(PI_2);
    //Здесть так же что бы не производить операции деления посчитал констаны ряда Тейлора
    static long one_110 = float_to_fixed_base_select(1./110, 27);
    static long one_72 = float_to_fixed_base_select(1./72, 27);
    static long one_42 = float_to_fixed_base_select(1./42, 27);
    static long one_20= float_to_fixed_base_select(1./20, 27);
    static long one_6 = float_to_fixed_base_select(1./6, 27);

    long long xx, tmp ;
    while(x >= FIXED_2PI) { x -= FIXED_2PI;}    //Помещаю аргумент в диапазон 2 ПИ
    while(x <= -FIXED_2PI) { x += FIXED_2PI;}
    //Так как ряды быстрее сходнятся при малых значениях, помещаю значение аргумента
    //в ближайшие к нулю области
    if(x > FIXED_PI)
    {
        x -= FIXED_2PI;
    }
    else if(x < -FIXED_PI)
    {
        x += FIXED_2PI;
    }
    if(x < -FIXED_PIna2)
    {
        x = -FIXED_PI - x;
    }
    else if(x > FIXED_PIna2)
    {
        x =  FIXED_PI - x;
    }
    //проверяю угол на значения, при которых синус раве 0 или 1
    if(x == 0) return 0;
    if(x == FIXED_PIna2) return int_to_fixed(1);
    if(x == -FIXED_PIna2) return int_to_fixed(-1);
    //Перевожу в формат с максимальной точностью для возможного дипазано значений
    x <<= (27 - GLOBAL_Q);
    //Считаю ряд фурье
    xx = multiply_27(x, x);
    tmp = ONE_27 - multiply_27(one_110, xx);
    tmp = multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(tmp, one_72);
    tmp = multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(tmp, one_42);
    tmp = multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(tmp, one_20);
    tmp = multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(tmp, one_6);
    tmp = multiply_27(x, tmp);
    return tmp >> (27 - GLOBAL_Q); //Перед возвращением из функции преобразую в первоначальный формат
}

long cos_fixed(long x)
{
    //Константы сделал ститическими, что бы они вычислялись во время запуска программы, а не исполнения
    static long FIXED_2PI = float_to_fixed(TWO_PI);
    static long FIXED_PI = float_to_fixed(PI);
    static long FIXED_PIna2 = float_to_fixed(PI_2);
    //Здесть так же что бы не производить операции деления посчитал констаны ряда Тейлора
    static long one_132 = float_to_fixed_base_select(1./132, 27);
    static long one_90 = float_to_fixed_base_select(1./90, 27);
    static long one_56 = float_to_fixed_base_select(1./56, 27);
    static long one_30 = float_to_fixed_base_select(1./30, 27);
    static long one_12 = float_to_fixed_base_select(1./12, 27);

    long xx, tmp, counter = 0;
    while(x >= FIXED_2PI) { x -= FIXED_2PI;}    //Помещаю аргумент в диапазон 2 ПИ
    while(x < 0) { x += FIXED_2PI;}
    x = _IQabs(x);   //Так как косинус симметричен относительно нуля, нахожу его модуль
    //проверяю угол на значения, при которых синус раве 0 или 1
    if(x == 0) return 1 << GLOBAL_Q;
    if(x == FIXED_PI) return -(1 << GLOBAL_Q);
    if(x == (FIXED_PIna2) || (x == FIXED_3PIna2))return 0;
    //Так как ряды быстрее сходнятся при малых значениях, помещаю значение аргумента
    //в ближайшие к нулю области
    while(x > FIXED_PIna2)
    {
        x -= FIXED_PIna2;
        counter++;
    }

    if(counter == 1 || counter == 3) { x = FIXED_PIna2 - x;}
    //Перевожу в формат с максимальной точностью для возможного дипазона значений
    x <<= (27 - GLOBAL_Q);
    //Считаю ряд фурье
    xx = multiply_27(x, x);
    tmp = ONE_27 - multiply_27(xx, one_132);
    tmp= multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(xx, one_90);
    tmp= multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(tmp, one_56);
    tmp = multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(tmp, one_30);
    tmp = multiply_27(xx, tmp);
    tmp = ONE_27 - multiply_27(tmp, one_12);
    tmp = multiply_27(xx, tmp);
    tmp = ONE_27 - (tmp >> 1);
    tmp >>= (27 - GLOBAL_Q);
    return (counter == 0) || (counter == 3) ? tmp : -tmp;
}

long sqrt_fixed(long x)
{
    int variable_size_bits = sizeof(x) << 3;
    long average_val, prev_avg_val;
    if(x <= 0) return 0;
    while(!(x & (1 << --variable_size_bits))); //Нахожу старший значащий бит
    //Нахожу приближение корня сдвгом на половину числа бит между старшим значащим битом
    //и положением точки
    if(variable_size_bits > GLOBAL_Q)
    {
        average_val = x >> ((variable_size_bits - GLOBAL_Q) >> 1);
    }
    else
    {
        average_val = x << ((GLOBAL_Q - variable_size_bits) >> 1);
    }
    prev_avg_val = divide(x, average_val); //Нахожу 1/А
    //В цикле нахожу среднее арифметическое между А и 1/А, пока число не перестанет меняться
    while(_IQabs(prev_avg_val - average_val) > 1)
    {
        prev_avg_val = average_val;
        average_val = (average_val + divide(x, average_val)) >> 1;
    }
    return average_val;
}