1structure OmegaSymbolic :> OmegaSymbolic =
2struct
3
4(* This file implements the horrid part of the Omega test, when you have to
5   do quantifier elimination symbolically, and without the benefit of being
6   able to work outside the logic.
7
8   In particular, this means that this part of the procedure has to work
9   using the theorems in OmegaTheory.
10
11   Input is a term of the form
12       ?x. r1 /\ r2 /\ .. rn
13   where each rn is in "Omega leaf normal form", i.e.
14       0 <= c1 * v1 + c2 * v2 + c3 * v3 + c
15*)
16
17open HolKernel boolLib intSyntax
18
19open integerTheory OmegaTheory
20
21val lhand = rand o rator
22
23
24val REWRITE_CONV = GEN_REWRITE_CONV TOP_DEPTH_CONV bool_rewrites
25
26fun EVERY_CONJ_CONV c t =
27    if is_conj t then BINOP_CONV (EVERY_CONJ_CONV c) t
28    else c t
29
30fun EVERY_DISJ_CONV c t =
31    if is_disj t then BINOP_CONV (EVERY_DISJ_CONV c) t
32    else c t
33
34fun ERR f msg = HOL_ERR { origin_structure = "OmegaSymbolic",
35                          origin_function = f,
36                          message = msg}
37
38(* ----------------------------------------------------------------------
39    clause_to_evals v t
40
41    where v is a variable, and t is a conjunction of <= or divisibility
42    leaves.
43    Returns a theorem of the form
44       t = (evalleft v lefts /\ evalright v rights) /\ extras
45    where v is not free in extras.  Extras may be the term T.
46
47   ---------------------------------------------------------------------- *)
48
49val pvar = mk_var("p", bool)
50val qvar = mk_var("q", bool)
51val xvar = mk_var("x", int_ty)
52fun clause_to_evals v tm = let
53  val step0 = REWRITE_CONV [GSYM CONJ_ASSOC]
54  fun step1 tm = INST [pvar |-> tm, xvar |-> v] eval_base
55  fun mk_upper cf t = let
56    (* cf will be the negative coefficient of v *)
57    val poscf_t = mk_mult(term_of_int (Arbint.~ cf), v)
58  in
59    K (SYM (SPECL [poscf_t, zero_tm, rand t] INT_LE_LADD)) THENC
60    FORK_CONV (REWR_CONV INT_ADD_RID, REWR_CONV INT_ADD_COMM) THENC
61    RAND_CONV OmegaMath.SORT_AND_GATHER1_CONV
62  end t
63  fun mk_lower cf t = let
64    (* cf will be the positive coefficient of v *)
65    val neg_t = mk_negated(mk_mult(term_of_int cf, v))
66  in
67    K (SYM (SPECL [neg_t, zero_tm, rand t] INT_LE_LADD)) THENC
68    FORK_CONV (REWR_CONV INT_ADD_RID, REWR_CONV INT_ADD_COMM) THENC
69    RAND_CONV (RAND_CONV (REWR_CONV INT_NEG_LMUL) THENC
70               OmegaMath.SORT_AND_GATHER1_CONV THENC
71               REWR_CONV (GSYM INT_NEGNEG)) THENC
72    REWR_CONV INT_LE_NEG
73  end t
74
75  fun normal_step tm = let
76    val (c1, c2) = dest_conj tm
77    val (evals, ex) = dest_conj c1
78    val (ups, lows) = dest_conj evals
79    val (ex1, ex2, up, low, cval, get_t) =
80        if is_conj c2 then
81          (eval_step_extra3, eval_step_extra4, eval_step_upper2,
82           eval_step_lower2, RAND_CONV o LAND_CONV, #1 o dest_conj)
83        else
84          (eval_step_extra1, eval_step_extra2, eval_step_upper1,
85           eval_step_lower1, RAND_CONV, I)
86    val t = get_t c2
87    val cf = if is_neg t orelse is_divides t then Arbint.zero
88             else CooperMath.sum_var_coeffs v (rand t)
89  in
90    if cf = Arbint.zero then
91      if is_const ex then REWR_CONV ex1
92      else REWR_CONV ex2
93    else if Arbint.<(cf, Arbint.zero) then
94      cval (mk_upper cf) THENC REWR_CONV up
95    else
96      cval (mk_lower cf) THENC REWR_CONV low
97  end tm
98in
99  step0 THENC step1 THENC REPEATC normal_step
100end tm
101
102(* ----------------------------------------------------------------------
103    prove_every_fstnzero t
104
105    t is a term of the form ``[ (num, int); (num, int); ... (num, int)]``
106    This function attempts to prove |- EVERY fst_nzero ^t
107   ---------------------------------------------------------------------- *)
108
109val c_ty = pairSyntax.mk_prod(numSyntax.num, int_ty)
110val clist_ty = listSyntax.mk_list_type c_ty
111val nil_t = listSyntax.mk_nil c_ty
112val every_t = mk_thy_const {Thy = "list", Name = "EVERY",
113                            Ty = (c_ty --> bool) --> clist_ty --> bool};
114val (every_nil, every_cons) = CONJ_PAIR listTheory.EVERY_DEF
115
116val fst_nzero_t = mk_thy_const {Thy = "Omega", Name = "fst_nzero",
117                                Ty = c_ty --> bool};
118val every_fst_nzero_nil = EQT_ELIM (ISPEC fst_nzero_t every_nil)
119val every_fst_nzero_cons = ISPEC fst_nzero_t every_cons
120fun prove_fstnzero t =
121    EQT_ELIM ((REWR_CONV fst_nzero_def THENC
122               RAND_CONV (REWR_CONV pairTheory.FST) THENC
123               CooperMath.REDUCE_CONV) (mk_comb(fst_nzero_t, t)))
124
125fun prove_every_fstnzero list_t =
126    case total listSyntax.dest_cons list_t of
127      NONE => every_fst_nzero_nil
128    | SOME (h,t) => let
129        val fnz_th = prove_fstnzero h
130        val rest = prove_every_fstnzero t
131      in
132        EQ_MP (SYM (SPECL [h, t] every_fst_nzero_cons))
133              (CONJ fnz_th rest)
134      end
135
136(* ----------------------------------------------------------------------
137    prove_every_fst1 t
138
139    t is a term of the form ``[ (num, int); (num, int); ... (num, int)]``
140    This function attempts to prove |- EVERY fst1 ^t
141   ---------------------------------------------------------------------- *)
142
143val fst1_t = ``Omega$fst1 : num # int -> bool``
144val every_fst1_nil = EQT_ELIM (ISPEC fst1_t every_nil)
145val every_fst1_cons = ISPEC fst1_t every_cons
146fun prove_fst1 t =
147    EQT_ELIM ((REWR_CONV fst1_def THENC
148               LAND_CONV (REWR_CONV pairTheory.FST) THENC
149               CooperMath.REDUCE_CONV) (mk_comb(fst1_t, t)))
150
151fun prove_every_fst1 list_t =
152    case total listSyntax.dest_cons list_t of
153      NONE => every_fst1_nil
154    | SOME(h,t) => let
155        val h_th = prove_fst1 h
156        val t_th = prove_every_fst1 t
157      in
158        EQ_MP (SYM (SPECL [h,t] every_fst1_cons)) (CONJ h_th t_th)
159      end
160
161(* ----------------------------------------------------------------------
162    prove_every_fst_lt_m m t
163
164    t is a term of the form ``[ (num, int); (num, int); ... (num, int)]``
165    m is a term of type :num, and a numeral.
166    This function attempts to prove |- EVERY (\p. FST p <= ^m) ^t
167   ---------------------------------------------------------------------- *)
168
169fun abs_t m = let
170  val p = mk_var("p", c_ty)
171  val body = numSyntax.mk_leq(pairSyntax.mk_fst p, m)
172in
173  mk_abs(p, body)
174end
175
176fun prove_fst_lt_m abs_t t =
177    EQT_ELIM ((BETA_CONV THENC LAND_CONV (REWR_CONV pairTheory.FST) THENC
178               CooperMath.REDUCE_CONV) (mk_comb(abs_t, t)))
179
180fun prove_every_fst_lt_m m t = let
181  val absn = abs_t m
182  fun recurse list_t =
183      case total listSyntax.dest_cons list_t of
184        NONE => EQT_ELIM (ISPEC absn every_nil)
185      | SOME(h,t) =>
186        EQ_MP (SYM (ISPECL [absn, h, t] every_cons))
187              (CONJ (prove_fst_lt_m absn h) (recurse t))
188in
189  recurse t
190end
191
192(* ----------------------------------------------------------------------
193    calculate_shadow (sdef, rowdef) t
194
195    sdef is a the "shadow definition", i.e. a couple of theorems
196    defining the shadow type (the conjuncts of real_shadow_def or
197    dark_shadow_def) and rowdef is the analogous pair of theorems
198    defining the corresponding row function (conjuncts of
199    rshadow_row_def or dark_shadow_row_def)
200   ---------------------------------------------------------------------- *)
201
202
203fun munge_to_altform varname th = let
204  val (nilth, consth0) = CONJ_PAIR th
205  val consth = SPEC_ALL consth0
206in
207  (REWRITE_RULE [nilth] (INST [mk_var(varname, clist_ty) |-> nil_t] consth),
208   consth)
209end
210
211val alt_dshadow = munge_to_altform "uppers" dark_shadow_def
212val alt_drow = munge_to_altform "rs" dark_shadow_row_def
213val alt_rshadow = munge_to_altform "ls" real_shadow_def
214val alt_rrow = munge_to_altform "rs" rshadow_row_def
215
216fun calculate_shadow (sdef, rowdef) = let
217  val (s1, scons) = sdef
218  val (r1, rcons) = rowdef
219  val mathnorm = OmegaMath.leaf_normalise
220  fun calculate_row t =
221      ((REWR_CONV r1 THENC mathnorm) ORELSEC
222       (REWR_CONV rcons THENC FORK_CONV (mathnorm, calculate_row))) t
223  fun main t =
224      ((REWR_CONV s1 THENC calculate_row) ORELSEC
225       (REWR_CONV scons THENC FORK_CONV (calculate_row, main))) t
226in
227  main
228end
229
230(* ----------------------------------------------------------------------
231    expand_evals tm
232
233    tm is of form evalupper x list1 /\ evallower x list2
234    rewrite away the evallower and evalupper terms, keeping everything
235    right associated
236   ---------------------------------------------------------------------- *)
237
238val (evalhi10, evalhi_cons0) = munge_to_altform "cs" evalupper_def
239val (evallo1, evallo_cons) = munge_to_altform "cs" evallower_def
240
241val evalhi1 = AP_THM (AP_TERM conjunction evalhi10) qvar
242val evalhi_cons =
243    CONV_RULE (RAND_CONV (REWR_CONV (GSYM CONJ_ASSOC)))
244              (AP_THM (AP_TERM conjunction evalhi_cons0) qvar)
245
246
247val expand_evals = let
248  fun reclos tm =
249      (REWR_CONV evallo1 ORELSEC
250       (REWR_CONV evallo_cons THENC RAND_CONV reclos)) tm
251  fun rechis tm =
252      ((REWR_CONV evalhi1 THENC RAND_CONV reclos) ORELSEC
253       (REWR_CONV evalhi_cons THENC RAND_CONV rechis)) tm
254in
255  rechis
256end
257
258
259
260(* ----------------------------------------------------------------------
261    do_divisibility_analysis v ctxt tm
262
263    tm is of the form   ?x. (c * x = e) /\ c1 /\ c2 /\ c3
264    where each ci is either of the form  d * x <= U  or  L <= e * x.
265    If e contains any variables that appear in ctxt, then
266    leaf_normalise all of the ci's and the equality term and then rename
267    x to be v (which is its correct name).
268
269    Otherwise, multiply the ci's through so as to make them include
270    c * x as a subterm, then rewrite with the first conjunct, and then
271    leaf normalise.  The variable x will have been eliminated from all
272    but the first conjunct.  Then push the ?x inwards, and turn that
273    first conjunct into a divides term (c int_divides e).
274   ---------------------------------------------------------------------- *)
275
276fun lcmify v c c_i tm = let
277  (* tm either d * v <= U or L <= e * v, need c * v to be present *)
278  val (l,r) = dest_leq tm
279  val (accessor, cval) =
280      if rand r = v then (rand, RAND_CONV) else (lhand, LAND_CONV)
281  val d = lhand (accessor tm)
282in
283  if  d = c then ALL_CONV
284  else let
285      open CooperMath
286      val d_i = int_of_term d
287      val lc = lcm(c_i, d_i)
288      fun multiply_through tm =
289          if lc <> d_i then let
290              val f_i = Arbint.div(lc, d_i)
291              val f = term_of_int f_i
292              val zero_lt_f =
293                  EQT_ELIM (REDUCE_CONV (mk_less(zero_tm, f)))
294              val finisher =
295                  if f = c then
296                    REWR_CONV INT_MUL_ASSOC THENC
297                    LAND_CONV (REWR_CONV INT_MUL_COMM) THENC
298                    REWR_CONV (GSYM INT_MUL_ASSOC)
299                  else
300                    REWR_CONV INT_MUL_ASSOC THENC LAND_CONV REDUCE_CONV
301            in
302              (K (SYM (MP (SPECL [f, l, r] INT_LE_MONO) zero_lt_f)) THENC
303               cval finisher) tm
304            end
305          else ALL_CONV tm
306      fun divide_out tm =
307          if lc <> c_i andalso rand (accessor tm) = v then let
308              val f_i = Arbint.div(lc, c_i)
309              val f = term_of_int f_i
310              val fc_eq_l = CooperMath.REDUCE_CONV(mk_mult(f, c))
311            in
312              (cval (LAND_CONV (K (SYM fc_eq_l)) THENC
313                     REWR_CONV (GSYM INT_MUL_ASSOC))) tm
314            end
315          else ALL_CONV tm
316    in
317      multiply_through THENC divide_out
318    end
319end tm
320
321fun do_divisibility_analysis v ctxt tm = let
322  val (x, body) = dest_exists tm
323  val (eqterm, rest) = dest_conj body
324in
325  if not (null (intersect (free_vars (rand eqterm)) ctxt)) then
326    (* leave it as an equality *)
327    BINDER_CONV (EVERY_CONJ_CONV OmegaMath.leaf_normalise) THENC
328    RAND_CONV (ALPHA_CONV v)
329  else let
330      val c = lhand (lhand eqterm)
331      val c_i = int_of_term c
332      fun ctxt_rwt tm = let
333        val (c1, c2) = dest_conj tm
334        val thm0 = DISCH_ALL (REWRITE_CONV [ASSUME c1] c2)
335      in
336        MATCH_MP CooperThms.simple_conj_congruence thm0
337      end
338    in
339      BINDER_CONV (RAND_CONV (EVERY_CONJ_CONV (lcmify x c c_i)) THENC
340                   ctxt_rwt) THENC
341      EXISTS_AND_CONV THENC
342      LAND_CONV (BINDER_CONV (LAND_CONV (REWR_CONV INT_MUL_COMM)) THENC
343                 REWR_CONV (GSYM INT_DIVIDES) THENC
344                 RAND_CONV OmegaMath.sum_normalise THENC
345                 CooperMath.minimise_divides) THENC
346      RAND_CONV (EVERY_CONJ_CONV OmegaMath.leaf_normalise) THENC
347      REWRITE_CONV []
348    end
349end tm
350
351
352
353
354(* ----------------------------------------------------------------------
355    calculate_nightmare ctxt t
356
357    t is a term of the form ?x. nightmare x m ups lows tlist.
358    This function expands it into an equation of the form
359       |- t =  D1 \/ D2 \/ D3 \/ .. Dn
360    Each Di is either free of x entirely (but with a divides term present
361    as the first conjunct) or is of the form
362       ?x. (c * x = R) /\ C1 /\ C2 /\ .. Cn
363    where x is absent in all of the Ci, and where R includes at least
364    one variable y, which is present in the list of variables ctxt.
365
366    In this latter situation, the equality and one of the x or y will
367    be eliminated through OmegaEq.  In the former situation, the divides
368    term will need special treatment later.
369   ---------------------------------------------------------------------- *)
370
371val (cnightmare10, cnightmare_cons0) = munge_to_altform "rs" calc_nightmare_def
372
373val reassoc_internals = LEFT_AND_EXISTS_CONV THENC
374                        BINDER_CONV (REWR_CONV (GSYM CONJ_ASSOC))
375val cnightmare1 = CONV_RULE (RAND_CONV reassoc_internals)
376                            (AP_THM (AP_TERM conjunction cnightmare10) pvar)
377
378val cnightmare_cons =
379    CONV_RULE (RAND_CONV (REWR_CONV RIGHT_AND_OVER_OR THENC
380                          LAND_CONV reassoc_internals))
381              (AP_THM (AP_TERM conjunction cnightmare_cons0) pvar)
382
383fun calculate_nightmare ctxt tm = let
384  val (v, body) = dest_exists tm
385  val reducer =
386      BINDER_CONV (LAND_CONV (RAND_CONV
387                                (RAND_CONV CooperMath.REDUCE_CONV))) THENC
388      OmegaMath.calculate_range_disjunct
389  fun recurse t =
390      ((REWR_CONV cnightmare1 THENC reducer) ORELSEC
391       (REWR_CONV cnightmare_cons THENC LAND_CONV reducer THENC
392        RAND_CONV recurse)) t
393in
394  BINDER_CONV (REWR_CONV calculational_nightmare THENC
395               RAND_CONV expand_evals THENC
396               recurse THENC REWRITE_CONV []) THENC
397  CooperSyntax.push_in_exists THENC
398  EVERY_DISJ_CONV (do_divisibility_analysis v ctxt) THENC
399  REWRITE_CONV []
400end tm
401
402
403(* ----------------------------------------------------------------------
404    apply_fmve cty t
405
406    t is of the form ?x. c1 /\ c2 /\ c3 .. /\ cn
407
408    Every ci is a valid Omega leaf.  This function converts the body
409    into a term involving eval_left and eval_right and an "extra" bit,
410    pushes the existential in over the former pair, and then uses an
411    appropriate rewrite from OmegaTheory.  The right choice of rewrite
412    is specified through the parameter cty.
413   ---------------------------------------------------------------------- *)
414
415datatype ctype = VACUOUS_LOW | VACUOUS_UP | EXACT_LOW | EXACT_UP
416               | NIGHTMARE of (term * term list)
417(* the nightmare constructor takes a pair as an argument:
418     * the term is of type ``:num``, corresponding to
419       the maximum coefficient of the variable to be eliminated in an
420       upper bound constraint
421     * the list of terms is a list of other existentially quantified
422       variables that have scope over this clause  *)
423
424fun apply_fmve ctype = let
425  fun initially t = let
426    val (v, body) = dest_exists t
427  in
428    BINDER_CONV (clause_to_evals v) THENC EXISTS_AND_CONV
429  end t
430
431  fun finisher t = let
432    val (v,body) = dest_exists t
433    val (e_ups, e_lows) = dest_conj body
434    val ups = rand e_ups
435    val lows = rand e_lows
436    val ups_nzero = prove_every_fstnzero ups
437    val lows_nzero = prove_every_fstnzero lows
438  in
439    case ctype of
440      VACUOUS_LOW => REWRITE_CONV [evalupper_def] THENC
441                     K (EQT_INTRO (MATCH_MP onlylowers_satisfiable lows_nzero))
442    | VACUOUS_UP =>  REWRITE_CONV [evallower_def] THENC
443                     K (EQT_INTRO (MATCH_MP onlyuppers_satisfiable ups_nzero))
444    | EXACT_LOW => let
445        val low_fst1 = prove_every_fst1 lows
446        val disj = DISJ2 (list_mk_comb(every_t, [fst1_t, ups])) low_fst1
447        val th = MP (MATCH_MP exact_shadow_case (CONJ ups_nzero lows_nzero))
448                    disj
449      in
450        K th THENC calculate_shadow (alt_rshadow, alt_rrow)
451      end
452    | EXACT_UP => let
453        val up_fst1 = prove_every_fst1 ups
454        val disj = DISJ1 up_fst1 (list_mk_comb(every_t, [fst1_t, lows]))
455        val th = MP (MATCH_MP exact_shadow_case (CONJ ups_nzero lows_nzero))
456                    disj
457      in
458        K th THENC calculate_shadow (alt_rshadow, alt_rrow)
459      end
460    | NIGHTMARE (m,ctxt) => let
461        val uppers_lt_m = prove_every_fst_lt_m m ups
462      in
463        K (MATCH_MP alternative_equivalence
464                    (LIST_CONJ [ups_nzero, lows_nzero, uppers_lt_m])) THENC
465        RAND_CONV (RAND_CONV (ALPHA_CONV v)) THENC
466        LAND_CONV (calculate_shadow (alt_dshadow, alt_drow) THENC
467                   REWRITE_CONV []) THENC
468        RAND_CONV (calculate_nightmare ctxt)
469      end
470  end t
471  fun elim_rT tm = (if rand tm = boolSyntax.T then REWR_CONV CooperThms.T_and_r
472                    else ALL_CONV) tm
473in
474  initially THENC LAND_CONV finisher THENC elim_rT
475end
476
477
478(* ----------------------------------------------------------------------
479    best_variable_to_eliminate vs t
480
481    Given a list of variables vs, and a conjunction of leaves t, pick the
482    variable that should be eliminated.  Do this by scanning the term
483    while maintaining a map to information about that variable.
484   ---------------------------------------------------------------------- *)
485
486type varinfo = { maxupc : Arbint.int ref, maxloc : Arbint.int ref,
487                 numups : int ref, numlos : int ref }
488fun new_varinfo () : varinfo =
489    { maxupc = ref Arbint.zero, maxloc = ref Arbint.zero,
490      numups = ref 0, numlos = ref 0}
491exception NotFound
492
493fun variable_information vs t = let
494  val table = ref (Redblackmap.mkDict Term.compare)
495  fun ins_initial_recs v =
496    table := Redblackmap.insert (!table, v, new_varinfo())
497  val _ = app ins_initial_recs vs
498  fun examine_cv t = let
499    val (c, v) = dest_mult t
500    val c_i = int_of_term c
501  in
502    case Redblackmap.peek (!table, v) of
503      NONE => ()
504    | SOME {maxupc, maxloc, numups, numlos} =>
505      if Arbint.<(c_i, Arbint.zero) then (* upper bound *)
506        (maxupc := Arbint.max(!maxupc, Arbint.~ c_i);
507         numups := !numups + 1)
508      else
509        (maxloc := Arbint.max(!maxloc, c_i);
510         numlos := !numlos + 1)
511  end handle HOL_ERR _ => ()
512in
513  app examine_cv (List.concat (map (strip_plus o rand) (strip_conj t)));
514  table
515end
516
517fun findleast gt f l =
518    case l of
519      [] => raise Fail "findleast : empty list"
520    | h::t => let
521        fun recurse (acc as (e, v)) l =
522            case l of
523              [] => (e, v)
524            | h::t => let val v' = f h
525                      in
526                        if gt(v, v') then recurse (h, v') t
527                        else recurse acc t
528                      end
529      in
530        recurse (h, f h) t
531      end
532
533fun best_variable_to_eliminate vs t = let
534  val table = variable_information vs t
535  val items = Redblackmap.listItems (!table)
536  fun is_vacuous (_, {numups, numlos,...}) = !numups = 0 orelse !numlos = 0
537  fun is_exact (_, {maxloc, maxupc,...}) = !maxloc = Arbint.one orelse
538                                           !maxupc = Arbint.one
539in
540  case List.find is_vacuous items of
541    NONE => let
542      val exacts = filter is_exact items
543    in
544      if null exacts then let
545          fun f (_, {maxloc, maxupc, ...}) = Arbint.max(!maxloc, !maxupc)
546          val ((v, {maxupc, ...}), _) = findleast Arbint.> f items
547        in
548          (v, NIGHTMARE (rand (term_of_int (!maxupc)), set_diff vs [v]))
549        end
550      else let
551          fun f (_, {numups, numlos,...}) = !numups * !numlos
552          val ((v, {maxupc, maxloc,...}), _) = findleast op> f exacts
553        in
554          (v, if !maxupc = Arbint.one then EXACT_UP else EXACT_LOW)
555        end
556    end
557  | SOME (v, {numups, numlos,...}) => (v, if !numlos = 0 then VACUOUS_UP
558                                          else VACUOUS_LOW)
559end
560
561
562
563
564(* ----------------------------------------------------------------------
565    eliminate_an_existential t
566
567    t is of the form ?x1..xn. body, where body is a conjunction of
568    fully normalised leaves.  This function picks one of the quantifiers
569    to eliminate, and then does this.
570   ---------------------------------------------------------------------- *)
571
572fun push_nthvar_to_bot n tm =
573    if n <= 0 then TRY_CONV (SWAP_VARS_CONV THENC
574                             BINDER_CONV (push_nthvar_to_bot 0)) tm
575    else BINDER_CONV (push_nthvar_to_bot (n - 1)) tm
576
577fun eliminate_an_existential0 t = let
578  val (vs, body) = strip_exists t
579  val (eliminate, category) = best_variable_to_eliminate vs body
580  val v_n = index (fn v => v = eliminate) vs
581  fun check_for_eqs tm = let
582    val (lvs, body) = strip_exists tm
583  in
584    if length lvs = length vs then
585      Profile.profile "eq" OmegaMath.OmegaEq
586    else
587      ALL_CONV
588  end tm
589  fun mypush_in_exs tm = let
590    val (vs, body) = strip_exists tm
591  in
592    CooperSyntax.push_in_exists THENC
593    EVERY_DISJ_CONV (RENAME_VARS_CONV (map (#1 o dest_var) vs) THENC
594                     check_for_eqs)
595  end tm
596in
597  push_nthvar_to_bot v_n THENC
598  LAST_EXISTS_CONV (apply_fmve category) THENC
599  mypush_in_exs
600end t
601
602val eliminate_an_existential =
603    TRY_CONV OmegaMath.eliminate_negative_divides THENC
604    EVERY_DISJ_CONV (TRY_CONV OmegaMath.eliminate_positive_divides THENC
605                     eliminate_an_existential0) THENC
606    REWRITE_CONV []
607
608(* ----------------------------------------------------------------------
609    eliminate_existentials t
610
611    given a normalised term of the form
612      ?x1..xn. body
613    eliminate all of its existentials.
614   ---------------------------------------------------------------------- *)
615
616fun eliminate_existentials tm =
617    if is_exists tm then
618      (eliminate_an_existential THENC
619       EVERY_DISJ_CONV eliminate_existentials) tm
620    else REWRITE_CONV [] tm
621
622
623(* ----------------------------------------------------------------------
624    sym_normalise tm
625
626    tm is of form
627      ?x1..xn. body
628    where body has no nested quantifiers.  Only difference with the
629    normalisation routine in OmegaShell is that we don't automatically
630    eliminate divisibility terms, because we're not necessarily going
631    to be able to.  We also have to eliminate equality terms that survive
632    the attempt to get rid of them with OmegaEq.
633   ---------------------------------------------------------------------- *)
634
635fun ISCONST_CONV tm = if is_const tm then ALL_CONV tm else NO_CONV tm
636fun IFEXISTS c tm = if is_exists tm then c tm else ALL_CONV tm
637
638val sym_normalise = let
639  fun push_exs tm = let
640    val vs = map (#1 o dest_var) (#1 (strip_exists tm))
641  in
642    CooperSyntax.push_in_exists THENC EVERY_DISJ_CONV (RENAME_VARS_CONV vs)
643  end tm
644  open OmegaMath
645  val elim_eq = REWR_CONV (GSYM INT_LE_ANTISYM) THENC
646                RAND_CONV leaf_normalise
647in
648  STRIP_QUANT_CONV (Canon.NNF_CONV leaf_normalise false THENC
649                    REPEATC (CHANGED_CONV CSimp.csimp THENC
650                             REWRITE_CONV [])) THENC
651  push_exs THENC
652  EVERY_DISJ_CONV (OmegaEq THENC DEPTH_CONV elim_eq THENC
653                   TRY_CONV (REWR_CONV EXISTS_SIMP) THENC
654                   IFEXISTS
655                     (STRIP_QUANT_CONV Canon.PROP_DNF_CONV THENC push_exs))
656end
657
658
659(* ----------------------------------------------------------------------
660    find_deep_existentials tm
661
662   ---------------------------------------------------------------------- *)
663
664fun is_bool_binop t = let
665  val (f,args) = strip_comb t
666in
667  length args = 2 andalso is_const f andalso
668  (List.exists (same_const f) [conjunction, disjunction]
669   orelse same_const f equality andalso type_of (hd args) = bool)
670end
671
672fun IFEXISTS c tm = if is_exists tm then c tm else ALL_CONV tm
673
674fun findelim_deep_existential tm = let
675in
676  if is_forall tm then
677    (STRIP_QUANT_CONV findelim_deep_existential) ORELSEC
678    (CooperSyntax.flip_foralls THENC RAND_CONV findelim_deep_existential)
679  else if is_exists tm then
680    (STRIP_QUANT_CONV findelim_deep_existential) ORELSEC
681    (sym_normalise THENC EVERY_DISJ_CONV (IFEXISTS eliminate_an_existential))
682  else if is_bool_binop tm then
683    (LAND_CONV findelim_deep_existential) ORELSEC
684    (RAND_CONV findelim_deep_existential)
685  else if is_neg tm then
686    RAND_CONV findelim_deep_existential
687  else
688    NO_CONV
689end tm
690
691end (* struct *)
692