From 2c7d51f5d1dee4c9742db7b5d8a9277746498a34 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 10 Sep 2026 13:56:31 -0700 Subject: [PATCH] wokr on domains --- rust/pspp/src/spv/read/graph/var_set.rs | 112 +++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/rust/pspp/src/spv/read/graph/var_set.rs b/rust/pspp/src/spv/read/graph/var_set.rs index 32f93339fb..31d35141f8 100644 --- a/rust/pspp/src/spv/read/graph/var_set.rs +++ b/rust/pspp/src/spv/read/graph/var_set.rs @@ -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), + Continuous(Option>), + 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 { + 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 { + match self { + DomainComponent::Categorical(items) => items, + DomainComponent::Continuous(_) => Vec::new(), + DomainComponent::Unity => vec!["0.5".into()], + } + } + + fn range(&self) -> Option> { + 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 { + 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 { + fn union( + a: &Option>, + b: &Option>, + ) -> Option> { + 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::>() + .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 { + todo!() + } +} + struct VarSet { data: Vec, //domain: Domain, -- 2.47.3