1#![cfg_attr(not(feature = "std"), no_std)]
20
21#[cfg(feature = "std")]
22use std::ops::{BitAnd, BitOr};
23
24#[cfg(not(feature = "std"))]
25use core::ops::{BitAnd, BitOr};
26
27pub const MAX_DEPTH: usize = 17;
28
29fn sub_mix<T>(seeds: &[T]) -> T where
30 T: BitAnd<Output = T> + BitOr<Output = T> + Copy
31{
32 (seeds[0] & seeds[1]) | (seeds[1] & seeds[2]) | (seeds[0] & seeds[2])
33}
34
35pub fn triplet_mix<T>(seeds: &[T]) -> Result<T, ()> where
37 T: BitAnd<Output = T> + BitOr<Output = T>,
38 T: Default + Copy
39{
40 Ok(seeds.iter().cloned().triplet_mix())
41}
42
43pub trait TripletMix {
45 type Item;
47 fn triplet_mix(self) -> Self::Item;
51}
52
53impl<I, T> TripletMix for I where
54 I: Iterator<Item = T>,
55 T: BitAnd<Output = T> + BitOr<Output = T> + Default + Copy
56{
57 type Item = T;
58 fn triplet_mix(self) -> Self::Item {
59 let mut accum = [[T::default(); 3]; MAX_DEPTH];
60 let mut result = T::default();
61 for (i, seed) in self.enumerate() {
62 accum[0][i % 3] = seed;
63 let mut index_at_depth = i;
64 for depth in 0..MAX_DEPTH {
65 if index_at_depth % 3 != 2 {
66 break;
67 }
68 index_at_depth /= 3;
69 result = sub_mix(&accum[depth]);
70
71 if depth == MAX_DEPTH - 1 {
73 break;
75 } else {
76 accum[depth + 1][index_at_depth % 3] = result;
78 }
79 }
80 }
81 result
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn sub_mix_works() {
91 assert_eq!(sub_mix(&[0, 0, 0][..]), 0);
92 assert_eq!(sub_mix(&[0, 0, 1][..]), 0);
93 assert_eq!(sub_mix(&[0, 1, 0][..]), 0);
94 assert_eq!(sub_mix(&[0, 1, 1][..]), 1);
95 assert_eq!(sub_mix(&[1, 0, 0][..]), 0);
96 assert_eq!(sub_mix(&[1, 0, 1][..]), 1);
97 assert_eq!(sub_mix(&[1, 1, 0][..]), 1);
98 assert_eq!(sub_mix(&[1, 1, 1][..]), 1);
99
100 assert_eq!(sub_mix(&[0, 0, 0][..]), 0);
101 assert_eq!(sub_mix(&[0, 0, 2][..]), 0);
102 assert_eq!(sub_mix(&[0, 2, 0][..]), 0);
103 assert_eq!(sub_mix(&[0, 2, 2][..]), 2);
104 assert_eq!(sub_mix(&[2, 0, 0][..]), 0);
105 assert_eq!(sub_mix(&[2, 0, 2][..]), 2);
106 assert_eq!(sub_mix(&[2, 2, 0][..]), 2);
107 assert_eq!(sub_mix(&[2, 2, 2][..]), 2);
108 }
109
110 #[test]
111 fn triplet_mix_works_on_first_level() {
112 assert_eq!(triplet_mix(&[0, 0, 0][..]).unwrap(), 0);
113 assert_eq!(triplet_mix(&[0, 0, 1][..]).unwrap(), 0);
114 assert_eq!(triplet_mix(&[0, 1, 0][..]).unwrap(), 0);
115 assert_eq!(triplet_mix(&[0, 1, 1][..]).unwrap(), 1);
116 assert_eq!(triplet_mix(&[1, 0, 0][..]).unwrap(), 0);
117 assert_eq!(triplet_mix(&[1, 0, 1][..]).unwrap(), 1);
118 assert_eq!(triplet_mix(&[1, 1, 0][..]).unwrap(), 1);
119 assert_eq!(triplet_mix(&[1, 1, 1][..]).unwrap(), 1);
120
121 assert_eq!(triplet_mix(&[0, 0, 0][..]).unwrap(), 0);
122 assert_eq!(triplet_mix(&[0, 0, 2][..]).unwrap(), 0);
123 assert_eq!(triplet_mix(&[0, 2, 0][..]).unwrap(), 0);
124 assert_eq!(triplet_mix(&[0, 2, 2][..]).unwrap(), 2);
125 assert_eq!(triplet_mix(&[2, 0, 0][..]).unwrap(), 0);
126 assert_eq!(triplet_mix(&[2, 0, 2][..]).unwrap(), 2);
127 assert_eq!(triplet_mix(&[2, 2, 0][..]).unwrap(), 2);
128 assert_eq!(triplet_mix(&[2, 2, 2][..]).unwrap(), 2);
129 }
130
131 #[test]
132 fn triplet_mix_works_on_second_level() {
133 assert_eq!(triplet_mix(&[0, 0, 0, 0, 0, 1, 0, 1, 0][..]).unwrap(), 0);
134 assert_eq!(triplet_mix(&[0, 1, 1, 1, 0, 0, 1, 0, 1][..]).unwrap(), 1);
135 assert_eq!(triplet_mix(&[1, 1, 0, 1, 1, 1, 0, 0, 0][..]).unwrap(), 1);
136 }
137
138 #[test]
139 fn triplet_mix_works_on_third_level() {
140 assert_eq!(triplet_mix(&[0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0][..]).unwrap(), 1);
141 }
142}