#include "lmath_ratio.h"
#include "math.h"

#define LMATH_RATIONAL_APPROX_MAX_ITER  20

// функция вычисляет НОД
int lmath_gcd( int a, int b ) {
    return b ? lmath_gcd( b, a % b ) : a;
}

// функция вычисляет НОК, используя НОД
int lmath_lcm( int a, int b ){
    return a / lmath_gcd( a, b ) * b;
}


// source:
// http://www.isi.edu/~johnh/BLOG/1999/0728_RATIONAL_PI/fast_convergence.txt
// theory:
// https://en.wikipedia.org/wiki/Continued_fraction#Best_rational_approximations

t_lmath_errs lmath_rational_approx(double val, long *n, long *d, long max_d, double rel_err) {
    long coeff[LMATH_RATIONAL_APPROX_MAX_ITER];
    int sign = 1;
    double x = val;
    int fnd = 0;
    int i;

    for (i = 0; (i < LMATH_RATIONAL_APPROX_MAX_ITER) && !fnd; ++i) {
        if (x < 0.0) {
            sign = -sign;
            x = -x;
        }
        long ipart = (long)x + (x - floor(x) > 0.5);
        coeff[i] = ipart * sign;
        x = 1.0 / (x - ipart);

        // compute numerator and denominator
        long num = coeff[i], den = 1;
        for (int j = i - 1; j >= 0; --j) {
            long t = num;
            num = coeff[j] * num + den;
            den = t;
        }
        if (den < 0) {
            num = -num;
            den = -den;
        }
        if (den > max_d)
            break;
        *n = num;
        *d = den;
        double approx_val = ((double)num) / den;
        double err = fabs(approx_val / val - 1.0);
        fnd = (err <= rel_err);
    }
    return fnd ? LMATH_ERR_OK : LMATH_ERR_PRECISION;
}
