(*
This file is part of the first order theorem prover Darwin
Copyright (C) 2006
              The University of Iowa

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 literal = Term.literal
type clause = Term.clause


exception EMPTY_CLAUSE of clause



(*** functions ***)



(* for each clause do unit subsumption / resolution *)
let rec simplify' ~(print:bool)
  (* processed clauses *)
  (processed: clause list)
  (* clauses to simplify *)
  (clauses: clause list)
  (* index of unit clauses to use for simplification right now. *)
  (index : literal Term_indexing.index)
  (* was a clause simplified? *)
  (simplified: bool)
  :
  (* processed * simplified *)
  clause list * bool =

  match clauses with
    | [] ->
        (* *)
        processed, simplified

    | clause :: tail ->
        begin
          match clause with
            | _ :: [] ->
                (* just keep any unit clause.
                   if several unit clauses subsume each other we ignore this,
                   this is efficiently handled by Assert anyway.
                   otherwise, if the same literal exists several times,
                   we would make sure to keep at least one. *)

                simplify' ~print:print (clause :: processed) tail index simplified

            | _ ->
                begin
                  (* subsumed by a unit clause? *)
                  if
                    List.exists
                      (fun literal ->
                        match (index#find literal)#find_generalization ~p_preserving:false literal.Term.atom with
                          | None -> false
                          | Some _ ->
                              if print then begin
                                print_endline ("Subsumed: " ^ Term.literal_to_string literal ^ " --> "
                                              ^ Term.clause_to_string clause);
                              end;
                              true
                      )
                      clause
                  then begin
                    (* subsuming unit clause exists. *)
                    simplify' ~print:print processed tail index true
                  end

                  (* try to resolve *)
                  else begin
                      (* find resolving unit clauses for each clause literal,
                       returns the (simplified) clause, and if it was really simplified . *)

                    let rec resolve (processed: literal list) (to_simplify: literal list)
                        (simplified: bool) : literal list * bool =
                      match to_simplify with
                        | [] ->
                            if simplified then begin
                               if print then begin
                                print_endline (Term.clause_to_string clause ^ " --> " ^ Term.clause_to_string processed);
                              end;
                              processed, simplified
                            end
                            else
                              clause, simplified

                        | literal :: tail ->
                            begin
                              let resolving_clause =
                                (index#find (Term.request_negated_literal ~insert_db:false literal))#find_generalization
                                  ~p_preserving:false literal.Term.atom
                              in
                                match resolving_clause with
                                  | None ->
                                      resolve (literal :: processed) tail simplified

                                  | Some resolving_literal ->
                                      if print then begin
                                        print_endline ("Resolved: " ^ Term.literal_to_string resolving_literal
                                        ^ " in " ^ Term.clause_to_string clause);
                                      end;
                                      resolve processed tail true
                            end
                    in
                    let clause', simplified' =
                      resolve [] clause false
                    in
                      match clause' with
                        | [] ->
                            raise (EMPTY_CLAUSE clause)

                        | literal :: [] ->
                            (* new unit clause *)
                            (index#find literal)#add literal.Term.atom literal;
                            simplify' ~print:print (clause' :: processed) tail index (simplified || simplified')

                              
                        | _ ->
                            simplify' ~print:print (clause' :: processed) tail index (simplified || simplified')
                    end
                end
        end




let simplify ~(print: bool) (clauses: clause list) : clause list =
  if print then begin
    print_endline ("Preprocessing: Unit");
  end;

  (* first build index of unit clauses *)
  let index =
    Discrimination_tree.create_literal_index false
  in
    List.iter
      (fun clause ->
         match clause with
           | literal :: [] ->
               (index#find literal)#add literal.Term.atom literal

           | _ ->
               ()
      )
      clauses;

  (* simplify *)
  let clauses', simplified =
    simplify' ~print:print [] clauses index false
  in
    if print then begin
      print_newline ();
    end;

  (* get original order of clauses back *)
  if simplified then
    List.rev clauses'
  else
    clauses