(* Copyright (c) 1998-2007 INRIA *) type t = int array let base = 0x1000000 let max_mul_base = max_int / base let zero = [||] let one = [| 1 |] let of_int i = if i < 0 then invalid_arg "Sosa.of_int" else if i = 0 then zero else if i < base then [| i |] else [| i mod base; i / base |] let to_int = function | [||] -> 0 | [| i |] -> i | [| m; d |] -> (d * base) + m | _ -> assert false let eq x y = x = y let gt x y = if Array.length x > Array.length y then true else if Array.length x < Array.length y then false else let rec loop i = if i < 0 then false else if x.(i) > y.(i) then true else if x.(i) < y.(i) then false else loop (i - 1) in loop (Array.length x - 1) let twice x = let l = let rec loop i r = if i = Array.length x then if r = 0 then [] else [ r ] else let v = (x.(i) lsl 1) + r in (v land (base - 1)) :: loop (i + 1) (if v >= base then 1 else 0) in loop 0 0 in Array.of_list l let half x = let l = let rec loop i r v = if i < 0 then v else let rd = if x.(i) land 1 = 0 then 0 else base / 2 in let v = let d = r + (x.(i) / 2) in if d = 0 && v = [] then v else d :: v in loop (i - 1) rd v in loop (Array.length x - 1) 0 [] in Array.of_list l let even x = if Array.length x = 0 then true else x.(0) land 1 = 0 let inc x n = let l = let rec loop i r = if i = Array.length x then if r = 0 then [] else [ r ] else let d = x.(i) + r in (d mod base) :: loop (i + 1) (d / base) in loop 0 n in Array.of_list l let add x y = let l = let rec loop i r = if i >= Array.length x && i >= Array.length y then if r = 0 then [] else [ r ] else let d, r = let xi = if i >= Array.length x then 0 else x.(i) in let yi = if i >= Array.length y then 0 else y.(i) in let s = xi + yi + r in (s mod base, s / base) in d :: loop (i + 1) r in loop 0 0 in Array.of_list l let normalize = let rec loop = function | [] -> [] | x :: l -> let r = loop l in if x = 0 && r = [] then r else x :: r in loop let sub x y = let l = let rec loop i r = if i >= Array.length x && i >= Array.length y then if r = 0 then [] else invalid_arg "Sosa.sub" else let d, r = let xi = if i >= Array.length x then 0 else x.(i) in let yi = if i >= Array.length y then 0 else y.(i) in if yi + r <= xi then (xi - (yi + r), 0) else (base + xi - (yi + r), 1) in d :: loop (i + 1) r in loop 0 0 in Array.of_list (normalize l) let mul0 x n = if n > max_mul_base then invalid_arg "Sosa.mul" else let l = let rec loop i r = if i = Array.length x then if r = 0 then [] else [ r ] else let d = (x.(i) * n) + r in (d mod base) :: loop (i + 1) (d / base) in loop 0 0 in Array.of_list l let mul x n = if n < max_mul_base then mul0 x n else let rec loop r x n = if n < max_mul_base then add r (mul0 x n) else loop (add r (mul0 x (n mod max_mul_base))) (mul0 x max_mul_base) (n / max_mul_base) in loop zero x n let div x n = if n > max_mul_base then invalid_arg "Sosa.div" else let l = let rec loop i l r = if i < 0 then l else let r = (r mod n * base) + x.(i) in let d = r / n in loop (i - 1) (d :: l) r in loop (Array.length x - 1) [] 0 in Array.of_list (normalize l) let modl x n = of_int @@ let r = sub x (mul0 (div x n) n) in if Array.length r = 0 then 0 else r.(0) let rec exp_gen x1 x2 n = if n = 0 || x1 = zero then one else if n = 1 then x1 else exp_gen (mul x1 (to_int x2)) x2 (n - 1) let exp x n = exp_gen x x n let compare x y = if gt x y then 1 else if eq x y then 0 else -1 let code_of_digit d = let d = to_int d in if d < 10 then Char.code '0' + d else Char.code 'A' + (d - 10) let to_string_sep_base sep base x = let digits = let rec loop d x = if eq x zero then d else loop (modl x base :: d) (div x base) in loop [] x in let digits = if digits = [] then [ zero ] else digits in let len = List.length digits in let slen = String.length sep in let s = Bytes.create (len + ((len - 1) / 3 * slen)) in let _ = List.fold_left (fun (i, j) d -> Bytes.set s j (Char.chr (code_of_digit d)); if i < len - 1 && (len - 1 - i) mod 3 = 0 then ( String.blit sep 0 s (j + 1) slen; (i + 1, j + 1 + slen)) else (i + 1, j + 1)) (0, 0) digits in Bytes.unsafe_to_string s let to_string_sep sep = to_string_sep_base sep 10 let to_string = to_string_sep_base "" 10 let of_string s = let rec loop n i = if i = String.length s then n else match s.[i] with | '0' .. '9' -> loop (inc (mul0 n 10) (Char.code s.[i] - Char.code '0')) (i + 1) | _ -> failwith "Sosa.of_string" in loop zero 0 let gen x = let s = to_string_sep_base "" 2 x in String.length s (* coherent with %sosa.lvl *) let branches x = if eq x one then [] else let s = to_string_sep_base "" 2 x in let rec loop acc i = if i <= 0 then acc else loop ((Char.code s.[i] - Char.code '0') :: acc) (i - 1) in loop [] (String.length s - 1)