Definition: A-eval
A-eval(AModel) ==  snd(snd(snd(snd(snd(snd(snd(snd(snd(snd(AModel))))))))))

Definition: A-return
A-return(AModel) ==  fst(AModel)

Definition: A-pre-val
A-pre-val(AType;A;i) ==  idx(AType) A

Definition: monad
This definition of monad is the one that functional programmers use.
There is categorical  definition of monad in the section on category theory,
and proof that from monad as defined here, one can construct category
theoretical monad over the category of Types.⋅

Monad ==
  M:Type ⟶ Type
  × return:⋂T:Type. (T ⟶ (M T))
  × bind:⋂T,S:Type.  ((M T) ⟶ (T ⟶ (M S)) ⟶ (M S))
  × leftunit:∀[T,S:Type]. ∀[x:T]. ∀[f:T ⟶ (M S)].  ((bind (return x) f) (f x) ∈ (M S))
  × rightunit:∀[T:Type]. ∀[m:M T].  ((bind return) m ∈ (M T))
  × (∀[T,S,U:Type]. ∀[m:M T]. ∀[f:T ⟶ (M S)]. ∀[g:S ⟶ (M U)].
       ((bind (bind f) g) (bind x.(bind (f x) g))) ∈ (M U)))

Lemma: monad_wf
Monad ∈ 𝕌'

Definition: M-map
M-map(mnd) ==  fst(mnd)

Lemma: M-map_wf
[Mnd:Monad]. (M-map(Mnd) ∈ Type ⟶ Type)

Definition: M-return
M-return(Mnd) ==  fst(snd(Mnd))

Lemma: M-return_wf
[Mnd:Monad]. (M-return(Mnd) ∈ ⋂T:Type. (T ⟶ (M-map(Mnd) T)))

Definition: M-bind
M-bind(Mnd) ==  fst(snd(snd(Mnd)))

Lemma: M-bind_wf
[Mnd:Monad]. (M-bind(Mnd) ∈ ⋂T,S:Type.  ((M-map(Mnd) T) ⟶ (T ⟶ (M-map(Mnd) S)) ⟶ (M-map(Mnd) S)))

Lemma: M-leftunit
[Mnd:Monad]. ∀[T,S:Type]. ∀[x:T]. ∀[f:T ⟶ (M-map(Mnd) S)].
  ((M-bind(Mnd) (M-return(Mnd) x) f) (f x) ∈ (M-map(Mnd) S))

Lemma: M-rightunit
[Mnd:Monad]. ∀[T:Type]. ∀[m:M-map(Mnd) T].  ((M-bind(Mnd) M-return(Mnd)) m ∈ (M-map(Mnd) T))

Lemma: M-associative
[Mnd:Monad]. ∀[T,S,U:Type]. ∀[m:M-map(Mnd) T]. ∀[f:T ⟶ (M-map(Mnd) S)]. ∀[g:S ⟶ (M-map(Mnd) U)].
  ((M-bind(Mnd) (M-bind(Mnd) f) g) (M-bind(Mnd) x.(M-bind(Mnd) (f x) g))) ∈ (M-map(Mnd) U))

Definition: mk_monad
mk_monad(M;return;bind) ==  <M, return, bind, Ax, Ax, Ax>

Lemma: mk_monad_wf
[M:Type ⟶ Type]. ∀[return:⋂T:Type. (T ⟶ (M T))]. ∀[bind:⋂T,S:Type.  ((M T) ⟶ (T ⟶ (M S)) ⟶ (M S))].
  (mk_monad(M;return;bind) ∈ Monad) supposing 
     ((∀[T,S,U:Type]. ∀[m:M T]. ∀[f:T ⟶ (M S)]. ∀[g:S ⟶ (M U)].
         ((bind (bind f) g) (bind x.(bind (f x) g))) ∈ (M U))) and 
     (∀[T:Type]. ∀[m:M T].  ((bind return) m ∈ (M T))) and 
     (∀[T,S:Type]. ∀[x:T].  ∀f:T ⟶ (M S). ((bind (return x) f) (f x) ∈ (M S))))

Lemma: member-concat-map
[T,S:Type].  ∀f:T ⟶ (S List). ∀L:T List. ∀x:S.  ((x ∈ concat(map(f;L))) ⇐⇒ (∃t∈L. (x ∈ t)))

