(*
Since our implementation does not have chars as regular values, we replaced
the uses of chars for int (except when they are used for printing to stdout).
*)

let inv_sqrt_2x_pi : float = 0.39894228040143270286 in

let cummulative_normal_distribution : float -> float =
	fun (x_input : float) ->
	let sign : bool = x_input <. 0.0 in
	let x : float = if x_input <. 0.0 then x_input *. -1.0 else x_input in
	let exp_values : float = exp ((x *. x) *. -0.5) in
	let n_prime_of_x : float = exp_values *. inv_sqrt_2x_pi in
	let x_k2 : float = 1.0 /. (1.0 +. (0.2316419 *. x)) in
	let x_k2sq : float = x_k2 *. x_k2 in
	let x_k2cube : float = x_k2sq *. x_k2 in
	let x_k2fourth : float = x_k2cube *. x_k2 in
	let x_k2fifth : float = x_k2fourth *. x_k2 in
	let x1 : float = 0.319381530 *. x_k2 in
	let x2 : float = -0.356563782 *. x_k2sq in
	let x3 : float = 1.781477937 *. x_k2cube in
	let x4 : float = -1.821255978 *. x_k2fourth in
	let x5 : float = 1.330274429 *. x_k2fifth in
	let x : float = x1 +. x2 +. x3 +. x4 +. x5 in
	let x : float = 1.0 -. (x *. n_prime_of_x) in
	if sign then 1.0 -. x else x
in

let black_scholes : float -> float -> float -> float -> float -> int -> float -> float =
	fun (spot : float)
			(strike : float)
			(rate : float)
			(volatility : float)
			(time1 : float)
			(option_type : int)
			(timet : float) ->
	let log1 : float = log (spot /. strike) in
	let pow : float = 0.5 *. (volatility *. volatility) in
	let den : float = volatility *. sqrt time1 in
	let d1 : float = (log1 +. (time1 *. (rate +. pow))) /. den in
	let d2 : float = d1 -. den in
	let n_of_d1 : float = cummulative_normal_distribution d1 in
	let n_of_d2 : float = cummulative_normal_distribution d2 in
	let fut_value : float = strike *. (exp (-1.0 *. (rate *. time1))) in
	if option_type = 0 then
		(spot *. n_of_d1) -. (fut_value *. n_of_d2)
	else
		(fut_value *. (1.0 -. n_of_d2)) -. (spot *. (1.0 -. n_of_d1))
in

let make_option : float -> float -> float -> float -> float -> float -> int -> float -> float ->
		  float * float * float * float * float * float * int * float * float =
	fun (spot_price : float)
			(strike_price : float)
			(rfi_rate : float)
			(divr : float)
			(volatility : float)
			(time1 : float)
	    (option_type : int)
			(divs : float)
			(derivgem_value : float) ->
	    (spot_price, strike_price, rfi_rate, divr, volatility, time1, option_type, divs, derivgem_value)
in

let read_option_type : int -> int =
	fun (_ : int) ->
		let c : int = read_char () in
		if c = 80 then
			1
		else if c = 67 then
			0
		else if c = 32 then
			read_char ()
		else
			read_char ()
in

let read_option : int -> float * float * float * float * float * float * int * float * float =
	fun (_ : int) ->
		let spot_price : float = read_float () in
		let strike_price : float = read_float () in
		let rfi_rate : float = read_float () in
		let dividend_rate : float = read_float () in
		let volatility : float = read_float () in
		let maturity_len : float = read_float () in
		let option_type : int = read_option_type 0 in
		let divs : float = read_float () in
		let derivgem_value : float = read_float () in
		make_option spot_price strike_price rfi_rate dividend_rate volatility maturity_len
					option_type divs derivgem_value
in

let number_of_runs : int = 100 in

let run_benchmark : int -> int =
	fun (_ : int) ->
		let number_of_options : int = read_int () in
		let fake_data : float * float * float * float *
				float * float * int * float * float =
				(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1, 0.0, 0.0) in
		let data : vec[float * float * float * float * float * float * int * float * float] =
				vector number_of_options fake_data in
		let spots : vec[float] = vector number_of_options 0.0 in
		let strikes : vec[float] = vector number_of_options 0.0 in
		let rates : vec[float] = vector number_of_options 0.0 in
		let volatilities : vec[float] = vector number_of_options 0.0 in
		let otypes : vec[int] = vector number_of_options 0 in
		let otimes : vec[float] = vector number_of_options 0.0 in
		for i = 0 to number_of_options do
			vector_set data i (read_option 0)
		done;
		(for i = 0 to number_of_options do
			let od : float * float * float * float *
					float * float * int * float * float =
					vector_get data i in
				vector_set otypes i (if od.(6) = 1 then 1 else 0);
				vector_set spots i od.(0);
				vector_set strikes i od.(1);
				vector_set rates i od.(2);
				vector_set volatilities i od.(4);
				vector_set otimes i od.(5)
		done);
		(let prices : vec[float] = vector number_of_options 0.0 in
			for j = 0 to number_of_runs do
				for i = 0 to number_of_options do
					vector_set prices i (black_scholes (vector_get spots i)
									   (vector_get strikes i)
									   (vector_get rates i)
									   (vector_get volatilities i)
									   (vector_get otimes i)
									   (vector_get otypes i)
									   0.0)
				done
			done;
			for i = 0 to number_of_options do
				if i = (number_of_options - 1) then
					print_float (vector_get prices i) ;
					print_char '\n' ;
					0
				else
					0
			done
		);
		0
in

time run_benchmark
