Prelude

A (very) brief history lesson

  • LISP

    • A family of programming languages
    • Very, very old
    • Pioneered many features of modern languages
  • Scheme

    • One of the main dialects of LISP
    • Invented in 1975 (Still very old)
    • Minimalist design
    • Reference is R5RS standard from 1998 (Still old – like Java)

Scheme: An Example

1
2
3
4
5
6
7
; This procedure computes the factorial
; of its parameter.
(define factorial
  (lambda (x)
    (if (= x 1)
        x
        (* (factorial (- x 1)) x))))

More on Parentheses

Pairs

Structured datatype with 2 fields

  • car and cdr similar to left, right, 1st, 2nd…
  • historical names, do not worry about it
(1 . 2)             Test

Lists

  • A crucial datatype
  • Defined as
    • empty list
    • pair with a list in cdr
(1 . 2)             Test
(1 2)               Test
(1 2 3)             Test

Slang Examples

Merging

  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
// #Sireum #Logika

import org.sireum._

@pure def sorted(seq: ISZ[Z]): B = {
  Contract(
    Ensures(Res == All(1 until seq.size)(i => seq(i - 1) <= seq(i)))
  )
  if (seq.size == 0) {
    return true
  } else {
    var res: B = true;
    var k: Z = 1;
    while (k < seq.size) {
      Invariant(
        Modifies(k, res),
        k >= 1,
        k <= seq.size,
        res == All(1 until k)(i => seq(i - 1) <= seq(i)),
      )
      if (seq(k - 1) > seq(k)) {
        res = false
      }
      k = k + 1
    }
    return res
  }
}

@pure def contained(s: ISZ[Z], t: ISZ[Z]): B = {
  Contract(
    Ensures(Res == All(0 until s.size)(i => Exists(0 until t.size)(j => s(i) == t(j))))
  )
  var res = true
  var m: Z = 0
  while (m < s.size & res) {
    Invariant(
      Modifies (m, res),
      m >= 0,
      m <= s.size,
      res == All(0 until m)(i => Exists(0 until t.size)(j => s(i) == t(j)))
    )
    var n: Z = 0
    var found: B = false
    while (n < t.size) {
      Invariant(
        Modifies(n),
        n >= 0,
        n <= t.size,
        found == Exists(0 until n)(j => s(m) == t(j))
      )
      if (s(m) == t(n)) {
        found = true
      }
      n = n+1
    }
    Deduce(|-(found == Exists(0 until t.size)(j => s(m) == t(j))))
    if (!found) {
      Deduce(|-(!Exists(0 until t.size)(j => s(m) == t(j))))
      res = false
      Deduce(|-(res == All(0 until m+1)(i => Exists(0 until t.size)(j => s(i) == t(j)))))
    }
    Deduce(|-(res == All(0 until m+1)(i => Exists(0 until t.size)(j => s(i) == t(j)))))
    m = m+1
    Deduce(|-(res == All(0 until m)(i => Exists(0 until t.size)(j => s(i) == t(j)))))
  }
  return res
}

def merge(s: ISZ[Z], t: ISZ[Z]): ISZ[Z] = {
  Contract(
    Requires(sorted(s), sorted(t)),
    Ensures(contained(s, Res), contained(t, Res), sorted(Res))
  )
  var res = ISZ[Z]()
  var x = 0
  var y = 0
  while (x < s.size & y < t.size) {
    if (s(x) < t(y)) {
      res = res :+ s(x)
      x = x+1
    } else {
      res = res :+ t(y)
      y = y+1
    }
  }
  while (x < s.size) {
    res = res :+ s(x)
    x = x+1
  }
  while (y < t.size) {
    res = res :+ t(y)
    y = y+1
  }
  return res
}