Definition: list-monad
ListMonad ==  mk_monad(λT.(T List);λx.[x];λL,f. concat(map(f;L)))

Lemma: list-monad_wf
ListMonad ∈ Monad

Comment: Notes on arrays
An element of array(Val;n) is structure that implements arrays of length n
holding values of type Val.  It's tuple containing type (whose elements
are the array values) and operations 

   idx   -- that returns the value at given index
   upd   -- that returns an array with an updated entry
   newarrray -- that returns an array with all values initialized
                to given value

These are the operations used for illustrative purposes in Wadlers' paper
"Monads for Functional Programming."  Thus, we can treat an array as an
abstract datatype by simply carrying around an array(Val;n) as parameter.

(Note: we'll need more if we want to model arrays that are only partially
filled -- and, e.g., be able to prove that we're not trying to access an
uninitialized slot.)

Following Wadler's paper we define two monads:

    array-monad(AType) 

takes as its argument an AType array(Val;n) for someVal and n.  This is 
about operations that potentially transform the state of an array,

    array-monad'(AType) 

is just about reading, without transforming, an array.

Wadler then puts together an abstract data type from their operations,
together with coercion function and some other operations defined in terms
of the array ops.   Ideally, we'd make this into an abstract
data type, too -- but that's not so easy, because it doesn't seem to satisfy
simple first-order axioms.  (And I'm not sure we'd gain anything by defining
the abstract data type by existential quantification.)  

The signature of that abstract data type (plus some extra operations) is given by

   array-model-type(AType;Val;n)

(Each of these takes an initial parameter that is an instance of an array-model.) 

For members of this type we introduce the selectors that pluck out its
operations

   A-return, A-bind, A-block, A-assign,
   A-return', A-bind', A-fetch',
   A-coerce

The extra operations are

   A-map
   A-map'

A-map and A-map' export the two monad "M" functions, but hide the underlying types.  
As far as array-model-type is concerned, A-map and A-map' simply have the
type Type -> Type.  (Note that the wf lemmas for the other eight operations
are expressed in terms of A-map and A-map', not the "M" functions implement
them.) 

So now we ought to be able to write array programs using just these ten
operations.  To make these readable we need good display forms -- which, for
now, is deferred.

Consider simple-swap-test.  Given the computation prog, which returns a
value in Unit

   simple-swap-test(...;prog;i;j)

is computation that returns value in Bool whose meaning is that prog has 
swapped the values at indices and j.  (Oops  -- forgot to say that all other
indices have the same value.)  The spec of swap program is that

  given any initial state A, 
     assert (pi1 (simple-swap-test(...;prog;i;j) A))

We can't do that application directly.  We have to break the abstraction by 
 including anoperation

  A-eval(AModel):  isect{T:Type} (A-map(array-model(AType)) T) -> Arr(AType) -> T

defined as 

  eval(AModel) == \m,A. pi1(m A) 

Then we the specification becomes

  forall i,j,k: N_n. forall A:Arr(AType). 
          assert(A-eval(AModel) simple-swap-test(...;prog;i;j) A))

The eval operation would be purely specification construct, not available
for writing programs.  Or is there better way to do this?  (Maybe the problem here
is to have made the whole state single array; thus, the initial state does not
really represent single data object for the program to manipulate.)

Also ... how do we representthe return value of a? 



Definition: array
array{i:l}(Val;n) ==
  Arr:Type
  × idx:ℕn ⟶ Arr ⟶ Val
  × upd:ℕn ⟶ Val ⟶ Arr ⟶ Arr
  × newarray:Val ⟶ Arr
  × ∀[i:ℕn]. ∀[v:Val].  ((idx (newarray v)) v ∈ Val)
  × (∀[i,j:ℕn]. ∀[x:Arr]. ∀[v:Val].  ((idx (upd x)) if (i =z j) then else idx fi  ∈ Val))

