-
Notifications
You must be signed in to change notification settings - Fork 1
/
Percentile.dfy
99 lines (86 loc) · 2.71 KB
/
Percentile.dfy
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
// Sum of elements of A from indices 0 to end.
// end is inclusive! (not James's normal way of thinking!!)
function SumUpto(A: array<real>, end: int): real
requires -1 <= end < A.Length
reads A
{
if end == -1 then
0.0
else
A[end] + SumUpto(A, end-1)
}
function Sum(A: array<real>): real
reads A
{
SumUpto(A, A.Length-1)
}
method Percentile(p: real, A: array<real>, total: real) returns (i: int)
requires forall i | 0 <= i < A.Length :: A[i] > 0.0
requires 0.0 <= p <= 100.0
requires total == Sum(A)
requires total > 0.0
ensures -1 <= i < A.Length
ensures SumUpto(A, i) <= (p/100.0) * total
ensures i+1 < A.Length ==> SumUpto(A, i+1) > (p/100.0) * total
{
i := -1;
var s: real := 0.0;
while i+1 != A.Length && s + A[i+1] <= (p/100.0) * total
invariant -1 <= i < A.Length
invariant s == SumUpto(A, i)
invariant s <= (p/100.0) * total
{
i := i + 1;
s := s + A[i];
}
}
// example showing that, with the original postcondition, the answer is non-unique!
method PercentileNonUniqueAnswer() returns (p: real, A: array<real>, total: real, i1: int, i2: int)
ensures forall i | 0 <= i < A.Length :: A[i] > 0.0
ensures 0.0 <= p <= 100.0
ensures total == Sum(A)
ensures total > 0.0
ensures -1 <= i1 < A.Length
ensures SumUpto(A, i1) <= (p/100.0) * total
ensures i1+1 < A.Length ==> SumUpto(A, i1+1) >= (p/100.0) * total
ensures -1 <= i2 < A.Length
ensures SumUpto(A, i2) <= (p/100.0) * total
ensures i2+1 < A.Length ==> SumUpto(A, i2+1) >= (p/100.0) * total
ensures i1 != i2
{
p := 100.0;
A := new real[1];
A[0] := 1.0;
total := 1.0;
assert SumUpto(A, 0) == 1.0;
i1 := -1;
i2 := 0;
}
// proof that, with the corrected postcondition, the answer is unique
lemma PercentileUniqueAnswer(p: real, A: array<real>, total: real, i1: int, i2: int)
requires forall i | 0 <= i < A.Length :: A[i] > 0.0
requires 0.0 <= p <= 100.0
requires total == Sum(A)
requires total > 0.0
requires -1 <= i1 < A.Length
requires SumUpto(A, i1) <= (p/100.0) * total
requires i1+1 < A.Length ==> SumUpto(A, i1+1) > (p/100.0) * total
requires -1 <= i2 < A.Length
requires SumUpto(A, i2) <= (p/100.0) * total
requires i2+1 < A.Length ==> SumUpto(A, i2+1) > (p/100.0) * total
decreases if i2 < i1 then 1 else 0 // wlog i1 <= i2
ensures i1 == i2
{
if i1+1< i2 {
SumUpto_increase(A, i1+1, i2);
}
}
// lemma for previous proof: when an array has strictly positive elements, the
// sums strictly increase left to right
lemma SumUpto_increase(A: array<real>, end1: int, end2: int)
requires forall i | 0 <= i < A.Length :: A[i] > 0.0
requires -1 <= end1 < A.Length
requires -1 <= end2 < A.Length
requires end1 < end2
ensures SumUpto(A, end1) < SumUpto(A, end2)
{}