let TRAPOUT cont mat_thm ex_thms fm =   
  try  
    cont mat_thm ex_thms  
  with Isign (false_thm,ex_thms) ->  
    let ftm = mk_eq(fm,f_tm) in  
    let fthm = CONTR ftm false_thm in
    let ex_thms' = sort (fun x y -> xterm_lt (fst y) (fst x)) ex_thms in
    let fthm' = rev_itlist CHOOSE ex_thms' fthm in
      fthm';;

let get_repeats l = 
  let rec get_repeats l seen ind = 
    match l with
        [] -> []
      | h::t -> 
          if mem h seen then ind::get_repeats t seen (ind + 1)
          else get_repeats t (h::seen) (ind + 1) in
    get_repeats l [] 0;;

let subtract_index l = 
  let rec subtract_index l ind = 
    match l with
        [] -> []
      | h::t -> (h - ind):: (subtract_index t (ind + 1)) in
    subtract_index l 0;;

(*
subtract_index (get_repeats [1; 2; 1; 2 ; 3])
*)

let remove_column n isigns_thm = 
  let thms = interpsigns_thms2 isigns_thm in
  let l,r = chop_list n thms in
  let thms' = l @ tl r in
    mk_interpsigns thms';;

let REMOVE_COLUMN n mat_thm = 
  let rol_thm,all_thm = interpmat_thms mat_thm in
  let ints,part,signs = dest_all2 (concl all_thm) in
  let part_thm = PARTITION_LINE_CONV (snd (dest_comb part)) in
  let isigns_thms = CONJUNCTS (REWRITE_RULE[ALL2;part_thm] all_thm) in
  let isigns_thms' = map (remove_column n) isigns_thms in 
  let all_thm' = mk_all2_interpsigns part_thm isigns_thms' in  
  let all_thm'' = REWRITE_RULE[GSYM part_thm] all_thm' in
  let mat_thm' = mk_interpmat_thm rol_thm all_thm'' in
    mat_thm';;

let SETIFY_CONV mat_thm = 
  let _,pols,_ = dest_interpmat(concl mat_thm) in
  let pols' = dest_list pols in
  let sols = setify (dest_list pols) in
  let indices = map (fun p -> try index p sols with _ -> failwith "SETIFY: no index") pols' in 
  let subtract_cols = subtract_index (get_repeats indices) in
    rev_itlist REMOVE_COLUMN subtract_cols mat_thm;;


(*
SETIFY_CONV 
(ASSUME `interpmat [] [(\x. x + &1); (\x. x + &1); (\x. x + &2); (\x. x + &3); (\x. x + &1); (\x. x + &2)][[Pos; Pos; Pos; Pos; Neg; Zero]]`)

*)
(*
let duplicate_column i j isigns_thm = 
  let thms = interpsigns_thms2 isigns_thm in
  let col = ith i thms in
  let l,r = chop_list j thms in
  let thms' = l @ (col :: r) in
    mk_interpsigns thms';;

let DUPLICATE_COLUMN i j mat_thm = 
  let rol_thm,all_thm = interpmat_thms mat_thm in
  let ints,part,signs = dest_all2 (concl all_thm) in
  let part_thm = PARTITION_LINE_CONV (snd (dest_comb part)) in
  let isigns_thms = CONJUNCTS (REWRITE_RULE[ALL2;part_thm] all_thm) in
  let isigns_thms' = map (duplicate_column i j) isigns_thms in 
  let all_thm' = mk_all2_interpsigns part_thm isigns_thms' in  
  let all_thm'' = REWRITE_RULE[GSYM part_thm] all_thm' in
  let mat_thm' = mk_interpmat_thm rol_thm all_thm'' in
    mat_thm';;
*)

let duplicate_columns new_cols isigns_thm = 
  let thms = interpsigns_thms2 isigns_thm in
  let thms' = map (fun i -> el i thms) new_cols in
    mk_interpsigns thms';;

