reed_solomon_16/engine/
tables.rs

1//! Lookup-tables used by [`Engine`]:s.
2//!
3//! All tables are global and each is initialized at most once.
4//!
5//! # Tables
6//!
7//! | Table        | Size    | Used in encoding | Used in decoding | By engines |
8//! | ------------ | ------- | ---------------- | ---------------- | ---------- |
9//! | [`Exp`]      | 128 kiB | yes              | yes              | all        |
10//! | [`Log`]      | 128 kiB | yes              | yes              | all        |
11//! | [`LogWalsh`] | 128 kiB | -                | yes              | all        |
12//! | [`Mul16`]    | 8 MiB   | yes              | yes              | [`NoSimd`] |
13//! | [`Skew`]     | 128 kiB | yes              | yes              | all        |
14//!
15//! [`NoSimd`]: crate::engine::NoSimd
16
17use once_cell::sync::OnceCell;
18
19use crate::engine::{
20    self, Engine, GfElement, CANTOR_BASIS, GF_BITS, GF_MODULUS, GF_ORDER, GF_POLYNOMIAL,
21};
22
23// ======================================================================
24// TYPE ALIASES - PUBLIC
25
26/// Used by [`Naive`] engine for multiplications
27/// and by all [`Engine`]:s to initialize other tables.
28///
29/// [`Naive`]: crate::engine::Naive
30pub type Exp = [GfElement; GF_ORDER];
31
32/// Used by [`Naive`] engine for multiplications
33/// and by all [`Engine`]:s to initialize other tables.
34///
35/// [`Naive`]: crate::engine::Naive
36pub type Log = [GfElement; GF_ORDER];
37
38/// Used by all [`Engine`]:s in [`Engine::eval_poly`].
39pub type LogWalsh = [GfElement; GF_ORDER];
40
41/// Used by [`NoSimd`] engine for multiplications.
42///
43/// [`NoSimd`]: crate::engine::NoSimd
44pub type Mul16 = [[[GfElement; 16]; 4]; GF_ORDER];
45
46/// Used by all [`Engine`]:s for FFT and IFFT.
47pub type Skew = [GfElement; GF_MODULUS as usize];
48
49// ======================================================================
50// ExpLog - PRIVATE
51
52struct ExpLog {
53    exp: Box<Exp>,
54    log: Box<Log>,
55}
56
57// ======================================================================
58// STATIC - PRIVATE
59
60static EXP_LOG: OnceCell<ExpLog> = OnceCell::new();
61static LOG_WALSH: OnceCell<Box<LogWalsh>> = OnceCell::new();
62static MUL16: OnceCell<Box<Mul16>> = OnceCell::new();
63static SKEW: OnceCell<Box<Skew>> = OnceCell::new();
64
65// ======================================================================
66// FUNCTIONS - PUBLIC - math
67
68/// Calculates `x * log_m` using [`Exp`] and [`Log`] tables.
69#[inline(always)]
70pub fn mul(x: GfElement, log_m: GfElement, exp: &Exp, log: &Log) -> GfElement {
71    if x == 0 {
72        0
73    } else {
74        exp[engine::add_mod(log[x as usize], log_m) as usize]
75    }
76}
77
78// ======================================================================
79// FUNCTIONS - PUBLIC - initialize tables
80
81/// Initializes and returns [`Exp`] and [`Log`] tables.
82#[allow(clippy::needless_range_loop)]
83pub fn initialize_exp_log() -> (&'static Exp, &'static Log) {
84    let exp_log = EXP_LOG.get_or_init(|| {
85        let mut exp = Box::new([0; GF_ORDER]);
86        let mut log = Box::new([0; GF_ORDER]);
87
88        // GENERATE LFSR TABLE
89
90        let mut state = 1;
91        for i in 0..GF_MODULUS {
92            exp[state] = i;
93            state <<= 1;
94            if state >= GF_ORDER {
95                state ^= GF_POLYNOMIAL;
96            }
97        }
98        exp[0] = GF_MODULUS;
99
100        // CONVERT TO CANTOR BASIS
101
102        log[0] = 0;
103        for i in 0..GF_BITS {
104            let width = 1usize << i;
105            for j in 0..width {
106                log[j + width] = log[j] ^ CANTOR_BASIS[i];
107            }
108        }
109
110        for i in 0..GF_ORDER {
111            log[i] = exp[log[i] as usize];
112        }
113
114        for i in 0..GF_ORDER {
115            exp[log[i] as usize] = i as GfElement;
116        }
117
118        exp[GF_MODULUS as usize] = exp[0];
119
120        ExpLog { exp, log }
121    });
122
123    (&exp_log.exp, &exp_log.log)
124}
125
126/// Initializes and returns [`LogWalsh`] table.
127pub fn initialize_log_walsh<E: Engine>() -> &'static LogWalsh {
128    LOG_WALSH.get_or_init(|| {
129        let (_, log) = initialize_exp_log();
130
131        let mut log_walsh: Box<LogWalsh> = Box::new([0; GF_ORDER]);
132
133        log_walsh.copy_from_slice(log.as_ref());
134        log_walsh[0] = 0;
135        E::fwht(log_walsh.as_mut(), GF_ORDER);
136
137        log_walsh
138    })
139}
140
141/// Initializes and returns [`Mul16`] table.
142pub fn initialize_mul16() -> &'static Mul16 {
143    MUL16.get_or_init(|| {
144        let (exp, log) = initialize_exp_log();
145
146        let mut mul16 = vec![[[0; 16]; 4]; GF_ORDER];
147
148        for log_m in 0..=GF_MODULUS {
149            let lut = &mut mul16[log_m as usize];
150            for i in 0..16 {
151                lut[0][i] = mul(i as GfElement, log_m, exp, log);
152                lut[1][i] = mul((i << 4) as GfElement, log_m, exp, log);
153                lut[2][i] = mul((i << 8) as GfElement, log_m, exp, log);
154                lut[3][i] = mul((i << 12) as GfElement, log_m, exp, log);
155            }
156        }
157
158        mul16.into_boxed_slice().try_into().unwrap()
159    })
160}
161
162/// Initializes and returns [`Skew`] table.
163#[allow(clippy::needless_range_loop)]
164pub fn initialize_skew() -> &'static Skew {
165    SKEW.get_or_init(|| {
166        let (exp, log) = initialize_exp_log();
167
168        let mut skew = Box::new([0; GF_MODULUS as usize]);
169
170        let mut temp = [0; GF_BITS - 1];
171
172        for i in 1..GF_BITS {
173            temp[i - 1] = 1 << i;
174        }
175
176        for m in 0..GF_BITS - 1 {
177            let step: usize = 1 << (m + 1);
178
179            skew[(1 << m) - 1] = 0;
180
181            for i in m..GF_BITS - 1 {
182                let s: usize = 1 << (i + 1);
183                let mut j = (1 << m) - 1;
184                while j < s {
185                    skew[j + s] = skew[j] ^ temp[i];
186                    j += step;
187                }
188            }
189
190            temp[m] =
191                GF_MODULUS - log[mul(temp[m], log[(temp[m] ^ 1) as usize], exp, log) as usize];
192
193            for i in m + 1..GF_BITS - 1 {
194                let sum = engine::add_mod(log[(temp[i] ^ 1) as usize], temp[m]);
195                temp[i] = mul(temp[i], sum, exp, log);
196            }
197        }
198
199        for i in 0..GF_MODULUS as usize {
200            skew[i] = log[skew[i] as usize];
201        }
202
203        skew
204    })
205}