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}