1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2020 IOTA Stiftung
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and limitations under the License.

//! This module contains logic to convert an integer encoded by 243 trits to the same integer encoded by 384 bits (or 48
//! signed bytes, `i8`).
//!
//! At the core of this a slice of binary-coded, balanced trits is interpreted fanned out `t243`, where `t243` is used
//! analogous to `i64` or `u64`. If the latter are 64-bit signed/unsigned integer types, then `t243` is a 243-trit
//! integer type. Analogous to fanning out a `u64` into 64 individual bits, `t243` is fanned out into 243 trits, each
//! (rather inefficiently) represented by one `u8`.

mod constants;

pub use constants::{BTRIT_0, BTRIT_1, BTRIT_NEG_1, UTRIT_0, UTRIT_1, UTRIT_2, UTRIT_U384_MAX, UTRIT_U384_MAX_HALF};

use crate::ternary::bigint::{
    binary_representation::{U32Repr, U8Repr},
    endianness::{BigEndian, LittleEndian},
    I384, T242, U384,
};

use bee_ternary::{Btrit, ShiftTernary, T1B1Buf, Trit, TritBuf, Utrit};

use std::cmp::Ordering;

def_and_impl_ternary!(T243, 243);

impl<T: Trit> T243<T> {
    /// Converts the `T243` to a `T242`.
    pub fn into_t242(self) -> T242<T> {
        let mut trit_buf = self.into_inner();
        trit_buf.pop();
        T242::new(trit_buf)
    }
}

impl T243<Utrit> {
    /// Converts a big-endian `u32` represented `U384` to an unbalanced `T243`.
    pub fn from_u384(value: U384<BigEndian, U32Repr>) -> Self {
        let mut u384_value = value;
        let mut u384_inner_slice = &mut u384_value.inner[..];

        let mut trit_buf = T243::<Utrit>::zero().into_inner();
        unsafe {
            for trit in trit_buf.as_i8_slice_mut() {
                let mut rem = 0;

                // Skip the most significant digits if they are 0.
                let mut first_digit = 0;
                for (i, digit) in u384_inner_slice.iter().enumerate() {
                    first_digit = i;
                    if *digit != 0 {
                        break;
                    }
                }
                u384_inner_slice = &mut u384_inner_slice[first_digit..];

                // Iterate over the digits of the bigint, starting from the most significant one.
                for digit in u384_inner_slice.iter_mut() {
                    let digit_with_rem = (u64::from(rem) << 32) | u64::from(*digit);
                    #[allow(clippy::cast_possible_truncation)]
                    // `digit_with_rem` is already truncated and `digit_with_rem % 3` is an integer modulo 3
                    {
                        *digit = (digit_with_rem / 3u64) as u32;
                        rem = (digit_with_rem % 3u64) as u32;
                    }
                }
                #[allow(clippy::cast_possible_truncation)] // `rem` is an integer modulo 3.
                {
                    *trit = rem as i8;
                }
            }
        }

        Self(trit_buf)
    }
}

impl<T: Trit> From<T242<T>> for T243<T> {
    fn from(value: T242<T>) -> Self {
        value.into_t243()
    }
}

impl From<I384<BigEndian, U8Repr>> for T243<Btrit> {
    fn from(value: I384<BigEndian, U8Repr>) -> Self {
        let be_u32 = Into::<I384<BigEndian, U32Repr>>::into(value);
        let le_u32 = Into::<I384<LittleEndian, U32Repr>>::into(be_u32);
        le_u32.into()
    }
}

impl From<I384<BigEndian, U32Repr>> for T243<Btrit> {
    fn from(value: I384<BigEndian, U32Repr>) -> Self {
        let value_little_endian: I384<LittleEndian, U32Repr> = value.into();
        value_little_endian.into()
    }
}

impl From<I384<LittleEndian, U32Repr>> for T243<Btrit> {
    fn from(value: I384<LittleEndian, U32Repr>) -> Self {
        let u384_value = value.shift_into_u384();
        let t243_unbalanced = T243::<Utrit>::from(u384_value);
        t243_unbalanced.into_shifted()
    }
}

impl From<U384<BigEndian, U32Repr>> for T243<Utrit> {
    fn from(value: U384<BigEndian, U32Repr>) -> Self {
        Self::from_u384(value)
    }
}

impl From<U384<LittleEndian, U32Repr>> for T243<Utrit> {
    fn from(value: U384<LittleEndian, U32Repr>) -> Self {
        let value: U384<BigEndian, U32Repr> = value.into();
        value.into()
    }
}