(******************************************************************************
Rainbow, a termination proof certification tool
See the COPYRIGHTS and LICENSE files.

- Frederic Blanqui, 2006-05-31
- Adam Koprowski, 2007-04-20, adding matrix interpretations
- Ducas Leo, 2007-08-10

from xml to the rainbow internal representation of termination proofs
******************************************************************************)

open Proof;;
open Xml;;
open Pb_of_xml;;
open Libxml;;
open Error;;

(*****************************************************************************)
(* Number, vectors and matrices *)
(*****************************************************************************)

let arcticNumber = function
  | Element ("minus_infinite", _, _, _) -> MinusInf
  | Element ("finite", _, _, x :: _) -> Fin (get_int x)
  | x -> error_xml x "not an arcticNumber";;

let tropicalNumber = function
  | Element ("plus_infinite", _, _, _) -> PlusInf
  | Element ("finite", _, _, x :: _) -> TroFin (get_int x)
  | x -> error_xml x "not a tropicalNumber";;

let natVector = List.map (get_first_son "velem" get_nonNegativeInteger);;

let arcticVector = List.map (get_first_son "velem" arcticNumber);;
let tropicalVector = List.map (get_first_son "velem" tropicalNumber);;

let natMatrix = List.map (get_sons "row" natVector);;

let arcticMatrix = List.map (get_sons "row" arcticVector);;
let tropicalMatrix = List.map (get_sons "row" tropicalVector);;

(*****************************************************************************)
(* Polynomials *)
(*****************************************************************************)

let monomial = function
  | Element ("monomial", _, _, x :: xs) ->
      get_first_son "coef" get_nonNegativeInteger x,
      List.map (get_first_son "var" get_nonNegativeInteger) xs
  | x -> error_xml x "not a monomial";;

let polynomial = get_sons "polynomial" (List.map monomial);;

(*****************************************************************************)
(* Polynomial and matrix interpretations *)
(*****************************************************************************)

let polynomialMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) -> fun_symbol x1, polynomial x2
  | x -> error_xml x "not a polynomialMapping";;

let dimension = get_first_son "dimension" get_positiveInteger;;

let funMatrixInterpretation = function
  | Element ("mi_fun", _, _, x :: xs) ->
      { mi_const = get_sons "const" natVector x;
	mi_args = List.map (get_sons "arg" natMatrix) xs }
  | x -> error_xml x "not a funMatrixInterpretation";;

let matrixMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) ->
      fun_symbol x1, funMatrixInterpretation x2
  | x -> error_xml x "not a matrixMapping";;

let mi_map f = get_sons "mi_map" (List.map f);;

let funArcticInterpretation = function
  | Element ("mi_fun", _, _, x :: xs) ->
      { mi_const = get_sons "const" arcticVector x;
	mi_args = List.map (get_sons "arg" arcticMatrix) xs }
  | x -> error_xml x "not a funArcticInterpretation";;

let funTropcialInterpretation = function
  | Element ("mi_fun", _, _, x :: xs) ->
      { mi_const = get_sons "const" tropicalVector x;
	mi_args = List.map (get_sons "arg" tropicalMatrix) xs }
  | x -> error_xml x "not a funTropicalInterpretation";;

let arcticMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) ->
      fun_symbol x1, funArcticInterpretation x2
  | x -> error_xml x "not an arcticMapping";;

let tropicalMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) ->
      fun_symbol x1, funTropcialInterpretation x2
  | x -> error_xml x "not a tropicalMapping";;

(*****************************************************************************)
(* Argument filterings *)
(*****************************************************************************)

let get_bool = function
  | Element ("true",_, _, _) -> true
  | Element ("false",_, _, _) -> false
  | x -> error_xml x "not a boolean";;

let vectorBool = List.map get_bool;;

let argBoolMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) ->
      fun_symbol x1, get_sons "bool" vectorBool x2
  | x -> error_xml x "not an argFilterMapping";;

let proj = function
  | [] -> None
  | x :: _ -> Some (get_nonNegativeInteger x);;

let argProjMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) ->
      fun_symbol x1, get_sons "proj" proj x2
  | x -> error_xml x "not an argProjMapping";;

let arg = get_first_son "arg" get_nonNegativeInteger;;

let listArg = List.map arg;;

let argPermMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) ->
      fun_symbol x1, get_sons "perm" listArg x2
  | x -> error_xml x "not an argPermMapping";;

let filter = function
  | Element ("bool", _, _, xs) -> Bool (vectorBool xs)
  | Element ("proj", _, _, x :: _) -> Proj (get_nonNegativeInteger x)
  | Element ("perm", _, _, xs) -> Perm (listArg xs)
  | x -> error_xml x "not a filter";;

