summaryrefslogtreecommitdiff
path: root/Test/dafny3/GenericSort.dfy
blob: 7ea038be7688dad0e96a3942edfa8e61aed54fe8 (plain)
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
// RUN: %dafny /compile:0 /dprint:"%t.dprint" "%s" > "%t"
// RUN: %diff "%s.expect" "%t"

abstract module TotalOrder {
  type T // the type to be compared
  predicate method Leq(a: T, b: T) // Leq(a,b) iff a <= b
  // Three properties of total orders.  Here, they are given as unproved
  // lemmas, that is, as axioms.
  lemma Antisymmetry(a: T, b: T)
    requires Leq(a, b) && Leq(b, a);
    ensures a == b;
  lemma Transitivity(a: T, b: T, c: T)
    requires Leq(a, b) && Leq(b, c);
    ensures Leq(a, c);
  lemma Totality(a: T, b: T)
    ensures Leq(a, b) || Leq(b, a);
}

abstract module Sort {
  import O as TotalOrder  // let O denote some module that has the members of TotalOrder

  predicate Sorted(a: array<O.T>, low: int, high: int)
    requires a != null && 0 <= low <= high <= a.Length;
    reads a;
    // The body of the predicate is hidden outside the module, but the postcondition is
    // "exported" (that is, visible, known) outside the module.  Here, we choose the
    // export the following property:
    ensures Sorted(a, low, high) ==> forall i, j :: low <= i < j < high ==> O.Leq(a[i], a[j]);
  {
    forall i, j :: low <= i < j < high ==> O.Leq(a[i], a[j])
  }

  // In the insertion sort routine below, it's more convenient to keep track of only that
  // neighboring elements of the array are sorted...
  predicate NeighborSorted(a: array<O.T>, low: int, high: int)
    requires a != null && 0 <= low <= high <= a.Length;
    reads a;
  {
    forall i :: low < i < high ==> O.Leq(a[i-1], a[i])
  }
  // ...but we show that property to imply all pairs to be sorted.  The proof of this
  // lemma uses the transitivity property.
  lemma NeighborSorted_implies_Sorted(a: array<O.T>, low: int, high: int)
    requires a != null && 0 <= low <= high <= a.Length;
    requires NeighborSorted(a, low, high);
    ensures Sorted(a, low, high);
    decreases high - low;
  {
    if low != high {
      NeighborSorted_implies_Sorted(a, low+1, high);
      forall j | low+1 < j < high
      {
        O.Transitivity(a[low], a[low+1], a[j]);
      }
    }
  }

  // Standard insertion sort method
  method InsertionSort(a: array<O.T>)
    requires a != null;
    modifies a;
    ensures Sorted(a, 0, a.Length);
    ensures multiset(a[..]) == old(multiset(a[..]));
  {
    if a.Length == 0 { return; }
    var i := 1;
    while i < a.Length
      invariant 0 < i <= a.Length;
      invariant NeighborSorted(a, 0, i);
      invariant multiset(a[..]) == old(multiset(a[..]));
    {
      var j := i;
      while 0 < j && !O.Leq(a[j-1], a[j])
        invariant 0 <= j <= i;
        invariant NeighborSorted(a, 0, j);
        invariant NeighborSorted(a, j, i+1);
        invariant 0 < j < i ==> O.Leq(a[j-1], a[j+1]);
        invariant multiset(a[..]) == old(multiset(a[..]));
      {
        // The proof of correctness uses the totality property here.
        // It implies that if two elements are not previously in
        // sorted order, they will be after swapping them.
        O.Totality(a[j-1], a[j]);
        a[j], a[j-1] := a[j-1], a[j];
        j := j - 1;
      }
      i := i + 1;
    }
    NeighborSorted_implies_Sorted(a, 0, a.Length);
  }
}

// Natural ordering of the integers
module IntOrder refines TotalOrder {
  // Instantiate type T with a datatype wrapper around an integer.
  // (If there were type synonyms, we could perhaps have written just "type T = int".)
  datatype T = Int(i: int)
  // Define the ordering on these integers
  predicate method Leq ...
    ensures Leq(a, b) ==> a.i <= b.i;
  {
    a.i <= b.i
  }
  // The three required properties of the order are proved here as lemmas.
  // The proofs are automatic and don't require any further assistance.
  lemma Antisymmetry ... { }
  lemma Transitivity ... { }
  lemma Totality ... { }
}

// Another example of a TotalOrder. Now we can sort pairs of integers.
// Lexiographic ordering of pairs of integers
module IntLexOrder refines TotalOrder {
  datatype T = Int(i: int, j: int)
  predicate method Leq ... {
    if a.i < b.i then true
    else if a.i == b.i then a.j <= b.j
    else false
  }
  lemma Antisymmetry ... { }
  lemma Transitivity ... { }
  lemma Totality ... { }
}


// A test harness.
module Client {
  module IntSort refines Sort {  // this creates a new sorting module, like Sort by fully revealing O to be IntOrder
    import O = IntOrder
  }
  import I = IntOrder
  method TheMain() {
     var a := new I.T[4];
     a[0] := I.T.Int(6);  // alternatively, we could have written the RHS as:  IntSort.O.T.Int(6)
     a[1] := I.T.Int(1);
     a[2] := I.T.Int(0);
     a[3] := I.T.Int(4);
     // These are now the elements of the array:
     assert a[..] == [I.T.Int(6), I.T.Int(1), I.T.Int(0), I.T.Int(4)];
     // Call the sorting routine to sort the array
     IntSort.InsertionSort(a);
     // Check the answer
     assert IntSort.O.Leq(a[0], a[1]);  // lemma
     assert IntSort.O.Leq(a[1], a[2]);  // lemma
     assert IntSort.O.Leq(a[2], a[3]);  // lemma
     assert a[..] == [I.T.Int(0), I.T.Int(1), I.T.Int(4), I.T.Int(6)];
     // why not print out the result!
     print a[..], "\n";
  }
}

// ----- Now for the actual 'int' type -----

module intOrder refines TotalOrder {
  // Instantiate type T with a datatype wrapper around an integer.
  type T = int
  // Define the ordering on these integers
  predicate method Leq ...
    ensures Leq(a, b) ==> a <= b;
  {
    a <= b
  }
  // The three required properties of the order are proved here as lemmas.
  // The proofs are automatic and don't require any further assistance.
  lemma Antisymmetry ... { }
  lemma Transitivity ... { }
  lemma Totality ... { }
}

module AnotherClient {
  module intSort refines Sort {
    import O = intOrder
  }
  import I = intOrder
  method TheMain() {
    var a := new int[4];  // alternatively, could have written 'new I.T[4]'
    a[0] := 6;
    a[1] := 1;
    a[2] := 0;
    a[3] := 4;
    // These are now the elements of the array:
    assert a[..] == [6, 1, 0, 4];
    // Call the sorting routine to sort the array
    intSort.InsertionSort(a);
    // Check the answer
    assert intSort.O.Leq(a[0], a[1]);  // lemma
    assert intSort.O.Leq(a[1], a[2]);  // lemma
    assert intSort.O.Leq(a[2], a[3]);  // lemma
    assert a[..] == [0, 1, 4, 6];
    // why not print out the result!
    print a[..], "\n";
  }
}

method Main() {
  Client.TheMain();
  AnotherClient.TheMain();
}