(*  File: ds_entropy.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: ds_entropy.ml,v 1.11 2006/01/17 00:23:37 mottl Exp $ *)

open Utils
open Algdt_types
open Algdt_utils
open Entropy_utils

type split_info = var array

(* TODO: eliminate redundant constructors for more efficiency *)
let rec destr_vars_entropy ix histos var_samples ofs len =
  if len <= 1 then 0.0
  else
    let histo = histos.(ix) in
    if ix = 0 then
      if histo_is_redundant histo len then 0.0 else calc_entropy histo len
    else if histo_is_redundant histo len then
      destr_vars_entropy (ix - 1) histos var_samples ofs len
    else (
      let init_e = calc_entropy histo len in
      let make_histo ix = Array.make (Array.length histos.(ix)) 0 in
      let next_len = Array.length histos - 1 in
      let make_new_histos _ = Array.init next_len make_histo in
      let n_cnstrs = Array.length histo in
      let many_new_histos = Array.init n_cnstrs make_new_histos in
      let offsets = Array.make n_cnstrs 0 in
      ignore (
        array_fold_lefti (fun i n freq ->
          offsets.(i) <- ofs + n; n + freq) 0 histo);
      let last_cnstr = n_cnstrs - 1 in
      let samples = var_samples.(ix) in
      let f_len = float len in
      let rec loop e cur_cnstr cnt =
        if cnt = 0 then
          let new_e =
            let freq = histo.(cur_cnstr) in
            let split_e =
              let new_ofs = offsets.(cur_cnstr) - freq in
              destr_vars_entropy
                (ix - 1) many_new_histos.(cur_cnstr) var_samples new_ofs freq in
            e +. float freq /. f_len *. split_e in
          if cur_cnstr = last_cnstr then new_e
          else
            let next_cnstr = cur_cnstr + 1 in
            let next_cnt =
              histo.(next_cnstr) - offsets.(next_cnstr) + offsets.(cur_cnstr) in
            loop new_e next_cnstr next_cnt
        else (
          let sample_ix = offsets.(cur_cnstr) in
          let sample = samples.(sample_ix) in
          let cnstr = fdsum_cnstr sample in
          let dst_sample_ix = offsets.(cnstr) in
          offsets.(cnstr) <- dst_sample_ix + 1;
          let new_histos = many_new_histos.(cnstr) in
          if cnstr = cur_cnstr then (
            for var_ix = 0 to ix - 1 do
              let dst_sample = var_samples.(var_ix).(sample_ix) in
              let dst_cnstr = fdsum_cnstr dst_sample in
              let new_histo = new_histos.(var_ix) in
              new_histo.(dst_cnstr) <- new_histo.(dst_cnstr) + 1
            done;
            loop e cnstr (cnt - 1))
          else (
            samples.(sample_ix) <- samples.(dst_sample_ix);
            for var_ix = 0 to ix - 1 do
              let dst_samples = var_samples.(var_ix) in
              let dst_sample = dst_samples.(sample_ix) in
              let other_dst_sample = dst_samples.(dst_sample_ix) in
              dst_samples.(dst_sample_ix) <- dst_sample;
              dst_samples.(sample_ix) <- other_dst_sample;
              let new_histo = new_histos.(var_ix) in
              let dst_cnstr = fdsum_cnstr dst_sample in
              new_histo.(dst_cnstr) <- new_histo.(dst_cnstr) + 1
            done;
            loop e cur_cnstr cnt)) in
      loop init_e 0 histo.(0))

let var_entropy _ var = calc_entropy var.histo (Array.length var.samples)

let copy_samples_of_var var = Array.copy var.samples
let copy_histo_of_var var = Array.copy var.histo

let vars_entropy _ vars =
  let n_vars = Array.length vars in
  if n_vars = 0 then 0.0
  else
    let var_samples = Array.map copy_samples_of_var vars in
    let histos = Array.map copy_histo_of_var vars in
    let n_samples = Array.length vars.(0).samples in
    destr_vars_entropy (n_vars - 1) histos var_samples 0 n_samples

let calc_split_infos _ dvar cfspec cvars = split_vars dvar cfspec cvars
let calc_split_info_entropy = vars_entropy
