File: BacktrackArray.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 (436 lines) | stat: -rw-r--r-- 14,837 bytes parent folder | download | duplicates (5)
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
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436

(* BactrackArray.mlw :
   provide support for a backtracking table.
   Idea : you have an infinite number of stack,initially empty,
   and you add elements with time.
   Moreover, you sometime need to undo some events.
   Primitive:
   1) create () : t 'a : create an initially empty table.
   2) add (i:int) (b:'a) (tb:t 'a) : unit :
      add an element b on a stack top i, and advance the time counter.
   3) get (i:int) (tb:t 'a) : list 'a :
      get the stack i at the current time.
   4) stamp (tb:t 'a) : timestamp 'a : current timestamp.
   5) backtrack (t:timestamp) (tb:t 'a) : unit :
      If the instant t is a past instant or the current instant,
      go back to the instant t. *)

module Types

  use int.Int
  use array.Array
  use list.List
  use Functions.Func
  use Predicates.Pred

  type timestamp 'a = {
    time : int ;
    size : int ;
    ghost table : int -> list 'a ;
  }

  type t 'a = {
    (* (-1) mean array resizing (doubling in size), i >= 0 mean
       update at case i. *)
    mutable history : list int ;
    mutable current_time : int ;
    mutable buffer : array (list 'a) ;
    (* Invariant of stored data. *)
    ghost inv : 'a -> bool ;
  }

end

