libreda_logic/truth_table/
small_lut.rs1use crate::{
9 bitmanip,
10 traits::{
11 BooleanFunction, BooleanSystem, NumInputs, NumOutputs, PartialBooleanFunction,
12 PartialBooleanSystem, StaticNumInputs, StaticNumOutputs,
13 },
14 truth_table::{PartialTruthTable, TruthTable},
15};
16
17use super::TruthTableEdit;
18
19#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
21pub struct SmallTruthTable {
22 lut: u64,
23 num_inputs: u8,
24}
25
26#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
31pub struct SmallStaticTruthTable<const NUM_INPUTS: usize> {
32 lut: u64,
33}
34
35impl<const STATIC_NUM_INPUTS: usize> From<SmallStaticTruthTable<STATIC_NUM_INPUTS>>
36 for SmallTruthTable
37{
38 fn from(tt: SmallStaticTruthTable<STATIC_NUM_INPUTS>) -> Self {
40 Self {
41 lut: tt.lut,
42 num_inputs: STATIC_NUM_INPUTS as u8,
43 }
44 }
45}
46
47impl<const NUM_INPUTS: usize> TryFrom<SmallTruthTable> for SmallStaticTruthTable<NUM_INPUTS> {
48 type Error = ();
49
50 fn try_from(tt: SmallTruthTable) -> Result<Self, Self::Error> {
53 if tt.num_inputs() == NUM_INPUTS {
54 Ok(Self { lut: tt.lut })
55 } else {
56 Err(())
57 }
58 }
59}
60
61impl<const N: usize> NumInputs for SmallStaticTruthTable<N> {
62 fn num_inputs(&self) -> usize {
63 N
64 }
65}
66
67impl<const N: usize> NumOutputs for SmallStaticTruthTable<N> {
68 fn num_outputs(&self) -> usize {
69 1
70 }
71}
72
73const INDEX_COLUMNS: [u64; 6] = index_columns();
76
77pub trait SmallTT: TruthTable + TruthTableEdit + Sized + Copy {
79 fn table(&self) -> u64;
81
82 fn set_table(self, table: u64) -> Self;
84
85 fn invert_if(self, condition: bool) -> Self {
87 let mask = (1 << ((condition as u64) << self.num_inputs())) - 1;
88 self.set_table(self.table() ^ mask)
89 }
90
91 fn invert(self) -> Self {
93 let mask = (1 << (1 << self.num_inputs())) - 1;
94 self.set_table(self.table() ^ mask)
95 }
96
97 fn bitwise_and(self, other: Self) -> Self {
99 bitwise_op2(self, other, |a, b| a & b)
100 }
101
102 fn swap_inputs(self, i: usize, j: usize) -> Self {
104 assert!(i < self.num_inputs());
105 assert!(j < self.num_inputs());
106
107 let (i, j) = match i <= j {
109 false => (j, i),
110 true => (i, j),
111 };
112
113 let idx_col_i = INDEX_COLUMNS[i];
114 let idx_col_j = INDEX_COLUMNS[j];
115
116 let shift_amount = (1 << j) - (1 << i);
117
118 let select_pattern = (idx_col_i ^ idx_col_j) & idx_col_i;
120
121 let permuted_output_bits =
123 bitmanip::swap_bit_patterns(self.table(), select_pattern, 0, shift_amount);
124
125 self.set_table(permuted_output_bits)
126 }
127
128 fn invert_input(self, i: usize) -> Self {
130 let select_pattern = !INDEX_COLUMNS[i];
131 let shift_amount = 1 << i;
132
133 let permuted_output_bits =
134 bitmanip::swap_bit_patterns(self.table(), select_pattern, 0, shift_amount);
135
136 self.set_table(permuted_output_bits)
137 }
138
139 fn count_ones(&self) -> usize {
141 self.table().count_ones() as usize
142 }
143}
144
145impl SmallTT for SmallTruthTable {
146 fn table(&self) -> u64 {
147 self.lut
148 }
149
150 fn set_table(mut self, table: u64) -> Self {
151 self.lut = table;
152 self
153 }
154}
155
156impl<const NUM_INPUTS: usize> SmallTT for SmallStaticTruthTable<NUM_INPUTS> {
157 fn table(&self) -> u64 {
158 self.lut
159 }
160
161 fn set_table(mut self, table: u64) -> Self {
162 self.lut = table;
163 self
164 }
165}
166
167impl<const NUM_INPUTS: usize> SmallStaticTruthTable<NUM_INPUTS> {
168 pub fn new(f: impl Fn([bool; NUM_INPUTS]) -> bool) -> SmallStaticTruthTable<NUM_INPUTS> {
171 assert!(
172 NUM_INPUTS <= 6,
173 "Number of inputs ({NUM_INPUTS}) exceeds the maximum (6)."
174 );
175
176 let table = (0..1 << NUM_INPUTS)
177 .map(|input_bits| {
178 let mut bits = [false; NUM_INPUTS];
179 (0..NUM_INPUTS)
180 .for_each(|bit_idx| bits[bit_idx] = (input_bits >> bit_idx) & 1 == 1);
181 (f(bits) as u64) << input_bits
182 })
183 .fold(0, |a, b| a | b);
184
185 Self { lut: table }
186 }
187}
188
189impl<const NUM_INPUTS: usize> PartialBooleanSystem for SmallStaticTruthTable<NUM_INPUTS> {
190 type LiteralId = u32;
191
192 type TermId = ();
193
194 fn evaluate_term_partial(&self, term: &(), input_values: &[bool]) -> Option<bool> {
195 Some(self.evaluate_term(term, input_values))
196 }
197}
198
199impl<const NUM_INPUTS: usize> BooleanSystem for SmallStaticTruthTable<NUM_INPUTS> {
200 fn evaluate_term(&self, _term: &(), input_values: &[bool]) -> bool {
201 let bits = input_values
203 .iter()
204 .rev()
205 .fold(0, |acc, bit| (acc << 1) | (*bit as u64));
206
207 self.get_bit(bits)
208 }
209}
210
211impl<const NUM_INPUTS: usize> PartialBooleanFunction for SmallStaticTruthTable<NUM_INPUTS> {
212 fn partial_eval(&self, input_values: &[bool]) -> Option<bool> {
213 Some(self.eval(input_values))
214 }
215}
216
217impl<const NUM_INPUTS: usize> BooleanFunction for SmallStaticTruthTable<NUM_INPUTS> {
218 fn eval(&self, input_values: &[bool]) -> bool {
219 let bits = input_values
221 .iter()
222 .rev()
223 .fold(0, |acc, &bit| (acc << 1) | (bit as u64));
224
225 self.get_bit(bits)
226 }
227}
228
229impl<const NUM_INPUTS: usize> PartialTruthTable for SmallStaticTruthTable<NUM_INPUTS> {
230 fn partial_evaluate(&self, input_bits: u64) -> Option<bool> {
231 Some(self.get_bit(input_bits))
232 }
233}
234
235impl<const NUM_INPUTS: usize> TruthTable for SmallStaticTruthTable<NUM_INPUTS> {
236 fn get_bit(&self, bits: u64) -> bool {
237 let mask = (1 << NUM_INPUTS) - 1;
238 let index = bits & mask;
239 (self.lut >> index) & 1 == 1
240 }
241}
242
243impl<const NUM_INPUTS: usize> TruthTableEdit for SmallStaticTruthTable<NUM_INPUTS> {
244 fn set_bit(&mut self, bit_index: usize, value: bool) {
245 assert!(
246 bit_index < (1 << self.num_inputs()),
247 "bit index out of range"
248 );
249 let mask = !(1 << bit_index);
250 self.lut = (self.lut & mask) | ((value as u64) << bit_index);
251 }
252}
253
254impl SmallTruthTable {
255 pub fn new<const NUM_INPUTS: usize>(f: impl Fn([bool; NUM_INPUTS]) -> bool) -> Self {
258 SmallStaticTruthTable::new(f).into()
259 }
260
261 pub fn zero(num_inputs: usize) -> Self {
263 assert!(num_inputs <= 6);
264 let num_inputs = num_inputs as u8;
265 Self { lut: 0, num_inputs }
266 }
267
268 pub const fn from_table(table: u64, num_inputs: usize) -> Self {
270 assert!(num_inputs <= 6);
271 Self {
272 lut: table,
273 num_inputs: num_inputs as u8,
274 }
275 }
276
277 pub fn from_boolean_function<F: BooleanFunction>(f: &F) -> Self {
282 let mut buffer = [false; 6];
283
284 let n_inputs = f.num_inputs();
285 assert!(
286 n_inputs <= 6,
287 "number of inputs must be <= 6 but is {n_inputs}"
288 );
289
290 let mut lut = 0u64;
291
292 for i in 0..(1 << n_inputs) {
293 for (j, item) in buffer.iter_mut().enumerate().take(n_inputs) {
294 *item = ((i >> j) & 1) == 1;
295 }
296
297 let output = f.eval(&buffer);
298 lut |= (output as u64) << i;
299 }
300
301 Self {
302 lut,
303 num_inputs: n_inputs as u8,
304 }
305 }
306}
307
308fn bitwise_op2<TT: SmallTT>(tt1: TT, tt2: TT, binary_op: impl Fn(u64, u64) -> u64) -> TT {
310 assert_eq!(tt1.num_inputs(), tt2.num_inputs());
311 tt1.set_table(binary_op(tt1.table(), tt2.table()))
312}
313
314const fn index_columns() -> [u64; 6] {
331 const N: usize = 6;
332 let mut index_columns = [0; N];
333 let mut state = !0u64;
334 let mut i = N as isize - 1;
335 while i >= 0 {
336 let shifted_state = state >> (1 << i);
337 state ^= shifted_state;
338 index_columns[i as usize] = state;
339 i -= 1;
340 }
341 index_columns
342}
343
344#[test]
345fn test_index_columns() {
346 let cols = index_columns();
347 assert_eq!(
348 cols[0],
349 0b1010101010101010101010101010101010101010101010101010101010101010
350 );
351 assert_eq!(
352 cols[1],
353 0b1100110011001100110011001100110011001100110011001100110011001100
354 );
355}
356
357#[test]
358fn test_swap_inputs() {
359 let mux_ab = SmallTruthTable::new(|[sel, a, b]| if sel { b } else { a });
360 assert_eq!(mux_ab.eval(&[false, false, true]), false);
361 assert_eq!(mux_ab.eval(&[true, false, true]), true);
362
363 let mux_ba = mux_ab.swap_inputs(1, 2);
364 assert_eq!(mux_ba.eval(&[false, false, true]), true);
365 assert_eq!(mux_ba.eval(&[true, false, true]), false);
366}
367
368#[test]
369fn test_swap_inputs_random_table() {
370 let tt = SmallTruthTable {
372 lut: 0xe3b0c44298fc1c14, num_inputs: 6,
374 };
375
376 for i in 0..6 {
377 for j in 0..6 {
378 let tt_swapped = tt.swap_inputs(i, j);
379
380 for inputs in 0..(1 << 6) {
382 let inputs_swapped = bitmanip::swap_bits(inputs, i, j);
383 assert_eq!(tt.get_bit(inputs), tt_swapped.get_bit(inputs_swapped));
384 }
385 }
386 }
387}
388#[test]
389fn test_invert_inputs_random_table() {
390 let tt = SmallTruthTable {
392 lut: 0xe3b0c44298fc1c14, num_inputs: 6,
394 };
395
396 for i in 0..6 {
397 let tt_swapped = tt.invert_input(i);
398
399 for inputs in 0..(1 << 6) {
401 let inputs_inverted_i = inputs ^ (1 << i);
402 assert_eq!(tt.get_bit(inputs), tt_swapped.get_bit(inputs_inverted_i));
403 }
404 }
405}
406
407pub mod truth_table_library {
409 use super::SmallTruthTable;
410
411 pub fn one() -> SmallTruthTable {
413 SmallTruthTable::new(|[]| true)
414 }
415
416 pub fn zero() -> SmallTruthTable {
418 SmallTruthTable::new(|[]| false)
419 }
420
421 pub fn identity1() -> SmallTruthTable {
423 SmallTruthTable::new(|[a]| a)
424 }
425
426 pub fn input_projection(num_inputs: usize, project_input: usize) -> SmallTruthTable {
428 assert!(project_input < num_inputs, "selected input out of range");
429 assert!(num_inputs <= 6, "no more than 6 inputs supported");
430 let num_tt_bits = 1 << num_inputs; let tt_bits = (0..num_tt_bits).map(|idx| ((idx >> project_input) & 1) << idx);
432
433 let tt = tt_bits.fold(0, |a, b| a | b);
435
436 SmallTruthTable::from_table(tt, num_inputs)
437 }
438
439 pub fn inv1() -> SmallTruthTable {
441 SmallTruthTable::new(|[a]| !a)
442 }
443
444 pub fn and(num_inputs: usize) -> SmallTruthTable {
446 assert!(num_inputs <= 6, "no more than 6 inputs supported");
447 let lut = 1 << ((1 << num_inputs) - 1);
448 let num_inputs = num_inputs as u8;
449 SmallTruthTable { lut, num_inputs }
450 }
451
452 pub fn or(num_inputs: usize) -> SmallTruthTable {
454 assert!(num_inputs <= 6, "no more than 6 inputs supported");
455 let lut = !1 & ((1 << (num_inputs + 1)) - 1);
456 let num_inputs = num_inputs as u8;
457 SmallTruthTable { lut, num_inputs }
458 }
459
460 pub fn and2() -> SmallTruthTable {
462 SmallTruthTable::new(|[a, b]| a & b)
463 }
464
465 pub fn or2() -> SmallTruthTable {
467 SmallTruthTable::new(|[a, b]| a | b)
468 }
469
470 pub fn nand2() -> SmallTruthTable {
472 SmallTruthTable::new(|[a, b]| !(a & b))
473 }
474
475 pub fn nor2() -> SmallTruthTable {
477 SmallTruthTable::new(|[a, b]| !(a | b))
478 }
479
480 pub fn xor2() -> SmallTruthTable {
482 SmallTruthTable::new(|[a, b]| a ^ b)
483 }
484
485 pub fn eq2() -> SmallTruthTable {
487 SmallTruthTable::new(|[a, b]| a == b)
488 }
489
490 pub fn implication() -> SmallTruthTable {
492 SmallTruthTable::new(|[a, b]| !a & b)
493 }
494
495 pub fn converse() -> SmallTruthTable {
497 SmallTruthTable::new(|[a, b]| a & !b)
498 }
499
500 pub fn less_than() -> SmallTruthTable {
502 SmallTruthTable::new(|[a, b]| a < b)
503 }
504
505 pub fn less_or_equal_than() -> SmallTruthTable {
507 SmallTruthTable::new(|[a, b]| a <= b)
508 }
509
510 pub fn greater_than() -> SmallTruthTable {
512 SmallTruthTable::new(|[a, b]| a > b)
513 }
514
515 pub fn greater_or_equal_than() -> SmallTruthTable {
517 SmallTruthTable::new(|[a, b]| a >= b)
518 }
519
520 pub fn maj3() -> SmallTruthTable {
522 SmallTruthTable::new(|[a, b, c]| (a as u8) + (b as u8) + (c as u8) >= 2)
523 }
524}
525
526#[test]
527fn test_create_small_truth_table() {
528 let t = SmallTruthTable::new(|[a, b]| a ^ b);
529 assert_eq!(t.num_inputs(), 2);
530 assert_eq!(t.lut, 0b0110)
531}
532
533#[test]
534fn test_input_projection() {
535 use truth_table_library::input_projection;
536 for num_inputs in 0..=6 {
537 for selected_input in 0..num_inputs {
538 let tt = input_projection(num_inputs, selected_input);
539 for i in 0..tt.size() {
540 assert_eq!(tt.get_bit(i as u64), ((i >> selected_input) & 1) == 1);
541 }
542 }
543 }
544}
545
546#[test]
547fn test_eval_small_truth_table() {
548 let maj3 = SmallTruthTable::new(|[a, b, c]| (a as u8) + (b as u8) + (c as u8) >= 2);
549
550 assert_eq!(maj3.get_bit(0b000), false);
551 assert_eq!(maj3.get_bit(0b001), false);
552 assert_eq!(maj3.get_bit(0b100), false);
553 assert_eq!(maj3.get_bit(0b011), true);
554}
555
556impl NumInputs for SmallTruthTable {
557 fn num_inputs(&self) -> usize {
558 self.num_inputs as usize
559 }
560}
561
562impl NumOutputs for SmallTruthTable {
563 fn num_outputs(&self) -> usize {
564 1
565 }
566}
567
568impl PartialBooleanSystem for SmallTruthTable {
569 type LiteralId = u32;
570
571 type TermId = ();
572
573 fn evaluate_term_partial(&self, term: &(), input_values: &[bool]) -> Option<bool> {
574 Some(self.evaluate_term(term, input_values))
575 }
576}
577
578impl BooleanSystem for SmallTruthTable {
579 fn evaluate_term(&self, _term: &(), input_values: &[bool]) -> bool {
580 let bits = input_values
582 .iter()
583 .rev()
584 .fold(0, |acc, bit| (acc << 1) | (*bit as u64));
585
586 self.get_bit(bits)
587 }
588}
589
590impl PartialBooleanFunction for SmallTruthTable {
591 fn partial_eval(&self, input_values: &[bool]) -> Option<bool> {
592 Some(self.eval(input_values))
593 }
594}
595
596impl PartialTruthTable for SmallTruthTable {
597 fn partial_evaluate(&self, input_bits: u64) -> Option<bool> {
598 Some(self.get_bit(input_bits))
599 }
600}
601
602impl TruthTableEdit for SmallTruthTable {
603 fn set_bit(&mut self, bit_index: usize, value: bool) {
604 assert!(
605 bit_index < (1 << self.num_inputs()),
606 "bit index out of range"
607 );
608 let mask = !(1 << bit_index);
609 self.lut = (self.lut & mask) | ((value as u64) << bit_index);
610 }
611}
612
613impl StaticNumOutputs<1> for SmallTruthTable {}
614
615impl<const N: usize> StaticNumOutputs<1> for SmallStaticTruthTable<N> {}
616
617impl<const N: usize> StaticNumInputs<N> for SmallStaticTruthTable<N> {}
618
619impl BooleanFunction for SmallTruthTable {
620 fn eval(&self, input_values: &[bool]) -> bool {
621 let bits = input_values
623 .iter()
624 .rev()
625 .fold(0, |acc, &bit| (acc << 1) | (bit as u64));
626
627 self.get_bit(bits)
628 }
629}
630
631impl TruthTable for SmallTruthTable {
632 fn get_bit(&self, bits: u64) -> bool {
633 let mask = (1 << self.num_inputs) - 1;
634 let index = bits & mask;
635 (self.lut >> index) & 1 == 1
636 }
637}
638
639