type symbol = Symbol.symbol
type literal = Term.literal
type var = {
sv_var: Var.var;
sv_offset: int;
}
type term = {
st_term: Term.term;
st_offset: int;
}
type binding = {
sb_var: var;
sb_term: term;
}
let input_literal_offset =
0
let fresh_par_offset =
1
let context_literal_offset (index: int) =
2 + index
type subst =
binding list
let var_equal (var1: var) (var2: var) : bool =
(var1.sv_offset == var2.sv_offset)
&&
(Var.equal var1.sv_var var2.sv_var)
let var_equal' (var1: Var.var) (offset1: int) (var2: var) : bool =
(offset1 == var2.sv_offset)
&&
(Var.equal var1 var2.sv_var)
let term_equal (term1: term) (term2: term) : bool =
(term1.st_offset == term2.st_offset)
&&
(Term.term_equal term1.st_term term2.st_term)
let binding_equal (binding1: binding) (binding2: binding) : bool =
(var_equal binding1.sb_var binding2.sb_var)
&&
(term_equal binding1.sb_term binding2.sb_term)
let subst_equal (subst1: subst) (subst2: subst) : bool =
subst1 == subst2
||
Tools.lists_unordered_equal
binding_equal
subst1
subst2
module VarTable =
Hashtbl.Make (
struct
type t = var
let equal = var_equal
let hash var =
var.sv_offset * 131 + Var.hash_of_var var.sv_var
end
)
type mapping = Term.term VarTable.t
type hash_subst = term VarTable.t
exception SET_FAIL
let empty =
[]
let make_var (var: Var.var) (offset: int) : var =
{
sv_var = var;
sv_offset = offset;
}
let make_term (term: Term.term) (offset: int) : term =
{
st_term = term;
st_offset = offset;
}
let make_binding var term = {
sb_var = var;
sb_term = term;
}
let append binding subst =
binding :: subst
let fold =
List.fold_left
let iter =
List.iter
let map =
List.map
let map' =
List.map
let exists =
List.exists
let for_all =
List.for_all
let length =
List.length
let is_empty subst =
subst == empty
let find =
List.find
let find_all =
List.find_all
let partition =
List.partition
let first subst : binding =
match subst with
| binding :: _ ->
binding
| [] ->
failwith "Subst.first"
let var_to_string (var: var) : string =
string_of_int var.sv_offset ^ ": " ^ Var.to_string var.sv_var
let term_to_string (term: term) : string =
string_of_int term.st_offset ^ ": " ^ Term.term_to_string term.st_term
let binding_to_string (binding: binding) : string =
"[ "
^ var_to_string binding.sb_var
^ " -> "
^ term_to_string binding.sb_term
^ " ]"
let subst_to_string (subst: subst) : string =
" { \n"
^ String.concat "\n"
(map' binding_to_string subst)
^ " }"
let check_binding (binding: binding) =
if Const.debug then
begin
if binding.sb_var.sv_offset = binding.sb_term.st_offset then begin
match binding.sb_term.st_term with
| Term.Var var2 when
Var.equal binding.sb_var.sv_var var2
->
failwith "Subst.append 1";
| _ ->
()
end
end
let check_subst subst =
if Const.debug then begin
List.iter check_binding subst
end
let rec get' (subst: subst) (var: Var.var) (offset: int) : term option =
match subst with
| [] ->
None
| binding :: tail ->
if
(offset == binding.sb_var.sv_offset)
&&
(Var.equal var binding.sb_var.sv_var)
then
Some binding.sb_term
else
get' tail var offset
let get (subst: subst) (var: var) : term option =
get' subst var.sv_var var.sv_offset
let orient (binding: binding) : binding =
let var, term =
binding.sb_var, binding.sb_term
in
if var.sv_offset < term.st_offset then
binding
else begin
let replace_binding var' =
make_binding
(make_var var' term.st_offset)
(make_term (Term.request_var var.sv_var) var.sv_offset)
in
match term.st_term with
| Term.Var var' ->
if var.sv_offset > term.st_offset then
replace_binding var'
else if Var.is_universal var.sv_var && Var.is_parametric var' then
binding
else if Var.is_parametric var.sv_var && Var.is_universal var' then
replace_binding var'
else if Var.id_of_var var.sv_var <= Var.id_of_var var' then
binding
else
replace_binding var'
| _ ->
binding
end
let rec dereference' (subst: hash_subst) (bound_term: term) : term option =
match bound_term.st_term with
| Term.Var bound_var ->
begin
try
let var =
make_var bound_var bound_term.st_offset
in
let bound_term' =
VarTable.find subst var
in
begin
match dereference' subst bound_term' with
| Some bound_term'' ->
VarTable.replace subst var bound_term'';
Some bound_term''
| None ->
VarTable.replace subst var bound_term';
Some bound_term'
end
with
| Not_found ->
None
end
| _ ->
None
let dereference' (subst: hash_subst) (term: term) : term =
match dereference' subst term with
| None ->
term
| Some term ->
term
let rec get_bound_vars' (subst: subst) (vars: var list) (acc: var list) : var list =
List.fold_left
(fun acc var ->
match get subst var with
| None ->
if List.exists (var_equal var) acc then
acc
else
var :: acc
| Some term ->
let term_vars =
List.map
(fun term_var ->
make_var term_var term.st_offset
)
(Term.vars_of_term term.st_term)
in
get_bound_vars'
subst
term_vars
acc
)
acc
vars
let get_bound_vars (subst: subst) (vars: var list) : var list =
get_bound_vars' subst vars []
let is_p_renaming (subst:subst) (offset: int) : bool =
let rec is_p_renaming' (bindings: subst) (mapping: (var * term) list) : bool =
match bindings with
| [] ->
true
| binding :: tail ->
if binding.sb_var.sv_offset != offset then
is_p_renaming' tail mapping
else if Var.is_universal binding.sb_var.sv_var then
is_p_renaming' tail mapping
else if binding.sb_term.st_offset == offset then
false
else
match binding.sb_term.st_term with
| Term.Var bound_var when Var.is_parametric bound_var ->
begin
try
let mapping' =
Tools.mapping_extend mapping
var_equal term_equal
binding.sb_var binding.sb_term
in
is_p_renaming' tail mapping'
with
| Exit ->
false
end
| _ ->
false
in
is_p_renaming' subst []
let dereference (subst: subst) (term: term) : term =
match term.st_term with
| Term.Var var ->
begin
match get' subst var term.st_offset with
| None ->
term
| Some bound_term ->
bound_term
end
| _ ->
term
let rec occur_check' (subst: subst) (var: var) (term: Term.term) (term_offset: int) : unit =
match term with
| Term.Var term_var ->
if ((var.sv_offset == term_offset)
&&
(Var.equal var.sv_var term_var))
then begin
raise SET_FAIL
end
else begin
match get' subst term_var term_offset with
| None ->
()
| Some term ->
occur_check' subst var term.st_term term.st_offset
end
| Term.Const _ ->
()
| Term.Func func ->
Array.iter
(fun term ->
occur_check' subst var term term_offset
)
func.Term.subterms
module type T_Preserving =
sig
val set: subst -> var -> term -> subst
val set': subst -> Var.var -> int -> Term.term -> int -> subst
end
module type Options =
sig
val recompute: bool
val p_preserving: bool
end
module Make (Options: Options) : T_Preserving =
struct
let check_bindings (subst: subst) (var: var) (term: term) : unit =
if Options.p_preserving && Var.is_parametric var.sv_var then begin
match term.st_term with
| Term.Var bound_var when
Var.is_parametric bound_var
&&
var.sv_offset != term.st_offset
->
()
| _ ->
raise SET_FAIL
end;
if Options.p_preserving && Var.is_parametric var.sv_var then begin
iter
(fun binding ->
if var_equal var binding.sb_var then
raise SET_FAIL
else if Var.is_parametric binding.sb_var.sv_var && term_equal term binding.sb_term then begin
raise SET_FAIL
end
else begin
match binding.sb_term.st_term with
| Term.Var bound_var ->
if
(var.sv_offset == binding.sb_term.st_offset)
&&
(Var.equal var.sv_var bound_var)
&&
Var.is_parametric binding.sb_var.sv_var
then
raise SET_FAIL
| _ ->
()
end
)
subst;
end
let rec compression_needed (subst: subst) (var: var) : bool =
match subst with
| [] ->
false
| binding :: tail ->
if var.sv_offset == binding.sb_term.st_offset then begin
match binding.sb_term.st_term with
| Term.Var bound_var when
(Var.equal var.sv_var bound_var) ->
true
| _ ->
compression_needed tail var
end
else
compression_needed tail var
let compress_old_bindings (subst: subst) (var: var) (term: term) : subst =
map
(fun binding ->
match binding.sb_term.st_term with
| Term.Var bound_var
when
((var.sv_offset = binding.sb_term.st_offset)
&&
(Var.equal var.sv_var bound_var)) ->
{
sb_var = binding.sb_var;
sb_term = term;
}
| _ ->
binding
)
subst
let set (subst: subst) (var: var) (term: term) : subst =
if not Options.recompute then
occur_check' subst var term.st_term term.st_offset;
let new_bound_term =
dereference subst term
in
if not Options.recompute then
check_bindings subst var new_bound_term;
let compressed_subst =
if compression_needed subst var then
compress_old_bindings subst var new_bound_term
else
subst
in
let new_binding = {
sb_var = var;
sb_term = new_bound_term;
}
in
append new_binding compressed_subst
let set' (subst: subst) (var: Var.var) (var_offset: int) (term: Term.term) (term_offset: int) : subst =
set subst (make_var var var_offset) (make_term term term_offset)
end
module PPreserving =
Make (
struct
let recompute = false
let p_preserving = true
end
)
module Preserving =
Make (
struct
let recompute = false
let p_preserving = false
end
)
module RPPreserving =
Make (
struct
let recompute = true
let p_preserving = true
end
)
module RPreserving =
Make (
struct
let recompute = true
let p_preserving = false
end
)
let set ~(recompute: bool) ?(p_preserving:bool = false)
(subst: subst) (var: var) (term: term) : subst =
if not recompute && p_preserving then
PPreserving.set subst var term
else if not recompute && not p_preserving then
Preserving.set subst var term
else if recompute && p_preserving then
RPPreserving.set subst var term
else
RPreserving.set subst var term
let set' ~(recompute: bool) ?(p_preserving:bool = false)
(subst: subst) (var: Var.var) (var_offset: int) (term: Term.term) (term_offset: int) : subst =
set ~recompute:recompute ~p_preserving:p_preserving
subst (make_var var var_offset) (make_term term term_offset)
let rec consult_mapping (var: Var.var) (offset: int) (mapping: (var * Term.term) list)
(mapping': (var * Term.term) list) :
Term.term * ((var * Term.term) list) =
match mapping with
| [] ->
let new_var =
Var.clone_renumbered var (List.length mapping')
in
let new_term =
Term.request_var new_var
in
new_term, (make_var var offset, new_term) :: mapping'
| (var', bound) :: tail ->
if var_equal' var offset var' then
bound, mapping'
else
consult_mapping var offset tail mapping'
let rec apply_to_term' ~(insert_db: bool) ~(normalize: bool)
(subst: subst) (term: Term.term) (offset: int)
(mapping: (var * Term.term) list) : Term.term * ((var * Term.term) list) =
match term with
| Term.Var var ->
begin
match get' subst var offset with
| None ->
if not normalize then
term, mapping
else
consult_mapping var offset mapping mapping
| Some term ->
apply_to_term' ~insert_db:insert_db ~normalize:normalize
subst term.st_term term.st_offset mapping
end
| Term.Const _ ->
term, mapping
| Term.Func func ->
let new_terms =
Array.copy func.Term.subterms
in
let rec do_at (i : int) mapping =
if i >= Array.length new_terms then
mapping
else
let new_term, mapping' =
apply_to_term' ~insert_db:insert_db ~normalize:normalize
subst func.Term.subterms.(i) offset mapping
in
new_terms.(i) <- new_term;
do_at (i + 1) mapping'
in
let mapping' =
do_at 0 mapping
in
let term' =
Term.request_func ~insert_db:insert_db (func.Term.symbol, new_terms)
in
term', mapping'
let rec apply_to_literal' ~(insert_db: bool) ~(normalize: bool)
(subst: subst) (literal: Term.literal) (offset: int)
(mapping: (var * Term.term) list) : Term.literal * ((var * Term.term) list) =
let term', mapping' =
apply_to_term' ~insert_db:insert_db ~normalize:normalize
subst literal.Term.atom offset mapping
in
Term.request_literal literal.Term.sign term',
mapping'
let apply_to_literals' (subst: subst) (literals: (literal * int) list)
(mapping: (var * Term.term) list) : Term.literal list * ((var * Term.term) list) =
let (mapping', literals') =
List.fold_left
(fun (mapping, literals') (literal, offset) ->
let literal', mapping' =
apply_to_literal' ~insert_db:true ~normalize:true subst literal offset mapping
in
(mapping', literal' :: literals')
)
(mapping, [])
literals
in
List.rev literals', mapping'
let apply_to_term ?(insert_db: bool = true) ?(normalize: bool = true)
(subst: subst) (term: Term.term) (offset: int) : Term.term =
fst (apply_to_term' ~insert_db:insert_db ~normalize:normalize subst term offset [])
let apply_to_literal ?(insert_db: bool = true) ?(normalize: bool = true)
(subst: subst) (literal: Term.literal) (offset: int) : Term.literal =
let term =
apply_to_term ~insert_db:insert_db ~normalize:normalize subst literal.Term.atom offset
in
Term.request_literal ~insert_db:insert_db literal.Term.sign term
let apply_to_clause (subst: subst) (clause: Term.clause) (offset: int) : Term.clause =
fst (apply_to_literals' subst (List.map (fun literal -> (literal, offset)) clause) [])
let apply_to_literals (subst: subst) (literals: (literal * int) list) : literal list =
fst (apply_to_literals' subst literals [])
let apply_to_literals_groups (subst: subst) (groups: (literal * int) list list) : literal list list =
let (_, groups') =
List.fold_left
(fun (mapping, groups') literals ->
let literals', mapping' =
apply_to_literals' subst literals mapping
in
(mapping', literals' :: groups')
)
([], [])
groups
in
List.rev groups'
let normalize_term (term: Term.term) : Term.term =
apply_to_term ~insert_db:true ~normalize:true
empty term 0
let normalize_literal (literal: Term.literal) : Term.literal =
apply_to_literal ~insert_db:true ~normalize:true
empty literal 0
let normalize_clause (clause: Term.clause) : Term.clause =
apply_to_clause empty clause 0
let rec apply_to_term'' ?(insert_db:bool = true) (subst: hash_subst) (term: Term.term) (offset: int)
(mapping: mapping) : Term.term =
match term with
| Term.Var var ->
begin
try
let term =
dereference' subst (VarTable.find subst (make_var var offset))
in
apply_to_term'' ~insert_db:insert_db subst term.st_term term.st_offset mapping
with
| Not_found ->
begin
try
VarTable.find mapping (make_var var offset)
with
| Not_found ->
let new_var =
Var.clone_renumbered var (VarTable.length mapping)
in
let new_term =
Term.request_var new_var
in
VarTable.add mapping (make_var var offset) new_term;
new_term
end
end
| Term.Const _ ->
term
| Term.Func func ->
let new_terms =
Array.copy func.Term.subterms
in
let rec do_at (i : int) =
if i >= Array.length new_terms then
()
else
let new_term =
apply_to_term'' ~insert_db:insert_db subst func.Term.subterms.(i) offset mapping
in
new_terms.(i) <- new_term;
do_at (i + 1)
in
do_at 0;
Term.request_func ~insert_db:insert_db (func.Term.symbol, new_terms)
let apply_to_literals'' (subst: hash_subst) (literals: (literal * int) list) (mapping: mapping) : literal list =
let applied =
List.fold_left
(fun acc (literal, offset) ->
let new_term =
apply_to_term'' subst literal.Term.atom offset mapping
in
let new_literal =
Term.request_literal literal.Term.sign new_term
in
new_literal :: acc
)
[]
literals
in
List.rev applied
let apply_to_literal_groups' (subst: hash_subst) (groups: (literal * int) list list) : literal list list =
let mapping =
VarTable.create 1024
in
let applied =
List.fold_left
(fun acc group ->
apply_to_literals'' subst group mapping :: acc
)
[]
groups
in
List.rev applied
let replace_binding_offset (binding: binding) (old_offset: int) (new_offset: int) : binding =
if
binding.sb_var.sv_offset = old_offset
&&
binding.sb_term.st_offset = old_offset
then
{
sb_var = {
sv_var = binding.sb_var.sv_var;
sv_offset = new_offset;
};
sb_term = {
st_term = binding.sb_term.st_term;
st_offset = new_offset;
};
}
else if
binding.sb_var.sv_offset = old_offset
then
{ binding with
sb_var = {
sv_var = binding.sb_var.sv_var;
sv_offset = new_offset;
};
}
else if
binding.sb_term.st_offset = old_offset
then
{ binding with
sb_term = {
st_term = binding.sb_term.st_term;
st_offset = new_offset;
};
}
else
binding
let replace_offset (subst: subst) (old_offset: int) (new_offset: int) : subst =
map
(fun binding -> replace_binding_offset binding old_offset new_offset)
subst
let replace_in_bound_terms (subst: subst) (old_term: Term.term) (new_term: Term.term) : subst =
if Term.term_equal old_term new_term then
subst
else
map
(fun binding ->
let new_bound_term =
Term.replace_term_in_term binding.sb_term.st_term old_term new_term
in
if Term.term_equal binding.sb_term.st_term new_bound_term then begin
binding
end
else begin
{ binding with
sb_term = {
binding.sb_term with
st_term = new_bound_term
};
}
end
)
subst
let rec reverse_bindings (subst: subst) (vars_to_reverse: var list) (vars_to_keep: var list): subst =
match vars_to_reverse with
| [] ->
subst
| var_to_reverse :: tail ->
let rec find' list =
match list with
| [] ->
reverse_bindings subst tail vars_to_keep
| binding :: tail' ->
if var_equal var_to_reverse binding.sb_var then
let binding_to_reverse =
binding
in
match binding_to_reverse.sb_term.st_term with
| Term.Var bound_par ->
if
(Var.is_universal bound_par)
||
(List.exists
(var_equal (make_var bound_par binding_to_reverse.sb_term.st_offset))
vars_to_keep)
then
reverse_bindings subst tail vars_to_keep
else begin
if
List.exists
(fun binding ->
(not (binding == binding_to_reverse))
&&
term_equal binding.sb_term binding_to_reverse.sb_term
&&
List.exists (var_equal binding.sb_var) vars_to_keep
)
subst
then
reverse_bindings subst tail vars_to_keep
else begin
let new_bound_term =
make_term (Term.request_var var_to_reverse.sv_var) var_to_reverse.sv_offset
in
let reversed_subst =
map
(fun binding ->
if binding == binding_to_reverse then
{
sb_var = make_var bound_par binding_to_reverse.sb_term.st_offset;
sb_term = new_bound_term;
}
else if term_equal binding.sb_term binding_to_reverse.sb_term then
{ binding with
sb_term = new_bound_term;
}
else
binding
)
subst
in
check_subst reversed_subst;
reverse_bindings reversed_subst tail vars_to_keep
end
end
| _ ->
reverse_bindings subst tail vars_to_keep
else
find' tail'
in
find' subst
let normalize_var_renamings (subst: subst) : subst =
let renamings =
fold
(fun renamings binding ->
if
binding.sb_var.sv_offset == input_literal_offset
&&
binding.sb_term.st_offset != input_literal_offset
&&
Var.is_universal binding.sb_var.sv_var
then
match binding.sb_term.st_term with
| Term.Var var when Var.is_universal var ->
if
for_all
(fun binding' ->
binding_equal binding binding'
||
not (term_equal binding.sb_term binding'.sb_term)
)
subst
then
(binding.sb_var, var, binding.sb_term.st_offset)
:: renamings
else
renamings
| _ ->
renamings
else
renamings
)
[]
subst
in
let renamed =
List.map
(fun (var, bound_var, bound_offset) ->
make_binding
(make_var bound_var bound_offset)
(make_term (Term.request_var var.sv_var) input_literal_offset)
)
renamings
in
begin
match renamings with
| [] ->
subst
| _ ->
fold
(fun acc binding ->
if
List.exists
(fun (from, _, _) -> var_equal from binding.sb_var)
renamings
then
acc
else begin
match binding.sb_term.st_term with
| Term.Var bound_var ->
begin
try
let from, _, _ =
List.find
(fun (_, to_var, to_offset) ->
binding.sb_term.st_offset == to_offset
&&
Var.equal bound_var to_var
)
renamings
in
let reversed =
make_binding
binding.sb_var
(make_term
(Term.request_var from.sv_var)
from.sv_offset
)
in
reversed :: acc
with
| Not_found ->
binding :: acc
end
| _ ->
binding :: acc
end
)
renamed
subst
end
let remove_context_var_renamings (subst: subst) : subst =
let subst =
normalize_var_renamings subst
in
List.find_all
(fun binding ->
binding.sb_var.sv_offset == input_literal_offset
||
(
List.exists
(fun binding' ->
binding' != binding
&&
binding'.sb_term.st_offset == binding.sb_var.sv_offset
&&
Term.term_contains_var binding'.sb_term.st_term binding.sb_var.sv_var
)
subst
)
)
subst