File: cursor_examples.mlw

package info (click to toggle)
why3 1.8.2-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 45,028 kB
  • sloc: xml: 185,443; ml: 111,224; ansic: 3,998; sh: 2,578; makefile: 2,568; java: 865; python: 720; javascript: 290; lisp: 205; pascal: 173
file content (219 lines) | stat: -rw-r--r-- 5,457 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

(** {1 Cursors}

    Some implementations and clients of module `cursor.Cursor`
    from the standard library.

    Author: Mário Pereira (Université Paris Sud) *)

module TestCursor

  use int.Int
  use int.Sum
  use seq.Seq
  use ref.Refint

  use cursor.Cursor

  (** sums all the remaining elements in the cursor *)
  let sum (c: cursor int) : int
    requires { permitted c }
    requires { c.visited = empty }
    ensures  { result = sum (get c.visited) 0 (length c.visited) }
    diverges
  = let s = ref 0 in
    while has_next c do
      invariant { permitted c }
      invariant { !s = sum (get c.visited) 0 (length c.visited) }
      let x = next c in
      s += x
    done;
    !s

end

(** {2 Iteration over an immuable collection}

    here we choose a list *)

module ListCursorImpl : cursor.ListCursor

  use int.Int
  use list.List
  use seq.Seq
  use seq.OfList

  type cursor 'a = {
    mutable ghost visited    : seq 'a;
            ghost collection : list 'a;
    mutable       to_visit   : list 'a;
  } invariant { visited ++ to_visit = collection }
    by { visited = empty; collection = Nil; to_visit = Nil }

  predicate permitted (c: cursor 'a) =
    length c.visited <= length c.collection /\
    forall i. 0 <= i < length c.visited -> c.visited[i] = c.collection[i]

  predicate complete (c: cursor 'a) =
    length c.visited = length c.collection

  let lemma snoc_Cons (s: seq 'a) (l: list 'a) (x: 'a)
    ensures { snoc s x ++ l == s ++ Cons x l }
  = ()

  scope C

    let next (c: cursor 'a) : 'a
      requires { not (complete c) }
      writes   { c }
      ensures  { c.visited = snoc (old c).visited result }
      ensures  { match (old c).to_visit with
                 | Nil -> false
                 | Cons x r -> c.to_visit = r /\ x = result
                 end }
    = match c.to_visit with
      | Nil -> absurd
      | Cons x r ->
        let ghost v0 = c.visited in
        c.visited <- snoc c.visited x; c.to_visit <- r;
        snoc_Cons v0 r x;
        assert { c.to_visit == c.collection [length c.visited ..] };
        x
      end

    let has_next (c: cursor 'a) : bool
      ensures { result <-> not (complete c) }
    = match c.to_visit with (* could define a [val eq (l1 l2: list 'a) : bool] *)
      | Nil -> false
      | _   -> true
      end

  end

  let create (t: list 'a) : cursor 'a
    ensures { result.visited = empty }
    ensures { result.collection = t }
    ensures { result.to_visit = t }
  = { visited = empty; collection = t; to_visit = t }

end

module TestListCursor

  use int.Int
  use int.Sum as S
  use list.List
  use list.Length
  use list.Sum
  use ref.Refint
  use seq.Seq
  use seq.OfList

  use ListCursorImpl

  lemma sum_of_list: forall l: list int.
    sum l = S.sum (get (of_list l)) 0 (length l)

  let list_sum (l: list int) : int
    ensures { result = sum l }
  = let s = ref 0 in
    let c = create l in
    while C.has_next c do
      invariant { !s = S.sum (get c.visited) 0 (length c.visited) }
      invariant { permitted c }
      variant   { length l - length c.visited }
      let x = C.next c in
      s += x
    done;
    !s

end

(** {2 Iteration over a mutable collection}

    here we choose an array of integers *)

module ArrayCursorImpl : cursor.ArrayCursor

  use int.Int
  use array.Array
  use array.ToSeq
  use list.List
  use list.Reverse
  use array.ToList
  use seq.Seq

  type cursor 'a = {
    mutable ghost visited    : seq 'a;
                  collection : seq 'a; (* FIXME : extraction of seq *)
    mutable       index      : int;    (** index of next element *)
  } invariant { 0 <= index <= length collection /\
                index = length visited /\
                forall i. 0 <= i < index -> collection[i] = visited[i] }
    by { visited = empty; collection = empty; index = 0 }

  predicate permitted (c: cursor 'a) =
    length c.visited <= length c.collection /\
    forall i. 0 <= i < length c.visited -> c.visited[i] = c.collection[i]

  predicate complete (c: cursor 'a) =
    length c.visited = length c.collection

  let create (a: array 'a) : cursor 'a
    ensures { result.visited = empty }
    ensures { result.index = 0 }
    ensures { result.collection = to_seq a }
  = { visited = empty; collection = to_seq a; index = 0; }

  scope C

    let has_next (c: cursor 'a) : bool
      ensures  { result <-> not (complete c) }
    = c.index < length c.collection

    let next (c: cursor 'a) : 'a
      requires { not (complete c) }
      writes   { c }
      ensures  { c.visited = snoc (old c).visited result }
      ensures  { c.index = (old c).index + 1 }
    = if c.index >= length c.collection then absurd
      else begin let x = c.collection[c.index] in
        c.visited <- snoc c.visited x;
        c.index <- c.index + 1;
        x end

  end

end

module TestArrayCursor

  use int.Int
  use array.Array
  use array.ToSeq
  use seq.Seq
  use int.Sum
  use ref.Refint

  use ArrayCursorImpl

  let array_sum (a: array int) : int
    ensures { result = sum (Array.([]) a) 0 (length a) }
  = let s = ref 0 in
    let c = create a in
    while C.has_next c do
      invariant { !s = sum (get c.visited) 0 (length c.visited) }
      invariant { permitted c }
      variant   { length c.collection - length c.visited }
      let x = C.next c in
      s += x
    done;
    !s

  let harness1 () : unit
  = let a = Array.make 42 0 in
    let c = create a in
    let x = C.next c in
    assert { x = 0 }

end