proximitylib/caching/
unbounded_linear_cache.rsuse crate::caching::approximate_cache::ApproximateCache;
use crate::numerics::comp::ApproxComparable;
pub struct UnboundedLinearCache<K, V>
where
K: ApproxComparable,
V: Clone,
{
keys: Vec<K>,
values: Vec<V>,
tolerance: f32,
}
impl<K, V> UnboundedLinearCache<K, V>
where
K: ApproxComparable,
V: Clone,
{
pub fn new(tolerance: f32) -> Self {
UnboundedLinearCache {
keys: Vec::new(),
values: Vec::new(),
tolerance,
}
}
pub fn with_initial_capacity(tolerance: f32, capacity: usize) -> Self {
UnboundedLinearCache {
keys: Vec::with_capacity(capacity),
values: Vec::with_capacity(capacity),
tolerance,
}
}
}
impl<K, V> ApproximateCache<K, V> for UnboundedLinearCache<K, V>
where
K: ApproxComparable,
V: Clone,
{
fn find(&mut self, to_find: &K) -> Option<V> {
let potential_match = self
.keys
.iter()
.position(|key| to_find.roughly_matches(key, self.tolerance));
potential_match.map(|i| self.values[i].clone())
}
fn insert(&mut self, key: K, value: V) {
self.keys.push(key);
self.values.push(value);
}
fn len(&self) -> usize {
self.keys.len()
}
}
#[cfg(test)]
mod tests {
use crate::caching::approximate_cache::COMPTIME_CACHE_SIZE;
use super::*;
use quickcheck::{QuickCheck, TestResult};
const TEST_TOLERANCE: f32 = 1e-8;
const TEST_MAX_SIZE: usize = COMPTIME_CACHE_SIZE;
#[test]
fn start_always_matches_exactly() {
fn qc_start_always_matches_exactly(
start_state: Vec<(f32, u8)>,
key: f32,
value: u8,
) -> TestResult {
let mut ulc = UnboundedLinearCache::<f32, u8>::new(TEST_TOLERANCE);
if !key.is_finite() || start_state.len() > TEST_MAX_SIZE {
return TestResult::discard();
}
ulc.insert(key, value);
for &(k, v) in start_state.iter() {
ulc.insert(k, v);
}
assert!(ulc.len() == start_state.len() + 1);
if let Some(x) = ulc.find(&key) {
TestResult::from_bool(x == value)
} else {
TestResult::failed()
}
}
QuickCheck::new()
.tests(10_000)
.min_tests_passed(1_000)
.quickcheck(
qc_start_always_matches_exactly as fn(Vec<(f32, u8)>, f32, u8) -> TestResult,
);
}
#[test]
fn middle_always_matches() {
fn qc_middle_always_matches(
start_state: Vec<(f32, u8)>,
key: f32,
value: u8,
end_state: Vec<(f32, u8)>,
) -> TestResult {
let mut ulc = UnboundedLinearCache::<f32, u8>::new(TEST_TOLERANCE);
if !key.is_finite() || start_state.len() > TEST_MAX_SIZE {
return TestResult::discard();
}
for &(k, v) in start_state.iter() {
ulc.insert(k, v);
}
ulc.insert(key, value);
for &(k, v) in end_state.iter() {
ulc.insert(k, v);
}
assert!(ulc.len() == start_state.len() + end_state.len() + 1);
TestResult::from_bool(ulc.find(&key).is_some())
}
QuickCheck::new()
.tests(10_000)
.min_tests_passed(1_000)
.quickcheck(
qc_middle_always_matches
as fn(Vec<(f32, u8)>, f32, u8, Vec<(f32, u8)>) -> TestResult,
);
}
}