let DUPLICATE_COLUMNS mat_thm ls = 
  if ls = [] then if mat_thm = empty_mat then empty_mat else failwith "empty duplication list" else
  let rol_thm,all_thm = interpmat_thms mat_thm in
  let ints,part,signs = dest_all2 (concl all_thm) in
  let part_thm = PARTITION_LINE_CONV (snd (dest_comb part)) in
  let isigns_thms = CONJUNCTS (REWRITE_RULE[ALL2;part_thm] all_thm) in
  let isigns_thms' = map (duplicate_columns ls) isigns_thms in 
  let all_thm' = mk_all2_interpsigns part_thm isigns_thms' in  
  let all_thm'' = REWRITE_RULE[GSYM part_thm] all_thm' in
  let mat_thm' = mk_interpmat_thm rol_thm all_thm'' in
    mat_thm';;


let DUPLICATE_COLUMNS mat_thm ls = 
  let start_time = Sys.time() in
  let res = DUPLICATE_COLUMNS mat_thm ls in
    duplicate_columns_timer +.= (Sys.time() -. start_time);
    res;;


let UNMONICIZE_ISIGN vars monic_thm isign_thm =
  let _,_,sign = dest_interpsign isign_thm in
  let const = (fst o dest_mult o lhs o concl) monic_thm in
  let const_thm = SIGN_CONST const in
  let op,_,_ = get_binop (concl const_thm) in
  let mp_thm = 
    if op = rgt then 
      if sign = spos_tm then gtpos 
      else if sign = sneg_tm then gtneg
      else if sign = szero_tm then gtzero
      else failwith "bad sign"
    else if op = rlt then 
      if sign = spos_tm then ltpos 
      else if sign = sneg_tm then ltneg
      else if sign = szero_tm then ltzero
      else failwith "bad sign"
    else (failwith "bad op") in
  let monic_thm' = GEN (hd vars) monic_thm in
    MATCH_MPL[mp_thm;monic_thm';const_thm;isign_thm];;

let UNMONICIZE_ISIGNS vars monic_thms isigns_thm = 
  let isign_thms = interpsigns_thms2 isigns_thm in
  let isign_thms' = map2 (UNMONICIZE_ISIGN vars) monic_thms isign_thms in
    mk_interpsigns isign_thms';;

let UNMONICIZE_MAT vars monic_thms mat_thm = 
  if monic_thms = [] then mat_thm else
  let rol_thm,all_thm = interpmat_thms mat_thm in
  let ints,part,signs = dest_all2 (concl all_thm) in
  let part_thm = PARTITION_LINE_CONV (snd (dest_comb part)) in
  let consts = map (fst o dest_mult o lhs o concl) monic_thms in
  let isigns_thms = CONJUNCTS (REWRITE_RULE[ALL2;part_thm] all_thm) in
  let isigns_thms' = map (UNMONICIZE_ISIGNS vars monic_thms) isigns_thms in 
  let all_thm' = mk_all2_interpsigns part_thm isigns_thms' in  
  let all_thm'' = REWRITE_RULE[GSYM part_thm] all_thm' in
  let mat_thm' = mk_interpmat_thm rol_thm all_thm'' in
    mat_thm';;

let UNMONICIZE_MAT vars monic_thms mat_thm = 
  let start_time = Sys.time() in
  let res = UNMONICIZE_MAT vars monic_thms mat_thm in
    unmonicize_mat_timer +.= (Sys.time() -. start_time);
    res;;


(* {{{ Examples *)

(*
let vars,monic_thms,mat_thm = 
 [], [], empty_mat


let monic_thm = hd monic_thms
length isigns_thms

MONIC_CONV [rx] `&1 + x * (&1 + x * (&1 + x * &7))`

let isign_thm = hd isign_thms

let isigns_thm = hd isigns_thms

    mk_interpsigns [TRUTH];;
let ls = [0;1;2;0;1;2]
 let mat_thm,ls = empty_mat,[]
1,3,

DUPLICATE_COLUMNS  
(ASSUME `interpmat [] [(\x. x + &1); (\x. x + &1); (\x. x + &2); (\x. x + &3); (\x. x + &1); (\x. x + &2)][[Pos; Pos; Pos; Pos; Neg; Zero]]`)
[5]

duplicate_columns [] (ASSUME `interpsigns [] (\x. T) []`)
let new_cols, isigns_thm = [],(ASSUME `interpsigns [] (\x. T) []`)

let isigns_thm = hd isigns_thms

*)

(* }}} *)


let SWAP_HEAD_COL_ROW i isigns_thm = 
  let s_thms = interpsigns_thms2 isigns_thm in
  let s_thms' = insertat i (hd s_thms) (tl s_thms) in
    mk_interpsigns s_thms';;

let SWAP_HEAD_COL i mat_thm = 
  let rol_thm,all_thm = interpmat_thms mat_thm in
  let ints,part,signs = dest_all2 (concl all_thm) in
  let part_thm = PARTITION_LINE_CONV (snd (dest_comb part)) in
  let isigns_thms = CONJUNCTS (REWRITE_RULE[ALL2;part_thm] all_thm) in
  let isigns_thms' = map (SWAP_HEAD_COL_ROW i) isigns_thms in
  let all_thm' = mk_all2_interpsigns part_thm isigns_thms' in
    mk_interpmat_thm rol_thm all_thm';;

let SWAP_HEAD_COL i mat_thm = 
  let start_time = Sys.time() in
  let res = SWAP_HEAD_COL i mat_thm in
    swap_head_col_timer +.= (Sys.time() -. start_time);
    res;;


let LENGTH_CONV = 
  let alength_tm = `LENGTH:(A list) -> num` in
  fun tm -> 
    try
      let ty = type_of tm in
      let lty,[cty] = dest_type ty in  
        if lty <> "list" then failwith "LENGTH_CONV: not a list" else
        let ltm = mk_comb(inst[cty,aty] alength_tm,tm) in
        let lthm = REWRITE_CONV[LENGTH] ltm in
          MATCH_MP main_lem000 lthm
    with _ -> failwith "LENGTH_CONV";;

let LAST_NZ_CONV =
  let alast_tm = `LAST:(A list) -> A` in
  fun nz_thm tm -> 
    try
      let ty = type_of tm in
      let lty,[cty] = dest_type ty in  
      if lty <> "list" then failwith "LAST_NZ_CONV: not a list" else
        let ltm = mk_comb(inst[cty,aty] alast_tm,tm) in
        let lthm = REWRITE_CONV[LAST;NOT_CONS_NIL] ltm in
          MATCH_MPL[main_lem001;nz_thm;lthm]
    with _ -> failwith "LAST_NZ_CONV";;

let rec first f l = 
  match l with 
      [] -> failwith "first"
    | h::t -> if can f h then f h else first f t;;

let NEQ_RULE thm = 
  let thms = CONJUNCTS main_lem002 in
    first (C MATCH_MP thm) thms;;

(*
NEQ_CONV (ARITH_RULE `~(&11 <= &2)`)
*)

let NORMAL_LIST_CONV nz_thm tm = 
  let nz_thm' = NEQ_RULE nz_thm in
  let len_thm = LENGTH_CONV tm in
  let last_thm = LAST_NZ_CONV nz_thm' tm in
  let cthm = CONJ len_thm last_thm in  
    MATCH_EQ_MP (GSYM (REWRITE_RULE[GSYM NEQ] NORMAL_ID)) cthm;; 

(*
|- poly_diff [&0; &0; &0 + a * &1] = [&0; &0 + a * &2]
let tm = `poly_diff [&0; &0 + a * &1]`
*)
let pdiff_tm = `poly_diff`;;
let GEN_POLY_DIFF_CONV vars tm =
  let thm1 = POLY_ENLIST_CONV vars tm in
  let l,x = dest_poly (rhs (concl thm1)) in
  let thm2 = CANON_POLY_DIFF_CONV (mk_comb(pdiff_tm,l)) in
  let thm3 = CONV_RULE (RAND_CONV (LIST_CONV (POLYNATE_CONV vars))) thm2 in
    thm3;;

(* 
   if \x. p = \x. q, where \x. p is the leading polynomial
   replace p by q in mat_thm, 
*)


(*
let peq,mat_thm = !rppeq,!rpmat
*)
let rppeq,rpmat = ref TRUTH,ref TRUTH;;
let REPLACE_POL = 
  let imat_tm = `interpmat` in
  fun peq mat_thm -> 
    rppeq := peq;
    rpmat := mat_thm;
    let pts,pols,sgnll = dest_interpmat (concl mat_thm) in
    let rep_p = lhs(concl peq) in
    let i = try index rep_p (dest_list pols) with _ -> failwith "REPLACE_POL: index" in
    let thm1 = EL_CONV (fun x -> GEN_REWRITE_CONV I [peq] x) i pols in  
      end_itlist (C (curry MK_COMB)) (rev [REFL imat_tm;REFL pts;thm1;REFL sgnll]);;


let REPLACE_POL peq mat_thm =
  let start_time = Sys.time() in
  let res = REPLACE_POL peq mat_thm in
    replace_pol_timer +.= (Sys.time() -. start_time);
    res;;

(* {{{ Examples *)

(*

let peq,mat_thm = 
ASSUME  `(\x. &0) =
        (\x. &0 + a * ((&0 + b * (&0 + b * -- &1)) + a * (&0 + c * &4)))`,
ASSUME `interpmat [x_44] [\x. (&0 + b * &1) + x * (&0 + a * &2); \x. &0]
        [[Pos; Zero]; [Zero; Zero]; [Neg; Zero]]`

let peq = ASSUME `(\x. &1 + x * (&1 + x * (&1 + x * &1))) = (\x. &1 + x)`

REPLACE_POL peq mat_thm

is_constant [`y:real`] `&1 + x * -- &1`

let vars,pols,cont,sgns,ex_thms = 
[`c:real`; `b:real`; `a:real`],
[`&0 + c * &1`],
(fun x y -> x),
[ASSUME `&0 + b * (&0 + b * -- &1) = &0`;
ASSUME ` &0 + b * (&0 + b * (&0 + a * -- &1)) = &0`;
ASSUME  `&0 + a * (&0 + a * &1) = &0`;ASSUME `&0 + b * &1 = &0`;
ASSUME `&0 + a * &1 = &0`; ASSUME ` &1 > &0`],
[]

*)

(* }}} *)



(* ---------------------------------------------------------------------- *)
(*  Factoring                                                             *)
(* ---------------------------------------------------------------------- *)

let UNFACTOR_ISIGN vars xsign_thm pol isign_thm = 
  let x = hd vars in
  let k,pol' = weakfactor x pol in 
  if k = 0 then isign_thm else
  let fact_thm = GEN x (GSYM (WEAKFACTOR_CONV x pol)) in 
  let par_thm = PARITY_CONV (mk_small_numeral k) in
  let _,_,xsign = dest_interpsign xsign_thm in
  let _,_,psign = dest_interpsign isign_thm in
  let parity,_ = dest_comb (concl par_thm) in
  if xsign = spos_tm then
    let mp_thm = 
      if psign = spos_tm then factor_pos_pos 
      else if psign = sneg_tm then factor_pos_neg 
      else if psign = szero_tm then factor_pos_zero
      else failwith "bad sign" in
      let ret = BETA_RULE(MATCH_MPL[mp_thm;xsign_thm;isign_thm]) in
        MATCH_MP ret fact_thm
  else if xsign = szero_tm then 
    let k_thm = prove(mk_neg(mk_eq(mk_small_numeral k,nzero)),ARITH_TAC) in
    let mp_thm = 
      if psign = spos_tm then factor_zero_pos 
      else if psign = sneg_tm then factor_zero_neg 
      else if psign = szero_tm then factor_zero_zero
      else failwith "bad sign" in
      let ret = BETA_RULE(MATCH_MPL[mp_thm;xsign_thm;isign_thm;k_thm]) in
        MATCH_MP ret fact_thm
  else if xsign = sneg_tm & parity = even_tm then
    let k_thm = prove(mk_neg(mk_eq(mk_small_numeral k,nzero)),ARITH_TAC) in
    let mp_thm = 
      if psign = spos_tm then factor_neg_even_pos
      else if psign = sneg_tm then factor_neg_even_neg
      else if psign = szero_tm then factor_neg_even_zero
      else failwith "bad sign" in
      let ret = BETA_RULE(MATCH_MPL[mp_thm;xsign_thm;isign_thm;par_thm;k_thm]) in
        MATCH_MP ret fact_thm
  else if xsign = sneg_tm & parity = odd_tm then
    let k_thm = prove(mk_neg(mk_eq(mk_small_numeral k,nzero)),ARITH_TAC) in
    let mp_thm = 
      if psign = spos_tm then factor_neg_odd_pos
      else if psign = sneg_tm then factor_neg_odd_neg
      else if psign = szero_tm then factor_neg_odd_zero
      else failwith "bad sign" in
      let ret = BETA_RULE(MATCH_MPL[mp_thm;xsign_thm;isign_thm;par_thm;k_thm]) in
        MATCH_MP ret fact_thm
  else failwith "bad something...";;

(* {{{ Examples *)

(*

let vars,xsign_thm,pol,isign_thm = 
[ry;rx],
`interpsign (\x. x < x1) (\x. x) Pos`,
ASSUME `interpsign (\x. x < x_254) (\y. &0 + y * &1) Neg`

`\x. &0 + x * (&4 + x * &6)`,
ASSUME `interpsign (\x. x < x1) (\x. &4 + x * &6) Pos`


let xsign_thm,pol,isign_thm = 
ASSUME `interpsign (\x. x < x1) (\x. x) Pos`,
`\x. &0 + x * (&4 + x * &6)`,
ASSUME `interpsign (\x. x < x1) (\x. &4 + x * &6) Pos`


*)

(* }}} *)

let UNFACTOR_ISIGNS vars pols isigns_thm = 
  let isign_thms = interpsigns_thms2 isigns_thm in
  let isign_thms' = map2 (UNFACTOR_ISIGN vars (hd isign_thms)) pols (tl isign_thms) in
    mk_interpsigns isign_thms';;

let UNFACTOR_MAT vars pols mat_thm = 
  let rol_thm,all_thm = interpmat_thms mat_thm in
  let ints,part,signs = dest_all2 (concl all_thm) in
  let part_thm = PARTITION_LINE_CONV (snd (dest_comb part)) in
  let isigns_thms = CONJUNCTS (REWRITE_RULE[ALL2;part_thm] all_thm) in
  let isigns_thms' = map (UNFACTOR_ISIGNS vars pols) isigns_thms in 
  let all_thm' = mk_all2_interpsigns part_thm isigns_thms' in  
  let all_thm'' = REWRITE_RULE[GSYM part_thm] all_thm' in
  let mat_thm' = mk_interpmat_thm rol_thm all_thm'' in
    mat_thm';;

let UNFACTOR_MAT vars pols mat_thm = 
  let start_time = Sys.time() in
  let res = UNFACTOR_MAT vars pols mat_thm in
    unfactor_mat_timer +.= (Sys.time() -. start_time);
    res;;
    
(* {{{ Examples *)

(*
#untrace UNFACTOR_ISIGN

let isigns_thm = el 0 isigns_thms
UNFACTOR_ISIGNS pols isigns_thm

let isign_thm = el 1 isign_thm

pols
  let isigns_thms' = map (UNFACTOR_ISIGNS pols) isigns_thms in 

let xsign_thm = hd isign_thms
let xsign_thm = ASSUME `interpsign (\x. x < x1) (\x. x) Neg`
let isign_thm = hd (tl isign_thms)
let pol = hd pols
let pol = `\x. &0 + x * (&0 + x * (&0 + x * (&0 + y * &1)))`

let isigns_thm = hd isigns_thms
let vars = [rx;ry;rz]


let pols =     
  [`\x. &0 + x * (&0 + x * (&0 + y * &1))`; `\x. &0 + x * (&4 + x * &6)`; `\x. &3 + x * (&6 + x * &9)`; 
      `\x. &0 + x * (&0 + x * (&0 + x * (&0 + z * &1)))`; `\x. -- &4 + x * (&0 + x * &1)`]

let mat_thm = ASSUME 
  `interpmat [x1; x2; x3; x4; x5] 
  [\x. x; \x. &0 + y * &1; \x. &4 + x * &6; \x. &3 + x * (&6 + x * &9); 
      \x. &0 + z * &1; \x. -- &4 + x * (&0 + x * &1)]
      [[Pos; Pos; Pos; Neg; Neg; Neg];     
      [Neg; Pos; Zero; Zero; Neg; Neg];     
      [Neg; Pos; Neg; Pos; Neg; Neg];     
      [Neg; Pos; Neg; Pos; Neg; Zero];     
      [Neg; Pos; Neg; Pos; Neg; Pos];     
      [Zero; Pos; Neg; Pos; Zero; Pos];     
      [Pos; Pos; Neg; Pos; Pos; Pos];     
      [Pos; Zero; Neg; Pos; Pos; Pos];     
      [Pos; Neg; Neg; Pos; Pos; Pos];     
      [Pos; Zero; Zero; Pos; Pos; Pos];     
      [Pos; Pos; Pos; Pos; Pos; Pos]]` 

UNFACTOR_MAT pols mat_thm

*)

(* }}} *)

let message_time s f x =
  report s;
  time f x;;


(* ---------------------------------------------------------------------- *)
(*  Matrix                                                                *)
(* ---------------------------------------------------------------------- *)

let matrix_count,splitzero_count,splitsigns_count,monicize_count = ref 0,ref 0,ref 0,ref 0;;
let reset_counts() = matrix_count := 0;splitzero_count := 0;splitsigns_count := 0;monicize_count := 0;;
let print_counts() = !matrix_count,!splitzero_count,!splitsigns_count,!monicize_count;;


let szvars,szdun,szpols,szcont,szsgns,szex_thms,szfm = ref [],ref [],ref [],ref (fun x y -> x),ref [],ref [],ref `T`;;
(*
let vars,dun,pols,cont,sgns,ex_thms,fm = !szvars,!szdun,!szpols,!szcont,!szsgns,!szex_thms,!szfm
*)


let rec MATRIX vars pols cont sgns ex_thms fm = 
  incr matrix_count;
  if pols = [] then TRAPOUT cont empty_mat [] fm else 
  if exists (is_constant vars) pols then 
    let p = find (is_constant vars) pols in
    let i = try index p pols with _ -> failwith "MATRIX: no such pol" in
    let pols1,pols2 = chop_list i pols in
    let pols' = pols1 @ tl pols2 in
    let cont' = MATINSERT vars i (FINDSIGN vars sgns p) cont in
      MATRIX vars pols' cont' sgns ex_thms fm
  else   
    let kqs = map (weakfactor (hd vars)) pols in 
      if exists (fun (k,q) -> k <> 0 & not(is_constant vars q)) kqs then 
        let pols' = poly_var(hd vars) :: map snd kqs in 
        let ks = map fst kqs in  
        let cont' mat_thm ex_thms = cont (UNFACTOR_MAT vars pols mat_thm) ex_thms in 
          MATRIX vars pols' cont' sgns ex_thms fm       
      else  
      let d = itlist (max o degree_ vars) pols (-1) in
      let p = find (fun p -> degree_ vars p = d) pols in
      let pl_thm = POLY_ENLIST_CONV vars p in
      let pl = rhs(concl pl_thm) in
      let l,x = dest_poly pl in
      let pdiff_thm = GEN_POLY_DIFF_CONV vars p in
      let p'l = rhs (concl pdiff_thm) in
      let p' = mk_comb(mk_comb(poly_tm,p'l),hd vars) in
      let p'thm = (POLY_DELIST_CONV THENC (POLYNATE_CONV vars)) p' in
      let p'c = rhs (concl p'thm) in
      let hdp' = last (dest_list p'l) in  
      let sign_thm = FINDSIGN vars sgns hdp' in
      let normal_thm = NORMAL_LIST_CONV sign_thm p'l in 
      let i = try index p pols with _ -> failwith "MATRIX: no such pol1" in
      let qs = let p1,p2 = chop_list i pols in p'c::p1 @ tl p2 in
      let gs,div_thms = unzip (map (PDIVIDES vars sgns p) qs) in
      let cont' mat_thm = cont (SWAP_HEAD_COL i mat_thm) in
      let dedcont mat_thm ex_thms = 
        DEDMATRIX vars sgns div_thms pdiff_thm normal_thm cont' mat_thm ex_thms in
        SPLITZERO vars qs gs dedcont sgns ex_thms fm 

