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

typedef double (*raw_fptr)(double);
struct real_function;
typedef double (*evaluate_function)(struct real_function*, double);

struct real_function {
        evaluate_function evaluate;
};
typedef struct real_function real_function;

double evaluate(real_function *f, double x) {
        if(f) {
                return f->evaluate(f, x);
        }
        return NAN;
}

struct raw_real_function {
        real_function real_function_base;
        raw_fptr raw_function;
};
typedef struct raw_real_function raw_real_function;

double evaluate_raw_real_function(real_function *f_base, double x) {
        if(f_base) {
                raw_real_function *f = (raw_real_function*)f_base;
                return f->raw_function(x);
        }
        return NAN;
}

raw_real_function make_raw_real_function(raw_fptr function) {
        raw_real_function result;
        result.raw_function = function;
        result.real_function_base.evaluate = evaluate_raw_real_function;
        return result;
}

struct derive_real_function {
        real_function real_function_base;
        real_function *function_to_derive;
};
typedef struct derive_real_function derive_real_function;

double derive(real_function *f_base, double x) {
        derive_real_function *f = (derive_real_function*)f_base;
        double epsilon = 1e-3;
        double upper = evaluate(f->function_to_derive, x+epsilon);
        double lower = evaluate(f->function_to_derive, x-epsilon);
        double result =  (upper - lower)/(2.0*epsilon);
        return result;
}

derive_real_function make_derivative(real_function * function_to_derive) {
        derive_real_function result;
        result.real_function_base.evaluate = derive;
        result.function_to_derive = function_to_derive;
        return result;
}

double x_cubed(double x) {
        return x * x * x;
}

int main(int argc, char **argv) {
        raw_real_function x_cubed_wrapped = make_raw_real_function(x_cubed);
        derive_real_function derived = make_derivative(&x_cubed_wrapped.real_function_base);
        derive_real_function derived_twice = make_derivative(&derived.real_function_base);
        double x;
        scanf("%lf", &x);
        double derivative = evaluate(&derived.real_function_base, x);
        double second_derivative = evaluate(&derived_twice.real_function_base, x);
        printf("derivative of x^3 at %f = %f\n", x, derivative);
        printf("second derivative of x^3 at %f = %f\n", x, second_derivative);
        return 0;
}