Lemma: array_wf
[Val:Type]. ∀[n:ℕ].  (array{i:l}(Val;n) ∈ 𝕌')

Lemma: array_subtype
[Val1,Val2:Type]. ∀[m,n:ℕ].
  (array{i:l}(Val1;m) ⊆array{i:l}(Val2;n)) supposing ((n ≤ m) and ((Val1 ⊆Val2) ∧ (Val2 ⊆Val1)))

Definition: Arr
Arr(AType) ==  fst(AType)

Lemma: Arr_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (Arr(AType) ∈ Type)

Definition: idx
idx(AType) ==  fst(snd(AType))

Lemma: idx_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (idx(AType) ∈ ℕn ⟶ Arr(AType) ⟶ Val)

Definition: upd
upd(AType) ==  fst(snd(snd(AType)))

Lemma: upd_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (upd(AType) ∈ ℕn ⟶ Val ⟶ Arr(AType) ⟶ Arr(AType))

Definition: newarray
newarray(AType) ==  fst(snd(snd(snd(AType))))

Lemma: newarray_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (newarray(AType) ∈ Val ⟶ Arr(AType))

Definition: mk_array
mk_array(Arr;idx;upd;newarray) ==  <Arr, idx, upd, newarray, Ax, Ax>

Lemma: mk_array_wf
[Val:Type]. ∀[n:ℕ]. ∀[Arr:Type]. ∀[idx:ℕn ⟶ Arr ⟶ Val]. ∀[upd:ℕn ⟶ Val ⟶ Arr ⟶ Arr]. ∀[newarray:Val ⟶ Arr].
  mk_array(Arr;idx;upd;newarray) ∈ array{i:l}(Val;n) 
  supposing (∀i:ℕn. ∀v:Val.  ((idx (newarray v)) v ∈ Val))
  ∧ (∀i,j:ℕn. ∀x:Arr. ∀v:Val.  ((idx (upd x)) if (i =z j) then else idx fi  ∈ Val))

Definition: fn_array
fn_array{i:l}(Val;n) ==  mk_array(ℕn ⟶ Val;λi,f. (f i);λi,v,f,k. if (k =z i) then else fi v,i. v)

Lemma: fn_array_wf
[Val:Type]. ∀[n:ℕ].  (fn_array{i:l}(Val;n) ∈ array{i:l}(Val;n))

Definition: array-monad
array-monad(AType) ==
  mk_monad(λT.(Arr(AType) ⟶ (T × Arr(AType)));λv,A. <v, A>m,k,A. let fst((m A)) in let A' snd((m A)) in A')

Lemma: array-monad_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (array-monad(AType) ∈ Monad)

Definition: array-monad'
array-monad'(AType) ==  mk_monad(λT.(Arr(AType) ⟶ T);λv,A. v;λm,k,A. (k (m A) A))

Lemma: array-monad'_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (array-monad'(AType) ∈ Monad)