and SPLITZERO vars dun pols cont sgns ex_thms fm = 
  incr splitzero_count;
  szvars := vars;
  szdun := dun;
  szpols := pols;
  szcont := cont;
  szsgns := sgns;
  szex_thms := ex_thms;
  szfm := fm;
  match pols with
      [] -> SPLITSIGNS vars [] dun cont sgns ex_thms fm
    | p::ops -> 
        if p = rzero then
          let cont' mat_thm ex_thms = MATINSERT vars (length dun) (REFL rzero) cont mat_thm ex_thms in
            SPLITZERO vars dun ops cont' sgns ex_thms fm
        else 
          let hp = behead vars p in 
          let h = head vars p in
          let nzcont = 
            let tmp = SPLITZERO vars (dun@[p]) ops cont in
              fun sgns ex_thms -> tmp sgns ex_thms fm in
          let zcont = 
            let tmp = SPLITZERO vars dun (hp :: ops) in
            fun sgns ex_thms -> 
              let zthm = FINDSIGN vars sgns h in 
              let b_thm = GSYM (BEHEAD vars zthm p) in     
              let lam_thm = ABS (hd vars) b_thm in
              let cont' mat_thm ex_thms =  
                let mat_thm' = REPLACE_POL (lam_thm) mat_thm in 
                let mat_thm'' = MATCH_EQ_MP mat_thm' mat_thm in
                  cont mat_thm'' ex_thms in
                tmp cont' sgns ex_thms fm in 
            SPLIT_ZERO (tl vars) sgns (head vars p) zcont nzcont ex_thms

