type elem = { num : int; symb : string; name : string; mass : float option; cpk_color : string option; e_config : string option; e_neg : float option; rad : float option; ionization_e : float option; e_affinity : float option; oxidation : string option; state : string option; melting : float option; boiling : float option; density : float option; block : string option; discovered : int option } let float_or_null stmt n = match (Sqlite3.column stmt n) with | Sqlite3.Data.NULL -> None | Sqlite3.Data.FLOAT x -> Some x | _ -> raise (Failure (Printf.sprintf "Column %d has invalid type" n)) let string_or_null stmt n = match (Sqlite3.column stmt n) with | Sqlite3.Data.NULL -> None | Sqlite3.Data.TEXT x -> Some x | _ -> raise (Failure (Printf.sprintf "Column %d has invalid type" n)) let int_or_null stmt n = match (Sqlite3.column stmt n) with | Sqlite3.Data.NULL -> None | Sqlite3.Data.INT x -> Some (Int64.to_int x) | _ -> raise (Failure (Printf.sprintf "Column %d has invalid type" n)) let chk = function | Sqlite3.Rc.ROW -> () | Sqlite3.Rc.OK -> () | _ -> raise (Failure "statement failure") let get_model db typ = let open Sqlite3 in let stmt = "SELECT dat FROM searchtable WHERE name = ?;" |> prepare db in bind_text stmt 1 typ |> Rc.check; step stmt |> chk; let v = column_blob stmt 0 in finalize stmt |> Rc.check; Batch_jaro_winkler.build_runtime_model v let search_model most model s = let open Batch_jaro_winkler in let encoding = Encoding.UTF8 in jaro_winkler_distance ~encoding ~n_best_results:most model s let by_name n db s = search_model n (get_model db "name") s let name_stmt db s = let open Sqlite3 in let stmt = "SELECT \ num, symb, name, mass, cpk_color, e_config, e_neg, rad, \ ionization_e, e_affinity, oxidation, state, melting, \ boiling, density, block, discovered \ FROM elements WHERE name = ?;" |> prepare db in bind_text stmt 1 s |> Rc.check; stmt let get_elem_from_stmt stmt = let open Sqlite3 in step stmt |> chk; { num = column_int stmt 0; symb = column_text stmt 1; name = column_text stmt 2; mass = float_or_null stmt 3; cpk_color = string_or_null stmt 4; e_config = string_or_null stmt 5; e_neg = float_or_null stmt 6; rad = float_or_null stmt 7; ionization_e = float_or_null stmt 8; e_affinity = float_or_null stmt 9; oxidation = string_or_null stmt 10; state = string_or_null stmt 11; melting = float_or_null stmt 12; boiling = float_or_null stmt 13; density = float_or_null stmt 14; block = string_or_null stmt 15; discovered = int_or_null stmt 16 } let get_elem db s = name_stmt db s |> get_elem_from_stmt let print_elem e = let p_str name = Option.iter (fun x -> Printf.printf "%s: %s\n" name x) in let p_flt name = Option.iter (fun x -> Printf.printf "%s: %f\n" name x) in let p_int name = Option.iter (fun x -> Printf.printf "%s: %d\n" name x) in Printf.printf "'%s' -- '%s' -- '%d'\n" e.name e.symb e.num; p_flt "Standard Atomic Mass" e.mass; p_str "CPK Color" e.cpk_color; p_str "Electron Configuration" e.e_config; p_flt "Electronegativity (Pauling)" e.e_neg; p_flt "Van der Walls (pm)" e.rad; p_flt "Ionization Energy (eV)" e.ionization_e; p_flt "Electron Affinity (eV)" e.e_affinity; p_str "Oxidation States" e.oxidation; p_str "Standard State" e.state; p_flt "Melting Point (K)" e.melting; p_flt "Boiling Point (K)" e.boiling; p_flt "Density (g/cm³)" e.density; p_str "Block" e.block; p_int "Year Discovered" e.discovered; print_newline() let print_by_name db most l = let serc = by_name (Some most) db in let on_match v = (match serc (String.lowercase_ascii v) with | [] -> Printf.printf "No matches found for '%s'\n" v; | (m,p)::[] -> if m <> v then Printf.printf "Best match '%s' for '%s' (%.0f%%)\n" m v (p *. 100.0); get_elem db m |> print_elem | l -> Printf.printf "Matches for '%s'\n" v; List.iter (fun (a,b) -> Printf.printf "%f -- %s\n" b a) (List.rev l) ) in List.iter on_match l