open Util open Util.Vars open Pure open Num (* exceptions *) exception Pacman exception Bottom exception Lambda exception Backtrack of string (* verbosity *) let _very_verbose = true;; (** Display measure of every term when printing problem *) let _measure_of_terms = false;; let verbose s = if _very_verbose then prerr_endline s ;; type problem = { freshno: int ; div: i_var ; conv: i_var list (* the inerts that must converge *) ; sigma: (int * nf) list (* the computed substitution *) ; initialSpecialK: int ; label : string ; var_names : string list (* names of the original free variables *) } ;; let label_of_problem {label} = label;; let string_of_var l x = try List.nth l x with Failure "nth" -> "`" ^ string_of_int x ;; let string_of_term p t = print ~l:p.var_names (t :> nf);; let first args p var f = let rec aux = function | [] -> raise (Backtrack ("no more alternatives for " ^ string_of_var p.var_names var)) | i::is -> try f p i with Backtrack s -> prerr_endline (">>>>>> BACKTRACK (reason: " ^ s ^") measure=$ "); prerr_endline("Now trying var="^string_of_var p.var_names var^" i="^string_of_int (i+1)); aux is in aux args let all_terms p = p.div :: p.conv ;; let measure_of_term, measure_of_terms = let rec aux = function | `Bottom | `Pacman -> 0 | `Var(_,ar) -> if ar = min_int then 0 else max 0 ar (*assert (ar >= 0); ar*) | `Lam(_,t) -> aux t | `I(v,args) -> aux (`Var v) + aux_many (Listx.to_list args :> nf list) and aux_many tms = List.fold_right ((+) ++ aux) tms 0 in (fun t -> aux (t :> nf)), (fun l -> aux_many (l :> nf list)) ;; let sum_arities p = measure_of_terms (all_terms p) let problem_measure p = sum_arities p;; let string_of_measure = string_of_int;; let string_of_problem label ({freshno; div; conv} as p) = let aux_measure_terms t = if _measure_of_terms then "(" ^ string_of_int (measure_of_term t) ^ ") " else "" in let l = p.var_names in String.concat "\n" ([ ""; "# DISPLAY PROBLEM (" ^ label ^ ") " ^ "measure=" ^ string_of_measure (problem_measure p); "#"; "$" ^ p.label; "D " ^ aux_measure_terms div ^ print ~l (div :> nf); ] @ List.map (fun t -> "C " ^ aux_measure_terms t ^ print ~l (t :> nf)) conv @ [""]) ;; let failwithProblem p reason = print_endline (string_of_problem "FAIL" p); failwith reason ;; let make_fresh_var p arity = let freshno = p.freshno + 1 in {p with freshno}, `Var(freshno,arity) ;; let make_fresh_vars p arities = List.fold_right (fun arity (p, vars) -> let p, var = make_fresh_var p arity in p, var::vars) arities (p, []) ;; let fixpoint f = let rec aux x = let x' = f x in if x <> x' then aux x' else x in aux ;; let subst_in_problem x inst ({div; conv} as p) = prerr_endline ("# INST0: " ^ string_of_var p.var_names x ^ " := " ^ string_of_term p inst); let aux t = match t with | #i_var as t -> t | `Lam _ | `Bottom | `Pacman -> assert false (* ??? *) in let div = (aux ++ (subst false false x inst)) (div :> nf) in let conv = List.map (subst false false x inst) (conv :> nf list) in let conv = List.filter (function `Lam _ -> false | _ -> true) conv in let conv = List.map aux conv in {p with div; conv} ;; exception Dangerous;; let arity_of arities hd = let pos,_,nargs = List.find (fun (_,hd',_) -> hd=hd') arities in nargs + if pos = -1 then - 1 else 0 ;; (* let rec dangerous arities showstoppers = function `N _ | `Var _ | `Lam _ | `Pacman -> () | `Match(t,_,liftno,bs,args) -> (* CSC: XXX partial dependency on the encoding *) (match t with `N _ -> List.iter (dangerous arities showstoppers) args | `Match _ as t -> dangerous arities showstoppers t ; List.iter (dangerous arities showstoppers) args | `Var(x,_) -> dangerous_inert arities showstoppers x args 2 | `I((x,_),args') -> dangerous_inert arities showstoppers x (Listx.to_list args' @ args) 2 ) | `I((k,_),args) -> dangerous_inert arities showstoppers k (Listx.to_list args) 0 and dangerous_inert arities showstoppers k args more_args = List.iter (dangerous arities showstoppers) args ; if List.mem k showstoppers then raise Dangerous else try let arity = arity_of arities k in if List.length args + more_args > arity then raise Dangerous else () with Not_found -> () (* cut & paste from above *) let rec dangerous_conv p arities showstoppers = function | `N _ | `Var _ | `Lam _ | `Pacman -> () | `Match(t,_,liftno,bs,args) -> ( (* CSC: XXX partial dependency on the encoding *) try (match t with | `N _ -> List.iter (dangerous_conv p arities showstoppers) args | `Match _ as t -> dangerous_conv p arities showstoppers t; List.iter (dangerous_conv p arities showstoppers) args | `Var(x,_) -> dangerous_inert_conv p arities showstoppers x [] args 2 | `I((x,_),args') -> dangerous_inert_conv p arities showstoppers x (Listx.to_list args') args 2 ) with TriggerMatchReduction x -> ( match Util.find_opt (fun (n, t) -> if hd_of (List.nth p.ps n) = Some x then Some t else None) !bs with | None -> () | Some t -> ( match t with | `Bottom -> raise Dangerous | #nf_nob as t -> dangerous_conv p arities showstoppers t ) ) ) | `I((k,_),args) -> dangerous_inert_conv p arities showstoppers k (Listx.to_list args) [] 0 and dangerous_inert_conv p arities showstoppers k args match_args more_args = let all_args = args @ match_args in List.iter (dangerous_conv p arities showstoppers) all_args; let all_args = (all_args :> nf list) in if List.mem k showstoppers then raise Dangerous else try let arity = arity_of arities k in prerr_endline ("dangerous_inert_conv: ar=" ^ string_of_int arity ^ " k="^string_of_var p.var_names k ^ " listlenargs=" ^ (string_of_int (List.length args)) ^ " more_args=" ^ string_of_int more_args); if more_args > 0 (* match argument*) && List.length args = arity then raise (TriggerMatchReduction k) else if List.length all_args + more_args > arity then raise Dangerous else () with Not_found -> () (* inefficient algorithm *) let rec edible p arities showstoppers = let rec aux f showstoppers tms = function | [] -> showstoppers | x::xs when List.exists (fun y -> hd_of x = Some y) showstoppers -> (* se la testa di x e' uno show-stopper *) let new_showstoppers = sort_uniq (showstoppers @ free_vars (x :> nf)) in (* aggiungi tutte le variabili libere di x *) if List.length showstoppers <> List.length new_showstoppers then aux f new_showstoppers tms tms else aux f showstoppers tms xs | x::xs -> match hd_of x with None -> aux f showstoppers tms xs | Some h -> try f showstoppers (x :> nf_nob) ; aux f showstoppers tms xs with Dangerous -> aux f (sort_uniq (h::showstoppers)) tms tms in let showstoppers = sort_uniq (aux (dangerous arities) showstoppers p.ps p.ps) in let dangerous_conv = sort_uniq (aux (dangerous_conv p arities) showstoppers p.conv p.conv) in (* prerr_endline ("dangerous_conv lenght:" ^ string_of_int (List.length dangerous_conv)); List.iter (fun l -> prerr_endline (String.concat " " (List.map (string_of_var p.var_names) l))) dangerous_conv; *) let showstoppers' = showstoppers @ dangerous_conv in let showstoppers' = sort_uniq (match p.div with | None -> showstoppers' | Some div -> if List.exists ((=) (hd_of_i_var div)) showstoppers' then showstoppers' @ free_vars (div :> nf) else showstoppers') in if showstoppers <> showstoppers' then edible p arities showstoppers' else showstoppers', dangerous_conv ;; let precompute_edible_data {ps; div} xs = let aux t = match t with `Var _ -> 0 | `I(_, args) -> Listx.length args | `N _ -> assert false in (fun l -> match div with | None -> l | Some div -> (-1, hd_of_i_var div, aux div) :: l) (List.map (fun hd -> let i, tm = Util.findi (fun y -> hd_of y = Some hd) ps in i, hd, aux tm ) xs) ;; (** Returns (p, showstoppers_step, showstoppers_eat) where: - showstoppers_step are the heads occurring twice in the same discriminating set - showstoppers_eat are the heads in ps having different number of arguments *) let critical_showstoppers p = let p = super_simplify p in let hd_of_div = match p.div with None -> [] | Some t -> [hd_of_i_var t] in let showstoppers_step = concat_map (fun bs -> let heads = List.map (fun (i,_) -> List.nth p.ps i) !bs in let heads = List.sort compare (hd_of_div @ filter_map hd_of heads) in snd (split_duplicates heads) ) p.deltas @ if List.exists (fun t -> [hd_of t] = List.map (fun x -> Some x) hd_of_div) p.conv then hd_of_div else [] in let showstoppers_step = sort_uniq showstoppers_step in let showstoppers_eat = let heads_and_arities = List.sort (fun (k,_) (h,_) -> compare k h) (filter_map (function `Var(k,_) -> Some (k,0) | `I((k,_),args) -> Some (k,Listx.length args) | _ -> None ) p.ps) in let rec multiple_arities = function [] | [_] -> [] | (x,i)::(y,j)::tl when x = y && i <> j -> x::multiple_arities tl | _::tl -> multiple_arities tl in multiple_arities heads_and_arities in let showstoppers_eat = sort_uniq showstoppers_eat in let showstoppers_eat = List.filter (fun x -> not (List.mem x showstoppers_step)) showstoppers_eat in List.iter (fun v -> prerr_endline ("DANGEROUS STEP: " ^ (string_of_var p.var_names) v)) showstoppers_step; List.iter (fun v -> prerr_endline ("DANGEROUS EAT: " ^ (string_of_var p.var_names) v)) showstoppers_eat; p, showstoppers_step, showstoppers_eat ;; let eat p = let ({ps} as p), showstoppers_step, showstoppers_eat = critical_showstoppers p in let showstoppers = showstoppers_step @ showstoppers_eat in let heads = List.sort compare (filter_map hd_of ps) in let arities = precompute_edible_data p (uniq heads) in let inedible, showstoppers_conv = edible p arities showstoppers in let l = List.filter (fun (_,hd,_) -> not (List.mem hd inedible)) arities in let p = List.fold_left (fun p (pos,hd,nargs) -> if pos = -1 then p else let v = `N pos in let inst = make_lams v nargs in prerr_endline ("# [INST_IN_EAT] eating: " ^ string_of_var p.var_names hd ^ " := " ^ string_of_term p inst); { p with sigma = p.sigma @ [hd,inst] } ) p l in (* to avoid applied numbers in safe positions that trigger assert failures subst_in_problem x inst p*) let ps = List.map (fun t -> try let j,_,_ = List.find (fun (_,hd,_) -> hd_of t = Some hd) l in `N j with Not_found -> t ) ps in let p = match p.div with | None -> p | Some div -> if List.mem (hd_of_i_var div) inedible then p else let n = match div with `I(_,args) -> Listx.length args | `Var _ -> 0 in let x = hd_of_i_var div in let inst = make_lams `Bottom n in subst_in_problem x inst p in (*let dangerous_conv = showstoppers_conv in prerr_endline ("dangerous_conv lenght:" ^ string_of_int (List.length dangerous_conv)); List.iter (fun l -> prerr_endline (String.concat " " (List.map (string_of_var p.var_names) l))) dangerous_conv; *) let conv = List.map (function t -> try if let hd = hd_of t in hd <> None && not (List.mem (Util.option_get hd) showstoppers_conv) then t else ( (match t with | `Var _ -> raise Not_found | _ -> ()); let _ = List.find (fun h -> hd_of t = Some h) inedible in t) with Not_found -> match hd_of t with | None -> assert (t = convergent_dummy); t | Some h -> prerr_endline ("FREEZING " ^ string_of_var p.var_names h); convergent_dummy ) p.conv in List.iter (fun bs -> bs := List.map (fun (n,t as res) -> match List.nth ps n with `N m -> m,t | _ -> res ) !bs ) p.deltas ; let old_conv = p.conv in let p = { p with ps; conv } in if l <> [] || old_conv <> conv then prerr_endline (string_of_problem "eat" p); if List.for_all (function `N _ -> true | _ -> false) ps && p.div = None then `Finished p else `Continue p let safe_arity_of_var p x = (* Compute the minimum number of arguments when x is in head position at p.div or p.ps *) let aux = function | `Var(y,_) -> if x = y then 0 else max_int | `I((y,_),args) -> if x = y then Listx.length args else max_int | _ -> max_int in let tms = ((match p.div with None -> [] | Some t -> [(t :> i_n_var)]) @ p.ps) in List.fold_left (fun acc t -> Pervasives.min acc (aux t)) max_int tms ;; *) let instantiate p x perm = let n = (prerr_endline ("WARNING: using constant initialSpecialK=" ^ string_of_int p.initialSpecialK)); p.initialSpecialK in let arities = Array.to_list (Array.make (n+1) min_int) in let p,vars = make_fresh_vars p arities in (* manual lifting of vars by perm in next line *) let vars = List.map (function `Var (n,ar) -> `Var (n+perm,ar)) vars in (* other_vars are the variables which are delayed and re-applied to the match *) let other_vars = Array.mapi (fun n () -> `Var(n+1,min_int)) (Array.make (perm-1) ()) in let other_vars = Array.to_list other_vars in (* 666, since it will be replaced anyway during subst: *) (* let inst = `Match(`I((0,min_int),args),(x,-666),perm,bs,other_vars) in *) let inst = `I((0,min_int),Listx.from_list (vars @ other_vars)) in (* FIXME *) (* Add a number of 'perm' leading lambdas *) let inst = Array.fold_left (fun t () -> `Lam(false, t)) inst (Array.make perm ()) in let p = {p with sigma=(x,inst)::p.sigma} in subst_in_problem x inst p ;; let compute_special_k tms = let rec aux k (t: nf) = Pervasives.max k (match t with | `Lam(b,t) -> aux (k + if b then 1 else 0) t | `I(n, tms) -> Listx.max (Listx.map (aux 0) (tms :> nf Listx.listx)) | `Bottom | `Pacman | `Var _ -> 0 ) in let rec aux' top t = match t with | `Lam(_,t) -> aux' false t | `I((_,ar), tms) -> max ar (Listx.max (Listx.map (aux' false) (tms :> nf Listx.listx))) | `Bottom | `Pacman | `Var _ -> 0 in Listx.max (Listx.map (aux 0) tms) + Listx.max (Listx.map (aux' true) tms) ;; (* let choose_step p = let p, showstoppers_step, showstoppers_eat = critical_showstoppers p in let x = match showstoppers_step, showstoppers_eat with | [], y::_ -> prerr_endline ("INSTANTIATING (critical eat) : " ^ string_of_var p.var_names y); y | x::_, _ -> prerr_endline ("INSTANTIATING (critical step): " ^ string_of_var p.var_names x); x | [], [] -> let heads = (* Choose only variables still alive (with arity > 0) *) List.sort compare (filter_map ( fun t -> match t with `Var _ -> None | x -> if arity_of_hd x <= 0 then None else hd_of x ) ((match p.div with Some t -> [(t :> i_n_var)] | _ -> []) @ p.ps)) in (match heads with | [] -> (try fst (List.find (((<) 0) ++ snd) (concat_map free_vars' (p.conv :> nf list))) with Not_found -> assert false) | x::_ -> prerr_endline ("INSTANTIATING TO EAT " ^ string_of_var p.var_names x); x) in let arity_of_x = Util.option_get (max_arity_tms x (all_terms p)) in let safe_arity_of_x = safe_arity_of_var p x in x, min arity_of_x safe_arity_of_x ;; *) let intersect l1 l2 = let rec aux n l1 l2 = match l1, l2 with | [], _ -> [] | _, [] -> assert false | x::xs, y::ys -> if x <> y then n :: aux (n+1) xs ys else aux (n+1) xs ys in aux 1 (Listx.to_list l1) (Listx.to_list l2);; exception Difference of int list;; let eat p = let hd, args, n = match p.div with `Var _ -> assert false | `I((x,_), l) -> x, l, Listx.length l in let rec aux = function | `Var _ | `Lam _ -> () | `Pacman | `Bottom -> assert false | `I((x,_),args') -> if x = hd then if Listx.length args' >= n then (let diff = intersect args args' in if diff <> [] then raise (Difference diff)); List.iter aux ((Listx.to_list args') :> nf list) in try List.iter aux (p.conv :> nf list) ; `Finished {p with sigma=(hd,make_lams `Bottom n)::p.sigma} with Difference l -> `Continue (hd, l) ;; let rec auto_eat p = prerr_endline (string_of_problem "auto_eat" p); prerr_endline "{{{{{{{{ Computing measure before auto_instantiate }}}}}}"; match eat p with | `Finished p -> prerr_endline "finished"; p | `Continue (x,positions) -> let m = problem_measure p in first positions p x (fun p j -> let p = instantiate p x j in prerr_endline "{{{{{{{{ Computing measure inafter auto_instantiate }}}}}}"; let delta = problem_measure p - m in if delta >= 0 then (prerr_endline ("Measure did not decrease (+=" ^ string_of_int delta ^ ")")) else prerr_endline ("$ Measure decreased: " ^ string_of_int delta); auto_eat p) ;; (******************************************************************************) let env_of_sigma freshno sigma = let rec aux n = if n > freshno then [] else let e = aux (n+1) in (try e,Pure.lift (-n-1) (snd (List.find (fun (i,_) -> i = n) sigma)),[] with Not_found -> ([],Pure.V n,[]))::e in aux 0 ;; (* ************************************************************************** *) type response = [ | `CompleteSeparable of string | `CompleteUnseparable of string | `Uncomplete ] type result = [ `Complete | `Uncomplete ] * [ | `Separable of (int * Num.nf) list | `Unseparable of string ] let run p = Console.print_hline(); prerr_endline (string_of_problem "main" p); let p_finale = auto_eat p in let freshno,sigma = p_finale.freshno, p_finale.sigma in prerr_endline ("------- ------ measure=. \n "); List.iter (fun (x,inst) -> prerr_endline (string_of_var p_finale.var_names x ^ " := " ^ string_of_term p_finale inst)) sigma; prerr_endline "------------------"; let scott_of_nf t = ToScott.scott_of_nf (t :> nf) in let div = scott_of_nf p.div in let conv = List.map scott_of_nf p.conv in let sigma' = List.map (fun (x,inst) -> x, ToScott.scott_of_nf inst) sigma in let e' = env_of_sigma freshno sigma' in prerr_endline "-----------------"; (* print_endline (Pure.print div); *) let t = Pure.mwhd (e',div,[]) in prerr_endline ("*:: " ^ (Pure.print t)); assert (t = Pure.B); List.iter (fun n -> (* verbose ("_::: " ^ (Pure.print n)); *) let t = Pure.mwhd (e',n,[]) in verbose ("_:: " ^ (Pure.print t)); assert (t <> Pure.B) ) conv ; prerr_endline "-------- --------"; p_finale.sigma ;; let solve (p, todo) = let completeness, to_run = match todo with | `CompleteUnseparable s -> `Complete, `False s | `CompleteSeparable _ -> `Complete, `True | `Uncomplete -> `Uncomplete, `True in completeness, match to_run with | `False s -> `Unseparable s | `True -> try `Separable (run p) with Backtrack _ -> `Unseparable "backtrack" ;; let no_bombs_pacmans p = not (List.exists (eta_subterm `Bottom) p.conv) && not (eta_subterm `Pacman p.div) ;; let check p = if not (List.exists (eta_subterm p.div) p.conv) && no_bombs_pacmans p then `CompleteSeparable "no bombs, pacmans and div" (* il check seguente e' spostato nel parser e lancia un ParsingError *) (* else if false (* TODO bombs or div fuori da lambda in ps@conv *) then `CompleteUnseparable "bombs or div fuori da lambda in ps@conv" *) else `Uncomplete ;; let problem_of (label, div, conv, var_names) = (* TODO: replace div with bottom in problem??? *) let all_tms = div :: conv in if all_tms = [] then failwith "problem_of: empty problem"; let initialSpecialK = compute_special_k (Listx.from_list (all_tms :> nf list)) in let freshno = List.length var_names in let sigma = [] in let p = {freshno; div; conv; sigma; initialSpecialK; var_names; label} in p, check p ;;