sapling_crypto_ce/
as_waksman.rs

1use rand::{
2    Rng
3};
4
5const EMPTY_STATE: usize = std::usize::MAX;
6
7// this is basically a grid of size x columns
8pub struct AsWaksmanTopology {
9    pub topology: Vec<Vec<(usize, usize)>>,
10    pub size: usize
11    // pub switches: Vec<std::collections::HashMap<usize, bool>>
12}
13
14impl AsWaksmanTopology {
15    pub fn new(size: usize) -> Self {
16        assert!(size > 1, "don't make strange moves");
17
18        let num_colunms = Self::num_colunms(size);
19
20        // make the grid
21        let mut topology = vec![vec![(EMPTY_STATE, EMPTY_STATE); size]; num_colunms];
22
23        let destinations: Vec<usize> = (0..size).collect();
24
25        // recursively iterate and construct the topology
26        Self::construct_inner(0, num_colunms-1, 0, size-1, &destinations, &mut topology);
27
28        Self {
29            topology,
30            size
31        }
32    }
33
34    fn calculate_top_height(size: usize) -> usize {
35        size / 2
36    }
37
38    fn construct_inner(left: usize, right: usize, 
39                        low: usize, high: usize,
40                        destinations: &[usize],
41                        topology: &mut [Vec<(usize, usize)>]) 
42    {
43        if left > right {
44            return;
45        }
46
47        // we should be working with a proper size
48        let rows_to_generate = high - low + 1;
49        assert_eq!(destinations.len(), rows_to_generate);
50
51        let columns_to_generate = Self::num_colunms(rows_to_generate);
52        let num_columns = right - left + 1;
53        assert!(num_columns >= columns_to_generate);
54
55        if num_columns > columns_to_generate
56        {
57            // 
58            // If there is more space for the routing network than needed,
59            // just add straight edges. This also handles the size-1 base case.
60            //
61            for idx in low..=high {
62                topology[left][idx].0 = idx;
63                topology[left][idx].1 = idx;
64
65                topology[right][idx].0 = destinations[idx - low];
66                topology[right][idx].1 = destinations[idx - low];
67            }
68            let mut subdestinations = vec![EMPTY_STATE; rows_to_generate];
69
70            for idx in low..=high {
71                subdestinations[idx-low] = idx;
72            }
73
74            Self::construct_inner(left+1, right-1, low, high, &subdestinations, topology);
75        } else if rows_to_generate == 2 {
76            topology[left][low].0 = destinations[0];
77            topology[left][high].1 = destinations[0];
78
79            topology[left][low].1 = destinations[1];
80            topology[left][high].0 = destinations[1];
81        } else {
82            // recursion step
83
84            let mut subdestinations = vec![EMPTY_STATE; rows_to_generate];
85            let limit = if rows_to_generate % 2 == 1 {
86                high
87            } else {
88                high + 1
89            };
90
91            for idx in (low..limit).step_by(2) {
92                let top_idx = Self::calculate_in_out_index(rows_to_generate, low, idx, true);
93                topology[left][idx].0 = top_idx;
94                topology[left][idx+1].1 = top_idx;
95
96                let bottom_idx = Self::calculate_in_out_index(rows_to_generate, low, idx, false);
97                topology[left][idx].1 = bottom_idx;
98                topology[left][idx+1].0 = bottom_idx;
99
100                subdestinations[(top_idx as usize) - low] = idx;
101                subdestinations[(bottom_idx as usize) - low] = idx + 1;
102
103                topology[right][idx].0 = destinations[idx - low];
104                topology[right][idx+1].1 = destinations[idx - low];
105
106                topology[right][idx].1 = destinations[idx + 1 - low];
107                topology[right][idx+1].0 = destinations[idx + 1 - low];
108            }
109
110            if rows_to_generate % 2 == 1
111            {
112                topology[left][high].0 = high;
113                topology[left][high].1 = high;
114
115                topology[right][high].0 = destinations[high - low];
116                topology[right][high].1 = destinations[high - low];
117
118                subdestinations[high - low] = high;
119            }
120            else
121            {
122                topology[left][high-1].1 = topology[left][high-1].0;
123                topology[left][high].1 = topology[left][high].0;
124            }
125
126            let d = AsWaksmanTopology::calculate_top_height(rows_to_generate);
127            let top_subnet_destinations = &subdestinations[..d];
128            let bottom_subnet_destinations = &subdestinations[d..];
129
130            Self::construct_inner(left+1, right-1, low, low + d - 1, top_subnet_destinations, topology);
131            Self::construct_inner(left+1, right-1, low + d, high, bottom_subnet_destinations, topology);
132        }
133    }
134
135    pub(crate) fn num_colunms(size: usize) -> usize {
136        if size <= 1 {
137            return 0;
138        }
139
140        let as_float = f64::from(size as u32);
141
142        let ceil = as_float.log(2.0).ceil();
143        let num_colunms = ceil as usize;
144
145        2*num_colunms - 1
146    }
147
148    fn calculate_in_out_index(
149        size: usize,
150        offset: usize,
151        index: usize,
152        is_top: bool
153    ) -> usize {
154        let mut relative_position = index - offset;
155        assert!(relative_position % 2 == 0 && relative_position + 1 < size);
156        relative_position >>= 1;
157        let mut suboffset = 0;
158        if !is_top {
159            suboffset = AsWaksmanTopology::calculate_top_height(size);
160        }
161
162        offset + relative_position + suboffset
163    }
164}
165
166// Integer representation should always be generated starting from 0 and only internal calls should
167// generate it from non-zero start
168
169#[derive(Debug)]
170pub struct IntegerPermutation {
171    pub elements: Vec<usize>,
172    pub min: usize,
173    pub max: usize
174}
175
176impl IntegerPermutation {
177    pub fn new(size: usize) -> Self {
178        Self::new_for_max_and_min(0, size - 1)
179    }
180
181    pub fn new_from_permutation(permutation: Vec<u64>) -> Self {
182        unimplemented!();
183    }
184
185    pub(crate) fn new_for_max_and_min(min: usize, max: usize) -> Self {
186        let elements: Vec<usize> = (min..=max).collect();
187
188        Self {
189            elements,
190            min: min,
191            max: max
192        }
193    }
194
195    pub fn size(&self) -> usize {
196        self.max - self.min + 1
197        // self.elements.len()
198    }
199
200    pub fn make_permutation<R: Rng>(&mut self, rng: &mut R) {
201        let mut copy = self.elements.clone();
202        rng.shuffle(&mut copy);
203        self.elements = copy;
204    }
205
206    pub fn get(&self, index: usize) -> usize {
207        assert!(index >= self.min && index <= self.max);
208        self.elements[index - self.min]
209    }
210
211    pub fn set(&mut self, index: usize, value: usize) {
212        assert!(index >= self.min && index <= self.max);
213        self.elements[index - self.min] = value;
214    }
215
216    pub fn slice(&self, min: usize, max: usize) -> Self {
217        assert!(self.min <= min && min <= max && max <= self.max);
218
219        let result = Self {
220            elements: self.elements[(min - self.min)..(max - self.min + 1)].to_vec(),
221            min: min,
222            max: max
223        };
224
225        assert!(result.size() == result.elements.len());
226
227        result
228    }
229
230    pub fn inverse(&self) -> Self {
231        let mut new = Self::new_for_max_and_min(self.min, self.max);
232        for idx in self.min..=self.max {
233            new.elements[self.elements[idx - self.min] - self.min] = idx;
234        }
235
236        new
237    }
238
239    pub fn is_valid(&self) -> bool {
240        if self.elements.len() == 0 {
241            return true;
242        }
243
244        let mut set: std::collections::HashSet<usize, > = std::collections::HashSet::with_capacity(self.elements.len());
245        for element in self.elements.iter() {
246            if *element < self.min || *element > self.max {
247                return false;
248            }
249            if set.contains(element) {
250                return false;
251            }
252            set.insert(*element);
253        }
254
255        true
256    }    
257}
258
259// this is basically a grid of size x columns
260pub struct AsWaksmanRoute {
261    pub switches: Vec<std::collections::HashMap<usize, bool>>,
262    pub size: usize
263}
264
265impl AsWaksmanRoute {
266    pub fn new(permutation: &IntegerPermutation) -> Self {
267        let size = permutation.size();
268        let num_columns = AsWaksmanTopology::num_colunms(size);
269        let empty_assignment: std::collections::HashMap<usize, bool> = std::collections::HashMap::new();
270        let mut assignments = vec![empty_assignment; num_columns];
271
272        let inversed_permutation = permutation.inverse();
273        assert!(inversed_permutation.inverse().elements == permutation.elements);
274        Self::construct_inner(0, num_columns-1, 0, size-1, permutation, &inversed_permutation, &mut assignments);
275
276        Self {
277            switches: assignments,
278            size
279        }
280    }
281
282    fn get_canonical_row_index(offset: usize, row_index: usize) -> usize {
283        let suboffset = row_index - offset;
284
285        let mask = std::usize::MAX - 1;
286
287        (suboffset & mask) + offset
288    }
289
290    fn get_switch_setting_from_top_bottom_decision(offset: usize, packet_index: usize, is_top: bool) -> bool {
291        let row_index = Self::get_canonical_row_index(offset, packet_index);
292
293        (packet_index == row_index) ^ is_top
294    }
295
296    fn get_top_bottom_decision_from_switch_setting(offset: usize, packet_index: usize, is_on: bool) -> bool {
297        let row_index = Self::get_canonical_row_index(offset, packet_index);
298
299        (row_index == packet_index) ^ is_on
300    }
301
302    fn calculate_other_position(offset: usize, packet_index: usize) -> usize {
303        let row_index = Self::get_canonical_row_index(offset, packet_index);
304        (1 - (packet_index - row_index)) + row_index
305    }
306
307    fn construct_inner(
308        left: usize, right: usize,
309        low: usize, high: usize,
310        permutation: &IntegerPermutation,
311        permutation_inversed: &IntegerPermutation,
312        switches: &mut [std::collections::HashMap<usize, bool>]
313    ) {
314        if left > right
315        {
316            return;
317        }
318
319        let rows_to_generate = high - low + 1;
320        let columns_to_generate = AsWaksmanTopology::num_colunms(rows_to_generate);
321        let num_columns = right - left + 1;
322        assert!(num_columns >= columns_to_generate);
323
324        assert!(permutation.min == low);
325        assert!(permutation.max == high);
326        assert!(permutation.size() == rows_to_generate);
327        assert!(permutation.is_valid());
328        assert!(permutation.inverse().elements == permutation_inversed.elements);
329        assert!(permutation_inversed.inverse().elements == permutation.elements);
330
331        if num_columns > columns_to_generate
332        {
333            Self::construct_inner(left+1, right - 1, low, high, permutation, permutation_inversed, switches);
334        }
335        else if rows_to_generate == 2
336        {
337            assert!(permutation.get(low) == low || permutation.get(low) == low+1);
338            assert!(permutation.get(low+1) == low || permutation.get(low+1) == low+1);
339            assert!(permutation.get(low) != permutation.get(low+1));
340
341            switches[left].insert(low, permutation.get(low) != low);
342        }
343        else
344        {
345            //
346            // The algorithm first assigns a setting to a LHS switch,
347            // route its target to RHS, which will enforce a RHS switch setting.
348            // Then, it back-routes the RHS value back to LHS.
349            // If this enforces a LHS switch setting, then forward-route that;
350            // otherwise we will select the next value from LHS to route.
351            //
352            let mut new_permutation = IntegerPermutation::new_for_max_and_min(low, high);
353            let mut new_permutation_inversed = IntegerPermutation::new_for_max_and_min(low, high);
354            let mut lhs_is_routed = vec![false; rows_to_generate];
355
356            let mut to_route;
357            let mut max_unrouted;
358
359            let mut should_route_left;
360
361            if rows_to_generate % 2 == 1
362            {
363                //
364                // ODD CASE: we first deal with the bottom-most straight wire,
365                // which is not connected to any of the switches at this level
366                // of recursion and just passed into the lower subnetwork.
367                //
368                if permutation.get(high) == high
369                {
370                    //
371                    // Easy sub-case: it is routed directly to the bottom-most
372                    // wire on RHS, so no switches need to be touched.
373                    //
374                    new_permutation.set(high, high);
375                    new_permutation_inversed.set(high, high);
376                    to_route = high - 1;
377                    should_route_left = true;
378                }
379                else
380                {
381                    //
382                    // Other sub-case: the straight wire is routed to a switch
383                    // on RHS, so route the other value from that switch
384                    // using the lower subnetwork.
385                    //
386                    let rhs_switch = Self::get_canonical_row_index(low, permutation.get(high));
387                    let rhs_switch_setting = Self::get_switch_setting_from_top_bottom_decision(low, permutation.get(high), false);
388                    switches[right].insert(rhs_switch, rhs_switch_setting);
389
390                    let tprime = AsWaksmanTopology::calculate_in_out_index(rows_to_generate, low, rhs_switch, false);
391                    new_permutation.set(high, tprime);
392                    new_permutation_inversed.set(tprime, high);
393
394                    to_route = Self::calculate_other_position(low, permutation.get(high));
395
396                    should_route_left = false;
397                }
398
399                lhs_is_routed[high - low] = true;
400                max_unrouted = high - 1;
401            }
402            else
403            {
404                //
405                // EVEN CASE: the bottom-most switch is fixed to a constant
406                // straight setting. So we route wire hi accordingly.
407                //
408                switches[left].insert(high - 1, false);
409                to_route = high;
410                should_route_left = true;
411                max_unrouted = high;
412            }
413
414            loop
415            {
416                //
417                // INVARIANT: the wire `to_route' on LHS (if route_left = true),
418                // resp., RHS (if route_left = false) can be routed.
419                //
420                if should_route_left
421                {
422                    // If switch value has not been assigned, assign it arbitrarily.
423                    let lhs_switch = Self::get_canonical_row_index(low, to_route);
424                    if switches[left].get(&lhs_switch).is_none()
425                    {
426                        switches[left].insert(lhs_switch, false);
427                    }
428                    let lhs_switch_setting = *switches[left].get(&lhs_switch).unwrap();
429                    let should_use_top = Self::get_top_bottom_decision_from_switch_setting(low, to_route, lhs_switch_setting);
430                    let t = AsWaksmanTopology::calculate_in_out_index(rows_to_generate, low, lhs_switch, should_use_top);
431                    if permutation.get(to_route) == high
432                    {
433                        //
434                        // We have routed to the straight wire for the odd case,
435                        // so now we back-route from it.
436                        //
437                        new_permutation.set(t, high);
438                        new_permutation_inversed.set(high, t);
439                        lhs_is_routed[to_route - low] = true;
440                        to_route = max_unrouted;
441                        should_route_left = true;
442                    }
443                    else
444                    {
445                        let rhs_switch = Self::get_canonical_row_index(low, permutation.get(to_route));
446                        //
447                        // We know that the corresponding switch on the right-hand side
448                        // cannot be set, so we set it according to the incoming wire.
449                        //
450                        assert!(switches[right].get(&rhs_switch).is_none());
451
452                        let switch_setting = Self::get_switch_setting_from_top_bottom_decision(low, permutation.get(to_route), should_use_top);
453                        switches[right].insert(rhs_switch, switch_setting);
454                        let tprime = AsWaksmanTopology::calculate_in_out_index(rows_to_generate, low, rhs_switch, should_use_top);
455                        new_permutation.set(t, tprime);
456                        new_permutation_inversed.set(tprime, t);
457
458                        lhs_is_routed[to_route - low] = true;
459                        to_route = Self::calculate_other_position(low, permutation.get(to_route));
460                        should_route_left = false;
461                    }
462                }
463                else
464                {
465                    //
466                    // We have arrived on the right-hand side, so the switch setting is fixed.
467                    // Next, we back route from here.
468                    //
469                    let rhs_switch = Self::get_canonical_row_index(low, to_route);
470                    let lhs_switch = Self::get_canonical_row_index(low, permutation_inversed.get(to_route));
471                    assert!(switches[right].get(&rhs_switch).is_some());
472                    let rhs_switch_setting = *switches[right].get(&rhs_switch).unwrap();
473                    let should_use_top = Self::get_top_bottom_decision_from_switch_setting(low, to_route, rhs_switch_setting);
474                    let lhs_switch_setting = Self::get_switch_setting_from_top_bottom_decision(low, permutation_inversed.get(to_route) as usize, should_use_top);
475
476                    // The value on the left-hand side is either the same or not set
477                    if let Some(value) = switches[left].get(&lhs_switch) {
478                        assert!(*value == lhs_switch_setting);
479                    }
480
481                    switches[left].insert(lhs_switch, lhs_switch_setting);
482
483                    let t = AsWaksmanTopology::calculate_in_out_index(rows_to_generate, low, rhs_switch, should_use_top);
484                    let tprime = AsWaksmanTopology::calculate_in_out_index(rows_to_generate, low, lhs_switch, should_use_top);
485                    new_permutation.set(tprime, t);
486                    new_permutation_inversed.set(t, tprime);
487
488                    lhs_is_routed[permutation_inversed.get(to_route) - low] = true;
489                    to_route = Self::calculate_other_position(low, permutation_inversed.get(to_route));
490                    should_route_left = true;
491                }
492
493                /* If the next packet to be routed hasn't been routed before, then try routing it. */
494                if !should_route_left || !lhs_is_routed[to_route-low]
495                {
496                    continue;
497                }
498
499                /* Otherwise just find the next unrouted packet. */
500                while max_unrouted > low && lhs_is_routed[max_unrouted-low]
501                {
502                    max_unrouted -= 1;
503                }
504
505                if max_unrouted < low || (max_unrouted == low && lhs_is_routed[0])
506                {
507                    /* All routed! */
508                    break;
509                }
510                else
511                {
512                    to_route = max_unrouted;
513                    should_route_left = true;
514                }
515            }
516
517            if rows_to_generate % 2 == 0
518            {
519                /* Remove the AS-Waksman switch with the fixed value. */
520                switches[left].remove(&(high - 1));
521            }
522
523            assert!(new_permutation.is_valid());
524            assert!(new_permutation_inversed.is_valid());
525
526            let d = AsWaksmanTopology::calculate_top_height(rows_to_generate);
527            let new_permutation_upper = new_permutation.slice(low, low + d - 1);
528            let new_permutation_lower = new_permutation.slice(low + d, high);
529
530            let new_permutation_inversed_upper = new_permutation_inversed.slice(low, low + d - 1);
531            let new_permutation_inversed_lower = new_permutation_inversed.slice(low + d, high);
532
533            Self::construct_inner(left+1, right-1, low, low + d - 1, &new_permutation_upper, &new_permutation_inversed_upper, switches);
534            Self::construct_inner(left+1, right-1, low + d, high, &new_permutation_lower, &new_permutation_inversed_lower, switches);
535        }
536    }
537
538    fn validate_routing_for_permutation(permutation: &IntegerPermutation,
539                                        routing: &Self) -> bool 
540    {
541        let size = permutation.size();
542        let num_columns = AsWaksmanTopology::num_colunms(size);
543        let topology = AsWaksmanTopology::new(size);
544
545        let mut current_perm = IntegerPermutation::new(size);
546
547        for column_idx in 0..num_columns {
548            let mut next_perm = IntegerPermutation::new(size);
549            for packet_idx in 0..size {
550                let routed_index;
551                if topology.topology[column_idx][packet_idx].0 == topology.topology[column_idx][packet_idx].1 {
552                    // straight switch
553                    routed_index = topology.topology[column_idx][packet_idx].0;
554                }
555                else
556                {
557                    let a = routing.switches[column_idx].get(&packet_idx);
558                    let b = if packet_idx > 0 {
559                        routing.switches[column_idx].get(&(packet_idx - 1))
560                    } else {
561                        None
562                    };
563                    assert!(a.is_some() ^ b.is_some());
564                    let switch_setting = if a.is_some() {
565                        *a.unwrap()
566                    } else {
567                        *b.unwrap()
568                    };
569                    
570                    routed_index = if switch_setting {
571                        topology.topology[column_idx][packet_idx].1
572                    } else {
573                        topology.topology[column_idx][packet_idx].0
574                    };
575                }
576
577                let val = current_perm.get(packet_idx);
578                next_perm.set(routed_index, val);
579            }
580
581            current_perm = next_perm;
582        }
583
584        current_perm.elements == permutation.inverse().elements
585    }
586
587    fn get_number_of_gates(&self) -> usize {
588        let mut result = 0;
589        for column in self.switches.iter() {
590            result += column.len();
591        }
592
593        result
594    }
595
596    fn assign_switches(&mut self, switch_assignments: &[bool]) {
597        let required_switches = self.get_number_of_gates();
598        assert!(switch_assignments.len() == required_switches);
599        let mut i = 0;
600        for column in self.switches.iter_mut() {
601            let mut keys = column.keys().cloned().into_iter().collect::<Vec<_>>();
602            keys.sort();
603            keys.reverse();
604            for k in keys.into_iter() {
605                column.insert(k, switch_assignments[i]);
606                i += 1;
607            }
608        }
609    }
610
611    fn dump_assignments(&self) -> Vec<bool> {
612        let mut result = vec![false; self.get_number_of_gates()];
613        let mut i = 0;
614        for column in self.switches.iter() {
615            let mut keys = column.keys().cloned().into_iter().collect::<Vec<_>>();
616            keys.sort();
617            keys.reverse();
618            for k in keys.into_iter() {
619                result[i] = *column.get(&k).unwrap();
620                i += 1;
621            }
622        }
623
624        result
625    }
626
627
628    // this function forwards newly created ordered set [0, n) into the permutation by switches
629    // that were supplied to the router
630    fn calculate_permutation(&self) -> IntegerPermutation 
631    {
632        let num_columns = AsWaksmanTopology::num_colunms(self.size);
633        let topology = AsWaksmanTopology::new(self.size);
634
635        let mut permutation = IntegerPermutation::new(self.size);
636
637        for column_idx in 0..num_columns {
638            let mut permutation_by_this_column = IntegerPermutation::new(self.size);
639            for packet_idx in 0..self.size {
640                let routed_into;
641                if topology.topology[column_idx][packet_idx].0 == topology.topology[column_idx][packet_idx].1 {
642                    // straight switch
643                    routed_into = topology.topology[column_idx][packet_idx].0;
644                }
645                else
646                {
647                    let a = self.switches[column_idx].get(&packet_idx);
648                    let b = if packet_idx > 0 {
649                        self.switches[column_idx].get(&(packet_idx - 1))
650                    } else {
651                        None
652                    };
653                    assert!(a.is_some() ^ b.is_some());
654                    let switch_setting = if a.is_some() {
655                        *a.unwrap()
656                    } else {
657                        *b.unwrap()
658                    };
659                    
660                    routed_into = if switch_setting {
661                        topology.topology[column_idx][packet_idx].1
662                    } else {
663                        topology.topology[column_idx][packet_idx].0
664                    };
665                }
666
667                let value_at_this_level = permutation.get(packet_idx);
668                permutation_by_this_column.set(routed_into, value_at_this_level);
669            }
670            
671            // permutation that we keep a track on is now replaced by result of permutation by this column
672            permutation = permutation_by_this_column;
673        }
674
675        permutation.inverse()
676    }
677}
678
679#[test]
680fn test_aswaksman() {
681    use rand::{Rand, thread_rng};
682    let size = 3;
683
684    let mut permutation = IntegerPermutation::new(size);
685    let rng = &mut thread_rng();
686    permutation.make_permutation(rng);
687    // println!("Permutation = {:?}", permutation);
688    // println!("Inverse = {:?}", permutation.inverse());
689    // println!("Permutation = {:?}", permutation.elements);
690    let no_permutation = IntegerPermutation::new(size);
691    assert!(permutation.inverse().inverse().elements == permutation.elements);
692
693    let router = AsWaksmanRoute::new(&permutation);
694
695    let is_valid = AsWaksmanRoute::validate_routing_for_permutation(&permutation, &router);
696}
697
698#[test]
699fn test_back_and_forward_pass() {
700    use rand::{Rand, thread_rng};
701    let rng = &mut thread_rng();
702    for size in 3..4 {
703        let mut permutation = IntegerPermutation::new(size);
704        permutation.make_permutation(rng);
705
706        let router = AsWaksmanRoute::new(&permutation);
707        println!("Permutation = {:?}", permutation.elements);
708
709        let is_valid = AsWaksmanRoute::validate_routing_for_permutation(&permutation, &router);
710        assert!(is_valid);
711
712        let new_shuffle = router.calculate_permutation();
713        println!("Obtained shuffle = {:?}", new_shuffle.elements);
714        assert!(new_shuffle.elements == permutation.elements);
715    }
716}
717
718#[test]
719fn test_forward_pass() {
720    use rand::{Rand, thread_rng};
721    let rng = &mut thread_rng();
722    for size in 3..9 {
723        let mut permutation = IntegerPermutation::new(size);
724        permutation.make_permutation(rng);
725
726        let router = AsWaksmanRoute::new(&permutation);
727
728        let is_valid = AsWaksmanRoute::validate_routing_for_permutation(&permutation, &router);
729        assert!(is_valid);
730
731        let dump = router.dump_assignments();
732        let no_permutation = IntegerPermutation::new(size);
733        let mut router = AsWaksmanRoute::new(&no_permutation);
734        router.assign_switches(&dump);
735        let shuffle = router.calculate_permutation();
736
737        assert!(shuffle.elements == permutation.elements);
738    }
739}
740
741#[test]
742fn test_trivial_permutations() {
743    use rand::{Rand, thread_rng};
744    let rng = &mut thread_rng();
745    let topology = AsWaksmanTopology::new(3);
746    // println!("Topology = {:?}", topology.topology);
747    for _ in 0..100 {
748        for size in 2..128 {
749            let mut permutation = IntegerPermutation::new(size);
750            permutation.make_permutation(rng);
751            assert!(permutation.inverse().inverse().elements == permutation.elements);
752        }
753    }
754}
755
756#[test]
757fn test_routing_for_permutation() {
758    use rand::{Rand, thread_rng};
759    let rng = &mut thread_rng();
760    let topology = AsWaksmanTopology::new(3);
761    // println!("Topology = {:?}", topology.topology);
762    for size in 2..128 {
763        println!("size = {}", size);
764        for _ in 0..100 {
765            let mut permutation = IntegerPermutation::new(size);
766            // permutation.elements = vec![2, 1, 0];
767            permutation.make_permutation(rng);
768            // println!("P = {:?}, P_inv = {:?}", permutation, permutation.inverse());
769            assert!(permutation.inverse().inverse().elements == permutation.elements);
770
771            let router = AsWaksmanRoute::new(&permutation);
772
773            let is_valid = AsWaksmanRoute::validate_routing_for_permutation(&permutation, &router);
774            assert!(is_valid);
775        }
776    }
777}
778
779#[test]
780fn test_uniformity() {
781    use rand::{Rand, thread_rng};
782    let rng = &mut thread_rng();
783    let size = 64;
784    let mut hists: Vec<std::collections::HashMap<usize, f64>> = vec![std::collections::HashMap::new(); size];
785    let permutation = IntegerPermutation::new(size);
786    let mut router = AsWaksmanRoute::new(&permutation);
787    let num_switches = router.get_number_of_gates();
788    let num_trials = 1000000;
789    for _ in 0..num_trials {
790        let switches: Vec<bool> = (0..num_switches).map(|_| rng.gen::<bool>()).collect();
791        router.assign_switches(&switches);
792        let shuffle = router.calculate_permutation();
793        for i in 0..size {
794            let subhist = &mut hists[i];
795            let routed_into = shuffle.elements[i];
796            if subhist.get(&routed_into).is_some() {
797                let e = subhist.get_mut(&routed_into).unwrap();
798                *e += 1.0;
799            } else {
800                subhist.insert(routed_into, 1.0);
801            }
802        }
803    }
804
805    let mut min_xi_squared = 10000000000f64;
806    let mut max_xi_squared = 0.0f64;
807
808    let mut max_idx = 0;
809    let mut min_idx = 0;
810
811    for i in 0..size {
812        let subhist = &hists[i];
813        let mut xi_squared = 0.0f64;
814        let expected = (num_trials as f64) / (size as f64);
815        for (_, v) in subhist.iter() {
816            xi_squared += (v - expected)*(v - expected)/expected;
817        }
818
819        if xi_squared > max_xi_squared {
820            max_xi_squared = xi_squared;
821            max_idx = i;
822        }
823
824        if xi_squared < min_xi_squared {
825            min_xi_squared = xi_squared;
826            min_idx = i;
827        }
828    }
829
830    println!("Max XI^2 = {} for bin {}", max_xi_squared, max_idx);
831    println!("Min XI^2 = {} for bin {}", min_xi_squared, min_idx);
832
833    // let norm = num_trials as f64;
834    // let mut reference_cdf = vec![0.0f64; size];
835    // for i in 0..size {
836    //     reference_cdf[i] = ((i+1) as f64)/(size as f64);
837    // }
838    // assert_eq!(reference_cdf[size-1], 1.0f64);
839
840    // let mut global_max = 0.0f64;
841
842    // for i in 0..size {
843    //     let mut normalized = vec![0.0f64; size];
844    //     let subhist = &hists[i];
845    //     for (k, v) in subhist.iter() {
846    //         normalized[*k] = v / norm;
847    //     }
848    //     let mut cdf = vec![0.0f64; size];
849    //     cdf[0] = normalized[0];
850    //     for k in 1..size {
851    //         cdf[k] = normalized[k] + cdf[k-1]; 
852    //     }
853    //     assert!(cdf[size-1] >= 0.99999);
854    //     let mut max = 0.0f64;
855    //     for k in 0..size {
856    //         if cdf[k] <= 0.01f64 {
857    //             panic!();
858    //         }
859    //         let val = (cdf[k] - reference_cdf[k]).abs();
860    //         if val > max {
861    //             max = val;
862    //         }
863    //     }
864    //     if max > global_max {
865    //         global_max = max;
866    //     }
867    // }
868    // // this is max cdf difference for Kolmogorov-Smirnov for sample size 1000000
869    // // and P = 0.99
870    // let alpha = 0.0015f64;
871    // assert!(global_max < alpha);
872}