| 1 | #!/usr/local/bin/webwork-perl |
1 | |
| 2 | |
2 | |
| 3 | # This is extraAnswerEvaluators.pl |
3 | # This is extraAnswerEvaluators.pl |
| 4 | |
4 | |
| 5 | # Most of the work is done in special namespaces |
5 | # Most of the work is done in special namespaces |
| 6 | # At the end, we provide one global function, the interval answer evaluator |
6 | # At the end, we provide one global function, the interval answer evaluator |
| … | |
… | |
| 21 | and equations. |
21 | and equations. |
| 22 | |
22 | |
| 23 | interval_cmp() -- checks answers which are unions of intervals. |
23 | interval_cmp() -- checks answers which are unions of intervals. |
| 24 | It can also be used for checking an ordered pair or |
24 | It can also be used for checking an ordered pair or |
| 25 | list of ordered pairs. |
25 | list of ordered pairs. |
| 26 | |
26 | |
| 27 | number_list_cmp() -- checks a comma separated list of numbers. By use of |
27 | number_list_cmp() -- checks a comma separated list of numbers. By use of |
| 28 | optional arguments, you can request that order be |
28 | optional arguments, you can request that order be |
| 29 | important, that complex numbers be allowed, and |
29 | important, that complex numbers be allowed, and |
| 30 | specify extra arguments to be sent to num_cmp (or |
30 | specify extra arguments to be sent to num_cmp (or |
| 31 | cplx_cmp) for checking individual entries. |
31 | cplx_cmp) for checking individual entries. |
| 32 | |
32 | |
| 33 | equation_cmp() -- provides a limited facility for checking equations. |
33 | equation_cmp() -- provides a limited facility for checking equations. |
| 34 | It makes no pretense of checking to see if the real locus |
34 | It makes no pretense of checking to see if the real locus |
| 35 | of the student's equation matches the real locus of the |
35 | of the student's equation matches the real locus of the |
| 36 | instructor's equation. The student's equation must be |
36 | instructor's equation. The student's equation must be |
| 37 | of the same general type as the instructors to get credit. |
37 | of the same general type as the instructors to get credit. |
| … | |
… | |
| 47 | =cut |
47 | =cut |
| 48 | |
48 | |
| 49 | |
49 | |
| 50 | { |
50 | { |
| 51 | package Intervals; |
51 | package Intervals; |
| 52 | |
52 | |
| 53 | # We accept any of the following as infinity (case insensitive) |
53 | # We accept any of the following as infinity (case insensitive) |
| 54 | @infinitywords = ("i", "inf", "infty", "infinity"); |
54 | @infinitywords = ("i", "inf", "infty", "infinity"); |
| 55 | $infinityre = join '|', @infinitywords; |
55 | $infinityre = join '|', @infinitywords; |
| 56 | $infinityre = "^([-+m]?)($infinityre)\$"; |
56 | $infinityre = "^([-+m]?)($infinityre)\$"; |
| 57 | |
57 | |
| … | |
… | |
| 123 | delete($opts{'unions'}); |
123 | delete($opts{'unions'}); |
| 124 | my($b1str,$b2str) = (', ', ', '); |
124 | my($b1str,$b2str) = (', ', ', '); |
| 125 | if($unions) { |
125 | if($unions) { |
| 126 | ($b1str,$b2str) = (' U ', ' \cup '); |
126 | ($b1str,$b2str) = (' U ', ' \cup '); |
| 127 | } |
127 | } |
| 128 | |
128 | |
| 129 | my($tmp_ae) = main::num_cmp(1, %opts); |
129 | my($tmp_ae) = main::num_cmp(1, %opts); |
| 130 | $self->{'normalized'} = ''; |
130 | $self->{'normalized'} = ''; |
| 131 | $self->{'value'} = ''; |
131 | $self->{'value'} = ''; |
| 132 | $self->{'latex'} = ''; |
132 | $self->{'latex'} = ''; |
| 133 | $self->{'htmlerror'} = ''; |
133 | $self->{'htmlerror'} = ''; |
| … | |
… | |
| 231 | $level--; |
231 | $level--; |
| 232 | } |
232 | } |
| 233 | } |
233 | } |
| 234 | $spot++; |
234 | $spot++; |
| 235 | } |
235 | } |
| 236 | |
236 | |
| 237 | if($level>0) { |
237 | if($level>0) { |
| 238 | $self->error("Your expression ended in the middle of an interval.", |
238 | $self->error("Your expression ended in the middle of an interval.", |
| 239 | [$hold, $spot]); |
239 | [$hold, $spot]); |
| 240 | return 0; |
240 | return 0; |
| 241 | } |
241 | } |
| … | |
… | |
| 356 | |
356 | |
| 357 | $str = uc($str); |
357 | $str = uc($str); |
| 358 | $str =~ s/\s//g; # remove white space |
358 | $str =~ s/\s//g; # remove white space |
| 359 | $str; |
359 | $str; |
| 360 | } |
360 | } |
| 361 | |
361 | |
| 362 | ##### The answer evaluator |
362 | ##### The answer evaluator |
| 363 | |
363 | |
| 364 | sub interval_cmp { |
364 | sub interval_cmp { |
| 365 | |
365 | |
| 366 | my $right_ans = shift; |
366 | my $right_ans = shift; |
| 367 | my %opts = @_; |
367 | my %opts = @_; |
| 368 | |
368 | |
| 369 | $opts{'mode'} = 'std' unless defined($opts{'mode'}); |
369 | $opts{'mode'} = 'std' unless defined($opts{'mode'}); |
| 370 | $opts{'tolType'} = 'relative' unless defined($opts{'tolType'}); |
370 | $opts{'tolType'} = 'relative' unless defined($opts{'tolType'}); |
| 371 | |
371 | |
| 372 | my $ans_eval = sub { |
372 | my $ans_eval = sub { |
| 373 | my $student = shift; |
373 | my $student = shift; |
| 374 | |
374 | |
| 375 | my $ans_hash = new AnswerHash( |
375 | my $ans_hash = new AnswerHash( |
| 376 | 'score'=>0, |
376 | 'score'=>0, |
| 377 | 'correct_ans'=>$right_ans, |
377 | 'correct_ans'=>$right_ans, |
| 378 | 'student_ans'=>$student, |
378 | 'student_ans'=>$student, |
| 379 | 'original_student_ans' => $student, |
379 | 'original_student_ans' => $student, |
| … | |
… | |
| 404 | # Error in student input |
404 | # Error in student input |
| 405 | $ans_hash->{'student_ans'} = "error: $student_int->{htmlerror}"; |
405 | $ans_hash->{'student_ans'} = "error: $student_int->{htmlerror}"; |
| 406 | $ans_hash->{'ans_message'} = "$student_int->{error_msg}"; |
406 | $ans_hash->{'ans_message'} = "$student_int->{error_msg}"; |
| 407 | return $ans_hash; |
407 | return $ans_hash; |
| 408 | } |
408 | } |
| 409 | |
409 | |
| 410 | $ans_hash->{'student_ans'} = $student_int->{'value'}; |
410 | $ans_hash->{'student_ans'} = $student_int->{'value'}; |
| 411 | $ans_hash->{'preview_text_string'} = $student_int->{'normalized'}; |
411 | $ans_hash->{'preview_text_string'} = $student_int->{'normalized'}; |
| 412 | $ans_hash->{'preview_latex_string'} = $student_int->{'latex'}; |
412 | $ans_hash->{'preview_latex_string'} = $student_int->{'latex'}; |
| 413 | } |
413 | } |
| 414 | |
414 | |
| … | |
… | |
| 439 | } |
439 | } |
| 440 | |
440 | |
| 441 | |
441 | |
| 442 | { |
442 | { |
| 443 | package Number_List; |
443 | package Number_List; |
| 444 | |
444 | |
| 445 | sub new { |
445 | sub new { |
| 446 | my $class = shift; |
446 | my $class = shift; |
| 447 | my $base_string = shift; |
447 | my $base_string = shift; |
| 448 | my $self = {}; |
448 | my $self = {}; |
| 449 | $self->{'original'} = $base_string; |
449 | $self->{'original'} = $base_string; |
| … | |
… | |
| 464 | my ($in,$PG_errors,$PG_errors_long) = main::PG_restricted_eval($instring); |
464 | my ($in,$PG_errors,$PG_errors_long) = main::PG_restricted_eval($instring); |
| 465 | return ($in+0*Complex1::i()); |
465 | return ($in+0*Complex1::i()); |
| 466 | } |
466 | } |
| 467 | |
467 | |
| 468 | |
468 | |
| 469 | |
469 | |
| 470 | sub parse_number_list { |
470 | sub parse_number_list { |
| 471 | my($self) = shift; |
471 | my($self) = shift; |
| 472 | my(%opts) = @_; |
472 | my(%opts) = @_; |
| 473 | my($str) = $self->{'original'}; |
473 | my($str) = $self->{'original'}; |
| 474 | my(@ans_list) = (); |
474 | my(@ans_list) = (); |
| … | |
… | |
| 532 | $level--; |
532 | $level--; |
| 533 | } # end of closing brace |
533 | } # end of closing brace |
| 534 | } |
534 | } |
| 535 | $spot++; |
535 | $spot++; |
| 536 | } |
536 | } |
| 537 | |
537 | |
| 538 | if($level>1) { |
538 | if($level>1) { |
| 539 | $self->error("Your expression has unmatched parens.", |
539 | $self->error("Your expression has unmatched parens.", |
| 540 | [$hold, $spot]); |
540 | [$hold, $spot]); |
| 541 | return 0; |
541 | return 0; |
| 542 | } |
542 | } |
| 543 | $cur = substr($str,$hold, $spot-$hold); |
543 | $cur = substr($str,$hold, $spot-$hold); |
| 544 | |
544 | |
| 545 | my($tmp_ah); |
545 | my($tmp_ah); |
| 546 | $tmp_ah = $tmp_ae->evaluate($cur); |
546 | $tmp_ah = $tmp_ae->evaluate($cur); |
| 547 | |
547 | |
| 548 | if(has_errors($tmp_ah)) { |
548 | if(has_errors($tmp_ah)) { |
| 549 | $self->error("I could not parse your input correctly",[$hold, $spot]); |
549 | $self->error("I could not parse your input correctly",[$hold, $spot]); |
| 550 | return 0; |
550 | return 0; |
| 551 | } |
551 | } |
| 552 | if(not ($cur =~ /\w/)) { # Input was empty |
552 | if(not ($cur =~ /\w/)) { # Input was empty |
| … | |
… | |
| 578 | $opts{'mode'} = 'std' unless defined($opts{'mode'}); |
578 | $opts{'mode'} = 'std' unless defined($opts{'mode'}); |
| 579 | $opts{'tolType'} = 'relative' unless defined($opts{'tolType'}); |
579 | $opts{'tolType'} = 'relative' unless defined($opts{'tolType'}); |
| 580 | |
580 | |
| 581 | my $ans_eval = sub { |
581 | my $ans_eval = sub { |
| 582 | my $student = shift; |
582 | my $student = shift; |
| 583 | |
583 | |
| 584 | my $ans_hash = new AnswerHash( |
584 | my $ans_hash = new AnswerHash( |
| 585 | 'score'=>0, |
585 | 'score'=>0, |
| 586 | 'correct_ans'=>$right_ans, |
586 | 'correct_ans'=>$right_ans, |
| 587 | 'student_ans'=>$student, |
587 | 'student_ans'=>$student, |
| 588 | 'original_student_ans' => $student, |
588 | 'original_student_ans' => $student, |
| … | |
… | |
| 600 | } |
600 | } |
| 601 | |
601 | |
| 602 | $ans_hash->{'student_ans'} = $student_list->{'value'}; |
602 | $ans_hash->{'student_ans'} = $student_list->{'value'}; |
| 603 | $ans_hash->{'preview_text_string'} = $student_list->{'normalized'}; |
603 | $ans_hash->{'preview_text_string'} = $student_list->{'normalized'}; |
| 604 | $ans_hash->{'preview_latex_string'} = $student_list->{'latex'}; |
604 | $ans_hash->{'preview_latex_string'} = $student_list->{'latex'}; |
| 605 | |
605 | |
| 606 | my $correct_list = new Number_List($right_ans); |
606 | my $correct_list = new Number_List($right_ans); |
| 607 | if(! $correct_list->parse_number_list(%opts)) { |
607 | if(! $correct_list->parse_number_list(%opts)) { |
| 608 | # Cannot parse instuctor's answer! |
608 | # Cannot parse instuctor's answer! |
| 609 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
609 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
| 610 | return $ans_hash; |
610 | return $ans_hash; |
| 611 | } |
611 | } |
| 612 | if (cmp_numlists($correct_list, $student_list, %opts)) { |
612 | if (cmp_numlists($correct_list, $student_list, %opts)) { |
| 613 | $ans_hash -> setKeys('score' => 1); |
613 | $ans_hash -> setKeys('score' => 1); |
| 614 | } |
614 | } |
| 615 | |
615 | |
| 616 | return $ans_hash; |
616 | return $ans_hash; |
| 617 | }; |
617 | }; |
| 618 | |
618 | |
| 619 | return $ans_eval; |
619 | return $ans_eval; |
| 620 | } |
620 | } |
| … | |
… | |
| 630 | my($strict_ordering) = 0; |
630 | my($strict_ordering) = 0; |
| 631 | if (defined($opts{'ordered'}) && ($opts{'ordered'} eq 'yes')) { |
631 | if (defined($opts{'ordered'}) && ($opts{'ordered'} eq 'yes')) { |
| 632 | $strict_ordering = 1; |
632 | $strict_ordering = 1; |
| 633 | } |
633 | } |
| 634 | delete($opts{'ordered'}); |
634 | delete($opts{'ordered'}); |
| 635 | |
635 | |
| 636 | my $complex=0; |
636 | my $complex=0; |
| 637 | if(defined($opts{'complex'}) && |
637 | if(defined($opts{'complex'}) && |
| 638 | ($opts{'complex'} =~ /(yes|ok)/i)) { |
638 | ($opts{'complex'} =~ /(yes|ok)/i)) { |
| 639 | $complex=1; |
639 | $complex=1; |
| 640 | delete($opts{'mode'}); |
640 | delete($opts{'mode'}); |
| … | |
… | |
| 642 | delete($opts{'complex'}); |
642 | delete($opts{'complex'}); |
| 643 | |
643 | |
| 644 | my(@fs1) = @{$in1->{'forsort'}}; |
644 | my(@fs1) = @{$in1->{'forsort'}}; |
| 645 | my(@fs2) = @{$in2->{'forsort'}}; |
645 | my(@fs2) = @{$in2->{'forsort'}}; |
| 646 | |
646 | |
| 647 | |
647 | |
| 648 | # Same number of values? |
648 | # Same number of values? |
| 649 | if (scalar(@fs1) != scalar(@fs2)) { |
649 | if (scalar(@fs1) != scalar(@fs2)) { |
| 650 | return 0; |
650 | return 0; |
| 651 | } |
651 | } |
| 652 | |
652 | |
| … | |
… | |
| 658 | |
658 | |
| 659 | if($strict_ordering==0) { |
659 | if($strict_ordering==0) { |
| 660 | @fs1 = main::PGsort(sub {$_[0]->[1] <=> $_[1]->[1];}, @fs1); |
660 | @fs1 = main::PGsort(sub {$_[0]->[1] <=> $_[1]->[1];}, @fs1); |
| 661 | @fs2 = main::PGsort(sub {$_[0]->[1] <=> $_[1]->[1];}, @fs2); |
661 | @fs2 = main::PGsort(sub {$_[0]->[1] <=> $_[1]->[1];}, @fs2); |
| 662 | } |
662 | } |
| 663 | |
663 | |
| 664 | for ($j=0; $j<scalar(@fs1);$j++) { |
664 | for ($j=0; $j<scalar(@fs1);$j++) { |
| 665 | my $ae; |
665 | my $ae; |
| 666 | if($complex) { |
666 | if($complex) { |
| 667 | $ae = main::cplx_cmp($fs1[$j]->[1], %opts); |
667 | $ae = main::cplx_cmp($fs1[$j]->[1], %opts); |
| 668 | } else { |
668 | } else { |
| … | |
… | |
| 689 | $ap->inittokenizer($self->{'original'}); |
689 | $ap->inittokenizer($self->{'original'}); |
| 690 | $ap->error(@args); |
690 | $ap->error(@args); |
| 691 | $self->{htmlerror} = $ap->{htmlerror}; |
691 | $self->{htmlerror} = $ap->{htmlerror}; |
| 692 | $self->{error_msg} = $ap->{error_msg}; |
692 | $self->{error_msg} = $ap->{error_msg}; |
| 693 | } |
693 | } |
| 694 | |
694 | |
| 695 | sub has_errors { |
695 | sub has_errors { |
| 696 | my($ah) = shift; |
696 | my($ah) = shift; |
| 697 | |
697 | |
| 698 | if($ah->{'student_ans'} =~ /error/) { |
698 | if($ah->{'student_ans'} =~ /error/) { |
| 699 | return 1; |
699 | return 1; |
| 700 | } |
700 | } |
| 701 | my($am) = $ah->{'ans_message'}; |
701 | my($am) = $ah->{'ans_message'}; |
| 702 | if($am =~ /error/) { |
702 | if($am =~ /error/) { |
| … | |
… | |
| 715 | # interval_cmp("[1,2) U [3, infty)", options) |
715 | # interval_cmp("[1,2) U [3, infty)", options) |
| 716 | # where options are key/value pairs for num_cmp. Also, we allow the option |
716 | # where options are key/value pairs for num_cmp. Also, we allow the option |
| 717 | # 'ordering' which can be 'strict', which means that we do not want to test rearrangements |
717 | # 'ordering' which can be 'strict', which means that we do not want to test rearrangements |
| 718 | # of the intervals. |
718 | # of the intervals. |
| 719 | |
719 | |
| 720 | |
720 | |
| 721 | } |
721 | } |
| 722 | |
722 | |
| 723 | { |
723 | { |
| 724 | package Equation_eval; |
724 | package Equation_eval; |
| 725 | |
725 | |
| 726 | sub split_eqn { |
726 | sub split_eqn { |
| 727 | my $instring = shift; |
727 | my $instring = shift; |
| 728 | |
728 | |
| 729 | split /=/, $instring; |
729 | split /=/, $instring; |
| 730 | } |
730 | } |
| 731 | |
731 | |
| 732 | |
732 | |
| 733 | sub equation_cmp { |
733 | sub equation_cmp { |
| 734 | my $right_ans = shift; |
734 | my $right_ans = shift; |
| 735 | my %opts = @_; |
735 | my %opts = @_; |
| 736 | my $vars = ['x','y']; |
736 | my $vars = ['x','y']; |
| 737 | |
737 | |
| 738 | |
738 | |
| 739 | $vars = $opts{'vars'} if defined($opts{'vars'}); |
739 | $vars = $opts{'vars'} if defined($opts{'vars'}); |
| 740 | |
740 | |
| 741 | my $ans_eval = sub { |
741 | my $ans_eval = sub { |
| 742 | my $student = shift; |
742 | my $student = shift; |
| 743 | |
743 | |
| 744 | my $ans_hash = new AnswerHash( |
744 | my $ans_hash = new AnswerHash( |
| 745 | 'score'=>0, |
745 | 'score'=>0, |
| 746 | 'correct_ans'=>$right_ans, |
746 | 'correct_ans'=>$right_ans, |
| 747 | 'student_ans'=>$student, |
747 | 'student_ans'=>$student, |
| 748 | 'original_student_ans' => $student, |
748 | 'original_student_ans' => $student, |
| … | |
… | |
| 751 | 'preview_text_string'=>'', |
751 | 'preview_text_string'=>'', |
| 752 | 'preview_latex_string'=>'', |
752 | 'preview_latex_string'=>'', |
| 753 | ); |
753 | ); |
| 754 | |
754 | |
| 755 | if(! ($student =~ /\S/)) { return $ans_hash; } |
755 | if(! ($student =~ /\S/)) { return $ans_hash; } |
| 756 | |
756 | |
| 757 | my @right= split_eqn($right_ans); |
757 | my @right= split_eqn($right_ans); |
| 758 | if(scalar(@right) != 2) { |
758 | if(scalar(@right) != 2) { |
| 759 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
759 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
| 760 | return $ans_hash; |
760 | return $ans_hash; |
| 761 | } |
761 | } |
| … | |
… | |
| 772 | $ah=main::check_syntax($ah); |
772 | $ah=main::check_syntax($ah); |
| 773 | if($ah->{error_flag}) { |
773 | if($ah->{error_flag}) { |
| 774 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
774 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
| 775 | return $ans_hash; |
775 | return $ans_hash; |
| 776 | } |
776 | } |
| 777 | |
777 | |
| 778 | $ah->input($right[1]); |
778 | $ah->input($right[1]); |
| 779 | $ah=main::check_syntax($ah); |
779 | $ah=main::check_syntax($ah); |
| 780 | if($ah->{error_flag}) { |
780 | if($ah->{error_flag}) { |
| 781 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
781 | $ans_hash->{'ans_message'} = "Tell your professor that there is an error in this problem."; |
| 782 | return $ans_hash; |
782 | return $ans_hash; |
| … | |
… | |
| 792 | $ans_hash->{'ans_message'} = "Syntax error on the left side of your equation."; |
792 | $ans_hash->{'ans_message'} = "Syntax error on the left side of your equation."; |
| 793 | return $ans_hash; |
793 | return $ans_hash; |
| 794 | } |
794 | } |
| 795 | $prevs[0] = $ah->{'preview_latex_string'}; |
795 | $prevs[0] = $ah->{'preview_latex_string'}; |
| 796 | $prevstxt[0] = $ah->{'preview_text_string'}; |
796 | $prevstxt[0] = $ah->{'preview_text_string'}; |
| 797 | |
797 | |
| 798 | |
798 | |
| 799 | $ah->input($studsplit[1]); |
799 | $ah->input($studsplit[1]); |
| 800 | $ah=main::check_syntax($ah); |
800 | $ah=main::check_syntax($ah); |
| 801 | if($ah->{error_flag}) { |
801 | if($ah->{error_flag}) { |
| 802 | $ans_hash->{'ans_message'} = "Syntax error on the right side of your equation."; |
802 | $ans_hash->{'ans_message'} = "Syntax error on the right side of your equation."; |
| 803 | return $ans_hash; |
803 | return $ans_hash; |
| … | |
… | |
| 805 | $prevs[1] = $ah->{'preview_latex_string'}; |
805 | $prevs[1] = $ah->{'preview_latex_string'}; |
| 806 | $prevstxt[1] = $ah->{'preview_text_string'}; |
806 | $prevstxt[1] = $ah->{'preview_text_string'}; |
| 807 | |
807 | |
| 808 | $ans_hash->{'preview_latex_string'} = "$prevs[0] = $prevs[1]"; |
808 | $ans_hash->{'preview_latex_string'} = "$prevs[0] = $prevs[1]"; |
| 809 | $ans_hash->{'preview_text_string'} = "$prevstxt[0] = $prevstxt[1]"; |
809 | $ans_hash->{'preview_text_string'} = "$prevstxt[0] = $prevstxt[1]"; |
| 810 | |
810 | |
| 811 | |
811 | |
| 812 | # Check for answer equivalent to 0=0 |
812 | # Check for answer equivalent to 0=0 |
| 813 | # Could be false positive below because of parameter |
813 | # Could be false positive below because of parameter |
| 814 | my $ae = main::fun_cmp("0", %opts); |
814 | my $ae = main::fun_cmp("0", %opts); |
| 815 | my $res = $ae->evaluate("$studsplit[0]-($studsplit[1])"); |
815 | my $res = $ae->evaluate("$studsplit[0]-($studsplit[1])"); |
| 816 | if($res->{'score'}==1) { |
816 | if($res->{'score'}==1) { |
| … | |
… | |
| 827 | if($res->{'score'}==1) { |
827 | if($res->{'score'}==1) { |
| 828 | return $ans_hash; |
828 | return $ans_hash; |
| 829 | } |
829 | } |
| 830 | |
830 | |
| 831 | # Finally, use fun_cmp to check the answers |
831 | # Finally, use fun_cmp to check the answers |
| 832 | |
832 | |
| 833 | $ae = main::fun_cmp("o*($right[0]-($right[1]))", vars=>$vars, params=>['o'], %opts); |
833 | $ae = main::fun_cmp("o*($right[0]-($right[1]))", vars=>$vars, params=>['o'], %opts); |
| 834 | $res= $ae->evaluate("$studsplit[0]-($studsplit[1])"); |
834 | $res= $ae->evaluate("$studsplit[0]-($studsplit[1])"); |
| 835 | $ans_hash-> setKeys('score' => $res->{'score'}); |
835 | $ans_hash-> setKeys('score' => $res->{'score'}); |
| 836 | |
836 | |
| 837 | return $ans_hash; |
837 | return $ans_hash; |
| 838 | }; |
838 | }; |
| 839 | |
839 | |
| 840 | return $ans_eval; |
840 | return $ans_eval; |
| 841 | } |
841 | } |