proximipy/caching/
unbounded_linear_cache.rs1use crate::caching::approximate_cache::ApproximateCache;
2use crate::numerics::comp::ApproxComparable;
3pub struct UnboundedLinearCache<K, V>
19where
20 K: ApproxComparable,
21 V: Clone,
22{
23 keys: Vec<K>,
24 values: Vec<V>,
25 tolerance: f32,
26}
27
28impl<K, V> UnboundedLinearCache<K, V>
29where
30 K: ApproxComparable,
31 V: Clone,
32{
33 pub fn new(tolerance: f32) -> Self {
34 UnboundedLinearCache {
35 keys: Vec::new(),
36 values: Vec::new(),
37 tolerance,
38 }
39 }
40
41 pub fn with_initial_capacity(tolerance: f32, capacity: usize) -> Self {
42 UnboundedLinearCache {
43 keys: Vec::with_capacity(capacity),
44 values: Vec::with_capacity(capacity),
45 tolerance,
46 }
47 }
48}
49
50impl<K, V> ApproximateCache<K, V> for UnboundedLinearCache<K, V>
51where
52 K: ApproxComparable,
53 V: Clone,
54{
55 fn find(&mut self, to_find: &K) -> Option<V> {
58 let potential_match = self
59 .keys
60 .iter()
61 .position(|key| to_find.roughly_matches(key, self.tolerance));
62
63 potential_match.map(|i| self.values[i].clone())
64 }
65
66 fn insert(&mut self, key: K, value: V) {
68 self.keys.push(key);
69 self.values.push(value);
70 }
71
72 fn len(&self) -> usize {
73 self.keys.len()
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use crate::caching::approximate_cache::COMPTIME_CACHE_SIZE;
80
81 use super::*;
82 use quickcheck::{QuickCheck, TestResult};
83
84 const TEST_TOLERANCE: f32 = 1e-8;
85 const TEST_MAX_SIZE: usize = COMPTIME_CACHE_SIZE;
86
87 #[test]
88 fn start_always_matches_exactly() {
89 fn qc_start_always_matches_exactly(
90 start_state: Vec<(f32, u8)>,
91 key: f32,
92 value: u8,
93 ) -> TestResult {
94 let mut ulc = UnboundedLinearCache::<f32, u8>::new(TEST_TOLERANCE);
95 if !key.is_finite() || start_state.len() > TEST_MAX_SIZE {
96 return TestResult::discard();
97 }
98
99 ulc.insert(key, value);
100 for &(k, v) in start_state.iter() {
101 ulc.insert(k, v);
102 }
103
104 assert!(ulc.len() == start_state.len() + 1);
105
106 if let Some(x) = ulc.find(&key) {
107 TestResult::from_bool(x == value)
108 } else {
109 TestResult::failed()
110 }
111 }
112
113 QuickCheck::new()
114 .tests(10_000)
115 .min_tests_passed(1_000)
116 .quickcheck(
117 qc_start_always_matches_exactly as fn(Vec<(f32, u8)>, f32, u8) -> TestResult,
118 );
119 }
120
121 #[test]
122 fn middle_always_matches() {
123 fn qc_middle_always_matches(
124 start_state: Vec<(f32, u8)>,
125 key: f32,
126 value: u8,
127 end_state: Vec<(f32, u8)>,
128 ) -> TestResult {
129 let mut ulc = UnboundedLinearCache::<f32, u8>::new(TEST_TOLERANCE);
130 if !key.is_finite() || start_state.len() > TEST_MAX_SIZE {
131 return TestResult::discard();
132 }
133
134 for &(k, v) in start_state.iter() {
135 ulc.insert(k, v);
136 }
137 ulc.insert(key, value);
138 for &(k, v) in end_state.iter() {
139 ulc.insert(k, v);
140 }
141
142 assert!(ulc.len() == start_state.len() + end_state.len() + 1);
143
144 TestResult::from_bool(ulc.find(&key).is_some())
146 }
147
148 QuickCheck::new()
149 .tests(10_000)
150 .min_tests_passed(1_000)
151 .quickcheck(
152 qc_middle_always_matches
153 as fn(Vec<(f32, u8)>, f32, u8, Vec<(f32, u8)>) -> TestResult,
154 );
155 }
156}