def merge_with_proof(s: ISZ[Z], t: ISZ[Z]): ISZ[Z] = {
  Contract(
    Requires(sorted(s), sorted(t)),
    Ensures(contained(s, Res), contained(t, Res), sorted(Res))
  )
  var res = ISZ[Z]()
  var x = 0
  var y = 0
  var z = 0
  while (x < s.size & y < t.size) {
    Invariant(
      Modifies(x, y, res),
      x >= 0,
      x <= s.size,
      y >= 0,
      y <= t.size,
      z == res.size,
      All(0 until x)(i => Exists(0 until z)(j => s(i) == res(j))),
      All(0 until y)(i => Exists(0 until z)(j => t(i) == res(j))),
      (z == 0 | x == s.size) || res(z-1) <= s(x),
      (z == 0 | y == t.size) || res(z-1) <= t(y),
      All(1 until z)(i => res(i - 1) <= res(i))
    )
    if (s(x) < t(y)) {
      res = res :+ s(x)
      Deduce(|- (s(x) == res(z)))
      Deduce(|- (Exists(0 until z+1)(j => s(x) == res(j))))
      z = z + 1
      Deduce(|- (All(0 until x+1)(i => Exists(0 until z)(j => s(i) == res(j)))))
      x = x+1
    } else {
      res = res :+ t(y)
      Deduce(|- (t(y) == res(z)))
      Deduce(|- (Exists(0 until z+1)(j => t(y) == res(j))))
      Deduce(|- (All(1 until z+1)(i => res(i - 1) <= res(i))))
      z = z+1
      Deduce(|- (All(0 until y+1)(i => Exists(0 until z)(j => t(i) == res(j)))))
      y = y+1
    }
    Deduce(|- (All(0 until x)(i => Exists(0 until z)(j => s(i) == res(j)))))
    Deduce(|- (All(0 until y)(i => Exists(0 until z)(j => t(i) == res(j)))))
    //Deduce(|- (z == 0 || res(z-1) <= s(x)))
  }
  while (x < s.size) {
    Invariant(
      Modifies(x, res),
      x >= 0,
      x <= s.size,
      z == res.size,
      All(0 until x)(i => Exists(0 until z)(j => s(i) == res(j)))
    )
    res = res :+ s(x)
    Deduce(|-(s(x) == res(z)))
    Deduce(|-(Exists(0 until z + 1)(j => s(x) == res(j))))
    z = z+1
    Deduce(|- (All(0 until x+1)(i => Exists(0 until z)(j => s(i) == res(j)))))
    x = x+1
  }
  while (y < t.size) {
    Invariant(
      Modifies(y, res),
      y >= 0,
      y <= t.size,
      z == res.size,
      All(0 until y)(i => Exists(0 until z)(j => t(i) == res(j)))
    )
    res = res :+ t(y)
    Deduce(|-(t(y) == res(z)))
    Deduce(|-(Exists(0 until z + 1)(j => t(y) == res(j))))
    z = z+1
    Deduce(|- (All(0 until y+1)(i => Exists(0 until z)(j => t(i) == res(j)))))
    y = y+1
  }
  return res
}

Quantification

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// #Sireum #Logika
import org.sireum._

assert(All(0 until 10)(i => i >= 0 & i < 10))

var x: Z = 0
while (x < 10) {
  assert(Exists(0 until 10)(i => i == x))
}

def allex_seq(s: ISZ[B]) {
  Contract(
    Requires(!s.isEmpty)
  )
  assert(!All(0 until s.size)(i => s(i)) | Exists(0 until s.size)(i => s(i)))
}

Searching

  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
// #Sireum #Logika

import org.sireum._

@pure def sorted(seq: ISZ[Z]): B = {
  Contract(
    Ensures(Res == All(1 until seq.size)(i => seq(i - 1) <= seq(i)))
  )
  if (seq.size == 0) {
    return true
  } else {
    var res: B = true;
    var k: Z = 1;
    while (k < seq.size) {
      Invariant(
        Modifies(k, res),
        k >= 1,
        k <= seq.size,
        res == All(1 until k)(i => seq(i - 1) <= seq(i)),
      )
      if (seq(k - 1) > seq(k)) {
        res = false
      }
      k = k + 1
    }
    return res
  }
}

@pure def sorted_upper_bound(seq: ISZ[Z], i: Z, b: Z): B = {
  Contract(
    Requires(sorted(seq), 0 <= i, i < seq.size, seq(i) <= b),
    Ensures(Res == All(0 until i)(k => seq(k) <= b) & Res == true)
  )
  var j: Z = i
  while (j > 0) {
    Invariant(
      Modifies(j),
      0 <= j,
      j < seq.size,
      seq(j) <= b,
      All(j until i)(k => seq(k) <= b)
    )
    j = j-1
  }
  return true
}

@pure def sorted_lower_bound(seq: ISZ[Z], i: Z, b: Z): B = {
  Contract(
    Requires(sorted(seq), 0 <= i, i < seq.size, b < seq(i)),
    Ensures(Res == All(i until seq.size)(k => b < seq(k)) & Res == true)
  )
  var j: Z = i
  while (j < seq.size-1) {
    Invariant(
      Modifies(j),
      0 <= j,
      j < seq.size,
      b < seq(j),
      All(i until j)(k => b < seq(k))
    )
    j = j+1
  }
  return true
}

@pure def linsearch(seq: ISZ[Z], v: Z): Option[Z] = {
  Contract(
    Requires(sorted(seq)),
    Ensures(
      (Res == None[Z]()) | Exists(0 until seq.size)(i => seq(i) == v & Res == Some(i)))
  )
  var res: Option[Z] = None()
  var k: Z = 0
  while (k < seq.size) {
    Invariant(
      Modifies(res, k),
      k >= 0,
      k <= seq.size,
      (res == None[Z]()) | Exists(0 until k)(i => seq(i) == v & res == Some(i))
    )
    Deduce(|-((res == None[Z]()) | Exists(0 until k)(i => seq(i) == v & res == Some(i))))
    if (seq(k) == v) {
      res = Some(k)
      Deduce(|-(res == None[Z]() | Exists(0 until k + 1)(i => seq(i) == v & res == Some(i))))
    } else {
      Deduce(|-(res == None[Z]() | Exists(0 until k)(i => seq(i) == v & res == Some(i))))
    }
    Deduce(|-(res == None[Z]() | Exists(0 until k + 1)(i => seq(i) == v & res == Some(i))))
    k = k + 1
    Deduce(|-(res == None[Z]() | Exists(0 until k)(i => seq(i) == v & res == Some(i))))
  }
  return res
}