let argFilterMapping = function
  | Element ("mapping", _, _, x1 :: []) -> fun_symbol x1, None
  | Element ("mapping", _, _, x1 :: x2 :: _) -> fun_symbol x1, Some (filter x2)
  | x -> error_xml x "not an argFilterMapping";;

let af_def f = get_sons "def" (List.map f);;

let simpleProjMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: _) ->
      fun_symbol x1, get_first_son "proj" get_nonNegativeInteger x2
  | x -> error_xml x "not a simpleProjMapping";;

(*****************************************************************************)
(* RPO *)
(*****************************************************************************)

let get_status = function
  | Element ("lex", _, _, _) -> Lex
  | Element ("mul", _, _, _) -> Mul
  | x -> error_xml x "not a status";;

let status = get_first_son "status" get_status;;

let precedence = get_first_son "prec" get_int;;

let statusMapping = function
  | Element ("mapping", _, _, x1 :: x2 :: x3 :: _) ->
      fun_symbol x1, (status x2, precedence x3)
  | x -> error_xml x "not a statusMapping";;

(*****************************************************************************)
(* Reduction orderings *)
(*****************************************************************************)

let rec reductionOrder = function
  | Element ("poly_int", _, _, xs) ->
      PolyInt (List.map polynomialMapping xs)
  | Element ("matrix_int", _, _, x1 :: x2 :: _) ->
      MatrixInt { mi_dim = dimension x1; mi_int = mi_map matrixMapping x2 }
  | Element ("arctic_int", _, _, x1 :: x2 :: _) ->
      ArcticInt { mi_dim = dimension x1; mi_int = mi_map arcticMapping x2 }
  | Element ("tropical_int", _, _, x1 :: x2 :: _) ->
      TropicalInt { mi_dim = dimension x1; mi_int = mi_map tropicalMapping x2 }
  | Element ("arctic_bz_int", _, _, x1 :: x2 :: _) ->
      ArcticBZInt { mi_dim = dimension x1; mi_int = mi_map arcticMapping x2 }
  | Element ("arg_bool", _, _, x1 :: x2 :: _) ->
      ArgBoolOrd (af_def argBoolMapping x1, order x2)
  | Element ("arg_proj", _, _, x1 :: x2 :: _) ->
      ArgProjOrd (af_def argProjMapping x1, order x2)
  | Element ("arg_filter", _, _, x1 :: x2 :: _) ->
      ArgFilterOrd (af_def argFilterMapping x1, order x2)
  | Element ("rpo", _, _, xs) -> Rpo (List.map statusMapping xs)
  | x -> error_xml x "not a reductionOrder"

and order x = get_first_son "order" reductionOrder x;;

(*****************************************************************************)
(* Dependency graph approximations *)
(*****************************************************************************)

let overDpGraph = function
  | Element ("hde", _, _, _) -> HDE
  | Element ("hde_marked", _, _, _) -> HDE_Marked
  | Element ("unif", _, _, _) -> Unif
  | (Element _ | PCData _) as x -> error_xml x "not an overDPGraph";;

(*****************************************************************************)
(* Counter-examples *)
(*****************************************************************************)

let trs_pos = get_sons "position" listArg;;

let trsModStep = function
  | Element ("step", _, _, x1 :: x2 :: _) ->
      { cet_mod_step_pos = trs_pos x1;
	cet_mod_step_rule = trsRule x2 }
  | x -> error_xml x "not a trsModStep";;

let trsModSteps = List.map trsModStep;;

let trsStep = function
  | Element ("step", _, _, x1 :: x2 :: []) ->
      { cet_step_mod_steps = [];
	cet_step_pos = trs_pos x1;
	cet_step_rule = trsRule x2 }
  | Element ("step", _, _, x1 :: x2 :: x3 :: _) ->
      { cet_step_mod_steps = get_sons "modulo" trsModSteps x1;
	cet_step_pos = trs_pos x2;
	cet_step_rule = trsRule x3 }
  | x -> error_xml x "not a trsStep";;

let trsSteps = List.map trsStep;;

let trsCounterExample = function
  | Element ("var_cond", _, _, _) -> CET_var_cond
  | Element ("loop", _, _, x1 :: x2 :: x3 :: x4 :: _) ->
      CET_loop { cet_start = get_first_son "start" term x1;
		 cet_steps = get_sons "steps" trsSteps x2;
		 cet_mod = get_sons "modulo" trsModSteps x3;
		 cet_pos = trs_pos x4 }
  | Element ("loop", _, _, x1 :: x2 :: x3 :: _) ->
      CET_loop { cet_start = get_first_son "start" term x1;
		 cet_steps = get_sons "steps" trsSteps x2;
		 cet_mod = [];
		 cet_pos = trs_pos x3 }
  | x -> error_xml x "not a trsCounterExample";;

