(**

{1 Defunctionalization}

This is inspired from student exercises proposed by
{h Olivier Danvy (http://cs.au.dk/~danvy/)}
at the {h JFLA 2014 conference (http://jfla.inria.fr/2014/)}

*)

(** {2 Simple Arithmetic Expressions} *)

module Expr

use export int.Int

(** Grammar of expressions
{h 

n  :  int

e  :  expression
e ::= n | e - e

p  :  program
p ::= e

}
*)

type expr = Cte int | Sub expr expr

type prog = expr

(** Examples:
{h 

p0 = 0
p1 = 10 - 6
p2 = (10 - 6) - (7 - 2)
p3 = (7 - 2) - (10 - 6)
p4 = 10 - (2 - 3)

}
*)

let constant p0 : prog = Cte 0

let constant p1 : prog = Sub (Cte 10) (Cte 6)

let constant p2 : prog = Sub (Sub (Cte 10) (Cte 6)) (Sub (Cte 7) (Cte 2))

let constant p3 : prog = Sub (Sub (Cte 7) (Cte 2)) (Sub (Cte 10) (Cte 6))

let constant p4 : prog = Sub (Cte 10) (Sub (Cte 2) (Cte 3))

end

(** {2 Direct Semantics} *)

module DirectSem

use Expr

(** Values:
{h 

v  :  value
v ::= n

}
Expressible Values:
{h 

ve  :  expressible_value
ve ::= v

}
Interpretation:
{h 

------
n => n

e1 => n1   e2 => n2   n1 - n2 = n3
----------------------------------
      e1 - e2 => n3

}
A program e is interpreted into a value n if judgement
{h 

  e => n

}
holds.
*)

(** {4 Exercise 0.0}
  Program the interpreter above and test it on the examples.
{h 

  eval_0 : expression -> expressible_value
  interpret_0 : program -> expressible_value

}
*)

(* Note: Why3 definitions introduced by "function" belong to the logic
   part of Why3 language *)

let rec function eval_0 (e:expr) : int =
  match e with
  | Cte n -> n
  | Sub e1 e2 -> eval_0 e1 - eval_0 e2
  end

let function interpret_0 (p:prog) : int = eval_0 p

(** Tests, can be replayed using
{h 

  why3 defunctionalization.mlw --exec DirectSem.test

}
(Why3 version at least 0.82 required)

*)

let test () =
   interpret_0 p0,
   interpret_0 p1,
   interpret_0 p2,
   interpret_0 p3,
   interpret_0 p4

constant v3 : int = eval_0 p3

goal eval_p3 : v3 = 1

end

(** {2 CPS: Continuation Passing Style} *)

module CPS

use Expr

(** {4 Exercise 0.1}

  CPS-transform (call by value, left to right) the function `eval_0`,
  and call it from the main interpreter with the identity function as
  initial continuation
{h 

      eval_1 : expression -> (expressible_value -> 'a) -> 'a
  interpret_1 : program -> expressible_value

}

*)

use DirectSem

let rec eval_1 (e:expr) (k: int->'a) : 'a
  variant { e }
  ensures { result = k (eval_0 e) }
= match e with
    | Cte n -> k n
    | Sub e1 e2 ->
      eval_1 e1 (fun v1 -> eval_1 e2 (fun v2 -> k (v1 - v2)))
  end

let interpret_1 (p : prog) : int
  ensures { result = eval_0 p }
= eval_1 p (fun n -> n)

end

(** {2 Defunctionalization} *)

module Defunctionalization

use Expr
use DirectSem

(** {4 Exercise 0.2}

  De-functionalize the continuation of `eval_1`.
{h 

         cont ::= ...

   continue_2 : cont -> value -> value
       eval_2 : expression -> cont -> value
  interpret_2 : program -> value

}
  The data type `cont` represents the grammar of contexts.

  The two mutually recursive functions `eval_2` and `continue_2`
  implement an abstract machine, that is a state transition system.

*)

type cont = A1 expr cont | A2 int cont | I

(** One would want to write in Why:
{h 

function eval_cont (c:cont) (v:int) : int =
  match c with
  | A1 e2 k ->
    let v2 = eval_0 e2 in
    eval_cont (A2 v k) v2
  | A2 v1 k -> eval_cont k (v1 - v)
  | I -> v
  end

}
But since the recursion is not structural, Why3 kernel rejects it
(definitions in the logic part of the language must be total)

We replace that with a relational definition, an inductive one.

*)

inductive eval_cont cont int int =
| a1 :
  forall e2:expr, k:cont, v:int, r:int.
  eval_cont (A2 v k) (eval_0 e2) r -> eval_cont (A1 e2 k) v r
| a2 :
  forall v1:int, k:cont, v:int, r:int.
  eval_cont k (v1 - v) r -> eval_cont (A2 v1 k) v r
| a3 :
  forall v:int. eval_cont I v v

(** Some functions to serve as measures for the termination proof *)

function size_e (e:expr) : int =
  match e with
  | Cte _ -> 1
  | Sub e1 e2 -> 3 + size_e e1 + size_e e2
  end

lemma size_e_pos: forall e: expr. size_e e >= 1

function size_c (c:cont) : int =
  match c with
  | I -> 0
  | A1 e2 k -> 2 + size_e e2 + size_c k
  | A2 _ k -> 1 + size_c k
  end

lemma size_c_pos: forall c: cont. size_c c >= 0

(** WhyML programs (declared with `let` instead of `function`),
    mutually recursive, resulting from de-functionalization *)

let rec continue_2 (c:cont) (v:int) : int
  variant { size_c c }
  ensures { eval_cont c v result }
  =
  match c with
    | A1 e2 k -> eval_2 e2 (A2 v k)
    | A2 v1 k -> continue_2 k (v1 - v)
    | I -> v
  end

with eval_2 (e:expr) (c:cont) : int
  variant { size_c c + size_e e }
  ensures { eval_cont c (eval_0 e) result }
  =
  match e with
    | Cte n -> continue_2 c n
    | Sub e1 e2 -> eval_2 e1 (A1 e2 c)
  end

(** The interpreter. The post-condition specifies that this program
    computes the same thing as `eval_0` *)

let interpret_2 (p:prog) : int
  ensures { result = eval_0 p }
  = eval_2 p I

let test () =
   interpret_2 p0,
   interpret_2 p1,
   interpret_2 p2,
   interpret_2 p3,
   interpret_2 p4

end


(** {2 Defunctionalization with an algebraic variant} *)

module Defunctionalization2

use Expr
use DirectSem

(** {4 Exercise 0.2}

  De-functionalize the continuation of `eval_1`.
{h 

         cont ::= ...

   continue_2 : cont -> value -> value
       eval_2 : expression -> cont -> value
  interpret_2 : program -> value

}
  The data type `cont` represents the grammar of contexts.

  The two mutually recursive functions `eval_2` and `continue_2`
  implement an abstract machine, that is a state transition system.

*)

type cont = A1 expr cont | A2 int cont | I

(** One would want to write in Why:
{h 

function eval_cont (c:cont) (v:int) : int =
  match c with
  | A1 e2 k ->
    let v2 = eval_0 e2 in
    eval_cont (A2 v k) v2
  | A2 v1 k -> eval_cont k (v1 - v)
  | I -> v
  end

}
But since the recursion is not structural, Why3 kernel rejects it
(definitions in the logic part of the language must be total)

We replace that with a relational definition, an inductive one.

*)

inductive eval_cont cont int int =
| a1 :
  forall e2:expr, k:cont, v:int, r:int.
  eval_cont (A2 v k) (eval_0 e2) r -> eval_cont (A1 e2 k) v r
| a2 :
  forall v1:int, k:cont, v:int, r:int.
  eval_cont k (v1 - v) r -> eval_cont (A2 v1 k) v r
| a3 :
  forall v:int. eval_cont I v v

(** Peano naturals to serve as the measure for the termination proof *)

type nat = S nat | O

function size_e (e:expr) (acc:nat) : nat =
  match e with
  | Cte _ -> S acc
  | Sub e1 e2 -> S (size_e e1 (S (size_e e2 (S acc))))
  end

function size_c (c:cont) (acc:nat) : nat =
  match c with
  | I -> acc
  | A1 e2 k -> S (size_e e2 (S (size_c k acc)))
  | A2 _ k -> S (size_c k acc)
  end

(** WhyML programs (declared with `let` instead of `function`),
   mutually recursive, resulting from de-functionalization *)

let rec continue_2 (c:cont) (v:int) : int
  variant { size_c c O }
  ensures { eval_cont c v result }
  =
  match c with
    | A1 e2 k -> eval_2 e2 (A2 v k)
    | A2 v1 k -> continue_2 k (v1 - v)
    | I -> v
  end

with eval_2 (e:expr) (c:cont) : int
  variant { size_e e (size_c c O) }
  ensures { eval_cont c (eval_0 e) result }
  =
  match e with
    | Cte n -> continue_2 c n
    | Sub e1 e2 -> eval_2 e1 (A1 e2 c)
  end

(** The interpreter. The post-condition specifies that this program
    computes the same thing as `eval_0` *)

let interpret_2 (p:prog) : int
  ensures { result = eval_0 p }
  = eval_2 p I

let test () =
   interpret_2 p0,
   interpret_2 p1,
   interpret_2 p2,
   interpret_2 p3,
   interpret_2 p4

end

(** {2 Semantics with errors} *)

module SemWithError

use Expr

(** Errors:
{h 

s  :  error

}
Expressible values:
{h 

ve  :  expressible_value
ve ::= v | s

}
*)

type value = Vnum int | Underflow
(* in (Vnum n), n should always be >= 0 *)

(** Interpretation:
{h 

------
n => n

     e1 => s
------------
e1 - e2 => s

e1 => n1   e2 => s
------------------
      e1 - e2 => s

e1 => n1   e2 => n2   n1 < n2
-----------------------------
      e1 - e2 => "underflow"

e1 => n1   e2 => n2   n1 >= n2   n1 - n2 = n3
---------------------------------------------
      e1 - e2 => n3

}
We interpret the program `e` into value `n` if the judgement
{h 

  e => n

}
holds, and into error `s` if the judgement
{h 

  e => s

}
holds.

{4 Exercise 1.0}

  Program the interpreter above and test it on the examples.
{h 

  eval_0 : expr -> expressible_value
  interpret_0 : program -> expressible_value

}
*)

function eval_0 (e:expr) : value =
  match e with
  | Cte n -> if n >= 0 then Vnum n else Underflow
  | Sub e1 e2 ->
     match eval_0 e1 with
     | Underflow -> Underflow
     | Vnum v1 ->
       match eval_0 e2 with
       | Underflow -> Underflow
       | Vnum v2 ->
         if v1 >= v2 then Vnum (v1 - v2) else Underflow
       end
     end
  end

function interpret_0 (p:prog) : value = eval_0 p

(** {4 Exercise 1.1}
  CPS-transform (call by value, from left to right)
  the function `eval_0`, call it from the main interpreter
  with the identity function as initial continuation.
{h 

      eval_1 : expr -> (expressible_value -> 'a) -> 'a
  interpret_1 : program -> expressible_value

}
*)

function eval_1 (e:expr) (k:value -> 'a) : 'a =
  match e with
    | Cte n -> k (if n >= 0 then Vnum n else Underflow)
    | Sub e1 e2 ->
      eval_1 e1 (fun v1 ->
       match v1 with
       | Underflow -> k Underflow
       | Vnum v1 ->
         eval_1 e2 (fun v2 ->
         match v2 with
         | Underflow -> k Underflow
         | Vnum v2 -> k (if v1 >= v2 then Vnum (v1 - v2) else Underflow)
         end)
       end)
  end

function interpret_1 (p : prog) : value = eval_1 p (fun n ->  n)

lemma cps_correct_expr:
  forall e: expr. forall k:value -> 'a. eval_1 e k = k (eval_0 e)

lemma cps_correct: forall p. interpret_1 p = interpret_0 p

(** {4 Exercise 1.2}

  Divide the continuation
{h 

    expressible_value -> 'a

}
  in two:
{h 

    (value -> 'a) * (error -> 'a)

}
  and adapt `eval_1` and `interpret_1` as
{h 

       eval_2 : expr -> (value -> 'a) -> (error -> 'a) -> 'a
  interpret_2 : program -> expressible_value

}
*)

(*
function eval_2 (e:expr) (k:int -> 'a) (kerr: unit -> 'a) : 'a =
  match e with
    | Cte n -> if n >= 0 then k n else kerr ()
    | Sub e1 e2 ->
      eval_2 e1 (fun v1 ->
         eval_2 e2 (fun v2 ->
           if v1 >= v2 then k (v1 - v2) else kerr ())
           kerr) kerr
  end
*)

function eval_2 (e:expr) (k:int -> 'a) (kerr: unit -> 'a) : 'a =
  match e with
    | Cte n -> if n >= 0 then k n else kerr ()
    | Sub e1 e2 ->
      eval_2 e1 (eval_2a e2 k kerr) kerr
  end

with eval_2a (e2:expr) (k:int -> 'a) (kerr : unit -> 'a) : int -> 'a =
  (fun v1 ->  eval_2 e2 (eval_2b v1 k kerr) kerr)

with eval_2b (v1:int) (k:int -> 'a) (kerr : unit -> 'a) : int -> 'a =
  (fun v2 ->  if v1 >= v2 then k (v1 - v2) else kerr ())

function interpret_2 (p : prog) : value =
  eval_2 p (fun n ->  Vnum n) (fun _ ->  Underflow)

lemma cps2_correct_expr_aux:
  forall k: int -> 'a, e1 e2, kerr: unit -> 'a.
  eval_2 (Sub e1 e2) k kerr = eval_2 e1 (eval_2a e2 k kerr) kerr

lemma cps2_correct_expr:
  forall e, kerr: unit->'a, k:int -> 'a. eval_2 e k kerr =
  match eval_0 e with Vnum n -> k n | Underflow -> kerr () end

lemma cps2_correct: forall p. interpret_2 p = interpret_0 p

(** {4 Exercise 1.3}

  Specialize the codomain of the continuations and of the evaluation function
  so that it is not polymorphic anymore but is `expressible_value`, and
  then short-circuit the second continuation to stop in case of error
{h 

       eval_3 : expr -> (value -> expressible_value) -> expressible_value
  interpret_3 : program -> expressible_value

}
  NB: Now there is only one continuation and it is applied only in
  absence of error.

*)

function eval_3 (e:expr) (k:int -> value) : value =
  match e with
    | Cte n -> if n >= 0 then k n else Underflow
    | Sub e1 e2 ->
      eval_3 e1 (eval_3a e2 k)
  end

with eval_3a (e2:expr) (k:int -> value) : int -> value =
  (fun v1 ->  eval_3 e2 (eval_3b v1 k))

with eval_3b (v1:int) (k:int -> value) : int -> value =
  (fun v2 ->  if v1 >= v2 then k (v1 - v2) else Underflow)

function interpret_3 (p : prog) : value = eval_3 p (fun n ->  Vnum n)

let rec lemma cps3_correct_expr (e:expr) : unit
  variant { e }
  ensures { forall k. eval_3 e k =
    match eval_0 e with Vnum n -> k n | Underflow -> Underflow end }
= match e with
  | Cte _ -> ()
  | Sub e1 e2 ->
      cps3_correct_expr e1;
      cps3_correct_expr e2;
      assert {forall k. eval_3 e k = eval_3 e1 (eval_3a e2 k) }
  end

lemma cps3_correct: forall p. interpret_3 p = interpret_0 p

(** {4 Exercise 1.4}
  De-functionalize the continuation of `eval_3`.
{h 

         cont ::= ...

    continue_4 : cont -> value -> expressible_value
        eval_4 : expr -> cont -> expressible_value
  interprete_4 : program -> expressible_value

}
  The data type `cont` represents the grammar of contexts.
  (NB. has it changed w.r.t to previous exercise?)

  The two mutually recursive functions `eval_4` and `continue_4`
  implement an abstract machine, that is a transition system that
  stops immediately in case of error, or and the end of computation.

*)

type cont = I | A expr cont | B int cont

(**

One would want to write in Why:
{h 

function eval_cont (c:cont) (v:value) : value =
  match v with
  | Underflow -> Underflow
  | Vnum v ->
  match c with
  | A e2 k -> eval_cont (B (Vnum v) k) (eval_0 e2)
  | B v1 k -> eval_cont k (if v1 >= v then Vnum (v1 - v) else Underflow)
  | I -> Vnum v (* v >= 0 by construction *)
  end

}
But since the recursion is not structural, Why3 kernel rejects it
(definitions in the logic part of the language must be total)

We replace that with a relational definition, an inductive one.

*)

inductive eval_cont cont value value =
| underflow :
  forall k:cont. eval_cont k Underflow Underflow
| a1 :
  forall e2:expr, k:cont, v:int, r:value.
  eval_cont (B v k) (eval_0 e2) r -> eval_cont (A e2 k) (Vnum v) r
| a2 :
  forall v1:int, k:cont, v:int, r:value.
  eval_cont k (if v1 >= v then Vnum (v1 - v) else Underflow) r
  -> eval_cont (B v1 k) (Vnum v) r
| a3 :
  forall v:int. eval_cont I (Vnum v) (Vnum v)

(** Some functions to serve as measures for the termination proof *)

function size_e (e:expr) : int =
  match e with
  | Cte _ -> 1
  | Sub e1 e2 -> 3 + size_e e1 + size_e e2
  end

lemma size_e_pos: forall e: expr. size_e e >= 1

use Defunctionalization as D

function size_c (c:cont) : int =
  match c with
  | I -> 0
  | A e2 k -> 2 + D.size_e e2 + size_c k
  | B _ k -> 1 + size_c k
  end

lemma size_c_pos: forall c: cont. size_c c >= 0

let rec continue_4 (c:cont) (v:int) : value
  requires { v >= 0 }
  variant { size_c c }
  ensures { eval_cont c (Vnum v) result }
  =
  match c with
    | A e2 k -> eval_4 e2 (B v k)
    | B v1 k -> if v1 >= v then continue_4 k (v1 - v) else Underflow
    | I -> Vnum v
  end

with eval_4 (e:expr) (c:cont) : value
  variant { size_c c + D.size_e e }
  ensures { eval_cont c (eval_0 e) result }
  =
  match e with
    | Cte n -> if n >= 0 then continue_4 c n else Underflow
    | Sub e1 e2 -> eval_4 e1 (A e2 c)
  end

(** The interpreter. The post-condition specifies that this program
    computes the same thing as `eval_0` *)

let interpret_4 (p:prog) : value
  ensures { result = eval_0 p }
  = eval_4 p I

let test () =
   interpret_4 p0,
   interpret_4 p1,
   interpret_4 p2,
   interpret_4 p3,
   interpret_4 p4

end

(** {2 Reduction Semantics} *)

module ReductionSemantics

(**
A small step semantics, defined iteratively with reduction contexts
*)

  use Expr
  use DirectSem

(**
Expressions:
{h 

n  :  int

e  :  expression
e ::= n | e - e

p  :  program
p ::= e

}

Values:
{h 

v  :  value
v ::= n

}

Potential Redexes:
{h 

  rp ::= n1 - n2

}

Reduction Rule:
{h 

  n1 - n2 -> n3

}
(in the case of relative integers Z, all potential redexes are true redexes;
 but for natural numbers, not all of them are true ones:
{h 

   n1 - n2 -> n3   if n1 >= n2

}
a potential redex that is not a true one is stuck.)

Contraction Function:
{h 

  contract : redex_potentiel -> expression + stuck
  contract (n1 - n2) = n3   si n3 = n1 - n2

}
and if there are only non-negative numbers:
{h 

  contract (n1 - n2) = n3     if n1 >= n2 and n3 = n1 - n2
  contract (n1 - n2) = stuck  if n1 < n2

}
*)

predicate is_a_redex (e:expr) =
  match e with
  | Sub (Cte _) (Cte _) -> true
  | _ -> false
  end

let contract (e:expr) : expr
  requires { is_a_redex e }
  ensures { eval_0 result = eval_0 e }
  =
  match e with
  | Sub (Cte v1) (Cte v2) -> Cte (v1 - v2)
  | _ -> absurd
  end

(**
Reduction Contexts:
{h 

C  : cont
C ::= [] | [C e] | [v C]

}
*)

type context = Empty | Left context expr | Right int context

(**
Recomposition:
{h 

             recompose : cont * expression -> expression
     recompose ([], e) = e
recompose ([C e2], e1) = recompose (C, e1 - e2)
recompose ([n1 C], e2) = recompose (C, n1 - e2)

}
*)

let rec function recompose (c:context) (e:expr) : expr =
  match c with
  | Empty -> e
  | Left c e2 -> recompose c (Sub e e2)
  | Right n1 c -> recompose c (Sub (Cte n1) e)
  end

let rec lemma recompose_values (c:context) (e1 e2:expr) : unit
  requires { eval_0 e1 = eval_0 e2 }
  variant  { c }
  ensures  { eval_0 (recompose c e1) = eval_0 (recompose c e2) }
= match c with
  | Empty -> ()
  | Left c e -> recompose_values c (Sub e1 e) (Sub e2 e)
  | Right n c -> recompose_values c (Sub (Cte n) e1) (Sub (Cte n) e2)
  end

(* not needed anymore
let rec lemma recompose_inversion (c:context) (e:expr)
  requires {
      match c with Empty -> false | _ -> true end \/
      match e with Cte _ -> false | Sub _ _ -> true end }
  variant { c }
  ensures {  match recompose c e with
               Cte _ -> false | Sub _ _ -> true end }
= match c with
  | Empty -> ()
  | Left c e2 -> recompose_inversion c (Sub e e2)
  | Right n1 c -> recompose_inversion c (Sub (Cte n1) e)
  end
*)

(**
Decomposition:
{h 

dec_or_val = (C, rp) | v

}
Decomposition function:
{h 

             decompose_term : expression * cont -> dec_or_val
      decompose_term (n, C) = decompose_cont (C, n)
decompose_term (e1 - e2, C) = decompose_term (e1, [C e2])

             decompose_cont : cont * value -> dec_or_val
     decompose_cont ([], n) = n
  decompose_cont ([C e], n) = decompose_term (e, [n c])
decompose_term ([n1 C], n2) = (C, n1 - n2)

  decompose : expression -> dec_or_val
decompose e = decompose_term (e, [])

}
*)

exception Stuck

predicate is_a_value (e:expr) =
  match e with
  | Cte _ -> true
  | _ -> false
  end

predicate is_empty_context (c:context) =
  match c with
  | Empty -> true
  | _ -> false
  end

use Defunctionalization as D (* for size_e *)

function size_e (e:expr) : int = D.size_e e

function size_c (c:context) : int =
  match c with
  | Empty -> 0
  | Left c e -> 2 + size_c c + size_e e
  | Right _ c -> 1 + size_c c
  end

lemma size_c_pos: forall c: context. size_c c >= 0

let rec decompose_term (e:expr) (c:context) : (context, expr)
  variant { size_e e + size_c c }
  ensures { let c1,e1 = result in
            recompose c1 e1 = recompose c e /\
            is_a_redex e1 }
  raises { Stuck -> is_empty_context c /\ is_a_value e }
  (* raises { Stuck -> is_a_value (recompose c e) } *)
  =
  match e with
  | Cte n -> decompose_cont c n
  | Sub e1 e2 -> decompose_term e1 (Left c e2)
  end

with decompose_cont (c:context) (n:int) : (context, expr)
  variant { size_c c }
  ensures { let c1,e1 = result in
            recompose c1 e1 = recompose c (Cte n)  /\
            is_a_redex e1 }
  raises { Stuck -> is_empty_context c }
(*  raises { Stuck -> is_a_value (recompose c (Cte n)) } *)
  =
  match c with
  | Empty -> raise Stuck
  | Left c e -> decompose_term e (Right n c)
  | Right n1 c -> c, Sub (Cte n1) (Cte n)
  end

let decompose (e:expr) : (context, expr)
  ensures { let c1,e1 = result in recompose c1 e1 = e  /\
            is_a_redex e1 }
  raises { Stuck -> is_a_value e }
  =
  decompose_term e Empty

(**
One reduction step:
{h 

reduce : expression -> expression + stuck

if decompose e = v
then reduce e = stuck

if decompose e = (C, rp)
and contract rp = stuck
then reduce e = stuck

if decompose e = (C, rp)
and contract rp = c
then reduce e = recompose (C, c)

}
*)

(** {4 Exercise 2.0}
  Implement the reduction semantics above and test it
*)

let reduce (e:expr) : expr
  ensures { eval_0 result = eval_0 e }
  raises { Stuck -> is_a_value e }
  =
  let c,r = decompose e in
  recompose c (contract r)

(**
Evaluation based on iterated reduction:
{h 

itere : red_ou_val -> value + erreur

itere v = v

if contract rp = stuck
then itere (C, rp) = stuck

if contract rp = c
then itere (C, rp) = itere (decompose (recompose (C, c)))

}
*)

let rec itere (e:expr) : int
  diverges (* termination could be proved but that's not the point *)
  ensures { eval_0 e = result }
  =
  match reduce e with
  | e' -> itere e'
  | exception Stuck ->
     match e with
     | Cte n -> n
     | _ -> absurd
     end
  end

(** {4 Exercise 2.1}
  Optimize the step recomposition / decomposition
  into a single function `refocus`.
*)

let refocus c e
  ensures { let c1,e1 = result in
            recompose c1 e1 = recompose c e /\
            is_a_redex e1 }
  raises { Stuck -> is_empty_context c /\ is_a_value e }
= decompose_term e c

let rec itere_opt (c:context) (e:expr) : int
  diverges
  ensures { result = eval_0 (recompose c e) }
= match refocus c e with
  | c, r -> itere_opt c (contract r)
  | exception Stuck ->
     assert { is_empty_context c };
     match e with
     | Cte n -> n
     | _ -> absurd
     end
  end

let rec normalize (e:expr)
  diverges
  ensures { result = eval_0 e }
= itere_opt Empty e

(** {4 Exercise 2.2}
  Derive an abstract machine
*)

(**
{h 

(c,Cte n)_1 -> (c,n)_2
(c,Sub e1 e2)_1 -> (Left c e2,e1)_1
(Empty,n)_2 -> stop with result = n
(Left c e,n)_2 -> (Right n c,e)_1
(Right n1 c,n)_2 -> (c,Cte (n1 - n))_1

}
*)

let rec eval_1 c e
  variant { 2 * (size_c c + size_e e) }
  ensures { result = eval_0 (recompose c e) }
= match e with
  | Cte n -> eval_2 c n
  | Sub e1 e2 -> eval_1 (Left c e2) e1
  end

with eval_2 c n
  variant { 1 + 2 * size_c c }
  ensures { result = eval_0 (recompose c (Cte n)) }
= match c with
  | Empty -> n
  | Left c e -> eval_1 (Right n c) e
  | Right n1 c -> eval_1 c (Cte (n1 - n))
  end

let interpret p
  ensures { result = eval_0 p }
= eval_1 Empty p

let test () =
   interpret p0,
   interpret p1,
   interpret p2,
   interpret p3,
   interpret p4

end

module RWithError

use Expr
use SemWithError

(** {4 Exercise 2.3}
  An abstract machine for the case with errors
*)

(**

{h 

(c,Cte n)_1 -> stop on Underflow if  n < 0
(c,Cte n)_1 -> (c,n)_2 if n >= 0
(c,Sub e1 e2)_1 -> (Left c e2,e1)_1
(Empty,n)_2 -> stop on Vnum n
(Left c e,n)_2 -> (Right n c,e)_1
(Right n1 c,n)_2 -> stop on Underflow if n1 < n
(Right n1 c,n)_2 -> (c,Cte (n1 - n))_1 if n1 >= n

}

*)

type context = Empty | Left context expr | Right int context

use Defunctionalization as D (* for size_e *)

function size_e (e:expr) : int = D.size_e e

function size_c (c:context) : int =
  match c with
  | Empty -> 0
  | Left c e -> 2 + size_c c + size_e e
  | Right _ c -> 1 + size_c c
  end

lemma size_c_pos: forall c: context. size_c c >= 0

function recompose (c:context) (e:expr) : expr =
  match c with
  | Empty -> e
  | Left c e2 -> recompose c (Sub e e2)
  | Right n1 c -> recompose c (Sub (Cte n1) e)
  end

let rec lemma recompose_values (c:context) (e1 e2:expr) : unit
  requires { eval_0 e1 = eval_0 e2 }
  variant  { c }
  ensures  { eval_0 (recompose c e1) = eval_0 (recompose c e2) }
= match c with
  | Empty -> ()
  | Left c e -> recompose_values c (Sub e1 e) (Sub e2 e)
  | Right n c -> recompose_values c (Sub (Cte n) e1) (Sub (Cte n) e2)
  end

let rec lemma recompose_underflow (c:context) (e:expr) : unit
  requires { eval_0 e = Underflow }
  variant { c }
  ensures { eval_0 (recompose c e) = Underflow }
= match c with
  | Empty -> ()
  | Left c e1 -> recompose_underflow c (Sub e e1)
  | Right n c -> recompose_underflow c (Sub (Cte n) e)
  end

let rec eval_1 c e
  variant { 2 * (size_c c + size_e e) }
  ensures { result = eval_0 (recompose c e) }
= match e with
  | Cte n -> if n >= 0 then eval_2 c n else Underflow
  | Sub e1 e2 -> eval_1 (Left c e2) e1
  end

with eval_2 c n
  variant { 1 + 2 * size_c c }
  requires { n >= 0 }
  ensures { result = eval_0 (recompose c (Cte n)) }
= match c with
  | Empty -> Vnum n
  | Left c e -> eval_1 (Right n c) e
  | Right n1 c -> if n1 >= n then eval_1 c (Cte (n1 - n)) else Underflow
  end

let interpret p
  ensures { result = eval_0 p }
= eval_1 Empty p

let test () =
   interpret p0,
   interpret p1,
   interpret p2,
   interpret p3,
   interpret p4

end
