You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('') and can be up to 35 characters long.
270 lines
10 KiB
OCaml
270 lines
10 KiB
OCaml
(* ************************************************************************** *)


(* *)


(* ::: :::::::: *)


(* main.ml :+: :+: :+: *)


(* +:+ +:+ +:+ *)


(* By: frdescam <marvin@42.fr> +#+ +:+ +#+ *)


(* +#+#+#+#+#+ +#+ *)


(* Created: 2022/11/27 23:10:10 by frdescam #+# #+# *)


(* Updated: 2023/03/10 17:10:37 by frdescam ### ########.fr *)


(* *)


(* ************************************************************************** *)




open Complex


open Types




let exit_error error_msg =


print_endline error_msg;


exit 1




let extract_data_from_argv () =


try


(


let polynomial_string = Sys.argv.(1) in


let lexbuf = Lexing.from_string polynomial_string in


Polynomial_parser.parse Polynomial_lexer.lex lexbuf


) with e > exit_error "Syntax error"




let add_default_terms polynomial =


let reduced_form = polynomial.reduced_form


in


let degree = polynomial.degree


in


let reduced_form =


if not (List.exists (fun {exponent; _} > exponent = 0) reduced_form)


then


{coefficient = 0.; exponent = 0} :: reduced_form


else


reduced_form


in


let reduced_form =


if degree > 0 && not (List.exists (fun {exponent; _} > exponent = 1) reduced_form)


then


{coefficient = 0.; exponent = 1} :: reduced_form


else


reduced_form


in


let reduced_form =


if degree > 1 && not (List.exists (fun {exponent; _} > exponent = 2) reduced_form)


then


{coefficient = 0.; exponent = 2} :: reduced_form


else


reduced_form


in


let reduced_form = List.sort (fun {exponent=e1; _} {exponent=e2; _} >


compare e2 e1) reduced_form


in


{ polynomial with reduced_form = reduced_form }




let find_degree polynomial =


let degree = match polynomial.reduced_form with


 [] > 0


 hd :: _ > hd.exponent


in


{ polynomial with degree = degree }




let reduce_polynomial polynomial =


let all_terms = polynomial.left_terms @ List.map (fun {coefficient; exponent} >


{


coefficient = 1. *. coefficient; exponent


}) polynomial.right_terms


in


let grouped_terms = List.fold_left (fun acc t >


let exponent = t.exponent in


let (same, other) = List.partition (fun t' > t'.exponent = exponent) acc in


let coefficient = List.fold_left


(fun acc {coefficient; _} > acc +. coefficient)


0.


(t :: same)


in


(if coefficient = 0. then other else {coefficient; exponent} :: other))


[]


all_terms


in


let reduced_terms = List.sort (fun {exponent=e1; _} {exponent=e2; _} >


compare e2 e1) grouped_terms


in


{ polynomial with reduced_form = reduced_terms }




let solve_0 polynomial =


let a = (List.nth polynomial.reduced_form 0).coefficient


in


{


polynomial with solution_1 =


Some (


match a with


 0. > AllReal


 _ > NoSolutions


)


}




let solve_1 polynomial =


let a = (List.nth polynomial.reduced_form 0).coefficient


and b = (List.nth polynomial.reduced_form 1).coefficient


in


{


(* Solution = b / a *)


polynomial with solution_1 =


Some (Solution (Complex.{


re = (.b /. a);


im = 0.


}))


}




let compute_discriminant polynomial =


let a = (List.nth polynomial.reduced_form 0).coefficient


and b = (List.nth polynomial.reduced_form 1).coefficient


and c = (List.nth polynomial.reduced_form 2).coefficient


in


{


(* Discriminent = b²  4ac *)


polynomial with discriminant =


Some (


b *. b . 4. *. a *. c


)


}




let compute_solution_null_discriminant polynomial =


let a = (List.nth polynomial.reduced_form 0).coefficient


and b = (List.nth polynomial.reduced_form 1).coefficient


in


{


(* Solution = (b / 2a *)


polynomial with solution_1 =


Some (Solution (Complex.{


re = (.b /. (2. *. a));


im = 0.


}))


}




let compute_solutions_negative_discriminant polynomial =


let a = (List.nth polynomial.reduced_form 0).coefficient


and b = (List.nth polynomial.reduced_form 1).coefficient


and discriminant =


match polynomial.discriminant with


 Some d > d;


 None > exit_error "Internal error"


in


{


polynomial with


(* Solution 1 = (b + i * sqrt(discriminant)) / 2a *)


solution_1 =


Some (Solution (Complex.{


re = (.b /. (2. *. a));


im = ((Stdlib.sqrt (abs_float discriminant)) /. (2. *. a))


}));


(* Solution 1 = (b  i * sqrt(discriminant)) / 2a *)


solution_2 =


Some (Solution (Complex.{


re = (.b /. (2. *. a));


im = (.(Stdlib.sqrt (abs_float discriminant)) /. (2. *. a))


}))


}




let compute_solutions_positive_discriminant polynomial =


let a = (List.nth polynomial.reduced_form 0).coefficient


and b = (List.nth polynomial.reduced_form 1).coefficient


and discriminant =


match polynomial.discriminant with


 Some d > d;


 None > exit_error "Internal error"


in


{


polynomial with


(* Solution 1 = (b  sqrt(discriminant)) / 2a *)


solution_1 =


Some (Solution (Complex.{


re = ((.b . Stdlib.sqrt discriminant) /. (2. *. a));


im = 0.


}));


(* Solution 1 = (b + sqrt(discriminant)) / 2a *)


solution_2 =


Some (Solution (Complex.{


re = ((.b +. Stdlib.sqrt discriminant) /. (2. *. a));


im = 0.


}))


}




let solve_2 polynomial =


let polynomial = compute_discriminant polynomial


in


match polynomial.discriminant with


 Some d when d < 0. > compute_solutions_negative_discriminant polynomial


 Some d when d > 0. > compute_solutions_positive_discriminant polynomial


 _ > compute_solution_null_discriminant polynomial




let print_solution solution =


let re = solution.re in


let im = solution.im in


if im = 0.


then Printf.printf "%f\n" re


else


let operator = if im > 0. then '+' else '' in


let im = abs_float im in


Printf.printf "%f %c %fi\n" re operator im




let print_solutions polynomial =


match polynomial.solution_1 with


 Some NoSolutions > print_endline "There is no solution to this polynomial."


 Some AllReal > print_endline "All real numbers are solution of this polynomial."


 Some Solution s > print_string "x1 : "; print_solution s


 None > ();


;


match polynomial.solution_2 with


 Some Solution s > print_string "x2 : "; print_solution s


 _ > ()




let print_polynomial_term term =


if term.coefficient != 1. then


(


if term.coefficient >= 0. then


print_string "+ "


else


print_string " ";


print_float (abs_float term.coefficient)


);


if term.exponent != 0 then


(


print_string "x";


if term.exponent != 1 then


(


print_string "^";


print_int term.exponent


)


);


print_string ""




let print_reduced_form polynomial =


print_string "Reduced form : ";


List.iter (fun t >


print_polynomial_term t;


print_string " "


) polynomial.reduced_form;


print_string "\n"




let print_discriminant polynomial =


match polynomial.discriminant with


 Some d > Printf.printf "Discriminant : %f\n" d


 None > ()




let print_resolved_polynomial polynomial =


print_reduced_form polynomial;


print_discriminant polynomial;


print_solutions polynomial




let _ =


if Array.length Sys.argv != 2 then


exit_error "Error: invalid argument count";




let polynomial =


extract_data_from_argv () > reduce_polynomial > find_degree > add_default_terms


in


let polynomial =


match polynomial.degree with


 0 > solve_0 polynomial


 1 > solve_1 polynomial


 2 > solve_2 polynomial


 _ > exit_error "Error: polynomials after 2nd degree aren't supported"


in


print_resolved_polynomial polynomial