@pure def binsearch(seq: ISZ[Z], v: Z): Option[Z] = {
  Contract(
    Requires(sorted(seq)),
    Ensures(
      (Res == None[Z]()) | Exists(0 until seq.size)(i => seq(i) == v & Res == Some(i)))
  )
  if (seq.isEmpty) {
    return None()
  } else {
    Deduce(|-(seq.size > 0))
    if (seq.size == 1) {
      Deduce(|- (0 < seq.size))
      if (seq(0) == v) {
        return Some(0)
      } else {
        return None()
      }
    }
    var x: Z = 0
    var y: Z = seq.size - 1
    if (v < seq(x) | seq(y) < v) {
      return None()
    } else {
      if (seq(y) == v) {
        return Some(y)
      } else {
        while (x + 1 != y) {
          Invariant(
            Modifies(x, y),
            x >= 0,
            y < seq.size,
            x < y,
            seq(x) <= v,
            v < seq(y),
            All(0 until x)(i => seq(i) <= v),
            All(y until seq.size)(i => v < seq(i))
          )
          Deduce(|- (All(0 until x)(i => seq(i) <= v)))
          val h: Z = (x + y) / 2
          Deduce(|- (x <= h))
          Deduce(|- (h < y))
          if (seq(h) <= v) {
            Deduce(|- (sorted_upper_bound(seq, h, v)))
            Deduce(|- (All(0 until h)(i => seq(i) <= v)))
            x = h
          } else {
            Deduce(|- (sorted_lower_bound(seq, h, v)))
            Deduce(|- (All(y until seq.size)(i => v < seq(i))))
            y = h
          }
        }
        if (seq(x) == v) {
          return Some(x)
        } else {
          return None()
        }
      }
    }
  }
}

def merge(s: ISZ[Z], t: ISZ[Z]): ISZ[Z] = {

}

Sequences

 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
// #Sireum #Logika
import org.sireum._

val empty: ISZ[Z] = ISZ()

val seq1: ISZ[Z] = ISZ(1)

val seq2: ISZ[Z] = ISZ(2)

val seq12: ISZ[Z] = ISZ(1, 2)

val seq21: ISZ[Z] = ISZ(2, 1)

val seq23: ISZ[Z] = ISZ(2, 3)

val seq123: ISZ[Z] = ISZ(1, 2, 3)

val seq34: ISZ[Z] = ISZ(3, 4)

val seq1234: ISZ[Z] = ISZ(1, 2, 3, 4)

assert(empty.isEmpty) // empty / non empty

assert(!seq1.isEmpty) // ...

assert(seq1.nonEmpty) // ...

assert(empty.size == 0) // size

assert(seq1.size == 1) // size

assert(seq12.size == 2) // size

assert(empty :+ 1 == seq1) // append

assert(seq2 :+ 1 == seq21) // append

assert(1 +: seq2 == seq12) // prepend

assert(seq12 ++ seq34 == seq1234)

assert(seq12(0 ~> seq12(1), 1 ~> seq12(0)) == seq21) // simultaneoud update, here: "swap"

def rev[E](s: ISZ[E]): ISZ[E] = {
  var k: Z = 0
  var res = ISZ[E]()
  while (k < s.size) {
    res = s(k) +: res
    k = k + 1
  }
  return res
}

// Using interprocedural check:
//assert(rev(seq12) == seq21)

// With quantification:

def rev_contract[E](s: ISZ[E]): ISZ[E] = {
  Contract(
    Ensures(Res.size == s.size & All(0 until s.size)(i => Res(i) == s(s.size - i - 1)))
  )
  var k: Z = 0
  var res = ISZ[E]()
  while (k < s.size) {
    Invariant(
      Modifies(k, res),
      k >= 0,
      k <= s.size,
      res.size == k,
      All(0 until k)(i => res(i) == s(k - i - 1))
    )
    res = s(k) +: res
    Deduce(|- (res(0) == s(k)))
    Deduce(|- (All(1 until k+1)(i => res(i) == s(k - i))))
    Deduce(|- (All(0 until k+1)(i => res(i) == s(k - i))))
    k = k + 1
  }
  return res
}

assert(rev_contract(seq12) == seq21)

Summary

  • So Far…

    • Initial Scheme concepts
    • Crash course on syntax
  • Optional reading

  • Next week

    • More Parentheses