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


static double newton_raphson(double (*f)(double), double (*df)(double),
        double start_x, double tolerance, unsigned int *steps,
        unsigned int max_steps, double approximation, int error_per_step)
{
    double x = start_x, last_x = x - 1;

    for( *steps = 1; fabs(x - last_x) >= tolerance; (*steps)++ ) {
        if ( *steps == max_steps )
            return NAN;

        last_x = x;
        x -= f(x) / df(x);

        if ( error_per_step )
            printf("step %2d error: %.3E (%3f%%)\n", *steps,
                fabs(approximation - last_x),
                100 * fabs(last_x - approximation) / approximation);
    }

    return x;
}

#ifdef MAIN_newton_raphson

static inline double f1(double x) { return x * x - x + 2; }
static inline double df1(double x) { return 2 * x - 1; }

static inline double f2(double x) { return x * x * x - 3 * x - 2; }
static inline double df2(double x) { return 3 * x * x - 3; }

static inline double f3(double x) { return (x * x + 1) * (x - 4); }
static inline double df3(double x) { return 3 * x * x - 8 * x + 1; }

int main(void)
{
    unsigned int steps;
    double result;

    result = newton_raphson(&f1, &df1, 0, 10e-12, &steps, 100, 0, 0);
    printf("f1 using newton-raphson = %.20f (%d steps)\n", result, steps);

    result = newton_raphson(&f2, &df2, -10, 10e-12, &steps, 100, 0, 0);
    printf("f2 using newton-raphson = %.20f (%d steps)\n", result, steps);

    result = newton_raphson(&f2, &df2, 10, 10e-12, &steps, 100, 0, 0);
    printf("f2 using newton-raphson = %.20f (%d steps)\n", result, steps);

    result = newton_raphson(&f3, &df3, 0, 10e-12, &steps, 100, 0, 0);
    printf("f3 using newton-raphson = %.20f (%d steps)\n", result, steps);

    return 0;
}

#endif