Definition: array-model-type
array-model-type{i:l}(AType;Val;n) ==
  let Mnd array-monad(AType) in
   let Mnd' array-monad'(AType) in
   ⋂T:Type. (T ⟶ (M-map(Mnd) T))
   × ⋂T,S:Type.  ((M-map(Mnd) T) ⟶ (T ⟶ (M-map(Mnd) S)) ⟶ (M-map(Mnd) S))
   × ⋂T:Type. (Val ⟶ (M-map(Mnd) T) ⟶ T)
   × ℕn ⟶ Val ⟶ (M-map(Mnd) Unit)
   × ⋂T:Type. (T ⟶ (M-map(Mnd') T))
   × ⋂T,S:Type.  ((M-map(Mnd') T) ⟶ (T ⟶ (M-map(Mnd') S)) ⟶ (M-map(Mnd') S))
   × ℕn ⟶ (M-map(Mnd') Val)
   × ⋂T:Type. ((M-map(Mnd') T) ⟶ (M-map(Mnd) T))
   × Type ⟶ Type
   × Type ⟶ Type
   × (⋂T:Type. ((Arr(AType) ⟶ (T × Arr(AType))) ⟶ Arr(AType) ⟶ T))

Lemma: array-model-type_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (array-model-type{i:l}(AType;Val;n) ∈ 𝕌')

Definition: array-model
array-model(AType) ==
  <M-return(array-monad(AType))
  M-bind(array-monad(AType))
  , λv,m. (fst((m (newarray(AType) v))))
  , λi,v,A. <⋅upd(AType) A>
  M-return(array-monad'(AType))
  M-bind(array-monad'(AType))
  , λi,A. (idx(AType) A)
  , λm,A. <A, A>
  M-map(array-monad(AType))
  M-map(array-monad'(AType))
  , λm,A. (fst((m A)))>

Lemma: array-model_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (array-model(AType) ∈ array-model-type{i:l}(AType;Val;n))

Definition: A-map
A-map ==  fst(snd(snd(snd(snd(snd(snd(snd(snd(AModel)))))))))

Lemma: A-map_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (A-map ∈ Type ⟶ Type)

Definition: A-map'
A-map'(AModel) ==  fst(snd(snd(snd(snd(snd(snd(snd(snd(snd(AModel))))))))))

Lemma: A-map'_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (A-map'(array-model(AType)) ∈ Type ⟶ Type)

Lemma: A-eval_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (A-eval(array-model(AType)) ∈ ⋂T:Type. ((A-map T) ⟶ Arr(AType) ⟶ T))

Lemma: A-eval_wf2
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T:Type]. ∀[m:A-map T]. ∀[A:Arr(AType)].
  (A-eval(array-model(AType)) A ∈ T)

Lemma: A-return_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (A-return(array-model(AType)) ∈ ⋂T:Type. (T ⟶ (A-map T)))

Definition: A-bind
A-bind(AModel) ==  fst(snd(AModel))

Lemma: A-bind_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T,S:Type].
  (A-bind(array-model(AType)) ∈ (A-map T) ⟶ (T ⟶ (A-map S)) ⟶ (A-map S))

Lemma: A-bind_wf2
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  (A-bind(array-model(AType)) ∈ ⋂T,S:Type.  ((A-map T) ⟶ (T ⟶ (A-map S)) ⟶ (A-map S)))

Lemma: A-bind_wf3
[AType:⋃Val:Type.⋃n:ℕ.array{i:l}(Val;n)]
  (A-bind(array-model(AType)) ∈ ⋂T,S:Type.  ((A-map T) ⟶ (T ⟶ (A-map S)) ⟶ (A-map S)))

Definition: A-block
A-block(AModel) ==  fst(snd(snd(AModel)))

Lemma: A-block_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T:Type].
  (A-block(array-model(AType)) ∈ ⋂T:Type. (Val ⟶ (A-map T) ⟶ T))

Definition: A-assign
A-assign(AModel) ==  fst(snd(snd(snd(AModel))))

Lemma: A-assign_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (A-assign(array-model(AType)) ∈ ℕn ⟶ Val ⟶ (A-map Unit))

Definition: A-return'
A-return'(AModel) ==  fst(snd(snd(snd(snd(AModel)))))

Lemma: A-return'_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  (A-return'(array-model(AType)) ∈ ⋂T:Type. (T ⟶ (A-map'(array-model(AType)) T)))

Definition: A-bind'
A-bind'(AModel) ==  fst(snd(snd(snd(snd(snd(AModel))))))

Lemma: A-bind'_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T,S:Type].
  (A-bind'(array-model(AType)) ∈ (A-map'(array-model(AType)) T)
   ⟶ (T ⟶ (A-map'(array-model(AType)) S))
   ⟶ (A-map'(array-model(AType)) S))

Definition: A-fetch'
A-fetch'(AModel) ==  fst(snd(snd(snd(snd(snd(snd(AModel)))))))

Lemma: A-fetch'_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  (A-fetch'(array-model(AType)) ∈ ℕn ⟶ (A-map'(array-model(AType)) Val))

Definition: A-coerce
A-coerce(AModel) ==  fst(snd(snd(snd(snd(snd(snd(snd(AModel))))))))

Lemma: A-coerce_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  (A-coerce(array-model(AType)) ∈ ⋂T:Type. ((A-map'(array-model(AType)) T) ⟶ (A-map T)))

Definition: A-fetch
A-fetch(AModel) ==  λi.(A-coerce(AModel) (A-fetch'(AModel) i))

Lemma: A-fetch_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (A-fetch(array-model(AType)) ∈ ℕn ⟶ (A-map Val))

Lemma: A-leftunit
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T,S:Type]. ∀[x:T]. ∀[f:T ⟶ (A-map S)].
  ((A-bind(array-model(AType)) (A-return(array-model(AType)) x) f) (f x) ∈ (A-map S))

Lemma: A-leftunit'
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T,S:Type]. ∀[x:T]. ∀[f:T ⟶ (A-map'(array-model(AType)) S)].
  ((A-bind'(array-model(AType)) (A-return'(array-model(AType)) x) f) (f x) ∈ (A-map'(array-model(AType)) S))

Lemma: A-rightunit
Val:Type. ∀n:ℕ. ∀AType:array{i:l}(Val;n). ∀T:Type. ∀m:A-map T.
  ((A-bind(array-model(AType)) A-return(array-model(AType))) m ∈ (A-map T))

Lemma: A-rightunit'
Val:Type. ∀n:ℕ. ∀AType:array{i:l}(Val;n). ∀T:Type. ∀m:A-map'(array-model(AType)) T.
  ((A-bind'(array-model(AType)) A-return'(array-model(AType))) m ∈ (A-map'(array-model(AType)) T))

Lemma: A-associative
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T,S,U:Type]. ∀[m:A-map T]. ∀[f:T ⟶ (A-map S)]. ∀[g:S ⟶ (A-map U)].
  ((A-bind(array-model(AType)) (A-bind(array-model(AType)) f) g)
  (A-bind(array-model(AType)) x.(A-bind(array-model(AType)) (f x) g)))
  ∈ (A-map U))

Lemma: A-associative'
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T,S,U:Type]. ∀[m:A-map'(array-model(AType)) T].
[f:T ⟶ (A-map'(array-model(AType)) S)]. ∀[g:S ⟶ (A-map'(array-model(AType)) U)].
  ((A-bind'(array-model(AType)) (A-bind'(array-model(AType)) f) g)
  (A-bind'(array-model(AType)) x.(A-bind'(array-model(AType)) (f x) g)))
  ∈ (A-map'(array-model(AType)) U))

Definition: A-post-val
A-post-val(AType;prog;A;i) ==
  A-eval(array-model(AType)) 
  (A-bind(array-model(AType)) prog x.(A-coerce(array-model(AType)) (A-fetch'(array-model(AType)) i)))) 
  A

Lemma: A-post-val_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[prog:A-map Unit]. ∀[A:Arr(AType)]. ∀[i:ℕn].
  (A-post-val(AType;prog;A;i) ∈ Val)

Lemma: A-pre-val_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[A:Arr(AType)]. ∀[i:ℕn].  (A-pre-val(AType;A;i) ∈ Val)

Definition: A-null
A-null(AType) ==  A-return(array-model(AType)) ⋅

Lemma: A-null_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].  (A-null(AType) ∈ A-map Unit)

Lemma: A-null-property
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  ∀A:Arr(AType). ∀k:ℕn.  (A-post-val(AType;A-null(AType);A;k) A-pre-val(AType;A;k) ∈ Val)

Lemma: fetch'-commutes
[Val,S:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[prog:Val ⟶ Val ⟶ (A-map'(array-model(AType)) S)].
  ∀j,k:ℕn.
    ((A-bind'(array-model(AType)) (A-fetch'(array-model(AType)) k) 
      val@k.(A-bind'(array-model(AType)) (A-fetch'(array-model(AType)) j) val@j.(prog val@k val@j)))))
    (A-bind'(array-model(AType)) (A-fetch'(array-model(AType)) j) 
       val@j.(A-bind'(array-model(AType)) (A-fetch'(array-model(AType)) k) val@k.(prog val@k val@j)))))
    ∈ (A-map'(array-model(AType)) S))

Lemma: coerce-fetch'-commutes
[Val,S:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[prog:Val ⟶ Val ⟶ (A-map S)].
  ∀j,k:ℕn.
    ((A-bind(array-model(AType)) (A-coerce(array-model(AType)) (A-fetch'(array-model(AType)) k)) 
      val@k.(A-bind(array-model(AType)) (A-coerce(array-model(AType)) (A-fetch'(array-model(AType)) j)) 
               val@j.(prog val@k val@j)))))
    (A-bind(array-model(AType)) (A-coerce(array-model(AType)) (A-fetch'(array-model(AType)) j)) 
       val@j.(A-bind(array-model(AType)) (A-coerce(array-model(AType)) (A-fetch'(array-model(AType)) k)) 
                val@k.(prog val@k val@j)))))
    ∈ (A-map S))

Definition: A-loop
A-loop(AType;lo;hi;body) ==
  fix((λA-loop,hi. if hi ≤lo
                  then A-null(AType)
                  else A-bind(array-model(AType)) (A-loop (hi 1)) x.(body (hi 1)))
                  fi )) 
  hi

Lemma: A-loop_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  ∀lo:ℕn. ∀k:ℕ.  (k < lo  (∀[body:{lo..lo k-} ⟶ (A-map Unit)]. (A-loop(AType;lo;lo k;body) ∈ A-map Unit)))

Lemma: A-loop_wf2
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  ∀lo,hi:ℕn.  ∀[body:{lo..hi-} ⟶ (A-map Unit)]. (A-loop(AType;lo;hi;body) ∈ A-map Unit)

Lemma: A-null-loop
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)].
  ∀lo,hi:ℕn. ∀body:{lo..hi-} ⟶ (A-map Unit).  ((hi ≤ lo)  (A-loop(AType;lo;hi;body) A-null(AType) ∈ (A-map Unit)))

Definition: simple-swap-test
simple-swap-test(AModel;prog;i;j) ==
  A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) i)) 
  in@i.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) j)) 
          in@j.(A-bind(AModel) prog 
                  x.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) i)) 
                       out@i.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) j)) 
                                out@j.(A-return(AModel) ((out@i =z in@j) ∧b (out@j =z in@i))))))))))))

Lemma: simple-swap-test_wf
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)].
  let AModel array-model(AType) in
      ∀[prog:A-map Unit]. ∀[i,j:ℕn].  (simple-swap-test(AModel;prog;i;j) ∈ A-map 𝔹)

