1#![allow(clippy::arithmetic_side_effects)]
2use {
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#[inline(never)]
19fn u64_multiply(multiplicand: u64, multiplier: u64) -> u64 {
20 multiplicand * multiplier
21}
22
23#[inline(never)]
25fn u64_divide(dividend: u64, divisor: u64) -> u64 {
26 dividend / divisor
27}
28
29#[inline(never)]
31fn f32_multiply(multiplicand: f32, multiplier: f32) -> f32 {
32 multiplicand * multiplier
33}
34
35#[inline(never)]
37fn f32_divide(dividend: f32, divisor: f32) -> f32 {
38 dividend / divisor
39}
40
41#[inline(never)]
43fn f32_exponentiate(base: f32, exponent: f32) -> f32 {
44 base.powf(exponent)
45}
46
47#[inline(never)]
49fn f32_natural_log(argument: f32) -> f32 {
50 argument.ln()
51}
52
53#[inline(never)]
55fn u128_multiply(multiplicand: u128, multiplier: u128) -> u128 {
56 multiplicand * multiplier
57}
58
59#[inline(never)]
61fn u128_divide(dividend: u128, divisor: u128) -> u128 {
62 dividend / divisor
63}
64
65#[inline(never)]
67fn f64_multiply(multiplicand: f64, multiplier: f64) -> f64 {
68 multiplicand * multiplier
69}
70
71#[inline(never)]
73fn f64_divide(dividend: f64, divisor: f64) -> f64 {
74 dividend / divisor
75}
76
77pub 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 let e = one.exp();
269
270 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}