adv_random/rules/
number_space.rs

1
2use crate::random::CurrentData;
3use crate::random_trait::get_random_trait;
4use crate::rules::{MapAnyValue, RuleTrait, IsWithinErrorType};
5use crate::settings::Settings;
6use std::any::Any;
7use std::collections::HashMap;
8use std::fmt;
9use std::fmt::{Debug, Display, Formatter, Result};
10
11use super::{ExcludeRuleTrait, is_excluded_helper};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14pub enum NumberSpaceType {
15    Lt(usize),
16    Lte(usize),
17    Eq(usize),
18    Gte(usize),
19    Gt(usize),
20    Between(usize, usize)
21}
22
23impl NumberSpaceType {
24    pub fn is_match(&self, num_space: usize) -> bool {
25        return match *self {//TODO check dereference 
26            NumberSpaceType::Lt(v) => num_space < v,
27            NumberSpaceType::Lte(v) => num_space <= v,
28            NumberSpaceType::Eq(v) => num_space == v,
29            NumberSpaceType::Gte(v) => num_space >= v,               
30            NumberSpaceType::Gt(v) => num_space > v, 
31            NumberSpaceType::Between(lower_bound, upper_bound) => num_space >= lower_bound && num_space <= upper_bound 
32        };
33    }
34
35    pub fn has(&self, num_spaces: &[usize]) -> usize {
36        let mut matches: usize = 0;
37        for num_space in num_spaces {
38            if self.is_match(*num_space) {
39                matches += 1;
40            }
41        }
42        return matches;
43    }
44
45    pub fn get_number(&self, number_space_base: usize, max: usize) -> usize {
46        return match *self {//TODO check dereference 7;3 between(1-3)
47            NumberSpaceType::Lt(v) => get_random_trait().get_number(number_space_base + 1, number_space_base + v - 1),     //7+1=8;7+3-1=9 -- 8,9
48            NumberSpaceType::Lte(v) => get_random_trait().get_number(number_space_base + 1, number_space_base + v),        //7+1=8;7+3=10  -- 8,9,10
49            NumberSpaceType::Eq(v) => number_space_base + v,                                                                        //7+3=10        -- 10
50            NumberSpaceType::Gt(v) => {
51                if (number_space_base + v + 1) <= max { 
52                    get_random_trait().get_number(number_space_base + v + 1, max)
53                } else {
54                    0
55                }
56            },                                                                                                                                                                                                                                                 
57            NumberSpaceType::Gte(v) => {
58                if (number_space_base + v) <= max { 
59                    get_random_trait().get_number(number_space_base + v, max)
60                } else {
61                    0
62                }
63            },                               
64            NumberSpaceType::Between(lower_bound, upper_bound) => get_random_trait().get_number(number_space_base + lower_bound, number_space_base + upper_bound), //7+1=8;7+3=10  -- 8,9,10
65        };
66    }
67}
68
69impl Display for NumberSpaceType {
70    fn fmt(&self, f: &mut Formatter) -> Result {
71        match self {
72            NumberSpaceType::Gte(v) => write!(f, "Gte:{}", v),
73            NumberSpaceType::Eq(v) => write!(f, "Eq:{}", v),
74            NumberSpaceType::Lt(v) => write!(f, "Lt:{}", v),
75            NumberSpaceType::Lte(v) => write!(f, "Lte:{}", v),
76            NumberSpaceType::Gt(v) => write!(f, "Gt:{}", v),
77            NumberSpaceType::Between(lower_bound, upper_bound) => write!(f, "Gte:{}-Lte:{}", lower_bound, upper_bound),
78        }
79    }
80}
81
82#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
83pub enum ProcessMissing {
84    AllPerRequest,
85    OnePerRequest
86}
87
88#[derive(Clone, Debug)]
89pub struct NumberSpaceItem {
90    number_space_type: NumberSpaceType,
91    needs: usize,
92    has: usize,
93    missing: usize,
94    process_missing: ProcessMissing
95}
96
97impl NumberSpaceItem {
98    pub fn new(number_space_type: &NumberSpaceType, needs: usize) -> NumberSpaceItem {
99        return NumberSpaceItem::new_full(number_space_type, needs, 0, 0);
100    }
101
102    pub fn new_full(number_space_type: &NumberSpaceType, needs: usize, has: usize, missing: usize) -> NumberSpaceItem {
103        return NumberSpaceItem::new_with_process_missing(number_space_type, needs, has, missing, ProcessMissing::OnePerRequest);
104    }
105
106    pub fn new_with_process_missing(number_space_type: &NumberSpaceType, needs: usize, has: usize, missing: usize, process_missing: ProcessMissing) -> NumberSpaceItem {
107        return NumberSpaceItem {
108            number_space_type: number_space_type.clone(),
109            needs,
110            has,
111            missing,
112            process_missing
113        };
114    }
115
116    pub fn get_num_spaces(list: &[usize], is_sorted: bool) -> Vec<usize> {    
117        if list.len() > 1 {
118            let mut num_spaces:Vec<usize> = Vec::new();
119            let sorted_list: Vec<usize> = if is_sorted { 
120                list.to_vec()
121            } else { 
122                let mut v = list.to_vec(); 
123                v.sort(); 
124                v
125            };
126            for x in 1..sorted_list.len() {
127                num_spaces.push(sorted_list[x] - sorted_list[x - 1]);
128            }
129            return num_spaces;
130        }
131        return Vec::new();
132    }
133
134    pub fn number_space_type(&self) -> &NumberSpaceType {
135        return &self.number_space_type;
136    }
137
138    pub fn needs(&self) -> usize {
139        return self.needs;
140    }
141
142    pub fn has(&self) -> usize {
143        return self.has;
144    }
145
146    pub fn missing(&self) -> usize {
147        return self.missing;
148    }
149
150    pub fn process_missing_count(&self) -> usize {
151        return if ProcessMissing::OnePerRequest == self.process_missing { 1 } else { self.missing };
152    }
153}
154
155#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
156pub enum ProcessNumberSpaceItems {
157    AllPerRequest,
158    OnePerRequest
159}
160
161#[derive(Clone)]
162pub struct NumberSpace {
163    number_space_items: Vec<NumberSpaceItem>,
164    process_number_space_items: ProcessNumberSpaceItems,
165}
166
167impl NumberSpace {
168    pub fn new(number_space_items: &[NumberSpaceItem]) -> NumberSpace {
169        return NumberSpace::new_with_process_number_space_items(number_space_items, ProcessNumberSpaceItems::AllPerRequest);
170    }
171
172    pub fn new_with_process_number_space_items(
173        number_space_items: &[NumberSpaceItem], 
174        process_number_space_items: ProcessNumberSpaceItems
175    ) -> NumberSpace {
176        return NumberSpace { 
177            number_space_items: number_space_items.to_vec(),
178            process_number_space_items
179        };
180    }
181
182    pub fn number_space_items(&self) -> &Vec<NumberSpaceItem> {
183        return &self.number_space_items;
184    }
185
186    fn from_numbers_2(
187        number_space_items: &Vec<NumberSpaceItem>,
188        numbers: &[usize],
189        is_sorted: bool,
190        process_number_space_items: ProcessNumberSpaceItems
191    ) -> NumberSpace {
192        return NumberSpace::from_numbers(
193            &number_space_items
194                .iter()
195                .map(|x| NumberSpaceItem::new(&x.number_space_type.clone(), x.needs))
196                .collect::<Vec<NumberSpaceItem>>(),
197                numbers,
198            false,
199            is_sorted,
200            process_number_space_items
201        );
202    }
203
204    pub fn from_numbers(
205        number_pool_items: &[NumberSpaceItem],
206        numbers: &[usize],
207        set_needs_eq_match: bool,
208        is_sorted: bool,
209        process_number_space_items: ProcessNumberSpaceItems
210    ) -> NumberSpace {
211        let mut number_space_items: Vec<NumberSpaceItem> = Vec::new();
212        for number_pool_item in number_pool_items {            
213            let match_count = number_pool_item.number_space_type.has(&NumberSpaceItem::get_num_spaces(numbers, is_sorted));
214            let missing = if match_count > number_pool_item.needs {
215                0
216            } else {
217                number_pool_item.needs - match_count
218            };
219            number_space_items.push(
220                NumberSpaceItem {
221                    number_space_type: number_pool_item.number_space_type.clone(),
222                    needs: if set_needs_eq_match {
223                        match_count
224                    } else {
225                        number_pool_item.needs
226                    },
227                    has: match_count,
228                    missing,
229                    process_missing: number_pool_item.process_missing
230                },
231            );
232        }
233        return NumberSpace {
234            number_space_items,
235            process_number_space_items
236        };
237    }
238}
239
240impl Display for NumberSpace {
241    fn fmt(&self, f: &mut Formatter) -> Result {
242        let mut s = String::from("");
243        if !self.number_space_items.is_empty() {
244            for number_space_item in &self.number_space_items {
245                s.push_str(&format!("{}", number_space_item.number_space_type));
246                s.push('=');
247                s.push_str(&number_space_item.has.to_string());
248                s.push(';');
249            }
250            s.pop();
251        }
252        write!(f,"{}",s)
253    }
254}
255
256impl Debug for NumberSpace {
257    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
258        write!(f, "{}", self)
259    }
260}
261
262impl RuleTrait for NumberSpace {
263    fn as_any(&self) -> &dyn Any {
264        self
265    }
266
267    fn share_data(
268        &self,
269        _current_data: &CurrentData,
270    ) -> Option<HashMap<String, MapAnyValue>> {
271        None
272    }
273
274    fn get_numbers(
275        &self,
276        current_data: &CurrentData
277    ) -> std::result::Result<Vec<usize>, String> {
278        let other_number_pool =
279        NumberSpace::from_numbers_2(&self.number_space_items, &current_data.selected_numbers_sorted(), true, self.process_number_space_items);
280        let mut numbers: Vec<usize> = Vec::new();
281        for number_space_item in &other_number_pool.number_space_items {
282            for _ in 0..number_space_item.process_missing_count() {
283                let number_space_base: usize = if !numbers.is_empty() {
284                    *numbers.last().unwrap()
285                } else if current_data.selected_numbers_sorted().len() > 0 {
286                    *current_data.selected_numbers_sorted().last().unwrap()
287                } else {
288                    let (min, max) = Settings::get_min_max("NumberRange", current_data.shared_data());
289                    let val = get_random_trait().get_number(min, max);
290                    numbers.push(val);
291                    val
292                };
293                numbers.push(number_space_item.number_space_type.get_number(
294                        number_space_base,
295                        Settings::get_min_max("NumberRange", current_data.shared_data()).1
296                    )
297                );
298            }
299            if self.process_number_space_items == ProcessNumberSpaceItems::OnePerRequest && !numbers.is_empty() { break; }
300        }
301        if !numbers.is_empty() {
302            return Ok(numbers);
303        }
304        
305        return Err(String::from("Skip"));
306    }
307
308    fn is_within_range(
309        &self,
310        current_data: &CurrentData
311    ) -> std::result::Result<(), (IsWithinErrorType, String)> {
312
313        let other_number_space = NumberSpace::from_numbers_2(&self.number_space_items, &current_data.selected_numbers_sorted(), true, self.process_number_space_items);
314
315        let mut total_missing: usize = 0;
316        for number_space_item in &other_number_space.number_space_items {
317            if number_space_item.has > number_space_item.needs {
318                return Err((IsWithinErrorType::Regular, format!(
319                    "Too many from pool {:?}, \"needs\" is {} and \"has\" {} from this pool",
320                    number_space_item.number_space_type, number_space_item.needs, number_space_item.has
321                )));
322            }
323            if number_space_item.needs > 0 {
324                total_missing += number_space_item.missing;
325            }
326        }
327        let len_remaining = if current_data.selected_numbers().len() > current_data.settings().count() {
328            0
329        } else {
330            current_data.settings().count() - current_data.selected_numbers().len()
331        };
332        if total_missing > 0 && total_missing > len_remaining {
333            return Err((IsWithinErrorType::MakePriority, format!(
334                "Need to pull from number pool, \"missing\" {} and there are {} numbers left to pick",
335                total_missing, len_remaining
336            )));
337        }
338        return Ok(());
339    }
340
341    fn is_match(
342        &self,
343        current_data: &CurrentData
344    ) -> std::result::Result<(), String> {
345        let other_number_space =
346            NumberSpace::from_numbers_2(&self.number_space_items, current_data.selected_numbers_sorted(), true, self.process_number_space_items);
347        for number_space_item in other_number_space.number_space_items {
348            if number_space_item.has != number_space_item.needs {
349                return Err(format!(
350                    "Expected--Pool:{:?}--Needs:{}. Actual Count:{}",
351                    number_space_item.number_space_type, number_space_item.needs, number_space_item.has
352                ));
353            }
354        }
355        return Ok(());
356    }
357
358    fn name(&self) -> String {
359        return String::from("NumberSpace");
360    }
361
362    fn check_count(
363        &self,
364        _count: usize,
365    ) -> std::result::Result<bool, String> {
366        return Ok(true);
367    }
368}
369
370impl ExcludeRuleTrait for NumberSpace {
371    fn as_any(&self) -> &dyn Any {
372        self
373    }
374
375    fn is_excluded(
376        &self,
377        current_data: &CurrentData,
378    ) -> std::result::Result<(), String> {
379        return is_excluded_helper(&self.is_match(current_data), &self.to_string());
380    }
381
382    fn is_within_excluded_range(
383        &self,
384        _current_data: &CurrentData,
385    ) -> std::result::Result<(), (IsWithinErrorType, String)> {
386        return Ok(());
387    }
388
389    fn exclude_name(&self) -> String {
390        return self.name();
391    }
392}