Definition: simple-swap-test2
simple-swap-test2(AModel;prog;i;j;k) ==
  A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) i)) 
  in@i.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) j)) 
          in@j.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) k)) 
                  in@k.(A-bind(AModel) prog 
                          x.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) i)) 
                               out@i.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) j)) 
                                        out@j.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) k)) 
                                                 out@k.(A-return(AModel) 
                                                          (((out@i =z in@j) ∧b (out@j =z in@i))
                                                          ∧b ((¬b(k =z i))
                                                             ∧b b(k =z j)) b (out@k =z in@k)))))))))))))))))

Lemma: simple-swap-test2_wf
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)]. ∀[prog:A-map Unit]. ∀[i,j,k:ℕn].
  (simple-swap-test2(array-model(AType);prog;i;j;k) ∈ A-map 𝔹)

Definition: simple-swap-specification
simple-swap-specification(AType;n;prog;i;j) ==
  ∀k:ℕn. ∀A:Arr(AType).  (↑(A-eval(array-model(AType)) simple-swap-test2(array-model(AType);prog;i;j;k) A))

Lemma: simple-swap-specification_wf
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)]. ∀[prog:A-map Unit].  ∀i,j:ℕn.  (simple-swap-specification(AType;n;prog;i;j) ∈ ℙ)

