spl_math/
processor.rs

1#![allow(clippy::arithmetic_side_effects)]
2//! Program state processor
3
4use {
5    crate::{
6        approximations::{f32_normal_cdf, sqrt},
7        instruction::MathInstruction,
8        precise_number::PreciseNumber,
9    },
10    borsh::BorshDeserialize,
11    solana_program::{
12        account_info::AccountInfo, entrypoint::ProgramResult, log::sol_log_compute_units, msg,
13        pubkey::Pubkey,
14    },
15};
16
17/// u64_multiply
18#[inline(never)]
19fn u64_multiply(multiplicand: u64, multiplier: u64) -> u64 {
20    multiplicand * multiplier
21}
22
23/// u64_divide
24#[inline(never)]
25fn u64_divide(dividend: u64, divisor: u64) -> u64 {
26    dividend / divisor
27}
28
29/// f32_multiply
30#[inline(never)]
31fn f32_multiply(multiplicand: f32, multiplier: f32) -> f32 {
32    multiplicand * multiplier
33}
34
35/// f32_divide
36#[inline(never)]
37fn f32_divide(dividend: f32, divisor: f32) -> f32 {
38    dividend / divisor
39}
40
41/// f32_exponentiate
42#[inline(never)]
43fn f32_exponentiate(base: f32, exponent: f32) -> f32 {
44    base.powf(exponent)
45}
46
47/// f32_natural_log
48#[inline(never)]
49fn f32_natural_log(argument: f32) -> f32 {
50    argument.ln()
51}
52
53/// u128_multiply
54#[inline(never)]
55fn u128_multiply(multiplicand: u128, multiplier: u128) -> u128 {
56    multiplicand * multiplier
57}
58
59/// u128_divide
60#[inline(never)]
61fn u128_divide(dividend: u128, divisor: u128) -> u128 {
62    dividend / divisor
63}
64
65/// f64_multiply
66#[inline(never)]
67fn f64_multiply(multiplicand: f64, multiplier: f64) -> f64 {
68    multiplicand * multiplier
69}
70
71/// f64_divide
72#[inline(never)]
73fn f64_divide(dividend: f64, divisor: f64) -> f64 {
74    dividend / divisor
75}
76
77/// Instruction processor
78pub fn process_instruction(
79    _program_id: &Pubkey,
80    _accounts: &[AccountInfo],
81    input: &[u8],
82) -> ProgramResult {
83    let instruction = MathInstruction::try_from_slice(input).unwrap();
84    match instruction {
85        MathInstruction::PreciseSquareRoot { radicand } => {
86            msg!("Calculating square root using PreciseNumber");
87            let radicand = PreciseNumber::new(radicand as u128).unwrap();
88            sol_log_compute_units();
89            let result = radicand.sqrt().unwrap().to_imprecise().unwrap() as u64;
90            sol_log_compute_units();
91            msg!("{}", result);
92            Ok(())
93        }
94        MathInstruction::SquareRootU64 { radicand } => {
95            msg!("Calculating u64 square root");
96            sol_log_compute_units();
97            let result = sqrt(radicand).unwrap();
98            sol_log_compute_units();
99            msg!("{}", result);
100            Ok(())
101        }
102        MathInstruction::SquareRootU128 { radicand } => {
103            msg!("Calculating u128 square root");
104            sol_log_compute_units();
105            let result = sqrt(radicand).unwrap();
106            sol_log_compute_units();
107            msg!("{}", result);
108            Ok(())
109        }
110        MathInstruction::U64Multiply {
111            multiplicand,
112            multiplier,
113        } => {
114            msg!("Calculating U64 Multiply");
115            sol_log_compute_units();
116            let result = u64_multiply(multiplicand, multiplier);
117            sol_log_compute_units();
118            msg!("{}", result);
119            Ok(())
120        }
121        MathInstruction::U64Divide { dividend, divisor } => {
122            msg!("Calculating U64 Divide");
123            sol_log_compute_units();
124            let result = u64_divide(dividend, divisor);
125            sol_log_compute_units();
126            msg!("{}", result);
127            Ok(())
128        }
129        MathInstruction::F32Multiply {
130            multiplicand,
131            multiplier,
132        } => {
133            msg!("Calculating f32 Multiply");
134            sol_log_compute_units();
135            let result = f32_multiply(multiplicand, multiplier);
136            sol_log_compute_units();
137            msg!("{}", result as u64);
138            Ok(())
139        }
140        MathInstruction::F32Divide { dividend, divisor } => {
141            msg!("Calculating f32 Divide");
142            sol_log_compute_units();
143            let result = f32_divide(dividend, divisor);
144            sol_log_compute_units();
145            msg!("{}", result as u64);
146            Ok(())
147        }
148        MathInstruction::F32Exponentiate { base, exponent } => {
149            msg!("Calculating f32 Exponent");
150            sol_log_compute_units();
151            let result = f32_exponentiate(base, exponent);
152            sol_log_compute_units();
153            msg!("{}", result as u64);
154            Ok(())
155        }
156        MathInstruction::F32NaturalLog { argument } => {
157            msg!("Calculating f32 Natural Log");
158            sol_log_compute_units();
159            let result = f32_natural_log(argument);
160            sol_log_compute_units();
161            msg!("{}", result as u64);
162            Ok(())
163        }
164        MathInstruction::F32NormalCDF { argument } => {
165            msg!("Calculating f32 Normal CDF");
166            sol_log_compute_units();
167            let result = f32_normal_cdf(argument);
168            sol_log_compute_units();
169            msg!("{}", result as u64);
170            Ok(())
171        }
172        MathInstruction::F64Pow { base, exponent } => {
173            msg!("Calculating f64 Pow");
174            sol_log_compute_units();
175            let result = base.powi(exponent as i32);
176            sol_log_compute_units();
177            msg!("{}", result as u64);
178            sol_log_compute_units();
179            let result = base.powf(exponent);
180            sol_log_compute_units();
181            msg!("{}", result as u64);
182            Ok(())
183        }
184        MathInstruction::U128Multiply {
185            multiplicand,
186            multiplier,
187        } => {
188            msg!("Calculating u128 Multiply");
189            sol_log_compute_units();
190            let result = u128_multiply(multiplicand, multiplier);
191            sol_log_compute_units();
192            msg!("{}", result);
193            Ok(())
194        }
195        MathInstruction::U128Divide { dividend, divisor } => {
196            msg!("Calculating u128 Divide");
197            sol_log_compute_units();
198            let result = u128_divide(dividend, divisor);
199            sol_log_compute_units();
200            msg!("{}", result);
201            Ok(())
202        }
203        MathInstruction::F64Multiply {
204            multiplicand,
205            multiplier,
206        } => {
207            msg!("Calculating f64 Multiply");
208            sol_log_compute_units();
209            let result = f64_multiply(multiplicand, multiplier);
210            sol_log_compute_units();
211            msg!("{}", result as u64);
212            Ok(())
213        }
214        MathInstruction::F64Divide { dividend, divisor } => {
215            msg!("Calculating f64 Divide");
216            sol_log_compute_units();
217            let result = f64_divide(dividend, divisor);
218            sol_log_compute_units();
219            msg!("{}", result as u64);
220            Ok(())
221        }
222        MathInstruction::Noop => {
223            msg!("Do nothing");
224            msg!("{}", 0_u64);
225            Ok(())
226        }
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use {super::*, crate::instruction::MathInstruction};
233
234    #[test]
235    fn test_u64_multiply() {
236        assert_eq!(2 * 2, u64_multiply(2, 2));
237        assert_eq!(4 * 3, u64_multiply(4, 3));
238    }
239
240    #[test]
241    fn test_u64_divide() {
242        assert_eq!(1, u64_divide(2, 2));
243        assert_eq!(2, u64_divide(2, 1));
244    }
245
246    #[test]
247    fn test_f32_multiply() {
248        assert_eq!(2.0 * 2.0, f32_multiply(2.0, 2.0));
249        assert_eq!(4.0 * 3.0, f32_multiply(4.0, 3.0));
250    }
251
252    #[test]
253    fn test_f32_divide() {
254        assert_eq!(1.0, f32_divide(2.0, 2.0));
255        assert_eq!(2.0, f32_divide(2.0, 1.0));
256    }
257
258    #[test]
259    fn test_f32_exponentiate() {
260        assert_eq!(16.0, f32_exponentiate(4.0, 2.0));
261        assert_eq!(4.0, f32_exponentiate(16.0, 0.5))
262    }
263
264    #[test]
265    fn test_f32_natural_log() {
266        let one = 1.0f32;
267        // e^1
268        let e = one.exp();
269
270        // ln(e) - 1 == 0
271        let abs_difference = (f32_natural_log(e) - 1.0).abs();
272
273        assert!(abs_difference <= f32::EPSILON);
274    }
275
276    #[test]
277    fn test_process_instruction() {
278        let program_id = Pubkey::new_unique();
279        for math_instruction in &[
280            MathInstruction::PreciseSquareRoot { radicand: u64::MAX },
281            MathInstruction::SquareRootU64 { radicand: u64::MAX },
282            MathInstruction::SquareRootU128 {
283                radicand: u128::MAX,
284            },
285            MathInstruction::U64Multiply {
286                multiplicand: 3,
287                multiplier: 4,
288            },
289            MathInstruction::U64Divide {
290                dividend: 2,
291                divisor: 2,
292            },
293            MathInstruction::F32Multiply {
294                multiplicand: 3.0,
295                multiplier: 4.0,
296            },
297            MathInstruction::F32Divide {
298                dividend: 2.0,
299                divisor: 2.0,
300            },
301            MathInstruction::F32Exponentiate {
302                base: 4.0,
303                exponent: 2.0,
304            },
305            MathInstruction::F32NaturalLog {
306                argument: std::f32::consts::E,
307            },
308            MathInstruction::Noop,
309        ] {
310            let input = borsh::to_vec(math_instruction).unwrap();
311            process_instruction(&program_id, &[], &input).unwrap();
312        }
313    }
314}