(*
This file is part of the first order theorem prover Darwin
Copyright (C) 2004, 2005
              The University of Iowa
              Universitaet Koblenz-Landau 

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*)




(*** types ***)


type var = Var.var
type literal = Term.literal
type clause = Term.clause
type subst = Subst.subst
type raw_context_unifier = Context_unifier.raw_context_unifier






(* get the variables of each input literal producing a remainder literal. *)
let vars_of_remainder_input_literals
  (raw_context_unifier: raw_context_unifier)
  (remainder_states: bool array) :
  (Subst.var list) list =

  Array.fold_left
    (fun acc input_partner ->
       if remainder_states.(input_partner.Context_unifier.ip_index) then
         input_partner.Context_unifier.ip_vars :: acc
       else
         acc
    )
    []
    raw_context_unifier.Context_unifier.rcu_space.Context_unifier.cus_input_partners


(* add the parameters of context literals not producing a remainder literal. *)
let add_pars_of_non_remainder_context_literals
  (raw_context_unifier: raw_context_unifier)
  (remainder_states: bool array)
  (old_pars: Subst.var list)
  : Subst.var list =

  Array.fold_left
    (fun acc input_partner ->
       let index =
         input_partner.Context_unifier.ip_index
       in
         if remainder_states.(index) then
           acc
         else
           let context_partner =
             raw_context_unifier.Context_unifier.rcu_context_partners.(index)
           in
           let context_offset =
             Subst.context_literal_offset index
           in
             List.fold_left
               (fun pars par ->
                  Subst.make_var par context_offset :: pars
               )
               acc
               context_partner.Context_unifier.cp_element.Context.el_pars
    )
    old_pars
    raw_context_unifier.Context_unifier.rcu_space.Context_unifier.cus_input_partners




(* reverse all var -> par bindings of the remaining remainder literals,
   except if this would also reverse bindings for protected variables. *)

let reverse_subst
  (subst: subst)
  (remainder_input_vars: Subst.var list list)
  (protected_vars: Subst.var list)
  (shared_vars: Subst.var list)
  : subst =

  List.fold_left
    (fun acc vars ->
       let to_reverse =
         List.find_all
           (fun var ->
              not
                (List.exists
                   (fun var2 ->
                      Subst.var_equal var var2)
                   shared_vars
                )
           )
           vars
       in
         Subst.reverse_bindings
           acc
           to_reverse
           protected_vars
    )
    subst
    remainder_input_vars




(* all fresh parameters created up to now for this substitution are needed
   in order to create new unique fresh parameters. *)

let rec make_v_free
  (subst: subst)
  (vars: Subst.var list)
  (fresh_pars: Subst.var list)
  : subst * Subst.var list =

  match vars with
    | [] ->
        (subst, fresh_pars)

    | var :: tail ->
        (* only bind variables, not parameters *)
        if Var.is_universal var.Subst.sv_var then begin

          match Subst.get subst var with
            | None ->
                let fresh_par =
                  Var.create_parametric (List.length fresh_pars)
                in
                let fresh_term = 
                  Term.request_var fresh_par
                in

                let new_subst =
                  Subst.set' ~recompute:false ~p_preserving:true
                    subst var.Subst.sv_var var.Subst.sv_offset fresh_term Subst.fresh_par_offset
                in
                  make_v_free
                    new_subst
                    tail
                    ((Subst.make_var fresh_par Subst.fresh_par_offset) :: fresh_pars)

            | Some _ ->
                (* already bound (to a fresh var?),
                   probably by make_v_free on another remainder literal *)

                (* :TODO: no, that's not the explanation, check...*)
                make_v_free subst tail fresh_pars
        end

        else
          make_v_free subst tail fresh_pars



(* make all remainder literals containing at least one parameter
   completeley free of universal variables. *)

let rec parameterize_mixed_literals
  (subst: subst)
  (vars: Subst.var list list)
  (fresh_pars: Subst.var list)
  : subst =
  
  match vars with
    | [] ->
        (* all literals have been processed *)
        subst
        
    | head :: tail ->
        (* ignore a literal containing solely universal variables.  *)
        if
          List.for_all
            (fun var ->
               Var.is_universal var.Subst.sv_var
            )
            head
        then begin
          parameterize_mixed_literals subst tail fresh_pars
        end
          
        (* completely parameterize a literal containing at least one parameter. *)
        else begin
          let universal_vars =
            List.find_all
              (fun var ->
                 Var.is_universal var.Subst.sv_var
              )
              head
          in
            
          let (new_subst, new_fresh_pars) =
            make_v_free subst universal_vars fresh_pars
          in
            parameterize_mixed_literals new_subst tail new_fresh_pars
        end



      





(* transforms the context unifier subst to an admissible context unifier. *)
let make_admissible
  (config: Config.config)
  (raw_context_unifier: raw_context_unifier)
  (subst: subst)
  (remainder_states: bool array)
  : subst =

  (* get the variables for each input literal producing a remainder literal. *)
  let (remainder_input_vars: Subst.var list list) =
    vars_of_remainder_input_literals raw_context_unifier remainder_states
  in

  (* get the variables of the remainder literals.
     as the remainder literals are not build yet,
     get the variables in the terms bound to the input literals instead. *)

  let (remainder_literal_vars: Subst.var list list) =
    List.map
      (fun vars ->
         Subst.get_bound_vars subst vars
      )
      remainder_input_vars
  in

  (* get the universal variables occurring in more than one remainder literal. *)
  let (shared_remainder_literal_vars: Subst.var list) =
    Tools.lists_shared Subst.var_equal remainder_literal_vars
  in
    
  (* map the shared variables to fresh parameters. *)
  let (subst, fresh_pars) =
    make_v_free
      subst
      shared_remainder_literal_vars
      []
  in

  (* fixed_vars are the variables which may not be reversed
     in an attempt to make the remainder literals more universal. *)

  let fixed_pars =
    add_pars_of_non_remainder_context_literals 
      raw_context_unifier
      remainder_states
      (
        fresh_pars
        @
        raw_context_unifier.Context_unifier.rcu_space.Context_unifier.cus_shared_vars
        @
        shared_remainder_literal_vars
      )
  in

  (* reverse as many par -> var mappings as possible. *)
  let reversed_subst =
    reverse_subst
      subst
      remainder_input_vars
      fixed_pars
      raw_context_unifier.Context_unifier.rcu_space.Context_unifier.cus_shared_vars
  in
    if Config.mixed_literals config then
      reversed_subst
    else
      (* make all mixed literals universal variable free. *)
      parameterize_mixed_literals
        reversed_subst
        remainder_literal_vars
        fresh_pars