onlinecode/
equations.rs

1//! Alternative data structures for online code decoding
2//!
3//! Reimplementation of the BipartiteGraph structure using tables
4//! (vectors), indices, etc. to avoid dynamically allocating explicit
5//! node and edge structures.
6
7
8use std::collections::{HashSet,HashMap};
9use std::collections::VecDeque;
10
11use crate::compat::*;
12
13// There's a degree of duplication in the structures below.
14//
15// An Equation represents a check block, eg:
16//
17// Msg1 + Msg6 + Aux3 = data_block_4
18//
19// Upon receipt, we check whether any of the variables on the left
20// side are known, and if so, we substitute them in. Then:
21//
22// case 1: all variables are known
23//
24// In this case, the equation provides no new information, so it is
25// discarded.
26//
27// case 2: all but one variables are known
28//
29// substitute in the knowns by subtracting them from both sides, eg:
30//
31// Msg6 = data_block_4 - Msg1 - Aux3
32//
33// This solves a new variable. In this case, we would mark Msg6 as
34// solved and queue up that variable for checking to see if makes any
35// other equations solvable.
36//
37// default:
38//
39// Substitute in any knowns and insert equation into the system
40//
41
42type EqID = usize;
43type VarID = usize;
44
45/*
46
47pub struct Equation {
48    // As more variables become solved (case 2 above), they can be
49    // substituted into other equations, so values (variable ids) will
50    // move from the HashSet on the left to the Vec on the right.
51    // Eventually, there will only be a single variable on the left
52    pub lhs : HashSet<VarID>,       // unknown variables
53    pub rhs : Vec<VarID>,           // expansion (data block [+vars])
54    // Note: we don't store block IDs explicitly, but it is part of
55    // the expansion on the right. The block ID can be inferred by the
56    // index of the equation in the table.
57
58    // count_unsolveds is not strictly necessary since we can simply
59    // count how many items are in the lhs HashSet ...
60    // count_unsolveds : usize,
61}
62
63// We also need to map variables onto the equations in which they
64// appear.
65pub struct Variable {
66    pub equations : HashSet<EqID>,
67}
68 */
69
70// Further note on variable, equation and block IDs... 
71//
72// Note that before we can accept check blocks, we have to set up
73// equations for the auxiliary mappings. Since they're explicitly set
74// up at the start, we have to reserve `ablocks` entries in the
75// equations array.
76//
77// Also, the equations in the auxiliary mapping are slightly different
78// from equations derived from check blocks...
79//
80// A check block equation:
81//
82// Msg1 + Msg6 + Aux3 = data_block_4
83//
84// An auxiliary block equation:
85//
86// Msg1 + Msg4 + Msg7 + ... = Aux1
87//
88// We can rewrite this by bringing Aux1 to the left, since at the
89// start of decoding, its value is unknown.
90//
91// Msg1 + Msg4 + Msg7 + ... + Aux1 = 0
92//
93// This zero on the right can be represented as a virtual BlockID 0.
94//
95// If we're not explicitly storing a block ID within an Equation, we
96// have to take care to:
97//
98// * check whether the index of the equation ID is less than
99//   `ablocks`, and if it is, treat the data block in the expansion on
100//   the right hand side as a block of zeros.
101//
102// * subtract `ablocks` from equation IDs to get the correct data
103//   block to use when expanding a check block solution
104//
105// One more thing ...
106//
107// Auxiliary blocks are encoded as the first `ablocks` equations
108// entered into the system, but as the last `ablocks` *variables*.
109
110#[derive(Debug)]
111pub enum EquationType {
112    // dropping AlreadySolved in favour of using Option<T>
113    // AlreadySolved,
114    // Solved: single variable on lhs, expansion on rhs
115    Solved(VarID, Vec<VarID>),
116    // Unsolved: several variables on lhs, expansion on rhs
117    Unsolved(HashSet<VarID>, Vec<VarID>),
118}
119pub enum VariableType {
120    Solved(EqID),
121    Unsolved(HashSet<EqID>)
122}
123
124use std::iter::Iterator;
125
126impl EquationType {
127    fn iter(&self) -> impl Iterator<Item = &'_ VarID> {
128        match self {
129            Self::Unsolved(hash,_) => { hash.iter() },
130            _ => {
131                panic!("Can't insert into already-solved variable");
132            }
133            
134        }
135    }
136}
137
138impl VariableType {
139    fn insert(&mut self, var : VarID) {
140        match self {
141            Self::Solved(_) => {
142                panic!("Can't insert into already-solved variable");
143            },
144            Self::Unsolved(hash) => {
145                hash.insert(var);
146            }
147        }
148    }
149    fn iter(&self) -> impl Iterator<Item = &'_ EqID> {
150        match self {
151            Self::Solved(_) => {
152                panic!("Can't insert into already-solved variable");
153            },
154            Self::Unsolved(ref hash) => {
155                hash.iter()
156            }
157        }
158    }
159}
160
161pub struct Decoder {
162    pub mblocks : usize,	// message blocks
163    pub ablocks : usize,        // auxiliary blocks
164    pub coblocks: usize,        // mblocks + ablocks
165
166    // These two arrays effectively implement the bipartite graph
167    // logic. Both types come in solved and unsolved flavours.
168
169    variables : Vec<VariableType>,
170    equations : Vec<EquationType>,
171
172    // maintain a list of variables that need visiting when an
173    // equation becomes solved
174    pub stack : VecDeque<VarID>,
175
176    // count only unsolved mblocks? That makes sense.
177    pub count_unsolveds : usize,
178    pub done : bool,
179}
180
181// to-do
182//
183// at the cost of a little more space, we can use an enum to
184// distinguish between unsolved variables and solved ones.
185//
186// Something like:
187//
188// enum Variable {
189//   Solved(EqID),
190//   Unsolved(HashSet<EqID>)
191// }
192
193
194impl Decoder {
195
196    pub fn new(mblocks : usize, ablocks : usize) -> Self {
197
198        // variables table is fixed size
199        let coblocks = mblocks + ablocks;
200        let mut variables = Vec::with_capacity(coblocks);
201
202        // allow headroom of ~11% in equations table. If space is
203        // tight, we can reduce this ...
204        let headroom = mblocks / 9;
205        let mut equations = Vec::with_capacity(coblocks + headroom);
206
207        // set up Variable struct for each mblock, ablock
208        for _ in 0..coblocks {
209            variables.push(
210                //    Variable { equations : HashSet::<EqID>::new() }
211                VariableType::Unsolved(HashSet::<EqID>::new())
212            );
213        }
214
215        let count_unsolveds = mblocks;
216
217        Self {
218            mblocks, ablocks, coblocks, variables, equations,
219            count_unsolveds,
220            done : false,
221            stack : VecDeque::with_capacity(40)
222        }
223    }
224
225    pub fn add_aux_equations(&mut self, map : &AuxMapping) {
226
227        eprintln!("Decoder adding {} aux blocks", map.aux_to_mblocks.len());
228
229        for (ablock, list) in map.aux_to_mblocks.iter().enumerate() {
230
231            // let mut equation = Equation { }
232            let mut lhs = HashSet::<VarID>::new();
233            let mut rhs = Vec::<VarID>::new();
234
235            // note: the way that auxiliary blocks are generated means
236            // that it is possible (though unlikely) that one is
237            // generated with no links to any message blocks. Strictly
238            // speaking, we should be storing it as "solved" (with
239            // zero value), but since no variables refer to it anyway,
240            // it doesn't affect the algorithm in any way.
241            
242            lhs.insert(self.mblocks + ablock);
243            for mblock in list.iter() {
244                lhs.insert(*mblock);
245                self.variables[*mblock].insert(ablock);
246            }
247            // link aux variable to equation
248            self.variables[self.mblocks + ablock].insert(ablock);
249            // store the equation
250            self.equations.push(EquationType::Unsolved(lhs,rhs));
251        }
252    }
253
254
255    /// Add check block to graph. 
256    pub fn add_check_equation(&mut self,
257                          coblocks : Vec<VarID>, step : bool)
258                          -> (bool, usize, Option<Vec<VarID>>) {
259
260        // let mut unknowns = coblocks.len();
261        // let mut equation = Equation {
262        //         lhs : HashSet::<VarID>::new(),
263        //         rhs : Vec::<VarID>::new(),
264        // };
265
266        // if we're already done, just return
267        if self.done { return (true, self.stack.len(), None) }
268
269        // substitute all previously-solved variables into the
270        // equation
271        let equation = self.new_equation(coblocks);
272
273        if equation.is_none() {
274            // eprintln!("add_check_equation classified as None");
275            return (false, self.stack.len(), None)
276        }
277
278        let equation = equation.unwrap();
279
280        match equation {
281            EquationType::Solved(var,_) => {
282                // insert as newly-solved
283                // eprintln!("add_check_equation classified as Solved");
284                let eq_position = self.equations.len();
285                self.equations.push(equation);
286                self.variables[var].insert(eq_position);
287
288                // search graph starting from newly solved variable
289                self.stack.push_back(var);
290
291                // At this point, we've added a solved *equation*, and
292                // linked the newly-solved variable to it. What
293                // cascade() does is to check if the variable can be
294                // substituted into other equations. It's responsible
295                // for marking the *variable* as solved afterwards.
296                self.cascade(step)
297            },
298            EquationType::Unsolved(ref hash,_) => {
299                // insert as unsolved equation
300                // eprintln!("add_check_equation classified as Unsolved");
301                let eq_position = self.equations.len();
302
303                // link unsolved variables to new equation
304                for var in equation.iter() {
305                    self.variables[*var].insert(eq_position);
306                }
307
308                // store the equation
309                self.equations.push(equation);
310                (false, self.stack.len(), None)
311            }
312        }
313
314    }
315
316    // If a variable has been newly solved, it's possible that it now
317    // allows other equations to be solved.
318    //
319    // We follow from the variable to the equations that it appears
320    // in. First, we do substitution, which involves moving the
321    // variable from the lhs to the rhs of the equation. Then we break
322    // the link from the variable to the equation, since it is no
323    // longer an unknown variable.
324    //
325    // If in the process of updating an equation we find that the
326    // number of unknowns becomes 1, then the remaining variable on
327    // the lhs of the equation also becomes solved. We queue up that
328    // variable to have the above step repeated on it.
329    //
330    // This should work equally well for solved message blocks and
331    // auxiliary blocks.
332
333    pub fn cascade(&mut self, stepping : bool)
334               -> (bool, usize, Option<Vec<VarID>>) {
335
336        // return list of newly-solved variables
337        let mut newly_solved = Vec::<VarID>::new();
338
339        while let Some(var) = self.stack.pop_front() {
340
341            if let VariableType::Solved(_) = self.variables[var] {
342                continue;
343                //panic!("variable {} already marked as solved",var)
344            }
345            
346            // var is always a solved variable at this point
347            newly_solved.push(var);
348
349            // we know that the variable is solved, but we don't know
350            // which equation solved it. After scanning the list of
351            // equations that the variable appears in, we should find
352            // exactly one equation that matches.
353            let mut found_solved = 0;
354            let mut solved_equation = 0;
355
356            // eprintln!("Propagating solved variable {} into equations", var);
357
358            for eq_id in self.variables[var].iter() {
359                // eprintln!("Propagating into eq {}: {:?}",
360                // *eq_id, self.equations[*eq_id]);
361                match &mut self.equations[*eq_id] {
362
363                    EquationType::Solved(v,rhs) => {
364                        assert_eq!(*v, var);
365                        // eprintln!("var {} solved by equation {}",var,*eq_id);
366                        found_solved += 1;
367                        solved_equation = *eq_id;
368                    },
369                    EquationType::Unsolved(hash, rhs) => {
370                        // Need to do a little dance here. If the
371                        // equation would become solved, we need to
372                        // replace it with a ::Solved flavour. The
373                        // borrow checker will not be happy, though. I
374                        // may need to use an if let form instead of a
375                        // match.
376
377                        // Anyway, the gist is...
378                        //
379                        // if there are two entries in hash, delete
380                        // var from it, placing it into the rhs, and
381                        // use the remaining variable in the hash to
382                        // create a Solved EquationType (using
383                        // remaining variable and updated rhs). Also
384                        // add the remaining variable to the stack,
385                        // since it is now newly-solved. Then write
386                        // the new Solved flavour back to the table.
387                        //
388                        // or else ... remove var from hash and append
389                        // it to rhs
390
391                        match hash.len() {
392                            2 => {
393                                let mut vars = hash.iter();
394                                let mut other = vars.next().unwrap();
395                                if *other == var {
396                                    other = vars.next().unwrap()
397                                }
398                                // eprintln!("Eq {} had {},{} on lhs",
399                                //           eq_id, var, other);
400                                rhs.push(var);
401                                self.stack.push_back(*other);
402                                // the contentious bit (I think to_vec() copies)
403                                self.equations[*eq_id] =
404                                    EquationType::Solved(*other,rhs.to_vec());
405                            },
406                            1 => {
407                                // This case shouldn't happen since
408                                // unsolved equation with a single
409                                // unknown should have been replaced
410                                // with a solved variant.
411
412                                // Actually, it can happen if this is
413                                // an aux block that didn't get linked
414                                // to any message blocks, so refine
415                                // the test:
416                                if (*eq_id >= self.ablocks) {
417                                    panic!("Internal Error: Unsolved eq {} wrong",
418                                           eq_id);
419                                }
420                            },
421                            0 => { panic!("Equation had no lhs"); },
422                            _ => { 
423                                hash.remove(&var);
424                                rhs.push(var);
425                            }
426                        };
427                    }
428                }
429                // eprintln!("After propagating var {} into eq {}: {:?}",
430                //           var, *eq_id, self.equations[*eq_id]);
431
432            } // for eq_id in self.variables.iter()
433
434            // variable also need to be marked as solved ...
435            // debug_assert_eq!(found_solved, 1);
436            match &self.variables[var] {
437                VariableType::Solved(_eq_id) => {
438                   // panic!("Internal error: var {} was solved twice", var);
439                },
440                VariableType::Unsolved(_hash) => {
441                    self.variables[var] = VariableType::Solved(solved_equation)
442                },
443            }
444
445            // update self's count of unsolved message blocks
446            if var < self.mblocks {
447                self.count_unsolveds -= 1;
448                if self.count_unsolveds == 0 {
449                    self.done = true;
450                    break;
451                }
452            }
453
454            // I should probably also be returning the number of items
455            // left in the stack if I want single-stepping to be
456            // useful.
457            //
458            // When single-stepping, call add_check_equation() once
459            // with the stepping option = true. Then while the second
460            // return value (the number of variables pending) is >0,
461            // call cascade(). This should solve equations/variables
462            // in the same order as a non-stepping call to
463            // add_check_equation().
464            if stepping { break }
465
466        }
467
468        ( self.done, self.stack.len(), Some(newly_solved) )
469    }
470
471    // There's a back-and-forth between variables and equations:
472    //
473    // we detect that a new or existing equation only has one unknown
474    // on the lhs. That counts as solving the equation (and hence, a
475    // variable).
476    //
477    // A newly-solved variable is substituted into all equations that
478    // it appears in. This may cause one of those equation to become
479    // solved.
480
481    // There's also the case where a new check block equation comes in
482    // and it contains only a single unknown.
483
484    // I want to have a nice, clean division among the three pieces of
485    // code above.
486
487    // I was thinking of making an enum for solved/unsolved variables.
488    // It might also be a good idea to do the same for equations.
489
490    // think of it in terms of forward/back propagation...
491    //
492    // substituting a newly-solved variable into all equations it
493    // appears in is like forward propagation
494    //
495    // if that equation becomes solved, then propagating the new info
496    // deriving from it is kind of like back propagation (from
497    // equations to variables)
498
499    // on enum describing equation ... besides solved and unsolved, we
500    // also have a third class, which represents "no new information".
501    // These correspond to a number of unknowns of 0 (no new info), 1
502    // (solved) and >1 (unsolved) in the code to add a new check block
503    // equation. The case of 0 unknowns (over-specified/idempotent
504    // equations) *could* be passed back to the caller to allow them
505    // to verify that all the received check blocks are consistent
506    // with each other, although it's easiest to just drop them and
507    // assume that the sender is working correctly.
508
509
510    /// Take a list of variables (block IDs) that comprise a check
511    /// block and substitute any already-solved ones in, returning
512    /// some kind of Option<EquationType> depending on the number of
513    /// unknowns:
514    ///
515    /// * 0 unknowns &rarr; None (already solved)
516    /// * 1 unknown &rarr; Some(Solved(VarID, Vec<VarID))
517    /// * 1+ unknowns &rarr; Some(Unsolved(HashSet<VarID>, Vec<VarID>))
518
519
520    // Hmm... might be better to get rid of already-solved enum type.
521    // They will never get stored in the graph, and is only needed to
522    // decide whether to drop a new check block. For that reason, I
523    // think I should return Option<EquationType> here instead
524    
525    pub fn new_equation(&self, vars : Vec<VarID>) -> Option<EquationType> {
526
527        // Use two passes. First determine how many unsolved vars
528        // there are. Then, depending on whether it's 0, 1 or more
529        // than 1, return different enum
530        
531        let mut count_unsolved = 0;
532        for var in vars.iter() {
533            // check to see if vars are solved
534            match self.variables[*var] {
535                VariableType::Solved(_)   => {},
536                VariableType::Unsolved(_) => {
537                    count_unsolved += 1;
538                    // if count_unsolved > 1 { break }
539                }
540            }
541        }
542
543        match count_unsolved {
544            0 => { return None },
545            1 => {
546                let mut single_unsolved = 0;
547                let mut rhs = Vec::new();
548                for var in vars.iter() {
549                    match self.variables[*var] {
550                        VariableType::Solved(_)   => {
551                            rhs.push(*var)
552                        },
553                        VariableType::Unsolved(_) => {
554                            single_unsolved = *var;
555                        }
556                    }
557                }
558                return Some(EquationType::Solved(single_unsolved, rhs))
559            },
560            _ => {
561                let mut lhs = HashSet::new();
562                let mut rhs = Vec::new();
563                for var in vars.iter() {
564                    match self.variables[*var] {
565                        VariableType::Solved(_)   => {
566                            rhs.push(*var)
567                        },
568                        VariableType::Unsolved(_) => {
569                            lhs.insert(*var);
570                        }
571                    }
572                }
573                return Some(EquationType::Unsolved(lhs,rhs))
574            }
575        }
576        
577    }
578
579    pub fn var_solution(&self, var : VarID)
580                    -> Option<(Option<usize>, Vec<EqID>)> {
581
582        // actually ... we don't need initial, do we? Isn't the
583        // expansion for the variable completely on the RHS of the
584        // equation? No, it's not...
585        
586        // var -> eq, and eq contains rhs (and a copy of var)
587        //
588        // if eq < ablocks, it represents an auxiliary block, so the
589        // initial block should be zero/not-present
590        //
591        // if eq >= ablocks, then it has an implicitly associated
592        // check block, which is eq - ablocks
593
594        // Or should I be checking whether var < mblocks instead?
595        // No, I should test eq < ablocks.
596        //
597        // Variables in general can be solved by auxiliary equations
598        // or check equations, so we look at the equation number, not
599        // the variable number to determine the correct expansion.
600
601        let mut initial = None;
602        
603        match self.variables[var] {
604            VariableType::Solved(eq_id) => {
605                
606                // if var < self.mblocks {
607                //     initial = Some(eq_id);
608                // }
609                match &self.equations[eq_id] {
610                    EquationType::Solved(lhs,rhs) => {
611                        debug_assert_eq!(*lhs, var); // internal error
612                        // let mut vec = Vec::new();
613                        if eq_id >= self.ablocks {
614                            // variable solved by check block
615                            // vec.push(eq_id - self.ablocks);
616                            initial = Some(eq_id - self.ablocks);
617                        }
618                        // vec.extend(rhs.to_vec());
619                        Some((initial, rhs.to_vec()))
620                    },
621                    _ => {
622                        // caller error
623                        panic!("eq[{}] not marked as solved", eq_id)
624                    }
625                }
626            },
627            _ => { None }
628        }
629    }
630}
631    
632
633#[cfg(test)]
634mod test_decoder {
635
636    use super::*;
637
638    #[test]
639    fn single_unknown_no_aux() {
640        let mut d = Decoder::new(1,0);
641        // check block with a single unknown
642        let (done,pending,solved)
643            = d.add_check_equation(vec![0usize], false);
644
645        assert_eq!(done, true); // last block was solved
646        assert_eq!(pending, 0); // cascade didn't solve extra
647        assert_eq!(solved.is_none(), false); // got Some(Vec)
648
649        // unwrap the vector of solved vars and ensure 0 is the only
650        // item in it
651        let solved = solved.unwrap();
652        assert_eq!(solved[0], 0);
653        assert_eq!(solved.len(), 1);
654    }
655    
656    #[test]
657    fn two_unknowns_no_aux_no_step() {
658        let mut d = Decoder::new(2,0);
659        // check block with two unknowns (blocks 0, 1)
660        let (done,pending,solved)
661            = d.add_check_equation(vec![0usize,1], false);
662
663        assert_eq!(done, false); // nothing solved
664        assert_eq!(pending, 0); // cascade didn't solve extra
665        assert!(solved.is_none()); // None instead of Some(Vec)
666
667        // a check block with either 0 or 1 by itself should
668        // solve both due to cascade.
669        let (done,pending,solved)
670            = d.add_check_equation(vec![0usize], false);
671
672        assert_eq!(done, true); // all solved
673        assert_eq!(pending, 0); // nothing left in pending
674        assert!(!solved.is_none()); // Some(Vec)
675
676        // unwrap the vector of solved vars. It should have [0,1] in
677        // that order
678
679        let solved = solved.unwrap();
680        assert_eq!(solved, [0,1]);
681        // assert_eq!(solved.len(), 2);
682
683        // confirm that solutions are actually correct
684        // var[0] -> eq[1], containing (1, empty vec)
685        // (interpretation: var_0 = check_block_1, no other xors)
686
687        match d.variables[0] {
688            VariableType::Solved(var0_eq) => {
689                assert_eq!(var0_eq, 1);
690                match &d.equations[var0_eq] {
691                    EquationType::Solved(lhs,rhs) => {
692                        assert_eq!(*lhs, 0); // solves variable 0
693                        assert_eq!(*rhs, vec![]);
694                    },
695                    _ => { panic!("eq[1] not marked as solved") }
696                }
697            },
698            _ => { panic!("var[0] not marked as solved") }
699        }
700
701        // var[1] -> eq[0], containing (0, vec![0])
702        // (interpretation: var_1 = check_block_0 xor var_0)
703
704        match d.variables[1] {
705            VariableType::Solved(var1_eq) => {
706                assert_eq!(var1_eq, 0);
707                match &d.equations[var1_eq] {
708                    EquationType::Solved(lhs,rhs) => {
709                        assert_eq!(*lhs, 1); // solves variable 1
710                        assert_eq!(*rhs, vec![0]);
711                    },
712                    _ => { panic!("eq[0] not marked as solved") }
713                }
714            },
715            _ => { panic!("var[1] not marked as solved") }
716        }
717    }
718
719
720    // make sure that solving via auxiliary blocks works as expected
721    //
722    // Setup (4 cases):
723    //
724    // let aux_0 = msg_0
725    // let chk_0 = (msg_0 or aux_0) + msg_1
726    // let chk_1 = (msg_0 or aux_0)
727    //
728    // If we receive a new check block solving either aux_0 or msg_0
729    // we should be able to solve msg_1.
730    //
731    // Code these as separate tests
732
733    // Case 1:
734    // chk_0 = msg_0 + msg_1
735    // chk_1 = msg_0
736    //    
737    // Doesn't use aux for solution (so it's like two unknowns test as
738    // above), but does check that add_aux_equations() works correctly
739    // and that array indices in solutions are still correct.
740    // #[test]
741    fn solve_via_aux_case_1() {
742
743        // system with two unknown message blocks (0, 1) and one aux
744        let mut d = Decoder::new(2,1);
745
746        // first, add auxiliary mapping (aux -*-> msg)
747        let aux_map = AuxMapping { aux_to_mblocks : vec![vec![0]] };
748        d.add_aux_equations(&aux_map);
749
750        // Test that system is set up as expected. We shouldn't need
751        // to repeat this part of the testing for other cases.
752
753        match &d.variables[0] {
754            VariableType::Unsolved(hash) => {
755                assert_eq!(hash.len(), 1);
756                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
757                match &d.equations[0] {
758                    EquationType::Unsolved(hash, vec) => {
759                        assert_eq!(hash.len(), 2);
760                        assert!(hash.contains(&0)); // msg0
761                        assert!(hash.contains(&2)); // aux0
762                        assert_eq!(vec.len(), 0);   // rhs empty
763                    },
764                    _ => { panic!("aux0 equation wrongly set as solved")
765                    }
766                }
767            },
768            _ => { panic!("msg0 wrongly set as solved") }
769        }
770        match &d.variables[1] {
771            VariableType::Unsolved(hash) => {
772                assert_eq!(hash.len(), 0);
773            },
774            _ => { panic!("msg1 wrongly set as solved") }
775        }
776        match &d.variables[2] {
777            VariableType::Unsolved(hash) => {
778                assert_eq!(hash.len(), 1);
779                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
780                // contents of equation already checked above
781            },
782            _ => { panic!("aux0 wrongly set as solved") }
783        }
784
785        // add chk0 = msg0 + msg1
786        let (done,pending,solved)
787            = d.add_check_equation(vec![0usize,1], false);
788        assert_eq!(done, false); // nothing solved
789        assert_eq!(pending, 0); // cascade didn't solve extra
790        assert!(solved.is_none()); // None instead of Some(Vec)
791
792        // add chk1 = msg0
793        let (done,pending,solved)
794            = d.add_check_equation(vec![0usize], false);
795        assert_eq!(done, true); // all solved
796
797        // Unfortunately, HashSet::iter() returns keys in
798        // nondeterministic order, so testing is not so
799        // straightforward. 
800        
801        assert_eq!(pending, 1); // aux should be pending?
802        // Actually, it depends on how hash keys are traversed. The
803        // order of keys from iterating HashMap/HashSet seems to be
804        // non-deterministic.
805
806        assert!(!solved.is_none()); // Some(Vec)
807        let solved = solved.unwrap();
808
809        // order of solutions should be msg0, msg1
810        assert_eq!(solved, [0,1]);
811
812        // check contents of solved variables ...
813        //
814        // There's a fair bit of boilerplate code involved in
815        // following variables to equations and then destructuring
816        // things. I'll add a new method to help with that...
817
818        // the method needs to return an optional equation number,
819        // which corresponds to Some(received check block) or None in
820        // the case that 
821        
822        let rhs = d.var_solution(0).unwrap();
823
824        // Some(1) means check block 1
825        // the empty vector means no solved variables to xor in
826        assert_eq!(rhs, (Some(1),vec![]));
827        
828    }
829
830    // Rewrite test cases above to eliminate nondeterminism
831    //
832    // Forget about msg1 completely
833    //
834    // R1: (aux) aux0 = msg0
835    // R2a: chk0 = aux0
836    // R2b: chk0 = msg0
837
838    #[test]
839    fn deterministic_aux_case_1() {
840
841        // system with one unknown message blocks 0 and one aux
842        let mut d = Decoder::new(1,1);
843
844        // first, add auxiliary mapping (aux -*-> msg)
845        let aux_map = AuxMapping { aux_to_mblocks : vec![vec![0]] };
846        d.add_aux_equations(&aux_map);
847
848        // Test that system is set up as expected. We shouldn't need
849        // to repeat this part of the testing for other cases.
850
851        match &d.variables[0] {
852            VariableType::Unsolved(hash) => {
853                assert_eq!(hash.len(), 1);
854                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
855                match &d.equations[0] {
856                    EquationType::Unsolved(hash, vec) => {
857                        assert_eq!(hash.len(), 2);
858                        assert!(hash.contains(&0)); // msg0
859                        assert!(hash.contains(&1)); // aux0
860                        assert_eq!(vec.len(), 0);   // rhs empty
861                    },
862                    _ => { panic!("aux0 equation wrongly set as solved")
863                    }
864                }
865            },
866            _ => { panic!("msg0 wrongly set as solved") }
867        }
868        match &d.variables[1] {
869            VariableType::Unsolved(hash) => {
870                assert_eq!(hash.len(), 1);
871                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
872                // contents of equation already checked above
873            },
874            _ => { panic!("aux0 wrongly set as solved") }
875        }
876
877        // add chk0 = aux0
878        let (done,pending,solved)
879            = d.add_check_equation(vec![1usize], false);
880        assert_eq!(done, true); // everything solved
881        assert_eq!(pending, 0); // cascade didn't solve extra
882        assert!(!solved.is_none()); // Some(Vec)
883
884        let solved = solved.unwrap();
885
886        // order of solutions should be aux0, msg0
887        assert_eq!(solved, [1,0]);
888
889        // test solution of aux block first (values in the returned
890        // array are all check block IDs)
891        let rhs = d.var_solution(1).unwrap();
892
893        assert_eq!(rhs, (Some(0), vec![])); // = chk0
894
895        // using the above, we can set chk1 (ie, aux0) = chk0
896
897        // That then tallies with the solution msg1 = chk1 (=chk0)
898        let rhs = d.var_solution(0).unwrap();
899
900        assert_eq!(rhs, (None, vec![1]));
901        
902    }
903    
904    #[test]
905    fn deterministic_aux_case_2() {
906
907        // system with one unknown message blocks 0 and one aux
908        let mut d = Decoder::new(1,1);
909
910        // first, add auxiliary mapping (aux -*-> msg)
911        let aux_map = AuxMapping { aux_to_mblocks : vec![vec![0]] };
912        d.add_aux_equations(&aux_map);
913
914        // Test that system is set up as expected. We shouldn't need
915        // to repeat this part of the testing for other cases.
916
917        match &d.variables[0] {
918            VariableType::Unsolved(hash) => {
919                assert_eq!(hash.len(), 1);
920                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
921                match &d.equations[0] {
922                    EquationType::Unsolved(hash, vec) => {
923                        assert_eq!(hash.len(), 2);
924                        assert!(hash.contains(&0)); // msg0
925                        assert!(hash.contains(&1)); // aux0
926                        assert_eq!(vec.len(), 0);   // rhs empty
927                    },
928                    _ => { panic!("aux0 equation wrongly set as solved")
929                    }
930                }
931            },
932            _ => { panic!("msg0 wrongly set as solved") }
933        }
934        match &d.variables[1] {
935            VariableType::Unsolved(hash) => {
936                assert_eq!(hash.len(), 1);
937                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
938                // contents of equation already checked above
939            },
940            _ => { panic!("aux0 wrongly set as solved") }
941        }
942
943        // add chk0 = msg0
944        let (done,pending,solved)
945            = d.add_check_equation(vec![0usize], false);
946        assert_eq!(done, true); // everything solved
947        assert_eq!(pending, 1); // cascade detected done, w/o solving aux
948        assert!(!solved.is_none()); // Some(Vec)
949
950        let solved = solved.unwrap();
951
952        // order of solutions should be msg0
953        assert_eq!(solved, [0]);
954
955        // test solution of aux block first (values in the returned
956        // array are all check block IDs)
957        let rhs = d.var_solution(0).unwrap();
958
959        assert_eq!(rhs,(Some(0), vec![])); // = chk0
960        
961    }
962    
963    // The first deterministic case above showed an aux block solving
964    // a msg block, but because of how `done` is handled, the second
965    // case didn't fully show what happens then a msg block gets
966    // solved first. Extend that by adding in a second, unrelated
967    // message block.
968    
969    #[test]
970    fn deterministic_aux_case_3() {
971
972        // system with one unknown message blocks 0 and one aux
973        let mut d = Decoder::new(2,1);
974
975        // first, add auxiliary mapping (aux -*-> msg)
976        let aux_map = AuxMapping { aux_to_mblocks : vec![vec![0]] };
977        d.add_aux_equations(&aux_map);
978
979        // Test that system is set up as expected. We shouldn't need
980        // to repeat this part of the testing for other cases.
981
982        match &d.variables[0] {
983            VariableType::Unsolved(hash) => {
984                assert_eq!(hash.len(), 1);
985                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
986                match &d.equations[0] {
987                    EquationType::Unsolved(hash, vec) => {
988                        assert_eq!(hash.len(), 2);
989                        assert!(hash.contains(&0)); // msg0
990                        assert!(hash.contains(&2)); // aux0
991                        assert_eq!(vec.len(), 0);   // rhs empty
992                    },
993                    _ => { panic!("aux0 equation wrongly set as solved")
994                    }
995                }
996            },
997            _ => { panic!("msg0 wrongly set as solved") }
998        }
999        // check aux block
1000        match &d.variables[2] {
1001            VariableType::Unsolved(hash) => {
1002                assert_eq!(hash.len(), 1);
1003                assert!(hash.contains(&0)); // eq0 = aux0 + msg0
1004                // contents of equation already checked above
1005            },
1006            _ => { panic!("aux0 wrongly set as solved") }
1007        }
1008
1009        // add chk0 = msg0
1010        let (done,pending,solved)
1011            = d.add_check_equation(vec![0usize], false);
1012        assert_eq!(done, false); // msg1 not solved yet
1013        assert_eq!(pending, 0); // cascade doesn't do done
1014        assert!(!solved.is_none()); // Some(Vec)
1015
1016        let solved = solved.unwrap();
1017
1018        // order of solutions should be msg0,aux0
1019        assert_eq!(solved, [0,2]);
1020
1021        // test solution of aux block first (values in the returned
1022        // array are all check block IDs)
1023        let rhs = d.var_solution(0).unwrap();
1024
1025        assert_eq!(rhs, (Some(0), vec![]) ); // = chk0
1026
1027        // aux0 should be solved by msg0
1028        let rhs = d.var_solution(2).unwrap();
1029
1030        assert_eq!(rhs, (None, vec![0])); // = chk0
1031        
1032    }
1033
1034
1035    // Random testing
1036    //
1037    // The Online Code algorithm specifies a particular construction
1038    // of auxiliary blocks and check blocks. However, we can simplify
1039    // that for the purposes of random testing on the decoder. It will
1040    // take longer to fully decode the original message, but it should
1041    // terminate at some point.
1042    //
1043    // There are two things to test in this mode:
1044    //
1045    // * that new solutions are composed only of already-solved
1046    //   variables; and
1047    //
1048    // * when we do the XORs that the decoder tells us to do, we
1049    //   actually recover the correct values
1050    //
1051    // If this fails, it probably won't be easy to find out why it
1052    // failed, and it won't be possible to reproduce the error. On the
1053    // other hand, if it succeeds, we can have very high confidence
1054    // that the decoder is bug-free.
1055
1056    // Will need some support routines which can be considered toy
1057    // versions of the proper Online Code algorithm
1058
1059    use rand::{Rng,thread_rng};
1060
1061    fn random_message(size : usize) -> Vec<usize> {
1062        let mut rng = thread_rng();
1063        let mut vec = Vec::with_capacity(size);
1064        for _ in 0..size {
1065            vec.push(rng.gen())
1066        }
1067        vec
1068    }
1069
1070    use crate::floyd::*;
1071
1072    fn generate_check_block(max_picks : usize, from : usize)
1073                            -> HashSet<usize> {
1074
1075        let mut rng = thread_rng();
1076        let how_many = rng.gen_range(1..max_picks + 1);
1077        assert!(how_many > 0);
1078        assert!(how_many <= max_picks);
1079
1080        floyd_usize(&mut rng, how_many, from)
1081    }
1082
1083    fn hashset_to_vec(set : &HashSet<usize>) -> Vec<usize> {
1084        set.iter().cloned().collect::<Vec<_>>()
1085    }
1086
1087    // ablocks each comprising a random selection of `m_per_a` mblocks
1088    fn toy_aux_mapping(mblocks : usize, ablocks : usize, m_per_a : usize)
1089                       -> Vec<Vec<usize>> {
1090
1091        let mut rng = thread_rng();
1092        let mut vec = Vec::with_capacity(ablocks);
1093        for _ in 0..ablocks {
1094            let aux_map = floyd_usize(&mut rng, m_per_a, mblocks);
1095            vec.push(hashset_to_vec(&aux_map))
1096        }
1097        vec
1098    }
1099
1100    #[test]
1101    fn test_toy_codec() {
1102
1103        let mblocks = 100;
1104        let ablocks = 25;
1105        let m_per_a = 5;
1106        let max_picks = 8;      // max blocks to put in check block
1107
1108        // coding side
1109        //
1110        let mut message = random_message(mblocks);
1111
1112        // aux blocks stored at the end of message
1113        // let aux_blocks = Vec::with_capacity(ablocks);
1114
1115        // check_blocks can be shared between coder and decoder
1116        let mut check_blocks = Vec::<usize>::with_capacity(200);
1117
1118        let aux_mapping = toy_aux_mapping(mblocks, ablocks, m_per_a);
1119
1120        // calculate aux blocks and append them to message for easier
1121        // check block creation
1122        for aux_list in aux_mapping.iter() {
1123            let mut sum = 0usize;
1124            for mblock in aux_list {
1125                sum ^= message[*mblock];
1126            }
1127            message.push(sum);
1128        }
1129
1130        // decoding side
1131        let mut d = Decoder::new(mblocks, ablocks);
1132        d.add_aux_equations(& AuxMapping { aux_to_mblocks : aux_mapping });
1133
1134        // validate ordering of solutions
1135        let mut solvedp = vec![false ; mblocks + ablocks];
1136
1137        while !d.done {
1138
1139            let check_vec = hashset_to_vec(
1140                &generate_check_block(max_picks, mblocks + ablocks));
1141            let mut check_val = 0;
1142            for index in check_vec.iter() {
1143                check_val ^= message[*index]
1144            }
1145
1146            // eprintln!("Check block {} comprising: {:?}, value {}",
1147            //           check_blocks.len(), check_vec,  check_val);
1148
1149            // sender and receiver can end up disagreeing on what the
1150            // current check block number is if the receiver drops the
1151            // block because it provides no new info. To fix this,
1152            // I'll restrict changes to just the codec code here...
1153
1154            let mut useless = true;
1155            for maybe_known in check_vec.iter() {
1156                if !solvedp[*maybe_known] {
1157                    useless = false;
1158                    break;
1159                }
1160            }
1161            if useless {
1162                // eprintln!("Not storing a useless block");
1163                continue
1164            } else {
1165                check_blocks.push(check_val);
1166            }
1167
1168            let (done, pending, solved) =
1169                d.add_check_equation(check_vec, false);
1170
1171            if solved.is_none() {
1172                // see above for proper fix for "useless" check blocks
1173                // check_blocks.pop();
1174                continue
1175            }
1176
1177            let solved = solved.unwrap();
1178
1179            for var in solved.iter() {
1180                // the first, optional part of return is a check block
1181                let (chk, vars) = d.var_solution(*var).unwrap();
1182
1183                // eprintln!("Checking solution for var {}: ({:?},{:?})",
1184                //           *var, chk, vars);
1185
1186                let mut sum = match chk {
1187                    None => 0,
1188                    Some(x) => {
1189                        let value = check_blocks[x];
1190                        // eprintln!("Check {} value is {}", x, value);
1191                        value
1192                    }
1193                };
1194
1195                // the remaining values are variables
1196                for v in vars.iter() {
1197                    // make sure the variables were already solved
1198                    assert_eq!(solvedp[*v], true);
1199                    // that being the case, and because we check each
1200                    // new var result (below), this is allowed: (in
1201                    // practice, decoder will have its own structure)
1202
1203                    // eprintln!("adding message {} value {}",
1204                    //           *v, message[*v]);
1205                    sum ^= message[*v];
1206                }
1207
1208                // eprintln!("Solving var {} ({} remain unsolved)",
1209                //           *var, d.count_unsolveds);
1210
1211                // mark var as solved
1212                if solvedp[*var] {
1213                    panic!("Var {} was already marked as solved", *var);
1214                } else {
1215                    solvedp[*var] = true;
1216                }
1217
1218                // compare result to original
1219                assert_eq!(sum, message[*var]);
1220            }
1221
1222        }
1223    }
1224}