let srs_pos = get_first_son "position" get_nonNegativeInteger;;

let srsModStep = function
  | Element ("step", _, _, x1 :: x2 :: _) ->
      { ces_mod_step_pos = srs_pos x1;
	ces_mod_step_rule = srsRule x2 }
  | x -> error_xml x "not an srsModStep";;

let srsModSteps = List.map srsModStep;;

let srsStep = function
  | Element ("step", _, _, x1 :: x2 :: []) ->
      { ces_step_mod_steps = [];
	ces_step_pos = srs_pos x1;
	ces_step_rule = srsRule x2 }
  | Element ("step", _, _, x1 :: x2 :: x3 :: _) ->
      { ces_step_mod_steps = get_sons "modulo" srsModSteps x1;
	ces_step_pos = srs_pos x2;
	ces_step_rule = srsRule x3 }
  | x -> error_xml x "not a srsStep";;

let srsSteps = List.map srsStep;;

let srsCounterExample = function
  | Element ("loop", _, _, x1 :: x2 :: x3 :: x4 :: _) ->
      CES_loop { ces_start = get_sons "start" word x1;
		 ces_steps = get_sons "steps" srsSteps x2;
		 ces_mod = get_sons "modulo" srsModSteps x3;
		 ces_pos = srs_pos x4 }
  | Element ("loop", _, _, x1 :: x2 :: x3 :: _) ->
      CES_loop { ces_start = get_sons "start" word x1;
		 ces_steps = get_sons "steps" srsSteps x2;
		 ces_mod = [];
		 ces_pos = srs_pos x3 }
  | x -> error_xml x "not an srsCounterExample";;

let counterExample = function
  | Element ("trs_counter_example", _, _, x :: _) ->
      CE_trs (trsCounterExample x)
  | Element ("srs_counter_example", _, _, x :: _) ->
      CE_srs (srsCounterExample x)
  | x -> error_xml x "not a counterExample";;

(*****************************************************************************)
(* Proof kinds *)
(*****************************************************************************)

let rec proofKind = function
  | Element ("trivial", _, _, _) -> Trivial
  | Element ("manna_ness", _, _, x1 :: x2 :: _ :: _) ->
      MannaNess (true, order x1, proof x2)
  | Element ("manna_ness", _, _, x1 :: x2 :: _) ->
      MannaNess (false, order x1, proof x2)
  | Element ("dp", _, _, x :: _) -> DP (proof x)
  | Element ("mark_symbols", _, _, x :: _) -> MarkSymb (proof x)
  | Element ("decomp", _, _, x :: xs) ->
      Decomp (get_first_son "graph" overDpGraph x, List.map component xs)
  | Element ("arg_bool", _, _, x1 :: x2 :: _) ->
      ArgBool (af_def argBoolMapping x1, proof x2)
  | Element ("arg_proj", _, _, x1 :: x2 :: _) ->
      ArgProj (af_def argProjMapping x1, proof x2)
  | Element ("arg_perm", _, _, x1 :: x2 :: _) ->
      ArgPerm (af_def argPermMapping x1, proof x2)
  | Element ("arg_filter", _, _, x1 :: x2 :: _) ->
      ArgFilter (af_def argFilterMapping x1, proof x2)
  | Element ("as_trs", _, _, x :: _) -> AsTrs (proof x)
  | Element ("as_srs", _, _, x :: _) -> AsSrs (proof x)
  | Element ("srs_rev", _, _, x :: _) -> SrsRev (proof x)
  | Element ("trs_rev", _, _, x :: _) -> TrsRev (proof x)
  | Element ("flat_cc", _, _, x :: _) -> FlatCC (proof x)
  | Element ("root_lab", _, _, x :: _) -> RootLab (proof x)
  | Element ("unlab", _, _, x :: _) -> Unlab (proof x)
  | Element ("subterm_crit", _, _, x1 :: x2 :: _) ->
      SubtermCrit (af_def simpleProjMapping x1, proof x2)
  | x -> error_xml x "not a proofKind"

and proof x = get_first_son "proof" proofKind x

and component = function
  | Element ("component", _, _, x1 :: x2 :: _) ->
      get_sons "rules" trsRules x1, Some (proof x2)
  | Element ("component", _, _, x :: _) ->
      get_sons "rules" trsRules x, None
  | x -> error_xml x "not a component";;

(*****************************************************************************)
(* Certificates *)
(*****************************************************************************)

let certificate = function
  | Element ("proof", _, _, x :: _) -> Proof (proofKind x)
  | Element ("counter_example", _, _, x :: _) ->
      Counter_example (counterExample x)
  | x -> error_xml x "not a certificate";;