(*  File: apply.ml

    AIFAD - Automated Induction of Functions over Algebraic Datatypes

    Author: Markus Mottl
    email:  markus.mottl@gmail.com
    WWW:    http://www.ocaml.info

    Copyright (C) 2002  Austrian Research Institute for Artificial Intelligence
    Copyright (C) 2003- Markus Mottl

    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
*)

(* $Id: apply.ml,v 1.17 2006/01/17 00:23:37 mottl Exp $ *)

open Format

open Pcre

open Utils
open Algdt_types
open Algdt_utils
open Algdt_pp
open Model_data
open C45_io
open Data_io
open Typing

(* Apply model to data *)

let handle_model pred_oc = function
  | `Model (dispec_info, { ispec = cispec; cnstr_tbl = ccnstr_tbl }, model) ->
      let dispec = dispec_info.ispec in
      let dfspec, _ = flatten_ispec dispec_info.ispec in
      let cfspec, _ = flatten_ispec cispec in
      let ppf = formatter_of_out_channel pred_oc in
      let lexbuf = Lexing.from_channel stdin in
      let cnv_drhs = make_cnv_data dispec_info dfspec in
      let cnv_many_prod_el = snd (make_deco_dsum_cnv cfspec cispec) in
      let module CIDataPP =
        Make_IData (struct let cnstr_tbl = ccnstr_tbl end) in
      let rec loop () =
        match read_lhs lexbuf dispec dfspec cnv_drhs with
        | Some fdsums ->
            let res = Model_utils.apply_model model fdsums in
            CIDataPP.pp_data ppf (cnv_many_prod_el init_prod_els res).(0);
            pp_force_newline ppf ();
            loop ()
        | None -> pp_print_flush ppf () in
      loop ()
  | `C45Model ((_, _, _, pat), dispec_info, cispec_info, mv, model) ->
      let rex = regexp ~flags:[`DOTALL] pat in
      let dcnstr_htbl = dispec_info.cnstr_htbl in
      let ccnstr_tbl = cispec_info.cnstr_tbl in
      let n_lines_ref = ref 1 in
      let find_cnstr ix str = FDAtom (Hashtbl.find dcnstr_htbl (ix + 1, str)) in
      let print_app fdsums =
        match (Model_utils.apply_model model fdsums).(0) with
        | FDAtom cnstr ->
            output_string pred_oc ccnstr_tbl.(1).(cnstr);
            output_char pred_oc '\n'
        | FDStrct _ -> assert false (* impossible *) in
      let full_match = false in
      match mv with
      | Ignore ->
          foreach_line (fun line ->
            try
              print_app (Array.mapi find_cnstr (extract ~full_match ~rex line));
              incr n_lines_ref;
            with Not_found ->
              let msg =
                sprintf
                  "invalid input line (maybe missing values?): %d\n"
                  !n_lines_ref in
              failwith msg)
      | MProb ->
          let cnv_cnstr ix str =
            if str = "?" then raise Exit
            else find_cnstr ix str in
          foreach_line (fun line ->
            try
              let fdsum =
                try
                  let subs =
                    Array.mapi cnv_cnstr (extract ~full_match ~rex line) in
                  FDStrct (1, subs)
                with Exit -> dummy_fdsum  (* dummy_fdsum = MV! *) in
              print_app [| fdsum |];
              incr n_lines_ref
            with Not_found ->
              failwith (sprintf "invalid input line: %d\n" !n_lines_ref))
      | Flat ->
          let cnv_cnstr ix str =
            if str = "?" then dummy_fdsum  (* dummy_fdsum = MV! *)
            else find_cnstr ix str in
          foreach_line (fun line ->
            try
              print_app (Array.mapi cnv_cnstr (extract ~full_match ~rex line));
              incr n_lines_ref
            with Not_found ->
              failwith (sprintf "invalid input line: %d\n" !n_lines_ref))
      | LiftAll ->
          let cnv_cnstr ix str =
            if str = "?" then dummy_fdsum  (* dummy_fdsum = MV! *)
            else FDStrct (1, [| find_cnstr ix str |]) in
          foreach_line (fun line ->
            try
              print_app (Array.mapi cnv_cnstr (extract ~full_match ~rex line));
              incr n_lines_ref
            with Not_found ->
              failwith (sprintf "invalid input line: %d\n" !n_lines_ref))

let apply model_name maybe_pred_name =
  let pred_oc, close_pred_oc =
    match maybe_pred_name with
    | Some pred_name -> let oc = open_out pred_name in oc, close_out
    | None -> stdout, flush in
  let mdat = open_mdat model_name in
  unwind_protect (handle_model pred_oc) mdat close_pred_oc pred_oc
