#[derive(Debug, Clone, Copy)] enum BCHErrors { ArgumentExceedsHexBounds, ByteExceedsHexBounds, BytesUninitalized, DoubleNullByteArgs, // Fatal IndexOutOfBounds, StrExceedsHexBounds, } #[derive(Debug, Clone, Copy)] enum BCHByteConditions { NullConditionFlag, DecompressionLeftNullByteFlag, // Tell the decompressor to ignore the first 4 bits of the target byte. } #[derive(Debug, Clone, Copy)] pub struct BCH { // Binary-coded Hex bytes: Option<[u8; N]> } impl BCH { pub const fn initalize() -> Self { BCH { bytes: Some([0u8; N]) } } /// Delete all bytes and retain the BCH object pub const fn delete_data(&mut self) -> &Self { self.bytes = None; self } pub const fn compress_byte(&mut self, a: Option, b: Option, i: usize, conditions_only: bool) -> Result<(&Self, BCHByteConditions), BCHErrors> { // Catch fatal errors match (a, b) { (None, Some(b)) => { if b > 15 { return Err(BCHErrors::ArgumentExceedsHexBounds) } } (Some(a), None) => { if a > 15 { return Err(BCHErrors::ArgumentExceedsHexBounds) } } (None, None) => { return Err(BCHErrors::DoubleNullByteArgs) } (Some(a), Some(b)) => { if a > 15 || b > 15 { return Err(BCHErrors::ArgumentExceedsHexBounds) } } }; match &mut self.bytes { Some(bytes) => { if i < N { let mut condition_flag = BCHByteConditions::NullConditionFlag; match (a, b) { (None, Some(b)) => { if !conditions_only { bytes[i] = b }; condition_flag = BCHByteConditions::DecompressionLeftNullByteFlag; // println!("none, some"); } (Some(a), None) => { if !conditions_only {bytes[i] = a }; condition_flag = BCHByteConditions::DecompressionLeftNullByteFlag; // println!("some, none"); } (Some(a), Some(b)) => { // Default path if !conditions_only { bytes[i] = (a << 4) + b }; self.bytes.unwrap()[i] = bytes[i]; condition_flag = BCHByteConditions::NullConditionFlag; // println!("some, some") } _ => { //println!("none, none. Should have err'd.") } } Ok((self, condition_flag)) } else { Err(BCHErrors::IndexOutOfBounds) } } None => { Err(BCHErrors::BytesUninitalized) } } } /// Take a IEEE-754 u8 and represent that number as a string. /// This function accepts ranges between 0 to 15 to enable ease of conversion from floating-point IEEE-754 formats to BCH. /// /// For example: /// /// let x: u8 = 15; /// /// let str: &str = byte_to_str(x); /// /// assert_eq!("15", str); pub const fn byte_to_str(&self, byte: u8) -> Result<&'static str, BCHErrors> { match byte { 0 => { Ok("0") } 1 => { Ok("1") } 2 => { Ok("2") } 3 => { Ok("3") } 4 => { Ok("4") } 5 => { Ok("5") } 6 => { Ok("6") } 7 => { Ok("7") } 8 => { Ok("8") } 9 => { Ok("9") } 10 => { Ok("10") } 11 => { Ok("11") } 12 => { Ok("12") } 13 => { Ok("13") } 14 => { Ok("14") } 15 => { Ok("15") } _ => { Err(BCHErrors::ByteExceedsHexBounds) } } } /// Convert a &str into a BCH byte. pub const fn compress_byte_from_str(&self, str: &str) -> Result<&Self, BCHErrors> { Ok(self) } pub const fn decompress_bytes(&self, bch_conditions: Option<[BCHByteConditions; N]>) -> Result<[Option; N*2], BCHErrors> where [(); N*2]: { // If none, then compress and decompress bytes to extract their BCHByteConditions to avoid forcing // BCH to have [u24; N] instead of [u8; N] and lose memory efficiency. // NOTE: (I should be able to get away with this since the compiler is compile-time friendly) //println!("{bch_conditions:?}"); let conditions: [BCHByteConditions; N] = match bch_conditions { Some(array) => { // println!("conditions_container: {bch_conditions:?}"); array } None => { // println!("Generating conditions"); // Extract BCHByteConditions (inefficient) let mut conditions_container: [BCHByteConditions; N] = [BCHByteConditions::NullConditionFlag; N]; let mut i = 0; let mut binding: BCH = BCH::initalize(); while i < N { let byte = self.bytes.unwrap()[i]; let a = Some( byte >> 4); let mut b = Some(byte & MASK); if i == N - 1 { b = None } // If b is the last one in all the bytes and its value is 0, // we can infer that it is none and continue using our regular compression algorithm. // Error occurs because I forgot to decompress the BCH byte while continuing to pass it like a regular byte. // println!("ERROR CAUSED BY BYTE {byte}"); match binding.compress_byte(a, b, i, true) { Ok(flag) => { conditions_container[i] = flag.1; } Err(e) => { // println!("COMPRESSION ERROR {e:?}"); return Err(e) } }; i += 1; } conditions_container } }; // println!("conditions_container: {conditions:?}"); let mut output_container: [Option; N*2] = [None; N*2]; const MASK: u8 = 0b00001111; let bytes = self.bytes.unwrap(); let mut i = 0; while i < N { let target_byte = bytes[i]; match conditions[i] { BCHByteConditions::NullConditionFlag => { let a = target_byte >> 4; let b = target_byte & MASK; // println!("target: {:08b} | a: {} | b: {}", target_byte, a, b); // println!("NULL CONDITION | a: {a} b: {b}"); output_container[i*2] = Some(a); output_container[i*2+1] = Some(b); } BCHByteConditions::DecompressionLeftNullByteFlag => { //todo!("Complete this conversion algorithm or implement BCHByteConditions scanning inside of self.convert_to_fp() instead. Maybe shift left by 4 bits on DLNB-flags so it turns 00001011 into 10110000 and leaves the 0 behind it to be discarded?"); // println!("DLNB target: {:08b}", target_byte); // Test if regular decompression method is necessary to prevent byte misinterpretation. if target_byte > 15 { let a = target_byte >> 4; let b = target_byte & MASK; output_container[i*2] = Some(a); output_container[i*2+1] = Some(b); } else { // println!("DLNB CONDITION | a: {target_byte} b: None"); output_container[i*2] = Some(target_byte); output_container[i*2+1] = None; } } } i += 1; } // println!("output_container: {:?}", output_container); Ok(output_container) } pub fn print_raw(&self) -> () { for byte in self.bytes.unwrap() { println!("{:08b}", byte)} } pub fn print(&self) -> () { for byte in self.bytes.unwrap() { print!("{byte}")} } } #[derive(Debug, Clone, Copy)] pub struct Decimal { exp: BCH, man: BCH, pow: i8, } impl Decimal { pub const fn initalize(exp_arg: [(Option, Option); E], man_arg: [(Option, Option); M], sign: bool) -> Self { let mut exp = BCH::::initalize(); let mut man = BCH::::initalize(); let mut i = 0; while i < E { // Load exponent let a = exp_arg[i].0; let b = exp_arg[i].1; match exp.compress_byte(a, b, i, false) { Ok(_) => { //println!("compression successful | exp"); } Err(_) => { panic!("Load Binary-Coded Hexadecimal Byte failed | EXP\nDebug suggestion: Did you enter a number within the range of 0..=15?") } }; i += 1; } i = 0; while i < M { // Load mantissa let a = man_arg[i].0; let b = man_arg[i].1; match man.compress_byte(a, b, i, false) { Ok(_) => { // println!("compression successful | man"); } Err(_) => { panic!("Load Binary-Coded Hexadecimal Byte failed | MAN\nDebug suggestion: Did you enter a number within the range of 0..=15?") } }; i += 1; } // println!("EXP {exp:?} MAN {man:?}"); let sign_bit: i8 = if sign { -1 } else { 1 }; // Prevent decimal -> f64 conversion edgecasing when M = 0. let final_pow: i8 = if M != 0 { M as i8 * sign_bit} else { sign_bit }; Decimal { exp: exp, man: man, pow: final_pow, } } pub const fn convert_to_fp_num(&self) -> f64 where [(); E*2]:, [(); M*2]: { let mut i: usize = 0; let mut exp_f64: u16 = 0; if E > 0 { match self.exp.decompress_bytes(None) { Ok(bytes) => { // Limit i from exceeding 4 digits (preventing buffer overflows). // Assume the equation of Val * 10^pow(max - i) let mut max: usize = if E*2 <= 3 { E*2 } else { 3 }; let mut j = 0; while j < E*2 { match bytes[j] { None => { if max > 0 { max -= 1; } } _ => { } } j += 1; } let mut pow_factor = 0; // println!("max: {max}"); while i < max { let byte = bytes[i]; match byte { Some(val) => { let byte = val as u16; // println!("this byte is a some value"); match byte { 0..=9 => { pow_factor += 1; } 10..=15 => { pow_factor += 2; } _ => {} } let increment = byte * 10_u16.pow((max - pow_factor) as u32); // println!("{byte} * 10^{max} - {pow_factor} = {increment}"); // println!("{future_exp_f64}"); // If exp_f64 will exceed the bounds of -1022 to +1023, do not append more data. let pos_safe: bool = exp_f64 <= 1023 && self.pow >= 1; let neg_safe: bool = exp_f64 <= 1022 && self.pow <= -1; // println!("pos_safe: {pos_safe} | neg_safe: {neg_safe}"); if pos_safe || neg_safe { exp_f64 += increment; // println!("exp_f64: {exp_f64} {i}"); } else { if self.pow <= 1 { exp_f64 = 1022 } else { exp_f64 = 1023 }; // println!("break"); break; } } None => { // println!("this byte is a none value") } }; i += 1; } } Err(_) => { // println!("fp_num_decompression failed. Bytes do not exist. | EXP"); // panic!("Attempted to convert uninitalized Decimal ({self:?}) to a floating-point number.\n Debug Suggestion: Try verifying the exponent bits."); } }; i = 0; } let mut man_f64: f64 = 0.0; if M > 0 { match self.man.decompress_bytes(None) { Ok(bytes) => { // Limit i from exceeding 38 digits (preventing buffer overflows and inaccuracy). // Assume the equation of Val / 10^pow(pow_factor) let mut max = if self.pow <= 38 { (self.pow.abs() * 2) as usize } else { 38 };// M*2; let mut j = 0; while j < M*2 { match bytes[j] { None => { if max > 0 { max -= 1; } } _ => { } } j += 1; } let mut pow_factor: u32 = 0; let mut increment: f64 = 0.0; while i < max { match bytes[i] { Some(val) => { let byte = val as u16; match byte { 0..=9 => { pow_factor += 1; } 10..=15 => { pow_factor += 2; } _ => {} } // Ensure pow_factor doesn't unintentionally exceed the 38th power to avoid overflow errors. if pow_factor <= 38 { increment = byte as f64 / 10_u128.pow(pow_factor) as f64; // println!("{:?} / 10^{} = {increment}", bytes[i], pow_factor); man_f64 += increment; } else { break; } // todo!("Fix this math lol"); // If man_f64 will exceed 52 digits, do not append more data. } None => { } _ => {} }; i += 1; } // println!("{man_f64}"); } Err(_) => { // println!("fp_num_decompression failed. Bytes do not exist. | MAN"); // panic!("Attempted to convert uninitalized Decimal ({self:?}) to a floating-point number.\n Debug Suggestion: Try verifying the exponent bits."); } }; } let mut final_f64: f64 = exp_f64 as f64 + man_f64; //println!("final_f64 {final_f64}"); if self.pow <= -1 { final_f64 *= -1.0 }; // If negative, flip its sign bit. //println!("self.pow: {}", self.pow); // println!("Final converted value. {:?} -> {final_f64}", self.print()); final_f64 } pub const fn memory_cost(&self) -> usize where [(); E*2]:, [(); M*2]: { let exponent_cost_as_bits = E * 8; let mantissa_cost_as_bits = M * 8; let power_cost_as_bits = 8; let sum = exponent_cost_as_bits + mantissa_cost_as_bits + power_cost_as_bits; sum } pub fn print_raw(&self) -> () { self.exp.print_raw(); self.man.print_raw(); println!("pow: {}", self.pow); } pub fn print(&self) -> () where [(); E*2]:, [(); M*2]: { if self.pow <= -1 { print!("-") } match self.exp.decompress_bytes(None) { Ok(bytes) => { if bytes.len() == 0 { print!("0"); } else { for byte in bytes { match byte { Some(val) => print!("{val:?}"), None => { } } } } } Err(_) => { print!("0"); } } print!("."); match self.man.decompress_bytes(None) { Ok(bytes) => { if bytes.len() == 0 { print!("0"); } else { for byte in bytes { match byte { Some(val) => print!("{val:?}"), None => { } } } } } Err(_) => { print!("0"); } } println!(); } }