1use crate::NQuin;
2use std::collections::HashMap;
3
4pub struct QuinIndex {
12 quins: Vec<NQuin>,
13 by_subject: HashMap<u64, Vec<usize>>,
14 by_predicate: HashMap<u64, Vec<usize>>,
15 by_object: HashMap<u64, Vec<usize>>,
16 by_context: HashMap<u64, Vec<usize>>,
17}
18
19impl QuinIndex {
20 pub fn from_slice(quins: &[NQuin]) -> Self {
22 let mut idx = Self {
23 quins: quins.to_vec(),
24 by_subject: HashMap::new(),
25 by_predicate: HashMap::new(),
26 by_object: HashMap::new(),
27 by_context: HashMap::new(),
28 };
29 for (i, q) in idx.quins.iter().enumerate() {
30 idx.by_subject.entry(q.subject).or_default().push(i);
31 idx.by_predicate.entry(q.predicate).or_default().push(i);
32 idx.by_object.entry(q.object).or_default().push(i);
33 idx.by_context.entry(q.context).or_default().push(i);
34 }
35 idx
36 }
37
38 pub fn new() -> Self {
40 Self {
41 quins: Vec::new(),
42 by_subject: HashMap::new(),
43 by_predicate: HashMap::new(),
44 by_object: HashMap::new(),
45 by_context: HashMap::new(),
46 }
47 }
48
49 pub fn insert(&mut self, quin: NQuin) {
51 let i = self.quins.len();
52 self.by_subject.entry(quin.subject).or_default().push(i);
53 self.by_predicate.entry(quin.predicate).or_default().push(i);
54 self.by_object.entry(quin.object).or_default().push(i);
55 self.by_context.entry(quin.context).or_default().push(i);
56 self.quins.push(quin);
57 }
58
59 pub fn len(&self) -> usize {
60 self.quins.len()
61 }
62
63 pub fn is_empty(&self) -> bool {
64 self.quins.is_empty()
65 }
66
67 pub fn by_subject(&self, id: u64) -> Vec<NQuin> {
68 self.lookup(&self.by_subject, id)
69 }
70
71 pub fn by_predicate(&self, id: u64) -> Vec<NQuin> {
72 self.lookup(&self.by_predicate, id)
73 }
74
75 pub fn by_object(&self, id: u64) -> Vec<NQuin> {
76 self.lookup(&self.by_object, id)
77 }
78
79 pub fn by_context(&self, id: u64) -> Vec<NQuin> {
80 self.lookup(&self.by_context, id)
81 }
82
83 pub fn by_subject_and_predicate(&self, s: u64, p: u64) -> Vec<NQuin> {
85 let Some(rows) = self.by_subject.get(&s) else {
86 return vec![];
87 };
88 rows.iter()
89 .filter_map(|&i| {
90 let q = &self.quins[i];
91 if q.predicate == p {
92 Some(*q)
93 } else {
94 None
95 }
96 })
97 .collect()
98 }
99
100 pub fn iter_by_subject(&self, s: u64) -> impl Iterator<Item = NQuin> + '_ {
111 self.by_subject
112 .get(&s)
113 .map(Vec::as_slice)
114 .unwrap_or(&[])
115 .iter()
116 .map(move |&i| self.quins[i])
117 }
118
119 pub fn iter_by_subject_and_predicate(
121 &self,
122 s: u64,
123 p: u64,
124 ) -> impl Iterator<Item = NQuin> + '_ {
125 self.iter_by_subject(s).filter(move |q| q.predicate == p)
126 }
127
128 pub fn object_of(&self, s: u64, p: u64) -> Option<u64> {
132 self.iter_by_subject_and_predicate(s, p)
133 .next()
134 .map(|q| q.object)
135 }
136
137 pub fn rows_by_subject(&self, s: u64) -> &[usize] {
140 self.by_subject.get(&s).map(Vec::as_slice).unwrap_or(&[])
141 }
142
143 #[inline]
145 pub fn quin_at(&self, i: usize) -> NQuin {
146 self.quins[i]
147 }
148
149 fn lookup(&self, map: &HashMap<u64, Vec<usize>>, key: u64) -> Vec<NQuin> {
150 map.get(&key)
151 .map(|indices| indices.iter().map(|&i| self.quins[i]).collect())
152 .unwrap_or_default()
153 }
154}
155
156impl Default for QuinIndex {
157 fn default() -> Self {
158 Self::new()
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 fn make_quin(s: u64, p: u64, o: u64, c: u64) -> NQuin {
167 NQuin {
168 subject: s,
169 predicate: p,
170 object: o,
171 context: c,
172 metadata: 0,
173 parity: NQuin::calculate_parity(s, p, o, c, 0),
174 }
175 }
176
177 #[test]
178 fn index_lookup_by_subject() {
179 let quins = vec![
180 make_quin(1, 10, 100, 1000),
181 make_quin(2, 20, 200, 2000),
182 make_quin(1, 30, 300, 3000),
183 ];
184 let idx = QuinIndex::from_slice(&quins);
185 let hits = idx.by_subject(1);
186 assert_eq!(hits.len(), 2);
187 assert!(hits.iter().all(|q| q.subject == 1));
188 }
189
190 #[test]
191 fn index_lookup_by_context() {
192 let quins = vec![
193 make_quin(1, 10, 100, 42),
194 make_quin(2, 20, 200, 42),
195 make_quin(3, 30, 300, 99),
196 ];
197 let idx = QuinIndex::from_slice(&quins);
198 assert_eq!(idx.by_context(42).len(), 2);
199 assert_eq!(idx.by_context(99).len(), 1);
200 assert_eq!(idx.by_context(0).len(), 0);
201 }
202
203 #[test]
204 fn index_incremental_insert() {
205 let mut idx = QuinIndex::new();
206 idx.insert(make_quin(5, 6, 7, 8));
207 idx.insert(make_quin(5, 9, 10, 11));
208 assert_eq!(idx.len(), 2);
209 assert_eq!(idx.by_subject(5).len(), 2);
210 }
211
212 #[test]
213 fn index_subject_and_predicate() {
214 let quins = vec![
215 make_quin(1, 10, 100, 1000),
216 make_quin(1, 20, 200, 2000),
217 make_quin(2, 10, 300, 3000),
218 ];
219 let idx = QuinIndex::from_slice(&quins);
220 let hits = idx.by_subject_and_predicate(1, 10);
221 assert_eq!(hits.len(), 1);
222 assert_eq!(hits[0].object, 100);
223 }
224
225 #[test]
226 fn incremental_insert_survives_reallocation_with_zero_alloc_accessors() {
227 let mut idx = QuinIndex::new();
231 idx.insert(make_quin(5000, 101, 12345, 0)); for i in 0..2000u64 {
234 idx.insert(make_quin(i, 200, i + 1, 0));
235 }
236 idx.insert(make_quin(6000, 101, 67890, 0)); assert_eq!(idx.len(), 2002);
238
239 assert_eq!(idx.object_of(5000, 101), Some(12345));
241 assert_eq!(idx.object_of(6000, 101), Some(67890));
242 assert_eq!(idx.iter_by_subject(5000).count(), 1);
243 assert_eq!(idx.iter_by_subject_and_predicate(6000, 101).count(), 1);
244 assert!(idx.object_of(5000, 999).is_none());
245 }
246}