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