1use endbasic_core::{
20 ArgSep, ArgSepSyntax, CallError, CallResult, Callable, CallableMetadata,
21 CallableMetadataBuilder, ExprType, RepeatedSyntax, RepeatedTypeSyntax, RequiredValueSyntax,
22 Scope, SingularArgSyntax,
23};
24use rand::rngs::SmallRng;
25use rand::{RngCore, SeedableRng};
26use std::borrow::Cow;
27use std::cell::RefCell;
28use std::cmp::Ordering;
29use std::rc::Rc;
30
31use crate::{Clearable, MachineBuilder};
32
33const CATEGORY: &str = "Numerical functions";
35
36pub fn double_to_integer(d: f64) -> Result<i32, String> {
38 let d = d.round();
39 if d.is_finite() && d >= (i32::MIN as f64) && (d <= i32::MAX as f64) {
40 Ok(d as i32)
41 } else {
42 Err(format!("Cannot cast {} to integer due to overflow", d))
43 }
44}
45
46pub enum AngleMode {
48 Degrees,
50
51 Radians,
53}
54
55struct ClearableAngleMode {
56 angle_mode: Rc<RefCell<AngleMode>>,
57}
58
59impl Clearable for ClearableAngleMode {
60 fn reset_state(&self) {
61 *self.angle_mode.borrow_mut() = AngleMode::Radians;
62 }
63}
64
65fn get_angle(scope: &mut Scope<'_>, angle_mode: &AngleMode) -> CallResult<f64> {
68 debug_assert_eq!(1, scope.nargs());
69 let angle = scope.get_double(0);
70
71 match angle_mode {
72 AngleMode::Degrees => Ok(angle.to_radians()),
73 AngleMode::Radians => Ok(angle),
74 }
75}
76
77fn to_angle(angle: f64, angle_mode: &AngleMode) -> f64 {
79 match angle_mode {
80 AngleMode::Degrees => angle.to_degrees(),
81 AngleMode::Radians => angle,
82 }
83}
84
85pub struct Prng {
89 prng: SmallRng,
90 last: u32,
91}
92
93impl Prng {
94 pub fn new_from_entryopy() -> Self {
96 let mut prng = SmallRng::from_entropy();
97 let last = prng.next_u32();
98 Self { prng, last }
99 }
100
101 pub fn new_from_seed(seed: i32) -> Self {
103 let mut prng = SmallRng::seed_from_u64(seed as u64);
104 let last = prng.next_u32();
105 Self { prng, last }
106 }
107
108 fn last(&self) -> f64 {
110 (self.last as f64) / (u32::MAX as f64)
111 }
112
113 fn next(&mut self) -> f64 {
115 self.last = self.prng.next_u32();
116 self.last()
117 }
118}
119
120pub struct AbsFunction {
122 metadata: Rc<CallableMetadata>,
123}
124
125impl AbsFunction {
126 pub fn new() -> Rc<Self> {
128 Rc::from(Self {
129 metadata: CallableMetadataBuilder::new("ABS")
130 .with_return_type(ExprType::Double)
131 .with_syntax(&[(
132 &[SingularArgSyntax::RequiredValue(
133 RequiredValueSyntax {
134 name: Cow::Borrowed("expr"),
135 vtype: ExprType::Double,
136 },
137 ArgSepSyntax::End,
138 )],
139 None,
140 )])
141 .with_category(CATEGORY)
142 .with_description("Returns the absolute value of a number.")
143 .build(),
144 })
145 }
146}
147
148impl Callable for AbsFunction {
149 fn metadata(&self) -> Rc<CallableMetadata> {
150 self.metadata.clone()
151 }
152
153 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
154 debug_assert_eq!(1, scope.nargs());
155 let value = scope.get_double(0);
156 scope.return_double(value.abs())
157 }
158}
159
160pub struct AcosFunction {
162 metadata: Rc<CallableMetadata>,
163 angle_mode: Rc<RefCell<AngleMode>>,
164}
165
166impl AcosFunction {
167 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
169 Rc::from(Self {
170 metadata: CallableMetadataBuilder::new("ACOS")
171 .with_return_type(ExprType::Double)
172 .with_syntax(&[(
173 &[SingularArgSyntax::RequiredValue(
174 RequiredValueSyntax { name: Cow::Borrowed("n"), vtype: ExprType::Double },
175 ArgSepSyntax::End,
176 )],
177 None,
178 )])
179 .with_category(CATEGORY)
180 .with_description(
181 "Computes the arc-cosine of a number.
182The resulting angle is measured in degrees or radians depending on the angle mode as selected by \
183the DEG and RAD commands.",
184 )
185 .build(),
186 angle_mode,
187 })
188 }
189}
190
191impl Callable for AcosFunction {
192 fn metadata(&self) -> Rc<CallableMetadata> {
193 self.metadata.clone()
194 }
195
196 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
197 debug_assert_eq!(1, scope.nargs());
198 let n = scope.get_double(0);
199 if !(-1.0..=1.0).contains(&n) {
200 return Err(CallError::Syntax(
201 scope.get_pos(0),
202 "Cannot take arc-cosine of a number outside of [-1, 1]".to_owned(),
203 ));
204 }
205
206 scope.return_double(to_angle(n.acos(), &self.angle_mode.borrow()))
207 }
208}
209
210pub struct AsinFunction {
212 metadata: Rc<CallableMetadata>,
213 angle_mode: Rc<RefCell<AngleMode>>,
214}
215
216impl AsinFunction {
217 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
219 Rc::from(Self {
220 metadata: CallableMetadataBuilder::new("ASIN")
221 .with_return_type(ExprType::Double)
222 .with_syntax(&[(
223 &[SingularArgSyntax::RequiredValue(
224 RequiredValueSyntax { name: Cow::Borrowed("n"), vtype: ExprType::Double },
225 ArgSepSyntax::End,
226 )],
227 None,
228 )])
229 .with_category(CATEGORY)
230 .with_description(
231 "Computes the arc-sine of a number.
232The resulting angle is measured in degrees or radians depending on the angle mode as selected by \
233the DEG and RAD commands.",
234 )
235 .build(),
236 angle_mode,
237 })
238 }
239}
240
241impl Callable for AsinFunction {
242 fn metadata(&self) -> Rc<CallableMetadata> {
243 self.metadata.clone()
244 }
245
246 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
247 debug_assert_eq!(1, scope.nargs());
248 let n = scope.get_double(0);
249 if !(-1.0..=1.0).contains(&n) {
250 return Err(CallError::Syntax(
251 scope.get_pos(0),
252 "Cannot take arc-sine of a number outside of [-1, 1]".to_owned(),
253 ));
254 }
255
256 scope.return_double(to_angle(n.asin(), &self.angle_mode.borrow()))
257 }
258}
259
260pub struct AtnFunction {
262 metadata: Rc<CallableMetadata>,
263 angle_mode: Rc<RefCell<AngleMode>>,
264}
265
266impl AtnFunction {
267 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
269 Rc::from(Self {
270 metadata: CallableMetadataBuilder::new("ATN")
271 .with_return_type(ExprType::Double)
272 .with_syntax(&[(
273 &[SingularArgSyntax::RequiredValue(
274 RequiredValueSyntax { name: Cow::Borrowed("n"), vtype: ExprType::Double },
275 ArgSepSyntax::End,
276 )],
277 None,
278 )])
279 .with_category(CATEGORY)
280 .with_description(
281 "Computes the arc-tangent of a number.
282The resulting angle is measured in degrees or radians depending on the angle mode as selected by \
283the DEG and RAD commands.",
284 )
285 .build(),
286 angle_mode,
287 })
288 }
289}
290
291impl Callable for AtnFunction {
292 fn metadata(&self) -> Rc<CallableMetadata> {
293 self.metadata.clone()
294 }
295
296 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
297 debug_assert_eq!(1, scope.nargs());
298 let n = scope.get_double(0);
299
300 scope.return_double(to_angle(n.atan(), &self.angle_mode.borrow()))
301 }
302}
303
304pub struct CintFunction {
306 metadata: Rc<CallableMetadata>,
307}
308
309impl CintFunction {
310 pub fn new() -> Rc<Self> {
312 Rc::from(Self {
313 metadata: CallableMetadataBuilder::new("CINT")
314 .with_return_type(ExprType::Integer)
315 .with_syntax(&[(
316 &[SingularArgSyntax::RequiredValue(
317 RequiredValueSyntax {
318 name: Cow::Borrowed("expr"),
319 vtype: ExprType::Double,
320 },
321 ArgSepSyntax::End,
322 )],
323 None,
324 )])
325 .with_category(CATEGORY)
326 .with_description(
327 "Casts the given numeric expression to an integer (with rounding).
328When casting a double value to an integer, the double value is first rounded to the closest \
329integer. For example, 4.4 becomes 4, but both 4.5 and 4.6 become 5.",
330 )
331 .build(),
332 })
333 }
334}
335
336impl Callable for CintFunction {
337 fn metadata(&self) -> Rc<CallableMetadata> {
338 self.metadata.clone()
339 }
340
341 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
342 debug_assert_eq!(1, scope.nargs());
343 let value = scope.get_double(0);
344
345 let i = double_to_integer(value)
346 .map_err(|e| CallError::Syntax(scope.get_pos(0), e.to_string()))?;
347 scope.return_integer(i)
348 }
349}
350
351pub struct CosFunction {
353 metadata: Rc<CallableMetadata>,
354 angle_mode: Rc<RefCell<AngleMode>>,
355}
356
357impl CosFunction {
358 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
360 Rc::from(Self {
361 metadata: CallableMetadataBuilder::new("COS")
362 .with_return_type(ExprType::Double)
363 .with_syntax(&[(
364 &[SingularArgSyntax::RequiredValue(
365 RequiredValueSyntax {
366 name: Cow::Borrowed("angle"),
367 vtype: ExprType::Double,
368 },
369 ArgSepSyntax::End,
370 )],
371 None,
372 )])
373 .with_category(CATEGORY)
374 .with_description(
375 "Computes the cosine of an angle.
376The input angle% or angle# is measured in degrees or radians depending on the angle mode as \
377selected by the DEG and RAD commands.",
378 )
379 .build(),
380 angle_mode,
381 })
382 }
383}
384
385impl Callable for CosFunction {
386 fn metadata(&self) -> Rc<CallableMetadata> {
387 self.metadata.clone()
388 }
389
390 fn exec(&self, mut scope: Scope<'_>) -> CallResult<()> {
391 let angle = get_angle(&mut scope, &self.angle_mode.borrow())?;
392 scope.return_double(angle.cos())
393 }
394}
395
396pub struct DegCommand {
398 metadata: Rc<CallableMetadata>,
399 angle_mode: Rc<RefCell<AngleMode>>,
400}
401
402impl DegCommand {
403 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
405 Rc::from(Self {
406 metadata: CallableMetadataBuilder::new("DEG")
407 .with_syntax(&[(&[], None)])
408 .with_category(CATEGORY)
409 .with_description(
410 "Sets degrees mode of calculation.
411The default condition for the trigonometric functions is to use radians. DEG configures the \
412environment to use degrees until instructed otherwise.",
413 )
414 .build(),
415 angle_mode,
416 })
417 }
418}
419
420impl Callable for DegCommand {
421 fn metadata(&self) -> Rc<CallableMetadata> {
422 self.metadata.clone()
423 }
424
425 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
426 debug_assert_eq!(0, scope.nargs());
427 *self.angle_mode.borrow_mut() = AngleMode::Degrees;
428 Ok(())
429 }
430}
431
432pub struct ExpFunction {
434 metadata: Rc<CallableMetadata>,
435}
436
437impl ExpFunction {
438 pub fn new() -> Rc<Self> {
440 Rc::from(Self {
441 metadata: CallableMetadataBuilder::new("EXP")
442 .with_return_type(ExprType::Double)
443 .with_syntax(&[(
444 &[SingularArgSyntax::RequiredValue(
445 RequiredValueSyntax {
446 name: Cow::Borrowed("expr"),
447 vtype: ExprType::Double,
448 },
449 ArgSepSyntax::End,
450 )],
451 None,
452 )])
453 .with_category(CATEGORY)
454 .with_description("Raises Euler's number to the power of a number.")
455 .build(),
456 })
457 }
458}
459
460impl Callable for ExpFunction {
461 fn metadata(&self) -> Rc<CallableMetadata> {
462 self.metadata.clone()
463 }
464
465 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
466 debug_assert_eq!(1, scope.nargs());
467 let value = scope.get_double(0);
468 let exp = value.exp();
469 if !exp.is_finite() {
470 return Err(CallError::Syntax(
471 scope.get_pos(0),
472 format!("Cannot exponentiate {} due to overflow", value),
473 ));
474 }
475 scope.return_double(exp)
476 }
477}
478
479pub struct FixFunction {
481 metadata: Rc<CallableMetadata>,
482}
483
484impl FixFunction {
485 pub fn new() -> Rc<Self> {
487 Rc::from(Self {
488 metadata: CallableMetadataBuilder::new("FIX")
489 .with_return_type(ExprType::Integer)
490 .with_syntax(&[(
491 &[SingularArgSyntax::RequiredValue(
492 RequiredValueSyntax {
493 name: Cow::Borrowed("expr"),
494 vtype: ExprType::Double,
495 },
496 ArgSepSyntax::End,
497 )],
498 None,
499 )])
500 .with_category(CATEGORY)
501 .with_description(
502 "Casts the given numeric expression to an integer (towards zero).
503When casting a double value to an integer, the double value is first truncated towards zero. For \
504example, 4.9 becomes 4 and -4.9 becomes -4.",
505 )
506 .build(),
507 })
508 }
509}
510
511impl Callable for FixFunction {
512 fn metadata(&self) -> Rc<CallableMetadata> {
513 self.metadata.clone()
514 }
515
516 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
517 debug_assert_eq!(1, scope.nargs());
518 let value = scope.get_double(0);
519
520 let i = double_to_integer(value.trunc())
521 .map_err(|e| CallError::Syntax(scope.get_pos(0), e.to_string()))?;
522 scope.return_integer(i)
523 }
524}
525
526pub struct IntFunction {
528 metadata: Rc<CallableMetadata>,
529}
530
531impl IntFunction {
532 pub fn new() -> Rc<Self> {
534 Rc::from(Self {
535 metadata: CallableMetadataBuilder::new("INT")
536 .with_return_type(ExprType::Integer)
537 .with_syntax(&[(
538 &[SingularArgSyntax::RequiredValue(
539 RequiredValueSyntax {
540 name: Cow::Borrowed("expr"),
541 vtype: ExprType::Double,
542 },
543 ArgSepSyntax::End,
544 )],
545 None,
546 )])
547 .with_category(CATEGORY)
548 .with_description(
549 "Casts the given numeric expression to an integer (with truncation).
550When casting a double value to an integer, the double value is first truncated to the smallest \
551integer that is not larger than the double value. For example, all of 4.4, 4.5 and 4.6 become 4.",
552 )
553 .build(),
554 })
555 }
556}
557
558impl Callable for IntFunction {
559 fn metadata(&self) -> Rc<CallableMetadata> {
560 self.metadata.clone()
561 }
562
563 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
564 debug_assert_eq!(1, scope.nargs());
565 let value = scope.get_double(0);
566
567 let i = double_to_integer(value.floor())
568 .map_err(|e| CallError::Syntax(scope.get_pos(0), e.to_string()))?;
569 scope.return_integer(i)
570 }
571}
572
573pub struct LogFunction {
575 metadata: Rc<CallableMetadata>,
576}
577
578impl LogFunction {
579 pub fn new() -> Rc<Self> {
581 Rc::from(Self {
582 metadata: CallableMetadataBuilder::new("LOG")
583 .with_return_type(ExprType::Double)
584 .with_syntax(&[(
585 &[SingularArgSyntax::RequiredValue(
586 RequiredValueSyntax { name: Cow::Borrowed("num"), vtype: ExprType::Double },
587 ArgSepSyntax::End,
588 )],
589 None,
590 )])
591 .with_category(CATEGORY)
592 .with_description("Computes the natural logarithm of the given number.")
593 .build(),
594 })
595 }
596}
597
598impl Callable for LogFunction {
599 fn metadata(&self) -> Rc<CallableMetadata> {
600 self.metadata.clone()
601 }
602
603 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
604 debug_assert_eq!(1, scope.nargs());
605 let num = scope.get_double(0);
606
607 if num <= 0.0 {
608 return Err(CallError::Syntax(
609 scope.get_pos(0),
610 "Cannot take logarithm of zero or a negative number".to_owned(),
611 ));
612 }
613 scope.return_double(num.ln())
614 }
615}
616
617pub struct MaxFunction {
619 metadata: Rc<CallableMetadata>,
620}
621
622impl MaxFunction {
623 pub fn new() -> Rc<Self> {
625 Rc::from(Self {
626 metadata: CallableMetadataBuilder::new("MAX")
627 .with_return_type(ExprType::Double)
628 .with_syntax(&[(
629 &[],
630 Some(&RepeatedSyntax {
631 name: Cow::Borrowed("expr"),
632 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Double),
633 sep: ArgSepSyntax::Exactly(ArgSep::Long),
634 require_one: true,
635 allow_missing: false,
636 }),
637 )])
638 .with_category(CATEGORY)
639 .with_description("Returns the maximum number out of a set of numbers.")
640 .build(),
641 })
642 }
643}
644
645impl Callable for MaxFunction {
646 fn metadata(&self) -> Rc<CallableMetadata> {
647 self.metadata.clone()
648 }
649
650 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
651 let mut max = f64::MIN;
652 for i in 0..(scope.nargs() as u8) {
653 let n = scope.get_double(i);
654 if n > max {
655 max = n;
656 }
657 }
658 scope.return_double(max)
659 }
660}
661
662pub struct MinFunction {
664 metadata: Rc<CallableMetadata>,
665}
666
667impl MinFunction {
668 pub fn new() -> Rc<Self> {
670 Rc::from(Self {
671 metadata: CallableMetadataBuilder::new("MIN")
672 .with_return_type(ExprType::Double)
673 .with_syntax(&[(
674 &[],
675 Some(&RepeatedSyntax {
676 name: Cow::Borrowed("expr"),
677 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Double),
678 sep: ArgSepSyntax::Exactly(ArgSep::Long),
679 require_one: true,
680 allow_missing: false,
681 }),
682 )])
683 .with_category(CATEGORY)
684 .with_description("Returns the minimum number out of a set of numbers.")
685 .build(),
686 })
687 }
688}
689
690impl Callable for MinFunction {
691 fn metadata(&self) -> Rc<CallableMetadata> {
692 self.metadata.clone()
693 }
694
695 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
696 let mut min = f64::MAX;
697 for i in 0..(scope.nargs() as u8) {
698 let n = scope.get_double(i);
699 if n < min {
700 min = n;
701 }
702 }
703 scope.return_double(min)
704 }
705}
706
707pub struct PiFunction {
709 metadata: Rc<CallableMetadata>,
710}
711
712impl PiFunction {
713 pub fn new() -> Rc<Self> {
715 Rc::from(Self {
716 metadata: CallableMetadataBuilder::new("PI")
717 .with_return_type(ExprType::Double)
718 .with_syntax(&[(&[], None)])
719 .with_category(CATEGORY)
720 .with_description("Returns the Archimedes' constant.")
721 .build(),
722 })
723 }
724}
725
726impl Callable for PiFunction {
727 fn metadata(&self) -> Rc<CallableMetadata> {
728 self.metadata.clone()
729 }
730
731 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
732 debug_assert_eq!(0, scope.nargs());
733 scope.return_double(std::f64::consts::PI)
734 }
735}
736
737pub struct RadCommand {
739 metadata: Rc<CallableMetadata>,
740 angle_mode: Rc<RefCell<AngleMode>>,
741}
742
743impl RadCommand {
744 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
746 Rc::from(Self {
747 metadata: CallableMetadataBuilder::new("RAD")
748 .with_syntax(&[(&[], None)])
749 .with_category(CATEGORY)
750 .with_description(
751 "Sets radians mode of calculation.
752The default condition for the trigonometric functions is to use radians but it can be set to \
753degrees with the DEG command. RAD restores the environment to use radians mode.",
754 )
755 .build(),
756 angle_mode,
757 })
758 }
759}
760
761impl Callable for RadCommand {
762 fn metadata(&self) -> Rc<CallableMetadata> {
763 self.metadata.clone()
764 }
765
766 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
767 debug_assert_eq!(0, scope.nargs());
768 *self.angle_mode.borrow_mut() = AngleMode::Radians;
769 Ok(())
770 }
771}
772
773pub struct RandomizeCommand {
775 metadata: Rc<CallableMetadata>,
776 prng: Rc<RefCell<Prng>>,
777}
778
779impl RandomizeCommand {
780 pub fn new(prng: Rc<RefCell<Prng>>) -> Rc<Self> {
782 Rc::from(Self {
783 metadata: CallableMetadataBuilder::new("RANDOMIZE")
784 .with_syntax(&[
785 (&[], None),
786 (
787 &[SingularArgSyntax::RequiredValue(
788 RequiredValueSyntax {
789 name: Cow::Borrowed("seed"),
790 vtype: ExprType::Integer,
791 },
792 ArgSepSyntax::End,
793 )],
794 None,
795 ),
796 ])
797 .with_category(CATEGORY)
798 .with_description(
799 "Reinitializes the pseudo-random number generator.
800If no seed is given, uses system entropy to create a new sequence of random numbers.
801WARNING: These random numbers offer no cryptographic guarantees.",
802 )
803 .build(),
804 prng,
805 })
806 }
807}
808
809impl Callable for RandomizeCommand {
810 fn metadata(&self) -> Rc<CallableMetadata> {
811 self.metadata.clone()
812 }
813
814 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
815 if scope.nargs() == 0 {
816 *self.prng.borrow_mut() = Prng::new_from_entryopy();
817 } else {
818 debug_assert_eq!(1, scope.nargs());
819 let n = scope.get_integer(0);
820 *self.prng.borrow_mut() = Prng::new_from_seed(n);
821 }
822 Ok(())
823 }
824}
825
826pub struct RndFunction {
828 metadata: Rc<CallableMetadata>,
829 prng: Rc<RefCell<Prng>>,
830}
831
832impl RndFunction {
833 pub fn new(prng: Rc<RefCell<Prng>>) -> Rc<Self> {
835 Rc::from(Self {
836 metadata: CallableMetadataBuilder::new("RND")
837 .with_return_type(ExprType::Double)
838 .with_syntax(&[
839 (&[], None),
840 (
841 &[SingularArgSyntax::RequiredValue(
842 RequiredValueSyntax {
843 name: Cow::Borrowed("n"),
844 vtype: ExprType::Integer,
845 },
846 ArgSepSyntax::End,
847 )],
848 None,
849 ),
850 ])
851 .with_category(CATEGORY)
852 .with_description(
853 "Returns a random number in the [0..1] range.
854If n% is negative, resets the pseudo-random number generator to a sequence derived from n% and \
855returns its first value. If n% is zero, returns the previously generated random number. If n% \
856is positive or is not specified, returns a new random number.
857If you need to generate an integer random number within a specific range, say [0..100], compute it \
858with an expression like CINT%(RND#(1) * 100.0).
859WARNING: These random numbers offer no cryptographic guarantees.",
860 )
861 .build(),
862 prng,
863 })
864 }
865}
866
867impl Callable for RndFunction {
868 fn metadata(&self) -> Rc<CallableMetadata> {
869 self.metadata.clone()
870 }
871
872 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
873 if scope.nargs() == 0 {
874 scope.return_double(self.prng.borrow_mut().next())
875 } else {
876 debug_assert_eq!(1, scope.nargs());
877 let n = scope.get_integer(0);
878 match n.cmp(&0) {
879 Ordering::Equal => scope.return_double(self.prng.borrow_mut().last()),
880 Ordering::Greater => scope.return_double(self.prng.borrow_mut().next()),
881 Ordering::Less => {
882 let mut prng = self.prng.borrow_mut();
883 *prng = Prng::new_from_seed(n);
884 scope.return_double(prng.last())
885 }
886 }
887 }
888 }
889}
890
891pub struct RoundFunction {
893 metadata: Rc<CallableMetadata>,
894}
895
896impl RoundFunction {
897 pub fn new() -> Rc<Self> {
899 Rc::from(Self {
900 metadata: CallableMetadataBuilder::new("ROUND")
901 .with_return_type(ExprType::Double)
902 .with_syntax(&[
903 (
904 &[SingularArgSyntax::RequiredValue(
905 RequiredValueSyntax {
906 name: Cow::Borrowed("expr"),
907 vtype: ExprType::Double,
908 },
909 ArgSepSyntax::End,
910 )],
911 None,
912 ),
913 (
914 &[
915 SingularArgSyntax::RequiredValue(
916 RequiredValueSyntax {
917 name: Cow::Borrowed("expr"),
918 vtype: ExprType::Double,
919 },
920 ArgSepSyntax::Exactly(ArgSep::Long),
921 ),
922 SingularArgSyntax::RequiredValue(
923 RequiredValueSyntax {
924 name: Cow::Borrowed("decimals"),
925 vtype: ExprType::Integer,
926 },
927 ArgSepSyntax::End,
928 ),
929 ],
930 None,
931 ),
932 ])
933 .with_category(CATEGORY)
934 .with_description(
935 "Rounds a number to the nearest value.
936If decimals% is omitted, rounds to the nearest integer. If decimals% is given, rounds to that \
937many digits after the decimal point. Negative values round digits before the decimal point.",
938 )
939 .build(),
940 })
941 }
942}
943
944impl Callable for RoundFunction {
945 fn metadata(&self) -> Rc<CallableMetadata> {
946 self.metadata.clone()
947 }
948
949 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
950 let value = scope.get_double(0);
951 if scope.nargs() == 1 {
952 scope.return_double(value.round())
953 } else {
954 debug_assert_eq!(2, scope.nargs());
955 let decimals = scope.get_integer(1);
956 let factor = 10f64.powi(decimals);
957 scope.return_double((value * factor).round() / factor)
958 }
959 }
960}
961
962pub struct SgnFunction {
964 metadata: Rc<CallableMetadata>,
965}
966
967impl SgnFunction {
968 pub fn new() -> Rc<Self> {
970 Rc::from(Self {
971 metadata: CallableMetadataBuilder::new("SGN")
972 .with_return_type(ExprType::Integer)
973 .with_syntax(&[(
974 &[SingularArgSyntax::RequiredValue(
975 RequiredValueSyntax { name: Cow::Borrowed("n"), vtype: ExprType::Double },
976 ArgSepSyntax::End,
977 )],
978 None,
979 )])
980 .with_category(CATEGORY)
981 .with_description("Returns -1, 0, or 1 depending on the sign of a number.")
982 .build(),
983 })
984 }
985}
986
987impl Callable for SgnFunction {
988 fn metadata(&self) -> Rc<CallableMetadata> {
989 self.metadata.clone()
990 }
991
992 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
993 debug_assert_eq!(1, scope.nargs());
994 let n = scope.get_double(0);
995 let sign = if n > 0.0 {
996 1
997 } else if n < 0.0 {
998 -1
999 } else {
1000 0
1001 };
1002 scope.return_integer(sign)
1003 }
1004}
1005
1006pub struct SinFunction {
1008 metadata: Rc<CallableMetadata>,
1009 angle_mode: Rc<RefCell<AngleMode>>,
1010}
1011
1012impl SinFunction {
1013 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
1015 Rc::from(Self {
1016 metadata: CallableMetadataBuilder::new("SIN")
1017 .with_return_type(ExprType::Double)
1018 .with_syntax(&[(
1019 &[SingularArgSyntax::RequiredValue(
1020 RequiredValueSyntax {
1021 name: Cow::Borrowed("angle"),
1022 vtype: ExprType::Double,
1023 },
1024 ArgSepSyntax::End,
1025 )],
1026 None,
1027 )])
1028 .with_category(CATEGORY)
1029 .with_description(
1030 "Computes the sine of an angle.
1031The input angle% or angle# is measured in degrees or radians depending on the angle mode as \
1032selected by the DEG and RAD commands.",
1033 )
1034 .build(),
1035 angle_mode,
1036 })
1037 }
1038}
1039
1040impl Callable for SinFunction {
1041 fn metadata(&self) -> Rc<CallableMetadata> {
1042 self.metadata.clone()
1043 }
1044
1045 fn exec(&self, mut scope: Scope<'_>) -> CallResult<()> {
1046 let angle = get_angle(&mut scope, &self.angle_mode.borrow())?;
1047 scope.return_double(angle.sin())
1048 }
1049}
1050
1051pub struct SqrFunction {
1053 metadata: Rc<CallableMetadata>,
1054}
1055
1056impl SqrFunction {
1057 pub fn new() -> Rc<Self> {
1059 Rc::from(Self {
1060 metadata: CallableMetadataBuilder::new("SQR")
1061 .with_return_type(ExprType::Double)
1062 .with_syntax(&[(
1063 &[SingularArgSyntax::RequiredValue(
1064 RequiredValueSyntax { name: Cow::Borrowed("num"), vtype: ExprType::Double },
1065 ArgSepSyntax::End,
1066 )],
1067 None,
1068 )])
1069 .with_category(CATEGORY)
1070 .with_description("Computes the square root of the given number.")
1071 .build(),
1072 })
1073 }
1074}
1075
1076impl Callable for SqrFunction {
1077 fn metadata(&self) -> Rc<CallableMetadata> {
1078 self.metadata.clone()
1079 }
1080
1081 fn exec(&self, scope: Scope<'_>) -> CallResult<()> {
1082 debug_assert_eq!(1, scope.nargs());
1083 let num = scope.get_double(0);
1084
1085 if num < 0.0 {
1086 return Err(CallError::Syntax(
1087 scope.get_pos(0),
1088 "Cannot take square root of a negative number".to_owned(),
1089 ));
1090 }
1091 scope.return_double(num.sqrt())
1092 }
1093}
1094
1095pub struct TanFunction {
1097 metadata: Rc<CallableMetadata>,
1098 angle_mode: Rc<RefCell<AngleMode>>,
1099}
1100
1101impl TanFunction {
1102 pub fn new(angle_mode: Rc<RefCell<AngleMode>>) -> Rc<Self> {
1104 Rc::from(Self {
1105 metadata: CallableMetadataBuilder::new("TAN")
1106 .with_return_type(ExprType::Double)
1107 .with_syntax(&[(
1108 &[SingularArgSyntax::RequiredValue(
1109 RequiredValueSyntax {
1110 name: Cow::Borrowed("angle"),
1111 vtype: ExprType::Double,
1112 },
1113 ArgSepSyntax::End,
1114 )],
1115 None,
1116 )])
1117 .with_category(CATEGORY)
1118 .with_description(
1119 "Computes the tangent of an angle.
1120The input angle% or angle# is measured in degrees or radians depending on the angle mode as \
1121selected by the DEG and RAD commands.",
1122 )
1123 .build(),
1124 angle_mode,
1125 })
1126 }
1127}
1128
1129impl Callable for TanFunction {
1130 fn metadata(&self) -> Rc<CallableMetadata> {
1131 self.metadata.clone()
1132 }
1133
1134 fn exec(&self, mut scope: Scope<'_>) -> CallResult<()> {
1135 let angle = get_angle(&mut scope, &self.angle_mode.borrow())?;
1136 scope.return_double(angle.tan())
1137 }
1138}
1139
1140pub fn add_all(machine: &mut MachineBuilder) {
1142 let angle_mode = Rc::from(RefCell::from(AngleMode::Radians));
1143 let prng = Rc::from(RefCell::from(Prng::new_from_entryopy()));
1144 machine.add_clearable(Box::from(ClearableAngleMode { angle_mode: angle_mode.clone() }));
1145 machine.add_callable(AbsFunction::new());
1146 machine.add_callable(AcosFunction::new(angle_mode.clone()));
1147 machine.add_callable(AsinFunction::new(angle_mode.clone()));
1148 machine.add_callable(AtnFunction::new(angle_mode.clone()));
1149 machine.add_callable(CintFunction::new());
1150 machine.add_callable(CosFunction::new(angle_mode.clone()));
1151 machine.add_callable(DegCommand::new(angle_mode.clone()));
1152 machine.add_callable(ExpFunction::new());
1153 machine.add_callable(FixFunction::new());
1154 machine.add_callable(IntFunction::new());
1155 machine.add_callable(LogFunction::new());
1156 machine.add_callable(MaxFunction::new());
1157 machine.add_callable(MinFunction::new());
1158 machine.add_callable(PiFunction::new());
1159 machine.add_callable(RadCommand::new(angle_mode.clone()));
1160 machine.add_callable(RandomizeCommand::new(prng.clone()));
1161 machine.add_callable(RndFunction::new(prng));
1162 machine.add_callable(RoundFunction::new());
1163 machine.add_callable(SgnFunction::new());
1164 machine.add_callable(SinFunction::new(angle_mode.clone()));
1165 machine.add_callable(SqrFunction::new());
1166 machine.add_callable(TanFunction::new(angle_mode));
1167}
1168
1169#[cfg(test)]
1170mod tests {
1171 use crate::testutils::*;
1172
1173 #[test]
1174 fn test_abs() {
1175 check_expr_ok(0.0, "ABS(0)");
1176 check_expr_ok(3.0, "ABS(3)");
1177 check_expr_ok(3.5, "ABS(-3.5)");
1178
1179 check_expr_ok_with_vars(2.5, "ABS(d)", [("d", (-2.5f64).into())]);
1180
1181 check_expr_compilation_error("1:10: ABS expected expr#", "ABS()");
1182 check_expr_compilation_error("1:14: BOOLEAN is not a number", "ABS(FALSE)");
1183 check_expr_compilation_error("1:10: ABS expected expr#", "ABS(3, 4)");
1184 }
1185
1186 #[test]
1187 fn test_acos() {
1188 check_expr_ok(1f64.acos(), "ACOS(1)");
1189 check_expr_ok(0.5f64.acos(), "ACOS(0.5)");
1190
1191 check_expr_ok_with_vars(0.5f64.acos(), "ACOS(d)", [("d", 0.5f64.into())]);
1192
1193 let mut t = Tester::default();
1194 t.run("DEG: result = ACOS(0.5)").expect_var("result", 0.5f64.acos().to_degrees()).check();
1195
1196 check_expr_compilation_error("1:10: ACOS expected n#", "ACOS()");
1197 check_expr_compilation_error("1:15: BOOLEAN is not a number", "ACOS(FALSE)");
1198 check_expr_compilation_error("1:10: ACOS expected n#", "ACOS(3, 4)");
1199 check_expr_error(
1200 "1:15: Cannot take arc-cosine of a number outside of [-1, 1]",
1201 "ACOS(1.1)",
1202 );
1203 }
1204
1205 #[test]
1206 fn test_asin() {
1207 check_expr_ok(0f64.asin(), "ASIN(0)");
1208 check_expr_ok(0.5f64.asin(), "ASIN(0.5)");
1209
1210 check_expr_ok_with_vars(0.5f64.asin(), "ASIN(d)", [("d", 0.5f64.into())]);
1211
1212 let mut t = Tester::default();
1213 t.run("DEG: result = ASIN(0.5)").expect_var("result", 0.5f64.asin().to_degrees()).check();
1214
1215 check_expr_compilation_error("1:10: ASIN expected n#", "ASIN()");
1216 check_expr_compilation_error("1:15: BOOLEAN is not a number", "ASIN(FALSE)");
1217 check_expr_compilation_error("1:10: ASIN expected n#", "ASIN(3, 4)");
1218 check_expr_error("1:15: Cannot take arc-sine of a number outside of [-1, 1]", "ASIN(-1.1)");
1219 }
1220
1221 #[test]
1222 fn test_atn() {
1223 check_expr_ok(123f64.atan(), "ATN(123)");
1224 check_expr_ok(45.5f64.atan(), "ATN(45.5)");
1225
1226 check_expr_ok_with_vars(123f64.atan(), "ATN(a)", [("a", 123i32.into())]);
1227
1228 check_expr_compilation_error("1:10: ATN expected n#", "ATN()");
1229 check_expr_compilation_error("1:14: BOOLEAN is not a number", "ATN(FALSE)");
1230 check_expr_compilation_error("1:10: ATN expected n#", "ATN(3, 4)");
1231 }
1232
1233 #[test]
1234 fn test_cint() {
1235 check_expr_ok(0, "CINT(0.1)");
1236 check_expr_ok(0, "CINT(-0.1)");
1237 check_expr_ok(1, "CINT(0.9)");
1238 check_expr_ok(-1, "CINT(-0.9)");
1239
1240 check_expr_ok_with_vars(1, "CINT(d)", [("d", 0.9f64.into())]);
1241
1242 check_expr_compilation_error("1:10: CINT expected expr#", "CINT()");
1243 check_expr_compilation_error("1:15: BOOLEAN is not a number", "CINT(FALSE)");
1244 check_expr_compilation_error("1:10: CINT expected expr#", "CINT(3.0, 4)");
1245
1246 check_expr_error(
1247 "1:15: Cannot cast -1234567890123456 to integer due to overflow",
1248 "CINT(-1234567890123456.0)",
1249 );
1250 }
1251
1252 #[test]
1253 fn test_cos() {
1254 check_expr_ok(123f64.cos(), "COS(123)");
1255 check_expr_ok(45.5f64.cos(), "COS(45.5)");
1256
1257 check_expr_ok_with_vars(123f64.cos(), "COS(i)", [("i", 123i32.into())]);
1258
1259 check_expr_compilation_error("1:10: COS expected angle#", "COS()");
1260 check_expr_compilation_error("1:14: BOOLEAN is not a number", "COS(FALSE)");
1261 check_expr_compilation_error("1:10: COS expected angle#", "COS(3, 4)");
1262 }
1263
1264 #[test]
1265 fn test_deg_rad_commands() {
1266 let mut t = Tester::default();
1267 t.run("result = SIN(90)").expect_var("result", 90f64.sin()).check();
1268 t.run("DEG: result = SIN(90)").expect_var("result", 1.0).check();
1269 t.run("RAD: result = SIN(90)").expect_var("result", 90f64.sin()).check();
1270 }
1271
1272 #[test]
1273 fn test_deg_rad_reset_on_clear() {
1274 Tester::default()
1275 .run("DEG")
1276 .check()
1277 .clear()
1278 .run("result = SIN(90)")
1279 .expect_clear()
1280 .expect_var("result", 90f64.sin())
1281 .check();
1282 }
1283
1284 #[test]
1285 fn test_deg_rad_errors() {
1286 check_stmt_compilation_err("1:1: DEG expected no arguments", "DEG 1");
1287 check_stmt_compilation_err("1:1: RAD expected no arguments", "RAD 1");
1288 }
1289
1290 #[test]
1291 fn test_exp() {
1292 check_expr_ok(0f64.exp(), "EXP(0)");
1293 check_expr_ok(1f64.exp(), "EXP(1)");
1294 check_expr_ok((-2.5f64).exp(), "EXP(-2.5)");
1295
1296 check_expr_ok_with_vars(2f64.exp(), "EXP(i)", [("i", 2i32.into())]);
1297
1298 check_expr_compilation_error("1:10: EXP expected expr#", "EXP()");
1299 check_expr_compilation_error("1:14: BOOLEAN is not a number", "EXP(FALSE)");
1300 check_expr_compilation_error("1:10: EXP expected expr#", "EXP(3, 4)");
1301 check_expr_error("1:14: Cannot exponentiate 1000 due to overflow", "EXP(1000)");
1302 }
1303
1304 #[test]
1305 fn test_fix() {
1306 check_expr_ok(0, "FIX(0.1)");
1307 check_expr_ok(0, "FIX(-0.1)");
1308 check_expr_ok(4, "FIX(4.9)");
1309 check_expr_ok(-4, "FIX(-4.9)");
1310
1311 check_expr_ok_with_vars(-4, "FIX(d)", [("d", (-4.9f64).into())]);
1312
1313 check_expr_compilation_error("1:10: FIX expected expr#", "FIX()");
1314 check_expr_compilation_error("1:14: BOOLEAN is not a number", "FIX(FALSE)");
1315 check_expr_compilation_error("1:10: FIX expected expr#", "FIX(3.0, 4)");
1316
1317 check_expr_error(
1318 "1:14: Cannot cast -1234567890123456 to integer due to overflow",
1319 "FIX(-1234567890123456.0)",
1320 );
1321 }
1322
1323 #[test]
1324 fn test_int() {
1325 check_expr_ok(0, "INT(0.1)");
1326 check_expr_ok(-1, "INT(-0.1)");
1327 check_expr_ok(0, "INT(0.9)");
1328 check_expr_ok(-1, "INT(-0.9)");
1329
1330 check_expr_ok_with_vars(0, "INT(d)", [("d", 0.9f64.into())]);
1331
1332 check_expr_compilation_error("1:10: INT expected expr#", "INT()");
1333 check_expr_compilation_error("1:14: BOOLEAN is not a number", "INT(FALSE)");
1334 check_expr_compilation_error("1:10: INT expected expr#", "INT(3.0, 4)");
1335
1336 check_expr_error(
1337 "1:14: Cannot cast -1234567890123456 to integer due to overflow",
1338 "INT(-1234567890123456.0)",
1339 );
1340 }
1341
1342 #[test]
1343 fn test_log() {
1344 check_expr_ok(1f64.ln(), "LOG(1)");
1345 check_expr_ok(10f64.ln(), "LOG(10)");
1346
1347 check_expr_ok_with_vars(10f64.ln(), "LOG(i)", [("i", 10i32.into())]);
1348
1349 check_expr_compilation_error("1:10: LOG expected num#", "LOG()");
1350 check_expr_compilation_error("1:14: BOOLEAN is not a number", "LOG(FALSE)");
1351 check_expr_compilation_error("1:10: LOG expected num#", "LOG(3, 4)");
1352 check_expr_error("1:14: Cannot take logarithm of zero or a negative number", "LOG(0)");
1353 check_expr_error("1:14: Cannot take logarithm of zero or a negative number", "LOG(-0.1)");
1354 }
1355
1356 #[test]
1357 fn test_max() {
1358 check_expr_ok(0.0, "MAX(0)");
1359 check_expr_ok(0.0, "MAX(0, 0)");
1360
1361 check_expr_ok(0.0, "MAX(0.0)");
1362 check_expr_ok(0.0, "MAX(0.0, 0.0)");
1363
1364 check_expr_ok(1.0, "MAX(1)");
1365 check_expr_ok(5.0, "MAX(5, 3, 4)");
1366 check_expr_ok(-3.0, "MAX(-5, -3, -4)");
1367
1368 check_expr_ok(1.0, "MAX(1.0)");
1369 check_expr_ok(5.3, "MAX(5.3, 3.5, 4.2)");
1370 check_expr_ok(-3.5, "MAX(-5.3, -3.5, -4.2)");
1371
1372 check_expr_ok(2.5, "MAX(1, 0.5, 2.5, 2)");
1373
1374 check_expr_ok_with_vars(
1375 5.0,
1376 "MAX(i, j, k)",
1377 [("i", 5i32.into()), ("j", 3i32.into()), ("k", 4i32.into())],
1378 );
1379
1380 check_expr_compilation_error("1:10: MAX expected expr1#[, .., exprN#]", "MAX()");
1381 check_expr_compilation_error("1:14: BOOLEAN is not a number", "MAX(FALSE)");
1382 }
1383
1384 #[test]
1385 fn test_min() {
1386 check_expr_ok(0.0, "MIN(0)");
1387 check_expr_ok(0.0, "MIN(0, 0)");
1388
1389 check_expr_ok(0.0, "MIN(0.0)");
1390 check_expr_ok(0.0, "MIN(0.0, 0.0)");
1391
1392 check_expr_ok(1.0, "MIN(1)");
1393 check_expr_ok(3.0, "MIN(5, 3, 4)");
1394 check_expr_ok(-5.0, "MIN(-5, -3, -4)");
1395
1396 check_expr_ok(1.0, "MIN(1.0)");
1397 check_expr_ok(3.5, "MIN(5.3, 3.5, 4.2)");
1398 check_expr_ok(-5.3, "MIN(-5.3, -3.5, -4.2)");
1399
1400 check_expr_ok(0.5, "MIN(1, 0.5, 2.5, 2)");
1401
1402 check_expr_ok_with_vars(
1403 3.0,
1404 "MIN(i, j, k)",
1405 [("i", 5i32.into()), ("j", 3i32.into()), ("k", 4i32.into())],
1406 );
1407
1408 check_expr_compilation_error("1:10: MIN expected expr1#[, .., exprN#]", "MIN()");
1409 check_expr_compilation_error("1:14: BOOLEAN is not a number", "MIN(FALSE)");
1410 }
1411
1412 #[test]
1413 fn test_pi() {
1414 check_expr_ok(std::f64::consts::PI, "PI");
1415
1416 check_expr_compilation_error("1:10: PI expected no arguments", "PI()");
1417 check_expr_compilation_error("1:10: PI expected no arguments", "PI(3)");
1418 }
1419
1420 #[test]
1421 fn test_randomize_and_rnd() {
1422 check_expr_ok(false, "RND(1) = RND(1)");
1426 check_expr_ok(false, "RND(1) = RND(10)");
1427 check_expr_ok(true, "RND(-1) = RND(-1)");
1428 check_expr_ok(false, "RND(-1) = RND(-2)");
1429 check_expr_ok(true, "RND(0) = RND(0)");
1430
1431 Tester::default()
1432 .run("RANDOMIZE 10")
1433 .check()
1434 .run("result = RND(1)")
1435 .expect_var("result", 0.7097578208683426)
1436 .check()
1437 .run("result = RND(-1)")
1438 .expect_var("result", 0.6150244305876607)
1439 .check()
1440 .run("result = RND(1)")
1441 .expect_var("result", 0.11707478019340774)
1442 .check()
1443 .run("result = RND(-1)")
1444 .expect_var("result", 0.6150244305876607)
1445 .check()
1446 .run("result = RND(1.1)")
1447 .expect_var("result", 0.11707478019340774)
1448 .check()
1449 .run("result = RND(0)")
1450 .expect_var("result", 0.11707478019340774)
1451 .check()
1452 .run("result = RND(10)")
1453 .expect_var("result", 0.8423819585801992)
1454 .check()
1455 .run("RANDOMIZE 10.2")
1456 .expect_var("result", 0.8423819585801992)
1457 .check()
1458 .run("result = RND(1)")
1459 .expect_var("result", 0.7097578208683426)
1460 .check();
1461
1462 check_expr_compilation_error("1:10: RND expected <> | <n%>", "RND(1, 7)");
1463 check_expr_compilation_error("1:14: BOOLEAN is not a number", "RND(FALSE)");
1464
1465 check_stmt_compilation_err("1:1: RANDOMIZE expected <> | <seed%>", "RANDOMIZE ,");
1466 check_stmt_compilation_err("1:11: BOOLEAN is not a number", "RANDOMIZE TRUE");
1467 }
1468
1469 #[test]
1470 fn test_round() {
1471 check_expr_ok(1.0, "ROUND(0.5)");
1472 check_expr_ok(-1.0, "ROUND(-0.5)");
1473 check_expr_ok(12.35, "ROUND(12.345, 2)");
1474 check_expr_ok(12.3, "ROUND(12.345, 1)");
1475 check_expr_ok(120.0, "ROUND(123.45, -1)");
1476
1477 check_expr_ok_with_vars(12.35, "ROUND(d, 2)", [("d", 12.345f64.into())]);
1478
1479 check_expr_compilation_error(
1480 "1:10: ROUND expected <expr#> | <expr#, decimals%>",
1481 "ROUND()",
1482 );
1483 check_expr_compilation_error("1:16: BOOLEAN is not a number", "ROUND(FALSE)");
1484 check_expr_compilation_error(
1485 "1:10: ROUND expected <expr#> | <expr#, decimals%>",
1486 "ROUND(1, 2, 3)",
1487 );
1488 }
1489
1490 #[test]
1491 fn test_sgn() {
1492 check_expr_ok(-1, "SGN(-3)");
1493 check_expr_ok(0, "SGN(0)");
1494 check_expr_ok(1, "SGN(3.5)");
1495
1496 check_expr_ok_with_vars(-1, "SGN(d)", [("d", (-1.5f64).into())]);
1497
1498 check_expr_compilation_error("1:10: SGN expected n#", "SGN()");
1499 check_expr_compilation_error("1:14: BOOLEAN is not a number", "SGN(FALSE)");
1500 check_expr_compilation_error("1:10: SGN expected n#", "SGN(3, 4)");
1501 }
1502
1503 #[test]
1504 fn test_sin() {
1505 check_expr_ok(123f64.sin(), "SIN(123)");
1506 check_expr_ok(45.5f64.sin(), "SIN(45.5)");
1507
1508 check_expr_ok_with_vars(123f64.sin(), "SIN(i)", [("i", 123i32.into())]);
1509
1510 check_expr_compilation_error("1:10: SIN expected angle#", "SIN()");
1511 check_expr_compilation_error("1:14: BOOLEAN is not a number", "SIN(FALSE)");
1512 check_expr_compilation_error("1:10: SIN expected angle#", "SIN(3, 4)");
1513 }
1514
1515 #[test]
1516 fn test_sqr() {
1517 check_expr_ok(0f64.sqrt(), "SQR(0)");
1518 check_expr_ok(-0f64.sqrt(), "SQR(-0.0)");
1519 check_expr_ok(9f64.sqrt(), "SQR(9)");
1520 check_expr_ok(100.50f64.sqrt(), "SQR(100.50)");
1521
1522 check_expr_ok_with_vars(9f64.sqrt(), "SQR(i)", [("i", 9i32.into())]);
1523
1524 check_expr_compilation_error("1:10: SQR expected num#", "SQR()");
1525 check_expr_compilation_error("1:14: BOOLEAN is not a number", "SQR(FALSE)");
1526 check_expr_compilation_error("1:10: SQR expected num#", "SQR(3, 4)");
1527 check_expr_error("1:14: Cannot take square root of a negative number", "SQR(-3)");
1528 check_expr_error("1:14: Cannot take square root of a negative number", "SQR(-0.1)");
1529 }
1530
1531 #[test]
1532 fn test_tan() {
1533 check_expr_ok(123f64.tan(), "TAN(123)");
1534 check_expr_ok(45.5f64.tan(), "TAN(45.5)");
1535
1536 check_expr_ok_with_vars(123f64.tan(), "TAN(i)", [("i", 123i32.into())]);
1537
1538 check_expr_compilation_error("1:10: TAN expected angle#", "TAN()");
1539 check_expr_compilation_error("1:14: BOOLEAN is not a number", "TAN(FALSE)");
1540 check_expr_compilation_error("1:10: TAN expected angle#", "TAN(3, 4)");
1541 }
1542}