File: wrap_lines.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 (146 lines) | stat: -rw-r--r-- 5,908 bytes parent folder | download | duplicates (3)
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

(** This micro challenge was proposed by Mattias Ulbrich (KIT)
    during his KeY tutorial at VerifyThis 2021. *)

module WrapLines

  use int.Int
  use array.Array

  type char

  val constant space: char
  val constant newline: char

  val (=) (x y: char) : bool ensures { result <-> x = y }

  (* Returns the index in s that has value c and
     is at least from, if it exists, and -1 otherwise. *)
  let index_of (s: array char) (c: char) (from: int) : int
    requires { 0 <= from <= length s }
    ensures  { result = -1 /\
                (forall i. from <= i < length s -> s[i] <> c)
            \/ from <= result < length s /\ s[result] = c /\
                (forall i. from <= i < result -> s[i] <> c) }
  = let ref k = from in
    while k < length s do
      invariant { from <= k <= length s }
      invariant { forall i. from <= i < k -> s[i] <> c }
      variant   { length s - k }
      if s[k] = c then return k;
      k <- k + 1
    done;
    return -1

  (* Every line (but the last) has at least line_length characters: *)
  predicate at_least_line_length (s: array char) (line_length: int)
  = forall i j. -1 <= i < j < length s ->
    (i = -1 \/ i <= 0 /\ s[i] = newline) -> s[j] = newline -> j - i >= line_length

  let wrap_lines (s: array char) (line_length: int) : unit
    (* initially, no newline at all *)
    requires { forall i. 0 <= i < length s -> s[i] <> newline }
    (* the only changes in s are turning ' ' into '\n' *)
    ensures  { forall i. 0 <= i < length s -> s[i] <> old s[i] ->
                old s[i] = space /\ s[i] = newline }
    ensures  { at_least_line_length s line_length }
  = let ref last_nl = -1 in
    let ref last_sp = index_of s space 0 in
    while last_sp <> -1 do
      invariant { -1 <= last_nl < length s }
      invariant { last_sp = -1
              \/ last_nl < last_sp < length s /\ s[last_sp] = space }
      invariant { forall i. last_nl < i < length s -> s[i] = old s[i] }
      invariant { forall i. 0 <= i < length s -> s[i] <> old s[i] ->
                    old s[i] = space /\ s[i] = newline }
      invariant { forall i. last_nl < i < last_sp -> s[i] <> newline }
      invariant { at_least_line_length s line_length }
      variant   { if last_sp = -1 then 0 else length s - last_sp }
      if last_sp - last_nl > line_length then (
        s[last_sp] <- newline;
        last_nl <- last_sp
      );
      last_sp <- index_of s space (last_sp + 1)
    done

  (*
    Implement a wrap_lines method such that
    * The original file may already contain '\n'
    * Every line has at most L characters or does not have a space
    * If there is an introduced newline character there is no space or
        newline that could have given a longer line.
  *)

  let wrap_lines_plus (s: array char) (line_length : int) : unit
    requires { line_length >= 0 }
    (* we only convert spaces into newlines *)
    ensures  { forall i. 0 <= i < length s -> s[i] <> old s[i] ->
                old s[i] = space /\ s[i] = newline }
    (* no line segment longer than line_length characters has a space *)
    ensures  { forall i. 0 <= i < length s - line_length ->
                (forall j. i <= j <= i + line_length -> s[j] <> newline) ->
                (forall j. i <= j <= i + line_length -> s[j] <> space) }
    (* no line is broken by an introduced newline if there is
       a further breakpoint (newline, space or EOF) within
       (line_length + 1) characters in the original file *)
    ensures  { forall i. -1 <= i < length s -> (0 <= i -> s[i] = newline) ->
                forall j. i < j <= length s -> j <= i + line_length + 1 ->
                  (j < length s -> old s[j] = space \/ old s[j] = newline) ->
                    forall k. i < k < j -> s[k] = old s[k] }
  = let ref last_nl = -1 in (* last seen newline or -1 *)
    let ref last_sp = -1 in (* last seen space, newline or -1 *)
    (* scan the file and at each breakpoint, decide whether we
       should have broken the line at the last space character *)
    for ind = 0 to length s - 1 do
      invariant { -1 <= last_nl <= last_sp < ind }
      invariant { 0 <= last_nl -> s[last_nl] = newline }
      invariant { last_nl < last_sp -> s[last_sp] = space }
      invariant { forall i. last_nl < i < ind -> s[i] <> newline }
      invariant { forall i. last_sp < i < ind -> s[i] <> space }
      invariant { forall i. 0 <= i < ind -> s[i] <> old s[i] ->
                    old s[i] = space /\ s[i] = newline }
      invariant { forall i. ind <= i < length s -> s[i] = old s[i] }
      invariant { forall i. 0 <= i < last_sp - line_length ->
                    (forall j. i <= j <= i + line_length -> s[j] <> newline) ->
                    (forall j. i <= j <= i + line_length -> s[j] <> space) }
      invariant { forall i. -1 <= i < length s -> (0 <= i -> s[i] = newline) ->
                  forall j. i < j <= length s -> j <= i + line_length + 1 ->
                    (j < length s -> old s[j] = space \/ old s[j] = newline) ->
                      forall k. i < k < j -> s[k] = old s[k] }
      if s[ind] = newline then begin
        if last_nl < last_sp && last_nl + line_length + 1 < ind then
        begin
          s[last_sp] <- newline;
        end;
        last_nl <- ind;
        last_sp <- ind;
      end else
      if s[ind] = space then begin
        if last_nl < last_sp && last_nl + line_length + 1 < ind then
        begin
          s[last_sp] <- newline;
          last_nl <- last_sp;
        end;
        last_sp <- ind;
      end
    done;
    if last_nl < last_sp && last_nl + line_length + 1 < length s then
      s[last_sp] <- newline

end

module WrapLinesOCaml

  use int.Int
  use string.Char
  use string.OCaml
  use array.Array

  let constant space: char = chr 32
  let constant newline: char = chr 10

  clone export WrapLines with
    type char = char, val space = space,
    val newline = newline, val (=) = eq_char

end