krabka_verified/remote_read/
remote_time_index_candidate_count.rs1use {crate::log_index::TimeEntry, creusot_std::prelude::*};
2
3#[cfg(creusot)]
5#[cfg_attr(test, mutants::skip)]
6#[logic(open)]
7fn finished_selection(
8 starts: Seq<i64>,
9 ends: Seq<i64>,
10 finished: Seq<bool>,
11 best: Option<usize>,
12 count: Int,
13 latest: bool,
14) -> bool {
15 pearlite! {
16 match best {
17 Some(best) => best@ < count && finished[best@]
18 && 0 <= starts[best@]@ && starts[best@]@ <= ends[best@]@
19 && forall<i: Int> 0 <= i && i < count && finished[i]
20 && 0 <= starts[i]@ && starts[i]@ <= ends[i]@
21 ==> if latest { ends[i]@ <= ends[best@]@ } else { starts[best@]@ <= starts[i]@ },
22 None => forall<i: Int> 0 <= i && i < count
23 ==> !finished[i] || starts[i]@ < 0 || ends[i]@ < starts[i]@,
24 }
25 }
26}
27
28#[krabka_macros::finished_selection(latest)]
29fn select_finished_index(
30 starts: &[i64],
31 ends: &[i64],
32 finished: &[bool],
33 latest: bool,
34) -> Option<usize> {
35 let mut best: Option<usize> = None;
36 let mut index = 0usize;
37 #[invariant(index@ <= [email protected]())]
38 #[invariant(finished_selection(starts@, ends@, finished@, best, index@, latest))]
39 #[variant([email protected]() - index@)]
40 while index < starts.len() {
41 if let (Some(true), Some(&start), Some(&end)) =
42 (finished.get(index), starts.get(index), ends.get(index))
43 && start >= 0
44 && start <= end
45 {
46 let keep = match best {
47 Some(current) if latest => {
48 matches!(ends.get(current), Some(&kept_end) if kept_end >= end)
49 }
50 Some(current) => {
51 matches!(starts.get(current), Some(&kept_start) if kept_start <= start)
52 }
53 None => false,
54 };
55 if !keep {
56 best = Some(index);
57 }
58 }
59 index += 1;
60 }
61 best
62}
63
64#[krabka_macros::finished_selection(false)]
65#[must_use]
71pub fn tiered_earliest_finished_index(
72 starts: &[i64],
73 ends: &[i64],
74 finished: &[bool],
75) -> Option<usize> {
76 select_finished_index(starts, ends, finished, false)
77}
78
79#[krabka_macros::finished_selection(true)]
80#[must_use]
82pub fn tiered_latest_finished_index(
83 starts: &[i64],
84 ends: &[i64],
85 finished: &[bool],
86) -> Option<usize> {
87 select_finished_index(starts, ends, finished, true)
88}
89
90#[requires([email protected]() == [email protected]())]
93#[requires(0 <= segment_start@)]
94#[requires(segment_start@ <= segment_end@)]
95#[ensures(owning_epoch_selection(epochs@, starts@, segment_start@, segment_end@, [email protected](), result))]
96#[must_use]
97pub fn tiered_owning_epoch_index(
98 epochs: &[i32],
99 starts: &[i64],
100 segment_start: i64,
101 segment_end: i64,
102) -> Option<usize> {
103 let mut best: Option<usize> = None;
104 let mut index = 0;
105 #[invariant(index@ <= [email protected]())]
106 #[invariant(owning_epoch_selection(epochs@, starts@, segment_start@, segment_end@, index@, best))]
107 #[variant([email protected]() - index@)]
108 while index < starts.len() {
109 if let (Some(&epoch), Some(&start)) = (epochs.get(index), starts.get(index))
110 && epoch >= 0
111 && start >= segment_start
112 && start <= segment_end
113 {
114 match best {
115 Some(current) if matches!(starts.get(current), Some(&kept) if kept >= start) => {}
116 _ => best = Some(index),
117 }
118 }
119 index += 1;
120 }
121 best
122}
123
124#[ensures(result@ <= [email protected]())]
139#[ensures(forall<i: Int> 0 <= i && i < result@
140 ==> entries@[i].key@ < target_timestamp@)]
141#[ensures(forall<i: Int> 1 <= i && i < result@
142 ==> entries@[i - 1].coordinate@ < entries@[i].coordinate@)]
143#[ensures(result@ < [email protected]() ==>
144 (result@ > 0 && entries@[result@].coordinate@ <= entries@[result@ - 1].coordinate@)
145 || entries@[result@].key@ >= target_timestamp@)]
146#[must_use]
147pub fn remote_time_index_candidate_count(entries: &[TimeEntry], target_timestamp: i64) -> usize {
148 let mut count = 0usize;
149 #[invariant(count@ <= [email protected]())]
150 #[invariant(forall<i: Int> 0 <= i && i < count@
151 ==> entries@[i].key@ < target_timestamp@)]
152 #[invariant(forall<i: Int> 1 <= i && i < count@
153 ==> entries@[i - 1].coordinate@ < entries@[i].coordinate@)]
154 #[variant([email protected]() - count@)]
155 while count < entries.len() {
156 let Some(entry) = entries.get(count) else {
157 break;
158 };
159 if count > 0
160 && matches!(entries.get(count - 1), Some(previous) if entry.coordinate <= previous.coordinate)
161 {
162 break;
163 }
164 if entry.key >= target_timestamp {
165 break;
166 }
167 count += 1;
168 }
169 count
170}
171
172#[cfg(creusot)]
174#[cfg_attr(test, mutants::skip)]
175#[logic(open)]
176fn owning_epoch_selection(
178 epochs: Seq<i32>,
179 starts: Seq<i64>,
180 first: Int,
181 last: Int,
182 count: Int,
183 selected: Option<usize>,
184) -> bool {
185 pearlite! { match selected {
186 Some(best) => best@ < count && 0 <= epochs[best@]@
187 && first <= starts[best@]@ && starts[best@]@ <= last
188 && forall<i: Int> 0 <= i && i < count && 0 <= epochs[i]@
189 && first <= starts[i]@ && starts[i]@ <= last
190 ==> starts[i]@ <= starts[best@]@,
191 None => forall<i: Int> 0 <= i && i < count
192 ==> epochs[i]@ < 0 || starts[i]@ < first || last < starts[i]@,
193 } }
194}