#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <limits.h>
#include <float.h>

#define _CRT_SECURE_NO_WARNINGS
#define LN10    2.302585092994045684018
#define RLN10  0.43429448190325182765

#define  MAX_LEN  128

// 1+2 +3 + ... q                     = (q+1)q/2
// 1^2+2^2 +3^2 + ... q^2 = q(q+1)(2q+1)/6
// 1^3+2^3 +3^3 + ... q^3 = (q(q+1)/2)^2
typedef struct
{
    double v;       //  p/q
    double e1;     // 1次项  (1+2+3 + ...q)/LN10= (q+1)*q/(2*LN10)
    double e2;    // 2次项    (1^2+2^2+3^2 + ...q^2)/(2*LN10)=q*(q+1)*(2q+1)/(12*LN10) 
    double e3;    // 3次项   (1^3+2^3+3^3 + ...q^3)/(3*LN10)= ((q+1)q)^2/(12LN10)
    int32_t p;
    int32_t q;
    int idx; 
} FRAC;

typedef struct   _dblArray
{
    double lgn;         //  log10(n)- floor( log10(n) )
    int n;
    FRAC ls[MAX_LEN];
    FRAC  hs[MAX_LEN];
}DBL_ARRAY;

inline double get_double(FRAC *pf)
{
    return (double)pf->p / (double)pf->q;
}

// 1+2 +3 + ... q                     = (q+1)q/2
// 1^2+2^2 +3^2 + ... q^2 = q(q+1)(2q+1)/6
// 1^3+2^3 +3^3 + ... q^3 = (q(q+1)/2)^2
inline void update_FRAC get_double(FRAC *p)
{
    //double e1;     // 1次项  (1+2+3 + ...q)/LN10= (q+1)*q/(2*LN10)
    //double e2;    // 2次项    (1^2+2^2+3^2 + ...q^2)/(2*LN10)=q*(q+1)*(2q+1)/(12*LN10) 
    //double e3;    // 3次项   (1^3+2^3+3^3 + ...q^3)/(3*LN10)= ((q+1)q)^2/(12LN10)
    double f = p->q;
    double e1 = f * (f + 1.0) * 0.5;
    p->e2 = f * (f + 1.0) * (f * 2 + 1) / (12.0 * LN10);
    p->e3 = (e1 * e1) / (3.0 * LN10);
    p->e1 = e1 / LN10;
    p->v= (double)(p->p) / (double)(p->q);
}

void create_DblArray ( DBL_ARRAY *dblArray, int n, double lgn)
{
    FRAC *pf1;
    FRAC *pf2;
    dblArray->lgn = lgn;
    dblArray->n = n;

    pf1 = dblArray->ls + 0;
    pf2 = dblArray->hs + 0;
    
    pf1->p = 1;
    pf1->q = (int32_t)ceil(1.0 / lgn);
    pf1->idx = 0;
    update_FRAC(pf1);

    pf2->p = 1;
    pf2->q = (int32_t)floor(1.0 / lgn);
    pf2->idx = 0;
    update_FRAC(pf2);
}


// (lg(n+d)!)的小数部分
// (lg(n + d)!)
// ~= lg(n) + d*lg(n) + d*(d+1)/(2n*ln(10)
inline double get_lg_new_n(double lgn, FRAC fr, double rk)
{
    double det = lgn - get_double(fr); //
    double r = det * (double)(fr.q) + (double)(fr.q * (fr.q + 1)) * rk;
    return r - round(r);
}
//首次算出欠周期和过周期
//当Q很大时或者欠周期>0时,使用常规方法求得步进
// 在计算序列时,一旦不收敛时(欠周期时得到的值>0或者过周期得到的值不再减少),直接退出
// 当欠时,使用过周期,取最后一个累积和大于0的数
// 当过时,使用欠周期,取最后一个累积和小于0的数

