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}