1use crate::error::PncError;
2use crate::value::Value;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5static RATIONAL_DIGIT_CAP: AtomicUsize = AtomicUsize::new(10_000);
6
7pub fn set_rational_digit_cap(cap: usize) {
8 RATIONAL_DIGIT_CAP.store(cap, Ordering::Relaxed);
9}
10
11fn rational_digit_cap() -> usize {
12 RATIONAL_DIGIT_CAP.load(Ordering::Relaxed)
13}
14use num_bigint::BigInt;
15use num_rational::BigRational;
16use num_traits::Signed;
17
18pub(crate) fn check_precision(v: &Value) -> Result<(), PncError> {
19 let cap = rational_digit_cap();
20 match v {
21 Value::Rational(r) => {
22 if r.numer().abs().to_string().len() > cap || r.denom().abs().to_string().len() > cap {
25 return Err(PncError::PrecisionBudgetExceeded);
26 }
27 }
28 Value::Unit { number, .. } => check_precision(number)?,
29 _ => {}
30 }
31 Ok(())
32}
33
34pub fn add(stack: &mut Vec<Value>) -> Result<(), PncError> {
36 if stack.len() < 2 {
37 return Err(PncError::StackUnderflow);
38 }
39 let b = stack.pop().unwrap();
40 let a = stack.pop().unwrap();
41 let result = add_values(a, b);
42 check_precision(&result)?;
43 stack.push(result);
44 Ok(())
45}
46
47fn add_values(a: Value, b: Value) -> Value {
48 match (a, b) {
49 (Value::Int(x), Value::Int(y)) => Value::Int(x + y),
50 (Value::Int(x), Value::Rational(y)) => Value::Rational(BigRational::from_integer(x) + y),
51 (Value::Rational(x), Value::Int(y)) => Value::Rational(x + BigRational::from_integer(y)),
52 (Value::Rational(x), Value::Rational(y)) => Value::Rational(x + y),
53 (
54 Value::Unit {
55 number: an,
56 unit: au,
57 },
58 Value::Unit {
59 number: bn,
60 unit: bu,
61 },
62 ) if au == bu => Value::Unit {
63 number: Box::new(add_values(*an, *bn)),
64 unit: au,
65 },
66 (Value::Unit { number, .. }, other) => {
67 add_values(*number, other)
69 }
70 (other, Value::Unit { number, .. }) => add_values(other, *number),
71 }
72}
73
74pub fn sub(stack: &mut Vec<Value>) -> Result<(), PncError> {
75 if stack.len() < 2 {
76 return Err(PncError::StackUnderflow);
77 }
78 let b = stack.pop().unwrap();
79 let a = stack.pop().unwrap();
80 let result = sub_values(a, b);
81 check_precision(&result)?;
82 stack.push(result);
83 Ok(())
84}
85
86fn sub_values(a: Value, b: Value) -> Value {
87 match (a, b) {
88 (Value::Int(x), Value::Int(y)) => Value::Int(x - y),
89 (Value::Int(x), Value::Rational(y)) => Value::Rational(BigRational::from_integer(x) - y),
90 (Value::Rational(x), Value::Int(y)) => Value::Rational(x - BigRational::from_integer(y)),
91 (Value::Rational(x), Value::Rational(y)) => Value::Rational(x - y),
92 (
93 Value::Unit {
94 number: an,
95 unit: au,
96 },
97 Value::Unit {
98 number: bn,
99 unit: bu,
100 },
101 ) if au == bu => Value::Unit {
102 number: Box::new(sub_values(*an, *bn)),
103 unit: au,
104 },
105 (Value::Unit { number, .. }, other) => sub_values(*number, other),
106 (other, Value::Unit { number, .. }) => sub_values(other, *number),
107 }
108}
109
110pub fn mul(stack: &mut Vec<Value>) -> Result<(), PncError> {
111 if stack.len() < 2 {
112 return Err(PncError::StackUnderflow);
113 }
114 let b = stack.pop().unwrap();
115 let a = stack.pop().unwrap();
116 let result = mul_values(a, b);
117 check_precision(&result)?;
118 stack.push(result);
119 Ok(())
120}
121
122fn mul_values(a: Value, b: Value) -> Value {
123 match (a, b) {
124 (Value::Int(x), Value::Int(y)) => Value::Int(x * y),
125 (Value::Int(x), Value::Rational(y)) => Value::Rational(BigRational::from_integer(x) * y),
126 (Value::Rational(x), Value::Int(y)) => Value::Rational(x * BigRational::from_integer(y)),
127 (Value::Rational(x), Value::Rational(y)) => Value::Rational(x * y),
128 (
129 Value::Unit {
130 number: an,
131 unit: au,
132 },
133 Value::Unit {
134 number: bn,
135 unit: bu,
136 },
137 ) if au == bu => Value::Unit {
138 number: Box::new(mul_values(*an, *bn)),
139 unit: au,
140 },
141 (Value::Unit { number, .. }, other) => mul_values(*number, other),
142 (other, Value::Unit { number, .. }) => mul_values(other, *number),
143 }
144}
145
146fn unit_of(v: &Value) -> Option<crate::value::UnitId> {
147 match v {
148 Value::Unit { unit, .. } => Some(unit.clone()),
149 _ => None,
150 }
151}
152
153fn strip_unit(v: Value) -> Value {
154 match v {
155 Value::Unit { number, .. } => *number,
156 other => other,
157 }
158}
159
160pub fn div(stack: &mut Vec<Value>) -> Result<(), PncError> {
161 if stack.len() < 2 {
162 return Err(PncError::StackUnderflow);
163 }
164 let b = stack.pop().unwrap();
165 let a = stack.pop().unwrap();
166 let result_unit = if unit_of(&a).is_some() && unit_of(&a) == unit_of(&b) {
167 unit_of(&a)
168 } else {
169 None
170 };
171 let a = strip_unit(a);
172 let b = strip_unit(b);
173
174 let result = match (a, b) {
175 (Value::Int(x), Value::Int(y)) => {
176 if y == BigInt::from(0) {
177 return Err(PncError::DivisionByZero);
178 }
179 if x.clone() % y.clone() == BigInt::from(0) {
180 Value::Int(x / y)
181 } else {
182 Value::Rational(BigRational::new(x, y))
183 }
184 }
185 (Value::Int(x), Value::Rational(y)) => {
186 if y == BigRational::from_integer(BigInt::from(0)) {
187 return Err(PncError::DivisionByZero);
188 }
189 Value::Rational(BigRational::from_integer(x) / y)
190 }
191 (Value::Rational(x), Value::Int(y)) => {
192 if y == BigInt::from(0) {
193 return Err(PncError::DivisionByZero);
194 }
195 Value::Rational(x / BigRational::from_integer(y))
196 }
197 (Value::Rational(x), Value::Rational(y)) => {
198 if y == BigRational::from_integer(BigInt::from(0)) {
199 return Err(PncError::DivisionByZero);
200 }
201 Value::Rational(x / y)
202 }
203 _ => return Err(PncError::Parse("unsupported operand types".to_string())),
204 };
205
206 let result = match result_unit {
207 Some(u) => Value::Unit {
208 number: Box::new(result),
209 unit: u,
210 },
211 None => result,
212 };
213 check_precision(&result)?;
214 stack.push(result);
215 Ok(())
216}
217
218pub fn clear(stack: &mut Vec<Value>) {
219 stack.clear();
220}
221
222pub fn remove_at(stack: &mut Vec<Value>, index: usize) -> Result<(), PncError> {
223 if index >= stack.len() {
224 return Err(PncError::StackUnderflow);
225 }
226 stack.remove(index);
227 Ok(())
228}
229
230pub fn swap_with_top(stack: &mut [Value], index: usize) -> Result<(), PncError> {
238 if stack.len() < 2 {
239 return Err(PncError::StackUnderflow);
240 }
241 if index >= stack.len() {
242 return Err(PncError::StackUnderflow);
243 }
244 let last = stack.len() - 1;
245 if index == last {
246 return Ok(());
247 }
248 stack.swap(index, last);
249 Ok(())
250}
251
252pub fn neg(stack: &mut Vec<Value>) -> Result<(), PncError> {
253 let v = stack.pop().ok_or(PncError::StackUnderflow)?;
254 let result = neg_value(v);
255 check_precision(&result)?;
256 stack.push(result);
257 Ok(())
258}
259
260fn neg_value(v: Value) -> Value {
261 match v {
262 Value::Int(n) => Value::Int(-n),
263 Value::Rational(r) => Value::Rational(-r),
264 Value::Unit { number, unit } => Value::Unit {
265 number: Box::new(neg_value(*number)),
266 unit,
267 },
268 }
269}
270
271pub fn abs(stack: &mut Vec<Value>) -> Result<(), PncError> {
272 let v = stack.pop().ok_or(PncError::StackUnderflow)?;
273 let result = abs_value(v);
274 check_precision(&result)?;
275 stack.push(result);
276 Ok(())
277}
278
279fn abs_value(v: Value) -> Value {
280 match v {
281 Value::Int(n) => Value::Int(n.abs()),
282 Value::Rational(r) => Value::Rational(r.abs()),
283 Value::Unit { number, unit } => Value::Unit {
284 number: Box::new(abs_value(*number)),
285 unit,
286 },
287 }
288}
289
290fn truncated_rem_rational(a: BigRational, b: BigRational) -> BigRational {
291 let q = (a.numer().clone() * b.denom().clone()) / (a.denom().clone() * b.numer().clone());
293 a - BigRational::from_integer(q) * b
294}
295
296pub fn rmod(stack: &mut Vec<Value>) -> Result<(), PncError> {
297 if stack.len() < 2 {
298 return Err(PncError::StackUnderflow);
299 }
300 {
303 let a = strip_unit(stack[stack.len() - 2].clone());
304 let b = strip_unit(stack[stack.len() - 1].clone());
305 match (&a, &b) {
306 (Value::Int(_), Value::Int(_))
307 | (Value::Int(_), Value::Rational(_))
308 | (Value::Rational(_), Value::Int(_))
309 | (Value::Rational(_), Value::Rational(_)) => {}
310 _ => return Err(PncError::Parse("unsupported operand types".to_string())),
311 }
312 }
313
314 let b = stack.pop().unwrap();
315 let a = stack.pop().unwrap();
316 let result_unit = if unit_of(&a).is_some() && unit_of(&a) == unit_of(&b) {
317 unit_of(&a)
318 } else {
319 None
320 };
321 let a = strip_unit(a);
322 let b = strip_unit(b);
323
324 let result = match (a, b) {
325 (Value::Int(x), Value::Int(y)) => {
326 if y == BigInt::from(0) {
327 return Err(PncError::DivisionByZero);
328 }
329 Value::Int(x % y)
330 }
331 (Value::Int(x), Value::Rational(y)) => {
332 if y == BigRational::from_integer(BigInt::from(0)) {
333 return Err(PncError::DivisionByZero);
334 }
335 Value::Rational(truncated_rem_rational(BigRational::from_integer(x), y))
336 }
337 (Value::Rational(x), Value::Int(y)) => {
338 if y == BigInt::from(0) {
339 return Err(PncError::DivisionByZero);
340 }
341 Value::Rational(truncated_rem_rational(x, BigRational::from_integer(y)))
342 }
343 (Value::Rational(x), Value::Rational(y)) => {
344 if y == BigRational::from_integer(BigInt::from(0)) {
345 return Err(PncError::DivisionByZero);
346 }
347 Value::Rational(truncated_rem_rational(x, y))
348 }
349 _ => return Err(PncError::Parse("unsupported operand types".to_string())),
350 };
351
352 let result = match result_unit {
353 Some(u) => Value::Unit {
354 number: Box::new(result),
355 unit: u,
356 },
357 None => result,
358 };
359 check_precision(&result)?;
360 stack.push(result);
361 Ok(())
362}
363
364pub fn over(stack: &mut Vec<Value>) -> Result<(), PncError> {
365 if stack.len() < 2 {
366 return Err(PncError::StackUnderflow);
367 }
368 let v = stack[stack.len() - 2].clone();
369 stack.push(v);
370 Ok(())
371}
372
373pub fn rot(stack: &mut Vec<Value>) -> Result<(), PncError> {
374 if stack.len() < 3 {
375 return Err(PncError::StackUnderflow);
376 }
377 let n = stack.len();
378 let v = stack.remove(n - 3);
379 stack.push(v);
380 Ok(())
381}
382
383pub fn to_rational(stack: &mut Vec<Value>) -> Result<(), PncError> {
384 let v = stack.pop().ok_or(PncError::StackUnderflow)?;
385 let result = match v {
386 Value::Int(n) => Value::Rational(BigRational::from_integer(n)),
387 Value::Rational(r) => Value::Rational(r),
388 Value::Unit { number, unit } => {
389 let inner = to_rational_value(*number)?;
390 Value::Unit {
391 number: Box::new(Value::Rational(inner)),
392 unit,
393 }
394 }
395 };
396 check_precision(&result)?;
397 stack.push(result);
398 Ok(())
399}
400
401fn to_rational_value(v: Value) -> Result<BigRational, PncError> {
402 match v {
403 Value::Int(n) => Ok(BigRational::from_integer(n)),
404 Value::Rational(r) => Ok(r),
405 _ => Err(PncError::Parse("unsupported".to_string())),
406 }
407}
408
409pub fn to_int(stack: &mut Vec<Value>) -> Result<(), PncError> {
410 let v = stack.pop().ok_or(PncError::StackUnderflow)?;
411 let result = match v {
412 Value::Int(n) => Value::Int(n),
413 Value::Rational(r) => Value::Int(r.to_integer()),
414 Value::Unit { number, unit } => {
415 let n = match *number {
416 Value::Int(n) => n,
417 Value::Rational(r) => r.to_integer(),
418 _ => return Err(PncError::Parse("unsupported".to_string())),
419 };
420 Value::Unit {
421 number: Box::new(Value::Int(n)),
422 unit,
423 }
424 }
425 };
426 check_precision(&result)?;
427 stack.push(result);
428 Ok(())
429}