balanced_ternary/
store.rs

1use crate::{Digit, Ternary};
2use alloc::string::ToString;
3use alloc::vec::Vec;
4use core::fmt::Display;
5
6/// A struct to store 5 ternary digits (~7.8 bits) value into one byte.
7///
8/// `TritsChunks` helps store ternary numbers into a compact memory structure.
9///
10/// From `0` to `± 121`.
11#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
12#[repr(transparent)]
13pub struct TritsChunk(i8);
14
15impl TritsChunk {
16    /// Creates a `TritsChunk` from a given decimal value.
17    ///
18    /// # Arguments
19    ///
20    /// * `from` - An `i8` value representing the decimal value to be converted into a `TritsChunk`.
21    ///
22    /// # Panics
23    ///
24    /// This function panics if the input value is out of the valid range `-121..=121`.
25    ///
26    /// # Example
27    ///
28    /// ```
29    /// use balanced_ternary::TritsChunk;
30    ///
31    /// let chunk = TritsChunk::from_dec(42);
32    /// assert_eq!(chunk.to_dec(), 42);
33    /// ```
34    pub fn from_dec(from: i8) -> Self {
35        if !(-121..=121).contains(&from) {
36            panic!("TritsChunk::from_dec(): Invalid value: {}", from);
37        }
38        Self(from)
39    }
40
41    /// Converts the `TritsChunk` into its decimal representation.
42    ///
43    /// # Returns
44    ///
45    /// An `i8` value representing the decimal form of the `TritsChunk`.
46    ///
47    /// # Example
48    ///
49    /// ```
50    /// use balanced_ternary::TritsChunk;
51    ///
52    /// let chunk = TritsChunk::from_dec(42);
53    /// assert_eq!(chunk.to_dec(), 42);
54    /// ```
55    pub fn to_dec(&self) -> i8 {
56        self.0
57    }
58
59    /// Converts the `TritsChunk` into its ternary representation.
60    ///
61    /// # Returns
62    ///
63    /// A `Ternary` type representing the ternary form of the `TritsChunk`.
64    ///
65    /// # Example
66    ///
67    /// ```
68    /// use balanced_ternary::{TritsChunk, Ternary};
69    ///
70    /// let chunk = TritsChunk::from_dec(42);
71    /// let ternary = chunk.to_ternary();
72    /// assert_eq!(ternary.to_dec(), 42);
73    /// ```
74    pub fn to_ternary(&self) -> Ternary {
75        Ternary::from_dec(self.0 as i64)
76    }
77
78    /// Converts the `TritsChunk` into its fixed-length ternary representation.
79    ///
80    /// # Returns
81    ///
82    /// A `Ternary` type representing the 5-digit fixed-length ternary form of the `TritsChunk`.
83    ///
84    /// # Example
85    ///
86    /// ```
87    /// use balanced_ternary::{TritsChunk, Ternary};
88    ///
89    /// let chunk = TritsChunk::from_dec(42);
90    /// let fixed_ternary = chunk.to_fixed_ternary();
91    /// assert_eq!(fixed_ternary.to_dec(), 42);
92    /// assert_eq!(fixed_ternary.to_digit_slice().len(), 5);
93    /// ```
94    pub fn to_fixed_ternary(&self) -> Ternary {
95        Ternary::from_dec(self.0 as i64).with_length(5)
96    }
97
98    /// Converts the `TritsChunk` into a vector of its individual ternary digits.
99    ///
100    /// # Returns
101    ///
102    /// A `Vec<Digit>` representing the individual ternary digits of the `TritsChunk`.
103    ///
104    /// The resulting vector will always contain 5 digits since the `TritsChunk` is
105    /// represented in a fixed-length ternary form.
106    ///
107    /// # Example
108    ///
109    /// ```
110    /// use balanced_ternary::{TritsChunk, Digit};
111    ///
112    /// let chunk = TritsChunk::from_dec(42);
113    /// let digits: Vec<Digit> = chunk.to_digits();
114    /// assert_eq!(digits.len(), 5);
115    /// ```
116    pub fn to_digits(&self) -> Vec<Digit> {
117        self.to_fixed_ternary().to_digit_slice().to_vec()
118    }
119
120    /// Creates a `TritsChunk` from a given `Ternary` value.
121    ///
122    /// # Arguments
123    ///
124    /// * `ternary` - A `Ternary` value to be converted into a `TritsChunk`.
125    ///
126    /// # Panics
127    ///
128    /// This function panics if the provided `ternary` value has a logarithmic length greater than 5,
129    /// indicating that it cannot be represented by a single `TritsChunk`.
130    ///
131    /// # Example
132    ///
133    /// ```
134    /// use balanced_ternary::{TritsChunk, Ternary};
135    ///
136    /// let ternary = Ternary::from_dec(42);
137    /// let chunk = TritsChunk::from_ternary(ternary);
138    /// assert_eq!(chunk.to_dec(), 42);
139    /// ```
140    pub fn from_ternary(ternary: Ternary) -> Self {
141        if ternary.log() > 5 {
142            panic!(
143                "TritsChunk::from_ternary(): Ternary is too long: {}",
144                ternary.to_string()
145            );
146        }
147        Self(ternary.to_dec() as i8)
148    }
149}
150
151/// Offers a compact structure to store a ternary number.
152///
153/// - A [Ternary] is 1 byte long per [Digit]. An 8 (16, 32, 64) digits ternary number is 8 (16, 32, 64) bytes long.
154/// - 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).
155///
156/// Use the [Ternary] type to execute operations on numbers and [DataTernary] to store numbers.
157#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
158pub struct DataTernary {
159    chunks: Vec<TritsChunk>,
160}
161
162impl DataTernary {
163    /// Creates a new instance of `DataTernary` from a given `Ternary` value.
164    ///
165    /// This method ensures that the total number of ternary digits is a multiple of 5
166    /// by padding as necessary. It then divides the ternary number into chunks of
167    /// 5 digits each, which are stored in the `DataTernary` structure.
168    ///
169    /// # Arguments
170    ///
171    /// * `ternary` - A `Ternary` value to be converted into a `DataTernary`.
172    ///
173    /// # Returns
174    ///
175    /// A new `DataTernary` instance containing the converted chunks.
176    ///
177    /// # Example
178    ///
179    /// ```
180    /// use balanced_ternary::{DataTernary, Ternary};
181    ///
182    /// let ternary = Ternary::from_dec(42);
183    /// let data_ternary = DataTernary::from_ternary(ternary);
184    /// assert_eq!(data_ternary.to_dec(), 42);
185    /// ```
186    pub fn from_ternary(ternary: Ternary) -> Self {
187        let len = ternary.log();
188        let diff = 5 - (len % 5);
189        let ternary = ternary.with_length(len + diff);
190        let mut chunks = Vec::new();
191        for i in 0..(ternary.log() / 5) {
192            let digits = ternary.to_digit_slice()[i * 5..(i + 1) * 5].to_vec();
193            chunks.push(TritsChunk::from_ternary(Ternary::new(digits)));
194        }
195        Self { chunks }
196    }
197
198    /// Converts a `DataTernary` into its equivalent `Ternary` representation.
199    ///
200    /// This function iterates over all the `TritsChunk` instances in the `DataTernary`,
201    /// extracts their ternary representations, and reconstructs them into the full
202    /// `Ternary` value. The resulting `Ternary` value may be trimmed to remove
203    /// any leading zeroes in its ternary digit representation.
204    ///
205    /// # Returns
206    ///
207    /// A `Ternary` value that represents the combined ternary digits of the
208    /// `DataTernary`.
209    ///
210    /// # Example
211    ///
212    /// ```
213    /// use balanced_ternary::{DataTernary, Ternary};
214    ///
215    /// let ternary = Ternary::from_dec(42);
216    /// let data_ternary = DataTernary::from_ternary(ternary.clone());
217    /// assert_eq!(data_ternary.to_ternary(), ternary);
218    /// ```
219    pub fn to_ternary(&self) -> Ternary {
220        let mut digits = Vec::new();
221        for chunk in &self.chunks {
222            digits.extend(chunk.to_ternary().to_digit_slice());
223        }
224        Ternary::new(digits).trim()
225    }
226
227    /// Converts the `DataTernary` into its fixed-length `Ternary` representation.
228    ///
229    /// This method iterates over all the `TritsChunk` instances in the `DataTernary` and
230    /// extracts and combines their ternary digits into a single `Ternary` value.
231    /// The resulting `Ternary` value will contain a fixed number of digits without trimming
232    /// or removing leading zeroes.
233    ///
234    /// # Returns
235    ///
236    /// A `Ternary` value representing the combined fixed-length ternary digits of the `DataTernary`.
237    ///
238    /// # Example
239    ///
240    /// ```
241    /// use balanced_ternary::{DataTernary, Ternary};
242    ///
243    /// let ternary = Ternary::from_dec(42);
244    /// let data_ternary = DataTernary::from_ternary(ternary);
245    /// let fixed_ternary = data_ternary.to_fixed_ternary();
246    /// assert_eq!(fixed_ternary.to_dec(), 42); // When properly encoded
247    /// ```
248    pub fn to_fixed_ternary(&self) -> Ternary {
249        let mut digits = Vec::new();
250        for chunk in &self.chunks {
251            digits.extend(chunk.to_digits());
252        }
253        Ternary::new(digits).trim()
254    }
255
256    /// Converts the `DataTernary` into a vector of ternary digits.
257    ///
258    /// This method first converts the `DataTernary` structure into its `Ternary` representation,
259    /// trims any leading zeroes, and then returns the sequence of ternary digits as a `Vec<Digit>`.
260    ///
261    /// # Returns
262    ///
263    /// A `Vec<Digit>` containing the ternary digits that represent the `DataTernary` value.
264    ///
265    /// # Example
266    ///
267    /// ```
268    /// use balanced_ternary::{DataTernary, Digit, Ternary};
269    ///
270    /// let ternary = Ternary::from_dec(42);
271    /// let data_ternary = DataTernary::from_ternary(ternary);
272    /// let digits = data_ternary.to_digits();
273    /// assert_eq!(digits, vec![Digit::Pos, Digit::Neg, Digit::Neg, Digit::Neg, Digit::Zero]);
274    /// ```
275    pub fn to_digits(&self) -> Vec<Digit> {
276        self.to_ternary().trim().to_digit_slice().to_vec()
277    }
278
279    /// Converts a decimal number into a `DataTernary` structure.
280    ///
281    /// This method takes a signed 64-bit integer as input and converts it into a
282    /// `Ternary` representation, which is then stored in the compact `DataTernary`
283    /// structure. The conversion ensures that the ternary representation uses
284    /// fixed-length chunks for efficient storage.
285    ///
286    /// # Arguments
287    ///
288    /// * `from` - A signed 64-bit integer value to be converted into `DataTernary`.
289    ///
290    /// # Returns
291    ///
292    /// A `DataTernary` instance that represents the given decimal number.
293    ///
294    /// # Example
295    ///
296    /// ```
297    /// use balanced_ternary::{DataTernary};
298    ///
299    /// let data_ternary = DataTernary::from_dec(42);
300    /// assert_eq!(data_ternary.to_dec(), 42);
301    /// ```
302    pub fn from_dec(from: i64) -> Self {
303        Self::from_ternary(Ternary::from_dec(from))
304    }
305
306    /// Converts a `DataTernary` into its decimal representation.
307    ///
308    /// This method reconstructs the ternary value represented by the `DataTernary`
309    /// structure and converts it into the corresponding signed 64-bit decimal integer.
310    ///
311    /// # Returns
312    ///
313    /// A signed 64-bit integer (`i64`) representing the decimal equivalent of the
314    /// `DataTernary` structure.
315    ///
316    /// # Example
317    ///
318    /// ```
319    /// use balanced_ternary::{DataTernary};
320    ///
321    /// let data_ternary = DataTernary::from_dec(42);
322    /// let decimal = data_ternary.to_dec();
323    /// assert_eq!(decimal, 42);
324    /// ```
325    pub fn to_dec(&self) -> i64 {
326        self.to_ternary().to_dec()
327    }
328}
329
330impl Display for DataTernary {
331    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
332        for chunk in &self.chunks {
333            write!(f, "{}", chunk.to_fixed_ternary())?;
334        }
335        Ok(())
336    }
337}