module Logic

  use Types
  use int.Int
  use int.ComputerDivision
  use import map.Map as M
  use array.Array
  use list.List
  use Functions.Func
  use Choice.Choice

  function func_of_array (a:array 'a) (def:'a) : int -> 'a
  axiom func_of_array_def : forall a:array 'a,def:'a,x:int.
    func_of_array a def x = if (0 <= x < a.length)
    then M.get a.elts x
    else def

  function current_timestamp (x:t 'a) : timestamp 'a =
    { time = x.current_time ;
      size = x.buffer.length ;
      table = func_of_array x.buffer Nil ; }

  predicate correct_table (sz:int) (b: int -> list 'a) =
    forall x:int. x >= sz \/ x < 0 -> b x = Nil

  function pop (l:list 'a) : list 'a = match l with
    | Nil -> default
    | Cons _x q -> q
  end

  function unroll (tm:int) (t0:int) (h:list int) (b:int -> list 'a)
    (sz:int) : timestamp 'a =
    if tm = 0
    then { time = t0 ; size = sz ; table = b }
    else match h with
      | Nil -> { time = (t0+tm) ; size = sz ; table = b ; }
      | Cons x q -> if x = (-1)
        then unroll (tm-1) t0 q b (div sz 2)
        else unroll (tm-1) t0 q (b[x <- pop (b x)]) sz
      end

  predicate unroll_correct (tm:int) (h:list int) (b:int -> list 'a) (sz:int)
    = match h with
      | Nil -> tm = 0
      | Cons x q -> if x = (-1)
        then let s0 = div sz 2 in
          s0 * 2 = sz /\ unroll_correct (tm-1) q b s0 /\
          (forall i:int. i >= s0 \/ i < 0 -> b i = Nil)
        else b x <> Nil /\ 0 <= x < sz
          /\ unroll_correct (tm-1) q (b[x <- pop (b x)]) sz
    end

  predicate past_time (t:timestamp 'a) (tb:t 'a) =
    tb.current_time >= t.time /\
    unroll (tb.current_time - t.time) t.time
      tb.history (func_of_array tb.buffer Nil) tb.buffer.length = t

  predicate precede (tb1:t 'a) (tb2:t 'a) =
    forall t:timestamp 'a. past_time t tb1 -> past_time t tb2

  predicate before (t1 t2:timestamp 'a) =
    t1.time <= t2.time

  predicate list_forall (p:'a -> bool) (l:list 'a) = match l with
    | Nil -> true
    | Cons x q -> p x /\ list_forall p q
  end

  predicate correct (tb:t 'a) =
    (forall t:timestamp 'a. past_time t tb -> t.size > 0) /\
    (forall t:timestamp 'a,i:int.
      past_time t tb /\ i >= 0 -> list_forall tb.inv (eval t.table i)) /\
    (forall t:timestamp 'a.
      past_time t tb -> correct_table t.size t.table) /\
    unroll_correct tb.current_time tb.history
      (func_of_array tb.buffer Nil) tb.buffer.length

  (* I MUST INTRODUCE THIS PREDICATE FOR ONLY ONE REASON :
     ABUSIVE RECORD DECONSTRUCTION IN ASSERTIONS. *)
  predicate backtrack_to (tbold:t 'a) (tbinter:t 'a) (tbnew:t 'a) =
    (forall tm:timestamp 'a.
     past_time tm tbold -> past_time tm tbinter
              && time tm <= time (current_timestamp tbold)
              && time tm <= time (current_timestamp tbnew)
              && before tm (current_timestamp tbnew)
              && past_time tm tbnew) && (forall tm:timestamp 'a.
            past_time tm tbold -> past_time tm tbnew) &&
            precede tbold tbnew

end

module Impl

  use Types
  use Logic
  use int.Int
  use int.ComputerDivision
  use import map.Map as M
  use array.Array
  use list.List
  use Functions.Func
  use Predicates.Pred
  use Choice.Choice

  (* extraction trick to speedup integer operations with
     constants. *)
  let constant mone : int = -1
  let constant zero : int = 0
  let constant one : int = 1
  let constant two : int = 2

  let create (ghost p: 'a -> bool) : t 'a
    ensures { forall t:timestamp 'a.
      past_time t result ->  t.table = const Nil }
    ensures { past_time (current_timestamp result) result }
    ensures { result.inv = p }
    ensures { correct result }
  =
    let res = {
      history = Nil ;
      current_time = zero ;
      buffer = make one Nil ;
      inv = p ;
    } in
    assert { extensionalEqual (func_of_array res.buffer Nil) (const Nil) } ;
    res


  (* Internal utility (break some of the invariants),
     but useful in practice. *)
  let add_event (x:int) (tb:t 'a) : unit
    writes { tb.history,tb.current_time }
    ensures { tb.history = Cons x (old tb).history }
    ensures { tb.current_time = (old tb).current_time + 1 }
  =
    tb.history <- Cons x tb.history ;
    tb.current_time <- tb.current_time + one


  (* Internal utility. *)
  let resize_for (x:int) (tb:t 'a) : unit
    writes { tb }
    requires { correct tb }
    requires { x >= tb.buffer.length }
    ensures { x < tb.buffer.length }
    ensures { precede (old tb) tb }
    ensures { correct tb }
    ensures { (current_timestamp tb).table =
      (current_timestamp (old tb)).table }
  =
    (* pattern : in order to do an optimization
       (resize only once), introduce a ghost value
       on which we 'execute' the unoptimized code and
       'check' at end that it give the same result. *)
    let ghost tbc = {
      history = tb.history ;
      current_time = tb.current_time ;
      buffer = copy tb.buffer;
      inv = tb.inv ;
    } in
    let rec aux (size:int) : int
      requires { 0 < size <= x }
      requires { correct tbc }
      requires { tbc.history = tb.history /\
        tbc.current_time = tb.current_time /\
        func_of_array tbc.buffer Nil = func_of_array tb.buffer Nil }
      requires { tbc.buffer.length = size }
      variant { x - size }
      writes { tb.history,tb.current_time,tbc.history,tbc.current_time,tbc.buffer }
      ensures { correct tbc }
      ensures { tbc.history = tb.history /\
        tbc.current_time = tb.current_time /\
        func_of_array tbc.buffer Nil = func_of_array tb.buffer Nil }
      ensures { tbc.buffer.length = result }
      ensures { result > x }
      ensures { result >= size }
      ensures { precede (old tbc) tbc }
    =
      label AuxInit in
      assert { past_time (current_timestamp tbc) tbc } ;
      add_event mone tb ;
      add_event mone tbc ;
      let size2 = two * size in
      let ghost buf2 = make size2 Nil in
      let buf1 = tbc.buffer in
      blit buf1 zero buf2 zero size ;
      tbc.buffer <- buf2 ;
      assert { extensionalEqual (func_of_array tbc.buffer Nil)
        (func_of_array (tbc at AuxInit).buffer Nil) } ;
      assert { forall t:timestamp 'a.
        (past_time t (tbc at AuxInit) \/ t = current_timestamp tbc) <-> past_time t tbc } ;
      if size2 > x
      then size2
      else aux size2 in
    let len = length tb.buffer in
    assert { extensionalEqual (func_of_array tbc.buffer Nil)
      (func_of_array tb.buffer Nil) } ;
    assert { len = (current_timestamp tb).size } ;
    let size = aux len in
    let buf2 = make size Nil in
    let buf1 = tb.buffer in
    blit buf1 zero buf2 zero len ;
    assert { extensionalEqual (func_of_array buf1 Nil)
      (func_of_array buf2 Nil) } ;
    tb.buffer <- buf2

  let iadd (x:int) (b:'a) (tb:t 'a) : unit
    writes { tb.history,tb.current_time,tb.buffer.elts }
    requires { 0 <= x < tb.buffer.length }
    requires { correct tb }
    requires { inv tb b }
    ensures { past_time (current_timestamp tb) tb }
    ensures { correct tb }
    ensures { precede (old tb) tb }
    ensures { let tb0 = (current_timestamp (old tb)).table in
      (current_timestamp tb).table = tb0[x <- Cons b (tb0 x)] }
  =
    label Init in
    let buf = tb.buffer in
    buf[x]<- Cons b (buf[x]) ;
    add_event x tb ;
    assert { let tb0 = (current_timestamp (tb at Init)).table in
      extensionalEqual ((current_timestamp tb).table) (tb0[x <- Cons b (tb0 x)]) } ;
    assert { let tb0 = (current_timestamp (tb at Init)).table in
      let tb1 = (current_timestamp tb).table in
      extensionalEqual (tb1[x <- pop (tb1 x)]) tb0 } ;
    assert { past_time (current_timestamp tb) tb } ;
    assert { forall t:timestamp 'a.
      past_time t tb <-> past_time t (tb at Init) \/ t = current_timestamp tb } ;
    assert { precede (tb at Init) tb }

  let add (x:int) (b:'a) (tb:t 'a) : unit
    writes { tb }
    requires { correct tb }
    requires { x >= 0 }
    requires { inv tb b }
    ensures { past_time (current_timestamp tb) tb }
    ensures { correct tb }
    ensures { precede (old tb) tb }
    ensures { let tb0 = (current_timestamp (old tb)).table in
      (current_timestamp tb).table = tb0[x <- Cons b (tb0 x)] }
  =
    if x >= length tb.buffer
    then resize_for x tb ;
    iadd x b tb


  let get (tb:t 'a) (x:int) : list 'a
    requires { correct tb }
    requires { x >= 0 }
    ensures { result = table (current_timestamp tb) x }
    ensures { list_forall tb.inv result }
  =
    if x >= length tb.buffer
    then Nil
    else let res = tb.buffer[x] in
      (assert { res = table (current_timestamp tb) x } ; res )

  let backtrack (t:timestamp 'a) (tb:t 'a) : unit
    writes { tb }
    requires { past_time t tb }
    requires { correct tb }
    ensures { correct tb }
    ensures { current_timestamp tb = t }
    ensures { past_time (current_timestamp tb) tb }
    ensures { forall t2:timestamp 'a. before t2 t /\ past_time t2 (old tb) ->
      past_time t2 tb }
    ensures { precede tb (old tb) }
  =
    let final_size = t.size in
    let ghost tbc = {
      history = tb.history ;
      current_time = tb.current_time ;
      buffer = copy tb.buffer ;
      inv = tb.inv ;
    } in
    let rec unroll (delta:int) : unit
      requires { correct tbc }
      requires { past_time t tbc }
      requires { delta >= 0 }
      requires { tbc.current_time = t.time + delta }
      requires { tbc.history = tb.history /\
        (forall x:int. 0 <= x < final_size /\ x < tbc.buffer.length ->
          func_of_array tbc.buffer Nil x = func_of_array tb.buffer Nil x) }
      requires { tb.buffer.length <= final_size }
      variant { delta }
      writes { tb.history,tb.buffer.elts,tbc }
      ensures { correct tbc }
      ensures { tbc.history = tb.history /\
        (forall x:int. 0 <= x < final_size ->
          func_of_array tbc.buffer Nil x = func_of_array tb.buffer Nil x) }
      ensures { current_timestamp tbc = t }
      (* This is a trick avoiding an inductive reasoning outside. *)
      ensures { tbc.buffer.length <= (old tbc).buffer.length }
      ensures { precede tbc (old tbc) }
      ensures { forall t2:timestamp 'a. before t2 t /\ past_time t2 (old tbc) ->
        past_time t2 tbc }
    =
      label UInit in
      if delta <> zero
      then begin
        match tb.history with
          | Nil -> absurd
          | Cons x q -> tb.history <- q ;
            tbc.history <- q ;
            tbc.current_time <- tbc.current_time - one ;
            if x = mone
            then begin
              let buf1 = tbc.buffer in
              let len = length buf1 in
              let len2 = div len two in
              (* nothing to do on non-ghost side. *)
              let buf2 = make len2 Nil in
              blit buf1 zero buf2 zero len2 ;
              tbc.buffer <- buf2 ;
              assert { let t0 = { time = tbc.current_time ;
                  size = len2 ;
                  table = func_of_array (tbc at UInit).buffer Nil
                } in
                let t1 = { t0 with table = func_of_array tbc.buffer Nil } in
                past_time t0 (tbc at UInit) &&
                extensionalEqual t0.table t1.table && t0 = t1 } ;
              assert { extensionalEqual (func_of_array tbc.buffer Nil)
                (func_of_array (tbc at UInit).buffer Nil) } ;
              assert { precede tbc (tbc at UInit) } ;
              unroll (delta - one)
            end else begin
              assert { 0 <= x < tbc.buffer.length } ;
              if x < final_size
              then begin
                let h = tb.buffer[x] in
                match h with
                  | Nil -> absurd
                  | Cons _ q -> tb.buffer[x]<- q ;
                    tbc.buffer[x]<- q ;
                    assert {
                      let tb0 = func_of_array (tbc at UInit).buffer Nil in
                      extensionalEqual (tb0[x <- pop (tb0 x)])
                        (func_of_array tbc.buffer Nil) } ;
                    assert { precede tbc (tbc at UInit) } ;
                    unroll (delta - one)
                end
              end
              else begin
                let hc = tbc.buffer[x] in
                match hc with
                  | Nil -> absurd
                  | Cons _ q -> tbc.buffer[x]<- q
                end ;
                assert { let tb0 = func_of_array (tbc at UInit).buffer Nil in
                  extensionalEqual (tb0[x <- pop (tb0 x)])
                  (func_of_array tbc.buffer Nil) } ;
                assert { precede tbc (tbc at UInit) } ;
                (* nothing to do on non-ghost side. *)
                unroll (delta - one)
              end
            end
        end
      end
    in
    assert { extensionalEqual (func_of_array tb.buffer Nil)
      (func_of_array tbc.buffer Nil) } ;
    (* direct resizing if necessary. *)
    if final_size < length tb.buffer
    then begin
      let buf2 = make final_size Nil in
      let buf1 = tb.buffer in
      blit buf1 zero buf2 zero final_size ;
      tb.buffer <- buf2
    end ;
    let tm0 = tb.current_time in
    tb.current_time <- t.time ;
    unroll (tm0 - t.time) ;
    assert { extensionalEqual (func_of_array tb.buffer Nil)
      (func_of_array tbc.buffer Nil) }

  val ghost hack_func_of_array (a:array 'a) (def:'a) : int -> 'a
    ensures { result = func_of_array a def }

  let stamp (tb:t 'a) : timestamp 'a
    requires { correct tb }
    ensures { result = current_timestamp tb }
  =
    {
      time = tb.current_time ;
      size = length tb.buffer ;
      table = hack_func_of_array tb.buffer Nil ;
    }

  (* look for the correct syntax :
    meta "remove_program" val hack_func_of_array*)

end