balanced_ternary/
lib.rs

1//! A [balanced ternary](https://en.wikipedia.org/wiki/Balanced_ternary) data structure.
2//!
3//! A `Ternary` object in this module represents a number in the balanced ternary numeral system.
4//! Balanced ternary is a non-standard positional numeral system that uses three digits: {-1, 0, +1}
5//! represented here as `Neg` for -1, `Zero` for 0, and `Pos` for +1. It is useful in some domains
6//! of computer science and mathematics due to its arithmetic properties and representation
7//! symmetry.
8//!
9//! # Data Structures
10//!
11//! - **`Digit` Enum**:
12//!     Represents a single digit for balanced ternary values, with possible values:
13//!     - `Neg` for -1
14//!     - `Zero` for 0
15//!     - `Pos` for +1
16//!
17//! - **`Ternary` Struct**:
18//!     Represents a balanced ternary number as a collection of `Digit`s.
19//!     Provides utility functions for conversion, parsing, and manipulation.
20//!
21//! # Examples
22//!
23//! ## Converting between representations:
24//! ```rust
25//! use balanced_ternary::*;
26//!
27//! let ternary = Ternary::from_dec(5);
28//! assert_eq!(ternary.to_string(), "+--");
29//! assert_eq!(ternary.to_dec(), 5);
30//!
31//! let parsed = Ternary::parse("+--");
32//! assert_eq!(parsed.to_string(), "+--");
33//! assert_eq!(parsed.to_dec(), 5);
34//! ```
35//!
36//! ## Negative numbers:
37//! ```rust
38//! use balanced_ternary::*;
39//!
40//! let neg_five = Ternary::from_dec(-5);
41//! assert_eq!(neg_five.to_string(), "-++");
42//! assert_eq!(neg_five.to_dec(), -5);
43//!
44//! let negated = -&neg_five;
45//! assert_eq!(negated.to_string(), "+--");
46//! assert_eq!(negated.to_dec(), 5);
47//! ```
48//!
49//! ## Larger numbers:
50//! ```rust
51//! use balanced_ternary::*;
52//!
53//! let big = Ternary::from_dec(121);
54//! assert_eq!(big.to_string(), "+++++");
55//! assert_eq!(big.to_dec(), 121);
56//!
57//! let neg_big = Ternary::from_dec(-121);
58//! assert_eq!(neg_big.to_string(), "-----");
59//! assert_eq!(neg_big.to_dec(), -121);
60//! ```
61//!
62//! ## Operations
63//! ```
64//! use balanced_ternary::Ternary;
65//!
66//! let repr9 = Ternary::parse("+00");
67//! let repr4 = Ternary::parse("++");
68//! let repr13 = &repr9 + &repr4;
69//! let repr17 = &repr13 + &repr4;
70//! let repr34 = &repr17 + &repr17;
71//!
72//! assert_eq!(repr13.to_string(), "+++");
73//! assert_eq!(repr17.to_string(), "+-0-");
74//! assert_eq!(repr34.to_string(), "++-+");
75//!
76//! let repr30 = &repr34 - &repr4;
77//! assert_eq!(repr30.to_dec(), 30);
78//! assert_eq!(repr30.to_string(), "+0+0");
79//! ```
80//!
81#![no_std]
82extern crate alloc;
83
84use alloc::string::{String, ToString};
85use alloc::vec::Vec;
86use alloc::{format, vec};
87use core::fmt::{Display, Formatter};
88use core::str::FromStr;
89
90/// Provides helper functions for formatting integers in a given radix.
91///
92/// Used internally to convert decimal numbers into their ternary representation.
93/// - `x`: The number to be formatted.
94/// - `radix`: The base of the numeral system.
95///
96/// Returns a string representation of the number in the specified base.
97fn format_radix(x: i64, radix: u32) -> String {
98    let mut result = vec![];
99    let sign = x.signum();
100    let mut x = x.abs() as u64;
101    loop {
102        let m = (x % radix as u64) as u32;
103        x /= radix as u64;
104        result.push(core::char::from_digit(m, radix).unwrap());
105        if x == 0 {
106            break;
107        }
108    }
109    format!(
110        "{}{}",
111        if sign == -1 { "-" } else { "" },
112        result.into_iter().rev().collect::<String>()
113    )
114}
115
116pub mod digit;
117
118pub use crate::digit::{
119    Digit,
120    Digit::{Neg, Pos, Zero},
121};
122
123/// Converts a character into a `Digit`.
124///
125/// # Arguments
126/// * `from` - A single character (`+`, `0`, or `-`).
127/// * **Panics** if the input character is invalid.
128///
129/// # Returns
130/// * A `Digit` enum corresponding to the character.
131///
132/// # Example
133/// ```
134/// use balanced_ternary::{trit, Digit};
135///
136/// let digit = trit('+');
137/// assert_eq!(digit, Digit::Pos);
138/// ```
139pub fn trit(from: char) -> Digit {
140    Digit::from_char(from)
141}
142
143/// Converts a string representation of a balanced ternary number into a `Ternary` object.
144///
145/// This function is a convenient shorthand for creating `Ternary` numbers
146/// from string representations. The input string must consist of balanced
147/// ternary characters: `+`, `0`, and `-`.
148///
149/// # Arguments
150///
151/// * `from` - A string slice representing the balanced ternary number.
152/// * **Panics** if an input character is invalid.
153///
154/// # Returns
155///
156/// A `Ternary` object created from the provided string representation.
157///
158/// # Example
159/// ```
160/// use balanced_ternary::{ter, Ternary};
161///
162/// let ternary = ter("+-0+");
163/// assert_eq!(ternary.to_string(), "+-0+");
164/// ```
165pub fn ter(from: &str) -> Ternary {
166    Ternary::parse(from)
167}
168
169#[cfg(feature = "tryte")]
170/// Creates a `Tryte` object from a string representation of a balanced ternary number.
171///
172/// This function first converts the input string representation into a `Ternary` object
173/// using the `ter` function, and then constructs a `Tryte` from that `Ternary`.
174///
175/// # Panics
176///
177/// This function panics if the `Ternary` contains more than 6 digits or if an input character is invalid.
178///
179/// # Arguments
180///
181/// * `from` - A string slice representing the balanced ternary number. It must contain
182///   valid balanced ternary characters (`+`, `0`, or `-`) only.
183/// * Panics if an input character is invalid.
184///
185/// # Returns
186///
187/// A `Tryte` object constructed from the provided balanced ternary string.
188///
189/// # Example
190/// ```
191/// use balanced_ternary::{tryte, Tryte};
192///
193/// let tryte_value = tryte("+0+0");
194/// assert_eq!(tryte_value.to_string(), "00+0+0");
195/// ```
196pub fn tryte(from: &str) -> Tryte {
197    Tryte::from_ternary(&ter(from))
198}
199
200/// Represents a balanced ternary number using a sequence of `Digit`s.
201///
202/// Provides functions for creating, parsing, converting, and manipulating balanced ternary numbers.
203#[derive(Debug, Clone, PartialEq, Eq, Hash)]
204pub struct Ternary {
205    digits: Vec<Digit>,
206}
207
208impl Ternary {
209    /// Creates a new balanced ternary number from a vector of `Digit`s.
210    pub fn new(digits: Vec<Digit>) -> Ternary {
211        Ternary { digits }
212    }
213
214    /// Returns the number of digits (length) of the balanced ternary number.
215    pub fn log(&self) -> usize {
216        self.digits.len()
217    }
218
219    /// Retrieves a slice containing the digits of the `Ternary`.
220    ///
221    /// # Returns
222    ///
223    /// A slice referencing the digits vec of the `Ternary`.
224    ///
225    /// This function allows access to the raw representation of the
226    /// balanced ternary number as a slice of `Digit` values.
227    pub fn to_digit_slice(&self) -> &[Digit] {
228        self.digits.as_slice()
229    }
230
231    /// Returns a reference to the [Digit] indexed by `index` if it exists.
232    ///
233    /// Digits are indexed **from the right**:
234    /// ```
235    /// use balanced_ternary::Ternary;
236    ///
237    /// // Indexes :
238    /// //                              32
239    /// //                             4||1
240    /// //                            5||||0
241    /// //                            ||||||
242    /// //                            vvvvvv
243    /// let ternary = Ternary::parse("+++--+");
244    /// assert_eq!(ternary.get_digit(1).unwrap().to_char(), '-')
245    /// ```
246    pub fn get_digit(&self, index: usize) -> Option<&Digit> {
247        self.digits.iter().rev().nth(index)
248    }
249
250    /// Parses a string representation of a balanced ternary number into a `Ternary` object.
251    ///
252    /// Each character in the string must be one of `+`, `0`, or `-`.
253    pub fn parse(str: &str) -> Self {
254        let mut repr = Ternary::new(vec![]);
255        for c in str.chars() {
256            repr.digits.push(Digit::from_char(c));
257        }
258        repr
259    }
260
261    /// Converts the `Ternary` object to its integer (decimal) representation.
262    ///
263    /// Calculates the sum of each digit's value multiplied by the appropriate power of 3.
264    pub fn to_dec(&self) -> i64 {
265        let mut dec = 0;
266        for (rank, digit) in self.digits.iter().rev().enumerate() {
267            dec += digit.to_i8() as i64 * 3_i64.pow(rank as u32);
268        }
269        dec
270    }
271
272    /// Creates a balanced ternary number from a decimal integer.
273    ///
274    /// The input number is converted into its balanced ternary representation,
275    /// with digits represented as `Digit`s.
276    pub fn from_dec(dec: i64) -> Self {
277        let sign = dec.signum();
278        let str = format_radix(dec.abs(), 3);
279        let mut carry = 0u8;
280        let mut repr = Ternary::new(vec![]);
281        for digit in str.chars().rev() {
282            let digit = u8::from_str(&digit.to_string()).unwrap() + carry;
283            if digit < 2 {
284                repr.digits.push(Digit::from_i8(digit as i8));
285                carry = 0;
286            } else if digit == 2 {
287                repr.digits.push(Digit::from_i8(-1));
288                carry = 1;
289            } else if digit == 3 {
290                repr.digits.push(Digit::from_i8(0));
291                carry = 1;
292            } else {
293                panic!("Ternary::from_dec(): Invalid digit: {}", digit);
294            }
295        }
296        if carry == 1 {
297            repr.digits.push(Digit::from_i8(1));
298        }
299        repr.digits.reverse();
300        if sign == -1 {
301            -&repr
302        } else {
303            repr
304        }
305    }
306
307    /// Converts the balanced ternary number to its unbalanced representation as a string.
308    ///
309    /// The unbalanced representation treats the digits as standard ternary (0, 1, 2),
310    /// instead of balanced ternary (-1, 0, +1). Negative digits are handled by
311    /// calculating the decimal value of the balanced ternary number and converting
312    /// it back to an unbalanced ternary string.
313    ///
314    /// Returns:
315    /// * `String` - The unbalanced ternary representation of the number, where each
316    /// digit is one of `0`, `1`, or `2`.
317    ///
318    /// Example:
319    /// ```
320    /// use balanced_ternary::Ternary;
321    ///
322    /// let repr = Ternary::parse("+--");
323    /// assert_eq!(repr.to_unbalanced(), "12");
324    /// assert_eq!(repr.to_dec(), 5);
325    /// let repr = Ternary::parse("-++");
326    /// assert_eq!(repr.to_unbalanced(), "-12");
327    /// assert_eq!(repr.to_dec(), -5);
328    /// ```
329    pub fn to_unbalanced(&self) -> String {
330        format_radix(self.to_dec(), 3)
331    }
332
333    /// Parses a string representation of an unbalanced ternary number into a `Ternary` object.
334    ///
335    /// The string must only contain characters valid in the unbalanced ternary numeral system (`0`, `1`, or `2`).
336    /// Each character is directly converted into its decimal value and then interpreted as a balanced ternary number.
337    ///
338    /// # Arguments
339    ///
340    /// * `unbalanced` - A string slice representing the unbalanced ternary number.
341    ///
342    /// # Returns
343    ///
344    /// A `Ternary` object representing the same value as the input string in balanced ternary form.
345    ///
346    /// # Panics
347    ///
348    /// This function will panic if the string is not a valid unbalanced ternary number.
349    /// For instance, if it contains characters other than `0`, `1`, or `2`.
350    ///
351    /// # Examples
352    ///
353    /// ```
354    /// use balanced_ternary::Ternary;
355    ///
356    /// let ternary = Ternary::from_unbalanced("-12");
357    /// assert_eq!(ternary.to_string(), "-++");
358    /// assert_eq!(ternary.to_dec(), -5);
359    /// ```
360    pub fn from_unbalanced(unbalanced: &str) -> Self {
361        Self::from_dec(i64::from_str_radix(unbalanced, 3).unwrap())
362    }
363
364    /// Applies a transformation function to each digit of the balanced ternary number,
365    /// returning a new `Ternary` object with the transformed digits.
366    ///
367    /// This method keeps the order of the digits unchanged while applying the provided
368    /// transformation function `f` to each digit individually.
369    ///
370    /// # Arguments
371    ///
372    /// * `f` - A closure or function that takes a `Digit` and returns a transformed `Digit`.
373    ///
374    /// # Returns
375    ///
376    /// * `Self` - A new `Ternary` object containing the transformed digits.
377    ///
378    /// # Digit transformations
379    ///
380    /// These methods (unary operators) from the [Digit] type can be called directly.
381    ///
382    /// * Returns either `Pos` or `Neg`:
383    ///     * [Digit::possibly]
384    ///     * [Digit::necessary]
385    ///     * [Digit::contingently]
386    ///     * [Digit::ht_not]
387    /// * Returns either `Zero` or `Pos` or `Neg`.
388    ///     * [Digit::pre]
389    ///     * [Digit::post]
390    ///     * [Digit::not]
391    ///     * [Digit::neg]
392    ///     * [Digit::absolute_positive]
393    ///     * [Digit::positive]
394    ///     * [Digit::not_negative]
395    ///     * [Digit::not_positive]
396    ///     * [Digit::negative]
397    ///     * [Digit::absolute_negative]
398    ///
399    /// # Examples
400    /// ```
401    /// use balanced_ternary::{Ternary, Digit};
402    ///
403    /// let orig_ternary = Ternary::parse("+0-");
404    /// let transformed = orig_ternary.each(Digit::necessary);
405    /// assert_eq!(transformed.to_string(), "+--");
406    /// let transformed = orig_ternary.each(Digit::positive);
407    /// assert_eq!(transformed.to_string(), "+00");
408    /// let transformed = orig_ternary.each(Digit::not_negative);
409    /// assert_eq!(transformed.to_string(), "++0");
410    /// let transformed = orig_ternary.each(Digit::absolute_negative);
411    /// assert_eq!(transformed.to_string(), "-0-");
412    /// ```
413    pub fn each(&self, f: impl Fn(Digit) -> Digit) -> Self {
414        let mut repr = Ternary::new(vec![]);
415        for digit in self.digits.iter() {
416            repr.digits.push(f(*digit));
417        }
418        repr
419    }
420
421    /// Applies a transformation function to each digit of the balanced ternary number,
422    /// using an additional parameter for the transformation process, returning a new `Ternary`
423    /// object with the transformed digits.
424    ///
425    /// This method keeps the order of the digits unchanged while applying the provided
426    /// transformation function `f` to each digit individually, along with the provided extra
427    /// `other` digit.
428    ///
429    /// # Arguments
430    ///
431    /// * `f` - A closure or function that takes a `Digit` and an additional `Digit`,
432    ///         and returns a transformed `Digit`.
433    /// * `other` - An additional `Digit` to be passed to the transformation function `f`.
434    ///
435    /// # Returns
436    ///
437    /// * `Self` - A new `Ternary` object containing the transformed digits.
438    ///
439    /// # Digit transformations
440    ///
441    /// These methods (binary operators) from the [Digit] type can be called directly.
442    ///
443    /// * [Digit::mul]
444    /// * [Digit::div]
445    /// * [Digit::bitand] (k3/p3 and)
446    /// * [Digit::bi3_and]
447    /// * [Digit::bitor]  (k3/p3 or)
448    /// * [Digit::bi3_or]
449    /// * [Digit::bitxor] (k3/p3 xor)
450    /// * [Digit::k3_imply]
451    /// * [Digit::k3_equiv]
452    /// * [Digit::bi3_imply]
453    /// * [Digit::l3_imply]
454    /// * [Digit::rm3_imply]
455    /// * [Digit::ht_imply]
456    ///
457    /// # Examples
458    /// ```
459    /// use std::ops::Mul;
460    /// use balanced_ternary::{Ternary, Digit};
461    ///
462    /// let original = Ternary::parse("+-0");
463    /// let transformed = original.each_with(Digit::mul, Digit::Neg);
464    /// assert_eq!(transformed.to_string(), "-+0");
465    /// ```
466    pub fn each_with(&self, f: impl Fn(Digit, Digit) -> Digit, other: Digit) -> Self {
467        let mut repr = Ternary::new(vec![]);
468        for digit in self.digits.iter() {
469            repr.digits.push(f(*digit, other));
470        }
471        repr
472    }
473
474
475    /// Applies a transformation function to each digit of the balanced ternary number,
476    /// along with a corresponding digit from another `Ternary` number.
477    ///
478    /// This method ensures that the digits of both `Ternary` objects are aligned from the least
479    /// significant to the most significant digit. If the `other` `Ternary` has fewer digits
480    /// than the current one, the process is reversed to handle the shorter `Ternary` consistently.
481    /// The result is a new `Ternary` object where each digit was transformed using the provided function `f`.
482    ///
483    /// # Arguments
484    ///
485    /// * `f` - A closure or function that takes two arguments:
486    ///     * a `Digit` from the current `Ternary`,
487    ///     * a `Digit` from the corresponding position in the `other` `Ternary`.
488    ///     * The function must return a transformed `Digit`.
489    /// * `other` - A `Ternary` object with digits to process alongside the digits of the current object.
490    ///
491    /// # Returns
492    ///
493    /// * `Self` - A new `Ternary` object containing the transformed digits.
494    ///
495    /// # Examples
496    ///
497    /// ```
498    /// use std::ops::Mul;
499    /// use balanced_ternary::{Ternary, Digit};
500    ///
501    /// let ternary1 = Ternary::parse("-+0-+0-+0");
502    /// let ternary2 = Ternary::parse("---000+++");
503    ///
504    /// let result = ternary1.each_zip(Digit::mul, ternary2.clone());
505    /// assert_eq!(result.to_string(), "+-0000-+0");
506    ///
507    /// let result = ternary1.each_zip(Digit::k3_imply, ternary2.clone());
508    /// assert_eq!(result.to_string(), "+-0+00+++");
509    /// let result = ternary1.each_zip(Digit::bi3_imply, ternary2.clone());
510    /// assert_eq!(result.to_string(), "+-0000++0");
511    /// let result = ternary1.each_zip(Digit::ht_imply, ternary2.clone());
512    /// assert_eq!(result.to_string(), "+--+0++++");
513    /// ```
514    pub fn each_zip(&self, f: impl Fn(Digit, Digit) -> Digit, other: Self) -> Self {
515        if self.digits.len() < other.digits.len() {
516            return other.each_zip(f, self.clone());
517        }
518        let mut repr = Ternary::new(vec![]);
519        for (i, digit) in self.digits.iter().rev().enumerate() {
520            let d_other = other.get_digit(i).unwrap();
521            let res= f(*digit, *d_other);
522            repr.digits.push(res);
523        }
524        repr.digits.reverse();
525        repr
526    }
527
528    /// Applies a transformation function to each digit of the balanced ternary number,
529    /// along with a corresponding digit from another `Ternary` number, and a carry digit.
530    ///
531    /// This method processes the digits in reverse order (from the least significant to the most significant),
532    /// keeping their transformed order correct by reversing the result afterward. Each digit from the
533    /// current `Ternary` object is processed with the corresponding digit from another `Ternary` object
534    /// and a carry digit using the provided closure or function `f`.
535    ///
536    /// # Arguments
537    ///
538    /// * `f` - A closure or function that takes three arguments:
539    ///         - a `Digit` from the current `Ternary`,
540    ///         - a `Digit` from the corresponding position in the `other` `Ternary`, and
541    ///         - the current carry `Digit`.
542    ///         The function must return a tuple containing a new carry `Digit` and a transformed `Digit`.
543    /// * `other` - A `Ternary` object with digits to process alongside the digits of the current object.
544    ///
545    /// # Returns
546    ///
547    /// * `Self` - A new `Ternary` object containing the transformed digits.
548    ///
549    /// # Notes
550    ///
551    /// The carry digit is initially `Zero` and is passed between each step of the transformation process.
552    /// If the `other` `Ternary` has fewer digits than the current one, the missing digits in `other`
553    /// are treated as `Zero`.
554    ///
555    /// # Examples
556    ///
557    /// ```
558    /// use balanced_ternary::{Digit, Ternary};
559    ///
560    /// let ternary1 = Ternary::parse("+-0");
561    /// let ternary2 = Ternary::parse("-+0");
562    ///
563    /// // Transformation function that adds digits with a carry digit
564    /// let combine = |d1: Digit, d2: Digit, carry: Digit| -> (Digit, Digit) {
565    ///     // Simple example operation: this just illustrates transforming with carry.
566    ///     // Replace with meaningful logic as needed for your application.
567    ///     let sum = d1.to_i8() + d2.to_i8() + carry.to_i8();
568    ///     (Digit::from_i8(sum / 3), Digit::from_i8(sum % 3))
569    /// };
570    ///
571    /// let result = ternary1.each_zip_carry(combine, ternary2.clone());
572    /// assert_eq!(result.trim().to_string(), (&ternary1 + &ternary2).to_string());
573    /// ```
574    pub fn each_zip_carry(&self, f: impl Fn(Digit, Digit, Digit) -> (Digit, Digit), other: Self) -> Self {
575        if self.digits.len() < other.digits.len() {
576            return other.each_zip_carry(f, self.clone());
577        }
578        let mut repr = Ternary::new(vec![]);
579        let mut carry = Zero;
580        for (i, digit) in self.digits.iter().rev().enumerate() {
581            let d_other = other.get_digit(i).unwrap();
582            let (c, res) = f(*digit, *d_other, carry);
583            carry = c;
584            repr.digits.push(res);
585        }
586        repr.digits.reverse();
587        repr
588    }
589
590    /// Removes leading `Zero` digits from the `Ternary` number, effectively trimming
591    /// it down to its simplest representation. The resulting `Ternary` number
592    /// will still represent the same value.
593    ///
594    /// # Returns
595    ///
596    /// * `Self` - A new `Ternary` object, trimmed of leading zeros.
597    ///
598    /// # Examples
599    ///
600    /// ```
601    /// use balanced_ternary::{ Neg, Pos, Ternary, Zero};
602    ///
603    /// let ternary = Ternary::new(vec![Zero, Zero, Pos, Neg]);
604    /// let trimmed = ternary.trim();
605    /// assert_eq!(trimmed.to_string(), "+-");
606    /// ```
607    ///
608    /// # Notes
609    ///
610    /// This method does not mutate the original `Ternary` object but returns a new representation.
611    pub fn trim(&self) -> Self {
612        if self.to_dec() == 0 {
613            return Ternary::parse("0");
614        }
615        let mut repr = Ternary::new(vec![]);
616        let mut first_digit = false;
617        for digit in self.digits.iter() {
618            if !first_digit && digit != &Zero {
619                first_digit = true;
620            }
621            if first_digit {
622                repr.digits.push(*digit);
623            }
624        }
625        repr
626    }
627
628    /// Adjusts the representation of the `Ternary` number to have a fixed number of digits.
629    ///
630    /// If the current `Ternary` has fewer digits than the specified `length`, leading zero digits
631    /// will be added to the `Ternary` to match the desired length. If the current `Ternary` has
632    /// more digits than the specified `length`, it will be returned unmodified.
633    ///
634    /// # Arguments
635    ///
636    /// * `length` - The desired length of the `Ternary` number.
637    ///
638    /// # Returns
639    ///
640    /// * `Self` - A new `Ternary` object with the specified fixed length.
641    ///
642    /// # Notes
643    ///
644    /// If `length` is smaller than the existing number of digits, the function does not truncate
645    /// the number but instead returns the original `Ternary` unchanged.
646    ///
647    /// # Examples
648    ///
649    /// ```
650    /// use balanced_ternary::{Ternary, Zero, Pos};
651    ///
652    /// let ternary = Ternary::new(vec![Pos]);
653    /// let fixed = ternary.with_length(5);
654    /// assert_eq!(fixed.to_string(), "0000+");
655    ///
656    /// let fixed = ternary.with_length(1);
657    /// assert_eq!(fixed.to_string(), "+");
658    /// ```
659    pub fn with_length(&self, length: usize) -> Self {
660        if length < self.log() {
661            return self.clone();
662        }
663        let zeroes = vec![Zero; length - self.log()];
664        let mut repr = Ternary::new(vec![]);
665        repr.digits.extend(zeroes);
666        repr.digits.extend(self.digits.iter().cloned());
667        repr
668    }
669}
670
671impl Display for Ternary {
672    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
673        let mut str = String::new();
674        for digit in self.digits.iter() {
675            str.push(digit.to_char());
676        }
677        write!(f, "{}", str)
678    }
679}
680
681pub mod operations;
682
683pub mod conversions;
684
685#[cfg(feature = "tryte")]
686pub mod tryte;
687
688#[cfg(feature = "tryte")]
689pub use crate::tryte::Tryte;
690
691#[cfg(test)]
692#[test]
693fn test_ternary() {
694    use crate::*;
695
696    let repr5 = Ternary::new(vec![Pos, Neg, Neg]);
697    assert_eq!(repr5.to_dec(), 5);
698    let repr5 = Ternary::from_dec(5);
699    assert_eq!(repr5.to_dec(), 5);
700
701    let repr13 = Ternary::new(vec![Pos, Pos, Pos]);
702    assert_eq!(repr13.to_dec(), 13);
703
704    let repr14 = Ternary::parse("+---");
705    let repr15 = Ternary::parse("+--0");
706    assert_eq!(repr14.to_dec(), 14);
707    assert_eq!(repr15.to_dec(), 15);
708    assert_eq!(repr14.to_string(), "+---");
709    assert_eq!(repr15.to_string(), "+--0");
710
711    let repr120 = Ternary::from_dec(120);
712    assert_eq!(repr120.to_dec(), 120);
713    assert_eq!(repr120.to_string(), "++++0");
714    let repr121 = Ternary::from_dec(121);
715    assert_eq!(repr121.to_dec(), 121);
716    assert_eq!(repr121.to_string(), "+++++");
717
718    let repr_neg_5 = Ternary::parse("-++");
719    assert_eq!(repr_neg_5.to_dec(), -5);
720    assert_eq!(repr_neg_5.to_string(), "-++");
721
722    let repr_neg_5 = Ternary::from_dec(-5);
723    assert_eq!(repr_neg_5.to_dec(), -5);
724    assert_eq!(repr_neg_5.to_string(), "-++");
725
726    let repr_neg_121 = Ternary::from_dec(-121);
727    assert_eq!(repr_neg_121.to_dec(), -121);
728    assert_eq!(repr_neg_121.to_string(), "-----");
729
730    let test = Ternary::from_dec(18887455);
731    assert_eq!(test.to_dec(), 18887455);
732    assert_eq!(test.to_string(), "++00--0--+-0++0+");
733
734    let unbalanced = Ternary::from_unbalanced("12");
735    assert_eq!(unbalanced.to_dec(), 5);
736    assert_eq!(unbalanced.to_string(), "+--");
737
738    let unbalanced = Ternary::from_unbalanced("-12");
739    assert_eq!(unbalanced.to_dec(), -5);
740    assert_eq!(unbalanced.to_string(), "-++");
741
742    let unbalanced = Ternary::from_dec(121);
743    assert_eq!(unbalanced.to_unbalanced(), "11111");
744    assert_eq!(unbalanced.to_string(), "+++++");
745}