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}