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

#include "convergence.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("===============================\n");
    printf("A report of root-finding\n");
    printf("student name: Hajin Ju, id: 2024062806\n");
    printf("--------------------------------\n");
    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);

    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`\nwhere x in [%.02f, %.02f]\n", a, b);
    printf("-------------------------------\n");
    printf("roots: ");
    for (i = 0; i < nb; i++) {
        printf("%f ", roots_interesting[i]);
    }
    printf("\n");

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

    printf("\n");

    printf("=== Discuss the convergence speed ===\n");
    printf("--------------------------------\n");

    a = 2.0f;
    b = 3.0f;
    printf("Find a root of bessj0(x) where x in [%.02f, %.02f].\n", a, b);
    printf("We want to get convergence speed(iteration count).\n");
    printf("-------------------------------\n");

    ConvergenceData conv_bisect = rtbis_with_conv(bessj0, a, b, xacc);
    ConvergenceData conv_flsp = rtflsp_with_conv(bessj0, a, b, xacc);
    ConvergenceData conv_secant = rtsec_with_conv(bessj0, a, b, xacc);
    ConvergenceData conv_newt = rtnewt_with_conv(dbessj0, a, b, xacc);
    ConvergenceData conv_newtsafe = rtsafe_with_conv(dbessj0, a, b, xacc);
    ConvergenceData conv_muller = rtmuller_with_conv(bessj0, a, b, xacc);

    printf("Convergence History(where root ~ 2.404825):\n");

    printf("bisect iteration count: %d\n", conv_bisect.size);
    printf("bisect iteration history: ");
    for (i = 0; i < conv_bisect.size; i++) {
        printf("%f ", conv_bisect.data[i]);
    }
    printf("\n");

    printf("lin-interpoalation iteration count: %d\n", conv_flsp.size);
    printf("lin-interpoalation iteration history: ");
    for (i = 0; i < conv_flsp.size; i++) {
        printf("%f ", conv_flsp.data[i]);
    }
    printf("\n");

    printf("secant iteration count: %d\n", conv_secant.size);
    printf("secant iteration history: ");
    for (i = 0; i < conv_secant.size; i++) {
        printf("%f ", conv_secant.data[i]);
    }
    printf("\n");

    printf("newton-raphson iteration count: %d\n", conv_newt.size);
    printf("newton-raphson iteration history: ");
    for (i = 0; i < conv_newt.size; i++) {
        printf("%f ", conv_newt.data[i]);
    }
    printf("\n");

    printf("newton with bracketing iteration count: %d\n", conv_newtsafe.size);
    printf("newton with bracketing iteration history: ");
    for (i = 0; i < conv_newtsafe.size; i++) {
        printf("%f ", conv_newtsafe.data[i]);
    }
    printf("\n");

    printf("muller method iteration count: %d\n", conv_muller.size);
    printf("muller method iteration history: ");
    for (i = 0; i < conv_muller.size; i++) {
        printf("%f ", conv_muller.data[i]);
    }
    printf("\n");

    printf("-------------------------------\n");
    printf("Convergence Error\n");
    float res;
    
    printf("bisect convergence error: ");
    res = conv_bisect.data[conv_bisect.size - 1];
    for (i =0;i < conv_bisect.size; i++) {
        printf("%f ", fabs(conv_bisect.data[i] - res) / res);
    }
    printf("\n");

    printf("flsp convergence error: ");
    res = conv_flsp.data[conv_flsp.size - 1];
    for (i =0;i < conv_flsp.size; i++) {
        printf("%f ", fabs(conv_flsp.data[i] - res) / res);
    }
    printf("\n");

    printf("secant convergence error: ");
    res = conv_secant.data[conv_secant.size - 1];
    for (i =0;i < conv_secant.size; i++) {
        printf("%f ", fabs(conv_secant.data[i] - res) / res);
    }
    printf("\n");

    printf("newton-raphson convergence error: ");
    res = conv_newt.data[conv_newt.size - 1];
    for (i =0;i < conv_newt.size; i++) {
        printf("%f ", fabs(conv_newt.data[i] - res) / res);
    }
    printf("\n");

    printf("newton with bracketing convergence error: ");
    res = conv_newtsafe.data[conv_newtsafe.size - 1];
    for (i =0;i < conv_newtsafe.size; i++) {
        printf("%f ", fabs(conv_newtsafe.data[i] - res) / res);
    }
    printf("\n");

    printf("muller method convergence error: ");
    res = conv_muller.data[conv_muller.size - 1];
    for (i =0;i < conv_muller.size; i++) {
        printf("%f ", fabs(conv_muller.data[i] - res) / res);
    }
    printf("\n");

    printf("-------------------------------\n");

    printf("Convergence Iteration Counts Table:\n");
    printf("--------------------------------------------------------------\n");
    printf("| Method                   | Iter |\n");
    printf("--------------------------------------------------------------\n");
    printf("| Bisection                | %-04d |\n", conv_bisect.size);
    printf("| Linear Interpolation     | %-04d |\n", conv_flsp.size);
    printf("| Secant                   | %-04d |\n", conv_secant.size);
    printf("| Newton-Raphson           | %-04d |\n", conv_newt.size);
    printf("| Newton with Bracketing   | %-04d |\n", conv_newtsafe.size);
    printf("| Muller                   | %-04d |\n", conv_muller.size);
    printf("--------------------------------------------------------------\n");

    printf("As a result, In order of performance(faster convergence speed),\n");
    printf("Newton-Raphson, Newton with Bracketing, Muller, Secant, Linear Interpolation, Bisection\n");

    printf("-------------------------------\n");

    printf("===============================\n");

    return 0;
}