void farey_search(uint64_t n, double f_lgn, double det)
{
    FRAC low, high, mid;
    uint32_t best_q;
    double rk = 1.0 / ((double)n * 2.0 * LN10);
    double big_lg_nn, small_lg_nn; //big lg(new_n) and small lg(new_n)
    double last_det, new_det, lg_nn, best_diff;
    bool isfirst;
    int s_det, s_ndet;

    low.p = 1;
    low.q = ceil(1.0 / f_lgn);
    high.p = 1;
    high.q = floor(1.0 / f_lgn);

    if (low.p >= 65535 || get_lg_new_n(f_lgn, high, rk) >= 0)
    {
        printf("Need use common method to do this when n=%lu", n);
        return;
    }

    printf("original det =%.16lf\n", det);
    big_lg_nn = get_lg_new_n(f_lgn, low, rk);	//过周期
    small_lg_nn = get_lg_new_n(f_lgn, high, rk); //欠周期
    if (det < 0)								 //使用过周期
    {
        best_diff = det + big_lg_nn;
        best_q = low.q;
    }
    else
    {
        best_diff = det + small_lg_nn;
        best_q = high.q;
    }

    isfirst = true;
    last_det = det;
    while (true)
    {
        mid.p = low.p + high.p;
        mid.q = low.q + high.q;

        printf("mid.q=%d\n", mid.q);

        lg_nn = get_lg_new_n(f_lgn, mid, rk);
        new_det = lg_nn + det;

        if (get_double(mid) >= f_lgn) //是上界,欠周期
        {
            if (lg_nn >= 0.0) // 欠周期时得到的值 >= 0
            {
                printf("Break from  %s:%d\n", __FILE__, __LINE__);
                break;
            }
            if (det > 0)
            {
                if (isfirst || fabs(new_det) < fabs(last_det))
                {
                    printf("run to %s:%d\n", __FILE__, __LINE__);
                    best_q = mid.p;
                    best_diff = new_det;
                }
                else
                {
                    printf("Break from %s:%d\n", __FILE__, __LINE__);
                    break;
                }
            }
            high = mid;
            small_lg_nn = lg_nn;
        }
        else //是下界,过周期
        {
            if (lg_nn >= big_lg_nn) //过周期得到的值不再减少
            {              
                printf("break from %s:%d\n", __FILE__, __LINE__);
                break;
            }
            
            if (det < 0)
            {
                if (isfirst || fabs(new_det) < fabs(last_det))
                {
                    printf("run to %s:%d\n", __FILE__, __LINE__);
                    best_q = mid.p;
                    best_diff = new_det;
                }
                else
                {
                    printf("break from  %s:%d\n", __FILE__, __LINE__);
                    break;
                }
            }
            low = mid;
            big_lg_nn = lg_nn;
        }
        
        if (mid.q >= 65535)
        {
            printf("run to %s:%d\n", __FILE__, __LINE__);
            break;
        }
        last_det = new_det;
        isfirst = false;
    }
    printf("best q is %u,new det =%.16lf\n", best_q, best_diff);
}

int main(int argc, char *argv[])
{
    //  lgpi = log(3.1415926535898) / log(10)
    //	n = 1544607046599
    //	lgn = log(n) / log(10)
    //	lg_fn = lngamma(n + 1) / log(10)
    //	f_lg_fn = lg_fn - floor(lg_fn)
    //	f_lgn = lgn - floor(lgn)
    //	det = lgpi - f_lg_fn

    //	0.188818011786880272870
    //	- 2.0713896587241805909e-14

    // 0.1888180120936348488209645817901717275944058254028858993817802728002129534568670651162967209435905600
    // -0.0004510269743969044278283267303117723666536375937437949826689256603143119413249000389634605276822730510

    //uint64_t n = 1544607046599;
    //double f_lgn = 0.188818011786880272870;
    //double det = -2.0713896587241805909e-14;

    uint64_t n = 1544607046599 + 1091;
    double f_lgn = 0.188818012093634848820964581790;
    double det = -0.000451026974396904427828326734;

    printf("n=?");
    scanf("%lu", &n);
    printf("f_lgn=?");
    scanf("%lf", &f_lgn);
    printf("det=?");
    scanf("%lf", &det);
    farey_search(n, f_lgn, det);

    return 0;
}