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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#![doc = include_str!("../README.md")]

pub use alphabet::Alphabet;
use base::BaseConversion;
use cascade::Cascade;
use permute::Permute;
use rotate::Rotate;
use std::{fmt::Debug, hash::Hash};
use transform::InvertableTransform;

mod add_mod;
mod alphabet;
mod base;
mod cascade;
mod permutation;
mod permute;
mod rotate;
mod transform;

/// Represents a specific, deterministic two-way mapping between `u64` values and opaque IDs.
///
/// For `BlockId<char>`, additional functionality is provided for mapping between `u64`s and
/// `String`s.
#[derive(Clone)]
pub struct BlockId<T: Copy + Hash + Eq> {
    alphabet: Alphabet<T>,
    base_convert: BaseConversion,
    cascade: Cascade,
    rotate: Rotate<u8>,
    permute: Permute,
}

impl<T: Copy + Hash + Eq> Debug for BlockId<T> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "BlockId with alphabet size {}", self.alphabet.len())
    }
}

impl<T: Copy + Hash + Eq> BlockId<T> {
    /// Construct a block ID mapping. Mappings are deterministic based on the three parameters
    /// passed at construction: the alphabet, seed, and minimum length.
    pub fn new(alphabet: Alphabet<T>, seed: u128, min_length: u8) -> Self {
        let base = alphabet.len();
        let base_convert = BaseConversion::new_with_min_length(base, min_length);
        let cascade = Cascade::new(base);
        let rotate = Rotate::new();
        let permute = Permute::new_from_seed(base, seed);

        BlockId {
            alphabet,
            base_convert,
            cascade,
            rotate,
            permute,
        }
    }

    /// Encode a given `u64` into an opaque `Vec<T>`.
    #[inline]
    pub fn encode(&self, v: u64) -> Option<Vec<T>> {
        self.forward(v)
    }

    /// Decode an opaque `Vec<T>` from a given `u64`.
    #[inline]
    pub fn decode(&self, v: Vec<T>) -> Option<u64> {
        self.backward(v)
    }
}

impl<T: Copy + Hash + Eq> InvertableTransform for BlockId<T> {
    type Input = u64;
    type Output = Vec<T>;

    fn forward(&self, v: u64) -> Option<Vec<T>> {
        let mut v = self.base_convert.forward(v)?;

        for _ in 0..v.len() {
            v = self.permute.forward(v)?;
            v = self.cascade.forward(v)?;
            v = self.rotate.forward(v)?;
        }

        v.iter().map(|d| self.alphabet.forward(*d)).collect()
    }

    fn backward(&self, v: Vec<T>) -> Option<u64> {
        let v: Option<Vec<u8>> = v.iter().map(|d| self.alphabet.backward(*d)).collect();
        let mut v = v?;

        for _ in 0..v.len() {
            v = self.rotate.backward(v)?;
            v = self.cascade.backward(v)?;
            v = self.permute.backward(v)?;
        }

        self.base_convert.backward(v)
    }
}

/// For the special case of `BlockId<char>`, helper encoders/decoders that use strings
/// rather than vectors are provided.
impl BlockId<char> {
    /// Encode a `u64` into an opaque string.
    pub fn encode_string(&self, v: u64) -> Option<String> {
        Some(self.forward(v)?.into_iter().collect())
    }

    /// Decode a `u64` from an opaque string.
    pub fn decode_string(&self, v: &str) -> Option<u64> {
        self.backward(v.chars().collect())
    }
}

#[cfg(test)]
mod test {
    use crate::{transform::test::round_trip, Alphabet, BlockId};

    #[test]
    fn long_str() {
        let permuter = BlockId::new(Alphabet::lowercase_alpha(), 118, 4);
        assert_eq!(
            permuter.decode("dsasfsdnlkdfjsl".chars().collect()),
            None,
            "if string is too long should return None since no 1:1 map to u64"
        );
    }

    #[test]
    fn test_round_trip() {
        let permuter = BlockId::new(Alphabet::lowercase_alpha(), 118, 4);

        for i in 600..800 {
            round_trip(&permuter, i);
        }
    }

    #[test]
    fn test_debug() {
        let block1 = BlockId::new(Alphabet::lowercase_alpha(), 118, 4);
        assert_eq!("BlockId with alphabet size 26", format!("{:?}", block1));

        let block2 = BlockId::new(Alphabet::alphanumeric(), 118, 4);
        assert_eq!("BlockId with alphabet size 62", format!("{:?}", block2));
    }
}