Skip to main content

libsurfer/translation/
basic_translators.rs

1use super::{TranslationPreference, ValueKind, VariableInfo};
2use crate::wave_container::{ScopeId, VarId, VariableMeta};
3
4use eyre::Result;
5use itertools::Itertools;
6use num::{One, Zero};
7use surfer_translation_types::{
8    BasicTranslator, NumericRange, VariableValue, check_vector_variable, extend_string,
9    kind_for_binary_representation, parse_value_to_numeric,
10};
11
12use super::integer_numeric_range;
13
14/// Splits a string into groups of `n` characters.
15/// If the string length is not divisible by `n`, the first group will be shorter.
16/// The string must only consist of ASCII characters.
17#[must_use]
18pub(crate) fn group_n_chars(s: &str, n: usize) -> Vec<&str> {
19    let mut groups = Vec::new();
20    let len = s.len();
21    let rem = len % n;
22    let mut start = 0;
23    if rem > 0 {
24        groups.push(&s[0..rem]);
25        start = rem;
26    }
27    while start < len {
28        let end = (start + n).min(len);
29        groups.push(&s[start..end]);
30        start += n;
31    }
32    groups
33}
34
35/// Map to radix-based representation, in practice hex or octal
36fn map_to_radix(s: &str, radix: usize, num_bits: u32) -> (String, ValueKind) {
37    let mut had_invalid_digit = false;
38    let formatted = group_n_chars(
39        &format!("{extra_bits}{s}", extra_bits = extend_string(s, num_bits)),
40        radix,
41    )
42    .into_iter()
43    .map(|g| match g {
44        g if g.contains('x') => "x".to_string(),
45        g if g.contains('z') => "z".to_string(),
46        g if g.contains('-') => "-".to_string(),
47        g if g.contains('u') => "u".to_string(),
48        g if g.contains('w') => "w".to_string(),
49        g if g.contains('h') => "h".to_string(),
50        g if g.contains('l') => "l".to_string(),
51        g => {
52            if let Ok(val) = u8::from_str_radix(g, 2) {
53                format!("{val:x}")
54            } else {
55                had_invalid_digit = true;
56                "?".to_string()
57            }
58        }
59    })
60    .join("");
61
62    let kind = if had_invalid_digit {
63        ValueKind::Error
64    } else {
65        kind_for_binary_representation(&formatted)
66    };
67    (formatted, kind)
68}
69
70fn check_wordlength(
71    num_bits: Option<u32>,
72    required: impl FnOnce(u32) -> bool,
73) -> Result<TranslationPreference> {
74    if let Some(num_bits) = num_bits {
75        if required(num_bits) {
76            Ok(TranslationPreference::Yes)
77        } else {
78            Ok(TranslationPreference::No)
79        }
80    } else {
81        Ok(TranslationPreference::No)
82    }
83}
84
85pub struct HexTranslator {}
86
87impl BasicTranslator<VarId, ScopeId> for HexTranslator {
88    fn name(&self) -> String {
89        String::from("Hexadecimal")
90    }
91
92    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
93        match value {
94            VariableValue::BigUint(v) => (
95                format!("{v:0width$x}", width = num_bits.div_ceil(4) as usize),
96                ValueKind::Normal,
97            ),
98            VariableValue::String(s) => map_to_radix(s, 4, num_bits),
99        }
100    }
101
102    fn basic_numeric_range(&self, num_bits: u32) -> Option<NumericRange> {
103        integer_numeric_range(num_bits, false)
104    }
105}
106
107pub struct BitTranslator {}
108
109impl BasicTranslator<VarId, ScopeId> for BitTranslator {
110    fn name(&self) -> String {
111        String::from("Bit")
112    }
113
114    fn basic_translate(&self, _num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
115        match value {
116            VariableValue::BigUint(v) => (
117                if (*v).is_zero() {
118                    "0".to_string()
119                } else if (*v).is_one() {
120                    "1".to_string()
121                } else {
122                    "-".to_string()
123                },
124                ValueKind::Normal,
125            ),
126            VariableValue::String(s) => (s.clone(), kind_for_binary_representation(s)),
127        }
128    }
129
130    fn translates(&self, variable: &VariableMeta) -> Result<TranslationPreference> {
131        if let Some(num_bits) = variable.num_bits {
132            if num_bits == 1u32 {
133                Ok(TranslationPreference::Prefer)
134            } else {
135                Ok(TranslationPreference::No)
136            }
137        } else {
138            Ok(TranslationPreference::No)
139        }
140    }
141
142    fn variable_info(&self, _variable: &VariableMeta) -> Result<VariableInfo> {
143        Ok(VariableInfo::Bool)
144    }
145}
146
147pub struct OctalTranslator {}
148
149impl BasicTranslator<VarId, ScopeId> for OctalTranslator {
150    fn name(&self) -> String {
151        String::from("Octal")
152    }
153
154    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
155        match value {
156            VariableValue::BigUint(v) => (
157                format!("{v:0width$o}", width = num_bits.div_ceil(3) as usize),
158                ValueKind::Normal,
159            ),
160            VariableValue::String(s) => map_to_radix(s, 3, num_bits),
161        }
162    }
163
164    fn basic_numeric_range(&self, num_bits: u32) -> Option<NumericRange> {
165        integer_numeric_range(num_bits, false)
166    }
167}
168
169pub struct GroupingBinaryTranslator {}
170
171impl BasicTranslator<VarId, ScopeId> for GroupingBinaryTranslator {
172    fn name(&self) -> String {
173        String::from("Binary (with groups)")
174    }
175
176    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
177        let (val, color) = match value {
178            VariableValue::BigUint(v) => (
179                format!("{v:0width$b}", width = num_bits as usize),
180                ValueKind::Normal,
181            ),
182            VariableValue::String(s) => (
183                format!("{extra_bits}{s}", extra_bits = extend_string(s, num_bits)),
184                kind_for_binary_representation(s),
185            ),
186        };
187
188        (group_n_chars(&val, 4).join(" "), color)
189    }
190
191    fn basic_numeric_range(&self, num_bits: u32) -> Option<NumericRange> {
192        integer_numeric_range(num_bits, false)
193    }
194}
195
196pub struct BinaryTranslator {}
197
198impl BasicTranslator<VarId, ScopeId> for BinaryTranslator {
199    fn name(&self) -> String {
200        String::from("Binary")
201    }
202
203    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
204        match value {
205            VariableValue::BigUint(v) => (
206                format!("{v:0width$b}", width = num_bits as usize),
207                ValueKind::Normal,
208            ),
209            VariableValue::String(s) => (
210                format!("{extra_bits}{s}", extra_bits = extend_string(s, num_bits)),
211                kind_for_binary_representation(s),
212            ),
213        }
214    }
215
216    fn basic_numeric_range(&self, num_bits: u32) -> Option<NumericRange> {
217        integer_numeric_range(num_bits, false)
218    }
219}
220
221pub struct ASCIITranslator {}
222
223impl BasicTranslator<VarId, ScopeId> for ASCIITranslator {
224    fn name(&self) -> String {
225        String::from("ASCII")
226    }
227
228    fn basic_translate(&self, _num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
229        match value {
230            VariableValue::BigUint(v) => (
231                v.to_bytes_be()
232                    .into_iter()
233                    .map(|val| (val as char).to_string())
234                    .join(""),
235                ValueKind::Normal,
236            ),
237            VariableValue::String(s) => match check_vector_variable(s) {
238                Some(v) => v,
239                None => (
240                    group_n_chars(s, 8)
241                        .into_iter()
242                        .map(|substr| {
243                            format!(
244                                "{cval}",
245                                cval = u8::from_str_radix(substr, 2).unwrap_or_else(|_| panic!(
246                                    "Found non-binary digit {substr} in value"
247                                )) as char
248                            )
249                        })
250                        .join(""),
251                    ValueKind::Normal,
252                ),
253            },
254        }
255    }
256}
257
258fn decode_lebxxx(value: &num::BigUint) -> Result<num::BigUint, &'static str> {
259    let bytes = value.to_bytes_be();
260    match bytes.first() {
261        Some(b) if b & 0x80 != 0 => return Err("invalid MSB"),
262        _ => (),
263    }
264
265    let first: num::BigUint = bytes.first().copied().unwrap_or(0).into();
266    bytes.iter().skip(1).try_fold(first, |result, b| {
267        if (b & 0x80 == 0) == (result.is_zero()) {
268            Ok((result << 7) + (*b & 0x7f))
269        } else {
270            Err("invalid flag")
271        }
272    })
273}
274
275pub struct LebTranslator {}
276
277impl BasicTranslator<VarId, ScopeId> for LebTranslator {
278    fn name(&self) -> String {
279        "LEBxxx".to_string()
280    }
281
282    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
283        let decoded = match value {
284            VariableValue::BigUint(v) => decode_lebxxx(v),
285            VariableValue::String(s) => match check_vector_variable(s) {
286                Some(v) => return v,
287                None => match num::BigUint::parse_bytes(s.as_bytes(), 2) {
288                    Some(bi) => decode_lebxxx(&bi),
289                    None => return ("INVALID".to_owned(), ValueKind::Warn),
290                },
291            },
292        };
293
294        match decoded {
295            Ok(decoded) => (decoded.to_str_radix(10), ValueKind::Normal),
296            Err(s) => (
297                s.to_owned()
298                    + ": "
299                    + &GroupingBinaryTranslator {}
300                        .basic_translate(num_bits, value)
301                        .0,
302                ValueKind::Warn,
303            ),
304        }
305    }
306
307    fn translates(&self, variable: &VariableMeta) -> Result<TranslationPreference> {
308        check_wordlength(variable.num_bits, |n| (n.is_multiple_of(8)) && n > 0)
309    }
310}
311
312pub struct NumberOfOnesTranslator {}
313
314impl BasicTranslator<VarId, ScopeId> for NumberOfOnesTranslator {
315    fn name(&self) -> String {
316        String::from("Number of ones")
317    }
318
319    fn basic_translate(&self, _num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
320        match value {
321            VariableValue::BigUint(v) => (v.count_ones().to_string(), ValueKind::Normal),
322            VariableValue::String(s) => (
323                s.bytes().filter(|b| *b == b'1').count().to_string(),
324                kind_for_binary_representation(s),
325            ),
326        }
327    }
328
329    fn basic_translate_numeric(&self, _num_bits: u32, value: &VariableValue) -> Option<f64> {
330        Some(parse_value_to_numeric(value, |v| v.count_ones() as f64))
331    }
332}
333
334pub struct TrailingOnesTranslator {}
335
336impl BasicTranslator<VarId, ScopeId> for TrailingOnesTranslator {
337    fn name(&self) -> String {
338        String::from("Trailing ones")
339    }
340
341    fn basic_translate(&self, _num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
342        match value {
343            VariableValue::BigUint(v) => (v.trailing_ones().to_string(), ValueKind::Normal),
344            VariableValue::String(s) => (
345                s.bytes()
346                    .rev()
347                    .take_while(|b| *b == b'1')
348                    .count()
349                    .to_string(),
350                kind_for_binary_representation(s),
351            ),
352        }
353    }
354
355    fn basic_translate_numeric(&self, _num_bits: u32, value: &VariableValue) -> Option<f64> {
356        Some(parse_value_to_numeric(value, |v| v.trailing_ones() as f64))
357    }
358}
359
360pub struct TrailingZerosTranslator {}
361
362impl BasicTranslator<VarId, ScopeId> for TrailingZerosTranslator {
363    fn name(&self) -> String {
364        String::from("Trailing zeros")
365    }
366
367    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
368        match value {
369            VariableValue::BigUint(v) => (
370                v.trailing_zeros()
371                    .unwrap_or(u64::from(num_bits))
372                    .to_string(),
373                ValueKind::Normal,
374            ),
375            VariableValue::String(s) => (
376                (extend_string(s, num_bits) + s)
377                    .bytes()
378                    .rev()
379                    .take_while(|b| *b == b'0')
380                    .count()
381                    .to_string(),
382                kind_for_binary_representation(s),
383            ),
384        }
385    }
386
387    fn basic_translate_numeric(&self, num_bits: u32, value: &VariableValue) -> Option<f64> {
388        Some(parse_value_to_numeric(value, |v| {
389            v.trailing_zeros().unwrap_or(u64::from(num_bits)) as f64
390        }))
391    }
392}
393
394pub struct LeadingOnesTranslator {}
395
396impl BasicTranslator<VarId, ScopeId> for LeadingOnesTranslator {
397    fn name(&self) -> String {
398        String::from("Leading ones")
399    }
400
401    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
402        match value {
403            VariableValue::BigUint(v) => {
404                let s = format!("{v:0width$b}", width = num_bits as usize);
405                self.basic_translate(num_bits, &VariableValue::String(s))
406            }
407            VariableValue::String(s) => (
408                if s.len() == (num_bits as usize) {
409                    s.bytes().take_while(|b| *b == b'1').count().to_string()
410                } else {
411                    "0".to_string()
412                },
413                kind_for_binary_representation(s),
414            ),
415        }
416    }
417
418    fn basic_translate_numeric(&self, num_bits: u32, value: &VariableValue) -> Option<f64> {
419        Some(parse_value_to_numeric(value, |v| {
420            leading_ones(v, u64::from(num_bits)) as f64
421        }))
422    }
423}
424
425pub struct LeadingZerosTranslator {}
426
427impl BasicTranslator<VarId, ScopeId> for LeadingZerosTranslator {
428    fn name(&self) -> String {
429        String::from("Leading zeros")
430    }
431
432    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
433        match value {
434            VariableValue::BigUint(v) => {
435                let s = format!("{v:0width$b}", width = num_bits as usize);
436                self.basic_translate(num_bits, &VariableValue::String(s))
437            }
438            VariableValue::String(s) => (
439                (extend_string(s, num_bits) + s)
440                    .bytes()
441                    .take_while(|b| *b == b'0')
442                    .count()
443                    .to_string(),
444                kind_for_binary_representation(s),
445            ),
446        }
447    }
448
449    fn basic_translate_numeric(&self, num_bits: u32, value: &VariableValue) -> Option<f64> {
450        Some(parse_value_to_numeric(value, |v| {
451            u64::from(num_bits).saturating_sub(v.bits()) as f64
452        }))
453    }
454}
455
456/// Counts leading ones in a `BigUint` value with a given bit width.
457/// Returns 0 if the value has leading zeros (i.e., `v.bits() < num_bits`).
458fn leading_ones(v: &num::BigUint, num_bits: u64) -> u64 {
459    if v.bits() < num_bits {
460        0
461    } else {
462        let mask = (num::BigUint::one() << num_bits) - 1u32;
463        num_bits.saturating_sub((mask ^ v).bits())
464    }
465}
466
467pub struct IdenticalMSBsTranslator {}
468
469impl BasicTranslator<VarId, ScopeId> for IdenticalMSBsTranslator {
470    fn name(&self) -> String {
471        String::from("Identical MSBs")
472    }
473
474    fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
475        match value {
476            VariableValue::BigUint(v) => {
477                let s = format!("{v:0width$b}", width = num_bits as usize);
478                self.basic_translate(num_bits, &VariableValue::String(s))
479            }
480            VariableValue::String(s) => {
481                let extended_string = extend_string(s, num_bits) + s;
482                let zeros = extended_string.bytes().take_while(|b| *b == b'0').count();
483                let ones = extended_string.bytes().take_while(|b| *b == b'1').count();
484                let count = ones.max(zeros);
485                (count.to_string(), kind_for_binary_representation(s))
486            }
487        }
488    }
489
490    fn basic_translate_numeric(&self, num_bits: u32, value: &VariableValue) -> Option<f64> {
491        Some(parse_value_to_numeric(value, |v| {
492            let num_bits = u64::from(num_bits);
493            let lz = num_bits.saturating_sub(v.bits());
494            let lo = leading_ones(v, num_bits);
495            lz.max(lo) as f64
496        }))
497    }
498}
499
500#[cfg(test)]
501mod test {
502
503    use num::BigUint;
504
505    use super::*;
506
507    #[test]
508    fn group_n_chars_splits_with_remainder() {
509        assert_eq!(group_n_chars("101010", 4), vec!["10", "1010"]);
510    }
511
512    #[test]
513    fn group_n_chars_splits_evenly() {
514        assert_eq!(group_n_chars("10101010", 4), vec!["1010", "1010"]);
515    }
516
517    #[test]
518    fn group_n_chars_handles_empty_input() {
519        assert_eq!(group_n_chars("", 4), Vec::<&str>::new());
520    }
521
522    #[test]
523    fn map_to_radix_formats_binary_groups() {
524        let (formatted, kind) = map_to_radix("10000", 4, 5);
525        assert_eq!(formatted, "10");
526        assert_eq!(kind, ValueKind::Normal);
527    }
528
529    #[test]
530    fn map_to_radix_prefers_special_digits_per_group() {
531        let (formatted, kind) = map_to_radix("zx01", 4, 4);
532        assert_eq!(formatted, "x");
533        assert_eq!(kind, kind_for_binary_representation("x"));
534    }
535
536    #[test]
537    fn map_to_radix_marks_invalid_binary_digit_as_error() {
538        let (formatted, kind) = map_to_radix("1002", 4, 4);
539        assert_eq!(formatted, "?");
540        assert_eq!(kind, ValueKind::Error);
541    }
542
543    #[test]
544    fn check_wordlength_returns_yes_when_predicate_matches() {
545        let preference = check_wordlength(Some(16), |n| n.is_multiple_of(8)).unwrap();
546        assert_eq!(preference, TranslationPreference::Yes);
547    }
548
549    #[test]
550    fn check_wordlength_returns_no_for_missing_or_nonmatching_wordlength() {
551        let missing = check_wordlength(None, |n| n.is_multiple_of(8)).unwrap();
552        assert_eq!(missing, TranslationPreference::No);
553
554        let nonmatching = check_wordlength(Some(7), |n| n.is_multiple_of(8)).unwrap();
555        assert_eq!(nonmatching, TranslationPreference::No);
556    }
557
558    #[test]
559    fn decode_lebxxx_decodes_valid_value() {
560        let decoded = decode_lebxxx(&BigUint::from(0b01011010_11101111u16)).unwrap();
561        assert_eq!(decoded, BigUint::from(11_631u32));
562    }
563
564    #[test]
565    fn decode_lebxxx_rejects_invalid_msb() {
566        let err = decode_lebxxx(&BigUint::from(0b10000000u8)).unwrap_err();
567        assert_eq!(err, "invalid MSB");
568    }
569
570    #[test]
571    fn hexadecimal_translation_groups_digits_correctly_string() {
572        assert_eq!(
573            HexTranslator {}
574                .basic_translate(5, &VariableValue::String("10000".to_string()))
575                .0,
576            "10"
577        );
578
579        assert_eq!(
580            HexTranslator {}
581                .basic_translate(5, &VariableValue::String("1000".to_string()))
582                .0,
583            "08"
584        );
585
586        assert_eq!(
587            HexTranslator {}
588                .basic_translate(5, &VariableValue::String("100000".to_string()))
589                .0,
590            "20"
591        );
592        assert_eq!(
593            HexTranslator {}
594                .basic_translate(10, &VariableValue::String("1z00x0".to_string()))
595                .0,
596            "0zx"
597        );
598        assert_eq!(
599            HexTranslator {}
600                .basic_translate(10, &VariableValue::String("z0110".to_string()))
601                .0,
602            "zz6"
603        );
604        assert_eq!(
605            HexTranslator {}
606                .basic_translate(24, &VariableValue::String("xz0110".to_string()))
607                .0,
608            "xxxxx6"
609        );
610    }
611
612    #[test]
613    fn hexadecimal_translation_groups_digits_correctly_bigint() {
614        assert_eq!(
615            HexTranslator {}
616                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0b10000u32)))
617                .0,
618            "10"
619        );
620        assert_eq!(
621            HexTranslator {}
622                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0b1000u32)))
623                .0,
624            "08"
625        );
626        assert_eq!(
627            HexTranslator {}
628                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0u32)))
629                .0,
630            "00"
631        );
632    }
633
634    #[test]
635    fn octal_translation_groups_digits_correctly_string() {
636        assert_eq!(
637            OctalTranslator {}
638                .basic_translate(5, &VariableValue::String("10000".to_string()))
639                .0,
640            "20"
641        );
642        assert_eq!(
643            OctalTranslator {}
644                .basic_translate(5, &VariableValue::String("100".to_string()))
645                .0,
646            "04"
647        );
648        assert_eq!(
649            OctalTranslator {}
650                .basic_translate(9, &VariableValue::String("x100".to_string()))
651                .0,
652            "xx4"
653        );
654    }
655
656    #[test]
657    fn octal_translation_groups_digits_correctly_bigint() {
658        assert_eq!(
659            OctalTranslator {}
660                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0b10000u32)))
661                .0,
662            "20"
663        );
664        assert_eq!(
665            OctalTranslator {}
666                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0b00100u32)))
667                .0,
668            "04"
669        );
670    }
671
672    #[test]
673    fn grouping_binary_translation_groups_digits_correctly_string() {
674        assert_eq!(
675            GroupingBinaryTranslator {}
676                .basic_translate(5, &VariableValue::String("1000w".to_string()))
677                .0,
678            "1 000w"
679        );
680        assert_eq!(
681            GroupingBinaryTranslator {}
682                .basic_translate(8, &VariableValue::String("100l00".to_string()))
683                .0,
684            "0010 0l00"
685        );
686        assert_eq!(
687            GroupingBinaryTranslator {}
688                .basic_translate(7, &VariableValue::String("10x00".to_string()))
689                .0,
690            "001 0x00"
691        );
692        assert_eq!(
693            GroupingBinaryTranslator {}
694                .basic_translate(7, &VariableValue::String("z10x00".to_string()))
695                .0,
696            "zz1 0x00"
697        );
698    }
699
700    #[test]
701    fn grouping_binary_translation_groups_digits_correctly_bigint() {
702        assert_eq!(
703            GroupingBinaryTranslator {}
704                .basic_translate(7, &VariableValue::BigUint(BigUint::from(0b100000u32)))
705                .0,
706            "010 0000"
707        );
708    }
709
710    #[test]
711    fn binary_translation_groups_digits_correctly_string() {
712        assert_eq!(
713            BinaryTranslator {}
714                .basic_translate(5, &VariableValue::String("10000".to_string()))
715                .0,
716            "10000"
717        );
718        assert_eq!(
719            BinaryTranslator {}
720                .basic_translate(8, &VariableValue::String("100h00".to_string()))
721                .0,
722            "00100h00"
723        );
724        assert_eq!(
725            BinaryTranslator {}
726                .basic_translate(7, &VariableValue::String("10x0-".to_string()))
727                .0,
728            "0010x0-"
729        );
730        assert_eq!(
731            BinaryTranslator {}
732                .basic_translate(7, &VariableValue::String("z10x00".to_string()))
733                .0,
734            "zz10x00"
735        );
736    }
737
738    #[test]
739    fn binary_translation_groups_digits_correctly_bigint() {
740        assert_eq!(
741            BinaryTranslator {}
742                .basic_translate(7, &VariableValue::BigUint(BigUint::from(0b100000u32)))
743                .0,
744            "0100000"
745        );
746    }
747
748    #[test]
749    fn ascii_translation_from_biguint() {
750        assert_eq!(
751            ASCIITranslator {}
752                .basic_translate(
753                    15,
754                    &VariableValue::BigUint(BigUint::from(0b100111101001011u32))
755                )
756                .0,
757            "OK"
758        );
759        assert_eq!(
760            ASCIITranslator {}
761                .basic_translate(72, &VariableValue::BigUint(BigUint::from(0b010011000110111101101110011001110010000001110100011001010111001101110100u128)))
762                .0,
763            "Long test"
764        );
765    }
766
767    #[test]
768    fn ascii_translation_from_string() {
769        assert_eq!(
770            ASCIITranslator {}
771                .basic_translate(15, &VariableValue::String("100111101001011".to_string()))
772                .0,
773            "OK"
774        );
775        assert_eq!(
776            ASCIITranslator {}
777                .basic_translate(
778                    72,
779                    &VariableValue::String(
780                        "010011000110111101101110011001110010000001110100011001010111001101110100"
781                            .to_string()
782                    )
783                )
784                .0,
785            "Long test"
786        );
787        assert_eq!(
788            ASCIITranslator {}
789                .basic_translate(16, &VariableValue::String("010x111101001011".to_string()))
790                .0,
791            "UNDEF"
792        );
793        // string too short for 2 characters, pads with 0
794        assert_eq!(
795            ASCIITranslator {}
796                .basic_translate(15, &VariableValue::String("11000001001011".to_string()))
797                .0,
798            "0K"
799        );
800    }
801
802    #[test]
803    fn bit_translation_from_biguint() {
804        assert_eq!(
805            BitTranslator {}
806                .basic_translate(1, &VariableValue::BigUint(BigUint::from(0b1u8)))
807                .0,
808            "1"
809        );
810        assert_eq!(
811            BitTranslator {}
812                .basic_translate(1, &VariableValue::BigUint(BigUint::from(0b0u8)))
813                .0,
814            "0"
815        );
816    }
817
818    #[test]
819    fn bit_translation_from_string() {
820        assert_eq!(
821            BitTranslator {}
822                .basic_translate(1, &VariableValue::String("1".to_string()))
823                .0,
824            "1"
825        );
826        assert_eq!(
827            BitTranslator {}
828                .basic_translate(1, &VariableValue::String("0".to_string()))
829                .0,
830            "0"
831        );
832        assert_eq!(
833            BitTranslator {}
834                .basic_translate(1, &VariableValue::String("x".to_string()))
835                .0,
836            "x"
837        );
838    }
839
840    #[test]
841    fn bit_translator_with_invalid_data() {
842        assert_eq!(
843            BitTranslator {}
844                .basic_translate(2, &VariableValue::BigUint(BigUint::from(3u8)))
845                .0,
846            "-"
847        );
848    }
849
850    #[test]
851    fn leb_translation_from_biguint() {
852        assert_eq!(
853            LebTranslator {}
854                .basic_translate(16, &VariableValue::BigUint(0b01011010_11101111u16.into()))
855                .0,
856            "11631"
857        );
858        assert_eq!(
859            LebTranslator {}
860                .basic_translate(16, &VariableValue::BigUint(0b00000000_00000001u16.into()))
861                .0,
862            "1"
863        );
864        assert_eq!(
865            LebTranslator{}.basic_translate(64, &VariableValue::BigUint(0b01001010_11110111_11101000_10100000_10111010_11110110_11100001_10011001u64.into())).0, "42185246214303897"
866        );
867    }
868    #[test]
869    fn leb_translation_from_string() {
870        assert_eq!(
871            LebTranslator {}
872                .basic_translate(16, &VariableValue::String("0111110011100010".to_owned()))
873                .0,
874            "15970"
875        );
876    }
877    #[test]
878    fn leb_translation_invalid_msb() {
879        assert_eq!(
880            LebTranslator {}
881                .basic_translate(16, &VariableValue::BigUint(0b1000000010000000u16.into()))
882                .0,
883            "invalid MSB: 1000 0000 1000 0000"
884        );
885    }
886    #[test]
887    fn leb_translation_invalid_continuation() {
888        assert_eq!(
889            LebTranslator {}
890                .basic_translate(16, &VariableValue::BigUint(0b0111111101111111u16.into()))
891                .0,
892            "invalid flag: 0111 1111 0111 1111"
893        );
894    }
895
896    #[test]
897    fn leb_tranlator_input_not_multiple_of_8() {
898        // act as if padded with 0s
899        assert_eq!(
900            LebTranslator {}
901                .basic_translate(16, &VariableValue::BigUint(0b00001111111u16.into()))
902                .0,
903            "127"
904        );
905    }
906
907    #[test]
908    fn number_of_ones_translation_string() {
909        assert_eq!(
910            NumberOfOnesTranslator {}
911                .basic_translate(5, &VariableValue::String("10000".to_string()))
912                .0,
913            "1"
914        );
915        assert_eq!(
916            NumberOfOnesTranslator {}
917                .basic_translate(5, &VariableValue::String("101".to_string()))
918                .0,
919            "2"
920        );
921        assert_eq!(
922            NumberOfOnesTranslator {}
923                .basic_translate(9, &VariableValue::String("1x100".to_string()))
924                .0,
925            "2"
926        );
927    }
928
929    #[test]
930    fn number_of_ones_translation_bigint() {
931        assert_eq!(
932            NumberOfOnesTranslator {}
933                .basic_translate(17, &VariableValue::BigUint(BigUint::from(0b101110000u32)))
934                .0,
935            "4"
936        );
937        assert_eq!(
938            NumberOfOnesTranslator {}
939                .basic_translate(40, &VariableValue::BigUint(BigUint::from(0b00100u32)))
940                .0,
941            "1"
942        );
943    }
944
945    #[test]
946    fn trailing_ones_translation_string() {
947        assert_eq!(
948            TrailingOnesTranslator {}
949                .basic_translate(5, &VariableValue::String("10111".to_string()))
950                .0,
951            "3"
952        );
953        assert_eq!(
954            TrailingOnesTranslator {}
955                .basic_translate(5, &VariableValue::String("101".to_string()))
956                .0,
957            "1"
958        );
959        assert_eq!(
960            TrailingOnesTranslator {}
961                .basic_translate(9, &VariableValue::String("x100".to_string()))
962                .0,
963            "0"
964        );
965    }
966
967    #[test]
968    fn trailing_ones_translation_bigint() {
969        assert_eq!(
970            TrailingOnesTranslator {}
971                .basic_translate(17, &VariableValue::BigUint(BigUint::from(0b101111111u32)))
972                .0,
973            "7"
974        );
975        assert_eq!(
976            TrailingOnesTranslator {}
977                .basic_translate(40, &VariableValue::BigUint(BigUint::from(0b00100u32)))
978                .0,
979            "0"
980        );
981        assert_eq!(
982            TrailingOnesTranslator {}
983                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0b11111u32)))
984                .0,
985            "5"
986        );
987    }
988
989    #[test]
990    fn trailing_zeros_translation_string() {
991        assert_eq!(
992            TrailingZerosTranslator {}
993                .basic_translate(5, &VariableValue::String("10000".to_string()))
994                .0,
995            "4"
996        );
997        assert_eq!(
998            TrailingZerosTranslator {}
999                .basic_translate(5, &VariableValue::String("101".to_string()))
1000                .0,
1001            "0"
1002        );
1003        assert_eq!(
1004            TrailingZerosTranslator {}
1005                .basic_translate(9, &VariableValue::String("x100".to_string()))
1006                .0,
1007            "2"
1008        );
1009    }
1010
1011    #[test]
1012    fn trailing_zeros_translation_bigint() {
1013        assert_eq!(
1014            TrailingZerosTranslator {}
1015                .basic_translate(17, &VariableValue::BigUint(BigUint::from(0b101111111u32)))
1016                .0,
1017            "0"
1018        );
1019        assert_eq!(
1020            TrailingZerosTranslator {}
1021                .basic_translate(40, &VariableValue::BigUint(BigUint::from(0b00100u32)))
1022                .0,
1023            "2"
1024        );
1025        assert_eq!(
1026            TrailingZerosTranslator {}
1027                .basic_translate(16, &VariableValue::BigUint(BigUint::from(0b0u32)))
1028                .0,
1029            "16"
1030        );
1031    }
1032
1033    #[test]
1034    fn leading_ones_translation_string() {
1035        assert_eq!(
1036            LeadingOnesTranslator {}
1037                .basic_translate(5, &VariableValue::String("11101".to_string()))
1038                .0,
1039            "3"
1040        );
1041        assert_eq!(
1042            LeadingOnesTranslator {}
1043                .basic_translate(5, &VariableValue::String("101".to_string()))
1044                .0,
1045            "0"
1046        );
1047        assert_eq!(
1048            LeadingOnesTranslator {}
1049                .basic_translate(9, &VariableValue::String("x100".to_string()))
1050                .0,
1051            "0"
1052        );
1053    }
1054
1055    #[test]
1056    fn leading_ones_translation_bigint() {
1057        assert_eq!(
1058            LeadingOnesTranslator {}
1059                .basic_translate(11, &VariableValue::BigUint(BigUint::from(0b11111111100u32)))
1060                .0,
1061            "9"
1062        );
1063        assert_eq!(
1064            LeadingOnesTranslator {}
1065                .basic_translate(40, &VariableValue::BigUint(BigUint::from(0b00100u32)))
1066                .0,
1067            "0"
1068        );
1069        assert_eq!(
1070            LeadingOnesTranslator {}
1071                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0b11111u32)))
1072                .0,
1073            "5"
1074        );
1075    }
1076
1077    #[test]
1078    fn leading_zeros_translation_string() {
1079        assert_eq!(
1080            LeadingZerosTranslator {}
1081                .basic_translate(5, &VariableValue::String("10000".to_string()))
1082                .0,
1083            "0"
1084        );
1085        assert_eq!(
1086            LeadingZerosTranslator {}
1087                .basic_translate(5, &VariableValue::String("101".to_string()))
1088                .0,
1089            "2"
1090        );
1091        assert_eq!(
1092            LeadingZerosTranslator {}
1093                .basic_translate(9, &VariableValue::String("x100".to_string()))
1094                .0,
1095            "0"
1096        );
1097    }
1098
1099    #[test]
1100    fn leading_zeros_translation_bigint() {
1101        assert_eq!(
1102            LeadingZerosTranslator {}
1103                .basic_translate(17, &VariableValue::BigUint(BigUint::from(0b101111111u32)))
1104                .0,
1105            "8"
1106        );
1107        assert_eq!(
1108            LeadingZerosTranslator {}
1109                .basic_translate(40, &VariableValue::BigUint(BigUint::from(0b00100u32)))
1110                .0,
1111            "37"
1112        );
1113        assert_eq!(
1114            LeadingZerosTranslator {}
1115                .basic_translate(16, &VariableValue::BigUint(BigUint::from(0b0u32)))
1116                .0,
1117            "16"
1118        );
1119    }
1120
1121    #[test]
1122    fn signbits_translation_string() {
1123        assert_eq!(
1124            IdenticalMSBsTranslator {}
1125                .basic_translate(5, &VariableValue::String("10000".to_string()))
1126                .0,
1127            "1"
1128        );
1129        assert_eq!(
1130            IdenticalMSBsTranslator {}
1131                .basic_translate(7, &VariableValue::String("0".to_string()))
1132                .0,
1133            "7"
1134        );
1135        assert_eq!(
1136            IdenticalMSBsTranslator {}
1137                .basic_translate(5, &VariableValue::String("101".to_string()))
1138                .0,
1139            "2"
1140        );
1141        assert_eq!(
1142            IdenticalMSBsTranslator {}
1143                .basic_translate(9, &VariableValue::String("x100".to_string()))
1144                .0,
1145            "0"
1146        );
1147        assert_eq!(
1148            IdenticalMSBsTranslator {}
1149                .basic_translate(5, &VariableValue::String("11101".to_string()))
1150                .0,
1151            "3"
1152        );
1153    }
1154
1155    #[test]
1156    fn signbits_translation_bigint() {
1157        assert_eq!(
1158            IdenticalMSBsTranslator {}
1159                .basic_translate(17, &VariableValue::BigUint(BigUint::from(0b101111111u32)))
1160                .0,
1161            "8"
1162        );
1163        assert_eq!(
1164            IdenticalMSBsTranslator {}
1165                .basic_translate(40, &VariableValue::BigUint(BigUint::from(0b00100u32)))
1166                .0,
1167            "37"
1168        );
1169        assert_eq!(
1170            IdenticalMSBsTranslator {}
1171                .basic_translate(16, &VariableValue::BigUint(BigUint::from(0b0u32)))
1172                .0,
1173            "16"
1174        );
1175        assert_eq!(
1176            IdenticalMSBsTranslator {}
1177                .basic_translate(11, &VariableValue::BigUint(BigUint::from(0b11111111100u32)))
1178                .0,
1179            "9"
1180        );
1181        assert_eq!(
1182            IdenticalMSBsTranslator {}
1183                .basic_translate(5, &VariableValue::BigUint(BigUint::from(0b11111u32)))
1184                .0,
1185            "5"
1186        );
1187    }
1188}