1use std::collections::HashMap;
2use std::marker::PhantomData;
3use std::{hash::Hash, ops::RangeInclusive};
4
5use fixed_map::key::Key as FixedKey;
6use slotmap::{new_key_type, SlotMap};
7
8use crate::linspace::Linspace;
9use crate::math::interp;
10use crate::terms::Terms;
11
12new_key_type! {
13 pub struct VariableKey;
15}
16
17pub struct Variable<I>(pub(crate) VariableKey, PhantomData<I>);
18
19impl<I> Clone for Variable<I> {
20 fn clone(&self) -> Self {
21 Variable(self.0, PhantomData)
22 }
23}
24
25impl<I> Copy for Variable<I> {}
26
27#[derive(Default)]
28pub struct Variables<T>(pub(crate) SlotMap<VariableKey, VariableContraints<T>>);
29
30impl<T: Eq + Hash> Variables<T> {
31 pub fn new() -> Self {
32 Self(SlotMap::with_key())
33 }
34
35 pub fn add<I: Into<T> + FixedKey + 'static>(
36 &mut self,
37 universe_range: RangeInclusive<f64>,
38 terms: Terms<I>,
39 ) -> Variable<I> {
40 let start_term_coords = terms.0.iter().map(|(k, v)| (k.into(), *v));
41 let key = self.0.insert(VariableContraints::new(
42 universe_range,
43 start_term_coords,
44 terms.0.len(),
45 ));
46
47 Variable(key, PhantomData)
48 }
49}
50
51pub(crate) struct VariableContraints<T> {
52 pub(crate) universe: Vec<f64>,
53 pub(crate) min_u: f64,
54 pub(crate) max_u: f64,
55 pub(crate) terms: HashMap<T, Vec<f64>>,
56}
57
58impl<T: Eq + Hash> VariableContraints<T> {
59 fn new<'t>(
60 universe_range: RangeInclusive<f64>,
61 start_term_coords: impl IntoIterator<Item = (T, &'t [(f64, f64)])>,
62 n_terms: usize,
63 ) -> Self {
64 let step = 0.1;
66 let min_u = *universe_range.start();
67 let max_u = *universe_range.end();
68 let num = ((max_u - min_u) / step).floor() as usize + 1;
71 let universe = Linspace::new(min_u, max_u, num).collect();
72 let mut this = Self {
73 universe,
74 min_u,
75 max_u,
76 terms: HashMap::with_capacity(n_terms),
77 };
78
79 if false {
81 unimplemented!();
82 } else {
84 for (term, membership) in start_term_coords {
85 let xp = membership.iter().map(|(xp, _)| *xp);
86 this.add_points_to_universe(xp);
87 this.terms
88 .insert(term, interp(this.universe.iter().copied(), membership.iter().copied()));
89 }
90 }
91
92 this
93 }
94
95 pub(crate) fn add_points_to_universe(&mut self, points: impl IntoIterator<Item = f64>) {
97 let iter = points.into_iter().map(|p| p.clamp(self.min_u, self.max_u));
99 let mut universe: Vec<_> = self.universe.iter().copied().chain(iter).collect();
100
101 universe.sort_unstable_by(|a, b| a.partial_cmp(b).expect("not to find unsortable floats"));
102 universe.dedup();
103
104 for term_values in self.terms.values_mut() {
106 let new_values = interp(
107 universe.iter().copied(),
108 self.universe.iter().copied().zip(term_values.iter().copied()),
109 );
110
111 *term_values = new_values;
112 }
113
114 self.universe = universe;
116 }
117
118 #[allow(clippy::let_and_return)]
119 pub(crate) fn get_modified_membership(&self, term: &T, _modifiers: &[()]) -> &[f64] {
120 let membership = &self.terms[term];
121
122 membership
125 }
126}