diff --git a/kosher_types.rs b/kosher_types.rs new file mode 100644 index 0000000..fe4d2ff --- /dev/null +++ b/kosher_types.rs @@ -0,0 +1,456 @@ +#[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!(); + } +} \ No newline at end of file