balanced_ternary/
store.rs

1use crate::concepts::DigitOperate;
2use crate::{Digit, Ternary};
3use alloc::string::ToString;
4use alloc::vec::Vec;
5use core::fmt::Display;
6use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Sub};
7
8/// A struct to store 5 ternary digits (~7.8 bits) value into one byte.
9///
10/// `TritsChunks` helps store ternary numbers into a compact memory structure.
11///
12/// From `0` to `± 121`.
13#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
14#[repr(transparent)]
15pub struct TritsChunk(i8);
16
17impl TritsChunk {
18    /// Creates a `TritsChunk` from a given decimal value.
19    ///
20    /// # Arguments
21    ///
22    /// * `from` - An `i8` value representing the decimal value to be converted into a `TritsChunk`.
23    ///
24    /// # Panics
25    ///
26    /// This function panics if the input value is out of the valid range `-121..=121`.
27    ///
28    /// # Example
29    ///
30    /// ```
31    /// use balanced_ternary::TritsChunk;
32    ///
33    /// let chunk = TritsChunk::from_dec(42);
34    /// assert_eq!(chunk.to_dec(), 42);
35    /// ```
36    pub fn from_dec(from: i8) -> Self {
37        if !(-121..=121).contains(&from) {
38            panic!("TritsChunk::from_dec(): Invalid value: {}", from);
39        }
40        Self(from)
41    }
42
43    /// Converts the `TritsChunk` into its decimal representation.
44    ///
45    /// # Returns
46    ///
47    /// An `i8` value representing the decimal form of the `TritsChunk`.
48    ///
49    /// # Example
50    ///
51    /// ```
52    /// use balanced_ternary::TritsChunk;
53    ///
54    /// let chunk = TritsChunk::from_dec(42);
55    /// assert_eq!(chunk.to_dec(), 42);
56    /// ```
57    pub fn to_dec(&self) -> i8 {
58        self.0
59    }
60
61    /// Converts the `TritsChunk` into its ternary representation.
62    ///
63    /// # Returns
64    ///
65    /// A `Ternary` type representing the ternary form of the `TritsChunk`.
66    ///
67    /// # Example
68    ///
69    /// ```
70    /// use balanced_ternary::{TritsChunk, Ternary};
71    ///
72    /// let chunk = TritsChunk::from_dec(42);
73    /// let ternary = chunk.to_ternary();
74    /// assert_eq!(ternary.to_dec(), 42);
75    /// ```
76    pub fn to_ternary(&self) -> Ternary {
77        Ternary::from_dec(self.0 as i64)
78    }
79
80    /// Converts the `TritsChunk` into its fixed-length ternary representation.
81    ///
82    /// # Returns
83    ///
84    /// A `Ternary` type representing the 5-digit fixed-length ternary form of the `TritsChunk`.
85    ///
86    /// # Example
87    ///
88    /// ```
89    /// use balanced_ternary::{TritsChunk, Ternary};
90    ///
91    /// let chunk = TritsChunk::from_dec(42);
92    /// let fixed_ternary = chunk.to_fixed_ternary();
93    /// assert_eq!(fixed_ternary.to_dec(), 42);
94    /// assert_eq!(fixed_ternary.to_digit_slice().len(), 5);
95    /// ```
96    pub fn to_fixed_ternary(&self) -> Ternary {
97        Ternary::from_dec(self.0 as i64).with_length(5)
98    }
99
100    /// Converts the `TritsChunk` into a vector of its individual ternary digits.
101    ///
102    /// # Returns
103    ///
104    /// A `Vec<Digit>` representing the individual ternary digits of the `TritsChunk`.
105    ///
106    /// The resulting vector will always contain 5 digits since the `TritsChunk` is
107    /// represented in a fixed-length ternary form.
108    ///
109    /// # Example
110    ///
111    /// ```
112    /// use balanced_ternary::{TritsChunk, Digit};
113    ///
114    /// let chunk = TritsChunk::from_dec(42);
115    /// let digits: Vec<Digit> = chunk.to_digits();
116    /// assert_eq!(digits.len(), 5);
117    /// ```
118    pub fn to_digits(&self) -> Vec<Digit> {
119        self.to_fixed_ternary().to_digit_slice().to_vec()
120    }
121
122    /// Creates a `TritsChunk` from a given `Ternary` value.
123    ///
124    /// # Arguments
125    ///
126    /// * `ternary` - A `Ternary` value to be converted into a `TritsChunk`.
127    ///
128    /// # Panics
129    ///
130    /// This function panics if the provided `ternary` value has a logarithmic length greater than 5,
131    /// indicating that it cannot be represented by a single `TritsChunk`.
132    ///
133    /// # Example
134    ///
135    /// ```
136    /// use balanced_ternary::{TritsChunk, Ternary};
137    ///
138    /// let ternary = Ternary::from_dec(42);
139    /// let chunk = TritsChunk::from_ternary(ternary);
140    /// assert_eq!(chunk.to_dec(), 42);
141    /// ```
142    pub fn from_ternary(ternary: Ternary) -> Self {
143        if ternary.log() > 5 {
144            panic!(
145                "TritsChunk::from_ternary(): Ternary is too long: {}",
146                ternary.to_string()
147            );
148        }
149        Self(ternary.to_dec() as i8)
150    }
151}
152
153/// Offers a compact structure to store a ternary number.
154///
155/// - A [Ternary] is 1 byte long per [Digit]. An 8 (16, 32, 64) digits ternary number is 8 (16, 32, 64) bytes long.
156/// - A [DataTernary] is stored into [TritsChunk]. An 8 (16, 32, 64) digits ternary number with this structure is 2 (4, 7, 13) bytes long (1 byte for 5 digits).
157///
158/// Use the [Ternary] type to execute operations on numbers and [DataTernary] to store numbers.
159#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
160pub struct DataTernary {
161    chunks: Vec<TritsChunk>,
162}
163
164impl DataTernary {
165    /// Creates a new instance of `DataTernary` from a given `Ternary` value.
166    ///
167    /// This method ensures that the total number of ternary digits is a multiple of 5
168    /// by padding as necessary. It then divides the ternary number into chunks of
169    /// 5 digits each, which are stored in the `DataTernary` structure.
170    ///
171    /// # Arguments
172    ///
173    /// * `ternary` - A `Ternary` value to be converted into a `DataTernary`.
174    ///
175    /// # Returns
176    ///
177    /// A new `DataTernary` instance containing the converted chunks.
178    ///
179    /// # Example
180    ///
181    /// ```
182    /// use balanced_ternary::{DataTernary, Ternary};
183    ///
184    /// let ternary = Ternary::from_dec(42);
185    /// let data_ternary = DataTernary::from_ternary(ternary);
186    /// assert_eq!(data_ternary.to_dec(), 42);
187    /// ```
188    pub fn from_ternary(ternary: Ternary) -> Self {
189        let len = ternary.log();
190        let diff = (5 - len % 5) % 5;
191        let ternary = ternary.with_length(len + diff);
192        let mut chunks = Vec::new();
193        for i in 0..(ternary.log() / 5) {
194            let digits = ternary.to_digit_slice()[i * 5..(i + 1) * 5].to_vec();
195            chunks.push(TritsChunk::from_ternary(Ternary::new(digits)));
196        }
197        Self { chunks }
198    }
199
200    /// Converts a `DataTernary` into its equivalent `Ternary` representation.
201    ///
202    /// This function iterates over all the `TritsChunk` instances in the `DataTernary`,
203    /// extracts their ternary representations, and reconstructs them into the full
204    /// `Ternary` value. The resulting `Ternary` value may be trimmed to remove
205    /// any leading zeroes in its ternary digit representation.
206    ///
207    /// # Returns
208    ///
209    /// A `Ternary` value that represents the combined ternary digits of the
210    /// `DataTernary`.
211    ///
212    /// # Example
213    ///
214    /// ```
215    /// use balanced_ternary::{DataTernary, Ternary};
216    ///
217    /// let ternary = Ternary::from_dec(42);
218    /// let data_ternary = DataTernary::from_ternary(ternary.clone());
219    /// assert_eq!(data_ternary.to_ternary(), ternary);
220    /// ```
221    pub fn to_ternary(&self) -> Ternary {
222        let mut digits = Vec::new();
223        for chunk in &self.chunks {
224            digits.extend(chunk.to_ternary().to_digit_slice());
225        }
226        Ternary::new(digits).trim()
227    }
228
229    /// Converts the `DataTernary` into its fixed-length `Ternary` representation.
230    ///
231    /// This method iterates over all the `TritsChunk` instances in the `DataTernary` and
232    /// extracts and combines their ternary digits into a single `Ternary` value.
233    /// The resulting `Ternary` value will contain a fixed number of digits without trimming
234    /// or removing leading zeroes.
235    ///
236    /// # Returns
237    ///
238    /// A `Ternary` value representing the combined fixed-length ternary digits of the `DataTernary`.
239    ///
240    /// # Example
241    ///
242    /// ```
243    /// use balanced_ternary::{DataTernary, Ternary};
244    ///
245    /// let ternary = Ternary::from_dec(42);
246    /// let data_ternary = DataTernary::from_ternary(ternary);
247    /// let fixed_ternary = data_ternary.to_fixed_ternary();
248    /// assert_eq!(fixed_ternary.to_dec(), 42); // When properly encoded
249    /// ```
250    pub fn to_fixed_ternary(&self) -> Ternary {
251        let mut digits = Vec::new();
252        for chunk in &self.chunks {
253            digits.extend(chunk.to_digits());
254        }
255        Ternary::new(digits).trim()
256    }
257
258    /// Converts the `DataTernary` into a vector of ternary digits.
259    ///
260    /// This method first converts the `DataTernary` structure into its `Ternary` representation,
261    /// trims any leading zeroes, and then returns the sequence of ternary digits as a `Vec<Digit>`.
262    ///
263    /// # Returns
264    ///
265    /// A `Vec<Digit>` containing the ternary digits that represent the `DataTernary` value.
266    ///
267    /// # Example
268    ///
269    /// ```
270    /// use balanced_ternary::{DataTernary, Digit, Ternary};
271    ///
272    /// let ternary = Ternary::from_dec(42);
273    /// let data_ternary = DataTernary::from_ternary(ternary);
274    /// let digits = data_ternary.to_digits();
275    /// assert_eq!(digits, vec![Digit::Pos, Digit::Neg, Digit::Neg, Digit::Neg, Digit::Zero]);
276    /// ```
277    pub fn to_digits(&self) -> Vec<Digit> {
278        self.to_ternary().trim().to_digit_slice().to_vec()
279    }
280
281    /// Converts a decimal number into a `DataTernary` structure.
282    ///
283    /// This method takes a signed 64-bit integer as input and converts it into a
284    /// `Ternary` representation, which is then stored in the compact `DataTernary`
285    /// structure. The conversion ensures that the ternary representation uses
286    /// fixed-length chunks for efficient storage.
287    ///
288    /// # Arguments
289    ///
290    /// * `from` - A signed 64-bit integer value to be converted into `DataTernary`.
291    ///
292    /// # Returns
293    ///
294    /// A `DataTernary` instance that represents the given decimal number.
295    ///
296    /// # Example
297    ///
298    /// ```
299    /// use balanced_ternary::{DataTernary};
300    ///
301    /// let data_ternary = DataTernary::from_dec(42);
302    /// assert_eq!(data_ternary.to_dec(), 42);
303    /// ```
304    pub fn from_dec(from: i64) -> Self {
305        Self::from_ternary(Ternary::from_dec(from))
306    }
307
308    /// Converts a `DataTernary` into its decimal representation.
309    ///
310    /// This method reconstructs the ternary value represented by the `DataTernary`
311    /// structure and converts it into the corresponding signed 64-bit decimal integer.
312    ///
313    /// # Returns
314    ///
315    /// A signed 64-bit integer (`i64`) representing the decimal equivalent of the
316    /// `DataTernary` structure.
317    ///
318    /// # Example
319    ///
320    /// ```
321    /// use balanced_ternary::{DataTernary};
322    ///
323    /// let data_ternary = DataTernary::from_dec(42);
324    /// let decimal = data_ternary.to_dec();
325    /// assert_eq!(decimal, 42);
326    /// ```
327    pub fn to_dec(&self) -> i64 {
328        self.to_ternary().to_dec()
329    }
330}
331
332impl Display for DataTernary {
333    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
334        for chunk in &self.chunks {
335            write!(f, "{}", chunk.to_fixed_ternary())?;
336        }
337        Ok(())
338    }
339}
340
341impl From<Ternary> for DataTernary {
342    fn from(value: Ternary) -> Self {
343        Self::from_ternary(value)
344    }
345}
346
347impl From<DataTernary> for Ternary {
348    fn from(value: DataTernary) -> Self {
349        value.to_ternary()
350    }
351}
352
353/// A struct to store 40 ternary digits (~63.398 bits) value into one `i64`.
354#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
355#[repr(transparent)]
356pub struct Ter40(i64);
357
358impl Ter40 {
359    pub fn from_dec(from: i64) -> Self {
360        Self(from)
361    }
362    pub fn to_dec(&self) -> i64 {
363        self.0
364    }
365    pub fn from_ternary(ternary: Ternary) -> Self {
366        Self(ternary.to_dec())
367    }
368    pub fn to_ternary(&self) -> Ternary {
369        Ternary::from_dec(self.0).with_length(40)
370    }
371}
372
373impl DigitOperate for Ter40 {
374    fn to_digits(&self) -> Vec<Digit> {
375        self.to_ternary().to_digits()
376    }
377
378    fn digit(&self, index: usize) -> Option<Digit> {
379        self.to_ternary().digit(index)
380    }
381
382    fn each(&self, f: impl Fn(Digit) -> Digit) -> Self
383    where
384        Self: Sized,
385    {
386        Self(self.to_ternary().each(f).to_dec())
387    }
388
389    fn each_with(&self, f: impl Fn(Digit, Digit) -> Digit, other: Digit) -> Self
390    where
391        Self: Sized,
392    {
393        Self(self.to_ternary().each_with(f, other).to_dec())
394    }
395
396    fn each_zip(&self, f: impl Fn(Digit, Digit) -> Digit, other: Self) -> Self
397    where
398        Self: Sized,
399    {
400        Self(self.to_ternary().each_zip(f, other.to_ternary()).to_dec())
401    }
402
403    fn each_zip_carry(&self, f: impl Fn(Digit, Digit, Digit) -> (Digit, Digit), other: Self) -> Self
404    where
405        Self: Sized,
406    {
407        Self(
408            self.to_ternary()
409                .each_zip_carry(f, other.to_ternary())
410                .to_dec(),
411        )
412    }
413}
414
415impl Display for Ter40 {
416    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
417        write!(f, "{}", self.to_ternary())
418    }
419}
420
421impl Add for Ter40 {
422    type Output = Self;
423    fn add(self, other: Self) -> Self::Output {
424        Self(self.0 + other.0)
425    }
426}
427impl Sub for Ter40 {
428    type Output = Self;
429    fn sub(self, other: Self) -> Self::Output {
430        Self(self.0 - other.0)
431    }
432}
433impl Mul for Ter40 {
434    type Output = Self;
435    fn mul(self, other: Self) -> Self::Output {
436        Self(self.0 * other.0)
437    }
438}
439impl Div for Ter40 {
440    type Output = Self;
441    fn div(self, other: Self) -> Self::Output {
442        Self(self.0 / other.0)
443    }
444}
445
446impl Neg for Ter40 {
447    type Output = Self;
448    fn neg(self) -> Self::Output {
449        Self(-self.0)
450    }
451}
452
453impl BitAnd for Ter40 {
454    type Output = Self;
455    fn bitand(self, other: Self) -> Self::Output {
456        self.each_zip(Digit::bitand, other)
457    }
458}
459
460impl BitOr for Ter40 {
461    type Output = Self;
462    fn bitor(self, other: Self) -> Self::Output {
463        self.each_zip(Digit::bitor, other)
464    }
465}
466
467impl BitXor for Ter40 {
468    type Output = Self;
469    fn bitxor(self, other: Self) -> Self::Output {
470        self.each_zip(Digit::bitxor, other)
471    }
472}
473
474impl From<i64> for Ter40 {
475    fn from(value: i64) -> Self {
476        Self(value)
477    }
478}
479
480impl From<Ter40> for i64 {
481    fn from(value: Ter40) -> Self {
482        value.0
483    }
484}
485
486impl From<Ternary> for Ter40 {
487    fn from(value: Ternary) -> Self {
488        Self::from_ternary(value)
489    }
490}
491
492impl From<Ter40> for Ternary {
493    fn from(value: Ter40) -> Self {
494        value.to_ternary()
495    }
496}
497
498#[cfg(test)]
499#[test]
500fn single_chunk_creation() {
501    use crate::Ternary;
502
503    let ternary = Ternary::parse("+-0-+");
504    let data = DataTernary::from_ternary(ternary.clone());
505
506    assert_eq!(data.chunks.len(), 1);
507    assert_eq!(data.to_ternary(), ternary);
508}
509
510#[cfg(test)]
511#[test]
512fn round_trip() {
513    use crate::Ternary;
514
515    let ternary = Ternary::parse("+0-0++-");
516    let data = DataTernary::from_ternary(ternary.clone());
517
518    assert_eq!(data.to_ternary(), ternary);
519}