Definition: simple-swap
simple-swap(AModel;i;j) ==
  A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) i)) 
  in@i.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) j)) 
          in@j.(A-bind(AModel) (A-assign(AModel) in@j) x.(A-assign(AModel) in@i))))))

Lemma: simple-swap_wf
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)]. ∀[i,j:ℕn].  (simple-swap(array-model(AType);i;j) ∈ A-map Unit)

Lemma: simple-swap-correct
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)]. ∀[prog:A-map Unit].
  ∀i,j:ℕn.  simple-swap-specification(AType;n;simple-swap(array-model(AType);i;j);i;j)

Definition: simple-swap2
simple-swap2(AModel;i;j) ==
  A-bind(AModel) (A-fetch(AModel) i) 
  in@i.(A-bind(AModel) (A-fetch(AModel) j) 
          in@j.(A-bind(AModel) (A-assign(AModel) in@j) x.(A-assign(AModel) in@i))))))

Lemma: simple-swap2_wf
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)]. ∀[i,j:ℕn].  (simple-swap2(array-model(AType);i;j) ∈ A-map Unit)

Definition: A-bind2
m >> \x.k[x] ==  A-bind(AModel) y.k[y])

Lemma: A-bind2_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[T,S:Type]. ∀[m:A-map T]. ∀[k:T ⟶ (A-map S)].
  (m >> \x.
   k[x] ∈ A-map S)

