balanced_ternary/digit.rs
1//! ## Module: Balanced Ternary `Digit`
2//!
3//! This module defines the `Digit` type for the balanced ternary numeral system,
4//! along with its associated operations and functionality.
5//!
6//! ### Key Features
7//!
8//! - **`Digit` Type**: Represents a digit in the balanced ternary numeral system.
9//! - Possible values: `Neg` (-1), `Zero` (0), `Pos` (+1).
10//! - Provides utility functions for converting between characters, integers, and other formats.
11//! - **Arithmetic Operators**: Implements arithmetic operations for digits, including:
12//! - Negation (`Neg`) and Bitwise Not (`Not`).
13//! - Addition (`Add`) and Subtraction (`Sub`).
14//! - Multiplication (`Mul`) and Division (`Div`), with safe handling of divisors (division by zero panics).
15//! - **Logical Operators**: Supports bitwise logical operations (AND, OR, XOR) based on ternary logic rules.
16//! - **Custom Methods**: Additional utility methods implementing balanced ternary logic principles.
17//!
18//! ### Supported Use Cases
19//!
20//! - Arithmetic in balanced ternary numeral systems.
21//! - Logic operations in custom numeral systems.
22//! - Conversion between balanced ternary representation and more common formats like integers and characters.
23//!
24//! ## `Digit` type arithmetical and logical operations
25//!
26//! - `Neg` and `Not` for `Digit`: Negates the digit value, adhering to balanced ternary rules.
27//! - `Add<Digit>` for `Digit`: Adds two `Digit` values and returns a `Ternary`.
28//! - `Sub<Digit>` for `Digit`: Subtracts one `Digit` from another and returns a `Ternary`.
29//! - `Mul<Digit>` for `Digit`: Multiplies two `Digit` values and returns a `Digit`.
30//! - `Div<Digit>` for `Digit`: Divides one `Digit` by another and returns a `Digit`. Division by zero panics.
31//!
32//! ### Logical Operations for `Digit`
33//!
34//! The `Digit` type supports bitwise logical operations, which are implemented according to logical rules applicable to balanced ternary digits.
35//!
36//! #### `BitAnd` for `Digit`
37//!
38//! Performs a bitwise AND operation between two `Digit` values.
39//!
40//! - `Digit::Neg & other` → `Digit::Neg`
41//! - `Digit::Zero & Digit::Neg` → `Digit::Neg`
42//! - `Digit::Zero & other` → `Digit::Zero`
43//! - `Digit::Pos & other` → `other`
44//!
45//! #### `BitOr` for `Digit`
46//!
47//! Performs a bitwise OR operation between two `Digit` values.
48//!
49//! - `Digit::Neg | other` → `other`
50//! - `Digit::Zero | Digit::Pos` → `Digit::Pos`
51//! - `Digit::Zero | other` → `Digit::Zero`
52//! - `Digit::Pos | other` → `Digit::Pos`
53//!
54//! #### `BitXor` for `Digit`
55//!
56//! Performs a bitwise XOR operation between two `Digit` values.
57//!
58//! - `Digit::Neg ^ other` → `other`
59//! - `Digit::Zero ^ other` → `Digit::Zero`
60//! - `Digit::Pos ^ other` → `-other`
61
62use crate::Ternary;
63use alloc::vec;
64use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub};
65
66/// Represents a digit in the balanced ternary numeral system.
67///
68/// A digit can have one of three values:
69/// - `Neg` (-1): Represents the value -1 in the balanced ternary system.
70/// - `Zero` (0): Represents the value 0 in the balanced ternary system.
71/// - `Pos` (+1): Represents the value +1 in the balanced ternary system.
72///
73/// Provides utility functions for converting to/from characters, integers, and negation.
74#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
75pub enum Digit {
76 /// Represents -1
77 Neg,
78 /// Represents 0
79 Zero,
80 /// Represents +1
81 Pos,
82}
83
84impl Digit {
85 /// Converts the `Digit` into its character representation.
86 ///
87 /// - Returns:
88 /// - `-` for `Digit::Neg`
89 /// - `0` for `Digit::Zero`
90 /// - `+` for `Digit::Pos`
91 pub fn to_char(&self) -> char {
92 match self {
93 Digit::Neg => '-',
94 Digit::Zero => '0',
95 Digit::Pos => '+',
96 }
97 }
98
99 /// Creates a `Digit` from its character representation.
100 ///
101 /// - Accepts:
102 /// - `-` for `Digit::Neg`
103 /// - `0` for `Digit::Zero`
104 /// - `+` for `Digit::Pos`
105 /// - Panics if the input character is invalid.
106 pub fn from_char(c: char) -> Digit {
107 match c {
108 '-' => Digit::Neg,
109 '0' => Digit::Zero,
110 '+' => Digit::Pos,
111 _ => panic!("Invalid value. A Ternary must be either -, 0 or +."),
112 }
113 }
114
115 /// Converts the `Digit` into its integer representation.
116 ///
117 /// - Returns:
118 /// - -1 for `Digit::Neg`
119 /// - 0 for `Digit::Zero`
120 /// - 1 for `Digit::Pos`
121 pub fn to_i8(&self) -> i8 {
122 match self {
123 Digit::Neg => -1,
124 Digit::Zero => 0,
125 Digit::Pos => 1,
126 }
127 }
128
129 /// Creates a `Digit` from its integer representation.
130 ///
131 /// - Accepts:
132 /// - -1 for `Digit::Neg`
133 /// - 0 for `Digit::Zero`
134 /// - 1 for `Digit::Pos`
135 /// - Panics if the input integer is invalid.
136 pub fn from_i8(i: i8) -> Digit {
137 match i {
138 -1 => Digit::Neg,
139 0 => Digit::Zero,
140 1 => Digit::Pos,
141 _ => panic!("Invalid value. A Ternary must be either -1, 0 or +1."),
142 }
143 }
144 /// Returns the corresponding possible value of the current `Digit`.
145 ///
146 /// - Returns:
147 /// - `Digit::Neg` for `Digit::Neg`
148 /// - `Digit::Pos` for `Digit::Zero`
149 /// - `Digit::Pos` for `Digit::Pos`
150 pub fn possibly(&self) -> Self {
151 match self {
152 Digit::Neg => Digit::Neg,
153 Digit::Zero => Digit::Pos,
154 Digit::Pos => Digit::Pos,
155 }
156 }
157
158 /// Determines the condition of necessity for the current `Digit`.
159 ///
160 /// - Returns:
161 /// - `Digit::Neg` for `Digit::Neg`
162 /// - `Digit::Neg` for `Digit::Zero`
163 /// - `Digit::Pos` for `Digit::Pos`
164 ///
165 /// This method is used to calculate necessity as part
166 /// of balanced ternary logic systems.
167 pub fn necessary(&self) -> Self {
168 match self {
169 Digit::Neg => Digit::Neg,
170 Digit::Zero => Digit::Neg,
171 Digit::Pos => Digit::Pos,
172 }
173 }
174
175 /// Determines the condition of contingency for the current `Digit`.
176 ///
177 /// - Returns:
178 /// - `Digit::Neg` for `Digit::Neg`
179 /// - `Digit::Pos` for `Digit::Zero`
180 /// - `Digit::Neg` for `Digit::Pos`
181 ///
182 /// This method represents contingency in balanced ternary logic,
183 /// which defines the specific alternation of `Digit` values.
184 pub fn contingently(&self) -> Self {
185 match self {
186 Digit::Neg => Digit::Neg,
187 Digit::Zero => Digit::Pos,
188 Digit::Pos => Digit::Neg,
189 }
190 }
191
192 /// Determines the condition of non-negativity for the current `Digit`.
193 ///
194 /// - Returns:
195 /// - `Digit::Zero` for `Digit::Neg`
196 /// - `Digit::Pos` for `Digit::Zero`
197 /// - `Digit::Pos` for `Digit::Pos`
198 ///
199 /// This method is used to filter out negative conditions
200 /// in computations with balanced ternary representations.
201 pub fn not_negative(&self) -> Self {
202 match self {
203 Digit::Neg => Digit::Zero,
204 Digit::Zero => Digit::Pos,
205 Digit::Pos => Digit::Pos,
206 }
207 }
208
209 /// Determines the strictly positive condition for the current `Digit`.
210 ///
211 /// - Returns:
212 /// - `Digit::Zero` for `Digit::Neg`
213 /// - `Digit::Zero` for `Digit::Zero`
214 /// - `Digit::Pos` for `Digit::Pos`
215 ///
216 /// This method is used to calculate strictly positive states
217 /// in association with ternary logic.
218 pub fn positive(&self) -> Self {
219 match self {
220 Digit::Neg => Digit::Zero,
221 Digit::Zero => Digit::Zero,
222 Digit::Pos => Digit::Pos,
223 }
224 }
225
226 /// Determines the condition of non-positivity for the current `Digit`.
227 ///
228 /// - Returns:
229 /// - `Digit::Pos` for `Digit::Neg`
230 /// - `Digit::Pos` for `Digit::Zero`
231 /// - `Digit::Zero` for `Digit::Pos`
232 ///
233 /// This method complements the `positive` condition and captures
234 /// states that are not strictly positive.
235 pub fn not_positive(&self) -> Self {
236 match self {
237 Digit::Neg => Digit::Pos,
238 Digit::Zero => Digit::Pos,
239 Digit::Pos => Digit::Zero,
240 }
241 }
242
243 /// Determines the strictly negative condition for the current `Digit`.
244 ///
245 /// - Returns:
246 /// - `Digit::Pos` for `Digit::Neg`
247 /// - `Digit::Zero` for `Digit::Zero`
248 /// - `Digit::Zero` for `Digit::Pos`
249 ///
250 /// This method calculates strictly negative states
251 /// in association with ternary logic.
252 pub fn negative(&self) -> Self {
253 match self {
254 Digit::Neg => Digit::Pos,
255 Digit::Zero => Digit::Zero,
256 Digit::Pos => Digit::Zero,
257 }
258 }
259
260 /// Performs Kleene implication with the current `Digit` as `self` and another `Digit`.
261 ///
262 /// - `self`: The antecedent of the implication.
263 /// - `other`: The consequent of the implication.
264 ///
265 /// - Returns:
266 /// - `Digit::Pos` when `self` is `Digit::Neg`.
267 /// - The positive condition of `other` when `self` is `Digit::Zero`.
268 /// - `other` when `self` is `Digit::Pos`.
269 ///
270 /// Implements Kleene ternary implication logic, which includes
271 /// determining the logical result based on antecedent and consequent.
272 pub fn k3_imply(&self, other: Self) -> Self {
273 match self {
274 Digit::Neg => Digit::Pos,
275 Digit::Zero => other.positive(),
276 Digit::Pos => other,
277 }
278 }
279
280 /// Performs Łukasiewicz implication with the current `Digit` as `self` and another `Digit`.
281 ///
282 /// - `self`: The antecedent of the implication.
283 /// - `other`: The consequent of the implication.
284 ///
285 /// - Returns:
286 /// - `Digit::Pos` when `self` is `Digit::Neg`.
287 /// - The non-negative condition of `other` when `self` is `Digit::Zero`.
288 /// - `other` when `self` is `Digit::Pos`.
289 ///
290 /// Implements Łukasiewicz ternary implication logic, which
291 /// evaluates an alternative approach for implication compared to Kleene logic.
292 pub fn l3_imply(&self, other: Self) -> Self {
293 match self {
294 Digit::Neg => Digit::Pos,
295 Digit::Zero => other.not_negative(),
296 Digit::Pos => other,
297 }
298 }
299
300 /// Performs RM3 implication with the current `Digit` as `self` and another `Digit`.
301 ///
302 /// - `self`: The antecedent of the implication.
303 /// - `other`: The consequent of the implication.
304 ///
305 /// - Returns:
306 /// - `Digit::Pos` when `self` is `Digit::Neg`.
307 /// - `other` when `self` is `Digit::Zero`.
308 /// - The necessary condition of `other` when `self` is `Digit::Pos`.
309 ///
310 /// Implements RM3 ternary implication logic, which defines a unique
311 /// perspective for implication operations in balanced ternary systems.
312 pub fn rm3_imply(&self, other: Self) -> Self {
313 match self {
314 Digit::Neg => Digit::Pos,
315 Digit::Zero => other,
316 Digit::Pos => other.necessary(),
317 }
318 }
319
320 /// Performs HT implication with the current `Digit` as `self` and another `Digit`.
321 ///
322 /// - `self`: The antecedent of the implication.
323 /// - `other`: The consequent of the implication.
324 ///
325 /// - Returns:
326 /// - `Digit::Pos` when `self` is `Digit::Neg`.
327 /// - The possibility condition of `other` when `self` is `Digit::Zero`.
328 /// - `other` when `self` is `Digit::Pos`.
329 ///
330 /// This method computes HT ternary implication based on heuristic logic.
331 pub fn ht_imply(&self, other: Self) -> Self {
332 match self {
333 Digit::Neg => Digit::Pos,
334 Digit::Zero => other.possibly(),
335 Digit::Pos => other,
336 }
337 }
338
339 /// Performs HT logical negation of the current `Digit`.
340 ///
341 /// - Returns:
342 /// - `Digit::Pos` when `self` is `Digit::Neg`.
343 /// - `Digit::Neg` when `self` is `Digit::Zero` or `Digit::Pos`.
344 ///
345 /// This method evaluates the HT negation result using heuristic ternary logic.
346 pub fn ht_not(&self) -> Self {
347 match self {
348 Digit::Neg => Digit::Pos,
349 Digit::Zero => Digit::Neg,
350 Digit::Pos => Digit::Neg,
351 }
352 }
353}
354
355impl Neg for Digit {
356 type Output = Self;
357
358 /// Returns the negation of the `Digit`.
359 ///
360 /// - `Digit::Neg` becomes `Digit::Pos`
361 /// - `Digit::Pos` becomes `Digit::Neg`
362 /// - `Digit::Zero` remains `Digit::Zero`
363 fn neg(self) -> Self::Output {
364 match self {
365 Digit::Neg => Digit::Pos,
366 Digit::Zero => Digit::Zero,
367 Digit::Pos => Digit::Neg,
368 }
369 }
370}
371
372impl Not for Digit {
373 type Output = Self;
374 fn not(self) -> Self::Output {
375 -self
376 }
377}
378
379impl Add<Digit> for Digit {
380 type Output = Ternary;
381 fn add(self, other: Digit) -> Self::Output {
382 match self {
383 Digit::Neg => match other {
384 Digit::Neg => Ternary::parse("-+"),
385 Digit::Zero => Ternary::parse("-"),
386 Digit::Pos => Ternary::parse("0"),
387 },
388 Digit::Zero => Ternary::new(vec![other]),
389 Digit::Pos => match other {
390 Digit::Neg => Ternary::parse("0"),
391 Digit::Zero => Ternary::parse("+"),
392 Digit::Pos => Ternary::parse("+-"),
393 },
394 }
395 }
396}
397
398impl Sub<Digit> for Digit {
399 type Output = Ternary;
400 fn sub(self, other: Digit) -> Self::Output {
401 match self {
402 Digit::Neg => match other {
403 Digit::Neg => Ternary::parse("0"),
404 Digit::Zero => Ternary::parse("-"),
405 Digit::Pos => Ternary::parse("-+"),
406 },
407 Digit::Zero => Ternary::new(vec![-other]),
408 Digit::Pos => match other {
409 Digit::Neg => Ternary::parse("+-"),
410 Digit::Zero => Ternary::parse("+"),
411 Digit::Pos => Ternary::parse("0"),
412 },
413 }
414 }
415}
416
417impl Mul<Digit> for Digit {
418 type Output = Digit;
419
420 fn mul(self, other: Digit) -> Self::Output {
421 match self {
422 Digit::Neg => -other,
423 Digit::Zero => Digit::Zero,
424 Digit::Pos => other,
425 }
426 }
427}
428
429impl Div<Digit> for Digit {
430 type Output = Digit;
431
432 fn div(self, other: Digit) -> Self::Output {
433 match self {
434 Digit::Neg => match other {
435 Digit::Neg => Digit::Pos,
436 Digit::Zero => panic!("Cannot divide by zero."),
437 Digit::Pos => Digit::Neg,
438 },
439 Digit::Zero => match other {
440 Digit::Neg => Digit::Zero,
441 Digit::Zero => panic!("Cannot divide by zero."),
442 Digit::Pos => Digit::Zero,
443 },
444 Digit::Pos => match other {
445 Digit::Neg => Digit::Neg,
446 Digit::Zero => panic!("Cannot divide by zero."),
447 Digit::Pos => Digit::Pos,
448 },
449 }
450 }
451}
452
453impl BitAnd for Digit {
454 type Output = Self;
455 fn bitand(self, other: Self) -> Self::Output {
456 match self {
457 Digit::Neg => Digit::Neg,
458 Digit::Zero => match other {
459 Digit::Neg => Digit::Neg,
460 _ => Digit::Zero,
461 },
462 Digit::Pos => other,
463 }
464 }
465}
466
467impl BitOr for Digit {
468 type Output = Self;
469 fn bitor(self, other: Self) -> Self::Output {
470 match self {
471 Digit::Neg => other,
472 Digit::Zero => match other {
473 Digit::Pos => Digit::Pos,
474 _ => Digit::Zero,
475 },
476 Digit::Pos => Digit::Pos,
477 }
478 }
479}
480
481impl BitXor for Digit {
482 type Output = Self;
483
484 fn bitxor(self, rhs: Self) -> Self::Output {
485 match self {
486 Digit::Neg => rhs,
487 Digit::Zero => Digit::Zero,
488 Digit::Pos => -rhs,
489 }
490 }
491}