src/HOL/Tools/coinduction.ML
author wenzelm
Sun, 05 Jul 2015 15:02:30 +0200
changeset 60642 48dd1cefb4ae
parent 59971 ea06500bb092
child 60784 4f590c08fd5d
permissions -rw-r--r--
simplified Thm.instantiate and derivatives: the LHS refers to non-certified variables -- this merely serves as index into already certified structures (or is ignored);
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
54540
5d7006e9205e moved 'coinduction' proof method to 'HOL'
blanchet
parents: 54366
diff changeset
     1
(*  Title:      HOL/Tools/coinduction.ML
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     2
    Author:     Johannes Hölzl, TU Muenchen
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     3
    Author:     Dmitriy Traytel, TU Muenchen
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     4
    Copyright   2013
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     5
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     6
Coinduction method that avoids some boilerplate compared to coinduct.
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     7
*)
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     8
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
     9
signature COINDUCTION =
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    10
sig
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    11
  val coinduction_tac: Proof.context -> term list -> thm option -> thm list -> cases_tactic
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    12
end;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    13
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    14
structure Coinduction : COINDUCTION =
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    15
struct
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    16
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    17
fun filter_in_out _ [] = ([], [])
58814
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
    18
  | filter_in_out P (x :: xs) =
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
    19
      let
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
    20
        val (ins, outs) = filter_in_out P xs;
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
    21
      in
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
    22
        if P x then (x :: ins, outs) else (ins, x :: outs)
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
    23
      end;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    24
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    25
fun ALLGOALS_SKIP skip tac st =
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    26
  let fun doall n = if n = skip then all_tac else tac n THEN doall (n - 1)
