(* ========================================================================= *)
(* Geometric algebra.                                                        *)
(*                                                                           *)
(*              (c) Copyright, John Harrison 1998-2008                       *)
(* ========================================================================= *)
needs "Multivariate/vectors.ml";;
needs "Library/binary.ml";;
prioritize_real();;
(* ------------------------------------------------------------------------- *)
(* Some basic lemmas, mostly set theory.                                     *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Index type for "multivectors" (k-vectors for all k <= N).                 *)
(* ------------------------------------------------------------------------- *)
let multivector_tybij =
  new_type_definition "multivector" ("mk_multivector","dest_multivector")
    multivector_tybij_th;;
let DEST_MK_MULTIVECTOR = prove
 (`!s. s 
SUBSET 1..dimindex(:N)
         ==> dest_multivector(mk_multivector s :(N)multivector) = s`,
  REPEAT STRIP_TAC THEN GEN_REWRITE_TAC I [GSYM multivector_tybij] THEN
  ASM_REWRITE_TAC[]);;
 
 
(* ------------------------------------------------------------------------- *)
(* The bijections we use for indexing.                                       *)
(*                                                                           *)
(* Note that we need a *single* bijection over the entire space that also    *)
(* works for the various subsets. Hence the tedious explicit construction.   *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Indexing directly via subsets.                                            *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("$$",(25,"left"));;
parse_as_binder "lambdas";;
(* ------------------------------------------------------------------------- *)
(* Crucial properties.                                                       *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Also componentwise operations; they all work in this style.               *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Basis vectors indexed by subsets of 1..N.                                 *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Linear and bilinear functions are determined by their effect on basis.    *)
(* ------------------------------------------------------------------------- *)
let LINEAR_EQ_MBASIS = prove
 (`!f:real^(M)multivector->real^N g b s.
        linear f /\ linear g /\
        (!s. s 
SUBSET 1..dimindex(:M) ==> f(mbasis s) = g(mbasis s))
        ==> f = g`,
 
 
let BILINEAR_EQ_MBASIS = prove
 (`!f:real^(M)multivector->real^(N)multivector->real^P g b s.
        bilinear f /\ bilinear g /\
        (!s t.  s 
SUBSET 1..dimindex(:M) /\ t 
SUBSET 1..dimindex(:N)
                ==> f (mbasis s) (mbasis t) = g (mbasis s) (mbasis t))
        ==> f = g`,
  REPEAT STRIP_TAC THEN SUBGOAL_THEN
   `!x y. x 
IN UNIV /\ y 
IN UNIV
          ==> (f:real^(M)multivector->real^(N)multivector->real^P) x y = g x y`
   (fun th -> MP_TAC th THEN REWRITE_TAC[
FUN_EQ_THM; 
IN_UNIV]) THEN
  MATCH_MP_TAC 
BILINEAR_EQ THEN
  EXISTS_TAC `{mbasis s :real^(M)multivector | s 
SUBSET 1..dimindex(:M)}` THEN
  EXISTS_TAC `{mbasis t :real^(N)multivector | t 
SUBSET 1..dimindex(:N)}` THEN
  ASM_REWRITE_TAC[
SPAN_MBASIS; 
SUBSET_REFL; 
IN_ELIM_THM] THEN
  ASM_MESON_TAC[]);;
 
 
(* ------------------------------------------------------------------------- *)
(* A way of proving linear properties by extension from basis.               *)
(* ------------------------------------------------------------------------- *)
let MBASIS_EXTENSION = prove
 (`!P. (!s. s 
SUBSET 1..dimindex(:N) ==> P(mbasis s)) /\
       (!c x. P x ==> P(c % x)) /\ (!x y. P x /\ P y ==> P(x + y))
       ==> !x:real^(N)multivector. P x`,
 
 
(* ------------------------------------------------------------------------- *)
(* Injection from regular vectors.                                           *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Subspace of k-vectors.                                                    *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("multivector",(12,"right"));;
(* ------------------------------------------------------------------------- *)
(* k-grade part of a multivector.                                            *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("grade",(22,"right"));;
(* ------------------------------------------------------------------------- *)
(* General product construct.                                                *)
(* ------------------------------------------------------------------------- *)
let Product_DEF = new_definition
 `(Product mult op
     :real^(N)multivector->real^(N)multivector->real^(N)multivector) x y =
        vsum {s | s SUBSET 1..dimindex(:N)}
         (\s. vsum {s | s SUBSET 1..dimindex(:N)}
                (\t. (x$$s * y$$t * mult s t) % mbasis (op s t)))`;; 
(* ------------------------------------------------------------------------- *)
(* This is always bilinear.                                                  *)
(* ------------------------------------------------------------------------- *)
let PRODUCT_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_PRODUCT;;
let PRODUCT_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_PRODUCT;;
(* ------------------------------------------------------------------------- *)
(* Under suitable conditions, it's also associative.                         *)
(* ------------------------------------------------------------------------- *)
let PRODUCT_ASSOCIATIVE = prove
 (`!op mult. (!s t. s 
SUBSET 1..dimindex(:N) /\ t 
SUBSET 1..dimindex(:N)
                    ==> (op s t) 
SUBSET 1..dimindex(:N)) /\
             (!s t u. op s (op t u) = op (op s t) u) /\
             (!s t u. mult t u * mult s (op t u) = mult s t * mult (op s t) u)
             ==> !x y z:real^(N)multivector.
                        Product mult op x (Product mult op y z) =
                        Product mult op (Product mult op x y) z`,
  let SUM_SWAP_POWERSET =
    SIMP_RULE[
FINITE_POWERSET; 
FINITE_NUMSEG]
     (repeat(SPEC `{s | s 
SUBSET 1..dimindex(:N)}`)
        (ISPEC `f:(num->bool)->(num->bool)->real` 
SUM_SWAP)) in
  let SWAP_TAC cnv n =
    GEN_REWRITE_TAC (cnv o funpow n BINDER_CONV) [SUM_SWAP_POWERSET] THEN
    REWRITE_TAC[] in
  let SWAPS_TAC cnv ns x =
    MAP_EVERY (SWAP_TAC cnv) ns THEN MATCH_MP_TAC 
SUM_EQ THEN X_GEN_TAC x THEN
    REWRITE_TAC[
IN_ELIM_THM] THEN STRIP_TAC in
  REWRITE_TAC[
Product_DEF] THEN REPEAT STRIP_TAC THEN
  SIMP_TAC[
MULTIVECTOR_EQ; 
MULTIVECTOR_VSUM_COMPONENT; 
MBASIS_COMPONENT;
           
MULTIVECTOR_MUL_COMPONENT] THEN
  SIMP_TAC[GSYM 
SUM_LMUL; GSYM 
SUM_RMUL] THEN
  X_GEN_TAC `r:num->bool` THEN STRIP_TAC THEN
  SWAPS_TAC RAND_CONV [1;0] `s:num->bool` THEN
  SWAP_TAC LAND_CONV 0 THEN SWAPS_TAC RAND_CONV [1;0] `t:num->bool` THEN
  SWAP_TAC RAND_CONV 0 THEN SWAPS_TAC LAND_CONV [0] `u:num->bool` THEN
  REWRITE_TAC[GSYM REAL_MUL_ASSOC;
    REAL_ARITH `(if p then a else &0) * b = if p then a * b else &0`;
    REAL_ARITH `a * (if p then b else &0) = if p then a * b else &0`] THEN
  SIMP_TAC[
SUM_DELTA] THEN ASM_SIMP_TAC[
IN_ELIM_THM] THEN
  COND_CASES_TAC THEN ASM_REWRITE_TAC[REAL_MUL_LID; 
REAL_MUL_RID] THEN
  ASM_REWRITE_TAC[] THEN REWRITE_TAC[
REAL_MUL_AC]);;
 
 
(* ------------------------------------------------------------------------- *)
(* Geometric product.                                                        *)
(* ------------------------------------------------------------------------- *)
overload_interface
 ("*",
  `geom_mul:real^(N)multivector->real^(N)multivector->real^(N)multivector`);;
let geom_mul = new_definition
 `(x:real^(N)multivector) * y =
        Product (\s t. --(&1) pow CARD {i,j | i IN 1..dimindex(:N) /\
                                              j IN 1..dimindex(:N) /\
                                              i IN s /\ j IN t /\ i > j})
                (\s t. (s DIFF t) UNION (t DIFF s))
                x y`;; 
let GEOM_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_GEOM;;
let GEOM_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_GEOM;;
(* ------------------------------------------------------------------------- *)
(* Outer product.                                                            *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("outer",(20,"right"));;
let OUTER = prove
 (`!x y:real^(N)multivector.
    x outer y =
        Product (\s t. if ~(s 
INTER t = {}) then &0
                       else --(&1) pow 
CARD {i,j | i 
IN 1..dimindex(:N) /\
                                                   j 
IN 1..dimindex(:N) /\
                                                   i 
IN s /\ j 
IN t /\ i > j})
                (
UNION)
                x y`,
 
 
let OUTER_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_OUTER;;
let OUTER_ASSOC = prove
 (`!x y z:real^(N)multivector. x outer (y outer z) = (x outer y) outer z`,
 
 
(* ------------------------------------------------------------------------- *)
(* Inner product.                                                            *)
(* ------------------------------------------------------------------------- *)
parse_as_infix("inner",(20,"right"));;
let inner = new_definition
 `!x y:real^(N)multivector.
    x inner y =
        Product (\s t. if s = {} \/ t = {} \/
                          ~((s DIFF t) = {} /\ ~(t DIFF s = {}))
                       then &0
                       else --(&1) pow CARD {i,j | i IN 1..dimindex(:N) /\
                                                   j IN 1..dimindex(:N) /\
                                                   i IN s /\ j IN t /\ i > j})
                (\s t. (s DIFF t) UNION (t DIFF s))
                x y`;; 
let INNER_LADD = (MATCH_MP BILINEAR_LADD o SPEC_ALL) BILINEAR_INNER;;
let INNER_RADD = (MATCH_MP BILINEAR_RADD o SPEC_ALL) BILINEAR_INNER;;
let INNER_LMUL = (MATCH_MP BILINEAR_LMUL o SPEC_ALL) BILINEAR_INNER;;
let INNER_RMUL = (MATCH_MP BILINEAR_RMUL o SPEC_ALL) BILINEAR_INNER;;
let INNER_LNEG = (MATCH_MP BILINEAR_LNEG o SPEC_ALL) BILINEAR_INNER;;
let INNER_RNEG = (MATCH_MP BILINEAR_RNEG o SPEC_ALL) BILINEAR_INNER;;
let INNER_LZERO = (MATCH_MP BILINEAR_LZERO o SPEC_ALL) BILINEAR_INNER;;
let INNER_RZERO = (MATCH_MP BILINEAR_RZERO o SPEC_ALL) BILINEAR_INNER;;
(* ------------------------------------------------------------------------- *)
(* Actions of products on basis and singleton basis.                         *)
(* ------------------------------------------------------------------------- *)
let PRODUCT_MBASIS = prove
 (`!s t. Product mult op (mbasis s) (mbasis t) :real^(N)multivector =
            if s 
SUBSET 1..dimindex(:N) /\ t 
SUBSET 1..dimindex(:N)
            then mult s t % mbasis(op s t)
            else vec 0`,
 
 
let PRODUCT_MBASIS_SING = prove
 (`!i j. Product mult op (mbasis{i}) (mbasis{j}) :real^(N)multivector =
            if i 
IN 1..dimindex(:N) /\ j 
IN 1..dimindex(:N)
            then mult {i} {j} % mbasis(op {i} {j})
            else vec 0`,
 
 
let GEOM_MBASIS_SING = prove
 (`!i j. mbasis{i} * mbasis{j} :real^(N)multivector =
                if i 
IN 1..dimindex(:N) /\ j 
IN 1..dimindex(:N)
                then if i = j then mbasis{}
                     else if i < j then mbasis{i,j}
                     else --(mbasis{i,j})
                else vec 0`,
 
 
let OUTER_MBASIS_SING = prove
 (`!i j. mbasis{i} outer mbasis{j} :real^(N)multivector =
                if i 
IN 1..dimindex(:N) /\ j 
IN 1..dimindex(:N) /\ ~(i = j)
                then if i < j then mbasis{i,j} else --(mbasis{i,j})
                else vec 0`,
 
 
(* ------------------------------------------------------------------------- *)
(* Some simple consequences.                                                 *)
(* ------------------------------------------------------------------------- *)
let OUTER_MBASIS_SKEWSYM = prove
 (`!i j. mbasis{i} outer mbasis{j} = --(mbasis{j} outer mbasis{i})`,
  REPEAT GEN_TAC THEN REWRITE_TAC[
OUTER_MBASIS_SING] THEN
  ASM_CASES_TAC `i:num = j` THEN ASM_REWRITE_TAC[
VECTOR_NEG_0] THEN
  FIRST_ASSUM(DISJ_CASES_TAC o MATCH_MP (ARITH_RULE
   `~(i:num = j) ==> i < j /\ ~(j < i) \/ j < i /\ ~(i < j)`)) THEN
  ASM_REWRITE_TAC[
CONJ_ACI] THEN COND_CASES_TAC THEN
  ASM_REWRITE_TAC[
VECTOR_NEG_NEG; 
VECTOR_NEG_0] THEN
  REPEAT AP_TERM_TAC THEN SET_TAC[]);;
 
 
(* ------------------------------------------------------------------------- *)
(* Just for generality, normalize a set enumeration.                         *)
(* ------------------------------------------------------------------------- *)
let SETENUM_NORM_CONV =
  let conv =
    GEN_REWRITE_CONV I [EXTENSION] THENC
    GEN_REWRITE_CONV TOP_SWEEP_CONV [IN_SING; IN_INSERT] THENC
    BINDER_CONV(EQT_INTRO o DISJ_ACI_RULE) THENC
    GEN_REWRITE_CONV I [FORALL_SIMP] in
  fun tm ->
    let nums = dest_setenum tm in
    let nums' = map mk_numeral (sort (</) (map dest_numeral (setify nums))) in
    if nums' = nums then REFL tm else
    let eq = mk_eq(tm,mk_setenum(nums',fst(dest_fun_ty(type_of tm)))) in
    EQT_ELIM(conv eq);;
(* ------------------------------------------------------------------------- *)
(* Conversion to split extended basis combinations.                          *)
(*                                                                           *)
(* Also 1-step merge from left, which can be DEPTH_CONV'd. In this case the  *)
(* order must be correct.                                                    *)
(* ------------------------------------------------------------------------- *)
let MBASIS_SPLIT_CONV,MBASIS_MERGE_CONV =
  let setlemma = SET_RULE
   `((!x:num. x IN {} ==> a < x) <=> T) /\
    ((!x:num. x IN (y INSERT s) ==> a < x) <=>
              a < y /\ (!x. x IN s ==> a < x))` in
  let SET_CHECK_CONV =
    GEN_REWRITE_CONV TOP_SWEEP_CONV [setlemma] THENC NUM_REDUCE_CONV
  and INST_SPLIT = PART_MATCH (lhs o rand) MBASIS_SPLIT
  and INST_MERGE = PART_MATCH (lhs o rand) (GSYM MBASIS_SPLIT) in
  let rec conv tm =
    if length(dest_setenum(rand tm)) <= 1 then REFL tm else
    let th = MP_CONV SET_CHECK_CONV (INST_SPLIT tm) in
    let th' = RAND_CONV conv (rand(concl th)) in
    TRANS th th' in
  (fun tm ->
    try let op,se = dest_comb tm in
        if fst(dest_const op) = "mbasis" & forall is_numeral (dest_setenum se)
        then (RAND_CONV SETENUM_NORM_CONV THENC conv) tm
        else fail()
    with Failure _ -> failwith "MBASIS_SPLIT_CONV"),
  (fun tm -> try MP_CONV SET_CHECK_CONV (INST_MERGE tm)
             with Failure _ -> failwith "MBASIS_MERGE_CONV");;
(* ------------------------------------------------------------------------- *)
(* Convergent (if slow) rewrite set to bubble into position.                 *)
(* ------------------------------------------------------------------------- *)
let OUTER_ACI = prove
 (`(!x y z. (x outer y) outer z = x outer (y outer z)) /\
   (!i j. i > j
          ==> mbasis{i} outer mbasis{j} =
              --(&1) % (mbasis{j} outer mbasis{i})) /\
   (!i j x. i > j
            ==> mbasis{i} outer mbasis{j} outer x =
                --(&1) % (mbasis{j} outer mbasis{i} outer x)) /\
   (!i. mbasis{i} outer mbasis{i} = vec 0) /\
   (!i x. mbasis{i} outer mbasis{i} outer x = vec 0) /\
   (!x. mbasis{} outer x = x) /\
   (!x. x outer mbasis{} = x)`,
 
 
(* ------------------------------------------------------------------------- *)
(* Group the final "c1 % mbasis s1 + ... + cn % mbasis sn".                  *)
(* ------------------------------------------------------------------------- *)
let MBASIS_GROUP_CONV tm =
  let tms = striplist(dest_binary "vector_add") tm in
  if length tms = 1 then LAND_CONV REAL_POLY_CONV tm else
  let vadd_tm = rator(rator tm) in
  let mk_vadd = mk_binop vadd_tm in
  let mbs = map (snd o dest_binary "%") tms in
  let tmbs = zip mbs tms and mset = setify mbs in
  let grps = map (fun x -> map snd (filter (fun (x',_) -> x' = x) tmbs))
                 mset in
  let tm' = end_itlist mk_vadd (map (end_itlist mk_vadd) grps) in
  let th1 = AC VECTOR_ADD_AC (mk_eq(tm,tm'))
  and th2 =
   (GEN_REWRITE_CONV DEPTH_CONV [GSYM VECTOR_ADD_RDISTRIB] THENC
    DEPTH_BINOP_CONV vadd_tm (LAND_CONV REAL_POLY_CONV)) tm' in
  TRANS th1 th2;;
(* ------------------------------------------------------------------------- *)
(* Overall conversion.                                                       *)
(* ------------------------------------------------------------------------- *)
let OUTER_CANON_CONV =
  ONCE_DEPTH_CONV MBASIS_SPLIT_CONV THENC
  GEN_REWRITE_CONV TOP_DEPTH_CONV
   [VECTOR_SUB; VECTOR_NEG_MINUS1;
    OUTER_LADD; OUTER_RADD; OUTER_LMUL; OUTER_RMUL; OUTER_LZERO; OUTER_RZERO;
    VECTOR_ADD_LDISTRIB; VECTOR_ADD_RDISTRIB; VECTOR_MUL_ASSOC;
    VECTOR_MUL_LZERO; VECTOR_MUL_RZERO] THENC
  REAL_RAT_REDUCE_CONV THENC
  PURE_SIMP_CONV[OUTER_ACI; ARITH_GT; ARITH_GE; OUTER_LMUL; OUTER_RMUL;
                 OUTER_LZERO; OUTER_RZERO] THENC
  PURE_REWRITE_CONV[VECTOR_MUL_LZERO; VECTOR_MUL_RZERO;
                    VECTOR_ADD_LID; VECTOR_ADD_RID; VECTOR_MUL_ASSOC] THENC
  GEN_REWRITE_CONV I [GSYM VECTOR_MUL_LID] THENC
  PURE_REWRITE_CONV
   [VECTOR_ADD_LDISTRIB; VECTOR_ADD_RDISTRIB; VECTOR_MUL_ASSOC] THENC
  REAL_RAT_REDUCE_CONV THENC PURE_REWRITE_CONV[GSYM VECTOR_ADD_ASSOC] THENC
  DEPTH_CONV MBASIS_MERGE_CONV THENC
  MBASIS_GROUP_CONV THENC
  GEN_REWRITE_CONV DEPTH_CONV [GSYM VECTOR_ADD_RDISTRIB] THENC
  REAL_RAT_REDUCE_CONV;;
(* ------------------------------------------------------------------------- *)
(* Iterated operation in order.                                              *)
(* I guess this ought to be added to the core...                             *)
(* ------------------------------------------------------------------------- *)
let seqiterate_EXISTS = prove
 (`!op f. ?h.
        !s. h s = if 
INFINITE s \/ s = {} then neutral op else
                  let i = minimal x. x 
IN s in
                  if s = {i} then f(i) else op (f i) (h (s 
DELETE i))`,
  REPEAT GEN_TAC THEN REWRITE_TAC[
INFINITE] THEN
  MATCH_MP_TAC(MATCH_MP 
WF_REC (ISPEC `CARD:(num->bool)->num` 
WF_MEASURE)) THEN
  REPEAT STRIP_TAC THEN COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN
  LET_TAC THEN CONV_TAC(ONCE_DEPTH_CONV let_CONV) THEN
  COND_CASES_TAC THEN ASM_REWRITE_TAC[] THEN AP_TERM_TAC THEN
  FIRST_X_ASSUM MATCH_MP_TAC THEN REWRITE_TAC[
MEASURE] THEN
  RULE_ASSUM_TAC(REWRITE_RULE[DE_MORGAN_THM]) THEN
  SUBGOAL_THEN `?i:num. i 
IN s` MP_TAC THENL
   [ASM_MESON_TAC[
MEMBER_NOT_EMPTY]; ALL_TAC] THEN
  GEN_REWRITE_TAC LAND_CONV [
MINIMAL] THEN
  ASM_SIMP_TAC[
CARD_DELETE; 
CARD_EQ_0; ARITH_RULE `n - 1 < n <=> ~(n = 0)`]);;
 
 
let EXISTS_SWAP = prove
 (`!P. (?f. P f) <=> (?f:A->B->C. P (\b a. f a b))`,
  GEN_TAC THEN EQ_TAC THEN DISCH_THEN CHOOSE_TAC THENL
   [EXISTS_TAC `\a b. (f:B->A->C) b a` THEN ASM_REWRITE_TAC[ETA_AX];
    ASM_MESON_TAC[]]);;
 
 
let SEQITERATE_CLAUSES = prove
 (`(!op f. seqiterate op {} f = neutral op) /\
   (!op f i. seqiterate op {i} f = f(i)) /\
   (!op f i s. 
FINITE s /\ ~(s = {}) /\ (!j. j 
IN s ==> i < j)
               ==> seqiterate op (i 
INSERT s) f =
                   op (f i) (seqiterate op s f))`,
 
 
(* ------------------------------------------------------------------------- *)
(* In the "common" case this agrees with ordinary iteration.                 *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Outermorphism extension.                                                  *)
(* ------------------------------------------------------------------------- *)
(* ------------------------------------------------------------------------- *)
(* Reversion operation.                                                      *)
(* ------------------------------------------------------------------------- *)