and SPLITSIGNS vars dun pols cont sgns ex_thms fm = 
  incr splitsigns_count;
  match pols with
      [] -> MONICIZE vars dun cont sgns ex_thms fm 
(*         [] -> MATRIX vars dun cont sgns ex_thms fm *)
    | p::ops -> 
        let cont' sgns ex_thms = SPLITSIGNS vars (dun@[p]) ops cont sgns ex_thms fm in
          SPLIT_SIGN (tl vars) sgns (head vars p) cont' cont' ex_thms

and MONICIZE vars pols cont sgns ex_thms fm =   
  incr monicize_count;
  let monic_thms = map (MONIC_CONV vars) pols in 
  let monic_pols = map (rhs o concl) monic_thms in  
  let sols = setify monic_pols in 
  let indices = map (fun p -> try index p sols with _ -> failwith "MONICIZE: no such pol") monic_pols in  
  let transform mat_thm =  
    let mat_thm' = DUPLICATE_COLUMNS mat_thm indices in 
(*       mat_thm'  *)
      UNMONICIZE_MAT vars monic_thms mat_thm' in 
  let cont' mat_thm ex_thms = cont (transform mat_thm) ex_thms in 
    MATRIX vars sols cont' sgns ex_thms fm 
;;

(* {{{ Examples *)

(*
let vars,pols,sgns,ex_thms = [],[],[],[]

let mat_thm = mat_thm'
monic_thms

let vars = [rx]
let mat_thm = ASSUME 
  `interpmat [x1; x2; x3; x4; x5] 
     [(\x. &1 + x * (&2 + x * &3)); (\x. &2 + x * (&4 + x * &6)); \x. &3 + x * (&6 + x * &9); \x. &2 + x * (-- &3 + x * &1); \x. -- &4 + x * (&0 + x * &1); \x. &8 + x * &4]
      [[Pos; Pos; Pos; Neg; Neg; Neg];     
      [Pos; Pos; Zero; Zero; Neg; Neg];     
      [Pos; Pos; Neg; Pos; Neg; Neg];     
      [Pos; Pos; Neg; Pos; Neg; Zero];     
      [Pos; Pos; Neg; Pos; Neg; Pos];     
      [Pos; Pos; Neg; Pos; Zero; Pos];     
      [Pos; Pos; Neg; Pos; Pos; Pos];     
      [Pos; Zero; Neg; Pos; Pos; Pos];     
      [Pos; Neg; Neg; Pos; Pos; Pos];     
      [Pos; Zero; Zero; Pos; Pos; Pos];     
      [Pos; Pos; Pos; Pos; Pos; Pos]]` 

let mat_thm = ASSUME 
 `interpmat [x1; x2; x3; x4; x5] 
  [\x. -- &4 + x * (&0 + x * &1); \x. &2 + x * &1; \x. &2 + x * (-- &3 + x * &1); \x. &1 / &3 + x * (&2 / &3 + x * &1)]
      [[Pos; Pos; Pos; Neg];     
      [Pos; Pos; Zero; Zero];     
      [Pos; Pos; Neg; Pos];      
      [Pos; Pos; Neg; Pos];
      [Pos; Pos; Neg; Pos];      
      [Pos; Pos; Neg; Pos];
      [Pos; Pos; Neg; Pos];      
      [Pos; Zero; Neg; Pos];
      [Pos; Neg; Neg; Pos];      
      [Pos; Zero; Zero; Pos];     
      [Pos; Pos; Pos; Pos]]`;;

let vars = [rx]
let pols = [`&1 + x * (&2 + x * &3)`;`&2 + x * (&4 + x * &6)`;`&3 + x * (&6 + x * &9)`; `&2 + x * (-- &3 + x * &1)`;`-- &4 + x * (&0 + x * &1)`;`&8 + x * &4`]


*)
(* }}} *)


(* ---------------------------------------------------------------------- *)
(*  Set up RQE                                                            *)
(* ---------------------------------------------------------------------- *)

let polynomials tm = 
  let rec polynomials tm = 
    if tm = t_tm or tm = f_tm then []
    else if is_conj tm or is_disj tm or is_imp tm or is_iff tm then
      let _,l,r = get_binop tm in polynomials l @ polynomials r 
    else if is_neg tm then polynomials (dest_neg tm)
    else if 
      can (dest_binop rlt) tm or 
      can (dest_binop rgt) tm or 
      can (dest_binop rle) tm or 
      can (dest_binop rge) tm or 
      can (dest_binop req) tm or 
      can (dest_binop rneq) tm then 
        let _,l,_ = get_binop tm in [l]
    else failwith "not a fol atom" in
    setify (polynomials tm);;
(* {{{ Examples *)

(*  
let pols = polynomials `(poly [&1; -- &2] x > &0 ==> poly [&1; -- &2] x >= &0 /\ (poly [&8] x = &0)) /\ ~(poly [y] x <= &0)`
*)

(* }}} *)


let BASIC_REAL_QELIM_CONV vars fm = 
  let x,bod = dest_exists fm in
  let pols = polynomials bod in
  let cont mat_thm ex_thms = 
    let ex_thms' = sort (fun x y -> xterm_lt (fst y) (fst x)) ex_thms in
    let comb_thm = COMBINE_TESTFORMS x mat_thm bod in
    let comb_thm' = rev_itlist CHOOSE ex_thms' comb_thm in
      comb_thm' in
  let ret_thm = SPLITZERO (x::vars) [] pols cont empty_sgns [] fm in
    PURE_REWRITE_RULE[NEQ] ret_thm;; 

let REAL_QELIM_CONV fm =  
  reset_counts();
  ((LIFT_QELIM_CONV POLYATOM_CONV (EVALC_CONV THENC SIMPLIFY_CONV) 
     BASIC_REAL_QELIM_CONV) THENC EVALC_CONV THENC SIMPLIFY_CONV) fm;; 

(* ---------------------------------------------------------------------- *)
(*  timers                                                                *)
(* ---------------------------------------------------------------------- *)

