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