ddk_trie/
multi_oracle.rs

1//! Utility functions to compute outcome combinations to work with
2//! multi oracle DLC.
3
4use digit_decomposition::{compose_value, decompose_value};
5
6use crate::utils::pre_pad_vec;
7
8fn adjust_prefix_size(
9    prefix: &[usize],
10    nb_digits: usize,
11    suffix_len: usize,
12) -> Result<Vec<usize>, ()> {
13    let expected_len = nb_digits - suffix_len;
14    match prefix.len() {
15        len if len <= expected_len => Ok(pre_pad_vec(prefix.to_vec(), expected_len)),
16        len => {
17            let mut res = prefix.to_vec();
18            if !res.drain(..len - expected_len).all(|x| x == 0) {
19                return Err(());
20            }
21            Ok(res)
22        }
23    }
24}
25
26/// Returns the interval represented by the given prefix in the given base with
27/// the given number of digits.
28fn compute_interval_from_prefix(
29    prefix: &[usize],
30    num_digits: usize,
31    base: usize,
32) -> (usize, usize) {
33    let suffix_len = num_digits - prefix.len();
34    let start = compose_value(
35        &prefix
36            .iter()
37            .cloned()
38            .chain((0..suffix_len).map(|_| 0))
39            .collect::<Vec<_>>(),
40        base,
41    );
42    let end = compose_value(
43        &prefix
44            .iter()
45            .cloned()
46            .chain((0..suffix_len).map(|_| base - 1))
47            .collect::<Vec<_>>(),
48        base,
49    );
50    (start, end)
51}
52
53fn num_to_vec(input: usize, nb_digits: usize, ignored_digits: usize, base: usize) -> Vec<usize> {
54    let decomposed = decompose_value(input, base, nb_digits);
55    let to_take = decomposed.len() - ignored_digits;
56
57    decomposed.into_iter().take(to_take).collect::<Vec<_>>()
58}
59
60fn compute_min_support_covering_prefix(
61    start: usize,
62    end: usize,
63    min_support: usize,
64    min_nb_digits: usize,
65) -> Vec<usize> {
66    let left_bound = start - min_support;
67    let right_bound = end + min_support;
68    let left_bound = decompose_value(left_bound, 2, min_nb_digits);
69    let right_bound = decompose_value(right_bound, 2, min_nb_digits);
70
71    left_bound
72        .into_iter()
73        .zip(right_bound)
74        .take_while(|(x, y)| x == y)
75        .map(|(x, _)| x)
76        .collect()
77}
78
79/// Take the largest prefix of the smallest interval that contains [max_error; end + min_support]
80fn compute_left_covering_prefix(
81    start: usize,
82    max_error_exp: usize,
83    min_support: usize,
84    nb_digits: usize,
85) -> Vec<usize> {
86    let left_bound = start - min_support;
87    let left_bound = decompose_value(left_bound, 2, nb_digits);
88    let (prefix, suffix) = left_bound.split_at(nb_digits - max_error_exp);
89
90    prefix
91        .iter()
92        .chain(suffix.iter().take_while(|x| **x == 1))
93        .cloned()
94        .collect()
95}
96
97/// Take the largest prefix of the smallest interval that contains [start - min_support; max_error]
98fn compute_right_covering_prefix(
99    end: usize,
100    max_error_exp: usize,
101    min_support: usize,
102    nb_digits: usize,
103) -> Vec<usize> {
104    let left_bound = end + min_support;
105    let left_bound = decompose_value(left_bound, 2, nb_digits);
106    let (prefix, suffix) = left_bound.split_at(nb_digits - max_error_exp);
107
108    prefix
109        .iter()
110        .chain(suffix.iter().take_while(|x| **x == 0))
111        .cloned()
112        .collect()
113}
114
115fn single_covering_prefix_combinations(
116    main_outcome_prefix: &[usize],
117    secondary_outcomes_prefix: &[usize],
118    oracle_digits_infos: &[usize],
119    suffix_len: usize,
120) -> Vec<Vec<usize>> {
121    let mut secondary = oracle_digits_infos
122        .iter()
123        .skip(1)
124        .map(|nb_digits| {
125            adjust_prefix_size(
126                secondary_outcomes_prefix,
127                *nb_digits,
128                main_outcome_prefix.len() - secondary_outcomes_prefix.len() + suffix_len,
129            )
130            .unwrap()
131        })
132        .collect::<Vec<Vec<_>>>();
133    let mut res =
134        vec![adjust_prefix_size(main_outcome_prefix, oracle_digits_infos[0], suffix_len).unwrap()];
135    res.append(&mut secondary);
136    res
137}
138
139/// All combinations of main outcome and other except for the one that contains
140/// only main outcome.
141fn double_covering_restricted_prefix_combinations(
142    main_outcome_prefix: &[usize],
143    other_interval_prefix: &[usize],
144    oracle_digits_infos: &[usize],
145    suffix_len: usize,
146) -> Vec<Vec<Vec<usize>>> {
147    let mut combinations = double_covering_prefix_combinations(
148        main_outcome_prefix,
149        main_outcome_prefix,
150        other_interval_prefix,
151        oracle_digits_infos,
152        suffix_len,
153    );
154
155    if main_outcome_prefix > other_interval_prefix {
156        combinations.remove(combinations.len() - 1);
157        combinations
158    } else {
159        combinations.into_iter().skip(1).collect::<Vec<_>>()
160    }
161}
162
163/// Generates all the combination of prefixes starting with `main_outcome_prefix`
164/// with `left_interval_prefix` and `right_interval_prefix` in lexicographic
165/// order.
166fn double_covering_prefix_combinations(
167    main_outcome_prefix: &[usize],
168    left_interval_prefix: &[usize],
169    right_interval_prefix: &[usize],
170    oracle_digits_infos: &[usize],
171    suffix_len: usize,
172) -> Vec<Vec<Vec<usize>>> {
173    let nb_oracles = oracle_digits_infos.len();
174    let mut res = Vec::with_capacity(nb_oracles);
175    let (first, second) = if left_interval_prefix <= right_interval_prefix {
176        (left_interval_prefix, right_interval_prefix)
177    } else {
178        (right_interval_prefix, left_interval_prefix)
179    };
180
181    for i in 0..(1 << (nb_oracles - 1)) {
182        let mut mid_res = Vec::with_capacity(nb_oracles);
183        for j in 0..(nb_oracles - 1) {
184            let val: Vec<usize> = match i & (1 << j) {
185                0 => first.to_vec(),
186                _ => second.to_vec(),
187            };
188            mid_res.push(val);
189        }
190        mid_res.push(main_outcome_prefix.to_vec());
191        mid_res.reverse();
192        let trimmed = mid_res
193            .into_iter()
194            .zip(oracle_digits_infos)
195            .map(|(x, y)| {
196                adjust_prefix_size(&x, *y, main_outcome_prefix.len() + suffix_len - x.len())
197            })
198            .collect::<Result<Vec<Vec<usize>>, ()>>();
199
200        if let Ok(mid_res) = trimmed {
201            res.push(mid_res)
202        }
203    }
204
205    res
206}
207
208/// Compute the outcome combinations required to cover intervals that will
209/// satisfy the specified min support and max error parameters.
210/// Expect that the first element of oracle_numeric_infos is the one with smallest
211/// `num_digits`.
212/// Throws if oracle_numeric_infos has less than 2 elements or max_error_exp is
213/// not greater than min_support_exp.
214pub fn compute_outcome_combinations(
215    oracle_digits_infos: &[usize],
216    main_outcome_prefix: &[usize],
217    max_error_exp: usize,
218    min_support_exp: usize,
219    maximize_coverage: bool,
220) -> Vec<Vec<Vec<usize>>> {
221    let nb_oracles = oracle_digits_infos.len();
222    assert!(nb_oracles > 1 && max_error_exp > min_support_exp);
223
224    let min_num_digits = oracle_digits_infos[0];
225    let (min_num_digits, max_num, main_outcome_prefix) = if oracle_digits_infos
226        .iter()
227        .skip(1)
228        .all(|x| x == &min_num_digits)
229    {
230        (
231            min_num_digits,
232            (1 << min_num_digits) - 1,
233            main_outcome_prefix.to_vec(),
234        )
235    } else {
236        let mut new_main_outcome_prefix = vec![0];
237        new_main_outcome_prefix.extend_from_slice(main_outcome_prefix);
238        (
239            min_num_digits + 1,
240            (1 << (min_num_digits + 1)) - 1,
241            new_main_outcome_prefix,
242        )
243    };
244    let max_error: usize = 1 << max_error_exp;
245    let half_max_error: usize = max_error >> 1;
246    let min_support: usize = 1 << min_support_exp;
247    let suffix_len = min_num_digits - main_outcome_prefix.len();
248
249    let (start, end) = compute_interval_from_prefix(&main_outcome_prefix, min_num_digits, 2);
250
251    // interval length is strictly smaller than max_error
252    if suffix_len < max_error_exp {
253        let start_max_error_suffix = start & ((1 << max_error_exp) - 1);
254        let left_bound = (start >> max_error_exp) << max_error_exp;
255        let right_bound = left_bound | (max_error - 1);
256        let error_interval_prefix = num_to_vec(left_bound, min_num_digits, max_error_exp, 2);
257
258        // interval length is less than or equal to min_support
259        if start_max_error_suffix >= min_support && end <= right_bound - min_support {
260            let support_interval_prefix = if maximize_coverage {
261                error_interval_prefix
262            } else {
263                compute_min_support_covering_prefix(start, end, min_support, min_num_digits)
264            };
265
266            return vec![single_covering_prefix_combinations(
267                &main_outcome_prefix,
268                &support_interval_prefix,
269                oracle_digits_infos,
270                suffix_len,
271            )];
272        } else if start_max_error_suffix < min_support {
273            let right_interval_prefix = if maximize_coverage {
274                error_interval_prefix
275            } else {
276                compute_right_covering_prefix(end, max_error_exp, min_support, min_num_digits)
277            };
278
279            return if left_bound == 0 {
280                vec![single_covering_prefix_combinations(
281                    &main_outcome_prefix,
282                    &right_interval_prefix,
283                    oracle_digits_infos,
284                    suffix_len,
285                )]
286            } else {
287                let left_interval_prefix = if maximize_coverage {
288                    num_to_vec(
289                        left_bound - half_max_error,
290                        min_num_digits,
291                        max_error_exp - 1,
292                        2,
293                    )
294                } else {
295                    compute_left_covering_prefix(start, max_error_exp, min_support, min_num_digits)
296                };
297                double_covering_prefix_combinations(
298                    &main_outcome_prefix,
299                    &right_interval_prefix,
300                    &left_interval_prefix,
301                    oracle_digits_infos,
302                    suffix_len,
303                )
304            };
305        } else if end > right_bound - min_support {
306            let left_interval_prefix = if maximize_coverage {
307                error_interval_prefix
308            } else {
309                compute_left_covering_prefix(start, max_error_exp, min_support, min_num_digits)
310            };
311
312            return if right_bound == max_num {
313                vec![single_covering_prefix_combinations(
314                    &main_outcome_prefix,
315                    &left_interval_prefix,
316                    oracle_digits_infos,
317                    suffix_len,
318                )]
319            } else {
320                let right_interval_prefix = if maximize_coverage {
321                    num_to_vec(right_bound + 1, min_num_digits, max_error_exp - 1, 2)
322                } else {
323                    compute_right_covering_prefix(end, max_error_exp, min_support, min_num_digits)
324                };
325
326                double_covering_prefix_combinations(
327                    &main_outcome_prefix,
328                    &left_interval_prefix,
329                    &right_interval_prefix,
330                    oracle_digits_infos,
331                    suffix_len,
332                )
333            };
334        } else {
335            unreachable!();
336        }
337    }
338
339    let mut res = Vec::new();
340
341    if start != 0 {
342        let right_interval_prefix = if maximize_coverage {
343            num_to_vec(start, min_num_digits, max_error_exp - 1, 2)
344        } else {
345            num_to_vec(start, min_num_digits, min_support_exp, 2)
346        };
347
348        let left_interval_prefix = if maximize_coverage {
349            num_to_vec(start - half_max_error, min_num_digits, max_error_exp - 1, 2)
350        } else {
351            num_to_vec(start - min_support, min_num_digits, min_support_exp, 2)
352        };
353
354        let mut combination = double_covering_restricted_prefix_combinations(
355            &right_interval_prefix,
356            &left_interval_prefix,
357            oracle_digits_infos,
358            min_num_digits - right_interval_prefix.len(),
359        );
360
361        res.append(&mut combination);
362    }
363
364    res.push(single_covering_prefix_combinations(
365        &main_outcome_prefix,
366        &main_outcome_prefix,
367        oracle_digits_infos,
368        suffix_len,
369    ));
370
371    if end != max_num {
372        let right_interval_prefix = if maximize_coverage {
373            num_to_vec(
374                end - half_max_error + 1,
375                min_num_digits,
376                max_error_exp - 1,
377                2,
378            )
379        } else {
380            num_to_vec(end - min_support + 1, min_num_digits, min_support_exp, 2)
381        };
382
383        let left_interval_prefix = if maximize_coverage {
384            num_to_vec(end + 1, min_num_digits, max_error_exp - 1, 2)
385        } else {
386            num_to_vec(end + 1, min_num_digits, min_support_exp, 2)
387        };
388
389        let mut combination = double_covering_restricted_prefix_combinations(
390            &right_interval_prefix,
391            &left_interval_prefix,
392            oracle_digits_infos,
393            min_num_digits - right_interval_prefix.len(),
394        );
395        res.append(&mut combination);
396    }
397
398    res
399}
400
401#[cfg(test)]
402mod tests {
403    use super::*;
404    use crate::test_utils::{
405        get_variable_oracle_numeric_infos, same_num_digits_oracle_numeric_infos,
406    };
407    use secp256k1_zkp::rand::{thread_rng, RngCore};
408
409    type CoveringCetsMinMax = (Vec<(Vec<usize>, Vec<usize>)>, Vec<(Vec<usize>, Vec<usize>)>);
410
411    fn compute_covering_cets_min_and_max(
412        oracle_digits_infos: &[usize],
413        main_outcome_prefix: &[usize],
414        max_error_exp: usize,
415        min_support_exp: usize,
416    ) -> CoveringCetsMinMax {
417        let covering_max = compute_outcome_combinations(
418            oracle_digits_infos,
419            main_outcome_prefix,
420            max_error_exp,
421            min_support_exp,
422            true,
423        );
424        let covering_min = compute_outcome_combinations(
425            oracle_digits_infos,
426            main_outcome_prefix,
427            max_error_exp,
428            min_support_exp,
429            false,
430        );
431
432        assert!(covering_max.iter().all(|x| x.len() == 2));
433        assert!(covering_min.iter().all(|x| x.len() == 2));
434
435        (
436            covering_max
437                .into_iter()
438                .map(|mut x| (x.remove(0), x.remove(x.len() - 1)))
439                .collect(),
440            covering_min
441                .into_iter()
442                .map(|mut x| (x.remove(0), x.remove(x.len() - 1)))
443                .collect(),
444        )
445    }
446
447    struct TestCase {
448        main_outcome_prefix: Vec<usize>,
449        nb_digits: usize,
450        max_error_exp: usize,
451        min_support_exp: usize,
452        expected_max: Vec<(Vec<usize>, Vec<usize>)>,
453        expected_min: Vec<(Vec<usize>, Vec<usize>)>,
454    }
455
456    fn outcome_prefixes() -> Vec<Vec<usize>> {
457        vec![
458            vec![0, 0, 1, 0, 1, 1, 0, 0, 1],
459            vec![0, 1, 0, 0, 0, 0, 0, 1, 1],
460            vec![0, 1, 1, 1, 1, 1, 0, 1, 0],
461            vec![0, 1],
462            vec![0, 0, 1],
463            vec![1, 1, 1, 1, 1, 1, 1, 1],
464            vec![0, 0],
465            vec![1, 1],
466        ]
467    }
468
469    fn prefix(index: usize) -> Vec<usize> {
470        outcome_prefixes().remove(index)
471    }
472
473    fn test_cases() -> Vec<TestCase> {
474        vec![
475            TestCase {
476                main_outcome_prefix: prefix(0),
477                nb_digits: 14,
478                max_error_exp: 11,
479                min_support_exp: 7,
480                expected_max: vec![(prefix(0), vec![0, 0, 1])],
481                expected_min: vec![(prefix(0), vec![0, 0, 1, 0, 1])],
482            },
483            TestCase {
484                main_outcome_prefix: prefix(1),
485                nb_digits: 13,
486                max_error_exp: 11,
487                min_support_exp: 7,
488                expected_max: vec![(prefix(1), vec![0, 0, 1]), (prefix(1), vec![0, 1])],
489                expected_min: vec![
490                    (prefix(1), vec![0, 0, 1, 1, 1, 1]),
491                    (prefix(1), vec![0, 1, 0, 0, 0]),
492                ],
493            },
494            TestCase {
495                main_outcome_prefix: prefix(2),
496                nb_digits: 13,
497                max_error_exp: 11,
498                min_support_exp: 7,
499                expected_max: vec![(prefix(2), vec![0, 1]), (prefix(2), vec![1, 0, 0])],
500                expected_min: vec![
501                    (prefix(2), vec![0, 1, 1, 1, 1]),
502                    (prefix(2), vec![1, 0, 0, 0, 0, 0, 0]),
503                ],
504            },
505            TestCase {
506                main_outcome_prefix: prefix(3),
507                nb_digits: 13,
508                max_error_exp: 11,
509                min_support_exp: 7,
510                expected_max: vec![
511                    (vec![0, 1, 0], vec![0, 0, 1]),
512                    (prefix(3), prefix(3)),
513                    (vec![0, 1, 1], vec![1, 0, 0]),
514                ],
515                expected_min: vec![
516                    (vec![0, 1, 0, 0, 0, 0], vec![0, 0, 1, 1, 1, 1]),
517                    (prefix(3), prefix(3)),
518                    (vec![0, 1, 1, 1, 1, 1], vec![1, 0, 0, 0, 0, 0]),
519                ],
520            },
521            TestCase {
522                main_outcome_prefix: prefix(4),
523                nb_digits: 15,
524                max_error_exp: 11,
525                min_support_exp: 7,
526                expected_max: vec![
527                    (vec![0, 0, 1, 0, 0], vec![0, 0, 0, 1, 1]),
528                    (prefix(4), prefix(4)),
529                    (vec![0, 0, 1, 1, 1], vec![0, 1, 0, 0, 0]),
530                ],
531                expected_min: vec![
532                    (vec![0, 0, 1, 0, 0, 0, 0, 0], vec![0, 0, 0, 1, 1, 1, 1, 1]),
533                    (prefix(4), prefix(4)),
534                    (vec![0, 0, 1, 1, 1, 1, 1, 1], vec![0, 1, 0, 0, 0, 0, 0, 0]),
535                ],
536            },
537            TestCase {
538                main_outcome_prefix: prefix(5),
539                nb_digits: 13,
540                max_error_exp: 11,
541                min_support_exp: 7,
542                expected_max: vec![(prefix(5), vec![1, 1])],
543                expected_min: vec![(prefix(5), vec![1, 1, 1, 1, 1])],
544            },
545            TestCase {
546                main_outcome_prefix: prefix(6),
547                nb_digits: 13,
548                max_error_exp: 11,
549                min_support_exp: 7,
550                expected_max: vec![(prefix(6), prefix(6)), (vec![0, 0, 1], vec![0, 1, 0])],
551                expected_min: vec![
552                    (prefix(6), prefix(6)),
553                    (vec![0, 0, 1, 1, 1, 1], vec![0, 1, 0, 0, 0, 0]),
554                ],
555            },
556            TestCase {
557                main_outcome_prefix: prefix(7),
558                nb_digits: 14,
559                max_error_exp: 11,
560                min_support_exp: 7,
561                expected_max: vec![(vec![1, 1, 0, 0], vec![1, 0, 1, 1]), (prefix(7), prefix(7))],
562                expected_min: vec![
563                    (vec![1, 1, 0, 0, 0, 0, 0], vec![1, 0, 1, 1, 1, 1, 1]),
564                    (prefix(7), prefix(7)),
565                ],
566            },
567        ]
568    }
569
570    #[test]
571    fn compute_outcome_combination_tests() {
572        for case in test_cases() {
573            let (max, min) = compute_covering_cets_min_and_max(
574                &same_num_digits_oracle_numeric_infos(2, case.nb_digits, 2).nb_digits,
575                &case.main_outcome_prefix,
576                case.max_error_exp,
577                case.min_support_exp,
578            );
579            assert_eq!(case.expected_max, max);
580            assert_eq!(case.expected_min, min);
581        }
582    }
583
584    #[test]
585    fn compute_outcome_three_oracles() {
586        let prefix = vec![0, 1, 0];
587
588        let res = compute_outcome_combinations(
589            &same_num_digits_oracle_numeric_infos(3, 3, 2).nb_digits,
590            &prefix,
591            2,
592            1,
593            true,
594        );
595
596        let expected = vec![
597            vec![vec![0, 1, 0], vec![0], vec![0]],
598            vec![vec![0, 1, 0], vec![0], vec![1, 0]],
599            vec![vec![0, 1, 0], vec![1, 0], vec![0]],
600            vec![vec![0, 1, 0], vec![1, 0], vec![1, 0]],
601        ];
602
603        assert_eq!(res, expected);
604    }
605
606    #[test]
607    fn multiple_interval_within_bounds() {
608        let mut rng = thread_rng();
609
610        let nb_digits = (rng.next_u32() % 29) + 2;
611        let nb_digits_used = rng.next_u32() % nb_digits + 1;
612        let mut main_outcome_prefix = Vec::with_capacity(nb_digits_used as usize);
613        for _ in 0..nb_digits_used {
614            main_outcome_prefix.push((rng.next_u32() % 2) as usize);
615        }
616        let max_error_exp = (rng.next_u32() % (nb_digits - 1)) + 1;
617        let min_support_exp = rng.next_u32() % max_error_exp;
618        let nb_digits = nb_digits as usize;
619        let max_error_exp = max_error_exp as usize;
620        let min_support_exp = min_support_exp as usize;
621        let max_error = 1 << max_error_exp;
622        let min_support = 1 << min_support_exp;
623        let max_val = (1 << nb_digits) - 1;
624
625        let (cover_max, cover_min) = compute_covering_cets_min_and_max(
626            &same_num_digits_oracle_numeric_infos(2, nb_digits, 2).nb_digits,
627            &main_outcome_prefix,
628            max_error_exp,
629            min_support_exp,
630        );
631
632        assert_eq!(cover_min.len(), cover_max.len());
633        assert!(cover_min
634            .iter()
635            .map(|(a, _)| a)
636            .zip(cover_max.iter().map(|(a, _)| a))
637            .all(|(a, b)| a.iter().zip(b.iter()).all(|(c, d)| c == d)));
638
639        let relevant_primary_prefixes = cover_max.iter().map(|(a, _)| a).collect::<Vec<_>>();
640        let (left, right) = compute_interval_from_prefix(&main_outcome_prefix, nb_digits, 2);
641
642        let primary_and_covering_intervals_max: Vec<((usize, usize), (usize, usize))> = cover_max
643            .iter()
644            .map(|(a, b)| {
645                (
646                    compute_interval_from_prefix(a, nb_digits, 2),
647                    compute_interval_from_prefix(b, nb_digits, 2),
648                )
649            })
650            .collect();
651        let cover_intervals_min: Vec<(usize, usize)> = cover_max
652            .iter()
653            .map(|(_, b)| compute_interval_from_prefix(b, nb_digits, 2))
654            .collect();
655
656        for (
657            ((primary_left, primary_right), (max_cover_left, max_cover_right)),
658            (min_cover_left, min_cover_right),
659        ) in primary_and_covering_intervals_max
660            .iter()
661            .cloned()
662            .zip(cover_intervals_min.iter().cloned())
663        {
664            assert!(max_cover_left <= min_cover_left);
665            assert!(max_cover_right <= min_cover_right);
666
667            if primary_left == max_cover_left && primary_right == max_cover_right {
668                assert_eq!(min_cover_left, max_cover_left);
669                assert_eq!(min_cover_right, max_cover_right);
670            }
671
672            let assert_valid_cover = |cover_left: usize, cover_right: usize, max_coverage: bool| {
673                if primary_left == cover_left && primary_right == cover_right {
674                } else if primary_left >= cover_left && primary_right <= cover_right {
675                    if max_coverage {
676                        assert_eq!(cover_right - cover_left + 1, max_error);
677                    } else {
678                        let side_to_boundary = std::cmp::max(
679                            primary_right % max_error,
680                            max_error - (primary_left % max_error),
681                        );
682                        assert!(cover_right - cover_left < 2 * side_to_boundary);
683                        assert!(cover_right - cover_left + 1 >= side_to_boundary);
684                    }
685
686                    assert!(
687                        primary_left - cover_left >= min_support
688                            || cover_left == 0
689                            || relevant_primary_prefixes.len() == 2
690                    );
691                    assert!(primary_right - cover_left < max_error);
692                    assert!(
693                        cover_right - primary_right >= min_support
694                            || cover_right == max_val
695                            || relevant_primary_prefixes.len() == 2
696                    );
697                } else {
698                    let (most_inner, least_inner, most_outer) = if primary_left <= cover_left {
699                        (primary_left, primary_right, cover_right)
700                    } else {
701                        (primary_right, primary_left, cover_left)
702                    };
703
704                    let diff = |x: usize, y: usize| -> usize {
705                        if x > y {
706                            x - y
707                        } else {
708                            y - x
709                        }
710                    };
711
712                    assert!(diff(least_inner, most_outer) >= min_support);
713                    assert!(diff(most_inner, most_outer) < max_error);
714                }
715            };
716
717            assert_valid_cover(max_cover_left, max_cover_right, true);
718            assert_valid_cover(min_cover_left, min_cover_right, false);
719        }
720
721        let primary_interval = primary_and_covering_intervals_max
722            .iter()
723            .map(|(a, _)| *a)
724            .fold((usize::MAX, 0), |(min, max), (start, end)| {
725                (std::cmp::min(min, start), std::cmp::max(max, end))
726            });
727        assert_eq!(primary_interval, (left, right));
728
729        let (max_cover_interval_left, max_cover_interval_right) =
730            primary_and_covering_intervals_max
731                .iter()
732                .map(|(_, b)| *b)
733                .fold((usize::MAX, 0), |(min, max), (start, end)| {
734                    (std::cmp::min(min, start), std::cmp::max(max, end))
735                });
736        let (min_cover_interval_left, min_cover_interval_right) = cover_intervals_min
737            .iter()
738            .fold((usize::MAX, 0), |(min, max), (start, end)| {
739                (std::cmp::min(min, *start), std::cmp::max(max, *end))
740            });
741        assert!(max_cover_interval_left <= min_cover_interval_left);
742        assert!(max_cover_interval_right >= min_cover_interval_right);
743
744        assert!(left - max_cover_interval_left >= min_support || max_cover_interval_left == 0);
745        assert!(left - max_cover_interval_left < max_error);
746        assert!(
747            max_cover_interval_right - right >= min_support || max_cover_interval_right == max_val
748        );
749        assert!(max_cover_interval_right - right < max_error);
750
751        assert!(left - min_cover_interval_left >= min_support || min_cover_interval_left == 0);
752        assert!(left - min_cover_interval_left < max_error);
753        assert!(
754            min_cover_interval_right - right >= min_support || min_cover_interval_right == max_val
755        );
756        assert!(min_cover_interval_right - right < max_error);
757    }
758
759    struct VariableLengthTestCase {
760        main_outcome_prefix: Vec<usize>,
761        nb_digits: Vec<usize>,
762        max_error_exp: usize,
763        min_support_exp: usize,
764        expected_max: Vec<(Vec<usize>, Vec<usize>)>,
765        expected_min: Vec<(Vec<usize>, Vec<usize>)>,
766    }
767
768    impl From<TestCase> for VariableLengthTestCase {
769        fn from(test_case: TestCase) -> VariableLengthTestCase {
770            let to_add = ((thread_rng().next_u32() % 10) + 1) as usize;
771
772            let extend = |y: &[usize]| {
773                let mut new_y = Vec::with_capacity(y.len() + to_add);
774                new_y.resize(to_add, 0);
775                new_y.extend(y.iter());
776                new_y
777            };
778
779            let expected_max = test_case
780                .expected_max
781                .iter()
782                .map(|(x, y)| (x.clone(), extend(y)))
783                .collect();
784
785            let expected_min = test_case
786                .expected_min
787                .iter()
788                .map(|(x, y)| (x.clone(), extend(y)))
789                .collect();
790
791            VariableLengthTestCase {
792                main_outcome_prefix: test_case.main_outcome_prefix,
793                nb_digits: vec![test_case.nb_digits, test_case.nb_digits + to_add],
794                max_error_exp: test_case.max_error_exp,
795                min_support_exp: test_case.min_support_exp,
796                expected_max,
797                expected_min,
798            }
799        }
800    }
801
802    fn variable_len_test_cases() -> Vec<VariableLengthTestCase> {
803        let black_list = [5, 7];
804        let mut test_cases = test_cases()
805            .into_iter()
806            .enumerate()
807            .filter(|(i, _)| !black_list.contains(i))
808            .map(|(_, x)| x.into())
809            .collect::<Vec<VariableLengthTestCase>>();
810        test_cases.append(&mut vec![VariableLengthTestCase {
811            main_outcome_prefix: vec![1, 1, 1, 1],
812            nb_digits: vec![4, 5],
813            max_error_exp: 2,
814            min_support_exp: 1,
815            expected_max: vec![
816                (vec![1, 1, 1, 1], vec![0, 1, 1]),
817                (vec![1, 1, 1, 1], vec![1, 0, 0, 0]),
818            ],
819            expected_min: vec![
820                (vec![1, 1, 1, 1], vec![0, 1, 1]),
821                (vec![1, 1, 1, 1], vec![1, 0, 0, 0]),
822            ],
823        }]);
824        test_cases
825    }
826
827    #[test]
828    fn variable_nb_digit_tests() {
829        for case in variable_len_test_cases() {
830            let (max, min) = compute_covering_cets_min_and_max(
831                &get_variable_oracle_numeric_infos(&case.nb_digits, 2).nb_digits,
832                &case.main_outcome_prefix,
833                case.max_error_exp,
834                case.min_support_exp,
835            );
836            assert_eq!(case.expected_max, max);
837            assert_eq!(case.expected_min, min);
838        }
839    }
840}