59582
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
    27
  in doall (Thm.nprems_of st) st  end;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    28
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    29
fun THEN_ALL_NEW_SKIP skip tac1 tac2 i st =
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    30
  st |> (tac1 i THEN (fn st' =>
59582
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
    31
    Seq.INTERVAL tac2 (i + skip) (i + Thm.nprems_of st' - Thm.nprems_of st) st'));
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    32
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    33
fun DELETE_PREMS_AFTER skip tac i st =
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    34
  let
59582
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
    35
    val n = nth (Thm.prems_of st) (i - 1) |> Logic.strip_assums_hyp |> length;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    36
  in
59498
50b60f501b05 proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents: 59158
diff changeset
    37
    (THEN_ALL_NEW_SKIP skip tac (REPEAT_DETERM_N n o eresolve0_tac [thin_rl])) i st
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    38
  end;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    39
59971
ea06500bb092 tuned signature;
wenzelm
parents: 59970
diff changeset
    40
fun coinduction_tac ctxt raw_vars opt_raw_thm prems = HEADGOAL (SUBGOAL_CASES (fn (goal, _) =>
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    41
  let
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    42
    val lhs_of_eq = HOLogic.dest_Trueprop #> HOLogic.dest_eq #> fst;
58814
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
    43
    fun find_coinduct t =
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    44
      Induct.find_coinductP ctxt t @
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    45
      (try (Induct.find_coinductT ctxt o fastype_of o lhs_of_eq) t |> the_default [])
56231
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
    46
    val raw_thm =
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
    47
      (case opt_raw_thm of
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
    48
        SOME raw_thm => raw_thm
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
    49
      | NONE => goal |> Logic.strip_assums_concl |> find_coinduct |> hd);
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    50
    val skip = Integer.max 1 (Rule_Cases.get_consumes raw_thm) - 1
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    51
    val cases = Rule_Cases.get raw_thm |> fst
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    52
  in
56231
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
    53
    HEADGOAL (
54742
7a86358a3c0b proper context for basic Simplifier operations: rewrite_rule, rewrite_goals_rule, rewrite_goals_tac etc.;
wenzelm
parents: 54540
diff changeset
    54
      Object_Logic.rulify_tac ctxt THEN'
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    55
      Method.insert_tac prems THEN'
54742
7a86358a3c0b proper context for basic Simplifier operations: rewrite_rule, rewrite_goals_rule, rewrite_goals_tac etc.;
wenzelm
parents: 54540
diff changeset
    56
      Object_Logic.atomize_prems_tac ctxt THEN'
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    57
      DELETE_PREMS_AFTER skip (Subgoal.FOCUS (fn {concl, context = ctxt, params, prems, ...} =>
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    58
        let
59582
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
    59
          val vars = raw_vars @ map (Thm.term_of o snd) params;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    60
          val names_ctxt = ctxt
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    61
            |> fold Variable.declare_names vars
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    62
            |> fold Variable.declare_thm (raw_thm :: prems);
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    63
          val thm_concl = Thm.cprop_of raw_thm |> strip_imp_concl;
60642
48dd1cefb4ae simplified Thm.instantiate and derivatives: the LHS refers to non-certified variables -- this merely serves as index into already certified structures (or is ignored);
wenzelm
parents: 59971
diff changeset
    64
          val (instT, inst) = Thm.match (thm_concl, concl);
48dd1cefb4ae simplified Thm.instantiate and derivatives: the LHS refers to non-certified variables -- this merely serves as index into already certified structures (or is ignored);
wenzelm
parents: 59971
diff changeset
    65
          val rhoTs = map (fn (v, T) => (TVar v, Thm.typ_of T)) instT;
48dd1cefb4ae simplified Thm.instantiate and derivatives: the LHS refers to non-certified variables -- this merely serves as index into already certified structures (or is ignored);
wenzelm
parents: 59971
diff changeset
    66
          val rhots = map (fn (v, t) => (Var v, Thm.term_of t)) inst;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    67
          val xs = hd (Thm.prems_of raw_thm) |> HOLogic.dest_Trueprop |> strip_comb |> snd
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    68
            |> map (subst_atomic_types rhoTs);
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    69
          val raw_eqs = map (fn x => (x, AList.lookup op aconv rhots x |> the)) xs;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    70
          val ((names, ctxt), Ts) = map_split (apfst fst o dest_Var o fst) raw_eqs
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    71
            |>> (fn names => Variable.variant_fixes names names_ctxt) ;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    72
          val eqs =
58634
9f10d82e8188 added parameterized ML antiquotations @{map N}, @{fold N}, @{fold_map N}, @{split_list N};
wenzelm
parents: 58002
diff changeset
    73
            @{map 3} (fn name => fn T => fn (_, rhs) =>
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    74
              HOLogic.mk_eq (Free (name, T), rhs))
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    75
            names Ts raw_eqs;
59582
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
    76
          val phi = eqs @ map (HOLogic.dest_Trueprop o Thm.prop_of) prems
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    77
            |> try (Library.foldr1 HOLogic.mk_conj)
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    78
            |> the_default @{term True}
59158
wenzelm
parents: 59058
diff changeset
    79
            |> Ctr_Sugar_Util.list_exists_free vars
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    80
            |> Term.map_abs_vars (Variable.revert_fixed ctxt)
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    81
            |> fold_rev Term.absfree (names ~~ Ts)
59621
291934bac95e Thm.cterm_of and Thm.ctyp_of operate on local context;
wenzelm
parents: 59582
diff changeset
    82
            |> Thm.cterm_of ctxt;
59158
wenzelm
parents: 59058
diff changeset
    83
          val thm = Ctr_Sugar_Util.cterm_instantiate_pos [SOME phi] raw_thm;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    84
          val e = length eqs;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    85
          val p = length prems;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    86
        in
59498
50b60f501b05 proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents: 59158
diff changeset
    87
          HEADGOAL (EVERY' [resolve_tac ctxt [thm],
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    88
            EVERY' (map (fn var =>
59498
50b60f501b05 proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents: 59158
diff changeset
    89
              resolve_tac ctxt
59158
wenzelm
parents: 59058
diff changeset
    90
                [Ctr_Sugar_Util.cterm_instantiate_pos
59621
291934bac95e Thm.cterm_of and Thm.ctyp_of operate on local context;
wenzelm
parents: 59582
diff changeset
    91
                  [NONE, SOME (Thm.cterm_of ctxt var)] exI]) vars),
59498
50b60f501b05 proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents: 59158
diff changeset
    92
            if p = 0 then Ctr_Sugar_Util.CONJ_WRAP' (K (resolve_tac ctxt [refl])) eqs
58839
ccda99401bc8 eliminated aliases;
wenzelm
parents: 58814
diff changeset
    93
            else
59498
50b60f501b05 proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents: 59158
diff changeset
    94
              REPEAT_DETERM_N e o (resolve_tac ctxt [conjI] THEN' resolve_tac ctxt [refl]) THEN'
50b60f501b05 proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents: 59158
diff changeset
    95
              Ctr_Sugar_Util.CONJ_WRAP' (resolve_tac ctxt o single) prems,
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    96
            K (ALLGOALS_SKIP skip
59498
50b60f501b05 proper context for resolve_tac, eresolve_tac, dresolve_tac, forward_tac etc.;
wenzelm
parents: 59158
diff changeset
    97
               (REPEAT_DETERM_N (length vars) o (eresolve_tac ctxt [exE] THEN' rotate_tac ~1) THEN'
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    98
               DELETE_PREMS_AFTER 0 (Subgoal.FOCUS (fn {prems, params, context = ctxt, ...} =>
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
    99
                 (case prems of
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   100
                   [] => all_tac
59970
e9f73d87d904 proper context for Object_Logic operations;
wenzelm
parents: 59621
diff changeset
   101
                 | inv :: case_prems =>
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   102
                     let
59970
e9f73d87d904 proper context for Object_Logic operations;
wenzelm
parents: 59621
diff changeset
   103
                       val (init, last) = funpow_yield (p + e - 1) (HOLogic.conj_elim ctxt) inv;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   104
                       val inv_thms = init @ [last];
54366
13bfdbcfbbfb swap equations and premises in the coinductive step for better proof automation
Andreas Lochbihler
parents: 54026
diff changeset
   105
                       val eqs = take e inv_thms;
58814
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   106
                       fun is_local_var t =
59582
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
   107
                         member (fn (t, (_, t')) => t aconv (Thm.term_of t')) params t;
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
   108
                        val (eqs, assms') =
0fbed69ff081 tuned signature -- prefer qualified names;
wenzelm
parents: 59580
diff changeset
   109
                          filter_in_out (is_local_var o lhs_of_eq o Thm.prop_of) eqs;
54366
13bfdbcfbbfb swap equations and premises in the coinductive step for better proof automation
Andreas Lochbihler
parents: 54026
diff changeset
   110
                        val assms = assms' @ drop e inv_thms
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   111
                      in
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   112
                        HEADGOAL (Method.insert_tac (assms @ case_prems)) THEN
59158
wenzelm
parents: 59058
diff changeset
   113
                        Ctr_Sugar_General_Tactics.unfold_thms_tac ctxt eqs
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   114
                      end)) ctxt)))])
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   115
        end) ctxt) THEN'
56231
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
   116
      K (prune_params_tac ctxt))
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
   117
    #> Seq.maps (fn st =>
59970
e9f73d87d904 proper context for Object_Logic operations;
wenzelm
parents: 59621
diff changeset
   118
      CASES (Rule_Cases.make_common ctxt (Thm.prop_of st) cases) all_tac st)
56231
b98813774a63 enforce subgoal boundaries via SUBGOAL/SUBGOAL_CASES -- clean tactical failure if out-of-range;
wenzelm
parents: 55954
diff changeset
   119
  end));
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   120
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   121
local
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   122
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   123
val ruleN = "rule"
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   124
val arbitraryN = "arbitrary"
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   125
fun single_rule [rule] = rule
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   126
  | single_rule _ = error "Single rule expected";
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   127
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   128
fun named_rule k arg get =
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   129
  Scan.lift (Args.$$$ k -- Args.colon) |-- Scan.repeat arg :|--
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   130
    (fn names => Scan.peek (fn context => Scan.succeed (names |> map (fn name =>
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   131
      (case get (Context.proof_of context) name of SOME x => x
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   132
      | NONE => error ("No rule for " ^ k ^ " " ^ quote name))))));
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   133
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   134
fun rule get_type get_pred =
55951
c07d184aebe9 tuned signature -- more uniform check_type_name/read_type_name;
wenzelm
parents: 54742
diff changeset
   135
  named_rule Induct.typeN (Args.type_name {proper = false, strict = false}) get_type ||
55954
a29aefc88c8d more uniform check_const/read_const;
wenzelm
parents: 55951
diff changeset
   136
  named_rule Induct.predN (Args.const {proper = false, strict = false}) get_pred ||
a29aefc88c8d more uniform check_const/read_const;
wenzelm
parents: 55951
diff changeset
   137
  named_rule Induct.setN (Args.const {proper = false, strict = false}) get_pred ||
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   138
  Scan.lift (Args.$$$ ruleN -- Args.colon) |-- Attrib.thms;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   139
59158
wenzelm
parents: 59058
diff changeset
   140
val coinduct_rule =
wenzelm
parents: 59058
diff changeset
   141
  rule Induct.lookup_coinductT Induct.lookup_coinductP >> single_rule;
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   142
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   143
fun unless_more_args scan = Scan.unless (Scan.lift
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   144
  ((Args.$$$ arbitraryN || Args.$$$ Induct.typeN ||
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   145
    Args.$$$ Induct.predN || Args.$$$ Induct.setN || Args.$$$ ruleN) -- Args.colon)) scan;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   146
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   147
val arbitrary = Scan.optional (Scan.lift (Args.$$$ arbitraryN -- Args.colon) |--
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   148
  Scan.repeat1 (unless_more_args Args.term)) [];
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   149
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   150
in
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   151
58814
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   152
val _ =
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   153
  Theory.setup
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   154
    (Method.setup @{binding coinduction}
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   155
      (arbitrary -- Scan.option coinduct_rule >>
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   156
        (fn (arbitrary, opt_rule) => fn ctxt => fn facts =>
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   157
          Seq.DETERM (coinduction_tac ctxt arbitrary opt_rule facts)))
4c0ad4162cb7 modernized setup;
wenzelm
parents: 58634
diff changeset
   158
      "coinduction on types or predicates/sets");
54026
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   159
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   160
end;
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   161
82d9b2701a03 new coinduction method
traytel
parents:
diff changeset
   162
end;