First instances of custom KosherTypes. Includes: - Decimal (incomplete math ops at the moment) Excludes: - Primitive datatypes KosherC will eventually have most of the primitive types found in most languages.
456 lines
18 KiB
Rust
456 lines
18 KiB
Rust
#[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<const N: usize> { // Binary-coded Hex
|
|
bytes: Option<[u8; N]>
|
|
}
|
|
|
|
impl<const N: usize> BCH<N> {
|
|
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<u8>, b: Option<u8>, 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<u8>; N*2], BCHErrors> where [(); N*2]: {
|
|
// If none, then compress and decompress bytes to extract their BCHByteConditions to avoid forcing
|
|
// BCH<N> 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<N> = 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<u8>; 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<const E: usize, const M: usize> {
|
|
exp: BCH<E>,
|
|
man: BCH<M>,
|
|
pow: i8,
|
|
}
|
|
|
|
impl<const E: usize, const M: usize> Decimal<E, M> {
|
|
pub const fn initalize(exp_arg: [(Option<u8>, Option<u8>); E], man_arg: [(Option<u8>, Option<u8>); M], sign: bool) -> Self {
|
|
let mut exp = BCH::<E>::initalize();
|
|
let mut man = BCH::<M>::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!();
|
|
}
|
|
} |