(*
This file is part of the first order theorem prover Darwin
Copyright (C) 2004, 2005, 2006
              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 config = Config.config
type bound = Bound.bound
type var = Var.var
type literal = Term.literal
type clause = Term.clause
type subst = Subst.subst
type context_unifier_space = Context_unifier.context_unifier_space
type context_partners = Context_unifier.context_partners
type raw_context_unifier = Context_unifier.raw_context_unifier



(*** checks ***)




let check_close
  (_config: config)
  (_subst: subst)
  (context_unifier_space: context_unifier_space)
  (context_partners: context_partners)
  : raw_context_unifier option =

  (* precheck in context_unifier_check assures that
     check_close is only called with an empty remainder *)

  if true (*(Config.is_horn config) || not (Context_unifier.is_remainder' subst)*) then begin
    let closing_context_unifier =
      Context_unifier.create_context_unifier
        context_unifier_space
        (Array.copy context_partners)
        false
    in
      Some closing_context_unifier
  end

  else
    None



let check_assert
  (config: config)
  (bound: bound)
  (context: Context.context)
  (subst: subst)
  (context_unifier_space: context_unifier_space)
  (context_partners: context_partners)
  (is_element_incomplete: bool) :
  raw_context_unifier option =
  
  (* is the assert literal parameter free? *)
  let is_assert_valid_at (i : int) : bool =
    let assert_vars =
      Subst.get_bound_vars
        subst
        context_unifier_space.Context_unifier.cus_input_partners.(i).Context_unifier.ip_vars
    in
      List.for_all 
        (fun var ->
           Var.is_universal var.Subst.sv_var
        )
        assert_vars
  in
  
  (* has the unifier an empty remainder
     and leaves the assert literal parameter free?
     returns also the index of the assert literal. *)

  (* precheck in context_unifier_check assures that
     check_close is only called with an empty remainder,
     so check only that assert literal instance is universal. *)

  let rec is_valid_at (i: int) (assert_index: int option) : bool * int option =
    if i >= Array.length context_partners then begin
(*      not (Context_unifier.is_remainder' subst), assert_index*)
(*      true, assert_index*)
      failwith "Context_unifier_check.is_valid_at: no assert literal"
    end

    else begin
      let input_index =
        context_unifier_space.Context_unifier.cus_input_partners.(i).Context_unifier.ip_index
      in
      let context_partner =
        context_partners.(input_index)
      in
        (* no parameter in the assert literal? *)
        if context_partner.Context_unifier.cp_element == Context.assert_element then begin
          (* the is_assert_valid_at check can be really expensive, so we want to avoid it if possible *)
          if
            (* parametric propagation on lemmas *)
            (
              Config.lemma_parametric_assert config > 0
              &&
              context_unifier_space.Context_unifier.cus_lemma_learned >= Config.lemma_parametric_assert config
            )
            ||
            (* if all context literals are universal the instance must be universal *)
            Context.is_universal context
            ||
            (* if all used context literals are universal the instance must be universal *)
            (Tools.array_for_all (fun cp -> cp.Context_unifier.cp_element.Context.el_pars == []) context_partners)
            ||
            (* ok, we have to do the expensive (yes, it is) check *)
            is_assert_valid_at i
          then
            (*is_valid_at (i + 1) (Some i)*)
            trueSome i
          else begin
            false, assert_index
          end
        end

        (* empty remainder? *)
        else begin
            (*
          if
            Context_unifier.is_remainder 
              subst
              context_partner.Context_unifier.cp_element(*.Context.el_pars*)
              input_index
          then begin
            false, assert_index
          end
        
          (* yes, so continue *)
          else*)

            is_valid_at (i + 1) assert_index
        end
    end
  in
(*    if (not (Config.is_horn config)) && Context_unifier.is_remainder' subst then
      None

    else*)

    match is_valid_at 0 None with
      | false, _ ->
          None

      | trueNone ->
          failwith "Context_unifier_check.check_assert"

      | trueSome assert_index ->
          let assert_clause_literal =
            context_unifier_space.Context_unifier.cus_input_partners.(assert_index).Context_unifier.ip_literal
          in
            (* this should be filtered in context_unifier_searach *)
            if
              Const.debug
              &&
              (Config.neg_assert_candidates config = Flags.NAC_Ignore)
              &&
              (not assert_clause_literal.Term.sign)
            then begin
              failwith "Context_unifier_check.check_assert: neg_assert";
            end;

            (* - within bound -> keep
               - exceeding bound and not delayed restarting -> register
               - exceeding bound and delayed restarting -> keep
               - exceeding bound and lookahead -> keep
               - otherwise: drop
            *)

            let complexity =
              bound#get_complexity_subst
                assert_clause_literal
                subst
                Subst.input_literal_offset
            in
            let keep, exceeding =
              (* exceeding bound *)
              if bound#exceeds complexity then begin
                (* exceeding bound, horn, positive, not delayed restarting -> register *)
                if Config.is_horn config && assert_clause_literal.Term.sign then begin
                  match Config.restart config with
                    | Flags.RS_Eager
                    | Flags.RS_Lazy ->
                        ignore (bound#register complexity
                                (Context_unifier.get_creation_choice_point context_partners)
                                : bool);
                    | Flags.RS_Delayed ->
                        ()
                end;
                
                (* this context element is already incomplete,
                   so all its context unifiers will be recomputed anyway if needed. *)

                if is_element_incomplete then
                  falsetrue

                (* keep for Delayed *)
                else if Config.restart config = Flags.RS_Delayed then
                  truetrue

                (* keep for exceeding lookeahead *)
                else if Config.lookahead_exceeding config then
                  truetrue

                (* otherwise drop for the time being *)
                else
                  falsetrue
              end

              (* within bound -> keep *)
              else
                truefalse
            in
              if keep then
                (* this candidate is obeying the bound
                   or needed for the lookahead check *)

                let raw_context_unifier = 
                  Context_unifier.create_context_unifier
                    context_unifier_space
                    (Array.copy context_partners)
                    exceeding
                in
                  Some raw_context_unifier
              else
                None




let check_split
  (config: config)
  (bound: bound)
  (subst: subst)
  (context_unifier_space: context_unifier_space)
  (context_partners: context_partners)
  (is_element_incomplete: bool)
  : raw_context_unifier option =

  let rec min_complexity_at (i: int) (acc: Bound.complexity option) : Bound.complexity option =
    (* all remainder literals exceed the bound? *)
    if i >= Array.length context_partners then begin
      acc
    end

    else
      let input_index =
        context_unifier_space.Context_unifier.cus_input_partners.(i).Context_unifier.ip_index
      in
      let context_partner =
        context_partners.(input_index)
      in
      let remainder =
        not context_partner.Context_unifier.cp_empty_remainder
        ||
        Context_unifier.is_remainder 
          subst
          context_partner.Context_unifier.cp_element
          input_index
      in
        if remainder then begin
          (* a remainder literal, so check its bound complexity *)
          let clause_literal =
            context_unifier_space.Context_unifier.cus_input_partners.(i).Context_unifier.ip_literal
          in
          let complexity =
            bound#get_complexity_subst
              clause_literal
              subst
              Subst.input_literal_offset
          in
            if bound#exceeds complexity then begin
              let new_acc =
                match acc with
                  | Some min_complexity when
                      bound#compare_complexity min_complexity complexity <= 0 ->
                      
                      acc
                    
                  | _ ->
                      Some complexity
              in
                min_complexity_at (i + 1) new_acc
            end

            else begin
              raise Exit
            end
        end

        else
          min_complexity_at (i + 1) acc
  in
    
    (* keep only split candidates within the deepening bound *)
    begin
      try
        match min_complexity_at 0 None with
          | None ->
              (* no remainder literal?!?! *)
              print_endline (Subst.subst_to_string subst);
              print_endline (Context_unifier.context_unifier_to_string
                               context_unifier_space context_partners);
              failwith "Context_unifier_check.check_split.min_complexity_at I"

          | Some complexity ->
              begin
                match Config.restart config with
                  | Flags.RS_Eager
                  | Flags.RS_Lazy ->
                      (* register the minimum complexity for the potential next restart *)
                      if
                        bound#register
                          complexity
                          (Context_unifier.get_creation_choice_point context_partners) 
                      then begin
                        None
                      end
                          
                      else
                        (* min complexity is within the bound?!? *)
                        failwith "Context_unifier_check.check_split.min_complexity_at II"

                  | Flags.RS_Delayed ->
                      (* exceeding bound and delayed restarting *)
                      (* this context element is already incomplete,
                         so all its context unifiers will be recomputed anyway. *)

                      if is_element_incomplete then
                        None

                      (* keep all split candidates *)
                      else
                        let raw_context_unifier =
                          Context_unifier.create_context_unifier
                            context_unifier_space
                            (Array.copy context_partners)
                            true
                        in
                          Some raw_context_unifier
              end
                
      with
        | Exit ->
            (* valid complexity found *)
            let raw_context_unifier =
              Context_unifier.create_context_unifier
                context_unifier_space
                (Array.copy context_partners)
                false
            in
              Some raw_context_unifier
    end