diff --git a/LICENSE b/LICENSE index 8df1667545..980aae59d3 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ Online Homework Delivery System Version 2.* - Copyright 2000-2018, The WeBWorK Project + Copyright 2000-2022, The WeBWorK Project All rights reserved. This program is free software; you can redistribute it and/or modify diff --git a/README b/README index 2326906afb..45ed6d2993 100644 --- a/README +++ b/README @@ -6,6 +6,6 @@ http://webwork.maa.org/wiki/Category:Release_Notes - Copyright 2000-2017, The WeBWorK Project + Copyright 2000-2022, The WeBWorK Project http://webwork.maa.org All rights reserved. diff --git a/VERSION b/VERSION index e9d9c39ee9..0048b529d4 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ -$PG_VERSION ='2.16+develop'; -$PG_COPYRIGHT_YEARS = '1996-2021'; +$PG_VERSION ='2.17'; +$PG_COPYRIGHT_YEARS = '1996-2022'; 1; diff --git a/htdocs/js/apps/GraphTool/cubictool.js b/htdocs/js/apps/GraphTool/cubictool.js index a07f026580..b059f9724a 100644 --- a/htdocs/js/apps/GraphTool/cubictool.js +++ b/htdocs/js/apps/GraphTool/cubictool.js @@ -38,6 +38,7 @@ } el.setPosition(JXG.COORDS_BY_USER, [x, el.Y()]); + gt.board.update(); } }, diff --git a/htdocs/js/apps/GraphTool/graphtool.js b/htdocs/js/apps/GraphTool/graphtool.js index 0885a92bf2..4bd6f88b62 100644 --- a/htdocs/js/apps/GraphTool/graphtool.js +++ b/htdocs/js/apps/GraphTool/graphtool.js @@ -452,6 +452,7 @@ window.graphTool = (containerId, options) => { else if (y > boundingBox[1]) y = boundingBox[1] - gt.snapSizeY; point1.setPosition(JXG.COORDS_BY_USER, [x, y]); + gt.board.update(); }; // Prevent paired points from being moved into the same position by a drag. This @@ -664,6 +665,11 @@ window.graphTool = (containerId, options) => { )); this.definingPts.push(center, point); this.focusPoint = center; + + // Redefine the circle's hasPoint method to return true if the center point has the given coordinates, so + // that a pointer over the center point will give focus to the object with the center point activated. + const circleHasPoint = this.baseObj.hasPoint.bind(this.baseObj); + this.baseObj.hasPoint = (x, y) => circleHasPoint(x, y) || center.hasPoint(x, y); } handleKeyEvent(e, el) { @@ -1174,7 +1180,8 @@ window.graphTool = (containerId, options) => { if (e.key !== 'Tab' || (index === 0 && e.shiftKey) || (index === gt.graphedObjs.length - 1 && !e.shiftKey) || - (a.length > 1 && ((pIndex === 0 && !e.shiftKey) || (pIndex === 1 && e.shiftKey))) + (a.length > 1 && + ((pIndex === 0 && !e.shiftKey) || (pIndex === a.length - 1 && e.shiftKey))) ) return; @@ -1194,8 +1201,8 @@ window.graphTool = (containerId, options) => { obj.blur(); }; + point.rendNode.addEventListener('keydown', point.focusOutHandler); } - point.rendNode.addEventListener('keydown', point.focusOutHandler); // Attach a focusin handler to all points to update the coordinates display. point.focusInHandler = (e) => gt.setTextCoords(point.X(), point.Y()); diff --git a/htdocs/js/apps/GraphTool/quadratictool.js b/htdocs/js/apps/GraphTool/quadratictool.js index 8b7530dc50..a487077b6e 100644 --- a/htdocs/js/apps/GraphTool/quadratictool.js +++ b/htdocs/js/apps/GraphTool/quadratictool.js @@ -38,6 +38,7 @@ } el.setPosition(JXG.COORDS_BY_USER, [x, el.Y()]); + gt.board.update(); } }, diff --git a/htdocs/js/apps/Knowls/knowl.js b/htdocs/js/apps/Knowls/knowl.js index bdfcbac8c4..57b8388a40 100644 --- a/htdocs/js/apps/Knowls/knowl.js +++ b/htdocs/js/apps/Knowls/knowl.js @@ -15,6 +15,11 @@ }; const initializeKnowl = (knowl) => { + if (getComputedStyle(knowl)?.display === '') { + setTimeout(() => initializeKnowl(knowl), 100); + return; + } + knowl.dataset.bsToggle = 'collapse'; if (!knowl.knowlContainer) { knowl.knowlContainer = document.createElement('div'); diff --git a/htdocs/js/apps/MathQuill/mqeditor.css b/htdocs/js/apps/MathQuill/mqeditor.css index 57d4889c72..1cfc8b61db 100644 --- a/htdocs/js/apps/MathQuill/mqeditor.css +++ b/htdocs/js/apps/MathQuill/mqeditor.css @@ -15,6 +15,7 @@ span[id^="mq-answer"].incorrect { } span[id^="mq-answer"] { + /*rtl:ignore*/ direction: ltr; padding: 4px 5px 2px 5px; border-radius: 4px !important; @@ -31,6 +32,7 @@ input[type="text"].codeshard.mq-edit { max-height: 95vh; position: fixed; font-size: .75em; + /*rtl:ignore*/ direction: ltr; display: flex; flex-direction: column; diff --git a/htdocs/js/apps/MathQuill/mqeditor.js b/htdocs/js/apps/MathQuill/mqeditor.js index 4793f2654f..1da4b9482e 100644 --- a/htdocs/js/apps/MathQuill/mqeditor.js +++ b/htdocs/js/apps/MathQuill/mqeditor.js @@ -84,8 +84,11 @@ { id: 'text', latex: '\\text', tooltip: 'text mode (")', icon: 'Tt' } ]; + answerQuill.hasFocus = false; + // Open the toolbar when the mathquill answer box gains focus. answerQuill.textarea.addEventListener('focusin', () => { + answerQuill.hasFocus = true; if (answerQuill.toolbar) return; answerQuill.toolbar = document.createElement('div'); @@ -114,6 +117,7 @@ })); button.addEventListener('click', () => { + answerQuill.hasFocus = true; answerQuill.mathField.cmd(button.dataset.latex); answerQuill.textarea.focus(); }) @@ -136,15 +140,15 @@ }); answerQuill.textarea.addEventListener('focusout', (e) => { - if (e.relatedTarget && (e.relatedTarget.closest('.quill-toolbar') || - e.relatedTarget.classList.contains('symbol-button'))) - return; - if (answerQuill.toolbar) { - window.removeEventListener('resize', answerQuill.toolbar.adjustWidth); - answerQuill.toolbar.tooltips.forEach((tooltip) => tooltip.dispose()); - answerQuill.toolbar.remove(); - delete answerQuill.toolbar; - } + answerQuill.hasFocus = false; + setTimeout(function() { + if (!answerQuill.hasFocus && answerQuill.toolbar) { + window.removeEventListener('resize', answerQuill.toolbar.adjustWidth); + answerQuill.toolbar.tooltips.forEach((tooltip) => tooltip.dispose()); + answerQuill.toolbar.remove(); + delete answerQuill.toolbar; + } + }, 200); }); // Trigger an answer preview when the enter key is pressed in an answer box. @@ -161,7 +165,7 @@ document.querySelector('input[name=previewAnswers]')?.click(); // For ww3 const previewButtonId = - answerQuill.textarea.closest('[name=problemMainForm]')[0]?.id + answerQuill.textarea.closest('[name=problemMainForm]')?.id .replace('problemMainForm', 'previewAnswers'); if (previewButtonId) document.getElementById(previewButtonId)?.click(); } diff --git a/htdocs/js/apps/Problem/problem.scss b/htdocs/js/apps/Problem/problem.scss index 465f12e7e9..1073c66f15 100644 --- a/htdocs/js/apps/Problem/problem.scss +++ b/htdocs/js/apps/Problem/problem.scss @@ -1,5 +1,5 @@ /* WeBWorK Online Homework Delivery System - * Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork + * Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork * * This program is free software; you can redistribute it and/or modify it under * the terms of either: (a) the GNU General Public License as published by the @@ -34,7 +34,6 @@ /* Problem elements */ label, input[type=text], select, textarea { - font-size: 16px; font-weight: normal; line-height: 18px; width: auto; @@ -48,10 +47,13 @@ vertical-align: middle; border: 1px solid #ccc; border-radius: 4px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; background-color: white; } + textarea, input[type=text] { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + } + input[type=text] { height: 30px; font-size: 14px; diff --git a/htdocs/js/apps/Scaffold/scaffold.scss b/htdocs/js/apps/Scaffold/scaffold.scss index 1c7d5da450..a62cbb3a5f 100644 --- a/htdocs/js/apps/Scaffold/scaffold.scss +++ b/htdocs/js/apps/Scaffold/scaffold.scss @@ -33,7 +33,7 @@ & > button.accordion-button { background: #eee; - &::focus { + &:focus { box-shadow: none; } diff --git a/lib/AnswerHash.pm b/lib/AnswerHash.pm index 8a029cc8a4..f199b4266f 100755 --- a/lib/AnswerHash.pm +++ b/lib/AnswerHash.pm @@ -3,16 +3,26 @@ ## ## Provides a data structure for answer hashes. Currently just a wrapper ## for the hash, but that might change -#################################################################### -# Copyright @ 1995-2002 WeBWorK Team -# All Rights Reserved -#################################################################### +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the +# Artistic License for more details. +################################################################################ #$Id$ =head1 NAME AnswerHash.pm -- located in the courseScripts directory - + This file contains the packages/classes: AnswerHash and AnswerEvaluator @@ -20,14 +30,14 @@ AnswerHash -- this class stores information related to the student's answer. It is little more than a standard perl hash with - a special name, but it does have some access and + a special name, but it does have some access and manipulation methods. More of these may be added as it becomes necessary. - + Usage: $rh_ans = new AnswerHash; - + AnswerEvaluator -- this class organizes the construction of - answer evaluator subroutines which check the + answer evaluator subroutines which check the student's answer. By plugging filters into the answer evaluator class you can customize the way the student's answer is normalized and checked. Our hope @@ -36,7 +46,7 @@ combinations to obtain different answer evaluators, thus greatly reducing the programming and maintenance required for constructing answer evaluators. - + Usage: $ans_eval = new AnswerEvaluator; =cut @@ -72,16 +82,16 @@ The answer hash class is guaranteed to contain the following instance variables: This is displayed in the section reporting the results of checking the student answers. - $ans_hash->{original_student_ans} -- This is the original student answer. + $ans_hash->{original_student_ans} -- This is the original student answer. This is displayed on the preview page and may be used for sticky answers. - $ans_hash->{ans_message} -- Any error message, or hint provided by + $ans_hash->{ans_message} -- Any error message, or hint provided by the answer evaluator. This is also displayed in the section reporting the results of checking the student answers. - $ans_hash->{type} -- A string indicating the type of answer evaluator. + $ans_hash->{type} -- A string indicating the type of answer evaluator. This helps in preprocessing the student answer for errors. Some examples: 'number_with_units' @@ -97,13 +107,13 @@ The answer hash class is guaranteed to contain the following instance variables: same as $ans_hash{student_ans}. - $ans_hash->{preview_latex_string} -- + $ans_hash->{preview_latex_string} -- THIS IS OPTIONAL. This is latex version of the student answer which is used to show a typeset view on the answer on the preview page. For a student answer of 2/3, this would be \frac{2}{3}. 'ans_message' => '', # null string - + 'preview_text_string' => undef, 'preview_latex_string' => undef, 'error_flag' => undef, @@ -116,7 +126,7 @@ The answer hash class is guaranteed to contain the following instance variables: BEGIN { # main::be_strict(); # an alias for use strict. This means that all global variable must contain main:: as a prefix. - + } package AnswerHash; @@ -140,14 +150,14 @@ my %fields = ( 'score' => undef, =head4 new Useage $rh_anshash = new AnswerHash; - + returns an object of type AnswerHash. - + =cut sub new { my $class = shift @_; - + my $self = { 'score' => 0, 'correct_ans' => 'No correct answer specified', 'student_ans' => undef, @@ -161,10 +171,10 @@ sub new { 'error_message' => '', }; # return a reference to a hash. - + bless $self, $class; $self -> setKeys(@_); - + return $self; } @@ -172,26 +182,26 @@ sub new { ## Checks to make sure that the keys are valid, ## then sets their value -=head4 setKeys - - $rh_ans->setKeys(score=>1, student_answer => "yes"); +=head4 setKeys + + $rh_ans->setKeys(score=>1, student_answer => "yes"); Sets standard elements in the AnswerHash (the ones defined above). Will give error if one attempts to set non-standard keys. - + To set a non-standard element in a hash use - + $rh_ans->{non-standard-key} = newValue; - + There are no safety checks when using this method. =cut - + sub setKeys { my $self = shift; my %inits = @_; foreach my $item (keys %inits) { - if ( exists $fields{$item} ) { + if ( exists $fields{$item} ) { $self -> {$item} = $inits{$item}; } else { @@ -206,14 +216,14 @@ sub setKeys { Usage: $rh_ans->data('foo'); set $rh_ans->{student_ans} = 'foo'; $student_input = $rh_ans->data(); retrieve value of $rh_ans->{student_ans} - + synonym for input -=head4 input +=head4 input Usage: $rh_ans->input('foo') sets $rh_ans->{student_ans} = 'foo'; $student_input = $rh_ans->input(); - + synonym for data =cut @@ -230,16 +240,16 @@ sub input { #$rh_ans->input('foo') is a synonym for $rh_ans->{student_ans}=' $self->{student_ans} } -=head4 input +=head4 input - Usage: $rh_ans->score(1) + Usage: $rh_ans->score(1) $score = $rh_ans->score(); - + Retrieve or set $rh_ans->{score}, the student's score on the problem. =cut -sub score { +sub score { my $self = shift; my $score = shift; $self->{score} = $score if defined($score); @@ -276,20 +286,20 @@ sub stringify_hash { =head4 throw_error Usage: $rh_ans->throw_error("FLAG", "message"); - - FLAG is a distinctive word that describes the type of error. + + FLAG is a distinctive word that describes the type of error. Examples are EVAL for an evaluation error or "SYNTAX" for a syntax error. The entry $rh_ans->{error_flag} is set to "FLAG". - + The catch_error and clear_error methods use this entry. - + message is a descriptive message for the end user, defining what error occured. =head4 catch_error Usage: $rh_ans->catch_error("FLAG2"); - + Returns true (1) if $rh_ans->{error_flag} equals "FLAG2", otherwise it returns false (empty string). @@ -298,8 +308,8 @@ sub stringify_hash { =head4 clear_error Usage: $rh_ans->clear_error("FLAG2"); - - If $rh_ans->{error_flag} equals "FLAG2" then the {error_flag} entry is set to + + If $rh_ans->{error_flag} equals "FLAG2" then the {error_flag} entry is set to the empty string as is the entry {error_message} =head4 error_flag @@ -307,11 +317,11 @@ sub stringify_hash { =head4 error_message Usage: $flag = $rh_ans -> error_flag(); - + $message = $rh_ans -> error_message(); - Retrieve or set the {error_flag} and {error_message} entries. - + Retrieve or set the {error_flag} and {error_message} entries. + Use catch_error and throw_error where possible. =cut @@ -359,15 +369,15 @@ sub error_message { # error print out method # =head4 pretty_print -# -# +# +# # Usage: $rh_ans -> pretty_print(); -# -# +# +# # Returns a string containing a representation of the AnswerHash as an HTML table. -# +# # =cut -# +# # sub pretty_print { # my $r_input = shift; # my $level = shift; @@ -391,7 +401,7 @@ sub error_message { # while (@array) { # $out .= pretty_print(shift @array, $level) . " , "; # } -# $out .= " )"; +# $out .= " )"; # } elsif (ref($r_input) eq 'CODE') { # $out = "$r_input"; # } else { @@ -401,14 +411,14 @@ sub error_message { # $out; # } -# action methods +# action methods =head4 OR Usage: $rh_ans->OR($rh_ans2); - + Returns a new AnswerHash whose score is the maximum of the scores in $rh_ans and $rh_ans2. - The correct answers for the two hashes are combined with "OR". + The correct answers for the two hashes are combined with "OR". The types are concatenated with "OR" as well. Currently nothing is done with the error flags and messages. @@ -418,9 +428,9 @@ sub error_message { Usage: $rh_ans->AND($rh_ans2); - + Returns a new AnswerHash whose score is the minimum of the scores in $rh_ans and $rh_ans2. - The correct answers for the two hashes are combined with "AND". + The correct answers for the two hashes are combined with "AND". The types are concatenated with "AND" as well. Currently nothing is done with the error flags and messages. @@ -433,11 +443,11 @@ sub error_message { sub OR { my $self = shift; - + my $rh_ans2 = shift; my %options = @_; return($self) unless defined($rh_ans2) and ref($rh_ans2) eq 'AnswerHash'; - + my $out_hash = new AnswerHash; # score is the maximum of the two scores $out_hash->{score} = ( $self->{score} < $rh_ans2->{score} ) ? $rh_ans2->{score} :$self->{score}; @@ -498,17 +508,17 @@ use PGUtil qw(not_null pretty_print); sub new { my $class = shift @_; - + my $self = { pre_filters => [ [\&blank_prefilter] ], evaluators => [], post_filters => [ [\&blank_postfilter] ], debug => 0, rh_ans => new AnswerHash, - + }; - + bless $self, $class; - $self->rh_ans(@_); #initialize answer hash + $self->rh_ans(@_); #initialize answer hash return $self; } sub clone { @@ -527,15 +537,15 @@ sub dereference_array_ans { } $rh_ans; } - + sub get_student_answer { my $self = shift; - my $input = shift; + my $input = shift; my %answer_options = @_; my $display_input = $input; $display_input =~ s/\0/\\0/g; # make null spacings visible eval (q!main::DEBUG_MESSAGE( "Raw student answer is |$display_input|")!) if $self->{debug}; - $input = '' unless defined($input); + $input = '' unless defined($input); if (ref($input) =~/AnswerHash/) { # in this case nothing needs to be done, since the student's answer is already in an answerhash. # This is useful when an AnswerEvaluator is used as a filter in another answer evaluator. @@ -544,17 +554,17 @@ sub get_student_answer { $self-> {rh_ans} -> {original_student_ans} = " ( " .join(", ",@input) . " ) "; $input = \@input; $self-> {rh_ans} -> {student_ans} = $input; - } elsif (ref($input) eq 'ARRAY' ) { # sometimes the answer may already be decoded into an array. + } elsif (ref($input) eq 'ARRAY' ) { # sometimes the answer may already be decoded into an array. my @input = @$input; $self-> {rh_ans} -> {original_student_ans} = " ( " .join(", ",@input) . " ) "; $input = \@input; $self-> {rh_ans} -> {student_ans} = $input; } else { - + $self-> {rh_ans} -> {original_student_ans} = $input; $self-> {rh_ans} -> {student_ans} = $input; } - $self->{rh_ans}->{ans_label} = $answer_options{ans_label} if defined($answer_options{ans_label}); + $self->{rh_ans}->{ans_label} = $answer_options{ans_label} if defined($answer_options{ans_label}); $self->{rh_ans}->{_filter_name} = 'get_student_answer'; $input; } @@ -572,12 +582,12 @@ sub evaluate { $self->get_student_answer(@_); # dereference $self->{rh_ans}; my $rh_ans = $self ->{rh_ans}; - $rh_ans->{error_flag}=undef; #reset the error flags in case + $rh_ans->{error_flag}=undef; #reset the error flags in case $rh_ans->{done}=undef; #the answer evaluator is called twice - + eval (q!main::DEBUG_MESSAGE( "
\n";
-
-
+
+
while ( ($varName, $globValue) = each %stash) {
emit "$varName\n";
-
+
*alias = $globValue;
next if $varName=~/main/;
-
+
#if (defined($alias) ) { # get rid of defined since this is deprecated
if ($alias ) {
emit " \$$varName $alias \n";
}
-
+
if ( @alias) {
emit " \@$varName @alias \n";
}
diff --git a/macros/LinearProgramming.pl b/macros/LinearProgramming.pl
index ea7c8f7070..d1edca911a 100644
--- a/macros/LinearProgramming.pl
+++ b/macros/LinearProgramming.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/MathObjects.pl b/macros/MathObjects.pl
index b1a17b7fe4..6c8392ecdf 100644
--- a/macros/MathObjects.pl
+++ b/macros/MathObjects.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PG.pl b/macros/PG.pl
index 4228bb852f..c3d5f33a8d 100644
--- a/macros/PG.pl
+++ b/macros/PG.pl
@@ -475,8 +475,11 @@ sub ENDDOCUMENT {
my $data_mq_opts = scalar(keys %$mq_part_opts)
? qq!data-mq-opts="@{[encode_pg_and_html(JSON->new->encode($mq_part_opts))]}"!
: "";
- TEXT(MODES(TeX => "",
- HTML => qq!!));
+ TEXT(MODES(
+ TeX => "",
+ PTX => "",
+ HTML => qq!!
+ ));
}
}
}
@@ -913,8 +916,7 @@ sub includePGproblem {
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/PG.pl,v 1.46 2010/05/27 02:22:51 gage Exp $
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/PGanswermacros.pl b/macros/PGanswermacros.pl
index cf3e3ef005..f2d23cbf95 100644
--- a/macros/PGanswermacros.pl
+++ b/macros/PGanswermacros.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/PGanswermacros.pl,v 1.72 2010/02/01 01:33:05 apizer Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGauxiliaryFunctions.pl b/macros/PGauxiliaryFunctions.pl
index b813e81524..53042ec4a6 100644
--- a/macros/PGauxiliaryFunctions.pl
+++ b/macros/PGauxiliaryFunctions.pl
@@ -1,12 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, http://openwebwork.sf.net/
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGbasicmacros.pl b/macros/PGbasicmacros.pl
index 94ccfe0c81..14599bf583 100644
--- a/macros/PGbasicmacros.pl
+++ b/macros/PGbasicmacros.pl
@@ -1,6 +1,6 @@
################################################################################
-# WeBWorK Program Generation Language
-# Copyright © 2000-2020 The WeBWorK Project, http://openwebwork.sf.net/
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/PGchoicemacros.pl b/macros/PGchoicemacros.pl
index 997d9837f6..1ed09e5550 100644
--- a/macros/PGchoicemacros.pl
+++ b/macros/PGchoicemacros.pl
@@ -1,7 +1,6 @@
################################################################################
-# WeBWorK Program Generation Language
-# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/PGcomplexmacros.pl b/macros/PGcomplexmacros.pl
index 7ad47e505e..16e89ca03e 100644
--- a/macros/PGcomplexmacros.pl
+++ b/macros/PGcomplexmacros.pl
@@ -1,13 +1,20 @@
# This file is PGcomplexmacros.pl
# This includes the subroutines for the ANS macros, that
# is, macros allowing a more flexible answer checking
-####################################################################
-# Copyright @ 1995-2002 The WeBWorK Team
-# All Rights Reserved
-####################################################################
-#$Id$
-
-
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
=head1 NAME
diff --git a/macros/PGcomplexmacros2.pl b/macros/PGcomplexmacros2.pl
index e307d004e5..d42cf9d242 100644
--- a/macros/PGcomplexmacros2.pl
+++ b/macros/PGcomplexmacros2.pl
@@ -2,10 +2,20 @@
# This file is PGcomplexmacros2.pl
# This includes the subroutines for the ANS macros, that
# is, macros allowing a more flexible answer checking
-####################################################################
-# Copyright @ 2006-2007 The WeBWorK Team
-# All Rights Reserved
-####################################################################
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
=head1 NAME
diff --git a/macros/PGessaymacros.pl b/macros/PGessaymacros.pl
index 164409ab63..6abfab559c 100644
--- a/macros/PGessaymacros.pl
+++ b/macros/PGessaymacros.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/PGanswermacros.pl,v 1.72 2010/02/01 01:33:05 apizer Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGfunctionevaluators.pl b/macros/PGfunctionevaluators.pl
index ae91f4faca..bfcecdf704 100644
--- a/macros/PGfunctionevaluators.pl
+++ b/macros/PGfunctionevaluators.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGinfo.pl b/macros/PGinfo.pl
index ad0a0e0291..d72c19be67 100644
--- a/macros/PGinfo.pl
+++ b/macros/PGinfo.pl
@@ -1,8 +1,18 @@
-####################################################################
-# Copyright @ 1995-2007 University of Rochester
-# All Rights Reserved
-####################################################################
+################################################################################
+# WeBWorK Online Homework Delivery System
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of either: (a) the GNU General Public License as published by the
+# Free Software Foundation; either version 2, or (at your option) any later
+# version, or (b) the "Artistic License" which comes with this package.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
+# Artistic License for more details.
+################################################################################
=head1 NAME
@@ -110,4 +120,4 @@ sub pp {
my $hash = shift;
"printing |". ref($hash)."|$BR". pretty_print($hash);
}
-1;
\ No newline at end of file
+1;
diff --git a/macros/PGlateximage.pl b/macros/PGlateximage.pl
index 59df97a77e..660b9c1082 100644
--- a/macros/PGlateximage.pl
+++ b/macros/PGlateximage.pl
@@ -1,12 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGmiscevaluators.pl b/macros/PGmiscevaluators.pl
index ef9c8e0a9d..d23c140b19 100644
--- a/macros/PGmiscevaluators.pl
+++ b/macros/PGmiscevaluators.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGnumericevaluators.pl b/macros/PGnumericevaluators.pl
index 684806eab7..12caad6bbb 100644
--- a/macros/PGnumericevaluators.pl
+++ b/macros/PGnumericevaluators.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGstringevaluators.pl b/macros/PGstringevaluators.pl
index af4ebb1b3f..34f690cb06 100644
--- a/macros/PGstringevaluators.pl
+++ b/macros/PGstringevaluators.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGtextevaluators.pl b/macros/PGtextevaluators.pl
index c3ac67728c..91648dfd77 100644
--- a/macros/PGtextevaluators.pl
+++ b/macros/PGtextevaluators.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/PGtikz.pl b/macros/PGtikz.pl
index 851fd87f77..a6583a638d 100644
--- a/macros/PGtikz.pl
+++ b/macros/PGtikz.pl
@@ -1,12 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/Parser.pl b/macros/Parser.pl
index 6c94f98e54..e2b60d6b11 100644
--- a/macros/Parser.pl
+++ b/macros/Parser.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/answerComposition.pl b/macros/answerComposition.pl
index 84930396da..f63fbcb1fc 100644
--- a/macros/answerComposition.pl
+++ b/macros/answerComposition.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/answerComposition.pl,v 1.8 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/answerCustom.pl b/macros/answerCustom.pl
index 2b166f9ef9..46ebaee69d 100644
--- a/macros/answerCustom.pl
+++ b/macros/answerCustom.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/answerHints.pl b/macros/answerHints.pl
index ad7faaf820..9ad34f7346 100644
--- a/macros/answerHints.pl
+++ b/macros/answerHints.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
@@ -114,59 +113,81 @@ =head1 AnswerHints()
=cut
-sub _answerHints_init {}
+sub _answerHints_init { }
sub AnswerHints {
- return (sub {
- my $ans = shift; $ans->{_filter_name} = "Answer Hints Post Filter";
- my $correct = $ans->{correct_value};
- my $student = $ans->{student_value};
- Value::Error("AnswerHints can only be used with MathObjects answer checkers") unless ref($correct);
- return $ans unless ref($student);
- my $context = $correct->context;
- my $hash = $context->{answerHash};
- $context->{answerHash} = $ans;
- my $processPreview = $correct->getFlag('answerHintsProcessPreview', 0);
- $context->{answerHash} = $hash;
-
- while (@_) {
- my $wrongList = shift; my $message = shift; my @options;
- ($message,@options) = @{$message} if ref($message) eq 'ARRAY';
- my %options = (
- checkCorrect => 0,
- replaceMessage => 0,
- checkTypes => 1,
- processPreview => $processPreview,
- score => undef,
- cmp_options => [],
- @options,
- );
- next if $options{checkTypes} && $correct->type ne $student->type;
- next if !$options{processPreview} && $ans->{isPreview};
- $wrongList = [$wrongList] unless ref($wrongList) eq 'ARRAY';
- foreach my $wrong (@{$wrongList}) {
- if (ref($wrong) eq 'CODE') {
- if (($ans->{score} < 1 || $options{checkCorrect}) &&
- ($ans->{ans_message} eq "" || $options{replaceMessage}) &&
- &$wrong($correct,$student,$ans)) {
- $ans->{ans_message} = $ans->{error_message} = $message;
- $ans->{score} = $options{score} if defined $options{score};
- last;
- }
- } else {
- $wrong = Value::makeValue($wrong);
- if (($ans->{score} < 1 || $options{checkCorrect} || AnswerHints::Compare($correct,$wrong,$ans)) &&
- ($ans->{ans_message} eq "" || $options{replaceMessage}) &&
- AnswerHints::Compare($wrong,$student,$ans,@{$options{cmp_options}})) {
- $ans->{ans_message} = $ans->{error_message} = $message;
- $ans->{score} = $options{score} if defined $options{score};
- last;
- }
- }
- }
- }
- return $ans;
- },@_);
+ return (
+ sub {
+ my $ans = shift;
+ $ans->{_filter_name} = "Answer Hints Post Filter";
+ my $correct = $ans->{correct_value};
+ my $student = $ans->{student_value};
+ Value::Error("AnswerHints can only be used with MathObjects answer checkers") unless ref($correct);
+ return $ans unless ref($student);
+ my $context = $correct->context;
+ my $hash = $context->{answerHash};
+ $context->{answerHash} = $ans;
+ my $processPreview = $correct->getFlag('answerHintsProcessPreview', 0);
+ $context->{answerHash} = $hash;
+
+ while (@_) {
+ my $wrongList = shift;
+ my $message = shift;
+ my @options;
+ ($message, @options) = @{$message} if ref($message) eq 'ARRAY';
+ my %options = (
+ checkCorrect => 0,
+ replaceMessage => 0,
+ checkTypes => 1,
+ processPreview => $processPreview,
+ score => undef,
+ cmp_options => [],
+ @options,
+ );
+ next if $options{checkTypes} && $correct->type ne $student->type;
+ next if !$options{processPreview} && $ans->{isPreview};
+ $wrongList = [$wrongList] unless ref($wrongList) eq 'ARRAY';
+
+ foreach my $wrong (@{$wrongList}) {
+ if (ref($wrong) eq 'CODE') {
+ if ( ($ans->{score} < 1 || $options{checkCorrect})
+ && ($ans->{ans_message} eq "" || $options{replaceMessage}))
+ {
+ # Make the call to run the function inside an eval to trap errors
+ my $myResult = 0;
+ eval { $myResult = &$wrong($correct, $student, $ans); 1; } or do {
+ warn "An error occurred in this problem.";
+ last;
+ };
+ if ($myResult) {
+ $ans->{ans_message} = $ans->{error_message} = $message;
+ $ans->{score} = $options{score} if defined $options{score};
+ last;
+ }
+ }
+ } else {
+ $wrong = Value::makeValue($wrong);
+ if (
+ (
+ $ans->{score} < 1
+ || $options{checkCorrect}
+ || AnswerHints::Compare($correct, $wrong, $ans)
+ )
+ && ($ans->{ans_message} eq "" || $options{replaceMessage})
+ && AnswerHints::Compare($wrong, $student, $ans, @{ $options{cmp_options} })
+ )
+ {
+ $ans->{ans_message} = $ans->{error_message} = $message;
+ $ans->{score} = $options{score} if defined $options{score};
+ last;
+ }
+ }
+ }
+ }
+ return $ans;
+ },
+ @_
+ );
}
package AnswerHints;
@@ -176,23 +197,27 @@ package AnswerHints;
# and returns true if the two values match and false otherwise.
#
sub Compare {
- my $self = shift; my $other = shift; my $ans = shift;
- $ans = bless {%{$ans},@_}, ref($ans); # make a copy
- $ans->{typeError} = 0; $ans->{ans_message} = $ans->{error_message} = ""; $ans->{score} = 0;
- if (sprintf("%p",$self) ne sprintf("%p",$ans->{correct_value})) {
- $ans->{correct_ans} = $self->string;
- $ans->{correct_value} = $self;
- $ans->{correct_formula} = Value->Package("Formula")->new($self);
- }
- if (sprintf("%p",$other) ne sprintf("%p",$ans->{student_value})) {
- $ans->{student_ans} = $other->string;
- $ans->{student_value} = $other;
- $ans->{student_formula} = Value->Package("Formula")->new($other);
- }
- $self->cmp_preprocess($ans);
- $self->cmp_equal($ans);
- $self->cmp_postprocess($ans) if !$ans->{error_message} && !$ans->{typeError};
- return $ans->{score} >= 1;
+ my $self = shift;
+ my $other = shift;
+ my $ans = shift;
+ $ans = bless { %{$ans}, @_ }, ref($ans); # make a copy
+ $ans->{typeError} = 0;
+ $ans->{ans_message} = $ans->{error_message} = "";
+ $ans->{score} = 0;
+ if (sprintf("%p", $self) ne sprintf("%p", $ans->{correct_value})) {
+ $ans->{correct_ans} = $self->string;
+ $ans->{correct_value} = $self;
+ $ans->{correct_formula} = Value->Package("Formula")->new($self);
+ }
+ if (sprintf("%p", $other) ne sprintf("%p", $ans->{student_value})) {
+ $ans->{student_ans} = $other->string;
+ $ans->{student_value} = $other;
+ $ans->{student_formula} = Value->Package("Formula")->new($other);
+ }
+ $self->cmp_preprocess($ans);
+ $self->cmp_equal($ans);
+ $self->cmp_postprocess($ans) if !$ans->{error_message} && !$ans->{typeError};
+ return $ans->{score} >= 1;
}
1;
diff --git a/macros/answerVariableList.pl b/macros/answerVariableList.pl
index 8b0c7a1a5e..32b3ea58b9 100644
--- a/macros/answerVariableList.pl
+++ b/macros/answerVariableList.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/compoundProblem5.pl b/macros/compoundProblem5.pl
index cf814cef4e..2523e16dbd 100644
--- a/macros/compoundProblem5.pl
+++ b/macros/compoundProblem5.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2014 The WeBWorK Project, http://openwebwork.sf.net/
-# $$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextABCD.pl b/macros/contextABCD.pl
index 774daa5be8..23e34303d8 100644
--- a/macros/contextABCD.pl
+++ b/macros/contextABCD.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextAlternateDecimal.pl b/macros/contextAlternateDecimal.pl
index c5b9b07d37..cfa4eb213e 100644
--- a/macros/contextAlternateDecimal.pl
+++ b/macros/contextAlternateDecimal.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2014 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextAlternateIntervals.pl b/macros/contextAlternateIntervals.pl
index 8500cd2766..fca902678f 100644
--- a/macros/contextAlternateIntervals.pl
+++ b/macros/contextAlternateIntervals.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2014 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextComplexExtras.pl b/macros/contextComplexExtras.pl
index dc5a468c31..c2441deb5b 100644
--- a/macros/contextComplexExtras.pl
+++ b/macros/contextComplexExtras.pl
@@ -1,7 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2012 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextComplexExtras.pl,v 1.0 2012/08/01 11:33:50 dpvc Exp $
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/contextComplexJ.pl b/macros/contextComplexJ.pl
index c386ccd39e..80bcdfcf0a 100644
--- a/macros/contextComplexJ.pl
+++ b/macros/contextComplexJ.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2014 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextCongruence.pl b/macros/contextCongruence.pl
index 8e1b9937e2..f7cea379cb 100644
--- a/macros/contextCongruence.pl
+++ b/macros/contextCongruence.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2017 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextCurrency.pl b/macros/contextCurrency.pl
index 0b8111efb8..68bac416a8 100644
--- a/macros/contextCurrency.pl
+++ b/macros/contextCurrency.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextCurrency.pl,v 1.17 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextFraction.pl b/macros/contextFraction.pl
index 2a5afdc1de..b85cfcbed3 100644
--- a/macros/contextFraction.pl
+++ b/macros/contextFraction.pl
@@ -907,11 +907,16 @@ sub string {
sub TeX {
my $self = shift; my $equation = shift; shift; shift; my $prec = shift;
my ($a,$b) = @{$self->{data}}; my $n = "";
+ my $textstyle = '';
return "$a" if $b == 1;
- if ($self->getFlagWithAlias("showMixedNumbers","showProperFractions") && CORE::abs($a) > $b)
- {$n = int($a/$b); $a = CORE::abs($a) % $b; $n .= " " unless $a == 0}
+ if ($self->getFlagWithAlias("showMixedNumbers","showProperFractions") && CORE::abs($a) > $b) {
+ $n = int($a/$b);
+ $a = CORE::abs($a) % $b;
+ $n .= ' ' unless $a == 0;
+ $textstyle = '\\textstyle';
+ }
my $s = ""; ($a,$s) = (-$a,"-") if $a < 0;
- $n .= ($self->{isHorizontal} ? "$s$a/$b" : "${s}{\\textstyle\\frac{$a}{$b}}")
+ $n .= ($self->{isHorizontal} ? "$s$a/$b" : "${s}{$textstyle\\frac{$a}{$b}}")
unless $a == 0 && $n ne '';
return "$n";
}
diff --git a/macros/contextInequalities.pl b/macros/contextInequalities.pl
index 6e209ce3f9..70e5a72efe 100644
--- a/macros/contextInequalities.pl
+++ b/macros/contextInequalities.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextInequalities.pl,v 1.23 2010/03/22 11:01:55 dpvc Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextInequalitySetBuilder.pl b/macros/contextInequalitySetBuilder.pl
index f3bc52fdba..c68e625e6d 100644
--- a/macros/contextInequalitySetBuilder.pl
+++ b/macros/contextInequalitySetBuilder.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2013 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextInteger.pl b/macros/contextInteger.pl
index 15878aaef8..47c943e9f9 100644
--- a/macros/contextInteger.pl
+++ b/macros/contextInteger.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2017 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextIntegerFunctions.pl b/macros/contextIntegerFunctions.pl
index 49c6ae9808..38488c5ca3 100644
--- a/macros/contextIntegerFunctions.pl
+++ b/macros/contextIntegerFunctions.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextLimitedComplex.pl b/macros/contextLimitedComplex.pl
index f58cb1bf47..39214b480a 100644
--- a/macros/contextLimitedComplex.pl
+++ b/macros/contextLimitedComplex.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextLimitedNumeric.pl b/macros/contextLimitedNumeric.pl
index adce6bdef0..c678fbbdc8 100644
--- a/macros/contextLimitedNumeric.pl
+++ b/macros/contextLimitedNumeric.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextLimitedPoint.pl b/macros/contextLimitedPoint.pl
index 72d93d8083..2d1ba73828 100644
--- a/macros/contextLimitedPoint.pl
+++ b/macros/contextLimitedPoint.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextLimitedPoint.pl,v 1.14 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextLimitedPolynomial.pl b/macros/contextLimitedPolynomial.pl
index 017dcc0bea..e4f8c94665 100644
--- a/macros/contextLimitedPolynomial.pl
+++ b/macros/contextLimitedPolynomial.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextLimitedPowers.pl b/macros/contextLimitedPowers.pl
index adb18de1b2..0a9de2deb2 100644
--- a/macros/contextLimitedPowers.pl
+++ b/macros/contextLimitedPowers.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextLimitedVector.pl b/macros/contextLimitedVector.pl
index abe12ed044..1c5314b4bb 100644
--- a/macros/contextLimitedVector.pl
+++ b/macros/contextLimitedVector.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextMatrixExtras.pl b/macros/contextMatrixExtras.pl
index d42e2483b3..d3e835c000 100644
--- a/macros/contextMatrixExtras.pl
+++ b/macros/contextMatrixExtras.pl
@@ -1,7 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2012 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextMatrixExtras.pl,v 1.0 2012/08/01 11:21:45 dpvc Exp $
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/contextPartition.pl b/macros/contextPartition.pl
index 2e0e4cbcae..d5d74c7b6a 100644
--- a/macros/contextPartition.pl
+++ b/macros/contextPartition.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2014 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextPercent.pl b/macros/contextPercent.pl
index fb7c788380..f4dd56bb2f 100644
--- a/macros/contextPercent.pl
+++ b/macros/contextPercent.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2013 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextCurrency.pl,v 1.17 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextPeriodic.pl b/macros/contextPeriodic.pl
index b8186c881b..0a3706326e 100644
--- a/macros/contextPeriodic.pl
+++ b/macros/contextPeriodic.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextPermutation.pl b/macros/contextPermutation.pl
index 4db42ab4c3..60fb1f61a0 100644
--- a/macros/contextPermutation.pl
+++ b/macros/contextPermutation.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2014 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextPermutationUBC.pl b/macros/contextPermutationUBC.pl
index b5972b7732..8b9215d176 100644
--- a/macros/contextPermutationUBC.pl
+++ b/macros/contextPermutationUBC.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2014 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader:$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextPiecewiseFunction.pl b/macros/contextPiecewiseFunction.pl
index 724574caa1..1bf49cfda8 100644
--- a/macros/contextPiecewiseFunction.pl
+++ b/macros/contextPiecewiseFunction.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextPolynomialFactors.pl b/macros/contextPolynomialFactors.pl
index 95686be6eb..40d342b844 100644
--- a/macros/contextPolynomialFactors.pl
+++ b/macros/contextPolynomialFactors.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2010 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextPolynomialFactors.pl,v 1.2 2010/03/31 21:45:42 dpvc Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextRationalFunction.pl b/macros/contextRationalFunction.pl
index 46f07c24ee..84f6f63d46 100644
--- a/macros/contextRationalFunction.pl
+++ b/macros/contextRationalFunction.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2010 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/contextRationalFunction.pl,v 1.1 2010/03/31 21:01:14 dpvc Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextScientificNotation.pl b/macros/contextScientificNotation.pl
index c098724e6c..dfe52e114e 100644
--- a/macros/contextScientificNotation.pl
+++ b/macros/contextScientificNotation.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextString.pl b/macros/contextString.pl
index 6a323c3b5b..1e418d12bd 100644
--- a/macros/contextString.pl
+++ b/macros/contextString.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextTF.pl b/macros/contextTF.pl
index 22fe43c053..29933658f5 100644
--- a/macros/contextTF.pl
+++ b/macros/contextTF.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/contextTrigDegrees.pl b/macros/contextTrigDegrees.pl
index a20aa7e7d9..3e8754859a 100644
--- a/macros/contextTrigDegrees.pl
+++ b/macros/contextTrigDegrees.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/draggableProof.pl b/macros/draggableProof.pl
index a9cdde8fdd..aba3b584c7 100644
--- a/macros/draggableProof.pl
+++ b/macros/draggableProof.pl
@@ -1,6 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/draggableSubsets.pl b/macros/draggableSubsets.pl
index d72b636987..4a82bcc69e 100644
--- a/macros/draggableSubsets.pl
+++ b/macros/draggableSubsets.pl
@@ -1,6 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/extraAnswerEvaluators.pl b/macros/extraAnswerEvaluators.pl
index 3c0c257d18..36a9329c5f 100644
--- a/macros/extraAnswerEvaluators.pl
+++ b/macros/extraAnswerEvaluators.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserAssignment.pl b/macros/parserAssignment.pl
index 763f09e840..a0ef1089d2 100644
--- a/macros/parserAssignment.pl
+++ b/macros/parserAssignment.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserAutoStrings.pl b/macros/parserAutoStrings.pl
index e260701d73..b5bd26b4e7 100644
--- a/macros/parserAutoStrings.pl
+++ b/macros/parserAutoStrings.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserCustomization.pl b/macros/parserCustomization.pl
index e57172b3f3..f28e64528a 100644
--- a/macros/parserCustomization.pl
+++ b/macros/parserCustomization.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserDifferenceQuotient.pl b/macros/parserDifferenceQuotient.pl
index 048c5f1371..7f0d4b4c48 100644
--- a/macros/parserDifferenceQuotient.pl
+++ b/macros/parserDifferenceQuotient.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserFormulaAnyVar.pl b/macros/parserFormulaAnyVar.pl
index 049661be60..2ae71ded43 100644
--- a/macros/parserFormulaAnyVar.pl
+++ b/macros/parserFormulaAnyVar.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2012 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserFormulaUpToConstant.pl b/macros/parserFormulaUpToConstant.pl
index f54e138c67..30db29374e 100644
--- a/macros/parserFormulaUpToConstant.pl
+++ b/macros/parserFormulaUpToConstant.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserFormulaUpToConstant.pl,v 1.23 2010/02/08 13:56:09 dpvc Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserFormulaWithUnits.pl b/macros/parserFormulaWithUnits.pl
index 556648c743..4eeba49335 100644
--- a/macros/parserFormulaWithUnits.pl
+++ b/macros/parserFormulaWithUnits.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserFunction.pl b/macros/parserFunction.pl
index b407c11d4d..13458982a1 100644
--- a/macros/parserFunction.pl
+++ b/macros/parserFunction.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserFunctionPrime.pl b/macros/parserFunctionPrime.pl
index 2382d183d6..a02183a4dc 100644
--- a/macros/parserFunctionPrime.pl
+++ b/macros/parserFunctionPrime.pl
@@ -1,7 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2012 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/parserGraphTool.pl b/macros/parserGraphTool.pl
index 4ffeda96f1..14d879eb66 100644
--- a/macros/parserGraphTool.pl
+++ b/macros/parserGraphTool.pl
@@ -1,6 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2020 The WeBWorK Project, http://openwebwork.sf.net/
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/parserImplicitEquation.pl b/macros/parserImplicitEquation.pl
index 269d8edad1..7e66a5c70a 100644
--- a/macros/parserImplicitEquation.pl
+++ b/macros/parserImplicitEquation.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserImplicitEquation.pl,v 1.14 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserImplicitPlane.pl b/macros/parserImplicitPlane.pl
index 50a8111c5f..f482f23b76 100644
--- a/macros/parserImplicitPlane.pl
+++ b/macros/parserImplicitPlane.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserLinearInequality.pl b/macros/parserLinearInequality.pl
index 943edcf6dd..6bcd34b3e4 100644
--- a/macros/parserLinearInequality.pl
+++ b/macros/parserLinearInequality.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserMultiAnswer.pl b/macros/parserMultiAnswer.pl
index 7d8670177a..b37a547ae0 100644
--- a/macros/parserMultiAnswer.pl
+++ b/macros/parserMultiAnswer.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserMultiAnswer.pl,v 1.11 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserMultiPart.pl b/macros/parserMultiPart.pl
index f5a1937e12..77b2196a76 100644
--- a/macros/parserMultiPart.pl
+++ b/macros/parserMultiPart.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl
index 3d58bae5b1..6c8169bbea 100644
--- a/macros/parserNumberWithUnits.pl
+++ b/macros/parserNumberWithUnits.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserOneOf.pl b/macros/parserOneOf.pl
index 424d496d09..5033cce410 100644
--- a/macros/parserOneOf.pl
+++ b/macros/parserOneOf.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2012 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserParametricLine.pl b/macros/parserParametricLine.pl
index 9cda736bc5..b903df6242 100644
--- a/macros/parserParametricLine.pl
+++ b/macros/parserParametricLine.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserParametricLine.pl,v 1.17 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserParametricPlane.pl b/macros/parserParametricPlane.pl
index 8c64772d31..ec6447221e 100644
--- a/macros/parserParametricPlane.pl
+++ b/macros/parserParametricPlane.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2013 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserParametricLine.pl,v 1.17 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserPopUp.pl b/macros/parserPopUp.pl
index f8781a6861..25aa271d9a 100644
--- a/macros/parserPopUp.pl
+++ b/macros/parserPopUp.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserPopUp.pl,v 1.10 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserPrime.pl b/macros/parserPrime.pl
index 089debb573..81deebfe05 100644
--- a/macros/parserPrime.pl
+++ b/macros/parserPrime.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2009 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserPrime.pl,v 1.2 2009/10/03 15:58:49 dpvc Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserRadioButtons.pl b/macros/parserRadioButtons.pl
index 751eb618b6..f3a9e90dad 100644
--- a/macros/parserRadioButtons.pl
+++ b/macros/parserRadioButtons.pl
@@ -1,7 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/macros/parserRoot.pl b/macros/parserRoot.pl
index 122beb9f01..30a7563e41 100644
--- a/macros/parserRoot.pl
+++ b/macros/parserRoot.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2013 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserSolutionFor.pl b/macros/parserSolutionFor.pl
index 894058f09e..6cf8778831 100644
--- a/macros/parserSolutionFor.pl
+++ b/macros/parserSolutionFor.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserVectorUtils.pl b/macros/parserVectorUtils.pl
index f43061561f..c554c81518 100644
--- a/macros/parserVectorUtils.pl
+++ b/macros/parserVectorUtils.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/parserWordCompletion.pl b/macros/parserWordCompletion.pl
index d55ce6dbc0..5c556cbbc7 100644
--- a/macros/parserWordCompletion.pl
+++ b/macros/parserWordCompletion.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright � 2000-2015 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/parserWordCompletion.pl,v 1.0 2015/11/25 23:28:44 paultpearson Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/problemPanic.pl b/macros/problemPanic.pl
index 4a398a63ac..fe11f3544a 100644
--- a/macros/problemPanic.pl
+++ b/macros/problemPanic.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2009 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/problemPanic.pl,v 1.6 2010/04/27 02:00:37 dpvc Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/problemPreserveAnswers.pl b/macros/problemPreserveAnswers.pl
index ad72195a2d..9ba7752c1a 100644
--- a/macros/problemPreserveAnswers.pl
+++ b/macros/problemPreserveAnswers.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader$
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/problemRandomize.pl b/macros/problemRandomize.pl
index 092e391949..e996990d4b 100644
--- a/macros/problemRandomize.pl
+++ b/macros/problemRandomize.pl
@@ -1,13 +1,12 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/
-# $CVSHeader: pg/macros/problemRandomize.pl,v 1.12 2009/06/25 23:28:44 gage Exp $
-#
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
+#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version, or (b) the "Artistic License" which comes with this package.
-#
+#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
diff --git a/macros/scaffold.pl b/macros/scaffold.pl
index 1b7d2b5cc7..54aba86773 100644
--- a/macros/scaffold.pl
+++ b/macros/scaffold.pl
@@ -1,6 +1,6 @@
################################################################################
# WeBWorK Online Homework Delivery System
-# Copyright © 2000-2021 The WeBWorK Project, https://github.com/openwebwork
+# Copyright © 2000-2022 The WeBWorK Project, https://github.com/openwebwork
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of either: (a) the GNU General Public License as published by the
diff --git a/t/README b/t/README
deleted file mode 100644
index 77ca89b829..0000000000
--- a/t/README
+++ /dev/null
@@ -1,14 +0,0 @@
-These files, contributed by Boyd Duffe, are an example of writing unit tests
-for PG modules so that we can determine (a) that they work, and (b) that they continue
-to work when some seemingly innocuous change elsewhere in the system causes troubles
-in this module.
-
-Of course this is not supposed to happen when modular programming practices are followed
-but it happens anyway. :-)
-
-We need more "unit tests" like this. (The unit does not refer to Unit.pm but to testing
-a small piece or unit of the WeBWorK/PG code.) I would also like to wire these
-in so that administrators can perform these tests from the admin page on the web
-to reassure themselves that the new version of PG they are using is functioning correctly.
-
-
diff --git a/t/README.md b/t/README.md
index 93f8ef8c8b..29f3053262 100644
--- a/t/README.md
+++ b/t/README.md
@@ -1,14 +1,45 @@
-# Unit Tests for PG
+# Testing PG
+
+This directory houses the resources for testing PG. It includes a mix
+of strategies for testing at different scales. It helps to catch errors
+before they are found in production and prevent regressions from being
+re-introduced.
+
+The philosophy of
+[Test Driven Design](https://en.wikipedia.org/wiki/Test-driven_development)
+is that when a bug is found, a test is written to show it failing
+and when it is fixed, the test will pass.
+The unit tests are easy to run and amenable to automation. Some services
+can be "mocked" so that behaviour can be tested in their absence.
+All of this is to provide confidence that the code does what is intended
+and a working test can be better than documentation because it shows how
+the code currently works in practice.
+
+Old references can be found on the WebWork wiki page
+[Unit Testing](https://webwork.maa.org/wiki/Unit_Testing)
+
+
+# Unit Tests
+
+[Unit tests](https://en.wikipedia.org/wiki/Unit_testing) look at small chunks
+of self-coherent code to verify the behaviour of a subroutine or module.
+This is the test you write to catch corner cases or to explore code branches.
+In this repository, all files with the `.t` extension are unit tests which
+are found by Perl's [prove](https://perldoc.perl.org/prove) command.
The individual unit tests are located in each of the directories.
+Best practice is to create a directory for each module being tested and
+group similar tests together in separate files with a descriptive name,
+such as **t/units/** for testing the **Units.pm** module.
-Formal unit tests are located in the the `macros` and `contexts` directories that are designed to test the pg macros and contexts respectively.
+Formal unit tests are located in the the `macros` and `contexts` directories
+that are designed to test the pg macros and contexts respectively.
## Running the tests
```bash
cd $PG_ROOT
-prove -r .
+prove -lr t/
```
will run all of the tests in `.t` files within subdirectories of `t`.
@@ -23,6 +54,7 @@ prove -v pgaux.t
```
which will be verbose (`-v`).
+Or you could use `prove -lv t/macros/pgaux.t` from the root directory.
## Writing a Unit Test
@@ -70,3 +102,34 @@ is(check_score($f->eval(x=>2),"4"),1,"math objects: eval x^2 at x=2");
```
The `check_score` subroutine evaluates and compares a MathObject with a string representation of the answer. If the score is 1, then the two are equal.
+
+
+# Integration tests
+
+[Integration testing](https://en.wikipedia.org/wiki/Integration_testing)
+tests components working together as a group. The files with the `.pg`
+extension are used to demonstrate the output of the rendering engine.
+
+**TODO:** add an explanation of how to run these integration tests
+and their requirements.
+
+
+# Test Dependencies
+
+The tests for **Units.pm** have brought in a new module dependency,
+[Test2](https://metacpan.org/pod/Test2::V0) which is the state of the art in
+testing Perl modules. It can compare data structures, examine warnings and
+catch fatal errors thrown under expected conditions. It provides many tools
+for testing and randomly executes its subtests to avoid the programmer
+depending on stateful data.
+
+To make these easier to install with
+[cpanm](https://metacpan.org/dist/App-cpanminus/view/bin/cpanm), there is a
+[cpanfile](https://metacpan.org/dist/Module-CPANfile/view/lib/cpanfile.pod)
+in the root directory. Use
+
+ cpanm --installdeps .
+
+which will install the runtime and test dependencies.
+To use the cpanfile for a minimal install skipping the test requirements,
+use the `--notest` option with cpanm.
diff --git a/t/contexts/fraction.t b/t/contexts/fraction.t
index ecebcd848f..ec10c3deaf 100644
--- a/t/contexts/fraction.t
+++ b/t/contexts/fraction.t
@@ -1,23 +1,15 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
+# should I "use" Parser Value Parser::Legacy here instead?
-use Test::More;
-use Test::Exception;
+use lib 't/lib';
+use Test::PG;
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
+=head2 Fraction context
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
-
-use lib "$main::pg_dir/lib";
-
-require("$main::pg_dir/t/build_PG_envir.pl");
+To test the reduction of fractions
-## END OF TOP_MATERIAL
+=cut
loadMacros("PGstandard.pl", "MathObjects.pl", "contextFraction.pl");
@@ -39,9 +31,9 @@ Context("Fraction");
# require("Parser::Legacy::LimitedNumeric::Number");
# require("Parser::Legacy");
-my $a1 = Compute("1/2");
-my $a2 = Compute("2/4");
+ok my $a1 = Compute("1/2");
+ok my $a2 = Compute("2/4");
-is($a1->value, $a2->value, "contextFraction: reduce fractions");
+is $a1->value, $a2->value, 'contextFraction: reduce fractions';
done_testing();
diff --git a/t/contexts/integer.t b/t/contexts/integer.t
index 7981c240dd..d5f495e84a 100644
--- a/t/contexts/integer.t
+++ b/t/contexts/integer.t
@@ -1,23 +1,15 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
+# should I "use" Parser Value Parser::Legacy here instead?
-use Test::More;
-use Test::Exception;
+use lib 't/lib';
+use Test::PG;
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
+=head2 Integer context
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
-
-use lib "$main::pg_dir/lib";
+To test for greatest common denomenators and such like.
-require("$main::pg_dir/t/build_PG_envir.pl");
-
-## END OF TOP_MATERIAL
+=cut
loadMacros("MathObjects.pl", "contextInteger.pl");
@@ -33,4 +25,3 @@ ANS($b->cmp);
ok(1, "integer test: dummy test");
done_testing();
-
diff --git a/t/contexts/toltype_digits.t b/t/contexts/toltype_digits.t
index f878cea032..4b3f741f85 100644
--- a/t/contexts/toltype_digits.t
+++ b/t/contexts/toltype_digits.t
@@ -1,64 +1,56 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
+use lib 't/lib';
+use Test::PG;
-use Test::More;
-use Test::Exception;
+=head2 TolType context
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
+To test for tolerances
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
-
-use lib "$main::pg_dir/lib";
-
-require("$main::pg_dir/t/build_PG_envir.pl");
-
-## END OF TOP_MATERIAL
+=cut
loadMacros("PGstandard.pl", "MathObjects.pl");
my $ctx = Context("Numeric");
-$ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 1);
-
my $pi = Real("pi");
-is(check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14");
-is(check_score($pi, Compute("3.141")), 1, "toltype digits: pi is 3.141");
-is(check_score($pi, Compute("3.142")), 1, "toltype digits: pi is 3.142");
-is(check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143");
-is(check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15");
-
-note("");
-note("change tolTrunction to 0");
-
-$ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 0);
-is(check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14");
-is(check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141");
-is(check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142");
-is(check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143");
-is(check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15");
-
-note("");
-note("set tolExtraDigits to 2");
-
-$ctx->flags->set(
- tolType => 'digits',
- tolerance => 3,
- tolTruncation => 0,
- tolExtraDigits => 2
-);
-is(check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14");
-is(check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141");
-is(check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142");
-is(check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143");
-is(check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15");
-
-is(check_score($pi, Compute("3.1416")), 1, "toltype digits: pi is 3.1416");
-is(check_score($pi, Compute("3.1415888")), 1, "toltype digits: pi is 3.1415888");
-is(check_score($pi, Compute("3.1415")), 0, "toltype digits: pi is not 3.1415");
+subtest 'set tolTrunction to 1' => sub {
+ $ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 1);
+
+ is check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14";
+ is check_score($pi, Compute("3.141")), 1, "toltype digits: pi is 3.141";
+ is check_score($pi, Compute("3.142")), 1, "toltype digits: pi is 3.142";
+ is check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143";
+ is check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15";
+};
+
+subtest 'set tolTrunction to 0' => sub {
+ $ctx->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 0);
+
+ is check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14";
+ is check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141";
+ is check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142";
+ is check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143";
+ is check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15";
+};
+
+subtest 'set tolExtraDigits to 2' => sub {
+ $ctx->flags->set(
+ tolType => 'digits',
+ tolerance => 3,
+ tolTruncation => 0,
+ tolExtraDigits => 2
+ );
+
+ is check_score($pi, Compute("3.14")), 1, "toltype digits: pi is 3.14";
+ is check_score($pi, Compute("3.141")), 0, "toltype digits: pi is not 3.141";
+ is check_score($pi, Compute("3.142")), 1, "toltype digits: pi is not 3.142";
+ is check_score($pi, Compute("3.143")), 0, "toltype digits: pi is not 3.143";
+ is check_score($pi, Compute("3.15")), 0, "toltype digits: pi is not 3.15";
+
+ is check_score($pi, Compute("3.1416")), 1, "toltype digits: pi is 3.1416";
+ is check_score($pi, Compute("3.1415888")), 1, "toltype digits: pi is 3.1415888";
+ is check_score($pi, Compute("3.1415")), 0, "toltype digits: pi is not 3.1415";
+};
done_testing();
diff --git a/t/contexts/trig_degrees.t b/t/contexts/trig_degrees.t
index 3d2230ad75..ba8481721f 100644
--- a/t/contexts/trig_degrees.t
+++ b/t/contexts/trig_degrees.t
@@ -1,23 +1,26 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
+use lib 't/lib';
+use Test::PG;
-use Test::More;
-use Test::Exception;
+# remove warnings about redefining trig functions
+delete $main::{sin};
+delete $main::{cos};
+delete $main::{atan2};
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
+=head2 Errors to be fixed
-use lib "$main::pg_dir/lib";
+There is something wrong with either contextTrigDegrees.pl or how this test
+sets up the context. It looks like it still calculates in radians.
+Maybe the problem is how it imports symbols?
-require("$main::pg_dir/t/build_PG_envir.pl");
+When you fix it, the test output will report the test numbers after C
+
+These are the same results as the original Test::More version with build_PG_env.pl
+
+=cut
-## END OF TOP_MATERIAL
loadMacros("contextTrigDegrees.pl");
@@ -25,9 +28,21 @@ my $ctx = Context("TrigDegrees");
ok(Value::isContext($ctx), "trig degrees: check context");
-my $cos60 = Compute("cos(60)");
+ok my $cos60 = Compute("cos(60)"), 'Call Compute';
+ok my $eval_cos60 = $cos60->cmp->evaluate("1/2"), 'evalute an answer to cos(60)';
+
+is $eval_cos60,
+ hash {
+ field type => 'Value (Real)';
+ field score => 0;
+ field correct_ans => "cos(60)";
+ field student_ans => 0.5;
+ field error_flag => U();
+ field error_message => DF();
+ etc();
+ }, 'What does the Compute("cos(60)") object look like?';
+
-Compute("cos(60)")->cmp->evaluate("1/2");
# dd Compute("1/2")->value;
# is (check_score($cos60,"1/2"),1,"trig degrees: cos(60) = 1/2");
@@ -38,4 +53,19 @@ Compute("cos(60)")->cmp->evaluate("1/2");
# is (check_score(Compute("cos(60)"),"sin(30)"),1,"trig degrees: cos(60) = 1/2");
+# simple sanity checking
+is check_score( Compute('sin(0)'), '0'), 1, 'trig degrees: sin(0) = 0';
+is check_score( Compute('sin(90)'), '1'), 1, 'trig degrees: sin(90) = 1';
+is check_score( Compute('cos(0)'), '1'), 1, 'trig degrees: cos(0) = 1';
+
+todo 'why is cos(90) not equal to 0' => sub {
+ # are we still computing in radians?
+ is check_score( Compute('cos(90)'), '0'), 1, 'trig degrees: cos(90) = 0';
+ is check_score( Compute('cos(90)'), '1.6155E-15'), 1, 'trig degrees: cos(90) ~ 0';
+
+ my $pi = 4 * atan2(1,1);
+ is check_score( Compute("cos($pi/3)"), "sin($pi/6)"), 1, 'trig degrees: cos(60) = sin(30)';
+};
+
+
done_testing();
diff --git a/t/lib/Test/PG.pm b/t/lib/Test/PG.pm
new file mode 100644
index 0000000000..c382a303b8
--- /dev/null
+++ b/t/lib/Test/PG.pm
@@ -0,0 +1,108 @@
+package Test::PG;
+
+BEGIN {
+ die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
+ $main::pg_dir = $ENV{PG_ROOT};
+}
+
+use warnings;
+use strict;
+
+use lib "$main::pg_dir/lib"; # only needed if not using prove with -l
+
+
+=head1 Test::PG
+
+This module provides the environment and some generic functions for
+writing tests for PG macros. Mostly copied from F,
+it also redefines Test2's "exists" function from C to C
+to avoid the conflict with WebWork's exponential function.
+Its final action is to load C.
+
+The reason for the module is that There Is More Than One Way To Do It
+and people develop different styles. This is my coding style.
+Also we learn the underlying structures better by re-inventing the wheel.
+We just try to make a better wheel, but success is not guaranteed.
+
+This module strives for elegance in reducing boiler plate in test files,
+a minimum of duplicated code and adheres to the principle of least surprise
+by locating modules below the C directory.
+It does not make a decent cup of tea.
+
+=head2 Usage
+
+To test a macro that needs to be loaded, include this preamble
+at the top of your test file and customize.
+
+ use Test2::V0;
+
+ use MyPGLib; # does your macro need a module?
+
+ use lib 't/lib';
+ use Test::PG; # setup a minimal WW environment
+
+ loadMacros("parserMyPGMacro.pl");
+
+ Context("Numeric");
+
+And run your test from the $PG_ROOT directory with
+
+ prove -l t/my_macro/my_test.t
+
+
+=head2 TODO
+
+=head3 Quiet warnings in F
+
+The functions sin, cos and atan2 are redefined and generate warnings.
+C them from the symbol table before loading the macro.
+
+F declares C<$deg> twice, it being
+a required file, the package scope isn't heeded by the second C.
+I wonder if this happens every time this macro is loaded.
+
+=cut
+
+
+package main;
+
+$main::{EXISTS} = delete $main::{E}; # redefine Test2's E() function as EXISTS()
+
+my $macros_dir = "$main::pg_dir/macros";
+
+# use WeBWorK::Localize;
+use PGcore;
+use Parser;
+
+# build up enough of a PG environment to get things running
+our %envir = (
+ htmlDirectory => '/opt/webwork/courses/daemon_course/html',
+ htmlURL => 'http://localhost/webwork2/daemon_course/html',
+ tempURL => 'http://localhost/webwork2/daemon_course/tmp',
+ pgDirectories => { macrosPath => ["$macros_dir"] },
+ macrosPath => ["$macros_dir"],
+ displayMode => 'HTML_MathJax',
+ language => 'en-us',
+ language_subroutine => sub { return @_; }, # return the string passed in instead going to maketext
+);
+
+sub be_strict {
+ require 'ww_strict.pm';
+ strict::import();
+}
+
+sub PG_restricted_eval {
+ WeBWorK::PG::Translator::PG_restricted_eval(@_);
+}
+
+sub check_score {
+ my ($correct_answer, $ans) = @_;
+ return $correct_answer->cmp->evaluate($ans)->{score};
+}
+
+require "$macros_dir/PG.pl";
+DOCUMENT();
+
+loadMacros('PGbasicmacros.pl');
+
+1;
diff --git a/t/macros/basicmacros.t b/t/macros/basicmacros.t
index eab341352f..fd69c3d5e7 100644
--- a/t/macros/basicmacros.t
+++ b/t/macros/basicmacros.t
@@ -1,29 +1,19 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
-
-use Test::More;
-use Test::Exception;
+use HTML::Entities;
+use HTML::TagParser;
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
+use lib 't/lib';
+use Test::PG;
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
-use lib "$main::pg_dir/lib";
+=head1 MathObjects
-require("$main::pg_dir/t/build_PG_envir.pl");
+=cut
-## END OF TOP_MATERIAL
loadMacros("PGbasicmacros.pl");
-use HTML::Entities;
-use HTML::TagParser;
-
my $name = "myansrule";
my $named_box = NAMED_ANS_RULE($name);
diff --git a/t/macros/math_objects_basics.t b/t/macros/math_objects_basics.t
index a0b5a18d72..933199f73a 100644
--- a/t/macros/math_objects_basics.t
+++ b/t/macros/math_objects_basics.t
@@ -1,25 +1,18 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
-
-use Test::More;
-use Test::Exception;
+use Parser;
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
+use lib 't/lib';
+use Test::PG;
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
-use lib "$main::pg_dir/lib";
+=head1 MathObjects
-require("$main::pg_dir/t/build_PG_envir.pl");
+Test MathObject properties and operations.
+Try out operations with Infinity.
-## END OF TOP_MATERIAL
+=cut
-use Parser;
loadMacros("MathObjects.pl");
@@ -28,71 +21,82 @@ Context("Numeric");
my ($val1, $val2) = (10, 5);
my $obj1 = Compute($val1);
my $obj2 = Compute($val2);
-my $one = Compute("1");
my $zero = Compute("0");
-
-is($obj1->class, "Real", "math objects: check class of object");
-is($obj2->type, "Number", "math objects: check type of object");
-ok($one->isOne, "math objects: check if a number is 1");
-ok(!$zero->isOne, "math objects: check if a number is not 1");
-ok($zero->isZero, "math objects: check if a number is 0");
-ok(!$one->isZero, "math objects: check if a number is not 0");
-
-ok(Value::isValue($obj1), "math objects: check if an object is a value");
-ok(Value::isNumber($obj1), "math objects: check if an object is a number");
-ok(Value::isReal($obj1), "math objects: check if a number is a real number");
-ok(!Value::isComplex($obj1), "math objects: check if an integer is complex");
-
-ok(!Value::isFormula($obj1), "math objects: check if a number is not a formula");
-
-# check infinite values
-note("Tests for infinite values");
-
-my $inf = Compute("inf");
-is($inf->value, "infinity", "math objects: check for infinity via a string");
-is($inf->class, "Infinity", "math objects: check that the class is Infinity");
-is($inf->type, "Infinity", "math objects: check that the type is Infinity");
-ok(!Value::isNumber($inf), "math objects: check if inf is a number");
-
-# check that operations with infinity are not allowed
-
-throws_ok {
- Compute("$obj1+$inf");
-}
-qr/can't be infinities/, "math objects: addition with infinity";
-throws_ok {
- Compute("$obj1-$inf");
-}
-qr/can't be infinities/, "math objects: subtraction with infinity";
-throws_ok {
- Compute("$obj1*$inf");
-}
-qr/can't be infinities/, "math objects: multiplication with infinity";
-throws_ok {
- Compute("$obj1/$inf");
-}
-qr/can't be infinities/, "math objects: division with infinity";
-
-# is($result1->value,"infinity","math objects: check that the sum of a finite and infinite value is infinite");
+ok my $one = Compute("1"), 'Create a MathObject with Compute';
+
+subtest 'Basic properties of MathObjects' => sub {
+ is $obj1->class, 'Real', 'math objects: check class of object';
+ is $obj2->type, 'Number', 'math objects: check type of object';
+ is $one->isOne, T(), 'math objects: check if a number is 1';
+ is $zero->isOne, F(), 'math objects: check if a number is not 1';
+ is $zero->isZero, T(), 'math objects: check if a number is 0';
+ is $one->isZero, F(), 'math objects: check if a number is not 0';
+};
+
+subtest 'Class methods of Value to determine type' => sub {
+ is Value::isValue($obj1), T(), 'math objects: check if an object is a value';
+ is Value::isNumber($obj1), T(), 'math objects: check if an object is a number';
+ is Value::isReal($obj1), T(), 'math objects: check if a number is a real number';
+ is Value::isComplex($obj1), F(), 'math objects: check if an integer is complex';
+
+ is Value::isFormula($obj1), F(), 'math objects: check if a number is not a formula';
+};
+
+ok my $inf = Compute("inf"), 'Can create Infinity';
+
+subtest 'Tests for infinite values' => sub {
+ is $inf->value, 'infinity', 'math objects: check for infinity via a string';
+ is $inf->class, 'Infinity', 'math objects: check that the class is Infinity';
+ is $inf->type, 'Infinity', 'math objects: check that the type is Infinity';
+ ok !Value::isNumber($inf), 'math objects: check if inf is a number';
+};
+
+subtest 'check that operations with infinity are not allowed' => sub {
+ like(
+ dies { Compute("$obj1+$inf") },
+ qr/can't be infinities/,
+ "math objects: addition with infinity"
+ );
+ like(
+ dies { Compute("$obj1-$inf") },
+ qr/can't be infinities/,
+ "math objects: subtraction with infinity"
+ );
+ like(
+ dies { Compute("$obj1*$inf") },
+ qr/can't be infinities/,
+ "math objects: multiplication with infinity"
+ );
+ like(
+ dies { Compute("$obj1/$inf") },
+ qr/can't be infinities/,
+ "math objects: division with infinity"
+ );
+
+ # is($result1->value,'infinity','math objects: check that the sum of a finite and infinite value is infinite');
+};
my $sum = $obj1 + $obj2;
my $diff = $obj1 - $obj2;
-my $prod = $obj1 * $obj2;
-
-is($sum->value, $val1 + $val2, "math objects: test sum");
-is($diff->value, $val1 - $val2, "math objects: test difference");
-is($prod->value, $val1 * $val2, "math objects: test product");
-
-## check scores using the cmp method
-
-is(check_score($sum, Compute($sum)), 1, "math object: use cmp to check sum");
-is(check_score($diff, Compute($diff)), 1, "math object: use cmp to check diff");
-is(check_score($prod, Compute($prod)), 1, "math object: use cmp to check prod");
-
-## check some wrong answers;
+ok my $prod = $obj1 * $obj2, 'Operate on two MathObjects';
+
+subtest 'check object operations' => sub {
+ is $sum->value, $val1 + $val2, 'math objects: test sum';
+ is $diff->value, $val1 - $val2, 'math objects: test difference';
+ is $prod->value, $val1 * $val2, 'math objects: test product';
+};
+
+subtest 'check scores using the cmp method' => sub {
+ is check_score($sum, Compute($sum)), 1, 'math object: use cmp to check sum';
+ is check_score($diff, Compute($diff)), 1, 'math object: use cmp to check diff';
+ is check_score($prod, Compute($prod)), 1, 'math object: use cmp to check prod';
+};
+
+subtest 'check some wrong answers' => sub {
+ is check_score($sum, Compute($sum + 1)), 0, 'math object: use cmp to check sum';
+ is check_score($diff, Compute($diff + 1)), 0, 'math object: use cmp to check diff';
+ is check_score($prod, Compute($prod + 1)), 0, 'math object: use cmp to check prod';
+};
-is(check_score($sum, Compute($sum + 1)), 0, "math object: use cmp to check sum");
-is(check_score($diff, Compute($diff + 1)), 0, "math object: use cmp to check diff");
-is(check_score($prod, Compute($prod + 1)), 0, "math object: use cmp to check prod");
done_testing();
diff --git a/t/macros/math_objects_more.t b/t/macros/math_objects_more.t
index e54bb409e8..37c0ec29d7 100644
--- a/t/macros/math_objects_more.t
+++ b/t/macros/math_objects_more.t
@@ -1,23 +1,15 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
+use lib 't/lib';
+use Test::PG;
-use Test::More;
-use Test::Exception;
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
+=head1 MathObjects
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
+Tests pass
-use lib "$main::pg_dir/lib";
+=cut
-require("$main::pg_dir/t/build_PG_envir.pl");
-
-## END OF TOP_MATERIAL
loadMacros("MathObjects.pl");
diff --git a/t/macros/pgaux.t b/t/macros/pgaux.t
index 73bc3031de..34bec6aeb7 100644
--- a/t/macros/pgaux.t
+++ b/t/macros/pgaux.t
@@ -1,21 +1,15 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
+use lib 't/lib';
+use Test::PG;
-use Test::More;
-## the following needs to include at the top of any testing down to TOP_MATERIAL
+=head1 PGauxiliaryFunctions
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
+Tests pass
-use lib "$main::pg_dir/lib";
-require("$main::pg_dir/t/build_PG_envir.pl");
+=cut
-## END OF TOP_MATERIAL
loadMacros("PGauxiliaryFunctions.pl");
diff --git a/t/macros/tableau.t b/t/macros/tableau.t
index a059ab4557..b09da818f4 100755
--- a/t/macros/tableau.t
+++ b/t/macros/tableau.t
@@ -1,40 +1,44 @@
-use warnings;
-use strict;
+use Test2::V0;
-package main;
+use Class::Accessor;
+use Parser;
+use PGcore;
+use Value;
-use Test::More;
-use Test::Exception;
+use lib 't/lib';
+use Test::PG;
-# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
-BEGIN {
- die "PG_ROOT not found in environment.\n" unless $ENV{PG_ROOT};
- $main::pg_dir = $ENV{PG_ROOT};
-}
+=head1 Tableau
-use lib "$main::pg_dir/lib";
+Deep recursion error possibly due to "context" being a reference
+to the original context variable.
+I can't find a way to stop the recursion.
+As a result Test::More::is_deeply( $A, $B )
+becomes Test2::Tools::Compare::is( $A->string, $B->string )
+I wrote a validator for the cases where you are trying to compare
+a Value::Matrix with an array ref of values which doesn't have
+a "string" method. If someone can clean it up, future coders
+will thank you.
-require("$main::pg_dir/t/build_PG_envir.pl");
+Tests many of the functions provided by F
-## END OF TOP_MATERIAL
+Removed the redefined WARN and DEBUG MESSAGES
-use Parser;
-use Value;
-use Class::Accessor;
-use PGcore;
+Error Messages:
-loadMacros("tableau.pl", "Value.pl"); #gives us Real() etc.
+A context appears to have been destroyed without first calling release().
+Based on $@ it does not look like an exception was thrown (this is not always
+a reliable test)
+
+=cut
-note(
- "THIS FILE TESTS MANY OF THE FUNCTIONS PROVIDED BY tableau.pl
-#
-#
-"
-);
+
+loadMacros('tableau.pl', 'Value.pl'); #gives us Real() etc.
my %context = ();
+
sub Context { Parser::Context->current(\%context, @_) }
unless (%context && $context{current}) {
# ^variable our %context
@@ -43,13 +47,6 @@ unless (%context && $context{current}) {
Context(); # Initialize context (for persistent mod_perl)
}
-sub WARN_MESSAGE {
- warn("WARN MESSAGE: ", @_);
-}
-
-sub DEBUG_MESSAGE {
- warn("DEBUG MESSAGE: ", @_);
-}
Context("Matrix");
Context()->flags->set(
@@ -57,11 +54,27 @@ Context()->flags->set(
zeroLevelTol => 1E-5
);
+
my $A = Real(.0000005);
my $B = Real(0);
-is($A, $B, "test zeroLevel tolerance");
-ok($A == $B, "test zeroLevel tolerance with ok");
+subtest 'test zeroLevel tolerance' => sub {
+ is($A->value, within($B->value, $B->getFlag('zeroLevel')), 'test zeroLevel tolerance');
+ ok($A == $B, 'test zeroLevel tolerance with ok');
+
+ my $real_object = object {
+ prop isa => 'Value::Real';
+
+ field data => array { item 0 => match qr/\d/; };
+ field context => D();
+
+ call string => match qr/\d/;
+ call TeX => match qr/\d/;
+ };
+
+ is $A, $real_object, 'Zero is a Real';
+ is $B, $real_object, 'Near-Zero is a Real';
+};
my $money_total = 6000;
my $time_total = 600;
@@ -93,192 +106,305 @@ my $b = Value::Vector->new([ -$bill_profit, -$steve_profit ]); # need vertica
my $c = Value::Vector->new([ $money_total, $time_total, 1, 1 ]);
my $tableau1 = Tableau->new(A => $a, b => $b, c => $c);
-###############################################################
-# Check mutators
-#
-#
+
###############################################################
-ok(1 == 1, "trivial first test");
-ok(defined($tableau1), 'tableau has been defined and loaded');
-is(ref($tableau1), "Tableau", 'has type Tableau');
-
-# test "close_enough_to_zero" subroutine
-is $tableau1->close_enough_to_zero(0), 1, "checking_close_enough to zero";
-is $tableau1->close_enough_to_zero(1e-9), 0, "checking_close_enough to zero";
-is $tableau1->close_enough_to_zero(1e-5), 0, "checking_close_enough to zero";
-is $tableau1->close_enough_to_zero(1e-10), 1, "checking_NOT_close_enough to zero for 1e-10 ";
-note("sanity check 1e-10 vs 10**(-10): ", 1e-10, " ", 10**(-10));
-note(1.e-10);
-note(0.9999e-10);
-note(-0.9999e-10);
-is $tableau1->close_enough_to_zero(0.9999e-10), 1, "checking_close_enough to zero for 0.9999e-10";
-
-is $tableau1->close_enough_to_zero(-0.9999e-10), 1, "checking_close_enough to zero for -0.9999e-10";
-note("display stringified \$tableau1: ", $tableau1, "\n");
-is ref($tableau1), "Tableau", "checking data type is Tableau";
-ok $tableau1 eq "[[-5000,-400,-1,0,1,0,0,-4700],[-3000,-500,0,-1,0,1,0,-4500]]", "checking_stringification of tableau";
-
-is($tableau1->{m}, 2, 'number of constraints is 2');
-is($tableau1->{n}, 4, 'number of variables is 4');
-is_deeply([ $tableau1->{m}, $tableau1->{n} ], [ $tableau1->{A}->dimensions ], '{m},{n} match dimensions of A');
-is_deeply($tableau1->{A}, $a, 'constraint matrix');
-is_deeply($tableau1->{b}, Matrix([$b])->transpose, 'constraint constants is m by 1 matrix');
-is_deeply($tableau1->{c}, $c, 'objective function constants');
-is_deeply($tableau1->{A}, $tableau1->A, '{A} original constraint matrix accessor');
-is_deeply($tableau1->{b}, $tableau1->b, '{b} orginal constraint constants accessor');
-is_deeply($tableau1->{c}, $tableau1->c, '{c} original objective function constants accessor');
+subtest 'Check mutators' => sub {
+ is $tableau1, D(), 'tableau has been defined and loaded';
+ is $tableau1, object { prop isa => 'Tableau' }, 'has type Tableau';
+};
+
+subtest 'test "close_enough_to_zero" subroutine' => sub {
+ is $tableau1->close_enough_to_zero(0), 1, 'checking_close_enough to zero';
+ is $tableau1->close_enough_to_zero(1e-9), 0, 'checking_close_enough to zero';
+ is $tableau1->close_enough_to_zero(1e-5), 0, 'checking_close_enough to zero';
+ is $tableau1->close_enough_to_zero(1e-10), 1, 'checking_NOT_close_enough to zero for 1e-10 ';
+ note('sanity check 1e-10 vs 10**(-10): ', 1e-10, ' ', 10**(-10));
+ note(1.e-10);
+ note(0.9999e-10);
+ note(-0.9999e-10);
+ is $tableau1->close_enough_to_zero(0.9999e-10), 1, 'checking_close_enough to zero for 0.9999e-10';
+
+ is $tableau1->close_enough_to_zero(-0.9999e-10), 1, 'checking_close_enough to zero for -0.9999e-10';
+};
+
+subtest 'Basic test warmup' => sub {
+ note("display stringified \$tableau1: ", $tableau1, "\n");
+ is ref($tableau1), "Tableau", "checking data type is Tableau";
+ is $tableau1,
+ '[[-5000,-400,-1,0,1,0,0,-4700],[-3000,-500,0,-1,0,1,0,-4500]]',
+ 'checking_stringification of tableau';
+
+ is $tableau1->{m}, 2, 'number of constraints is 2';
+};
+
+subtest 'check data structure of tableau object' => sub {
+ is($tableau1->{m}, 2, 'number of constraints is 2');
+ is($tableau1->{n}, 4, 'number of variables is 4');
+ is [ $tableau1->{m}, $tableau1->{n} ], [ $tableau1->{A}->dimensions ],
+ '{m},{n} match dimensions of A';
+
+ is $tableau1,
+ object {
+ field A => object {
+ prop isa => 'Value::Matrix';
+ call string => "$a";
+ field context => D();
+ field data => D();
+ };
+ field b => D();
+ field c => D();
+ etc();
+ }, 'tableau attributes';
+
+ # call the string method to avoid the circular refs
+ is $tableau1->{A}->string, $a->string, 'Constraint matrix';
+ is $tableau1->{b}->string, Matrix([$b])->transpose->string, 'Constraint constants is m by 1 matrix';
+ is $tableau1->{c}->string, $c->string, 'Objective function constants';
+
+ is $tableau1->{A}->string, $tableau1->A->string, '{A} original constraint matrix accessor';
+ is $tableau1->{b}->string, $tableau1->b->string, '{b} original constraint constants accessor';
+ is $tableau1->{c}->string, $tableau1->c->string, '{c} original objective function constants accessor';
+};
my $test_constraint_matrix = Matrix($ra_matrix->[0], $ra_matrix->[1]);
-is_deeply($tableau1->{current_constraint_matrix},
- $test_constraint_matrix, 'initialization of current_constraint_matrix');
-is_deeply(
- $tableau1->{current_constraint_matrix},
- $tableau1->current_constraint_matrix,
- 'current_constraint_matrix accessor'
-);
-is_deeply($tableau1->{current_b}, $tableau1->{b}, 'initialization of current_b');
-is_deeply($tableau1->{current_b}, $tableau1->current_b, 'current_b accessor');
-is_deeply([ $tableau1->current_b->dimensions ], [ 2, 1 ], 'dimensions of current_b');
-my $obj_row_test = [ ((-$c)->value, 0, 0, 1, 0) ];
-is_deeply($tableau1->objective_row, $obj_row_test, 'initialization of $tableau->{obj_row}');
-
-is(ref($tableau1->{obj_row}), 'Value::Matrix', '->{obj_row} has type Value::Matrix');
-is(ref($tableau1->obj_row), 'Value::Matrix', '->obj_row has type Value::Matrix');
-is_deeply($tableau1->obj_row, $tableau1->{obj_row}, 'verify mutator for {obj_row}');
-is_deeply(ref($tableau1->objective_row), 'ARRAY', '->objective_row has type ARRAY');
-is_deeply($tableau1->objective_row, [ $tableau1->{obj_row}->value ], 'access to {obj_row}');
-is_deeply($tableau1->objective_row, [ $tableau1->obj_row->value ], 'objective_row is obj_row->value = ARRAY');
-
-is(ref($tableau1->current_tableau), 'Value::Matrix', '-> current_tableau is Value::Matrix');
-is_deeply($tableau1->current_tableau, Matrix($ra_matrix), 'entire tableau including obj coeff row');
-
-is(ref($tableau1->S), "Value::Matrix", 'slack variables are a Value::Matrix');
-is_deeply($tableau1->S, $tableau1->I($tableau1->m), 'slack variables are identity matrix');
-
-# test basis
-is_deeply(ref($tableau1->basis_columns), "ARRAY", "{basis_column} has type ARRAY");
-is_deeply($tableau1->basis_columns, [ 5, 6 ], "initialization of basis");
-is(
- ref($tableau1->current_basis_matrix),
- ref(Value::Matrix->I($tableau1->m)),
- "current_basis_matrix type is MathObjectMatrix"
-);
-is_deeply($tableau1->current_basis_matrix, Value::Matrix->I($tableau1->m), "initialization of basis");
-
-# change basis and test again
-$tableau1->basis(2, 3);
-is_deeply(ref($tableau1->basis_columns), "ARRAY", "{basis_column} has type ARRAY");
-is_deeply($tableau1->basis_columns, [ 2, 3 ], " basis columns set to {2,3}");
-is(
- ref($tableau1->current_basis_matrix),
- ref($test_constraint_matrix->column_slice(2, 3)),
- "current_basis_matrix type is MathObjectMatrix"
-);
-is_deeply(
- $tableau1->current_basis_matrix,
- $test_constraint_matrix->column_slice(2, 3),
- "basis_matrix for columns {2,3} is correct"
-);
-is_deeply($tableau1->basis(Set(2, 3)), List([ 2, 3 ]), "->basis(Set(2,3))");
-is_deeply($tableau1->basis(List(2, 3)), List([ 2, 3 ]), "->basis(List(2,3))");
-is_deeply($tableau1->basis([ 2, 3 ]), List([ 2, 3 ]), "->basis([2,3])");
-
-# find basis column index corresponding to row index (and value of the basis coefficient)
-
-$tableau1->basis(5, 6);
-note("\nbasis is", $tableau1->basis(5, 6));
-note(print $tableau1->current_tableau, "\n");
-is_deeply([ $tableau1->find_leaving_column(1) ], [ 5, 1 ], "find_leaving_column returns [col_index, pivot_value] ");
-is_deeply([ $tableau1->find_leaving_column(2) ], [ 6, 1 ], "find_leaving_column returns [col_index, pivot_value] ");
-
-is_deeply($tableau1->find_next_basis_from_pivot(1, 2), Set(2, 6), "find next basis from pivot (1,2)");
-is_deeply($tableau1->find_next_basis_from_pivot(1, 3), Set(3, 6), "find next basis from pivot (1,3)");
-is_deeply($tableau1->find_next_basis_from_pivot(2, 1), Set(1, 5), "find next basis from pivot (2,1)");
-is_deeply($tableau1->find_next_basis_from_pivot(1, 1), Set(1, 6), "find next basis from pivot (1,1)");
-
-throws_ok(
- sub { $tableau1->find_next_basis_from_pivot(2, 5) },
- qr/pivot point should not be in a basis column/,
- "can't pivot in basis column (2,5)"
-); # probably shouldn't be doing this.
-throws_ok(
- sub { $tableau1->find_next_basis_from_pivot(1, 6) },
- qr/pivot point should not be in a basis column/,
- "can't pivot in basis column (2,6)"
-); # probably shouldn't be doing this.
-is_deeply($tableau1->find_next_basis_from_pivot(2, 1), Set(1, 5), "find next basis from pivot (2,1)");
-throws_ok(
- sub { $tableau1->find_next_basis_from_pivot(2, 6) },
- qr/pivot point should not be in a basis column/,
- "can't pivot in basis column (2,6)"
-); # probably shouldn't be doing this.
-
-$tableau1->basis(2, 3);
-note("\nbasis is", $tableau1->basis());
-note(print $tableau1->current_tableau, "\n");
-is_deeply([ $tableau1->find_leaving_column(1) ], [ 2, 500 ], "find_leaving_column returns [col_index, pivot_value] ");
-is_deeply([ $tableau1->find_leaving_column(2) ], [ 3, 500 ], "find_leaving_column returns [col_index, pivot_value] ");
-
-throws_ok(
- sub { $tableau1->find_next_basis_from_pivot(1, 2) },
- qr/pivot point should not be in a basis column/,
- "can't pivot in basis column (1,2)"
-); # probably shouldn't be doing this either.
-throws_ok(
- sub { $tableau1->find_next_basis_from_pivot(1, 3) },
- qr/pivot point should not be in a basis column.*/,
- "can't pivot in basis column (1,3)"
-); # probably shouldn't be doing this.
-is_deeply($tableau1->find_next_basis_from_pivot(2, 1), Set(1, 2), "find next basis from pivot (2,1)");
-is_deeply($tableau1->find_next_basis_from_pivot(1, 1), Set(1, 3), "find next basis from pivot (1,1)");
-
-$tableau1->basis(5, 6);
-note("\nbasis is ", $tableau1->basis());
-note($tableau1->current_tableau, "\n");
-note("find next short cut pivots");
-# ($row_index, $value, $feasible_point) = $self->find_short_cut_row()
-is_deeply([ $tableau1->find_short_cut_row() ], [ 1, -4700, 0 ], "row 1");
-is_deeply([ $tableau1->find_short_cut_column(1) ], [ 1, -5000, 0 ], "column 1 ");
-is_deeply([ $tableau1->next_short_cut_pivot() ], [ 1, 1, 0, 0 ], "pivot (1,1)");
-is_deeply([ $tableau1->next_short_cut_basis() ], [ 1, 6, undef ], "new basis {1,6} continue");
-$tableau1->current_tableau(1, 6);
-note($tableau1->current_tableau);
-
-is_deeply([ $tableau1->find_short_cut_row ], [ 2, Value::Real->new(-8.4E+06), 0 ], "find short cut row");
-is_deeply([ $tableau1->find_short_cut_column(2) ], [ 2, Value::Real->new(-1.3E+06), 0 ], "find short cut col 2 ");
-is_deeply([ $tableau1->next_short_cut_pivot() ], [ 2, 2, 0, 0 ], "pivot (2,2)");
-is_deeply([ $tableau1->next_short_cut_basis() ], [ 1, 2, undef ], "new basis {1,2} continue");
-
-$tableau1->current_tableau(1, 2);
-note($tableau1->current_tableau);
-
-is_deeply([ $tableau1->next_short_cut_pivot() ], [ undef, undef, 1, 0 ], "feasible point found");
-is_deeply(
- [ $tableau1->next_short_cut_basis() ],
- [ 1, 2, 'feasible_point' ],
- "all constraints positive at basis {1,2} --start phase2"
-);
-is_deeply([ $tableau1->find_pivot_column('max') ], [ 3, Value::Real->new(-100000), 0 ], "col 3");
-is_deeply([ $tableau1->find_pivot_row(3) ], [ 1, Value::Real->new(550000 / 500), 0 ], "row 1 ");
-is_deeply([ $tableau1->find_next_pivot('max') ], [ 1, 3, 0, 0 ], "pivot (1,3)");
-is_deeply([ $tableau1->find_next_basis('max') ], [ 2, 3, undef ], "new basis {2,3} continue");
-
-$tableau1->current_tableau(2, 3);
-note($tableau1->current_tableau);
-is_deeply([ $tableau1->find_pivot_column('max') ], [ 4, Value::Real->new(-300), 0 ], "col 4");
-is_deeply([ $tableau1->find_pivot_row(4) ], [ 1, 4500, 0 ], "row 2) ");
-
-is_deeply([ $tableau1->find_next_pivot('max') ], [ 1, 4, 0, 0 ], "pivot 1,4");
-is_deeply([ $tableau1->find_next_basis('max') ], [ 3, 4, undef ], "new basis {3,4} continue");
-
-$tableau1->current_tableau(3, 4);
-note($tableau1->current_tableau);
-is_deeply([ $tableau1->find_pivot_column('max') ], [ 5, Value::Real->new(-1), 0 ], "col 5");
-is_deeply([ $tableau1->find_pivot_row(5) ], [ undef, undef, 1 ], "row 2) ");
-
-is_deeply([ $tableau1->find_next_pivot('max') ], [ undef, 5, 0, 1 ], "unbounded -- no pivot");
-is_deeply([ $tableau1->find_next_basis('max') ], [ 3, 4, 'unbounded' ], "basis 3,4 unbounded");
+
+subtest 'Current constraint matrix' => sub {
+ is $tableau1->{current_constraint_matrix}->string,
+ $test_constraint_matrix->string,
+ 'initialization of current_constraint_matrix';
+ is
+ $tableau1->{current_constraint_matrix}->string,
+ $tableau1->current_constraint_matrix->string,
+ 'current_constraint_matrix accessor';
+ is $tableau1->{current_b}->string, $tableau1->{b}->string, 'initialization of current_b';
+ is $tableau1->{current_b}->string, $tableau1->current_b->string, 'current_b accessor';
+
+ is [ $tableau1->current_b->dimensions ], [ 2, 1 ], 'dimensions of current_b';
+};
+
+subtest 'Objective row properties' => sub {
+ my $obj_row_test = [ ((-$c)->value, 0, 0, 1, 0) ];
+
+ for (my $i = 0; $i < 4; $i++) {
+ is $tableau1->objective_row->[$i]->string, $obj_row_test->[$i]->string,
+ 'initialization of $tableau->{obj_row} (first half)';
+ }
+ is @{$tableau1->objective_row}[4..7],
+ @{$obj_row_test}[4..7],
+ 'initialization of $tableau->{obj_row} (second half)';
+
+ is $tableau1->{obj_row}, object { prop isa => 'Value::Matrix' }, '->{obj_row} has type Value::Matrix';
+ is $tableau1->obj_row, object { prop isa => 'Value::Matrix' }, '->obj_row has type Value::Matrix';
+ is $tableau1->obj_row->string, $tableau1->{obj_row}->string, 'verify mutator for {obj_row}';
+ is ref $tableau1->objective_row, 'ARRAY', '->objective_row has type ARRAY';
+
+ # the first 4 elements are Value::Real's and the remainder are perl scalars (numbers)
+ # these are all mapped to array refs of scalars
+ # should these use the validator( $compare_data ) pattern below?
+ is [ map { ref $_ ? $_->{data} : [$_] } $tableau1->objective_row->@* ],
+ [ map { ref $_ ? $_->{data} : $_ } $tableau1->{obj_row}->value ],
+ 'access to {obj_row}';
+ is [ map { ref $_ ? $_->{data} : [$_] } $tableau1->objective_row->@* ],
+ [ map { ref $_ ? $_->{data} : $_ } $tableau1->obj_row->value ],
+ 'objective_row is obj_row->value = ARRAY';
+};
+
+subtest 'Current tableau' => sub {
+ is $tableau1->current_tableau,
+ object { prop isa => 'Value::Matrix' },
+ '-> current_tableau is Value::Matrix';
+ is $tableau1->current_tableau,
+ Matrix($ra_matrix)->string,
+ 'entire tableau including obj coeff row';
+
+ is $tableau1->S, object { prop isa => 'Value::Matrix' }, 'slack variables are a Value::Matrix';
+ is $tableau1->S, $tableau1->I($tableau1->m)->string, 'slack variables are identity matrix';
+};
+
+subtest 'Verify stringify subroutine' => sub {
+ my $aref = [ [qw/1 2/], 7, [3, 0.4], [ (5, -.6, [8, 9])], 0, -1, [qw/-2 -3/]];
+ my $expected_string = '[[1,2],7,[3,0.4],[5,-0.6,[8,9]],0,-1,[-2,-3]]';
+ is stringify($aref), $expected_string, 'Local stringify recursively descends the refs';
+};
+
+# try out validator for mixed data types
+my $compare_data = sub {
+ my %params = @_;
+
+ # postfix dereferencing stable in perl 5.24
+ my ($g, $e) = map { ref $_ =~ /Value/ ? $_->copy : $_ } @{$params{got}};
+
+ my ($got, $exp);
+ $got = ref $g eq 'Value::Matrix' ? $g->string : stringify($g);
+ $exp = ref $e eq 'Value::Matrix' ? $e->string : stringify($e);
+
+ return is $got, $exp, 'Compare datastructures of MathObjects';
+};
+
+subtest 'Verify objective_row methods and properties' => sub {
+ is [ $tableau1->obj_row, $tableau1->{obj_row} ],
+ validator( $compare_data ),
+ 'verify mutator for {obj_row}';
+
+ is [ $tableau1->objective_row, [$tableau1->obj_row->value] ],
+ validator( $compare_data ),
+ 'objective_row is obj_row->value = ARRAY';
+};
+
+subtest 'test basis' => sub {
+ is ref $tableau1->basis_columns, 'ARRAY', '{basis_column} has type ARRAY';
+ is [$tableau1->basis_columns, [ 5, 6 ]], validator( $compare_data ), 'initialization of basis';
+ is(
+ ref($tableau1->current_basis_matrix),
+ ref(Value::Matrix->I($tableau1->m)),
+ 'current_basis_matrix type is MathObjectMatrix'
+ );
+ is $tableau1->current_basis_matrix->string,
+ Value::Matrix->I($tableau1->m)->string,
+ 'initialization of basis';
+};
+
+
+subtest 'change basis and test again' => sub {
+ $tableau1->basis(2, 3);
+
+ is ref $tableau1->basis_columns, 'ARRAY', '{basis_column} has type ARRAY';
+ is [$tableau1->basis_columns, [ 2, 3 ]], validator( $compare_data ), ' basis columns set to {2,3}';
+ is(
+ ref($tableau1->current_basis_matrix),
+ ref($test_constraint_matrix->column_slice(2, 3)),
+ 'current_basis_matrix type is MathObjectMatrix'
+ );
+ is(
+ $tableau1->current_basis_matrix->string,
+ $test_constraint_matrix->column_slice(2, 3)->string,
+ 'basis_matrix for columns {2,3} is correct'
+ );
+ is $tableau1->basis(Set(2, 3))->string, List([ 2, 3 ])->string, '->basis(Set(2,3))';
+ is $tableau1->basis(List(2, 3))->string, List([ 2, 3 ])->string, '->basis(List(2,3))';
+ is $tableau1->basis([ 2, 3 ])->string, List([ 2, 3 ])->string, '->basis([2,3])';
+};
+
+subtest 'find basis column index corresponding to row index' => sub {
+ # and value of the basis coefficient
+
+ $tableau1->basis(5, 6);
+ note("\nbasis is", $tableau1->basis(5, 6));
+ note(print $tableau1->current_tableau, "\n");
+ is [ $tableau1->find_leaving_column(1) ], [ 5, 1 ],
+ 'find_leaving_column returns [col_index, pivot_value] ';
+ is [ $tableau1->find_leaving_column(2) ], [ 6, 1 ],
+ 'find_leaving_column returns [col_index, pivot_value] ';
+
+ is $tableau1->find_next_basis_from_pivot(1, 2)->string, Set(2, 6)->string,
+ 'find next basis from pivot (1,2)';
+ is $tableau1->find_next_basis_from_pivot(1, 3)->string, Set(3, 6)->string,
+ 'find next basis from pivot (1,3)';
+ is $tableau1->find_next_basis_from_pivot(2, 1)->string, Set(1, 5)->string,
+ 'find next basis from pivot (2,1)';
+ is $tableau1->find_next_basis_from_pivot(1, 1)->string, Set(1, 6)->string,
+ 'find next basis from pivot (1,1)';
+
+ like(
+ dies { $tableau1->find_next_basis_from_pivot(2, 5) },
+ qr/pivot point should not be in a basis column/,
+ "can't pivot in basis column (2,5)"
+ ); # probably shouldn't be doing this.
+ like(
+ dies { $tableau1->find_next_basis_from_pivot(1, 6) },
+ qr/pivot point should not be in a basis column/,
+ "can't pivot in basis column (2,6)"
+ ); # probably shouldn't be doing this.
+
+ is $tableau1->find_next_basis_from_pivot(2, 1)->string, Set(1, 5)->string,
+ 'find next basis from pivot (2,1)';
+ like(
+ dies { $tableau1->find_next_basis_from_pivot(2, 6) },
+ qr/pivot point should not be in a basis column/,
+ "can't pivot in basis column (2,6)"
+ ); # probably shouldn't be doing this.
+};
+
+subtest 'find another basis (2,3)' => sub {
+ $tableau1->basis(2, 3);
+ note("\nbasis is", $tableau1->basis());
+ note(print $tableau1->current_tableau, "\n");
+
+ is [ $tableau1->find_leaving_column(1) ], [ 2, 500 ],
+ 'find_leaving_column returns [col_index, pivot_value] ';
+ is [ $tableau1->find_leaving_column(2) ], [ 3, 500 ],
+ 'find_leaving_column returns [col_index, pivot_value] ';
+
+ like(
+ dies { $tableau1->find_next_basis_from_pivot(1, 2) },
+ qr/pivot point should not be in a basis column/,
+ "can't pivot in basis column (1,2)"
+ ); # probably shouldn't be doing this either.
+ like(
+ dies { $tableau1->find_next_basis_from_pivot(1, 3) },
+ qr/pivot point should not be in a basis column/,
+ "can't pivot in basis column (1,3)"
+ ); # probably shouldn't be doing this.
+
+ is $tableau1->find_next_basis_from_pivot(2, 1)->string, Set(1, 2)->string,
+ 'find next basis from pivot (2,1)';
+ is $tableau1->find_next_basis_from_pivot(1, 1)->string, Set(1, 3)->string,
+ 'find next basis from pivot (1,1)';
+};
+
+subtest 'find next short cut pivots' => sub {
+ $tableau1->basis(5, 6);
+ note("\nbasis is ", $tableau1->basis());
+ note($tableau1->current_tableau, "\n");
+
+ # ($row_index, $value, $feasible_point) = $self->find_short_cut_row()
+
+ is [ $tableau1->find_short_cut_row() ], [ 1, -4700, 0 ], 'row 1';
+ is [ $tableau1->find_short_cut_column(1) ], [ 1, -5000, 0 ], 'column 1 ';
+ is [ $tableau1->next_short_cut_pivot() ], [ 1, 1, 0, 0 ], 'pivot (1,1)';
+ is [ $tableau1->next_short_cut_basis() ], [ 1, 6, undef ], 'new basis {1,6} continue';
+
+ $tableau1->current_tableau(1, 6);
+ note($tableau1->current_tableau);
+
+ is [ $tableau1->find_short_cut_row ],
+ [ 2, Value::Real->new(-8.4E+06)->string, 0 ], 'find short cut row';
+ is [ $tableau1->find_short_cut_column(2) ],
+ [ 2, Value::Real->new(-1.3E+06)->string, 0 ], 'find short cut col 2 ';
+ is [ $tableau1->next_short_cut_pivot() ], [ 2, 2, 0, 0 ], 'pivot (2,2)';
+ is [ $tableau1->next_short_cut_basis() ], [ 1, 2, undef ], 'new basis {1,2} continue';
+
+ $tableau1->current_tableau(1, 2);
+ note($tableau1->current_tableau);
+
+ is [ $tableau1->next_short_cut_pivot() ], [ undef, undef, 1, 0 ], 'feasible point found';
+ is(
+ [ $tableau1->next_short_cut_basis() ],
+ [ 1, 2, 'feasible_point' ],
+ 'all constraints positive at basis {1,2} --start phase2'
+ );
+ is [ $tableau1->find_pivot_column('max') ], [ 3, Value::Real->new(-100000)->string, 0 ], 'col 3';
+ is [ $tableau1->find_pivot_row(3) ], [ 1, Value::Real->new(550000 / 500)->string, 0 ], 'row 1';
+ is [ $tableau1->find_next_pivot('max') ], [ 1, 3, 0, 0 ], 'pivot (1,3)';
+ is [ $tableau1->find_next_basis('max') ], [ 2, 3, undef ], 'new basis {2,3} continue';
+
+ $tableau1->current_tableau(2, 3);
+ note($tableau1->current_tableau);
+ is [ $tableau1->find_pivot_column('max') ], [ 4, Value::Real->new(-300)->string, 0 ], 'col 4';
+ is [ $tableau1->find_pivot_row(4) ], [ 1, 4500, 0 ], 'row 2';
+
+ is [ $tableau1->find_next_pivot('max') ], [ 1, 4, 0, 0 ], 'pivot 1,4';
+ is [ $tableau1->find_next_basis('max') ], [ 3, 4, undef ], 'new basis {3,4} continue';
+
+ $tableau1->current_tableau(3, 4);
+ note($tableau1->current_tableau);
+ is [ $tableau1->find_pivot_column('max') ], [ 5, Value::Real->new(-1)->string, 0 ], 'col 5';
+ is [ $tableau1->find_pivot_row(5) ], [ undef, undef, 1 ], 'row 2';
+
+ is [ $tableau1->find_next_pivot('max') ], [ undef, 5, 0, 1 ], 'unbounded -- no pivot';
+ is [ $tableau1->find_next_basis('max') ], [ 3, 4, 'unbounded' ], 'basis 3,4 unbounded';
+};
# note that the column is returned from find_next_pivot so one can find a certificate
# of unboundedness (can return a line going off to infinity)
@@ -292,31 +418,44 @@ is_deeply([ $tableau1->find_next_basis('max') ], [ 3, 4, 'unbounded' ], "basis 3
# # "unbounded, feasible_point, infeasible_tableau, optimal"?
# # it might be easier to remember.
#
-note("reset tableau to feasible point and try to minimize it for phase2");
-$tableau1->current_tableau(1, 2);
-note($tableau1->current_tableau);
-is_deeply([ $tableau1->next_short_cut_pivot() ], [ undef, undef, 1, 0 ], "feasible point found");
-is_deeply(
- [ $tableau1->next_short_cut_basis() ],
- [ 1, 2, 'feasible_point' ],
- "all constraints positive at basis {1,2} --start phase2"
-);
-
-is_deeply([ $tableau1->find_pivot_column('min') ], [ undef, undef, 1 ], "all neg coeff");
-is_deeply([ $tableau1->find_pivot_row(1) ], [ 1, Value::Real->new(550000 / 1300000), 0 ], "row 1 ");
-is_deeply([ $tableau1->find_next_pivot('min') ], [ undef, undef, 1, 0 ], "optimum");
-is_deeply([ $tableau1->find_next_basis('min') ], [ 1, 2, 'optimum' ], "optimum");
-#
-#
-is_deeply(
- $tableau1->statevars, # round off errors
- [ 550000 / 1300000, 8400000 / 1300000, 0, 0, 0, 0, 8.339999999999999E9 / 1300000 ], "state variables"
-);
+subtest 'reset tableau to feasible point and try to minimize it for phase2' => sub {
+ $tableau1->current_tableau(1, 2);
+ note($tableau1->current_tableau);
+ is [ $tableau1->next_short_cut_pivot() ], [ undef, undef, 1, 0 ], 'feasible point found';
+ is(
+ [ $tableau1->next_short_cut_basis() ],
+ [ 1, 2, 'feasible_point' ],
+ 'all constraints positive at basis {1,2} --start phase2'
+ );
+
+ is [ $tableau1->find_pivot_column('min') ], [ undef, undef, 1 ], 'all neg coeff';
+ is [ $tableau1->find_pivot_row(1) ],
+ [ 1, Value::Real->new(550000 / 1300000)->string, 0 ],
+ 'row 1';
+ is [ $tableau1->find_next_pivot('min') ], [ undef, undef, 1, 0 ], 'optimum';
+ is [ $tableau1->find_next_basis('min') ], [ 1, 2, 'optimum' ], 'optimum';
+
+ is(
+ $tableau1->statevars, # round off errors
+ [ 550000 / 1300000, 8400000 / 1300000, 0, 0, 0, 0, 8.339999999999999E9 / 1300000 ],
+ 'state variables'
+ );
+
+ is $tableau1->align, 'cccc|cc|c|c', 'check align';
+ is $tableau1->toplevel, [qw(x1 x2 x3 x4 x5 x6 z b)], 'check toplevel';
+
+ # diag($tableau1->align);
+ # diag(join(q{ } , @{$tableau1->toplevel}));
+};
-is($tableau1->align, 'cccc|cc|c|c', "check align");
-is_deeply($tableau1->toplevel, [qw(x1 x2 x3 x4 x5 x6 z b)], "check toplevel");
-# diag($tableau1->align);
-# diag(join(" " , @{$tableau1->toplevel}));
done_testing();
+
+sub stringify {
+ my $arrayref = shift;
+ warn "Not an array ref [$arrayref]" unless ref $arrayref eq 'ARRAY';
+ return sprintf("[%s]",
+ join(',', map { my $s = $_; ref $s eq 'ARRAY' ? stringify($s) : $s } @{$arrayref})
+ );
+}
diff --git a/t/math_objects/factorial.t b/t/math_objects/factorial.t
new file mode 100644
index 0000000000..5342054ad5
--- /dev/null
+++ b/t/math_objects/factorial.t
@@ -0,0 +1,78 @@
+use warnings;
+use strict;
+
+package main;
+
+use Test::More;
+use Test::Exception;
+
+# The following needs to include at the top of any testing down to END OF TOP_MATERIAL.
+
+BEGIN {
+ die 'PG_ROOT not found in environment.\n' unless $ENV{PG_ROOT};
+ $main::pg_dir = $ENV{PG_ROOT};
+}
+
+use lib "$main::pg_dir/lib";
+
+require("$main::pg_dir/t/build_PG_envir.pl");
+
+## END OF TOP_MATERIAL
+
+use Parser;
+
+loadMacros('MathObjects.pl');
+
+Context('Numeric');
+Context()->variables->add(y => "Real");
+Context()->variables->add(n => "Real");
+
+my $five_fact = Compute('5!');
+
+use Data::Dumper;
+print Dumper $five_fact->class;
+
+is($five_fact->class, 'Real', 'factorial: check class of object');
+is($five_fact->type, 'Number', 'factorial: check type of object');
+
+ok(Value::isValue($five_fact), 'factorial: check if an object is a value');
+ok(Value::isNumber($five_fact), 'factorial: check if an object is a number');
+ok(Value::isReal($five_fact), 'factorial: check if a number is a real number');
+ok(!Value::isComplex($five_fact), 'factorial: check if an integer is complex');
+ok(!Value::isFormula($five_fact), 'factorial: check if a number is not a formula');
+
+is($five_fact->value,120, 'factorial: 5! is 120');
+is(Compute("0!")->value, 1, 'factorial: 0! is 1');
+
+note('The double factorial is not defined here.');
+my $four_double_fact = Compute("4!!")->value;
+ok(6.2e+23 < $four_double_fact && $four_double_fact < 6.3e+23, 'factorial: 4!! is defined as (4!)!=24!' );
+
+ok(Compute("170!") > 1e+306, 'factorial: 170! is large but not infinite.');
+
+note('Tests for throwing exceptions.');
+
+throws_ok {
+ Compute("(-1)!");
+}
+qr/Factorial can only be taken of \(non-negative\) integers/, 'factorial: can\'t take factorial of negative integers.';
+
+throws_ok {
+ Compute("1.5!");
+}
+qr/Factorial can only be taken of \(non-negative\) integers/, 'factorial: can\'t take factorial of non-integer reals.';
+
+note('Try taking factorials of variables');
+my $n_fact = Compute("n!");
+is($n_fact->class, "Formula", "factorial: n! is a Formula");
+is($n_fact->type, "Number", "factorial: n! has type is Number");
+is($n_fact->eval(n=>5), 120, 'factorial: n! evaluated at n=5 is correct.');
+
+# check infinite values
+note('Tests for infinite values');
+
+my $large_fact = Compute('171!');
+my $inf = Compute('inf');
+is($large_fact->value, $inf, '171! is infinite.');
+
+done_testing();
diff --git a/t/units/basic_module.t b/t/units/basic_module.t
new file mode 100644
index 0000000000..0ad70fe095
--- /dev/null
+++ b/t/units/basic_module.t
@@ -0,0 +1,24 @@
+use Test2::V0;
+
+use Units;
+
+# get unit hashes
+my %joule = evaluate_units('J');
+my %newton_metre = evaluate_units('N*m');
+my %energy_base_units = evaluate_units('kg*m^2/s^2');
+
+# basic definitions of energy equivalence
+is \%joule, \%newton_metre,
+ 'A joule is a newton-metre';
+is \%joule, \%energy_base_units,
+ 'A joule is a kg metre squared per second squared';
+
+
+# test the error handling
+my $fake = 'bleurg';
+ok my %error = evaluate_units($fake);
+like $error{ERROR}, qr/UNIT ERROR Unrecognizable unit: \|$fake\|/,
+ "No unit '$fake' defined in Units file";
+
+
+done_testing;
diff --git a/t/units/basic_parser.t b/t/units/basic_parser.t
new file mode 100644
index 0000000000..81e2be0f9e
--- /dev/null
+++ b/t/units/basic_parser.t
@@ -0,0 +1,202 @@
+use Test2::V0;
+
+use Parser::Legacy::NumberWithUnits; # load this before the parser macro
+use Units;
+
+use lib 't/lib';
+use Test::PG;
+
+loadMacros("parserNumberWithUnits.pl");
+
+Context("Numeric");
+
+
+=head1 NumberWithUnits
+
+We test the basic functionality of the NumberWithUnits parser,
+F, to give us faith
+that the parser and its methods are working.
+Other test files will probe deeper into specific use cases of
+the NumberWithUnits macro and the L module.
+
+=head2 Testing Strategy
+
+Test all the methods of an object, check the attributes, verify the errors
+are thrown correctly, display strings look the way they should and
+all the ways that a student could answer produce the appropriate results.
+Check that the objects we create belong to their expected class.
+
+Demonstrate some of the flavour of Test2, with hash, bag, dies, todo, etc.
+Group similar tests into subtests.
+
+=head3 Setup
+
+All the boilerplate code is loaded with Test::PG and assume that people run
+it from the root directory with C.
+Load your base modules before loading the macros which depend on them
+and set the Context, if appropriate.
+
+See the example in the documentation of L
+
+ perldoc t/lib/Test/PG.pm
+
+=head2 TODO list
+
+=over 4
+
+=item Fix display of temperature units
+
+=item Test adding new units
+
+=item Look up how to get the value of the object instead of reaching into the hashref
+
+=item Test messages from wrong student answer submissions
+
+=back
+
+=cut
+
+
+# define some basic objects
+ok my $joule = NumberWithUnits(1, 'J');
+ok my $Nm = NumberWithUnits(1, 'N*m');
+ok my $energy_base_units = NumberWithUnits(1, 'kg*m^2/s^2');
+
+
+subtest 'Verify classes and methods' => sub {
+ isa_ok $joule, 'Parser::Legacy::NumberWithUnits';
+ can_ok $joule,
+ [ qw/cmp splitUnits getUnitNames getUnits TeXunits cmp_parse adjustCorrectValue
+ add_fundamental_unit add_unit string TeX / ],
+ 'Can we NumberWithUnits';
+
+ ok my $evaluator = $joule->cmp($Nm), 'Get an AnswerEvaluator';
+ isa_ok $evaluator, 'AnswerEvaluator';
+ can_ok $evaluator, [ qw/ evaluate / ], 'We Can Evaluate';
+};
+
+
+subtest 'Check attributes' => sub {
+ is(
+ $joule,
+ {
+ data => [ 1 ],
+ units => 'J',
+ units_ref => { kg => 1, m => 2, s => -2, factor => 1,
+ amp => 0, cd => 0, mol => 0, rad => 0,
+ degC => 0, degF => 0, degK => 0,
+ },
+ isValue => T(),
+ context => check_isa 'Parser::Context',
+ },
+ 'This looks like a joule'
+ );
+};
+
+subtest 'Basic definitions of energy equivalence' => sub {
+ is $joule->{data}->[0], $Nm->{data}->[0], 'One joule is one newton-metre';
+ is $joule->getUnits, $Nm->getUnits, 'A joule has the same dimensions as a newton-metre';
+
+ is (check_score($joule, $Nm), 1, 'A Joule is a Newton-metre');
+ is (check_score($joule, $energy_base_units), 1, 'A Joule can be expressed in SI base units');
+};
+
+subtest 'Test error handling' => sub {
+ my $fake = 'bleurg';
+
+ like(
+ dies { NumberWithUnits(1, "$fake") },
+ qr/Unrecognizable unit: \|$fake\|/,
+ "No unit '$fake' defined in Units file"
+ );
+ like(
+ dies { NumberWithUnits(1) },
+ qr/You must provide units for your number/,
+ "No unit given"
+ );
+ like(
+ dies { NumberWithUnits('J') },
+ qr/You must provide units for your number/,
+ "No value given, wants 2 arguments"
+ );
+};
+
+subtest 'Check parsing of arguments' => sub {
+ ok my $three_args = NumberWithUnits(1, 'N', 'm'), 'Ignores extra argument';
+ is $three_args->string, '1 N', 'Only sees the first unit';
+
+ ok my $string_arg = NumberWithUnits('1J'), 'Parses string argument';
+ is $string_arg->string, '1 J', 'Parses string correctly';
+};
+
+subtest 'Check some known units' => sub {
+ ok my @unit_names = (split /\|/, $joule->getUnitNames), 'Can getUnitNames';
+
+ is \@unit_names,
+ bag {
+ all_items( match qr/^[-%\w]+$/ );
+ item 'J'; item 'N';
+ item 'm'; item 'kg'; item 's';
+ etc;
+ },
+ 'Basic units loaded, sanity check';
+};
+
+subtest 'Check other methods' => sub {
+ is [ $joule->splitUnits ], ['1', 'J'], 'splitUnits creates an array';
+
+ is $joule->adjustCorrectValue, 0, 'What is adjustCorrectValue?';
+};
+
+subtest 'Check display methods' => sub {
+ is $joule->string, '1 J', 'Displays string - Joule';
+ is $joule->TeX, '1\ {\rm J}', 'Displays LaTeX string - Joule';
+ is $joule->TeXunits, '{\rm 1 J}', 'Displays LaTeX string - Joule';
+ is $Nm->TeX, '1\ {\rm N\,m}', 'Displays LaTeX string - Newton metre';
+ like $energy_base_units->TeX,
+ qr/ 1\\ \s \{ \S* \\frac\{ \\rm\S* \s kg \\, m\^\{2\}\} \{\\rm\S* \s s\^\{2\}\}\} /x,
+ 'Displays LaTeX string - energy in SI base units';
+
+ ok my $celsius = NumberWithUnits(1, 'degC');
+ ok my $kelvin = NumberWithUnits(1, 'degK');
+ todo 'Fix the display of temperatures' => sub {
+ is $celsius->TeX, '1\ {\rm ^{\circ}C}', 'Displays LaTeX string for degrees (finally)';
+ is $kelvin->TeX, '1\ {\rm K}', 'Displays LaTeX string for kelvin, no degree sign';
+ };
+};
+
+subtest 'Check possible answer format branches' => sub {
+ # re-write without check_score so we can get the messages to students
+
+ is check_score($joule, '1 J'), 1, 'one Joule plain';
+ is check_score($joule, '1.00 J'), 1, 'one Joule float';
+ is check_score($joule, '1E0 J'), 1, 'one Joule exponential notation';
+ is check_score($joule, '7/7 J'), 1, 'one Joule value calculated';
+ is check_score($joule, '1 J^1'), 1, 'one Joule to the power of one';
+ is check_score($joule, 'J 1'), 0, 'one Joule wrong order';
+ is check_score($joule, '2 J'), 0, 'one Joule wrong value';
+ is check_score($joule, '1 j'), 0, 'one Joule wrong case';
+ is check_score($joule, '1'), 0, 'one Joule missing unit';
+ is check_score($joule, 'J'), 0, 'one Joule missing value';
+ is check_score($joule, '1J'), 1, 'one Joule missing space between value and unit is valid';
+ is check_score($joule, '1 N'), 0, 'one Joule wrong unit force not energy';
+ is check_score($joule, '1 Nm'), 0, 'one Joule Nm missing *';
+ is check_score($joule, '1 N*m'), 1, 'one Joule as Newton metre';
+ is check_score($joule, '1 Joule'), 0, 'one Joule in words';
+ is check_score($joule, '1E-3 kJ'), 1, 'one Joule value as exponential';
+};
+
+todo 'check_score is stateful. Cannot handle repeated calls' => sub {
+ is check_score($joule, '1E-3 kJ'), 1, 'one Joule value as exponential second call';
+ is check_score($joule, '1E-3 kJ'), 1, 'one Joule value as exponential third call';
+
+ # the other tests I'd like to run
+ is check_score($joule, '0.001 kJ'), 1, 'one Joule decimal kJ';
+ is check_score($joule, '1/1000 kJ'), 1, 'one Joule fractional kJ';
+ is check_score($joule, '10^-3 kJ'), 1, 'one Joule latex power kJ';
+ is check_score($joule, '1 x 10^-3 kJ'), 1, 'one Joule scientific notation';
+ is check_score($joule, '10**-3 kJ'), 1, 'one Joule power of 10 kJ';
+};
+
+
+done_testing;
diff --git a/t/units/electron_volts.t b/t/units/electron_volts.t
new file mode 100644
index 0000000000..2a86920c6f
--- /dev/null
+++ b/t/units/electron_volts.t
@@ -0,0 +1,44 @@
+use Test2::V0;
+
+use Units;
+
+my %joule = evaluate_units('J');
+my %newton_metre = evaluate_units('N*m');
+my %base_units = evaluate_units('kg*m^2/s^2');
+
+my %electron_volt = evaluate_units('eV');
+my %kev = evaluate_units('keV');
+my %mev = evaluate_units('MeV');
+my %gev = evaluate_units('GeV');
+
+SKIP: {
+ skip('New eV units not available until PG-2.17')
+ if $kev{ERROR} =~ /^UNIT ERROR Unrecognizable unit/;
+
+ is \%electron_volt, by_factor( 1.6022E-19, \%joule ),
+ 'eV and joules differ by a factor of 1.6022 x 10^19';
+
+ is \%kev, by_factor( 1000, \%electron_volt ), 'kilo is factor 1000';
+ is \%mev, by_factor( 10**6, \%electron_volt ), 'mega is factor 10^6';
+ is \%gev, by_factor( 10**9, \%electron_volt ), 'giga is factor 10^9';
+}
+
+subtest 'electron volt has units of energy' => sub {
+ my ($ev, $J) = ( { %electron_volt }, { %joule } );
+ delete $ev->{factor};
+ delete $J->{factor};
+
+ is $ev, $J, 'electron volt has units of energy';
+};
+
+
+done_testing;
+
+sub by_factor {
+ my ($value, $unit) = @_;
+ my $new_unit = { %$unit }; # shallow copy hash values
+
+ $new_unit->{factor} *= $value;
+
+ return $new_unit;
+}