Definition: A-fetch2
A-fetch2(AModel;i) ==  A-fetch(AModel) i

Lemma: A-fetch2_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[i:ℕn].  (A-fetch2(array-model(AType);i) ∈ A-map Val)

Definition: A-assign2
A-assign2(AModel;i;v) ==  A-assign(AModel) v

Lemma: A-assign2_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[i:ℕn]. ∀[v:Val].  (A-assign2(array-model(AType);i;v) ∈ A-map Unit)

Definition: simple-swap3
simple-swap3(AModel;i;j) ==
  A-fetch2(AModel;i) >> \in@i.
  A-fetch2(AModel;j) >> \in@j.
  A-assign2(AModel;i;in@j) >> \%1.
  A-assign2(AModel;j;in@i)

Lemma: simple-swap3_wf
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)]. ∀[i,j:ℕn].  (simple-swap3(array-model(AType);i;j) ∈ A-map Unit)

Definition: alt-swap-spec
alt-swap-spec(AType;n;prog) ==
  ∀A:Arr(AType). ∀i,j,k:ℕn.
    (((A-post-val(AType;prog j;A;j) A-pre-val(AType;A;i) ∈ ℤ)
    ∧ (A-post-val(AType;prog j;A;i) A-pre-val(AType;A;j) ∈ ℤ))
    ∧ (((¬(k i ∈ ℤ)) ∧ (k j ∈ ℤ)))  (A-post-val(AType;prog j;A;k) A-pre-val(AType;A;k) ∈ ℤ)))

Lemma: alt-swap-spec_wf2
[n:ℕ]. ∀[AType:array{i:l}(ℤ;n)]. ∀[prog:ℕn ⟶ ℕn ⟶ (A-map Unit)]. ∀[i,j:ℕn].  (alt-swap-spec(AType;n;prog) ∈ ℙ)

Lemma: swap-exists
n:ℕ. ∀AType:array{i:l}(ℤ;n).  ∃prog:ℕn ⟶ ℕn ⟶ (A-map Unit). ∀[i,j:ℕn].  alt-swap-spec(AType;n;prog)

Definition: A-shift-spec
A-shift-spec(AType; Val; n; prog) ==
  ∀A:Arr(AType). ∀i:ℕn.
    ((i <  (A-post-val(AType;prog;A;i) A-pre-val(AType;A;i 1) ∈ Val))
    ∧ (A-post-val(AType;prog;A;n 1) A-pre-val(AType;A;0) ∈ Val))

Lemma: A-shift-spec_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[prog:A-map Unit].  (A-shift-spec(AType; Val; n; prog) ∈ ℙ)

Definition: A-shift-upto-spec
A-shift-upto-spec(AType; Val; n; prog; j) ==
  ∀A:Arr(AType). ∀i:ℕn.
    if i <1
    then A-post-val(AType;prog;A;i) A-pre-val(AType;A;i 1) ∈ Val
    else A-post-val(AType;prog;A;i) A-pre-val(AType;A;i) ∈ Val
    fi 

Lemma: A-shift-upto-spec_wf
[Val:Type]. ∀[n:ℕ]. ∀[AType:array{i:l}(Val;n)]. ∀[prog:A-map Unit]. ∀[j:ℕn].
  (A-shift-upto-spec(AType; Val; n; prog; j) ∈ ℙ)

