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

#include "muller.h"
#include "nr.h"

void dbessj0(float x, float *y, float *dy) {
    *y = bessj0(x);
    *dy = -bessj1(x);
}

float test_func(float x) {
    return x * sin(x) - 1;
}

float test_func_derivative(float x) {
    return sin(x) + x * cos(x);
}

void test_funcd(float x, float *y, float *dy) {
    *y = test_func(x);
    *dy = test_func_derivative(x);
}

int main() {
    /*
    1. using zbrak, find all possible intervals with roots.
    2. and use routines and enumerate all roots.
    */
    printf("=== A Program that find bessj0 roots in [1, 10] ===\n");
    printf("--------------------------------\n");
    float a = 1.0, b = 10.0;
    float xacc = 0.000001;

    int n = 23;
    float *xb1 = calloc(sizeof(float), n);
    float *xb2 = calloc(sizeof(float), n);
    int nb;

    int i;
    zbrak(bessj0, a, b, n, xb1, xb2, &nb);

    /* for (i = 1; i <= nb; i++) {
        printf("[%f, %f]\n", xb1[i], xb2[i]);
    } */

    float *roots_bisect = calloc(sizeof(float), nb);
    float *roots_linter = calloc(sizeof(float), nb);
    float *roots_secant = calloc(sizeof(float), nb);
    float *roots_newt = calloc(sizeof(float), nb);
    float *roots_newtsafe = calloc(sizeof(float), nb);
    float *roots_muller = calloc(sizeof(float), nb);

    for (i = 0; i < nb; i++) {
        float l = xb1[i + 1];
        float r = xb2[i + 1];
        roots_bisect[i] = rtbis(bessj0, l, r, xacc);
        roots_linter[i] = rtflsp(bessj0, l, r, xacc);
        roots_secant[i] = rtsec(bessj0, l, r, xacc);
        roots_newt[i] = rtnewt(dbessj0, l, r, xacc);
        roots_newtsafe[i] = rtsafe(dbessj0, l, r, xacc);
        roots_muller[i] = rtmuller(bessj0, l, r, xacc);
    }

    printf("besection roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_bisect[i]);
    }
    printf("\n");

    printf("lin-interpoalation roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_linter[i]);
    }
    printf("\n");

    printf("secant roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_secant[i]);
    }
    printf("\n");

    printf("newton-raphson roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_newt[i]);
    }
    printf("\n");

    printf("newton with bracketing roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_newtsafe[i]);
    }
    printf("\n");

    printf("muller method roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_muller[i]);
    }
    printf("\n");

    /* free */
    free(xb1);
    free(xb2);

    free(roots_bisect);
    free(roots_linter);
    free(roots_secant);
    free(roots_newt);
    free(roots_newtsafe);
    free(roots_muller);
    printf("\n");
    printf("=== Solve one interesting nonlinear equation ===\n");
    printf(": using the routine of rtsafe.c in NR in C\n");
    printf("--------------------------------\n");
    n = 21;
    xb1 = calloc(sizeof(float), n);
    xb2 = calloc(sizeof(float), n);
    nb = 0;
    
    zbrak(test_func, a, b, n, xb1, xb2, &nb);
    float *roots_interesting = calloc(sizeof(float), nb);

    for (i = 0; i < nb; i++) {
        float l = xb1[i + 1];
        float r = xb2[i + 1];

        roots_interesting[i] = rtsafe(test_funcd, l, r, xacc);
    }

    printf("I want to find roots of `x * sin(x) - 1`\n");
    printf("roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_interesting[i]);
    }
    printf("\n");

    free(xb1);
    free(xb2);
    free(roots_interesting);

    return 0;
}