1use std::cmp::min;
2use std::iter::IntoIterator;
3
4use crate::bounds::*;
5use crate::clocks::*;
6use crate::constants::*;
7use crate::storage::*;
8
9pub struct Constraint<B: Bound> {
11 pub(crate) left: Clock,
12 pub(crate) right: Clock,
13 pub(crate) bound: B,
14}
15
16impl<B: Bound> Constraint<B> {
17 pub fn left(&self) -> Clock {
19 self.left
20 }
21
22 pub fn right(&self) -> Clock {
24 self.right
25 }
26
27 pub fn bound(&self) -> &B {
29 &self.bound
30 }
31
32 pub fn new(left: impl AnyClock, right: impl AnyClock, bound: B) -> Self {
34 Self {
35 left: left.as_clock(),
36 right: right.as_clock(),
37 bound,
38 }
39 }
40
41 pub fn new_le(clock: impl AnyClock, constant: B::Constant) -> Self {
43 Constraint {
44 left: clock.as_clock(),
45 right: Clock::ZERO,
46 bound: B::new_le(constant),
47 }
48 }
49
50 pub fn new_lt(clock: impl AnyClock, constant: B::Constant) -> Self {
52 Constraint {
53 left: clock.as_clock(),
54 right: Clock::ZERO,
55 bound: B::new_lt(constant),
56 }
57 }
58
59 pub fn new_ge(clock: impl AnyClock, constant: B::Constant) -> Self {
63 Constraint {
64 left: Clock::ZERO,
65 right: clock.as_clock(),
66 bound: B::new_le(
67 constant
68 .checked_neg()
69 .expect("overflow while negating constant"),
70 ),
71 }
72 }
73
74 pub fn new_gt(clock: impl AnyClock, constant: B::Constant) -> Self {
78 Constraint {
79 left: Clock::ZERO,
80 right: clock.as_clock(),
81 bound: B::new_lt(
82 constant
83 .checked_neg()
84 .expect("overflow while negating constant"),
85 ),
86 }
87 }
88 pub fn new_diff_le(left: impl AnyClock, right: impl AnyClock, constant: B::Constant) -> Self {
90 Constraint {
91 left: left.as_clock(),
92 right: right.as_clock(),
93 bound: B::new_le(constant),
94 }
95 }
96
97 pub fn new_diff_lt(left: impl AnyClock, right: impl AnyClock, constant: B::Constant) -> Self {
99 Constraint {
100 left: left.as_clock(),
101 right: right.as_clock(),
102 bound: B::new_lt(constant),
103 }
104 }
105
106 pub fn new_diff_ge(left: impl AnyClock, right: impl AnyClock, constant: B::Constant) -> Self {
110 Constraint {
111 left: right.as_clock(),
113 right: left.as_clock(),
114 bound: B::new_le(
115 constant
116 .checked_neg()
117 .expect("overflow while negating constant"),
118 ),
119 }
120 }
121
122 pub fn new_diff_gt(left: impl AnyClock, right: impl AnyClock, constant: B::Constant) -> Self {
126 Constraint {
127 left: right.as_clock(),
129 right: left.as_clock(),
130 bound: B::new_lt(
131 constant
132 .checked_neg()
133 .expect("overflow while negating constant"),
134 ),
135 }
136 }
137}
138
139pub trait Zone {
141 type Bound: Bound;
142
143 fn new_unconstrained(num_variables: usize) -> Self;
145 fn new_zero(num_variables: usize) -> Self;
147
148 fn with_constraints<U>(num_variables: usize, constraints: U) -> Self
150 where
151 U: IntoIterator<Item = Constraint<Self::Bound>>;
152
153 fn num_variables(&self) -> usize;
155
156 fn num_clocks(&self) -> usize;
161
162 fn get_bound(&self, left: impl AnyClock, right: impl AnyClock) -> &Self::Bound;
164
165 fn is_empty(&self) -> bool;
167
168 fn add_constraint(&mut self, constraint: Constraint<Self::Bound>);
170 fn add_constraints<U>(&mut self, constraints: U)
172 where
173 U: IntoIterator<Item = Constraint<Self::Bound>>;
174
175 fn intersect(&mut self, other: &Self);
177
178 fn future(&mut self);
182 fn past(&mut self);
186
187 fn reset(&mut self, clock: Variable, value: <Self::Bound as Bound>::Constant);
189
190 fn is_unbounded(&self, clock: impl AnyClock) -> bool;
192
193 fn get_upper_bound(&self, clock: impl AnyClock) -> Option<<Self::Bound as Bound>::Constant>;
195 fn get_lower_bound(&self, clock: impl AnyClock) -> Option<<Self::Bound as Bound>::Constant>;
197
198 fn is_satisfied(&self, constraint: Constraint<Self::Bound>) -> bool;
200
201 fn includes(&self, other: &Self) -> bool;
203
204 fn resize(&self, num_variables: usize) -> Self;
208}
209
210#[inline(always)]
212pub fn clocks<Z: Zone>(zone: &Z) -> impl Iterator<Item = Clock> {
213 (0..zone.num_clocks()).map(Clock)
214}
215
216#[inline(always)]
218pub fn variables<Z: Zone>(zone: &Z) -> impl Iterator<Item = Variable> {
219 (1..zone.num_clocks()).map(Variable)
220}
221
222#[derive(Eq, PartialEq, Hash, Clone, Debug)]
226pub struct Dbm<B: Bound, L: Layout<B> = LinearLayout<B>> {
227 dimension: usize,
229 layout: L,
231
232 _phantom_bound: std::marker::PhantomData<B>,
233}
234
235impl<B: Bound, L: Layout<B>> Dbm<B, L> {
236 fn new(num_variables: usize, default: B) -> Self {
237 let dimension = num_variables + 1;
238 let mut layout = L::new(num_variables, default);
239 layout.set(Clock::ZERO, Clock::ZERO, B::le_zero());
240 for index in 1..dimension {
241 let clock = Clock::from_index(index);
242 layout.set(Clock::ZERO, clock, B::le_zero());
243 layout.set(clock, clock, B::le_zero());
244 }
245 Dbm {
246 dimension,
247 layout,
248 _phantom_bound: std::marker::PhantomData,
249 }
250 }
251
252 #[inline(always)]
254 fn canonicalize_touched(&mut self, touched: Clock) {
255 for index in 0..self.dimension {
256 let left = Clock::from_index(index);
257 for index in 0..self.dimension {
258 let right = Clock::from_index(index);
259 let bound = self
260 .layout
261 .get(left, touched)
262 .add(self.layout.get(touched, right))
263 .expect("overflow while adding bounds");
264 if bound.is_tighter_than(self.layout.get(left, right)) {
265 self.layout.set(left, right, bound);
266 }
267 }
268 }
269 }
270
271 fn canonicalize(&mut self) {
272 for index in 0..self.dimension {
273 let touched = Clock::from_index(index);
274 self.canonicalize_touched(touched);
275 }
276 }
277}
278
279impl<B: Bound, L: Layout<B>> Zone for Dbm<B, L> {
280 type Bound = B;
281
282 fn new_unconstrained(num_variables: usize) -> Self {
283 Dbm::new(num_variables, B::unbounded())
284 }
285 fn new_zero(num_variables: usize) -> Self {
286 Dbm::new(num_variables, B::le_zero())
287 }
288
289 fn with_constraints<U>(num_variables: usize, constraints: U) -> Self
290 where
291 U: IntoIterator<Item = Constraint<B>>,
292 {
293 let mut zone = Self::new_unconstrained(num_variables);
294 for constraint in constraints {
295 zone.layout
296 .set(constraint.left, constraint.right, constraint.bound.clone());
297 }
298 zone.canonicalize();
299 zone
300 }
301
302 #[inline(always)]
303 fn num_variables(&self) -> usize {
304 self.dimension - 1
305 }
306
307 #[inline(always)]
308 fn num_clocks(&self) -> usize {
309 self.dimension
310 }
311
312 #[inline(always)]
313 fn get_bound(&self, left: impl AnyClock, right: impl AnyClock) -> &B {
314 self.layout.get(left, right)
315 }
316
317 #[inline(always)]
318 fn is_empty(&self) -> bool {
319 self.layout
320 .get(Clock::ZERO, Clock::ZERO)
321 .is_tighter_than(&B::le_zero())
322 }
323
324 fn add_constraint(&mut self, constraint: Constraint<B>) {
325 let bound = self.layout.get(constraint.left, constraint.right);
326 if constraint.bound.is_tighter_than(&bound) {
327 self.layout
328 .set(constraint.left, constraint.right, constraint.bound);
329 self.canonicalize_touched(constraint.left);
330 self.canonicalize_touched(constraint.right);
331 }
332 }
333
334 fn add_constraints<U>(&mut self, constraints: U)
335 where
336 U: IntoIterator<Item = Constraint<B>>,
337 {
338 for constraint in constraints {
339 self.add_constraint(constraint);
340 }
341 }
342
343 fn intersect(&mut self, other: &Self) {
344 assert_eq!(
345 self.dimension, other.dimension,
346 "unable to intersect zones of different dimension"
347 );
348 for index in 0..self.dimension {
349 let left = Clock::from_index(index);
350 for index in 0..self.dimension {
351 let right = Clock::from_index(index);
352 if other
353 .layout
354 .get(left, right)
355 .is_tighter_than(&self.layout.get(left, right))
356 {
357 self.layout
358 .set(left, right, other.layout.get(left, right).clone());
359 }
360 }
361 }
362 self.canonicalize();
363 }
364
365 fn future(&mut self) {
366 for index in 1..self.dimension {
367 let left = Clock::from_index(index);
368 self.layout.set(left, Clock::ZERO, B::unbounded());
369 }
370 }
371 fn past(&mut self) {
372 for index in 1..self.dimension {
373 let right = Clock::from_index(index);
374 self.layout.set(Clock::ZERO, right, B::le_zero());
375 for index in 1..self.dimension {
376 let left = Clock::from_index(index);
377 if self
378 .layout
379 .get(left, right)
380 .is_tighter_than(self.layout.get(Clock::ZERO, right))
381 {
382 self.layout
383 .set(Clock::ZERO, right, self.layout.get(left, right).clone());
384 }
385 }
386 }
387 }
388
389 fn reset(&mut self, clock: Variable, value: B::Constant) {
390 let le_pos_value = B::new_le(value.clone());
391 let le_neg_value = B::new_le(value.checked_neg().unwrap());
392 for index in 0..self.dimension {
393 let other = Clock::from_index(index);
394 self.layout.set(
395 clock,
396 other,
397 self.layout
398 .get(Clock::ZERO, other)
399 .add(&le_pos_value)
400 .unwrap(),
401 );
402 self.layout.set(
403 other,
404 clock,
405 self.layout
406 .get(other, Clock::ZERO)
407 .add(&le_neg_value)
408 .unwrap(),
409 );
410 }
411 }
412
413 fn is_unbounded(&self, clock: impl AnyClock) -> bool {
414 self.layout.get(clock, Clock::ZERO).is_unbounded()
415 }
416
417 fn get_upper_bound(&self, clock: impl AnyClock) -> Option<B::Constant> {
418 self.layout.get(clock, Clock::ZERO).constant()
419 }
420
421 fn get_lower_bound(&self, clock: impl AnyClock) -> Option<B::Constant> {
422 Some(
423 self.layout
424 .get(Clock::ZERO, clock)
425 .constant()?
426 .checked_neg()
427 .unwrap(),
428 )
429 }
430
431 fn is_satisfied(&self, constraint: Constraint<B>) -> bool {
432 !constraint
433 .bound
434 .is_tighter_than(self.layout.get(constraint.left, constraint.right))
435 }
436
437 fn includes(&self, other: &Self) -> bool {
438 for index in 0..self.dimension {
439 let left = Clock::from_index(index);
440 for index in 0..self.dimension {
441 let right = Clock::from_index(index);
442 if self
443 .layout
444 .get(left, right)
445 .is_tighter_than(other.layout.get(left, right))
446 {
447 return false;
448 }
449 }
450 }
451 true
452 }
453
454 fn resize(&self, num_variables: usize) -> Self {
455 let mut other = Self::new_unconstrained(num_variables);
456 for index in 0..min(self.dimension, other.dimension) {
457 let left = Clock::from_index(index);
458 for index in 0..min(self.dimension, other.dimension) {
459 let right = Clock::from_index(index);
460 let bound = self.layout.get(left, right);
461 other.layout.set(left, right, bound.clone());
462 }
463 }
464 other.canonicalize();
465 other
466 }
467}
468
469pub type ZoneI32 = Dbm<i32>;
471
472pub type ZoneI64 = Dbm<i64>;
474
475#[cfg(feature = "float")]
477pub type ZoneF32 = Dbm<ConstantBound<ordered_float::NotNan<f32>>>;
478
479#[cfg(feature = "float")]
481pub type ZoneF64 = Dbm<ConstantBound<ordered_float::NotNan<f64>>>;
482
483#[cfg(test)]
484mod tests {
485 use super::*;
486
487 #[test]
488 fn test_basics() {
489 let mut zone: Dbm<i64> = Dbm::new_zero(3);
490 let x = Clock::variable(0);
491 let y = Clock::variable(1);
492
493 assert_eq!(zone.get_lower_bound(x), Some(0));
494 assert_eq!(zone.get_upper_bound(x), Some(0));
495 zone.future();
496 assert_eq!(zone.get_lower_bound(x), Some(0));
497 assert_eq!(zone.get_upper_bound(x), None);
498 assert!(zone.is_unbounded(x));
499 let mut copy = zone.clone();
500 zone.add_constraint(Constraint::new_lt(x, 4));
501 assert!(!zone.is_unbounded(x));
502 assert_eq!(zone.get_lower_bound(x), Some(0));
503 assert_eq!(zone.get_upper_bound(x), Some(4));
504 assert!(copy.includes(&zone));
505 copy.add_constraint(Constraint::new_le(x, 4));
506 assert!(copy.includes(&zone));
507 copy.add_constraint(Constraint::new_lt(x, 3));
508 assert!(!copy.includes(&zone));
509 assert!(zone.includes(©));
510 zone.intersect(©);
511 assert!(zone.includes(©));
512 assert!(copy.includes(&zone));
513 copy.add_constraint(Constraint::new_diff_lt(x, y, 4));
514 copy.add_constraint(Constraint::new_diff_gt(x, y, 5));
515 assert!(copy.is_empty());
516 }
517}