ctoolbox/formats/
base16b.rs

1/* SPDX-License-Identifier: MIT */
2/* Copyright © 2009 Base16b.org */
3
4/* Based on https://web.archive.org/web/20090902074623/http://www.base16b.org/doc/specification/version/0.1/base16b.pdf */
5
6// This code for the Base16b object is included under the following license:
7/*
8 * Base16b family encode / decode
9 * http://base16b.org/lib/version/0.1/js/base16b.js
10 * or http://base16b.org/lib/js/base16b.js
11 */
12/*
13    Copyright (c) 2009 Base16b.org
14    Permission is hereby granted, free of charge, to any person
15    obtaining a copy of this software and associated documentation
16    files (the "Software"), to deal in the Software without
17    restriction, including without limitation the rights to use,
18    copy, modify, merge, publish, distribute, sublicense, and/or sell
19    copies of the Software, and to permit persons to whom the
20    Software is furnished to do so, subject to the following
21    conditions:
22    The above copyright notice and this permission notice shall be
23    included in all copies or substantial portions of the Software.
24    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
26    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
28    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
29    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
30    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
31    OTHER DEALINGS IN THE SOFTWARE.
32*/
33
34//! Implements the Base16b format.
35//!
36//! Based on
37//! <https://web.archive.org/web/20090902074623/http://www.base16b.org/doc/specification/version/0.1/base16b.pdf>
38//! Note that the Base16b and Basenb formats provided here are different from
39//! the Base16b formats in the specification, due to what appears to be a bug in
40//! the specification (requiring the remainder length to be stored to decode the
41//! remainder correctly when it starts with a 0 bit and is not 16 bits long).
42
43use anyhow::{Result, bail};
44
45use crate::bail_if_none;
46use crate::formats::unicode::{
47    js_like_slice_utf16, string_to_scalars, ucs2encode,
48};
49
50#[derive(Debug, Clone, Copy)]
51struct CpValue {
52    value: u32,
53    cp: u32,
54}
55
56// Private constants
57// +UF0000 is the first code point in the Asyntactic script
58const AS_START: CpValue = CpValue {
59    value: 0x0000,
60    cp: 0xF0000,
61};
62
63// Private non-contiguous code points
64fn noncont() -> [CpValue; 4] {
65    [
66        CpValue {
67            value: 0xFFFE,
68            cp: 0xF80A,
69        },
70        CpValue {
71            value: 0xFFFF,
72            cp: 0xF80B,
73        },
74        CpValue {
75            value: 0x1FFFE,
76            cp: 0xF80C,
77        },
78        CpValue {
79            value: 0x1FFFF,
80            cp: 0xF80D,
81        },
82    ]
83}
84
85// Private: bytes needed for a character (usually 2)
86fn char_bytes(segm_cp: &Vec<u16>) -> usize {
87    if fixed_char_code_at(segm_cp, 0).is_ok()
88        && fixed_char_code_at(segm_cp, 1).is_ok()
89    {
90        2
91    } else {
92        1
93    }
94}
95
96// Private: bytes needed for a character (fixed surrogate logic)
97fn char_bytes_fixed(segm_cp: &[u16]) -> u8 {
98    let code = *(segm_cp
99        .first()
100        .expect("Call should have included a segment"));
101    if (0xD800..=0xDBFF).contains(&code) || (0xDC00..=0xDFFF).contains(&code) {
102        2
103    } else {
104        1
105    }
106}
107
108// Private: Two's complement for the value for this base
109fn invert_val(segm_val: u32, base: u32) -> u32 {
110    2u32.pow(base) - (segm_val + 1)
111}
112
113// Private: from code point to value
114fn from_code_point(segm_cp: &[u16], bytes: u8) -> Result<u32> {
115    // Map Code Point to a segment value as specified by the mapping table for this base in the Asyntactic script
116    let code = fixed_char_code_at(segm_cp, 0)?;
117    if bytes == 2 {
118        return Ok(code - AS_START.cp);
119    }
120    for nc in &noncont() {
121        // handle non-contiguous code points for last two CPs in bases 16
122        // and 17
123        if nc.cp == code {
124            return Ok(nc.value);
125        }
126    }
127    bail!(format!("from_code_point: unknown code point {segm_cp:?}"))
128}
129
130// Private: value to code point
131fn to_code_point(segm_val: u32, base: u32) -> u32 {
132    // Map a segment value to the Code Point specified by the mapping table for this base in the Asyntactic script
133    if base < 16 {
134        AS_START.cp + segm_val
135    } else {
136        for nc in &noncont() {
137            // handle non-contiguous code points for bases 16 and 17
138            if nc.value == segm_val {
139                return nc.cp;
140            }
141        }
142        AS_START.cp + segm_val
143    }
144}
145
146// Private: code point to String (fixed surrogate logic)
147fn fixed_from_char_code(code_pt: u32) -> String {
148    std::char::from_u32(code_pt)
149        .map_or_else(|| String::from("\u{FFFD}"), |c| c.to_string())
150}
151
152// Private: get the code point at a string index (surrogate aware)
153fn fixed_char_code_at(str_: &[u16], idx: usize) -> Result<u32> {
154    // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/charCodeAt
155    if idx >= str_.len() {
156        return Err(anyhow::anyhow!("index out of bounds"));
157    }
158    let code = u32::from(str_[idx]);
159    if (0xD800..=0xDBFF).contains(&code) {
160        // High surrogate (could change last hex to 0xDB7F to treat high private surrogates as single characters)
161        if idx + 1 < str_.len() {
162            let hi = code;
163            let low = u32::from(str_[idx + 1]);
164            return Ok(((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000);
165        }
166    }
167    if (0xDC00..=0xDFFF).contains(&code) {
168        // Low surrogate
169        if idx >= 1 {
170            let hi = u32::from(str_[idx - 1]);
171            let low = code;
172            return Ok(((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000);
173        }
174    }
175    Ok(code)
176}
177
178/// Encode an array of pseudo-booleans (0 or 1) to a string using the
179/// Asyntactic script.
180pub fn encode(input_arr: &[u8], base: u32) -> Result<String> {
181    /*
182    Encode an array of pseudo-booleans (0 or 1)
183    The specification of the encoding is documented elsewhere on this site. (Search Asyntactic script and Base16b.)
184
185    See `byte_array_to_int_bit_array` and other functions in bitwise.rs for
186    converting to and from these arrays of bits.
187    */
188    if !(7..=17).contains(&base) {
189        bail!("invalid base".to_string());
190    }
191    let base_usize = usize::try_from(base)?;
192    let mut result_arr: Vec<String> = Vec::new();
193    let full_segments = input_arr.len() / base_usize;
194    let remain_bits = input_arr.len() - (full_segments * base_usize);
195    for segment in 0..full_segments {
196        let segmstart = base_usize * segment;
197        let currsegm = &input_arr[segmstart..segmstart + base_usize];
198        let mut segm_val = 0u32;
199        for (bit_idx, &b) in currsegm.iter().enumerate() {
200            let shift = u32::try_from((base_usize - 1) - bit_idx)?;
201            segm_val += u32::from(b) << shift;
202        }
203        let cp = to_code_point(segm_val, base);
204        result_arr.push(fixed_from_char_code(cp));
205    }
206    // encode termination character
207    let segmstart = base_usize * full_segments;
208    let currsegm = &input_arr[segmstart..];
209    // most significant bit at the start (left) / least significant bit at
210    // the end (right).
211    let mut segm_val = 0u32;
212    if remain_bits > 0 {
213        for (bit_idx, &b) in currsegm.iter().enumerate() {
214            let shift = u32::try_from((remain_bits - 1) - bit_idx)?;
215            segm_val += u32::from(b) << shift;
216        }
217    }
218    let term_cp = to_code_point(invert_val(segm_val, base), base);
219    result_arr.push(fixed_from_char_code(term_cp));
220    Ok(result_arr.concat())
221}
222
223/// Decode a string encoded in the Asyntactic script. Return an array of
224/// pseudo-booleans (0 or 1).
225///
226/// remainderLength is not in the original version of this code. It should
227/// be provided to get round-trip output. It is the input length in bits,
228/// mod the number of bits per character (the second argument to the encode
229/// function). Other fixes to decoding are also made if remainderLength is
230/// provided. If it is not provided, the output should be the same as with
231/// original API (if not, that's a bug) - WITH THE EXCEPTION that in some
232/// cases the original version sometimes returned NaN for some bits. See the
233/// tests for an example. I haven't looked into why that is.
234///
235/// Pass None as the `remainder_length` to use per the original API.
236pub fn decode(
237    input_str: &str,
238    remainder_length: Option<u32>,
239) -> Result<Vec<u8>> {
240    let input_str = ucs2encode(&string_to_scalars(input_str))?;
241    let original_api = remainder_length.is_none();
242    let mut result_arr: Vec<u8> = Vec::new();
243    let term_char_bytes = char_bytes_fixed(&input_str[input_str.len() - 1..]);
244    let term_char_bytes_usize: usize = term_char_bytes.into();
245    let term_char_cp = &input_str[input_str.len() - term_char_bytes_usize..];
246    let term_char_val = from_code_point(term_char_cp, term_char_bytes)?;
247    let mut bit: u32 = 17;
248    let base: u32;
249    // decode the base from the termination character
250    while bit >= 7 && (term_char_val / 2u32.pow(bit - 1)) == 0 {
251        bit -= 1;
252    }
253    if (7..=17).contains(&bit) {
254        base = bit;
255    } else {
256        bail!("invalid base decoded");
257    }
258    let mut bytes_used: usize = 0;
259    let full_bytes = input_str.len() - term_char_bytes_usize;
260    while bytes_used < full_bytes {
261        // decode the code point segments in sequence
262        let curr_char_bytes =
263            char_bytes_fixed(&input_str[bytes_used..=bytes_used]); // taste before taking a byte
264        let curr_char_bytes_usize: usize = curr_char_bytes.into();
265        let segment_bit_length: u32 = if original_api {
266            u32::from(curr_char_bytes) * 8
267        } else {
268            base
269        };
270        let segment =
271            &input_str[bytes_used..bytes_used + curr_char_bytes_usize];
272        let segm_val = from_code_point(segment, curr_char_bytes)?;
273        // most significant bit at the start (left) / least significant bit at the end (right).
274        let sbl_usize = usize::try_from(segment_bit_length)?;
275        for bit_pos_usize in (0..sbl_usize).rev() {
276            let bit_pos = u32::try_from(bit_pos_usize)?;
277            let raw: u32 =
278                u32::try_from(((u64::from(segm_val)) / 2u64.pow(bit_pos)) % 2)?;
279            let decoded_bit = u8::try_from(raw)?;
280            if !original_api && decoded_bit > 1 {
281                bail!("Found incorrect bit while decoding");
282            }
283            result_arr.push(decoded_bit);
284        }
285        bytes_used += curr_char_bytes_usize;
286    }
287    // remainder
288    let remain_val = invert_val(term_char_val, base); // decode the remainder from the termination character
289    let mut bit: i16 = i16::from(term_char_bytes * 8) - 1;
290    if (bit > 0) && !original_api {
291        bit = i16::try_from(bail_if_none!(remainder_length))? - 1;
292    }
293    if bit >= 0 {
294        while bit >= 0 {
295            result_arr.push(
296                ((remain_val / 2u32.pow(bit.try_into()?)) % 2).try_into()?,
297            );
298            bit -= 1;
299        }
300    }
301    Ok(result_arr)
302}
303
304/// Public: count Unicode characters in the way Base16b does
305pub fn true_length(input_str: &str) -> usize {
306    /*
307    Count the number of characters in a string.
308    This function can handle stings of mixed BMP plane and higher Unicode planes.
309    Fixes a problem with Javascript which incorrectly that assumes each character is only one byte.
310    */
311    let str_bytes = ucs2encode(&string_to_scalars(input_str))
312        .expect("Rust string should already be valid")
313        .len();
314    let mut str_length = 0;
315    let mut tally_bytes = 0;
316    while tally_bytes < str_bytes {
317        let slice_end = if tally_bytes + 2 > input_str.len() {
318            input_str.len()
319        } else {
320            tally_bytes + 2
321        };
322        // select utf-16 slices. This will sometimes index into the middle
323        // of surrogate pairs.
324        let slice = js_like_slice_utf16(input_str, tally_bytes, slice_end);
325        let char_bytes = char_bytes(&slice);
326        tally_bytes += char_bytes;
327        str_length += 1;
328    }
329    str_length
330}
331
332#[cfg(test)]
333mod tests {
334    use crate::formats::assert_string_ok_eq;
335    use crate::utilities::assert_vec_u8_ok_eq;
336
337    use super::*;
338
339    #[crate::ctb_test]
340    fn test_char_bytes() {
341        assert_eq!(char_bytes(&"a".encode_utf16().collect()), 1);
342        assert_eq!(char_bytes(&"𠜎".encode_utf16().collect()), 2);
343        assert_eq!(char_bytes(&"🥴".encode_utf16().collect()), 2);
344    }
345
346    #[crate::ctb_test]
347    fn test_true_length() {
348        assert_eq!(true_length("aa"), 1);
349        assert_eq!(true_length("a𠜎"), 2);
350        assert_eq!(true_length("a🥴"), 2);
351    }
352
353    #[crate::ctb_test]
354
355    fn test_encode_decode() {
356        // This is UUID e82eef60-19bc-4a00-a44a-763a3445c16f as bits
357        // To convert:
358        // ctoolbox base2base 2 2 -q --limit 1 --pad --separator ', ' "$(ctoolbox base2base 16 2 -q --limit 255 --pad --separator ', ' 'e82eef60-19bc-4a00-a44a-763a3445c16f')"
359        let input = vec![
360            1, 1, 1, 0, 1, 0, 0, 0, //
361            0, 0, 1, 0, 1, 1, 1, 0, //
362            1, 1, 1, 0, 1, 1, 1, 1, //
363            0, 1, 1, 0, 0, 0, 0, 0, //
364            0, 0, 0, 1, 1, 0, 0, 1, //
365            1, 0, 1, 1, 1, 1, 0, 0, //
366            0, 1, 0, 0, 1, 0, 1, 0, //
367            0, 0, 0, 0, 0, 0, 0, 0, //
368            1, 0, 1, 0, 0, 1, 0, 0, //
369            0, 1, 0, 0, 1, 0, 1, 0, //
370            0, 1, 1, 1, 0, 1, 1, 0, //
371            0, 0, 1, 1, 1, 0, 1, 0, //
372            0, 0, 1, 1, 0, 1, 0, 0, //
373            0, 1, 0, 0, 0, 1, 0, 1, //
374            1, 1, 0, 0, 0, 0, 0, 1, //
375            0, 1, 1, 0, 1, 1, 1, 1,
376        ];
377        let expected7 = "\u{f0074}\u{f000b}\u{f005d}\u{f0076}\u{f0000}\u{f0066}\u{f0078}\u{f004a}\u{f0000}\u{f0029}\u{f0009}\u{f0027}\u{f0031}\u{f0068}\u{f0068}\u{f0045}\u{f0060}\u{f005b}\u{f007c}";
378        let expected8 = "\u{f00e8}\u{f002e}\u{f00ef}\u{f0060}\u{f0019}\u{f00bc}\u{f004a}\u{f0000}\u{f00a4}\u{f004a}\u{f0076}\u{f003a}\u{f0034}\u{f0045}\u{f00c1}\u{f006f}\u{f00ff}";
379        let expected9 = "\u{f01d0}\u{f00bb}\u{f017b}\u{f0001}\u{f0137}\u{f0112}\u{f0100}\u{f00a4}\u{f0094}\u{f01d8}\u{f01d1}\u{f0144}\u{f00b8}\u{f005b}\u{f01fc}";
380        let expected10 = "\u{f03a0}\u{f02ee}\u{f03d8}\u{f0019}\u{f02f1}\u{f00a0}\u{f0029}\u{f004a}\u{f01d8}\u{f03a3}\u{f0111}\u{f01c1}\u{f0390}";
381        let expected11 = "\u{f0741}\u{f03bb}\u{f06c0}\u{f019b}\u{f0625}\u{f0002}\u{f0489}\u{f0276}\u{f01d1}\u{f0511}\u{f0382}\u{f0790}";
382        let expected12 = "\u{f0e82}\u{f0eef}\u{f0601}\u{f09bc}\u{f04a0}\u{f00a4}\u{f04a7}\u{f063a}\u{f0344}\u{f05c1}\u{f0f90}";
383        let expected13 = "\u{f1d05}\u{f1bbd}\u{f100c}\u{f1bc4}\u{f1401}\u{f0912}\u{f13b1}\u{f1a34}\u{f08b8}\u{f1e90}";
384        let expected14 = "\u{f3a0b}\u{f2ef6}\u{f0066}\u{f3c4a}\u{f0029}\u{f04a7}\u{f18e8}\u{f3445}\u{f305b}\u{f3ffc}";
385        let expected15 = "\u{f7417}\u{f3bd8}\u{f0337}\u{f44a0}\u{f0522}\u{f29d8}\u{f7468}\u{f45c1}\u{f7f90}";
386        let expected16 = "\u{fe82e}\u{fef60}\u{f19bc}\u{f4a00}\u{fa44a}\u{f763a}\u{f3445}\u{fc16f}\u{f80b}";
387        let expected17 = "\u{10d05d}\u{10bd80}\u{fcde2}\u{fa00a}\u{f894e}\u{108e8d}\u{f22e0}\u{10fe90}";
388
389        assert!(encode(&input, 6).is_err());
390        assert!(encode(&input, 18).is_err());
391        let result7 = assert_string_ok_eq(expected7, encode(&input, 7));
392        let result8 = assert_string_ok_eq(expected8, encode(&input, 8));
393        let result9 = assert_string_ok_eq(expected9, encode(&input, 9));
394        let result10 = assert_string_ok_eq(expected10, encode(&input, 10));
395        let result11 = assert_string_ok_eq(expected11, encode(&input, 11));
396        let result12 = assert_string_ok_eq(expected12, encode(&input, 12));
397        let result13 = assert_string_ok_eq(expected13, encode(&input, 13));
398        let result14 = assert_string_ok_eq(expected14, encode(&input, 14));
399        let result15 = assert_string_ok_eq(expected15, encode(&input, 15));
400        let result16 = assert_string_ok_eq(expected16, encode(&input, 16));
401        let result17 = assert_string_ok_eq(expected17, encode(&input, 17));
402
403        assert!(decode("abcd", None).is_err());
404
405        let input_len = u32::try_from(input.len()).unwrap();
406        assert_vec_u8_ok_eq(&input, decode(&result7, Some(input_len % 7)));
407        assert_vec_u8_ok_eq(&input, decode(&result8, Some(input_len % 8)));
408        assert_vec_u8_ok_eq(&input, decode(&result9, Some(input_len % 9)));
409        assert_vec_u8_ok_eq(&input, decode(&result10, Some(input_len % 10)));
410        assert_vec_u8_ok_eq(&input, decode(&result11, Some(input_len % 11)));
411        assert_vec_u8_ok_eq(&input, decode(&result12, Some(input_len % 12)));
412        assert_vec_u8_ok_eq(&input, decode(&result13, Some(input_len % 13)));
413        assert_vec_u8_ok_eq(&input, decode(&result14, Some(input_len % 14)));
414        assert_vec_u8_ok_eq(&input, decode(&result15, Some(input_len % 15)));
415        assert_vec_u8_ok_eq(&input, decode(&result16, Some(input_len % 16)));
416        assert_vec_u8_ok_eq(&input, decode(&result17, Some(input_len % 17)));
417
418        #[rustfmt::skip]
419        let no_remainder_res7 = &[0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1];
420        #[rustfmt::skip]
421        let no_remainder_res8 = &[0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,0,0,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
422        #[rustfmt::skip]
423        let no_remainder_res9 = &[0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,0,0,0,0,0,0,0,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1];
424        #[rustfmt::skip]
425        let no_remainder_res10 = &[0,0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,0,0,0,0,0,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0,0,0,0,0,1,0,1,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,1,1,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1];
426        #[rustfmt::skip]
427        let no_remainder_res11 = &[0,0,0,0,0,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,1,1,1,0,1,1,1,0,1,1,0,0,0,0,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,0,0,0,0,0,1,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,0,0,1,0,0,1,1,1,0,1,1,0,0,0,0,0,0,0,0,1,1,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1];
428        #[rustfmt::skip]
429        let no_remainder_res12 = &[0,0,0,0,1,1,1,0,1,0,0,0,0,0,1,0,0,0,0,0,1,1,1,0,1,1,1,0,1,1,1,1,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,1,0,1,1,1,1,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,1,0,0,0,0,0,1,1,0,0,0,1,1,1,0,1,0,0,0,0,0,0,0,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,1,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1];
430        #[rustfmt::skip]
431        let no_remainder_res13 = &[0,0,0,1,1,1,0,1,0,0,0,0,0,1,0,1,0,0,0,1,1,0,1,1,1,0,1,1,1,1,0,1,0,0,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,1,1,0,1,1,1,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0,1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,1,1,0,1,1,0,0,0,1,0,0,0,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,0,0,1,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,1,1,1];
432        #[rustfmt::skip]
433        let no_remainder_res14 = &[0,0,1,1,1,0,1,0,0,0,0,0,1,0,1,1,0,0,1,0,1,1,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,1,1,1,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,1,0,0,1,0,1,0,0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0,1,0,0,0,0,0,1,1,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,0,0,0,0,0,1,0,1,1,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1];
434        #[rustfmt::skip]
435        let no_remainder_res15 = &[0,1,1,1,0,1,0,0,0,0,0,1,0,1,1,1,0,0,1,1,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,0,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,0,1,0,1,0,0,1,1,1,0,1,1,0,0,0,0,1,1,1,0,1,0,0,0,1,1,0,1,0,0,0,0,1,0,0,0,1,0,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,1];
436        // NOTE: The original returns NaN for some bits:
437        // I haven't looked into why.
438        // let no_remainder_res16 = &[1,1,1,0,1,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,0,1,0,0,0,1,1,0,1,0,0,0,1,0,0,0,1,0,1,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,NaN,0,0,0,0,0,0,0,0];
439        #[rustfmt::skip]
440        let no_remainder_res16 = &[1,1,1,0,1,0,0,0,0,0,1,0,1,1,1,0,1,1,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,0,1,0,0,0,1,1,0,1,0,0,0,1,0,0,0,1,0,1,1,1,0,0,0,0,0,1,0,1,1,0,1,1,1,1,0,0,0,0,0,0,0,0];
441        #[rustfmt::skip]
442        let no_remainder_res17 = &[1,1,0,1,0,0,0,0,0,1,0,1,1,1,0,1,1,0,1,1,1,1,0,1,1,0,0,0,0,0,0,0,1,1,0,0,1,1,0,1,1,1,1,0,0,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,1,0,1,0,1,0,0,0,1,0,0,1,0,1,0,0,1,1,1,0,1,0,0,0,1,1,1,0,1,0,0,0,1,1,0,1,0,0,1,0,0,0,1,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,0,1,1,1,1];
443        assert_vec_u8_ok_eq(no_remainder_res7, decode(&result7, None));
444        assert_vec_u8_ok_eq(no_remainder_res8, decode(&result8, None));
445        assert_vec_u8_ok_eq(no_remainder_res9, decode(&result9, None));
446        assert_vec_u8_ok_eq(no_remainder_res10, decode(&result10, None));
447        assert_vec_u8_ok_eq(no_remainder_res11, decode(&result11, None));
448        assert_vec_u8_ok_eq(no_remainder_res12, decode(&result12, None));
449        assert_vec_u8_ok_eq(no_remainder_res13, decode(&result13, None));
450        assert_vec_u8_ok_eq(no_remainder_res14, decode(&result14, None));
451        assert_vec_u8_ok_eq(no_remainder_res15, decode(&result15, None));
452        assert_vec_u8_ok_eq(no_remainder_res16, decode(&result16, None));
453        assert_vec_u8_ok_eq(no_remainder_res17, decode(&result17, None));
454
455        // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
456        let input: Vec<u8> = vec![
457            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, //
458            0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, //
459            0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, //
460            0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, //
461            0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, //
462            0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, //
463            0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, //
464            0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, //
465            0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, //
466            0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, //
467            0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, //
468            0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, //
469            0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, //
470            0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, //
471            0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, //
472            0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1,
473        ];
474        let encoded = "\u{f0002}\u{f080c}\u{f2028}\u{f6070}\u{100121}\u{f82c3}\u{f0687}\u{f0f10}\u{f2224}\u{f4c50}\u{fa8b0}\u{107181}\u{102343}\u{fc707}\u{f8f0f}\u{f80c}";
475        let res = assert_string_ok_eq(encoded, encode(&input, 17));
476        crate::log!(res);
477        assert_vec_u8_ok_eq(
478            &input,
479            decode(&res, Some(u32::try_from(input.len() % 17).unwrap())),
480        );
481    }
482}