]> pintos-os.org Git - pspp/commitdiff
wokr on domains
authorBen Pfaff <blp@cs.stanford.edu>
Thu, 10 Sep 2026 20:56:31 +0000 (13:56 -0700)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 10 Sep 2026 20:56:31 +0000 (13:56 -0700)
rust/pspp/src/spv/read/graph/var_set.rs

index 32f93339fb801bcdb21508f97a20384be49029c7..31d35141f8c8c4dcb3aa83d3158eefaf458f7394 100644 (file)
@@ -1,4 +1,13 @@
-use std::{iter::Peekable, str::FromStr};
+use std::{
+    borrow::Cow,
+    collections::BTreeSet,
+    iter::{Peekable, chain},
+    ops::{Not as _, RangeInclusive},
+    str::FromStr,
+};
+
+use either::Either;
+use itertools::Itertools as _;
 
 use crate::spv::read::graph::Series;
 
@@ -193,6 +202,107 @@ impl FromStr for Expression {
     }
 }
 
+#[derive(Clone, Debug)]
+enum DomainComponent {
+    Categorical(Vec<String>),
+    Continuous(Option<RangeInclusive<f64>>),
+    Unity,
+}
+
+/// Cannot blend continuous and categorical domains.
+#[derive(Copy, Clone, Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
+struct BlendError;
+
+impl DomainComponent {
+    fn categories(&self) -> impl Iterator<Item = &str> {
+        match self {
+            DomainComponent::Categorical(items) => Either::Left(items.iter().map(|s| s.as_str())),
+            DomainComponent::Unity => Either::Right(["0.5"].into_iter()),
+            DomainComponent::Continuous(_) => {
+                // Avoid the need for a third return type by using the same type
+                // as `Unity` but dropping the first (only) element.
+                Either::Right([""].into_iter().dropping(1))
+            }
+        }
+    }
+
+    fn into_categories(self) -> Vec<String> {
+        match self {
+            DomainComponent::Categorical(items) => items,
+            DomainComponent::Continuous(_) => Vec::new(),
+            DomainComponent::Unity => vec!["0.5".into()],
+        }
+    }
+
+    fn range(&self) -> Option<RangeInclusive<f64>> {
+        match self {
+            Self::Categorical(items) => items.is_empty().not().then(|| 1.0..=items.len() as f64),
+            Self::Continuous(range) => range.clone(),
+            Self::Unity => Some(0.5..=0.5),
+        }
+    }
+
+    fn split(&self, s: &str) -> Option<Self> {
+        if let Self::Categorical(items) = self {
+            Some(Self::Categorical(
+                match items.into_iter().find(|element| s == *element) {
+                    Some(_) => vec![s.into()],
+                    None => Vec::new(),
+                },
+            ))
+        } else {
+            None
+        }
+    }
+
+    fn blend(&self, other: &Self) -> Result<Self, BlendError> {
+        fn union(
+            a: &Option<RangeInclusive<f64>>,
+            b: &Option<RangeInclusive<f64>>,
+        ) -> Option<RangeInclusive<f64>> {
+            match (a.clone(), b.clone()) {
+                (Some(a), Some(b)) => {
+                    let (a0, a1) = a.into_inner();
+                    let (b0, b1) = b.into_inner();
+                    Some(a0.min(b0)..=a1.max(b1))
+                }
+                (Some(range), None) | (None, Some(range)) => Some(range),
+                (None, None) => None,
+            }
+        }
+
+        match (self, other) {
+            (Self::Categorical(a), Self::Categorical(b)) => Ok(Self::Categorical(
+                chain(a, b)
+                    .collect::<BTreeSet<_>>()
+                    .into_iter()
+                    .cloned()
+                    .collect(),
+            )),
+            (Self::Continuous(a), Self::Continuous(b)) => Ok(Self::Continuous(union(a, b))),
+            (c @ Self::Categorical(_), Self::Unity) | (Self::Unity, c @ Self::Categorical(_)) => {
+                Ok(c.clone())
+            }
+            (Self::Unity, Self::Unity) => Ok(Self::Unity),
+            (Self::Unity, Self::Continuous(range)) | (Self::Continuous(range), Self::Unity) => {
+                Ok(Self::Continuous(union(range, &Some(0.5..=0.5))))
+            }
+            (Self::Categorical(_), Self::Continuous(_))
+            | (Self::Continuous(_), Self::Categorical(_)) => Err(BlendError),
+        }
+    }
+}
+
+struct Categories<'a>(&'a DomainComponent);
+
+impl<'a> Iterator for Categories<'a> {
+    type Item = Cow<'a, str>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        todo!()
+    }
+}
+
 struct VarSet {
     data: Vec<Series>,
     //domain: Domain,