1use std::ops::{BitAnd, BitOr, Sub};
15
16use crate::DocId;
17
18#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
23pub struct DocSet {
24 doc_ids: Vec<DocId>,
25}
26
27impl DocSet {
28 pub fn new() -> Self {
30 Self::default()
31 }
32
33 pub fn from_unsorted(mut doc_ids: Vec<DocId>) -> Self {
35 doc_ids.sort_unstable();
36 doc_ids.dedup();
37 Self { doc_ids }
38 }
39
40 pub(crate) fn from_sorted_unchecked(doc_ids: Vec<DocId>) -> Self {
41 debug_assert!(
42 doc_ids.windows(2).all(|window| window[0] < window[1]),
43 "DocSet::from_sorted_unchecked invariant violated"
44 );
45 Self { doc_ids }
46 }
47
48 pub fn union(&self, other: &Self) -> Self {
50 let mut result = Vec::with_capacity(self.len() + other.len());
51 let (mut left, mut right) = (0, 0);
52
53 while left < self.len() && right < other.len() {
54 match self.doc_ids[left].cmp(&other.doc_ids[right]) {
55 std::cmp::Ordering::Less => {
56 result.push(self.doc_ids[left]);
57 left += 1;
58 }
59 std::cmp::Ordering::Equal => {
60 result.push(self.doc_ids[left]);
61 left += 1;
62 right += 1;
63 }
64 std::cmp::Ordering::Greater => {
65 result.push(other.doc_ids[right]);
66 right += 1;
67 }
68 }
69 }
70
71 result.extend_from_slice(&self.doc_ids[left..]);
72 result.extend_from_slice(&other.doc_ids[right..]);
73 Self::from_sorted_unchecked(result)
74 }
75
76 pub fn intersect(&self, other: &Self) -> Self {
78 let mut result = Vec::with_capacity(self.len().min(other.len()));
79 let (mut left, mut right) = (0, 0);
80
81 while left < self.len() && right < other.len() {
82 match self.doc_ids[left].cmp(&other.doc_ids[right]) {
83 std::cmp::Ordering::Less => left += 1,
84 std::cmp::Ordering::Equal => {
85 result.push(self.doc_ids[left]);
86 left += 1;
87 right += 1;
88 }
89 std::cmp::Ordering::Greater => right += 1,
90 }
91 }
92
93 Self::from_sorted_unchecked(result)
94 }
95
96 pub fn difference(&self, other: &Self) -> Self {
98 let mut result = Vec::with_capacity(self.len());
99 let (mut left, mut right) = (0, 0);
100
101 while left < self.len() && right < other.len() {
102 match self.doc_ids[left].cmp(&other.doc_ids[right]) {
103 std::cmp::Ordering::Less => {
104 result.push(self.doc_ids[left]);
105 left += 1;
106 }
107 std::cmp::Ordering::Equal => {
108 left += 1;
109 right += 1;
110 }
111 std::cmp::Ordering::Greater => right += 1,
112 }
113 }
114
115 result.extend_from_slice(&self.doc_ids[left..]);
116 Self::from_sorted_unchecked(result)
117 }
118
119 pub fn complement(&self, universe: &Self) -> Self {
121 universe.difference(self)
122 }
123
124 pub fn contains(&self, doc_id: DocId) -> bool {
126 self.doc_ids.binary_search(&doc_id).is_ok()
127 }
128
129 pub fn as_slice(&self) -> &[DocId] {
131 &self.doc_ids
132 }
133
134 pub fn iter(&self) -> impl ExactSizeIterator<Item = DocId> + DoubleEndedIterator + '_ {
136 self.doc_ids.iter().copied()
137 }
138
139 pub fn len(&self) -> usize {
141 self.doc_ids.len()
142 }
143
144 pub fn is_empty(&self) -> bool {
146 self.doc_ids.is_empty()
147 }
148}
149
150impl FromIterator<DocId> for DocSet {
151 fn from_iter<I: IntoIterator<Item = DocId>>(iter: I) -> Self {
152 Self::from_unsorted(iter.into_iter().collect())
153 }
154}
155
156impl From<Vec<DocId>> for DocSet {
157 fn from(doc_ids: Vec<DocId>) -> Self {
158 Self::from_unsorted(doc_ids)
159 }
160}
161
162impl IntoIterator for DocSet {
163 type Item = DocId;
164 type IntoIter = std::vec::IntoIter<DocId>;
165
166 fn into_iter(self) -> Self::IntoIter {
167 self.doc_ids.into_iter()
168 }
169}
170
171impl<'a> IntoIterator for &'a DocSet {
172 type Item = DocId;
173 type IntoIter = std::iter::Copied<std::slice::Iter<'a, DocId>>;
174
175 fn into_iter(self) -> Self::IntoIter {
176 self.doc_ids.iter().copied()
177 }
178}
179
180impl BitOr for &DocSet {
181 type Output = DocSet;
182
183 fn bitor(self, rhs: Self) -> Self::Output {
184 self.union(rhs)
185 }
186}
187
188impl BitAnd for &DocSet {
189 type Output = DocSet;
190
191 fn bitand(self, rhs: Self) -> Self::Output {
192 self.intersect(rhs)
193 }
194}
195
196impl Sub for &DocSet {
197 type Output = DocSet;
198
199 fn sub(self, rhs: Self) -> Self::Output {
200 self.difference(rhs)
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::DocSet;
207
208 #[test]
209 fn construction_sorts_and_deduplicates() {
210 assert_eq!(
211 DocSet::from_unsorted(vec![3, 1, 3, 2]).as_slice(),
212 &[1, 2, 3]
213 );
214 }
215
216 #[test]
217 fn operations_preserve_set_semantics() {
218 let left = DocSet::from(vec![1, 3, 5]);
219 let right = DocSet::from(vec![2, 3, 4]);
220 let universe = DocSet::from(vec![1, 2, 3, 4, 5]);
221
222 assert_eq!((&left | &right).as_slice(), &[1, 2, 3, 4, 5]);
223 assert_eq!((&left & &right).as_slice(), &[3]);
224 assert_eq!((&left - &right).as_slice(), &[1, 5]);
225 assert_eq!(left.complement(&universe).as_slice(), &[2, 4]);
226 }
227}