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 {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 {NumberSpaceType::Lt(v) => get_random_trait().get_number(number_space_base + 1, number_space_base + v - 1), NumberSpaceType::Lte(v) => get_random_trait().get_number(number_space_base + 1, number_space_base + v), NumberSpaceType::Eq(v) => number_space_base + v, 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), };
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, ¤t_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, ¤t_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}