pseudo_encrypt/
lib.rs

1//! This module provides a native-Rust generic implementation of the pseudo-random generator based
2//! on Postgresql's pseudo_encrypt.
3//!
4//! The [`pseudo_encrypt`](fn.pseudo_encrypt.html) implementation provided maps 1 to 1 wih the
5//! orgininal Psql implementation for 32-bit values
6//!
7//! # Example
8//! ```
9//! # use pseudo_encrypt::pseudo_encrypt;
10//! let input_expected: Vec<(i32, i32)> = vec![
11//!     (-10, -1270576520),
12//!     (-9, -236348969),
13//!     (-8, -1184061109),
14//!     (-7, -25446276),
15//!     (-6, -1507538963),
16//!     (-5, -518858927),
17//!     (-4, -1458116927),
18//!     (-3, -532482573),
19//!     (-2, -157973154),
20//!     (-1, -1105881908),
21//!     (0, 1777613459),
22//!     (1, 561465857),
23//!     (2, 436885871),
24//!     (3, 576481439),
25//!     (4, 483424269),
26//!     (5, 1905133426),
27//!     (6, 971249312),
28//!     (7, 1926833684),
29//!     (8, 735327624),
30//!     (9, 1731020007),
31//!     (10, 792482838),
32//! ];
33//! for (input, expected) in input_expected {
34//!     let r = pseudo_encrypt(input);
35//!     assert_eq!(expected, r);
36//! }
37//! ```
38//!
39//! Integers represented with more bits are also supported out of the box
40//!
41//! # Example
42//!
43//! ```
44//! # use pseudo_encrypt::pseudo_encrypt;
45//! let r = pseudo_encrypt(u128::MAX);
46//! assert_eq!(340282366920938384363736019365210866432, r);
47//! ```
48//!
49//! For more information, see [the Psql documentation for pseudo_encrypt](https://wiki.postgresql.org/wiki/Pseudo_encrypt)
50use std::ops::*;
51
52/// Function that acts as a pseudo-random generator of unique values. It produces an integer output
53/// that is uniquely associated to its integer input (by a mathematical permutation), but looks
54/// random at the same time, with zero collision
55///
56/// # Example
57///
58/// ```
59/// # use pseudo_encrypt::pseudo_encrypt;
60/// let r = pseudo_encrypt(u128::MAX);
61/// assert_eq!(340282366920938384363736019365210866432, r);
62/// ```
63#[inline]
64pub fn pseudo_encrypt<A>(a: A) -> A
65where
66    A: Shl<usize, Output = A>
67        + Shr<usize, Output = A>
68        + Add<Output = A>
69        + Mul<Output = A>
70        + Rem<Output = A>
71        + BitAnd<Output = A>
72        + BitXor<Output = A>
73        + PseudoEncryptable
74        + From<<A as PseudoEncryptable>::HalfBitType>,
75{
76    let left_select = (a >> <A as PseudoEncryptable>::HALF_BIT_SIZE)
77        & <A as PseudoEncryptable>::HALF_BIT_MAX.into();
78    let right_select = a & <A as PseudoEncryptable>::HALF_BIT_MAX.into();
79    let (l, r) = (0..3).fold((left_select, right_select), |(l1, r1), _| {
80        let l2 = r1;
81        let r2 = l1
82            ^ (<A as PseudoEncryptable>::cast_from_f32(
83                ((<A as PseudoEncryptable>::cast_to_f32(
84                    (r1 * <A as PseudoEncryptable>::cast_from_i32(1366)
85                        + <A as PseudoEncryptable>::cast_from_i32(150889))
86                        % <A as PseudoEncryptable>::cast_from_i32(714025),
87                ) / 714025.0)
88                    * 32767.0)
89                    .round(),
90            ));
91        (l2, r2)
92    });
93    (r << <A as PseudoEncryptable>::HALF_BIT_SIZE) + l
94}
95
96pub trait PseudoEncryptable: Copy {
97    type HalfBitType: Copy;
98    const HALF_BIT_SIZE: usize;
99    const HALF_BIT_MAX: Self::HalfBitType;
100
101    fn cast_from_i32(f: i32) -> Self;
102
103    fn cast_from_f32(f: f32) -> Self;
104
105    fn cast_to_f32(f: Self) -> f32;
106}
107
108impl PseudoEncryptable for i32 {
109    type HalfBitType = u16;
110    const HALF_BIT_SIZE: usize = 16;
111    const HALF_BIT_MAX: Self::HalfBitType = u16::MAX;
112
113    #[inline]
114    fn cast_from_i32(f: i32) -> Self {
115        f
116    }
117
118    #[inline]
119    fn cast_from_f32(f: f32) -> Self {
120        f as Self
121    }
122
123    #[inline]
124    fn cast_to_f32(f: Self) -> f32 {
125        f as f32
126    }
127}
128
129impl PseudoEncryptable for i64 {
130    type HalfBitType = u32;
131    const HALF_BIT_SIZE: usize = 32;
132    const HALF_BIT_MAX: Self::HalfBitType = u32::MAX;
133
134    #[inline]
135    fn cast_from_i32(f: i32) -> Self {
136        f as Self
137    }
138
139    #[inline]
140    fn cast_from_f32(f: f32) -> Self {
141        f as Self
142    }
143
144    #[inline]
145    fn cast_to_f32(f: Self) -> f32 {
146        f as f32
147    }
148}
149
150impl PseudoEncryptable for i128 {
151    type HalfBitType = u64;
152    const HALF_BIT_SIZE: usize = 64;
153    const HALF_BIT_MAX: Self::HalfBitType = u64::MAX;
154
155    #[inline]
156    fn cast_from_i32(f: i32) -> Self {
157        f as Self
158    }
159
160    #[inline]
161    fn cast_from_f32(f: f32) -> Self {
162        f as Self
163    }
164
165    #[inline]
166    fn cast_to_f32(f: Self) -> f32 {
167        f as f32
168    }
169}
170
171impl PseudoEncryptable for u32 {
172    type HalfBitType = u16;
173    const HALF_BIT_SIZE: usize = 16;
174    const HALF_BIT_MAX: Self::HalfBitType = u16::MAX;
175
176    #[inline]
177    fn cast_from_i32(f: i32) -> Self {
178        f as Self
179    }
180
181    #[inline]
182    fn cast_from_f32(f: f32) -> Self {
183        f as Self
184    }
185    #[inline]
186    fn cast_to_f32(f: Self) -> f32 {
187        f as f32
188    }
189}
190
191impl PseudoEncryptable for u64 {
192    type HalfBitType = u32;
193    const HALF_BIT_SIZE: usize = 32;
194    const HALF_BIT_MAX: Self::HalfBitType = u32::MAX;
195
196    #[inline]
197    fn cast_from_i32(f: i32) -> Self {
198        f as Self
199    }
200
201    #[inline]
202    fn cast_from_f32(f: f32) -> Self {
203        f as Self
204    }
205
206    #[inline]
207    fn cast_to_f32(f: Self) -> f32 {
208        f as f32
209    }
210}
211
212impl PseudoEncryptable for u128 {
213    type HalfBitType = u64;
214    const HALF_BIT_SIZE: usize = 64;
215    const HALF_BIT_MAX: Self::HalfBitType = u64::MAX;
216
217    #[inline]
218    fn cast_from_i32(f: i32) -> Self {
219        f as Self
220    }
221
222    #[inline]
223    fn cast_from_f32(f: f32) -> Self {
224        f as Self
225    }
226
227    #[inline]
228    fn cast_to_f32(f: Self) -> f32 {
229        f as f32
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use crate::*;
236    use proptest::prelude::*;
237    use std::collections::{HashMap, HashSet};
238    use std::sync::Mutex;
239
240    const TEST_CASES: u32 = 1000000;
241
242    #[test]
243    fn test_test_pseudo_encrypt_values_u32() {
244        let input_expected = vec![
245            (0, 1777613459),
246            (1, 561465857),
247            (2, 436885871),
248            (3, 576481439),
249            (4, 483424269),
250            (5, 1905133426),
251            (6, 971249312),
252            (7, 1926833684),
253            (8, 735327624),
254            (9, 1731020007),
255            (10, 792482838),
256        ];
257        for (input, expected) in input_expected {
258            let r = pseudo_encrypt(input);
259            assert_eq!(expected, r);
260        }
261    }
262
263    #[test]
264    fn test_test_pseudo_encrypt_values_i32() {
265        let input_expected: Vec<(i32, i32)> = vec![
266            (-10, -1270576520),
267            (-9, -236348969),
268            (-8, -1184061109),
269            (-7, -25446276),
270            (-6, -1507538963),
271            (-5, -518858927),
272            (-4, -1458116927),
273            (-3, -532482573),
274            (-2, -157973154),
275            (-1, -1105881908),
276            (0, 1777613459),
277            (1, 561465857),
278            (2, 436885871),
279            (3, 576481439),
280            (4, 483424269),
281            (5, 1905133426),
282            (6, 971249312),
283            (7, 1926833684),
284            (8, 735327624),
285            (9, 1731020007),
286            (10, 792482838),
287        ];
288        for (input, expected) in input_expected {
289            let r = pseudo_encrypt(input);
290            assert_eq!(expected, r);
291        }
292    }
293
294    #[test]
295    fn test_test_pseudo_encrypt_no_collisions_u32() {
296        let seen_inputs_mutex = Mutex::new(HashSet::with_capacity(TEST_CASES as usize));
297        let seen_results_mutex = Mutex::new(HashMap::with_capacity(TEST_CASES as usize));
298        proptest!(ProptestConfig::with_cases(TEST_CASES), move |(u: u32)| {
299            let mut seen_inputs = seen_inputs_mutex.lock().unwrap();
300            prop_assume!(!seen_inputs.contains(&u));
301            seen_inputs.insert(u);
302
303            let mut seen_results = seen_results_mutex.lock().unwrap();
304            let r = pseudo_encrypt(u);
305
306            let previous_input_for_result = seen_results.get(&r);
307            prop_assert!(previous_input_for_result.is_none(), "Previous input [{:?}] yielded the same result [{}]", previous_input_for_result, r);
308            seen_results.insert(r, u);
309        });
310    }
311
312    #[test]
313    fn test_test_pseudo_encrypt_no_collisions_i32() {
314        let seen_inputs_mutex = Mutex::new(HashSet::with_capacity(TEST_CASES as usize));
315        let seen_results_mutex = Mutex::new(HashMap::with_capacity(TEST_CASES as usize));
316        proptest!(ProptestConfig::with_cases(TEST_CASES), move |(u: i32)| {
317            let mut seen_inputs = seen_inputs_mutex.lock().unwrap();
318            prop_assume!(!seen_inputs.contains(&u));
319
320            seen_inputs.insert(u);
321
322            let mut seen_results = seen_results_mutex.lock().unwrap();
323            let r = pseudo_encrypt(u);
324
325            let previous_input_for_result = seen_results.get(&r);
326            prop_assert!(previous_input_for_result.is_none(), "Previous input [{:?}] yielded the same result [{}]", previous_input_for_result, r);
327            seen_results.insert(r, u);
328        });
329    }
330
331    #[test]
332    fn test_test_pseudo_encrypt_no_collisions_i64() {
333        let seen_inputs_mutex = Mutex::new(HashSet::with_capacity(TEST_CASES as usize));
334        let seen_results_mutex = Mutex::new(HashMap::with_capacity(TEST_CASES as usize));
335        proptest!(ProptestConfig::with_cases(TEST_CASES), move |(u: i64)| {
336            let mut seen_inputs = seen_inputs_mutex.lock().unwrap();
337            prop_assume!(!seen_inputs.contains(&u));
338            seen_inputs.insert(u);
339
340            let mut seen_results = seen_results_mutex.lock().unwrap();
341            let r = pseudo_encrypt(u);
342
343            let previous_input_for_result = seen_results.get(&r);
344            prop_assert!(previous_input_for_result.is_none(), "Previous input [{:?}] yielded the same result [{}]", previous_input_for_result, r);
345            seen_results.insert(r, u);
346        });
347    }
348
349    #[test]
350    fn test_test_pseudo_encrypt_no_collisions_u64() {
351        let seen_inputs_mutex = Mutex::new(HashSet::with_capacity(TEST_CASES as usize));
352        let seen_results_mutex = Mutex::new(HashMap::with_capacity(TEST_CASES as usize));
353        proptest!(ProptestConfig::with_cases(TEST_CASES), move |(u: u64)| {
354            let mut seen_inputs = seen_inputs_mutex.lock().unwrap();
355            prop_assume!(!seen_inputs.contains(&u));
356            seen_inputs.insert(u);
357
358            let mut seen_results = seen_results_mutex.lock().unwrap();
359            let r = pseudo_encrypt(u);
360
361            let previous_input_for_result = seen_results.get(&r);
362            prop_assert!(previous_input_for_result.is_none(), "Previous input [{:?}] yielded the same result [{}]", previous_input_for_result, r);
363            seen_results.insert(r, u);
364        });
365    }
366
367    #[test]
368    fn test_test_pseudo_encrypt_no_collisions_i128() {
369        let seen_inputs_mutex = Mutex::new(HashSet::with_capacity(TEST_CASES as usize));
370        let seen_results_mutex = Mutex::new(HashMap::with_capacity(TEST_CASES as usize));
371        proptest!(ProptestConfig::with_cases(TEST_CASES), move |(u: i128)| {
372            let mut seen_inputs = seen_inputs_mutex.lock().unwrap();
373            prop_assume!(!seen_inputs.contains(&u));
374            seen_inputs.insert(u);
375
376            let mut seen_results = seen_results_mutex.lock().unwrap();
377            let r = pseudo_encrypt(u);
378
379            let previous_input_for_result = seen_results.get(&r);
380            prop_assert!(previous_input_for_result.is_none(), "Previous input [{:?}] yielded the same result [{}]", previous_input_for_result, r);
381            seen_results.insert(r, u);
382        });
383    }
384
385    #[test]
386    fn test_test_pseudo_encrypt_no_collisions_u128() {
387        let seen_inputs_mutex = Mutex::new(HashSet::with_capacity(TEST_CASES as usize));
388        let seen_results_mutex = Mutex::new(HashMap::with_capacity(TEST_CASES as usize));
389        proptest!(ProptestConfig::with_cases(TEST_CASES), move |(u: u128)| {
390            let mut seen_inputs = seen_inputs_mutex.lock().unwrap();
391            prop_assume!(!seen_inputs.contains(&u));
392            seen_inputs.insert(u);
393
394            let mut seen_results = seen_results_mutex.lock().unwrap();
395            let r = pseudo_encrypt(u);
396
397            let previous_input_for_result = seen_results.get(&r);
398            prop_assert!(previous_input_for_result.is_none(), "Previous input [{:?}] yielded the same result [{}]", previous_input_for_result, r);
399            seen_results.insert(r, u);
400        });
401    }
402}