#![allow(clippy::integer_arithmetic)] //! Program state processor use { crate::{ approximations::{f32_normal_cdf, sqrt}, instruction::MathInstruction, precise_number::PreciseNumber, }, borsh::BorshDeserialize, solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, log::sol_log_compute_units, msg, pubkey::Pubkey, }, }; /// u64_multiply #[inline(never)] fn u64_multiply(multiplicand: u64, multiplier: u64) -> u64 { multiplicand * multiplier } /// u64_divide #[inline(never)] fn u64_divide(dividend: u64, divisor: u64) -> u64 { dividend / divisor } /// f32_multiply #[inline(never)] fn f32_multiply(multiplicand: f32, multiplier: f32) -> f32 { multiplicand * multiplier } /// f32_divide #[inline(never)] fn f32_divide(dividend: f32, divisor: f32) -> f32 { dividend / divisor } /// f32_exponentiate #[inline(never)] fn f32_exponentiate(base: f32, exponent: f32) -> f32 { base.powf(exponent) } /// f32_natural_log #[inline(never)] fn f32_natural_log(argument: f32) -> f32 { argument.ln() } /// Instruction processor pub fn process_instruction( _program_id: &Pubkey, _accounts: &[AccountInfo], input: &[u8], ) -> ProgramResult { let instruction = MathInstruction::try_from_slice(input).unwrap(); match instruction { MathInstruction::PreciseSquareRoot { radicand } => { msg!("Calculating square root using PreciseNumber"); let radicand = PreciseNumber::new(radicand as u128).unwrap(); sol_log_compute_units(); let result = radicand.sqrt().unwrap().to_imprecise().unwrap() as u64; sol_log_compute_units(); msg!("{}", result); Ok(()) } MathInstruction::SquareRootU64 { radicand } => { msg!("Calculating u64 square root"); sol_log_compute_units(); let result = sqrt(radicand).unwrap(); sol_log_compute_units(); msg!("{}", result); Ok(()) } MathInstruction::SquareRootU128 { radicand } => { msg!("Calculating u128 square root"); sol_log_compute_units(); let result = sqrt(radicand).unwrap(); sol_log_compute_units(); msg!("{}", result); Ok(()) } MathInstruction::U64Multiply { multiplicand, multiplier, } => { msg!("Calculating U64 Multiply"); sol_log_compute_units(); let result = u64_multiply(multiplicand, multiplier); sol_log_compute_units(); msg!("{}", result); Ok(()) } MathInstruction::U64Divide { dividend, divisor } => { msg!("Calculating U64 Divide"); sol_log_compute_units(); let result = u64_divide(dividend, divisor); sol_log_compute_units(); msg!("{}", result); Ok(()) } MathInstruction::F32Multiply { multiplicand, multiplier, } => { msg!("Calculating f32 Multiply"); sol_log_compute_units(); let result = f32_multiply(multiplicand, multiplier); sol_log_compute_units(); msg!("{}", result as u64); Ok(()) } MathInstruction::F32Divide { dividend, divisor } => { msg!("Calculating f32 Divide"); sol_log_compute_units(); let result = f32_divide(dividend, divisor); sol_log_compute_units(); msg!("{}", result as u64); Ok(()) } MathInstruction::F32Exponentiate { base, exponent } => { msg!("Calculating f32 Exponent"); sol_log_compute_units(); let result = f32_exponentiate(base, exponent); sol_log_compute_units(); msg!("{}", result as u64); Ok(()) } MathInstruction::F32NaturalLog { argument } => { msg!("Calculating f32 Natural Log"); sol_log_compute_units(); let result = f32_natural_log(argument); sol_log_compute_units(); msg!("{}", result as u64); Ok(()) } MathInstruction::F32NormalCDF { argument } => { msg!("Calculating f32 Normal CDF"); sol_log_compute_units(); let result = f32_normal_cdf(argument); sol_log_compute_units(); msg!("{}", result as u64); Ok(()) } MathInstruction::Noop => { msg!("Do nothing"); msg!("{}", 0_u64); Ok(()) } } } #[cfg(test)] mod tests { use super::*; use crate::instruction::MathInstruction; use borsh::BorshSerialize; #[test] fn test_u64_multiply() { assert_eq!(2 * 2, u64_multiply(2, 2)); assert_eq!(4 * 3, u64_multiply(4, 3)); } #[test] fn test_u64_divide() { assert_eq!(1, u64_divide(2, 2)); assert_eq!(2, u64_divide(2, 1)); } #[test] fn test_f32_multiply() { assert_eq!(2.0 * 2.0, f32_multiply(2.0, 2.0)); assert_eq!(4.0 * 3.0, f32_multiply(4.0, 3.0)); } #[test] fn test_f32_divide() { assert_eq!(1.0, f32_divide(2.0, 2.0)); assert_eq!(2.0, f32_divide(2.0, 1.0)); } #[test] fn test_f32_exponentiate() { assert_eq!(16.0, f32_exponentiate(4.0, 2.0)); assert_eq!(4.0, f32_exponentiate(16.0, 0.5)) } #[test] fn test_f32_natural_log() { let one = 1.0f32; // e^1 let e = one.exp(); // ln(e) - 1 == 0 let abs_difference = (f32_natural_log(e) - 1.0).abs(); assert!(abs_difference <= f32::EPSILON); } #[test] fn test_process_instruction() { let program_id = Pubkey::new_unique(); for math_instruction in &[ MathInstruction::PreciseSquareRoot { radicand: u64::MAX }, MathInstruction::SquareRootU64 { radicand: u64::MAX }, MathInstruction::SquareRootU128 { radicand: u128::MAX, }, MathInstruction::U64Multiply { multiplicand: 3, multiplier: 4, }, MathInstruction::U64Divide { dividend: 2, divisor: 2, }, MathInstruction::F32Multiply { multiplicand: 3.0, multiplier: 4.0, }, MathInstruction::F32Divide { dividend: 2.0, divisor: 2.0, }, MathInstruction::F32Exponentiate { base: 4.0, exponent: 2.0, }, MathInstruction::F32NaturalLog { argument: std::f32::consts::E, }, MathInstruction::Noop, ] { let input = math_instruction.try_to_vec().unwrap(); process_instruction(&program_id, &[], &input).unwrap(); } } }