Definition: A-shift-upto
A-shift-upto(AType;j) ==
  let AModel array-model(AType) in
      A-loop(AType;1;j;λi.(A-bind(AModel) (A-coerce(AModel) (A-fetch'(AModel) i)) 
                           in@i.(A-assign(AModel) (i 1) in@i))))

Lemma: A-shift-upto_wf
[Val:Type]. ∀[n:ℕ].  (1 <  (∀[AType:array{i:l}(Val;n)]. ∀[j:ℕn].  (A-shift-upto(AType;j) ∈ A-map Unit)))

Definition: mtype
mtype(M) ==  fst(M)

Lemma: mtype_wf
[Mn:Monad]. (mtype(Mn) ∈ Type ⟶ Type)

Lemma: provisional-equiv
[T:𝕌']. EquivRel(ok:ℙ × supposing ↓ok;x,y.(↓fst(x) ⇐⇒ ↓fst(y)) ∧ ((↓fst(x))  ((snd(x)) (snd(y)) ∈ T)))

Definition: provisional-type
Provisional(T) ==  x,y:ok:ℙ × supposing ↓ok//((↓fst(x) ⇐⇒ ↓fst(y)) ∧ ((↓fst(x))  ((snd(x)) (snd(y)) ∈ T)))

Lemma: provisional-type_wf
[T:𝕌']. (Provisional(T) ∈ 𝕌')

Lemma: provisional-type-cumulativity
[T:𝕌']. (Provisional(T) ⊆Provisional'(T))

Definition: provision
provision(ok; v) ==  <ok, v>

Lemma: provision_wf
[T:𝕌']. ∀[ok:ℙ]. ∀[v:T supposing ok].  (provision(ok; v) ∈ Provisional(T))

Lemma: provision-equality
[T:𝕌']. ∀[ok1,ok2:ℙ]. ∀[v1:⋂:↓ok1. T]. ∀[v2:⋂:↓ok2. T].
  (provision(ok1; v1) provision(ok2; v2) ∈ Provisional(T)) supposing (((↓ok1)  (v1 v2 ∈ T)) and (↓ok1 ⇐⇒ ↓ok2))

Definition: allowed
allowed(x) ==  usquash(fst(x))

Lemma: allowed_wf
[T:𝕌']. ∀[x:Provisional(T)].  (allowed(x) ∈ ℙ)

Lemma: sq_stable__allowed
[T:𝕌']. ∀[x:Provisional(T)].  SqStable(allowed(x))

Lemma: allowed-trivial-iff
[T:𝕌']. ∀[x:Provisional(T)]. ∀[X:ℙ].  (SqStable(X)  allowed(x) ⇐⇒ usquash(allowed(x) ∧ X) supposing X)

Definition: allow
allow(x) ==  snd(x)

Lemma: allow_wf
[T:𝕌']. ∀[x:Provisional(T)].  allow(x) ∈ supposing allowed(x)

Lemma: provisional-type-equality
[T:𝕌']. ∀[x,y:Provisional(T)].
  (x y ∈ Provisional(T)) supposing ((allowed(x)  (allow(x) allow(y) ∈ T)) and (allowed(x) ⇐⇒ allowed(y)))

Definition: bind-provision
bind-provision(x;t.f[t]) ==  provision(allowed(x) ∧ allowed(f[allow(x)]); allow(f[allow(x)]))

Lemma: bind-provision_wf
[T,S:𝕌']. ∀[x:Provisional(T)]. ∀[f:{t:T| allowed(x) ∧ (t allow(x) ∈ T)}  ⟶ Provisional(S)].
  (bind-provision(x;t.f[t]) ∈ Provisional(S))

Definition: provisional-monad
provisional-monad{i:l}() ==  mk_monad(λT.Provisional(T);λx.OK(x);λx,f. bind-provision(x;u.f u))

Lemma: provisional-monad_wf
provisional-monad{i:l}() ∈ Monad'

Lemma: provisional-type-wf2
[T:𝕌'']. (Provisional(T) ∈ 𝕌'')

Lemma: provisional-subtype
[T,S:𝕌'].  Provisional(T) ⊆Provisional(S) supposing T ⊆S

Lemma: allowed_provision_lemma
v,ok:Top.  (allowed(provision(ok; v)) usquash(ok))

Lemma: allow_provision_lemma
v,ok:Top.  (allow(provision(ok; v)) v)

Definition: provisional-apply2
provisional-apply2(f;a;b) ==  provision(allowed(a) ∧ allowed(b); allow(a) allow(b))

Lemma: provisional-apply2_wf
[A,B,C:Type]. ∀[f:A ⟶ B ⟶ C]. ∀[a:Provisional(A)]. ∀[b:Provisional(B)].  (provisional-apply2(f;a;b) ∈ Provisional(C))



Home Index