decodedrecord works
[pspp] / rust / src / dictionary.rs
1 use std::{
2     collections::{HashMap, HashSet},
3     fmt::Debug,
4     ops::{Bound, RangeBounds}, cmp::Ordering,
5 };
6
7 use encoding_rs::Encoding;
8 use indexmap::IndexSet;
9 use num::integer::div_ceil;
10 use ordered_float::OrderedFloat;
11
12 use crate::{
13     format::Spec,
14     identifier::{ByIdentifier, HasIdentifier, Identifier},
15     raw::{Alignment, CategoryLabels, Measure, MissingValues, VarType, self, RawStr, Decoder},
16 };
17
18 pub type DictIndex = usize;
19
20 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
21 pub enum VarWidth {
22     Numeric,
23     String(u16),
24 }
25
26 impl PartialOrd for VarWidth {
27     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
28         match (self, other) {
29             (VarWidth::Numeric, VarWidth::Numeric) => Some(Ordering::Equal),
30             (VarWidth::String(a), VarWidth::String(b)) => Some(a.cmp(b)),
31             _ => None,
32         }
33     }
34 }
35
36 impl VarWidth {
37     pub const MAX_STRING: u16 = 32767;
38
39     fn n_dict_indexes(self) -> usize {
40         match self {
41             VarWidth::Numeric => 1,
42             VarWidth::String(w) => div_ceil(w as usize, 8),
43         }
44     }
45
46     fn width_predicate(
47         a: Option<VarWidth>,
48         b: Option<VarWidth>,
49         f: impl Fn(u16, u16) -> u16,
50     ) -> Option<VarWidth> {
51         match (a, b) {
52             (Some(VarWidth::Numeric), Some(VarWidth::Numeric)) => Some(VarWidth::Numeric),
53             (Some(VarWidth::String(a)), Some(VarWidth::String(b))) => {
54                 Some(VarWidth::String(f(a, b)))
55             }
56             _ => None,
57         }
58     }
59
60     /// Returns the wider of `self` and `other`:
61     /// - Numerical variable widths are equally wide.
62     /// - Longer strings are wider than shorter strings.
63     /// - Numerical and string types are incomparable, so result in `None`.
64     /// - Any `None` in the input yields `None` in the output.
65     pub fn wider(a: Option<VarWidth>, b: Option<VarWidth>) -> Option<VarWidth> {
66         Self::width_predicate(a, b, |a, b| a.max(b))
67     }
68
69     /// Returns the narrower of `self` and `other` (see [`Self::wider`]).
70     pub fn narrower(a: Option<VarWidth>, b: Option<VarWidth>) -> Option<VarWidth> {
71         Self::width_predicate(a, b, |a, b| a.min(b))
72     }
73
74     pub fn default_display_width(&self) -> u32 {
75         match self {
76             VarWidth::Numeric => 8,
77             VarWidth::String(width) => *width.min(&32) as u32,
78         }
79     }
80 }
81
82 impl From<VarWidth> for VarType {
83     fn from(source: VarWidth) -> Self {
84         match source {
85             VarWidth::Numeric => VarType::Numeric,
86             VarWidth::String(_) => VarType::String,
87         }
88     }
89 }
90
91 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
92 pub enum Value {
93     Number(Option<OrderedFloat<f64>>),
94     String(String),
95 }
96
97 impl Value {
98     pub fn decode(raw: &raw::Value<RawStr<8>>, decoder: &Decoder) -> Self {
99         match raw {
100             raw::Value::Number(x) => Value::Number(x.map(|x| x.into())),
101             raw::Value::String(s) => Value::String(decoder.decode_exact_length(&s.0).into()),
102         }
103     }
104 }
105
106 #[derive(Clone, Debug)]
107 pub struct Dictionary {
108     pub variables: IndexSet<ByIdentifier<Variable>>,
109     pub split_file: Vec<DictIndex>,
110     pub weight: Option<DictIndex>,
111     pub filter: Option<DictIndex>,
112     pub case_limit: Option<u64>,
113     pub file_label: Option<String>,
114     pub documents: Vec<String>,
115     pub vectors: HashSet<ByIdentifier<Vector>>,
116     pub attributes: HashMap<Identifier, Vec<String>>,
117     pub mrsets: HashSet<ByIdentifier<MultipleResponseSet>>,
118     pub variable_sets: HashSet<ByIdentifier<VariableSet>>,
119     pub encoding: &'static Encoding,
120 }
121
122 impl Dictionary {
123     pub fn new(encoding: &'static Encoding) -> Self {
124         Self {
125             variables: IndexSet::new(),
126             split_file: Vec::new(),
127             weight: None,
128             filter: None,
129             case_limit: None,
130             file_label: None,
131             documents: Vec::new(),
132             vectors: HashSet::new(),
133             attributes: HashMap::new(),
134             mrsets: HashSet::new(),
135             variable_sets: HashSet::new(),
136             encoding,
137         }
138     }
139
140     pub fn add_var(&mut self, variable: Variable) -> Result<(), ()> {
141         if self.variables.insert(ByIdentifier::new(variable)) {
142             Ok(())
143         } else {
144             Err(())
145         }
146     }
147
148     pub fn reorder_var(&mut self, from_index: DictIndex, to_index: DictIndex) {
149         if from_index != to_index {
150             self.variables.move_index(from_index, to_index);
151             self.update_dict_indexes(&|index| {
152                 if index == from_index {
153                     Some(to_index)
154                 } else if from_index < to_index {
155                     if index > from_index && index <= to_index {
156                         Some(index - 1)
157                     } else {
158                         Some(index)
159                     }
160                 } else {
161                     if index >= to_index && index < from_index {
162                         Some(index + 1)
163                     } else {
164                         Some(index)
165                     }
166                 }
167             })
168         }
169     }
170
171     pub fn retain_vars<F>(&mut self, keep: F)
172     where
173         F: Fn(&Variable) -> bool,
174     {
175         let mut deleted = Vec::new();
176         let mut index = 0;
177         self.variables.retain(|var_by_id| {
178             let keep = keep(&var_by_id.0);
179             if !keep {
180                 deleted.push(index);
181             }
182             index += 1;
183             keep
184         });
185         if !deleted.is_empty() {
186             self.update_dict_indexes(&|index| match deleted.binary_search(&index) {
187                 Ok(_) => None,
188                 Err(position) => Some(position),
189             })
190         }
191     }
192
193     pub fn delete_vars<R>(&mut self, range: R)
194     where
195         R: RangeBounds<DictIndex>,
196     {
197         let start = match range.start_bound() {
198             Bound::Included(&start) => start,
199             Bound::Excluded(&start) => start + 1,
200             Bound::Unbounded => 0,
201         };
202         let end = match range.end_bound() {
203             Bound::Included(&end) => end + 1,
204             Bound::Excluded(&end) => end,
205             Bound::Unbounded => self.variables.len(),
206         };
207         if end > start {
208             self.variables.drain(start..end);
209             self.update_dict_indexes(&|index| {
210                 if index < start {
211                     Some(index)
212                 } else if index < end {
213                     None
214                 } else {
215                     Some(index - end - start)
216                 }
217             })
218         }
219     }
220
221     fn update_dict_indexes<F>(&mut self, f: &F)
222     where
223         F: Fn(DictIndex) -> Option<DictIndex>,
224     {
225         update_dict_index_vec(&mut self.split_file, f);
226         self.weight = self.weight.map(|index| f(index)).flatten();
227         self.filter = self.filter.map(|index| f(index)).flatten();
228         self.vectors = self
229             .vectors
230             .drain()
231             .filter_map(|vector_by_id| {
232                 vector_by_id
233                     .0
234                     .with_updated_dict_indexes(f)
235                     .map(|vector| ByIdentifier::new(vector))
236             })
237             .collect();
238         self.mrsets = self
239             .mrsets
240             .drain()
241             .filter_map(|mrset_by_id| {
242                 mrset_by_id
243                     .0
244                     .with_updated_dict_indexes(f)
245                     .map(|mrset| ByIdentifier::new(mrset))
246             })
247             .collect();
248         self.variable_sets = self
249             .variable_sets
250             .drain()
251             .filter_map(|var_set_by_id| {
252                 var_set_by_id
253                     .0
254                     .with_updated_dict_indexes(f)
255                     .map(|var_set| ByIdentifier::new(var_set))
256             })
257             .collect();
258     }
259 }
260
261 fn update_dict_index_vec<F>(dict_indexes: &mut Vec<DictIndex>, f: F)
262 where
263     F: Fn(DictIndex) -> Option<DictIndex>,
264 {
265     dict_indexes.retain_mut(|index| {
266         if let Some(new) = f(*index) {
267             *index = new;
268             true
269         } else {
270             false
271         }
272     });
273 }
274
275 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
276 pub enum Role {
277     Input,
278     Target,
279     Both,
280     None,
281     Partition,
282     Split,
283 }
284
285 impl Default for Role {
286     fn default() -> Self {
287         Self::Input
288     }
289 }
290
291 pub enum DictClass {
292     Ordinary,
293     System,
294     Scratch,
295 }
296
297 impl DictClass {
298     pub fn from_identifier(id: &Identifier) -> Self {
299         if id.0.starts_with('$') {
300             Self::System
301         } else if id.0.starts_with('#') {
302             Self::Scratch
303         } else {
304             Self::Ordinary
305         }
306     }
307
308     pub fn must_leave(self) -> bool {
309         match self {
310             DictClass::Ordinary => false,
311             DictClass::System => false,
312             DictClass::Scratch => true,
313         }
314     }
315 }
316
317 #[derive(Clone, Debug)]
318 pub struct Variable {
319     pub name: Identifier,
320     pub width: VarWidth,
321     pub missing_values: MissingValues,
322     pub print_format: Spec,
323     pub write_format: Spec,
324     pub value_labels: HashMap<Value, String>,
325     pub label: Option<String>,
326     pub measure: Option<Measure>,
327     pub role: Role,
328     pub display_width: u32,
329     pub alignment: Alignment,
330     pub leave: bool,
331     pub short_names: Vec<Identifier>,
332     pub attributes: HashSet<ByIdentifier<Attribute>>,
333 }
334
335 impl Variable {
336     pub fn new(name: Identifier, width: VarWidth) -> Self {
337         let var_type = VarType::from_width(width);
338         let leave = DictClass::from_identifier(&name).must_leave();
339         Self {
340             name,
341             width,
342             missing_values: MissingValues::default(),
343             print_format: Spec::default_for_width(width),
344             write_format: Spec::default_for_width(width),
345             value_labels: HashMap::new(),
346             label: None,
347             measure: Measure::default_for_type(var_type),
348             role: Role::default(),
349             display_width: width.default_display_width(),
350             alignment: Alignment::default_for_type(var_type),
351             leave,
352             short_names: Vec::new(),
353             attributes: HashSet::new()
354         }
355     }
356 }
357
358 impl HasIdentifier for Variable {
359     fn identifier(&self) -> &Identifier {
360         &self.name
361     }
362 }
363
364 #[derive(Clone, Debug)]
365 pub struct Vector {
366     pub name: Identifier,
367     pub variables: Vec<DictIndex>,
368 }
369
370 impl Vector {
371     fn with_updated_dict_indexes(
372         mut self,
373         f: impl Fn(DictIndex) -> Option<DictIndex>,
374     ) -> Option<Self> {
375         update_dict_index_vec(&mut self.variables, f);
376         (!self.variables.is_empty()).then_some(self)
377     }
378 }
379
380 impl HasIdentifier for Vector {
381     fn identifier(&self) -> &Identifier {
382         &self.name
383     }
384 }
385
386 #[derive(Clone, Debug)]
387 pub struct Attribute {
388     pub name: Identifier,
389     pub values: Vec<String>,
390 }
391
392 impl HasIdentifier for Attribute {
393     fn identifier(&self) -> &Identifier {
394         &self.name
395     }
396 }
397
398 #[derive(Clone, Debug)]
399 pub struct MultipleResponseSet {
400     pub name: Identifier,
401     pub label: String,
402     pub mr_type: MultipleResponseType,
403     pub variables: Vec<DictIndex>,
404 }
405
406 impl MultipleResponseSet {
407     fn with_updated_dict_indexes(
408         mut self,
409         f: impl Fn(DictIndex) -> Option<DictIndex>,
410     ) -> Option<Self> {
411         update_dict_index_vec(&mut self.variables, f);
412         (self.variables.len() > 1).then_some(self)
413     }
414 }
415
416 impl HasIdentifier for MultipleResponseSet {
417     fn identifier(&self) -> &Identifier {
418         &self.name
419     }
420 }
421
422 #[derive(Clone, Debug)]
423 pub enum MultipleResponseType {
424     MultipleDichotomy {
425         value: Value,
426         labels: CategoryLabels,
427     },
428     MultipleCategory,
429 }
430
431 #[derive(Clone, Debug)]
432 pub struct VariableSet {
433     pub name: Identifier,
434     pub variables: Vec<DictIndex>,
435 }
436
437 impl VariableSet {
438     fn with_updated_dict_indexes(
439         mut self,
440         f: impl Fn(DictIndex) -> Option<DictIndex>,
441     ) -> Option<Self> {
442         update_dict_index_vec(&mut self.variables, f);
443         (!self.variables.is_empty()).then_some(self)
444     }
445 }
446
447 impl HasIdentifier for VariableSet {
448     fn identifier(&self) -> &Identifier {
449         &self.name
450     }
451 }
452
453 #[cfg(test)]
454 mod test {
455     use std::collections::HashSet;
456
457     use crate::identifier::Identifier;
458
459     use super::{ByIdentifier, HasIdentifier};
460
461     #[derive(PartialEq, Eq, Debug, Clone)]
462     struct Variable {
463         name: Identifier,
464         value: i32,
465     }
466
467     impl HasIdentifier for Variable {
468         fn identifier(&self) -> &Identifier {
469             &self.name
470         }
471     }
472
473     #[test]
474     fn test() {
475         // Variables should not be the same if their values differ.
476         let abcd = Identifier::new_utf8("abcd").unwrap();
477         let abcd1 = Variable {
478             name: abcd.clone(),
479             value: 1,
480         };
481         let abcd2 = Variable {
482             name: abcd,
483             value: 2,
484         };
485         assert_ne!(abcd1, abcd2);
486
487         // But `ByName` should treat them the same.
488         let abcd1_by_name = ByIdentifier::new(abcd1);
489         let abcd2_by_name = ByIdentifier::new(abcd2);
490         assert_eq!(abcd1_by_name, abcd2_by_name);
491
492         // And a `HashSet` of `ByName` should also treat them the same.
493         let mut vars: HashSet<ByIdentifier<Variable>> = HashSet::new();
494         assert!(vars.insert(ByIdentifier::new(abcd1_by_name.0.clone())));
495         assert!(!vars.insert(ByIdentifier::new(abcd2_by_name.0.clone())));
496         assert_eq!(
497             vars.get(&Identifier::new_utf8("abcd").unwrap())
498                 .unwrap()
499                 .0
500                 .value,
501             1
502         );
503     }
504 }