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
//! This module implements the widening operator for the BrickDomain and BricksDomain.
//! The exact widening procedure depends on three constants.
//! - The *interval threshold* overapproximates the number of times string sequences can occur in a brick.
//! - The *sequence threshold* overapproximates the number of string sequences in a brick by forcing a *Top* value.
//! - The *length threshold* overapproximates the number of bricks in the BricksDomain and forces a *Top* value.
//! A merge is processed without widening when none of the thresholds are exceeded.
use std::{
cmp::{
max, min,
Ordering::{Equal, Greater, Less},
},
collections::BTreeSet,
};
use crate::abstract_domain::AbstractDomain;
use super::{brick::Brick, BrickDomain, BricksDomain};
pub const INTERVAL_THRESHOLD: usize = 8;
pub const SEQUENCE_THRESHOLD: usize = 8;
pub const LENGTH_THRESHOLD: usize = 32;
impl BricksDomain {
/// The widen function of the BricksDomain widens the values during a merge.
/// If the two BrickDomain lists are not comparable or either list exceeds
/// the length threshold, *Top* is returned.
/// Otherwise, the shorter list is padded and the widen function of the
/// BrickDomain is applied to each element in both lists.
/// If after the widening all BrickDomain values are *Top*, return
/// the *Top* value for the BricksDomain.
pub fn widen(&self, other: &BricksDomain) -> Self {
let self_num_of_bricks = self.unwrap_value().len();
let other_num_of_bricks = other.unwrap_value().len();
let mut new_self = self.clone();
let mut new_other = other.clone();
match self_num_of_bricks.cmp(&other_num_of_bricks) {
Less => {
new_self = self.pad_list(other);
}
Greater => {
new_other = other.pad_list(self);
}
Equal => (),
}
if !new_self.is_less_or_equal(other) && !new_other.is_less_or_equal(self)
|| self_num_of_bricks > LENGTH_THRESHOLD
|| other_num_of_bricks > LENGTH_THRESHOLD
{
return BricksDomain::Top;
}
let mut widened_brick_domain_list: Vec<BrickDomain> = Vec::new();
for (self_brick, other_brick) in new_self
.unwrap_value()
.iter()
.zip(new_other.unwrap_value().iter())
{
widened_brick_domain_list.push(self_brick.merge(other_brick));
}
if BricksDomain::all_bricks_are_top(&widened_brick_domain_list) {
return BricksDomain::Top;
}
BricksDomain::Value(widened_brick_domain_list)
}
/// Checks whether all bricks of the BricksDomain are *Top* values.
/// If so, the BricksDomain itself should be converted into a *Top* value.
pub fn all_bricks_are_top(bricks: &[BrickDomain]) -> bool {
bricks.iter().all(|brick| matches!(brick, BrickDomain::Top))
}
/// Checks whether the current BricksDomain is less or equal than the other BricksDomain
/// by definition of the partial order.
pub fn is_less_or_equal(&self, other: &BricksDomain) -> bool {
self.unwrap_value()
.iter()
.zip(other.unwrap_value().iter())
.all(|(self_brick, other_brick)| self_brick.is_less_or_equal(other_brick))
}
}
impl BrickDomain {
/// The widen function of the BrickDomain takes the union of both
/// BrickDomains and returns *Top* if the number of sequences exceeds
/// a certain threshold.
/// If neither of the domains are *Top*, the minimum and maximum
/// of the interval bounds are taken and it is checked whether
/// their difference exceeds a certain threshold.
/// If so *min* is set to 0 and *max* is set to infinity (here Max value of 32 bits).
/// Otherwise, their values are taken as new bounds for the merged domain.
pub fn widen(&self, other: &BrickDomain) -> Self {
let self_brick = self.unwrap_value();
let other_brick = other.unwrap_value();
let merged_sequence = self_brick
.get_sequence()
.union(other_brick.get_sequence())
.cloned()
.collect::<BTreeSet<String>>();
if merged_sequence.len() > SEQUENCE_THRESHOLD {
return BrickDomain::Top;
}
let mut widened_brick = Brick::new();
let min_bound = min(self_brick.get_min(), other_brick.get_min());
let max_bound = max(self_brick.get_max(), other_brick.get_max());
if max_bound - min_bound > INTERVAL_THRESHOLD as u32 {
widened_brick.set_min(0);
widened_brick.set_max(u32::MAX);
} else {
widened_brick.set_min(min_bound);
widened_brick.set_max(max_bound);
}
widened_brick.set_sequence(merged_sequence);
BrickDomain::Value(widened_brick)
}
/// Checks whether the current BrickDomain is less or equal than the other BrickDomain
/// by definition of the partial order.
/// Empty strings are ignored for order comparisons.
pub fn is_less_or_equal(&self, other: &BrickDomain) -> bool {
match (self.is_top(), other.is_top()) {
(false, false) => {
let self_brick = self.unwrap_value();
let other_brick = other.unwrap_value();
if self_brick.is_empty_string() || other_brick.is_empty_string() {
return true;
}
if self_brick
.get_sequence()
.is_subset(other_brick.get_sequence())
&& self_brick.get_min() >= other_brick.get_min()
&& self_brick.get_max() <= other_brick.get_max()
{
return true;
}
false
}
(true, false) => false,
(false, true) | (true, true) => true,
}
}
}