}
}
-#[derive(Debug)]
+#[derive(Clone, Debug)]
pub enum Domain {
Empty,
Component(DomainComponent),
Self::Component(DomainComponent::for_variable(series))
}
- pub fn cross(a: Domain, b: Domain) -> Self {
- Self::Cross(Box::new([a, b]))
+ pub fn cross(self, other: Domain) -> Self {
+ Self::Cross(Box::new([self, other]))
}
- pub fn nest(a: Domain, b: Domain, data: Vec<Series>) -> Self {
+ pub fn nest(self, other: Domain, data: Vec<Series>) -> Self {
Self::Nest {
- domains: Box::new([a, b]),
+ domains: Box::new([self, other]),
data,
}
}
+ pub fn is_empty(&self) -> bool {
+ self.len() == 0
+ }
/// The number of domain components in this domain.
pub fn len(&self) -> usize {
match self {
pub struct VarSet {
data: Vec<Series>,
domain: Domain,
+ case_mapping: CaseMapping,
}
impl VarSet {
Self {
data: vec![Series::new_empty()],
domain: Domain::new_unity(),
+ case_mapping: CaseMapping::new_empty(),
}
}
Self {
data: vec![series.clone()],
domain: Domain::for_variable(series),
+ case_mapping: CaseMapping::new_indexed(series.values.len()),
}
}
}
pub fn cross(self, other: Self) -> Self {
- if self.domain.len() == 0 {
+ if self.domain.is_empty() {
other
- } else if other.domain.len() == 0 {
+ } else if other.domain.is_empty() {
self
- } else {
+ } else if self.len() == other.len()
+ && self.case_mapping == other.case_mapping
+ && self.case_mapping.is_all_unique()
+ {
Self {
data: chain(self.data, other.data).collect(),
- domain: Domain::cross(self.domain, other.domain),
+ domain: self.domain.cross(other.domain),
+ case_mapping: self.case_mapping,
+ }
+ } else {
+ let [a, b] = self.case_mapping.cross(&other.case_mapping);
+ Self {
+ data: chain(
+ self.data
+ .into_iter()
+ .map(|series| series.subset(a.iter().copied())),
+ other
+ .data
+ .into_iter()
+ .map(|series| series.subset(b.iter().copied())),
+ )
+ .collect(),
+ case_mapping: self.case_mapping.subset(a),
+ domain: self.domain.cross(other.domain),
}
}
}
pub fn nest(self, other: Self) -> Self {
- if self.domain.len() == 0 {
+ if self.domain.is_empty() {
other
- } else if other.domain.len() == 0 {
+ } else if other.domain.is_empty() {
self
} else {
- let data = chain(self.data, other.data).collect_vec();
+ let self_domain = self.domain.clone();
+ let other_domain = other.domain.clone();
+ let crossed = self.cross(other);
Self {
- domain: Domain::nest(self.domain, other.domain, data.clone()),
- data,
+ data: crossed.data.clone(),
+ domain: self_domain.nest(other_domain, crossed.data),
+ case_mapping: crossed.case_mapping,
}
}
}
is_categorical: bool,
}
+// `PartialEq` might need to be implemented manually if we introduce a field for
+// caching the reverse mapping.
+#[derive(Debug, PartialEq, Eq)]
+struct CaseMapping {
+ map: Vec<usize>,
+}
+
+impl CaseMapping {
+ pub fn new_empty() -> Self {
+ Self { map: Vec::new() }
+ }
+ pub fn new_indexed(n: usize) -> Self {
+ Self {
+ map: (0..n).collect(),
+ }
+ }
+
+ pub fn new_map(map: Vec<usize>) -> Self {
+ Self { map }
+ }
+
+ pub fn len(&self) -> usize {
+ self.map.len()
+ }
+ pub fn all_cases(&self) -> &[usize] {
+ self.map.as_slice()
+ }
+ pub fn get(&self, index: usize) -> usize {
+ self.map[index]
+ }
+
+ /// True if each input index maps to a different case.
+ pub fn is_all_unique(&self) -> bool {
+ self.map.iter().all_unique()
+ }
+
+ pub fn cross(&self, other: &Self) -> [Vec<usize>; 2] {
+ let mut other_cases = Vec::new();
+ for (i, &j) in other.all_cases().into_iter().enumerate() {
+ if j >= other_cases.len() {
+ other_cases.resize(j + 1, Vec::new());
+ }
+ other_cases[j].push(i);
+ }
+ let mut result = [Vec::new(), Vec::new()];
+ for (i, &j) in self.all_cases().iter().enumerate() {
+ if let Some(cases) = other_cases.get(j) {
+ for &k in cases {
+ result[0].push(i);
+ result[1].push(k);
+ }
+ }
+ }
+ result
+ }
+
+ pub fn subset(mut self, indexes: impl IntoIterator<Item = usize>) -> Self {
+ self.map = indexes.into_iter().map(|index| self.map[index]).collect();
+ self
+ }
+}
+
#[cfg(test)]
mod tests {
use std::str::FromStr;