From 95d5f29bfab8691273a2c8df0be2775f859bef8c Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Fri, 21 Jul 2023 06:38:10 -0500 Subject: [PATCH] Lots of clean up and issue fixing for the sample problems. Fix warnings and errors in many of the sample problems, and clean up the categories. Note that algebra and trigonometry should not be categories. Those are subject areas. The documentation in the problems has been updated for consistency, readability, and correctness. Cases where new PGML constructs could be used have been implemented (tables, images, tags, and such). --- lib/SampleProblemParser.pm | 2 +- .../Algebra/AlgebraicFractionAnswer.pg | 112 ++++++------ .../Algebra/AnswerBlankInExponent.pg | 33 ++-- ...ultiplication.pg => AnswerUpToMultiple.pg} | 5 +- .../sample-problems/Algebra/DomainRange.pg | 56 +++--- .../sample-problems/Algebra/DynamicGraph.pg | 61 ++++--- .../Algebra/EquationDefiningFunction.pg | 9 +- .../Algebra/EquationImplicitFunction.pg | 25 +-- .../Algebra/ExpandedPolynomial.pg | 34 ++-- .../Algebra/FactoredPolynomial.pg | 30 ++-- .../sample-problems/Algebra/FractionAnswer.pg | 13 +- .../Algebra/FunctionDecomposition.pg | 22 +-- .../Algebra/GraphToolCircle.pg | 23 +-- .../sample-problems/Algebra/GraphToolCubic.pg | 21 ++- .../Algebra/GraphToolCustomChecker.pg | 21 +-- .../sample-problems/Algebra/GraphToolLine.pg | 32 ++-- .../Algebra/GraphToolNumberLine.pg | 14 +- .../Algebra/GraphToolPoints.pg | 17 +- .../Algebra/InequalityAnswer.pg | 12 +- .../Algebra/LinearInequality.pg | 14 +- .../sample-problems/Algebra/Logarithms.pg | 28 ++- .../sample-problems/Algebra/NoSolution.pg | 68 +++++++ .../sample-problems/Algebra/PointAnswers.pg | 17 +- .../Algebra/ScalingTranslating.pg | 12 +- .../Algebra/SimpleFactoring.pg | 33 ++-- .../Algebra/SolutionForEquation.pg | 8 +- .../Algebra/StringOrOtherType.pg | 26 +-- .../sample-problems/Algebra/TableOfValues.pg | 52 +++--- .../Algebra/UnorderedAnswers.pg | 15 +- .../Complex/ComplexOperations.pg | 35 ++-- .../sample-problems/Complex/LimitedComplex.pg | 45 ++--- .../Complex/OtherOperations.pg | 29 ++- .../DiffCalc/AnswerWithUnits.pg | 22 +-- .../DiffCalc/DifferenceQuotient.pg | 7 +- .../DiffCalc/DifferentiateFunction.pg | 69 ++++---- .../sample-problems/DiffCalc/LinearApprox.pg | 23 +-- .../sample-problems/DiffCalcMV/ContourPlot.pg | 54 +++--- .../DiffCalcMV/ImplicitPlane.pg | 27 +-- .../DiffEq/GeneralSolutionODE.pg | 42 ++--- .../sample-problems/DiffEq/HeavisideStep.pg | 80 +++++---- .../DiffEq/PrimesInFormulas.pg | 14 +- .../IntegralCalc/DoubleIntegral.pg | 35 ++-- .../IntegralCalc/GraphShading.pg | 60 ++++--- .../IntegralCalc/IndefiniteIntegrals.pg | 27 +-- .../IntegralCalc/LimitsOfIntegration.pg | 50 +++--- .../IntegralCalc/RiemannSums.pg | 150 +++++++++------- .../IntegralCalc/VolumeOfRevolution.pg | 167 ++++++++---------- .../LinearAlgebra/MatrixAnswer1.pg | 18 +- .../LinearAlgebra/MatrixAnswer2.pg | 66 +++---- .../MatrixCustomAnswerChecker.pg | 32 ++-- .../LinearAlgebra/MatrixOperations.pg | 17 +- .../LinearAlgebra/RowOperations.pg | 15 +- .../sample-problems/Misc/ChemicalReaction.pg | 26 ++- .../sample-problems/Misc/DraggableProof.pg | 39 ++-- .../Misc/DynamicGraphPolygon.pg | 98 +++++----- tutorial/sample-problems/Misc/EssayAnswer.pg | 66 +++---- .../sample-problems/Misc/FormulaAnswer.pg | 8 +- .../sample-problems/Misc/FormulaDomain.pg | 41 ++--- .../sample-problems/Misc/FormulaTestPoints.pg | 41 ++--- .../sample-problems/Misc/IframeEmbedding.pg | 16 +- .../Misc/ManyMultipleChoice.pg | 22 +-- tutorial/sample-problems/Misc/Matching.pg | 36 ++-- tutorial/sample-problems/Misc/MatchingAlt.pg | 78 ++++---- .../sample-problems/Misc/MatchingGraphs.pg | 149 ++++++++-------- .../Misc/MultipleChoiceCheckbox.pg | 32 ++-- .../Misc/MultipleChoicePopup.pg | 29 +-- .../Misc/MultipleChoiceRadio.pg | 19 +- tutorial/sample-problems/Misc/RandomPerson.pg | 21 ++- tutorial/sample-problems/Misc/Scaffolding.pg | 13 +- .../Parametric/ParametricEquationAnswers.pg | 47 +++-- .../Parametric/ParametricPlot.pg | 77 ++++---- .../sample-problems/Parametric/PolarGraph.pg | 46 ++--- .../Parametric/SpaceCurveGraph.pg | 22 ++- .../sample-problems/Parametric/Spacecurve.pg | 37 ++-- .../Parametric/SurfaceGraph.pg | 30 ++-- .../Parametric/VectorParametricDerivative.pg | 55 ++++-- .../Parametric/VectorParametricFunction.pg | 64 ++++--- .../Parametric/VectorParametricLines.pg | 41 +++-- .../AdaptiveParameters.pg | 13 +- .../ProblemTechniques/AnswerHints.pg | 89 ++++++++++ .../AnswerInExponent.pg | 15 +- .../AnswerIsSolutionToEquation.pg | 15 +- .../AnyAnswerMarkedCorrect.pg | 14 +- .../CalculatingWithPoints.pg | 70 ++++++++ .../ComposingFunctions.pg | 13 +- .../ProblemTechniques/ConstantsInProblems.pg | 58 ++++++ .../ProblemTechniques/CustomAnswerCheckers.pg | 70 ++++++++ .../CustomAnswerListChecker.pg | 133 ++++++++++++++ .../ProblemTechniques/DataTables.pg | 117 ++++++++++++ .../DefiningFunctions.pg} | 24 +-- .../DifferentiatingFormulas.pg | 62 +++++++ .../ProblemTechniques/DigitsTolType.pg | 98 ++++++++++ .../ProblemTechniques/DisableFunctions.pg | 102 +++++++++++ .../DraggableSubsets.pg | 39 ++-- .../ProblemTechniques/EquationEvaluators.pg | 103 +++++++++++ .../EquationsDefiningFunctions.pg | 32 ++-- .../ErrorMessageCustomization.pg | 11 +- .../ProblemTechniques/EvalVersusSubstitute.pg | 93 ++++++++++ .../ExtractingCoordinatesFromPoint.pg | 85 +++++++++ .../FactoringAndExpanding.pg | 106 +++++++++++ .../ProblemTechniques/FormattingDecimals.pg | 93 ++++++++++ .../FormulasToConstants.pg | 31 ++-- .../ProblemTechniques/GraphsInTables.pg | 140 +++++++++++++++ .../ProblemTechniques/HtmlLinks.pg | 72 ++++++++ .../ProblemTechniques/Images.pg | 97 ++++++++++ .../ProblemTechniques/InequalityEvaluators.pg | 66 +++++++ .../ProblemTechniques/IntervalEvaluators.pg | 60 +++++++ .../ProblemTechniques/Knowls.pg | 56 ++++++ .../ProblemTechniques/LayoutTable.pg | 131 ++++++++++++++ .../ProblemTechniques/Multianswer.pg | 107 +++++++++++ .../ProblemTechniques/NumericalTolerance.pg | 89 ++++++++++ .../ProblemTechniques/OtherVariables.pg | 60 +++++++ .../Percent.pg | 14 +- .../RandomFunction.pg | 20 ++- .../RestrictAnswerToFraction.pg | 83 +++++++++ .../RestrictingFunctions.pg | 11 +- .../SimplePopUp.pg | 24 +-- .../ProblemTechniques/StaticImages.pg | 55 ++++++ .../StringsInContext.pg | 41 +++-- .../ProblemTechniques/TikZImages.pg | 102 +++++++++++ .../WeightedGrader.pg | 23 ++- .../image.png | Bin .../ProblemTechniques/local.html | 5 + tutorial/sample-problems/README.md | 2 +- .../Sequences/AnswerOrderedList.pg | 20 +-- .../Sequences/ExplicitSequence.pg | 26 +-- .../Sequences/RecursiveSequence.pg | 32 ++-- .../sample-problems/Sequences/SeriesTest.pg | 125 ++++++++----- .../CommentsForInstructors.pg | 9 +- .../Statistics/LinearRegression.pg | 66 +++++++ .../sample-problems/Statistics/MeanStdDev.pg | 73 ++++++++ .../Statistics/linearRegression.pg | 68 ------- .../sample-problems/Statistics/meanStdDev.pg | 64 ------- .../Trig/DisableFunctionsTrig.pg | 42 ++--- .../sample-problems/Trig/DraggableIdentity.pg | 31 ++-- .../sample-problems/Trig/PeriodicAnswers.pg | 18 +- .../Trig/ProvingTrigIdentities.pg | 48 +++-- .../sample-problems/Trig/SpecialTrigValues.pg | 24 +-- tutorial/sample-problems/Trig/TrigDegrees.pg | 38 ++-- .../sample-problems/Trig/TrigIdentities.pg | 31 ++-- .../VectorCalc/CylindricalGraph3D.pg | 78 +++++--- .../VectorCalc/DirectionField.pg | 73 ++++---- .../VectorCalc/RectangularGraph3D.pg | 37 ++-- .../VectorCalc/VectorFieldGraph2D.pg | 61 ++++--- .../VectorFieldGraph3D/VectorFieldGraph3D1.pg | 52 +++--- .../VectorCalc/VectorLineSegment1.pg | 57 +++--- .../VectorCalc/VectorLineSegment2.pg | 29 +-- .../VectorCalc/VectorOperations.pg | 54 +++--- .../VectorCalc/VectorParametricLine.pg | 24 +-- .../sample-problems/VectorCalc/Vectors.pg | 106 +++++------ .../problem-techniques/AnswerHints.pg | 90 ---------- .../CalculatingWithPoints.pg | 72 -------- .../problem-techniques/ConstantsInProblems.pg | 57 ------ .../CustomAnswerCheckers.pg | 76 -------- .../CustomAnswerListChecker.pg | 106 ----------- .../problem-techniques/DataTables.pg | 91 ---------- .../DifferentiatingFormulas.pg | 58 ------ .../problem-techniques/DigitsTolType.pg | 88 --------- .../problem-techniques/DisableFunctions.pg | 93 ---------- .../problem-techniques/EquationEvaluators.pg | 109 ------------ .../EvalVersusSubstitute.pg | 89 ---------- .../ExtractingCoordinatesFromPoint.pg | 72 -------- .../FactoringAndExpanding.pg | 100 ----------- .../problem-techniques/FormattingDecimals.pg | 94 ---------- .../problem-techniques/GraphsInTables.pg | 141 --------------- .../problem-techniques/HtmlLinks.pg | 69 -------- .../problem-techniques/Images.pg | 84 --------- .../InequalityEvaluators.pg | 67 ------- .../problem-techniques/IntervalEvaluators.pg | 59 ------- .../problem-techniques/Knowls.pg | 46 ----- .../problem-techniques/LayoutTable.pg | 103 ----------- .../problem-techniques/Multianswer.pg | 110 ------------ .../problem-techniques/NumericalTolerance.pg | 76 -------- .../problem-techniques/OtherVariables.pg | 54 ------ .../RestrictAnswerToFraction.pg | 68 ------- .../problem-techniques/StaticImages.pg | 69 -------- .../problem-techniques/TikZImages.pg | 89 ---------- .../problem-techniques/local.html | 4 - 178 files changed, 4914 insertions(+), 4402 deletions(-) rename tutorial/sample-problems/Algebra/{AnswerUpToMultiplication.pg => AnswerUpToMultiple.pg} (89%) create mode 100644 tutorial/sample-problems/Algebra/NoSolution.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/AdaptiveParameters.pg (89%) create mode 100644 tutorial/sample-problems/ProblemTechniques/AnswerHints.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/AnswerInExponent.pg (76%) rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/AnswerIsSolutionToEquation.pg (64%) rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/AnyAnswerMarkedCorrect.pg (67%) create mode 100644 tutorial/sample-problems/ProblemTechniques/CalculatingWithPoints.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/ComposingFunctions.pg (70%) create mode 100644 tutorial/sample-problems/ProblemTechniques/ConstantsInProblems.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/CustomAnswerCheckers.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/CustomAnswerListChecker.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/DataTables.pg rename tutorial/sample-problems/{problem-techniques/AddingFunctions.pg => ProblemTechniques/DefiningFunctions.pg} (51%) create mode 100644 tutorial/sample-problems/ProblemTechniques/DifferentiatingFormulas.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/DigitsTolType.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/DisableFunctions.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/DraggableSubsets.pg (55%) create mode 100644 tutorial/sample-problems/ProblemTechniques/EquationEvaluators.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/EquationsDefiningFunctions.pg (60%) rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/ErrorMessageCustomization.pg (77%) create mode 100644 tutorial/sample-problems/ProblemTechniques/EvalVersusSubstitute.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/ExtractingCoordinatesFromPoint.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/FactoringAndExpanding.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/FormattingDecimals.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/FormulasToConstants.pg (52%) create mode 100644 tutorial/sample-problems/ProblemTechniques/GraphsInTables.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/HtmlLinks.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/Images.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/InequalityEvaluators.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/IntervalEvaluators.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/Knowls.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/LayoutTable.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/Multianswer.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/NumericalTolerance.pg create mode 100644 tutorial/sample-problems/ProblemTechniques/OtherVariables.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/Percent.pg (57%) rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/RandomFunction.pg (59%) create mode 100644 tutorial/sample-problems/ProblemTechniques/RestrictAnswerToFraction.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/RestrictingFunctions.pg (67%) rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/SimplePopUp.pg (71%) create mode 100644 tutorial/sample-problems/ProblemTechniques/StaticImages.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/StringsInContext.pg (51%) create mode 100644 tutorial/sample-problems/ProblemTechniques/TikZImages.pg rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/WeightedGrader.pg (55%) rename tutorial/sample-problems/{problem-techniques => ProblemTechniques}/image.png (100%) create mode 100644 tutorial/sample-problems/ProblemTechniques/local.html rename tutorial/sample-problems/{snippets => Snippets}/CommentsForInstructors.pg (67%) create mode 100644 tutorial/sample-problems/Statistics/LinearRegression.pg create mode 100644 tutorial/sample-problems/Statistics/MeanStdDev.pg delete mode 100644 tutorial/sample-problems/Statistics/linearRegression.pg delete mode 100644 tutorial/sample-problems/Statistics/meanStdDev.pg delete mode 100644 tutorial/sample-problems/problem-techniques/AnswerHints.pg delete mode 100644 tutorial/sample-problems/problem-techniques/CalculatingWithPoints.pg delete mode 100644 tutorial/sample-problems/problem-techniques/ConstantsInProblems.pg delete mode 100644 tutorial/sample-problems/problem-techniques/CustomAnswerCheckers.pg delete mode 100644 tutorial/sample-problems/problem-techniques/CustomAnswerListChecker.pg delete mode 100644 tutorial/sample-problems/problem-techniques/DataTables.pg delete mode 100644 tutorial/sample-problems/problem-techniques/DifferentiatingFormulas.pg delete mode 100644 tutorial/sample-problems/problem-techniques/DigitsTolType.pg delete mode 100644 tutorial/sample-problems/problem-techniques/DisableFunctions.pg delete mode 100644 tutorial/sample-problems/problem-techniques/EquationEvaluators.pg delete mode 100644 tutorial/sample-problems/problem-techniques/EvalVersusSubstitute.pg delete mode 100644 tutorial/sample-problems/problem-techniques/ExtractingCoordinatesFromPoint.pg delete mode 100644 tutorial/sample-problems/problem-techniques/FactoringAndExpanding.pg delete mode 100644 tutorial/sample-problems/problem-techniques/FormattingDecimals.pg delete mode 100644 tutorial/sample-problems/problem-techniques/GraphsInTables.pg delete mode 100644 tutorial/sample-problems/problem-techniques/HtmlLinks.pg delete mode 100644 tutorial/sample-problems/problem-techniques/Images.pg delete mode 100644 tutorial/sample-problems/problem-techniques/InequalityEvaluators.pg delete mode 100644 tutorial/sample-problems/problem-techniques/IntervalEvaluators.pg delete mode 100644 tutorial/sample-problems/problem-techniques/Knowls.pg delete mode 100644 tutorial/sample-problems/problem-techniques/LayoutTable.pg delete mode 100644 tutorial/sample-problems/problem-techniques/Multianswer.pg delete mode 100644 tutorial/sample-problems/problem-techniques/NumericalTolerance.pg delete mode 100644 tutorial/sample-problems/problem-techniques/OtherVariables.pg delete mode 100644 tutorial/sample-problems/problem-techniques/RestrictAnswerToFraction.pg delete mode 100644 tutorial/sample-problems/problem-techniques/StaticImages.pg delete mode 100644 tutorial/sample-problems/problem-techniques/TikZImages.pg delete mode 100644 tutorial/sample-problems/problem-techniques/local.html diff --git a/lib/SampleProblemParser.pm b/lib/SampleProblemParser.pm index 2123ac5093..6f255d3410 100644 --- a/lib/SampleProblemParser.pm +++ b/lib/SampleProblemParser.pm @@ -91,7 +91,7 @@ sub parseSampleProblem ($file, %global) { @code_rows = (); } elsif ($row =~ /^#:/) { # This section is documentation to be parsed. - $row = $row =~ s/^#://r; + $row = $row =~ s/^#:\s?//r; # Parse any LINK/PODLINK/PROBLINK commands in the documentation. if ($row =~ /(POD|PROB)?LINK\('(.*?)'\s*(,\s*'(.*)')?\)/) { diff --git a/tutorial/sample-problems/Algebra/AlgebraicFractionAnswer.pg b/tutorial/sample-problems/Algebra/AlgebraicFractionAnswer.pg index a7b9cbe1de..156d7dfba6 100644 --- a/tutorial/sample-problems/Algebra/AlgebraicFractionAnswer.pg +++ b/tutorial/sample-problems/Algebra/AlgebraicFractionAnswer.pg @@ -17,44 +17,40 @@ #:% categories = [fraction] #:% section = preamble -#: We include the macros file `niceTables.pl` to be able to display the answer boxes -#: on top of each other (as a fraction). +#: Load the PODLINK('parserMultiAnswer.pl') macro to be able to consider two +#: answer rules together (the numerator and denominator) in the answer checker. +#: Note that the PODLINK('niceTables.pl') macro is implicitly used, and is +#: automatically loaded by the `PGML.pl` macro. DOCUMENT(); -loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'niceTables.pl', 'parserMultiAnswer.pl', - 'PGcourse.pl' -); +loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); #:% section = setup -#: We define MathObjects formulas `$num` and `$den` that are the correct -#: numerator and denominator for the answer, as well as some bogus answers -#: `$numbogus` and `$denbogus` that result from not finding a common -#: denominator. We use `MultiAnswer` to manipulate both student answers at -#: the same time. In `$multians` we allow for answers to be left blank, -#: which requires one of two things: either we disable the error message or -#: do type checking on the students input by using `ref($f1) eq ref($f1stu)` -#: to see if the correct numerator `$f1` and the student numerator `$f1stu` -#: have the same type. We used the code -#: `Context()->{error}{msg}{"Operands of '*' can't be words"} = ' ';` to -#: disable the error message because this method allows the -#: "Simplify your answer" feature to work more reliably. We also allow for -#: the student to enter the fraction as either -#: `(6y-3)/(y-2)` or `(3-6y)/(2-y)`, since both are correct and it is not -#: clear that one is preferable to the other, which requires that we check -#: `$f1 == $f1stu || -$f1 == $f1stu`. Here `||` is perl's "or" operator. We -#: provide some custom answer hints by testing for bogus numerators and -#: denominators and displaying answer messages via -#: `$self->setMessage(1, "Simplify your answer further");`, -#: where the 1 stands for the first answer blank. +#: Define MathObject formulas `$num` and `$den` which are the correct numerator +#: and denominator for the answer, as well as the bogus answers `$numbogus` and +#: `$denbogus` that result from not finding a common denominator (used in the +#: custom answer checker). A `MultiAnswer` is used to check the numerator and +#: denominator together. +#: +#: The `allowBlankAnswers => 1` option for the `MultiAnswer` object is set which +#: allows the answers to be left blank, so that partial credit can be given if +#: the student has the numerator or denominator correct but does not enter both. +#: This requires that the type of the students input be checked before using +#: those values in computations or warnings will be issued (in this case the +#: warning "Operands of '*' can't be words" is issued if +#: `$f1 * $f2stu == $f1stu * $f2` is computed). This is done for the +#: numerator, for example, with `Value::classMatch($f1stu, 'Formula')`. +#: +#: The student is also allowed to enter the fraction as either `(6y-3)/(y-2)` or +#: `(3-6y)/(2-y)`, since both are correct and it is not clear that one is +#: preferable to the other. For this the check `$f1 == $f1stu || -$f1 == $f1stu` +#: is used. Note that `||` is perl's "or" operator. #: -#: The fraction answer is created using a `LayoutTable` from `niceTables.pl`. -#: The outer `LayoutTable` has a single row with the mathematical expression -#: and then another `LayoutTable` that formats the fraction with a bottom -#: horizontal line. The padding is changed to improve the look of the fraction. +#: Custom answer messages can be displayed by calling the `setMessage` method of +#: the `MultiAnswer` object that `$self` refers to. For example, with +#: `$self->setMessage(1, "Simplify your answer further")`, +#: where 1 means to set the message for the first answer blank. Context()->variables->are(y => 'Real'); -Context()->{error}{msg}{"Operands of '*' can't be words"} = ' '; do { $a = random(2, 8, 2); @@ -69,12 +65,11 @@ $numbogus = Formula("$a*y+$b"); $denbogus = Formula("(y-$c)*($c-y)"); $multians = MultiAnswer($num, $den)->with( - singleResult => 0, allowBlankAnswers => 1, checker => sub { my ($correct, $student, $self) = @_; - my ($f1stu, $f2stu) = @{$student}; - my ($f1, $f2) = @{$correct}; + my ($f1stu, $f2stu) = @$student; + my ($f1, $f2) = @$correct; if (($f1 == $f1stu && $f2 == $f2stu) || (-$f1 == $f1stu && -$f2 == $f2stu)) @@ -90,7 +85,10 @@ $multians = MultiAnswer($num, $den)->with( return [ 0, 0 ]; } elsif ($f2 == $f2stu || -$f2 == $f2stu) { return [ 0, 1 ]; - } elsif ($f1 * $f2stu == $f1stu * $f2) { + } elsif (Value::classMatch($f1stu, 'Formula') + && Value::classMatch($f2stu, 'Formula') + && $f1 * $f2stu == $f1stu * $f2) + { $self->setMessage(1, "Simplify your answer further"); $self->setMessage(2, "Simplify your answer further"); return [ 0, 0 ]; @@ -100,32 +98,34 @@ $multians = MultiAnswer($num, $den)->with( } ); -$frac = LayoutTable( - [ [ - "\(\displaystyle\frac{$a y}{y-$c} + \frac{$b}{$c - y}=\)", - LayoutTable( - [ [ [ ans_rule(4), bottom => 1 ] ], [ ans_rule(4) ], ], - padding => [ 0.5, 0 ], - ) - ] ], - padding => [ 0, 0.5 ], - valign => 'middle', -); - #:% section = statement -#: Everything is as usual. Insert the fraction and answer blanks using `$showfraction`. +#: The fraction answer is created using a `LayoutTable` from +#: PODLINK('niceTables.pl') via its `PGML` syntax. A `LayoutTable` is started +#: with `[#` and is ended with `#]*`. Options for the table are set in braces +#: after the ending `#]*`. Cells of the table are started wtih `[.` and ended +#: with `.]`. Options for a cell (some of which apply to the row as a whole) +#: are set in braces after the cell's ending `.]`. Rows of the table are ended +#: by a starred cell. For example `[. ... .]*`. Note that the second cell of +#: this table contains a nested `LayoutTable`. +#: +#: The outer `LayoutTable` has a single row with the mathematical expression and +#: then another `LayoutTable` with two rows that formats the fraction with a +#: bottom horizontal line under the first row. The padding is changed to improve +#: the look of the fraction. BEGIN_PGML Perform the indicated operations. Express your answer in reduced form. -[$frac]* - +[# + [. [``\frac{[$a]y}{y - [$c]} + \frac{[$b]}{[$c] - y} =``] .] + [. + [# + [. [_]{$multians} .]*{ bottom => 1 } + [. [_]{$multians} .] + #]*{ padding => [ 0.5, 0 ] } + .] +#]*{ padding => [ 0, 0.5 ], valign => 'middle' } END_PGML -#:% section = answer -#: It is necessary to use the answer evaluator `ANS` since -#: `ans_rule` was used to produce answer blanks. -ANS($multians->cmp()); - #:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. diff --git a/tutorial/sample-problems/Algebra/AnswerBlankInExponent.pg b/tutorial/sample-problems/Algebra/AnswerBlankInExponent.pg index f140382582..6df2a9d538 100644 --- a/tutorial/sample-problems/Algebra/AnswerBlankInExponent.pg +++ b/tutorial/sample-problems/Algebra/AnswerBlankInExponent.pg @@ -1,3 +1,4 @@ +## Please see file perltidy.ERR ## DESCRIPTION ## Answer blank in the exponent ## ENDDESCRIPTION @@ -22,11 +23,12 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: We want the only variables to be `a` and `b` and choose a random power. +#: Set the context to allow only the variables `a` and `b` and choose a random +#: exponent. #: #: The exponential layout is in HTML using a pair of adjacent `span` elements #: with the right one shifted up using the CSS style `vertical-align`. -#: In hardcopy mode, we use the LaTeX exponent. +#: In hardcopy mode, a LaTeX exponent is used. Context()->variables->are(a => 'Real', b => 'Real'); $n = random(3, 9); @@ -39,19 +41,22 @@ $base = Formula("a*b"); $exponent = Formula("$n"); # Display exponents nicely -$exp = MODES( - HTML => "\(\displaystyle $expression= \Big(\)" +if ($displayMode eq 'TeX') { + $exp = + "\( \displaystyle $expression = (" + . ans_rule(4) . ")^{" + . ans_rule(4) . "}\)"; +} else { + $exp = + "\(\displaystyle $expression = \Big(\)" . ans_rule(4) . '\(\Big)\)' . ans_rule(4) - . '', - TeX => "\( \displaystyle $expression = (" - . ans_rule(4) . ")^{" - . ans_rule(4) . "}\)" -); + . ''; +} #:% section = statement -#: We insert exponential stored as `$exp`. +#: Insert the exponential stored as `$exp`. BEGIN_PGML Rewrite the following using a single exponent. @@ -59,10 +64,10 @@ Rewrite the following using a single exponent. END_PGML #:% section = answer -#: Because the answer blanks are traditional ans_rule, then we need to use this -#: style of answer checking. -ANS($base->cmp()); -ANS($exponent->cmp()); +#: It is necessary to install the answer evaluator with `ANS` +#: since `ans_rule` was used to produce answer blanks. +ANS($base->cmp); +ANS($exponent->cmp); #:% section = solution BEGIN_PGML_SOLUTION diff --git a/tutorial/sample-problems/Algebra/AnswerUpToMultiplication.pg b/tutorial/sample-problems/Algebra/AnswerUpToMultiple.pg similarity index 89% rename from tutorial/sample-problems/Algebra/AnswerUpToMultiplication.pg rename to tutorial/sample-problems/Algebra/AnswerUpToMultiple.pg index fdc3758d9f..abb00d4437 100644 --- a/tutorial/sample-problems/Algebra/AnswerUpToMultiplication.pg +++ b/tutorial/sample-problems/Algebra/AnswerUpToMultiple.pg @@ -14,7 +14,7 @@ #:% name = Answer up to a Constant Multiple #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [answer, adaptive parameters] +#:% categories = [answers, adaptive parameters] #:% section = preamble DOCUMENT(); @@ -24,8 +24,7 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup #: The answer checker uses a local context with an adaptive parameter to check #: if the student answer is a parameter `C0` multiple of the correct answer. -#: For more on adaptive parameters, see -#: [AdaptiveParameters](../problem-techniques/AdaptiveParameters.html). +#: For more on adaptive parameters, see PROBLINK('AdaptiveParameters.pg'). $ans = Compute('(x + 1)(x - 2)')->cmp( checker => sub { diff --git a/tutorial/sample-problems/Algebra/DomainRange.pg b/tutorial/sample-problems/Algebra/DomainRange.pg index 4199484de9..e6814ab60b 100644 --- a/tutorial/sample-problems/Algebra/DomainRange.pg +++ b/tutorial/sample-problems/Algebra/DomainRange.pg @@ -17,42 +17,42 @@ #:% categories = [domain] #:% section = preamble -#: The `contextInequalities.pl` macro is used for inequality answers. +#: The PODLINK('contextInequalities.pl') macro is used for inequality answers. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'contextInequalities.pl', 'PGcourse.pl'); #:% section = setup -#: Different contexts can be used for different answers in a problem. -#: Calling Context('Inequalities-Only') creates a new instance of the context. -#: The two contexts in this problem have different variables defined. The -#: first only has the variable "x", and the second only has the variable "y". -#: Note that calling `->variables->are(x => 'Real')` for the first instance is -#: actually unnecessary since "x" is the only variable in the context by -#: default. It is only shown here for emphasis. - -#: For the first part, the "Inequalities-Only" context is used. That context -#: requires students to enter their answer using inequalities. If the -#: "Inequalities" context had been used instead, then students would also be -#: able to enter answers using interval notation. For more details, please see -#: `contextInequalities.pl`. +#: In addition to showing how to use the PODLINK('contextInequalities.pl') +#: macro, this example problem demonstrates how different contexts can be used +#: for different answers in a problem. #: -#: Calling Context('Inequalities-Only') creates a new instance of the context. -#: The first two contexts in this problem have different variables defined. The -#: first only has the variable "x", and the second only has the variable "y". -#: Note that calling `->variables->are(x => 'Real')` for the first instance is -#: actually unnecessary since "x" is the only variable in the context by -#: default. It is only shown here for emphasis. +#: First, `Context('Inequalities-Only')->variables->are(x => 'Real')` is called, +#: and this creates a new context instance. This instance only has the variable +#: `x`. Note that calling `->variables->are(x => 'Real')` for this instance is +#: actually unnecessary since `x` is the only variable in the context by +#: default. It is only shown here for emphasis. The `$domain` is computed in +#: this context. +#: +#: Next, `Context('Inequalities-Only')->variables->are(y => 'Real')` is called, +#: and this creates another context instance. This instance only has the +#: variable `y`. The `$range` is computed in this context. +#: +#: Note that the "Inequalities-Only" requires students to enter their answer +#: using inequalities. The more general "Inequalities" context provided in the +#: PODLINK('contextInequalities.pl') macro, also allows answers to be entered +#: using interval notation. For more details, please see +#: PODLINK('contextInequalities.pl'). #: #: Setting the context flag `formatStudentAnswer => 'parsed'` insists that the #: `parsed` student answers be displayed and no further reduction or evaluation -#: is done. Generally this means the student answer is displayed much as it is -#: entered. In particular in this instance it prevents the student's answer +#: be done. Generally this means the student answer is displayed much as it is +#: entered. In particular in this instance it prevents the student's answer #: from being reduced to a decimal. #: -#: For the second part, the "Interval" context is used instead. Change to this -#; context by calling `Context('Interval')`. Note that `inf` is built-in for -#: infinte intervals. +#: For the last part, the "Interval" context is used instead. This context is +#: changed to by calling `Context('Interval')`. Note that `inf` is built-in for +#: infinite intervals. $a = random(1, 6); Context('Inequalities-Only')->variables->are(x => 'Real'); @@ -72,7 +72,7 @@ $range_interval = Compute('[0, inf)'); #:% section = statement BEGIN_PGML -Suppose [`f(x) = \sqrt(x - [$a])`]. +Suppose [`f(x) = \sqrt{x - [$a]}`]. Enter inequalities for the domain and range of [`f`]. @@ -82,9 +82,9 @@ Range: [_]{$range}{15} Use interval notation to give the domain and range of [`f`]. -Domain: [____]{$domain_interval} +Domain: [_]{$domain_interval}{15} -Range: [____]{$range_interval} +Range: [_]{$range_interval}{15} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Algebra/DynamicGraph.pg b/tutorial/sample-problems/Algebra/DynamicGraph.pg index e3751968c4..9245446dab 100644 --- a/tutorial/sample-problems/Algebra/DynamicGraph.pg +++ b/tutorial/sample-problems/Algebra/DynamicGraph.pg @@ -23,23 +23,24 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); #:% section = setup -#: The code between `$graph->BEGIN_TIKZ` and `END_TIKZ` are tikz commands. Information -#: on tikz can be found at the [homepage for tikz](https://tikz.dev) and details on -#: using tikz within pg problems can be found in PODLINK('PGtikz.pl'). +#: The code between `$graph->BEGIN_TIKZ` and `END_TIKZ` are TikZ commands. +#: Information on TikZ can be found at the [homepage for TikZ](https://tikz.dev) +#: and details on using TikZ within pg problems can be found in +#: PODLINK('PGtikz.pl'). #: #: This problem creates a parabola with random intercepts. #: -#: Some notes about the command in the `TIKZ` block: +#: Some notes about the commands in the `BEGIN_TIKZ/END_TIKZ` block: #: #: * The first `\filldraw` command produces a frame around the plotting region #: * The first two `\draw` commands draw the axes as well as the axis labels. -#: The `->` gives the lines arrows in that direction and the `thick` makes -#: the lines a bit thicker. +#: The `->` gives the lines arrows in that direction and the `thick` makes +#: the lines a bit thicker. #: * The two `\foreach` commands produce the tick marks and labels. #: * The last `\draw` command produces the graph of the function. The `domain` -#: option gives the plotting domain and the `smooth` attempts to make the -#: resulting graph smooth. Lastly, the function itself needs to be in -#: `{}` in order for the function to be computed correctly. +#: option gives the plotting domain and the `smooth` attempts to make the +#: resulting graph smooth. Lastly, the function itself needs to be in +#: `{}` in order for the function to be computed correctly. $a = random(1, 4); # negative of left x-intercept $b = random(2, 4); # right x-intercept $c = random(2, 6); # y-intercept @@ -49,31 +50,41 @@ $k = -$c / ($a * $b); $graph = createTikZImage(); $graph->tikzLibraries('arrows.meta'); $graph->BEGIN_TIKZ -\tikzset{>={Stealth[scale=1.5]}} +\tikzset{>={Stealth[scale = 1.5]}} \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-6,7) rectangle (6,-1); -\draw[->,thick] (-6,0) -- (6,0) node[above left,outer sep=3pt] {\(x\)}; -\foreach \x in {-5,...,-1,1,2,...,5} - \draw(\x,5pt) -- (\x,-5pt) node [below] {\(\x\)}; -\draw[->,thick] (0,-1) -- (0,7) node[below right,outer sep=3pt] {\(y\)}; -\foreach \y in {1,...,6} - \draw (5pt,\y) -- (-5pt,\y) node[left] {\(\y\)}; -\draw[blue,ultra thick] plot[domain=-6:6,smooth] (\x,{$k*(\x+$a)*(\x-$b)}); + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-6, 7) rectangle (6, -1); +\draw[->, thick] (-6, 0) -- (6, 0) node[above left, outer sep = 3pt] {\(x\)}; +\foreach \x in {-5, ..., -1, 1, 2, ..., 5} + \draw(\x, 5pt) -- (\x, -5pt) node [below] {\(\x\)}; +\draw[->, thick] (0, -1) -- (0, 7) node[below right, outer sep = 3pt] {\(y\)}; +\foreach \y in {1, ..., 6} + \draw (5pt, \y) -- (-5pt, \y) node[left] {\(\y\)}; +\draw[blue, ultra thick] + plot[domain = -6:6, smooth] (\x, {$k * (\x + $a) * (\x-$b)}); END_TIKZ +$altText = + "Graph of a downward opening parabola. " + . "The graph is at a height of $c when x is 0, " + . "and is at a height of 0 when x is -$a or $c."; + #:% section = statement -#: Note that the tikz graph in `$graph` to be shown is placed in the `image` function -#: and since this is a function, it must go in a `[@ ... @]*` block. +#: Note that the TikZ graph in `$graph` is inserted into the problem text using +#: the PGML image syntax which is `[!alt text!]{image object}`. For +#: accessibility purposes, all images inserted into a problem should be provided +#: with alternate text that provides screen reader users the information they +#: would need to work the problem. BEGIN_PGML Use the graph to find the missing values. There may be more than one correct answer, in which case you should enter your answers as a comma separated list. If there are no correct answers, enter NONE. -[@ image($graph, width => 400, tex_size => 600) @]* +[![$altText]!]{$graph}{400} a) [`f(0) =`] [__]{$c} diff --git a/tutorial/sample-problems/Algebra/EquationDefiningFunction.pg b/tutorial/sample-problems/Algebra/EquationDefiningFunction.pg index 424b3d5085..865244c51a 100644 --- a/tutorial/sample-problems/Algebra/EquationDefiningFunction.pg +++ b/tutorial/sample-problems/Algebra/EquationDefiningFunction.pg @@ -15,17 +15,18 @@ #:% name = Answer is an Equation #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [equation, function, answers] +#:% categories = [equations, functions, answers] #:% section = preamble -#: We need to include the macro file `parserAssignment.pl`. +#: The macro file PODLINK('parserAssignment.pl') needs to be included. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserAssignment.pl', 'PGcourse.pl'); #:% section = setup -#: We must allow assignment, and declare any function names we wish to use. For -#: more details and examples in other MathObjects contexts, see +#: To allow assignments in the current context call `parser::Assignment->Allow`. +#: To allow students to enter an assignment in which the left side is a function +#: declaration call `parser::Assignment->Function('f')`. For more details, see #: PODLINK('parserAssignment.pl'). Context()->variables->are(x => 'Real', y => 'Real'); parser::Assignment->Allow; diff --git a/tutorial/sample-problems/Algebra/EquationImplicitFunction.pg b/tutorial/sample-problems/Algebra/EquationImplicitFunction.pg index 41f0a5fc6e..a2c0d3ba49 100644 --- a/tutorial/sample-problems/Algebra/EquationImplicitFunction.pg +++ b/tutorial/sample-problems/Algebra/EquationImplicitFunction.pg @@ -17,7 +17,7 @@ #:% categories = [implicit function] #:% section = preamble -#: The macro `parserImplicitEquation.pl` allows the entry of equations. +#: The macro PODLINK('parserImplicitEquation.pl') allows the entry of equations. DOCUMENT(); loadMacros( @@ -26,16 +26,17 @@ loadMacros( ); #:% section = setup -#: We quash some error messages by redefining them to be a blank string ' ' (notice -#: the space). Since the circle will always be contained in a rectangle with two -#: opposite corners at (-4, -4) and (10, 10), we set the limits for the variables x -#: and y to be outside of this rectangle. The ImplicitEquation object allows us to -#: specify as many solutions as we like, and doing so should improve the accuracy of -#: the answer evaluator. +#: Some error messages are prevented by redefining them to be a blank string ' ' +#: (notice the space). Since the circle defined by the implicit equation in this +#: problem will always be contained in a rectangle with two opposite corners at +#: the points `(-4, -4)` and `(10, 10)`, the limits for the variables `x` and +#: `y` are set to to contain this rectangle. The `ImplicitEquation` object +#: allows any number of solutions to be specified. Specifying additional +#: solutions should improve the accuracy of the answer evaluator. #: -#: If your equation is linear of the form `x = 3`, `4x + 3y = 12`, or -#: `4x + 3y + 5z = 21` for example, you should use the `parserImplicitPlane.pl` -#: context and answer evaluator instead. +#: Note that if the equation is linear, for example of the form `x = 3`, +#: `4x + 3y = 12`, or `4x + 3y + 5z = 21`, then the +#: PODLINK('parserImplicitPlane.pl') macro should be used instead. Context('ImplicitEquation'); Context()->{error}{msg}{"Can't find any solutions to your equation"} = ' '; Context()->{error}{msg}{"Can't generate enough valid points for comparison"} = @@ -64,8 +65,8 @@ $answer = ImplicitEquation( #:% section = statement BEGIN_PGML -Enter an equation for a circle in the [`xy`]-plane -of radius [`[$r]`] centered at [`[$p]`]. +Enter an equation for a circle in the [`xy`]-plane of radius [`[$r]`] centered +at [`[$p]`]. [_]{$answer}{25} END_PGML diff --git a/tutorial/sample-problems/Algebra/ExpandedPolynomial.pg b/tutorial/sample-problems/Algebra/ExpandedPolynomial.pg index 33a8811d6c..512c385b50 100644 --- a/tutorial/sample-problems/Algebra/ExpandedPolynomial.pg +++ b/tutorial/sample-problems/Algebra/ExpandedPolynomial.pg @@ -16,10 +16,10 @@ #:% name = Expanded Polynomial #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [polynomial] +#:% categories = [polynomials] #:% section = preamble -#: We must load `contextLimitedPolynomial.pl` +#: Load the PODLINK('contextLimitedPolynomial.pl') macro. DOCUMENT(); loadMacros( @@ -28,21 +28,25 @@ loadMacros( ); #:% section = setup -#: The macro contextLimitedPolynomial.pl provides two contexts: +#: The PODLINK('contextLimitedPolynomial.pl') macro provides two contexts: #: -#:```{#contexts .perl} +#: ```{#contexts .perl} #: Context('LimitedPolynomial'); #: Context('LimitedPolynomial-Strict'); -#:``` -#: The strict version does not allow any mathematical operations within coefficients, -#: so `(5+3)x` must be simplified to `8x`. For more details, see PODLINK('contextLimitedPolynomial.pl'). +#: ``` +#: The strict version does not allow any mathematical operations within +#: coefficients, so `(5+3)x` must be simplified to `8x`. For more details, see +#: PODLINK('contextLimitedPolynomial.pl'). #: -#: We use the LimitedPolynomial-Strict context, construct the coefficients $b and $c -#: as Perl reals, and then construct $expandedform using these pre-computed coefficients. -#: This is because the `LimitedPolynomial-Strict` context balks at answers that are not -#: already simplified completely. Notice that we called the `->reduce()` method on the -#: expanded form of the polynomial, which will ensure that the polynomial will be -#: displayed as `x^2 - 6x + 4` instead of `x^2 + -6x + 4`. +#: Switch to the `LimitedPolynomial-Strict` context, construct the coefficients +#: `$b` and `$c`, and then construct `$expandedform` using these coefficients. +#: Note that the coefficients must be provided as simplified numeric values +#: because the `LimitedPolynomial-Strict` context will not accept answers that +#: are not already simplified completely. That is done here by computing those +#: values in Perl before using them in the formula definition. Notice that the +#: `reduce` method is called for the expanded form of the polynomial, which +#: ensures that the polynomial will be displayed as `x^2 - 6x + 4` instead of +#: `x^2 + -6x + 4`. Context('Numeric'); $h = 3; $k = 5; @@ -55,8 +59,8 @@ $c = $h**2 - $k; $expandedform = Formula("x^2 + $b x + $c")->reduce(); #:% section = statement -#: To help students understand how to format their answers, we give an example -#: `ax^2+bx+c` of what the answer should look like. +#: The example form `ax^2+bx+c` for the answer is given to help students +#: understand how to format their answers. BEGIN_PGML The quadratic expression [`[$vertexform]`] is written in vertex form. Write the expression in expanded form [`ax^2 + bx + c`]. diff --git a/tutorial/sample-problems/Algebra/FactoredPolynomial.pg b/tutorial/sample-problems/Algebra/FactoredPolynomial.pg index 195d627d24..49718f614f 100644 --- a/tutorial/sample-problems/Algebra/FactoredPolynomial.pg +++ b/tutorial/sample-problems/Algebra/FactoredPolynomial.pg @@ -15,10 +15,11 @@ #:% name = Factored Polynomial #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [polynomial] +#:% categories = [polynomials] #:% section = preamble -#: We require additional contexts provided by `contextPolynomialFactors.pl` and `contextLimitedPowers.pl` +#: Additional contexts provided by the PODLINK('contextPolynomialFactors.pl') +#: and PODLINK('contextLimitedPowers.pl') macros are needed. DOCUMENT(); loadMacros( @@ -28,16 +29,19 @@ loadMacros( ); #:% section = setup -#: For the factored form we need to change to the `PolynomialFactors-Strict` context -#: and restrict the allowed powers to either 0 or 1 using the `LimitedPowers::OnlyIntegers` -#: block of code. Note: restricting all exponents to 0 or 1 means that repeated factors -#: will have to be entered in the form `k(ax+b)(ax+b)` instead of `k(ax+b)^2`. Also, -#: restricting all exponents to 0 or 1 means that the polynomial must factor as a -#: product of linear factors (no irreducible quadratic factors can appear). Of course, -#: we could allow exponents to be 0, 1, or 2, but then students would be allowed to -#: enter reducible quadratic factors. There are no restrictions on the coefficients, i.e., -#: the quadratic could have any nonzero leading coefficient. We set `singleFactors => 0` -#: so that repeated, non-simplified factors do not generate errors. +#: Before computing the answer which will be the factored form of the +#: polynomial, change to the `PolynomialFactors-Strict` context, and restrict +#: the allowed powers to only 0 and 1 using the `LimitedPowers::OnlyIntegers` +#: method. Note that restricting all powers to 0 or 1 means that repeated +#: factors will have to be entered in the form `k(ax+b)(ax+b)` instead of +#: `k(ax+b)^2`. Also, restricting all exponents to 0 or 1 means that the +#: polynomial must factor as a product of linear factors (no irreducible +#: quadratic factors can appear). If the exponents of 0, 1, or 2 were allowed, +#: then students would be allowed to enter reducible quadratic factors. There +#: are no restrictions on the coefficients, so the quadratic could have any +#: nonzero leading coefficient. Also set `singleFactors => 0` so that repeated, +#: non-simplified factors do not generate errors. + # Expanded form Context('Numeric'); $poly = Compute('8x^2 + 28x + 12'); @@ -53,7 +57,7 @@ LimitedPowers::OnlyIntegers( $factored = Compute('4(2x+1)(x+3)'); #:% section = statement -#: We should explicitly tell students to enter answers in the form `k(ax+b)(cx+d)`. +#: Explicitly inform students to enter answers in the form `k(ax+b)(cx+d)`. BEGIN_PGML Write the quadratic expression [`[$poly]`] in factored form [`k(ax+b)(cx+d)`]. diff --git a/tutorial/sample-problems/Algebra/FractionAnswer.pg b/tutorial/sample-problems/Algebra/FractionAnswer.pg index 8397d1d22c..94c94aea3a 100644 --- a/tutorial/sample-problems/Algebra/FractionAnswer.pg +++ b/tutorial/sample-problems/Algebra/FractionAnswer.pg @@ -23,23 +23,24 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'contextFraction.pl', 'PGcourse.pl'); #:% section = setup -#: The macro `contextFraction.pl` provides four contexts: +#: The PODLINK('contextFraction.pl') macro provides four contexts: #: -#:```{#contexts .perl} +#: ```{#contexts .perl} #: Context('Fraction'); #: Context('Fraction-NoDecimals'); #: Context('LimitedFraction'); #: Context('LimitedProperFraction'); -#:``` -#: For the differences among these, see the POD documentation for -#: PODLINK('contextFraction.pl'). +#: ``` +#: +#: See PODLINK('contextFraction.pl') for the differences between these contexts. Context('Fraction-NoDecimals'); $answer = Compute('3/2'); #:% section = statement #: There are many context flags that control how fraction answers are checked. -#: See the POD documentation for PODLINK('contextFraction.pl'). +#: See the documentation for the PODLINK('contextFraction.pl') macro for more +#: details. BEGIN_PGML Simplify [``\frac{6}{4}``]. diff --git a/tutorial/sample-problems/Algebra/FunctionDecomposition.pg b/tutorial/sample-problems/Algebra/FunctionDecomposition.pg index abaa3d185a..2b1accffb3 100644 --- a/tutorial/sample-problems/Algebra/FunctionDecomposition.pg +++ b/tutorial/sample-problems/Algebra/FunctionDecomposition.pg @@ -17,19 +17,19 @@ #:% categories = [composition] #:% section = preamble -#: We need to include the macro file `answerComposition.pl`, which provides an answer -#: checker that determines if two functions compose to form a given function. This -#: can be used in problems where you ask a student to break a given function into a -#: composition of two simpler functions, neither of which is allowed to be the -#: identity function. +#: Include the macro file PODLINK('answerComposition.pl') which provides an +#: answer checker that determines if two functions compose to form a given +#: function. This can be used in problems where a student is asked to break a +#: given function into a composition of two simpler functions, neither of which +#: is allowed to be the identity function. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'answerComposition.pl', 'PGcourse.pl'); #:% section = setup -#: We will ask the students for a function `f(u)` and and function `g(x)` such -#: that `f(g(x))` is a given function. Therefore, we need to make `u` a -#: variable and define `$f` and `$g`. +#: This problem asks the students for functions `y = f(u)` and `u = g(x)` such +#: that `f(g(x))` is a given function. Therefore, the variable `u` needs to be +#: added to the context, and the functions `$f` and `$g` defined. Context()->variables->add(u => 'Real'); $a = random(2, 9); @@ -44,12 +44,12 @@ of two simpler functions [`y = f(u)`] and [`u = g(x)`]. * [`f(u) =`] [_]{ width => 15 } -* [`g(x) =`] [_]{ width => 15 } +* [`g(x) =`] [_]{ width => 15 } END_PGML #:% section = answer -#: We use the `COMPOSITION_ANS()` routine to evaluate both answer blanks. It is -#: possible to use the same variable for both answer blanks. See +#: Use the `COMPOSITION_ANS` routine to evaluate the answers. It is possible to +#: use the same variable for both answer rules. See #: PODLINK('answerComposition.pl') for more options and details. COMPOSITION_ANS($f, $g, vars => [ 'u', 'x' ], showVariableHints => 1); diff --git a/tutorial/sample-problems/Algebra/GraphToolCircle.pg b/tutorial/sample-problems/Algebra/GraphToolCircle.pg index 2b0761e645..cf4051d39f 100644 --- a/tutorial/sample-problems/Algebra/GraphToolCircle.pg +++ b/tutorial/sample-problems/Algebra/GraphToolCircle.pg @@ -17,21 +17,22 @@ #:% section = preamble #: This example shows how to get student input in the form of a graph (a circle) -#: by using interactive graphing tools. Load the parserGraphTool.pl macro for -#: this. +#: by using interactive graphing tools. Load the PODLINK('parserGraphTool.pl') +#: macro for this. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); #:% section = setup -#: The variables `$h`, `$k` and `$r` randomly pick a center and radius of the circle. +#: The variables `$h`, `$k` and `$r` randomly pick a center and radius of the +#: circle. #: #: The lines #: -#:```{#equation .perl} +#: ```{#equation .perl} #: Context()->variables->add(y => 'Real'); #: $circle_eq_lhs = Formula("(x - $h)^2 + (y - $k)^2")->reduce; -#:``` +#: ``` #: #: define the equation of the circle that is shown in the problem and solution. #: @@ -58,7 +59,7 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); #: PROBLINK('GraphToolCustomChecker.pg') for an example of how to use a custom #: checker. #: -#: For more details, see the PODLINK('POD documentation','parserGraphTool.pl') +#: For more details, see PODLINK('parserGraphTool.pl'). $h = non_zero_random(-5, 5); $k = non_zero_random(-5, 5); $r = random(1, 4); @@ -70,7 +71,7 @@ $gt = GraphTool("{circle, solid, ($h, $k), ($h + $r, $k)}") ->with(bBox => [ -11, 11, 11, -11 ]); #:% section = statement -#: This asks to graph the circle given by the equation. The code +#: This asks the student to graph the circle given by the equation. The code #: `[_]{$gt}` inserts the GraphTool. BEGIN_PGML Graph the circle given by the following equation. @@ -84,8 +85,8 @@ END_PGML #: The solution describes how to obtain the graph of the circle from the #: equation. #: -#: The line `[@ $gt->generateAnswerGraph @]*` inserts the correct answer -#: graph. +#: The line `[! the graph of a circle centered at ([$h], [$k]) of radius [$r] !]{$gt}` +#: inserts the correct answer graph. BEGIN_PGML_SOLUTION The equation of the circle of the form: @@ -93,11 +94,11 @@ The equation of the circle of the form: has a center at [`([$h],[$k])`] and radius [$r]. To enter the graph, click the circle tool, then click the center at [`([$h],[$k])`] and then click a second -point that is [$r] units from the center. This is easist going left, right, up +point that is [$r] units from the center. This is easiest going left, right, up or down from the center. The solution is -[@ $gt->generateAnswerGraph @]* +[! the graph of a circle centered at ([$h], [$k]) of radius [$r] !]{$gt} END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Algebra/GraphToolCubic.pg b/tutorial/sample-problems/Algebra/GraphToolCubic.pg index dc02f1c6d0..d36a5a8f49 100644 --- a/tutorial/sample-problems/Algebra/GraphToolCubic.pg +++ b/tutorial/sample-problems/Algebra/GraphToolCubic.pg @@ -17,8 +17,8 @@ #:% section = preamble #: This example shows how to get student input in the form of a graph (a cubic) -#: by using interactive graphing tools. Load the parserGraphTool.pl macro for -#: this. +#: by using interactive graphing tools. Load the PODLINK('parserGraphTool.pl') +#: macro for this. DOCUMENT(); loadMacros( @@ -74,25 +74,28 @@ $gt = ); #:% section = statement -#: This asks to graph the cubic throw the given points. The code -#: `[_]{$gt}` inserts the GraphTool. +#: This asks to the student to graph the cubic defined by the given equation. +#: The code `[_]{$gt}` inserts the GraphTool. BEGIN_PGML Graph the cubic function [``p(x) = [$k](x-[$x1])(x-[$x2])(x-[$x3])``] [_]{$gt} END_PGML +$altText = 'the graph of a cubic function through the points ' + . "($x1, 0), ($x2, 0), ($x3, 0), and (0, $y0)"; + #:% section = solution #: The solution describes how to obtain the graph of the circle from the #: equation. BEGIN_PGML_SOLUTION -To graph the cubic, you'll need 4 points. Because of the form, there are -3 zeros [`([$x1],0), ([$x2],0)`] and [`([$x3],0)`]. Any other point can be -chosen, but another easy one is the [`y`]-intercept, which by evaluating -[`p(0)=[$y0]`], then select [`(0,[$y0])`]. +To graph the cubic, you'll need 4 points. Because of the form, there are 3 +zeros [`([$x1], 0)`], [`([$x2], 0)`] and [`([$x3], 0)`]. Any other point can be +chosen, but another easy one is the [`y`]-intercept, which can be found by +evaluating [`p(0) = [$y0]`], then select [`(0, [$y0])`]. The solution is -[@ $gt->generateAnswerGraph @]* +[![$altText]!]{$gt} END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Algebra/GraphToolCustomChecker.pg b/tutorial/sample-problems/Algebra/GraphToolCustomChecker.pg index c5647d1012..8e3d10fe5a 100644 --- a/tutorial/sample-problems/Algebra/GraphToolCustomChecker.pg +++ b/tutorial/sample-problems/Algebra/GraphToolCustomChecker.pg @@ -18,13 +18,14 @@ #:% section = preamble #: This example shows how to get student input in the form of a graph (a circle) #: by using interactive graphing tools, and demonstrates the usage of a custom -#: checker. Load the `parserGraphTool.pl` macro for this. +#: checker. Load the PODLINK('parserGraphTool.pl') macro for this. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); #:% section = setup -#: The variables `$h`, `$k` and `$r` randomly pick a center and radius of the circle. +#: The variables `$h`, `$k` and `$r` randomly pick a center and radius of the +#: circle. #: #: The lines #: @@ -49,8 +50,8 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); #: #: * `bBox`: this is an array reference of four values xmin, ymax, xmax, ymin #: indicating the upper left and lower right corners of the visible graph. -#: * `cmpOptions`: this is a hash of options passed to the cmp method for checking -#: the answer. +#: * `cmpOptions`: this is a hash of options passed to the cmp method for +#: checking the answer. #: #: The option #: @@ -140,7 +141,7 @@ $gt = GraphTool("{circle, solid, ($h, $k), ($h + $r, $k)}")->with( ); #:% section = statement -#: This asks to graph the circle given by the equation. The code +#: This asks the student to graph the circle given by the equation. The code #: `[_]{$gt}` inserts the GraphTool. BEGIN_PGML Graph the circle given by the following equation. @@ -154,14 +155,14 @@ END_PGML #: The solution describes how to obtain the graph of the circle from the #: equation. BEGIN_PGML_SOLUTION -The equation of the circle of the form: +The equation of the circle given by the equation [`[$circle_eq_lhs] = [$r ** 2]`] -has a center at [`([$h],[$k])`] and radius [$r]. To enter the graph, click the -circle tool, then click the center at [`([$h],[$k])`] and then click a second -point that is [$r] units from the center. This is easist going left, right, up -or down from the center. +has center at [`([$h],[$k])`] and radius [$r]. To enter the graph, click the +circle tool, then click on the center at [`([$h],[$k])`] and then click on a +second point that is [$r] units from the center. This is easiest going left, +right, up or down from the center. END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Algebra/GraphToolLine.pg b/tutorial/sample-problems/Algebra/GraphToolLine.pg index a569ba3ffa..4538d44590 100644 --- a/tutorial/sample-problems/Algebra/GraphToolLine.pg +++ b/tutorial/sample-problems/Algebra/GraphToolLine.pg @@ -17,8 +17,8 @@ #:% section = preamble #: This example shows how to get student input in the form of a graph (a line) -#: by using interactive graphing tools. Load the parserGraphTool.pl macro for -#: this. +#: by using interactive graphing tools. Load the PODLINK('parserGraphTool.pl') +#: macro for this. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); @@ -32,7 +32,7 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); #: of the attributes of the object. The first attribute in each list is the #: type of object to be graphed, `line` in this case. What the remaining #: attributes are depend on the type. For a line the second attribute is -#: whether the object is to be `solid` or `dashed`, and the remaining attibutes +#: whether the object is to be `solid` or `dashed`, and the remaining attributes #: are two distinct points on the line. #: #: The `->with` method is then used to set options for the `GraphTool` object. @@ -65,28 +65,26 @@ $gt = GraphTool("{line, solid, ($x0, 0), (0, $y0)}")->with( #:% section = statement. #: The code `[_]{$gt}` inserts the GraphTool. BEGIN_PGML -On the graph below, plot the line [`[$line] = [$x0*$y0]`] +On the graph below, plot the line [`[$line] = [$x0 * $y0]`]. [_]{$gt} - END_PGML -#:% section=solution - +#:% section = solution BEGIN_PGML_SOLUTION -Two points are needed off this line. It could be put in slope-intercept form -which would give a [`y`]-intercept and then a second point could be determined +Two points on the line are needed. It could be put in slope-intercept form +which would give a [`y`]-intercept, and then a second point could be determined from the slope. -Alternatively, the intercept form on the line is found by dividing the equation -by the right hand side to -[```\frac{x}{[$x0]}+ \frac{y}{[$y0]}=1```] -and thus the [`x`]-intercept is number in the fraction under the [`x`] or - [$x0] and the [`y`]-intercept is the number in the fraction under the [`y`] - or [$y0]. The solution is - -[@ $gt->generateAnswerGraph @]* +Alternatively, the intercept form on the line is found by dividing both sides of +the equation by [`[$x0 * $y0]`] to get +[```\frac{x}{[$x0]} + \frac{y}{[$y0]} = 1.```] +Thus the [`x`]-intercept is the denominator of the fraction involving [`x`] or +[`[$x0]`] and the [`y`]-intercept is the denominator of the fraction involving +[`y`] or [`[$y0]`]. +The solution is +[! the graph of the line through ([$x0],0) and (0,[$y0]) !]{$gt} END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Algebra/GraphToolNumberLine.pg b/tutorial/sample-problems/Algebra/GraphToolNumberLine.pg index 86735a2349..0e98ea9b4d 100644 --- a/tutorial/sample-problems/Algebra/GraphToolNumberLine.pg +++ b/tutorial/sample-problems/Algebra/GraphToolNumberLine.pg @@ -17,8 +17,8 @@ #:% section = preamble #: This example shows how to get student input in the form of a graph -#: by using interactive graphing tools. Load the `parserGraphTool.pl` macro for -#: this. +#: by using interactive graphing tools. Load the PODLINK('parserGraphTool.pl') +#: macro for this. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); @@ -39,15 +39,15 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); #: In this case the options that are set are: #: #: * `numberLine` is set to 1 (true) to indicate this is a one-dimensional -#: graph. +#: graph. #: * `bBox`: For a number line the bounding box is an array reference [xmin, xmax] -#: containing the left and right limits of the visible graph. +#: containing the left and right limits of the visible graph. #: * `availableTools`: This determines which tools will be available for the -#: student to use. +#: student to use. #: * `ticksDistanceX`: The distance between tick marks in the x direction. #: * `minorTicksX`: The number of minor ticks to show. This can be 0. -#: * `useBracketEnds`: 1 (true) means to use () and [] to denote the intervals -#: a value of 0 means to use open and solid circles. +#: * `useBracketEnds`: 1 (true) means to use () and [] to denote the interval +#: ends, and a value of 0 means to use open and solid circles. $x1 = random(1, 5); $gt1 = GraphTool("{interval, (-$x1,$x1]}")->with( diff --git a/tutorial/sample-problems/Algebra/GraphToolPoints.pg b/tutorial/sample-problems/Algebra/GraphToolPoints.pg index 0a07e02abd..6838d0fae9 100644 --- a/tutorial/sample-problems/Algebra/GraphToolPoints.pg +++ b/tutorial/sample-problems/Algebra/GraphToolPoints.pg @@ -18,8 +18,8 @@ #:% section = preamble #: This example shows how to get student input in the form of points -#: by using interactive graphing tools. Load the `parserGraphTool.pl` macro for -#: this. +#: by using interactive graphing tools. Load the PODLINK('parserGraphTool.pl') +#: macro for this. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); @@ -39,13 +39,13 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserGraphTool.pl', 'PGcourse.pl'); #: In this case the options that are set are: #: #: * `bBox`: This is an array reference of four values `xmin, ymax, xmax, ymin` -#: indicating the upper left and lower right corners of the visible graph. +#: indicating the upper left and lower right corners of the visible graph. #: * `availableTools`: This determines which tools will be available for the -#: student to use. +#: student to use. #: * `showCoordinateHints`: Setting this to 0 turns off coordinate hints, which -#: would show students the coordinates the cursor is over. +#: would show the coordinates of the point the cursor is over. #: -#: For more details, see the PODLINK('POD documentation','parserGraphTool.pl') +#: For more details, see the PODLINK('POD','parserGraphTool.pl'). $x1 = non_zero_random(-5, 5); $y1 = non_zero_random(-5, 5); @@ -66,10 +66,9 @@ Graph the points [`([$x1], [$y1])`] and [`([$x2], [$y2])`]. [_]{$gt} END_PGML -#:% section=solution - +#:% section = solution BEGIN_PGML_SOLUTION - +Solution explanation goes here. END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Algebra/InequalityAnswer.pg b/tutorial/sample-problems/Algebra/InequalityAnswer.pg index 6b2a1c0cf0..1115aab912 100644 --- a/tutorial/sample-problems/Algebra/InequalityAnswer.pg +++ b/tutorial/sample-problems/Algebra/InequalityAnswer.pg @@ -17,19 +17,19 @@ #:% categories = [fraction] #:% section = preamble -#: We must load `contextInequalities.pl`. +#: The PODLINK('contextInequalities.pl') macro must be loaded DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'contextInequalities.pl', 'PGcourse.pl'); #:% section = setup -#: We require students to use inequalities by using `Context('Inequalities-Only')`. -#: If we had used `Context('Inequalities')` instead, then students could also enter -#: their answer using interval notation. For more details, please see +#: Require students to use inequalities by using `Context('Inequalities-Only')`. +#: Note that if `Context('Inequalities')` is used instead, then students could +#: also enter answers using interval notation. For more details, please see #: PODLINK('contextInequalities.pl'). #: -#: We use `formatStudentAnswer => 'parsed'` and `Compute()` so that the student's -#: answer is left as a fraction rather than reduced to a decimal. +#: The context flag `formatStudentAnswer => 'parsed'` is used so that the +#: student's answer is left as a fraction rather than reduced to a decimal. Context('Inequalities-Only'); Context()->flags->set(formatStudentAnswer => 'parsed'); diff --git a/tutorial/sample-problems/Algebra/LinearInequality.pg b/tutorial/sample-problems/Algebra/LinearInequality.pg index cba0884152..4ce11b8d73 100644 --- a/tutorial/sample-problems/Algebra/LinearInequality.pg +++ b/tutorial/sample-problems/Algebra/LinearInequality.pg @@ -17,8 +17,8 @@ #:% categories = [inequality] #:% section = preamble -#: We include the macro file `parserLinearRelation.pl` to be able to the a -#: LinearRelation object. +#: The macro file PODLINK('parserLinearRelation.pl') must be loaded to be able +#: to use the LinearRelation object. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserLinearRelation.pl', @@ -36,15 +36,13 @@ $ab = $a * $b; $lr = LinearRelation("$a x + $b y < $ab")->reduce; #:% section = statement -#: Everything is as usual. Insert the fraction and answer blanks using `$showfraction`. BEGIN_PGML -The line [`L`] that passes through the point [`([$b],0)`] and [`(0,[$a])`] divides -the [`xy`]-plane. What is the linear relation that describes the set of -points in half-plane containing the origin? Note, do not include the -points on the line? +The line [`L`] that passes through the point [`([$b],0)`] and [`(0,[$a])`] +divides the [`xy`]-plane into two regions. What is the linear relation that +describes the half-plane containing the origin? Note, do not include the points +on the line? [__]{$lr} - END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Algebra/Logarithms.pg b/tutorial/sample-problems/Algebra/Logarithms.pg index d11b26721c..c94c7b311f 100644 --- a/tutorial/sample-problems/Algebra/Logarithms.pg +++ b/tutorial/sample-problems/Algebra/Logarithms.pg @@ -22,13 +22,14 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: We add the variables to the context and reset their limits since logarithms are -#: not defined on the default domain [-1,1]. After defining `$answer`, then we -#: undefine certain operators and functions so that students will have to simplify -#: their answer. Since the answer requires multiplication no matter how it is written, -#: we cannot prevent students from entering an answer such as `ln(x*x*x...)` instead -#: of `$a * ln(x)`, but by choosing large values for `$a, $b, $c`, we can strongly -#: discourage them from entering `ln(x*x*x...)`. +#: Add the variables `x`, `y`, and `z` to the context and set their limits to be +#: `[2, 3]` since logarithms are not defined on the default domain `[-1, 1]`. +#: After defining `$answer`, undefine certain operators and functions so that +#: students will have to give their answer in the desired form. Since the answer +#: requires multiplication, students cannot be prevented from entering an answer +#: such as `ln(x*x*x...)` instead of `$a * ln(x)`. However, by choosing large +#: values for `$a, $b, $c` such answers can be strongly discouraged. (Note +#: that this can be done using Bizarro arithmetic and a custom answer checker.) Context()->variables->are(x => 'Real', y => 'Real', z => 'Real'); Context()->variables->set(x => { limits => [ 2, 3 ] }); Context()->variables->set(y => { limits => [ 2, 3 ] }); @@ -36,12 +37,9 @@ Context()->variables->set(z => { limits => [ 2, 3 ] }); $a = random(20, 40); $b = random(20, 40); -do { $c = random(20, 40); } until ($c != $b); +do { $c = random(20, 40); } until $c != $b; # TeX -$expr = - "\displaystyle \ln \left( \frac{ x^{$a} y^{$b} }{ z^{$c} } \right)"; - $answer = Compute("$a * ln(x) + $b * ln(y) - $c * ln(z)"); Context()->operators->undefine('/', '^', '**'); @@ -49,11 +47,11 @@ Context()->functions->undefine('sqrt'); #:% section = statement BEGIN_PGML -Using laws of logarithms, write the expression below using sums and/or -differences of logarithmic expressions which do not contain the logarithms of -products, quotients, or powers. +Using laws of logarithms, write the expression below using sums or differences +of logarithmic expressions which do not contain the logarithms of products, +quotients, or powers. -[`\displaystyle [$expr] =`] [_]{$answer}{20} +[``\ln\left(\frac{x^{[$a]} y^{[$b]}}{z^{[$c]}}\right) =``] [_]{$answer}{20} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Algebra/NoSolution.pg b/tutorial/sample-problems/Algebra/NoSolution.pg new file mode 100644 index 0000000000..1f170b7ee0 --- /dev/null +++ b/tutorial/sample-problems/Algebra/NoSolution.pg @@ -0,0 +1,68 @@ +## DESCRIPTION +## Answer that may not be an equation. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(PGML tutorial 2023) +## Date(07/21/2023) +## Institution(MWSU) +## Author(Glenn Rice) +## KEYWORDS('algebra', 'alternate answer forms') + +#:% name = Answers with Alternate Forms +#:% type = Sample +#:% subject = [algebra, precalculus] +#:% categories = [equations] + +#:% section = preamble +#: Load the PODLINK('parserRadioMultiAnswer.pl') macro in addition to the usual +#: macros. +DOCUMENT(); + +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'parserRadioMultiAnswer.pl', 'PGcourse.pl' +); + +#:% section = setup +#: A `RadioMultiAnswer` produces a set of radio buttons for each of the given +#: statements. The format is +#: +#: ```{#radio-multi-answer-usage .perl} +#: RadioMultiAnswer([ +#: [statement1, first answer in statement 1, second answer in statement 1, ...], +#: [statement2, first answer in statement 2, ...], +#: ... +#: [last statement] +#: ], correct index, options) +#: ``` +#: +#: Answer blanks can be added to a part with the `%s` in the string. If an array +#: answer is desired, use `%s*` instead. See the +#: PODLINK('parserRadioMultiAnswer.pl') macro for more details. + +$y = random(0, 4); + +$rma = RadioMultiAnswer( + [ + [ 'The equation of the line is \(y = \) %s.', '2x' ], + ['No such line exists.'] + ], + $y < 4 ? 1 : 0 +); + +#:% section = statement +BEGIN_PGML +Is there a line through the points [`(0, 0)`], [`(1, 2)`], and [`(2, [$y])`]? +If there is, then give the equation for this line. + +[_]{$rma} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Algebra/PointAnswers.pg b/tutorial/sample-problems/Algebra/PointAnswers.pg index 3b4c2d6970..c543ca72c3 100644 --- a/tutorial/sample-problems/Algebra/PointAnswers.pg +++ b/tutorial/sample-problems/Algebra/PointAnswers.pg @@ -17,18 +17,19 @@ #:% categories = [point, answers] #:% section = preamble -#: We only need to load `contextLimitedPoint.pl` if we want to prevent operations between points. +#: Load PODLINK('contextLimitedPoint.pl') to prevent operations between points. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'contextLimitedPoint.pl', 'PGcourse.pl'); #:% section = setup -#: We could have used `Context("Point");` instead, which would allow mathematical -#: operations between points (such as adding points as if they were vectors). The -#: x-intercepts are clearly a list of points. We used a list with only one element -#: for the y-intercepts so that a student who mistakenly enters two points will be -#: told their second point is incorrect. If we did not use a list for the y-intercepts, -#: a student who enters two points would be given an error message instead. +#: `Context('Point')` could be used instead, which would allow mathematical +#: operations between points (such as adding points as if they were vectors). +#: The `x`-intercepts are clearly a list of points. A list with only one +#: element is used for the `y`-intercepts so that a student who mistakenly +#: enters two points will be told their second point is incorrect. If a list +#: is not used for the `y`-intercepts, a student who enters two points would be +#: given an error message instead. Context('LimitedPoint'); $f = Compute("x^2 - 1"); @@ -53,6 +54,4 @@ BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION -COMMENT('MathObject version. Uses PGML.'); - ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Algebra/ScalingTranslating.pg b/tutorial/sample-problems/Algebra/ScalingTranslating.pg index a03891ef1a..59d8d7b311 100644 --- a/tutorial/sample-problems/Algebra/ScalingTranslating.pg +++ b/tutorial/sample-problems/Algebra/ScalingTranslating.pg @@ -17,17 +17,17 @@ #:% categories = [transformation] #:% section = preamble -#: We must load `parserFunction.pl` so that we can add a named function to the context. +#: Load PODLINK('parserFunction.pl') so that a named function can be added to +#: the context. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserFunction.pl', 'PGcourse.pl'); #:% section = setup -#: The `parserFunction` method allows us to add a named function to the context. We can -#: define this function however we want, so we chose a function whose formula the -#: students will not guess, whose domain is all real numbers, and which will have no -#: issues during answer evaluation. Once a named function is added to the context, you -#: can use it like you would any other named function. +#: The `parserFunction` method allows the addition of a named function to the +#: context. For this problem a function is chosen whose formula would be +#: difficult to guess, whose domain is all real numbers. Once a named function +#: is added to the context, it can be used like any other named function. parserFunction(f => 'sin(e * x) + 5.5 * pi * x^2'); $answer = Formula('f(x - 2) + 1'); diff --git a/tutorial/sample-problems/Algebra/SimpleFactoring.pg b/tutorial/sample-problems/Algebra/SimpleFactoring.pg index bc26843ba8..ee70cc3bff 100644 --- a/tutorial/sample-problems/Algebra/SimpleFactoring.pg +++ b/tutorial/sample-problems/Algebra/SimpleFactoring.pg @@ -15,7 +15,7 @@ #:% name = Simple factoring #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [polynomial] +#:% categories = [polynomials] #:% see_also = [FactoredPolynomial.pg, ExpandedPolynomial.pg, FactoringAndExpanding.pg] #:% section = preamble @@ -23,24 +23,24 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: First, we create two random roots and then create the factors. Note: -#: the `->reduce` will help make `x-(-3)` into `x+3`. In addition, we -#: create the expanded form of the quadratic. +#: First, create two random roots and then create the factors. Note that calling +#: `->reduce` will simplify `x-(-3)` to `x+3`. In addition, create the expanded +#: form of the quadratic. #: -#: Note that the argument of the List call are the objects in the list, -#: which can be any MathObjects. Here we create a list of Formulas and a list -#: of Reals (the numbers that we use in the second list will be promoted to -#: Real MathObjects when the List is created). +#: Note that the argument of the `List` call are the objects in the list, which +#: can be any MathObjects. Here we create a list of Formulas and a list of Reals +#: (the numbers that we use in the second list will be promoted to Real +#: MathObjects when the List is created). #: -#: If, for example, there were no real roots, we should set -#: `$roots = List("NONE");` so that students who enter a list of roots will not -#: receive an error message about entering the wrong type of answer. If we were -#: to use `$roots = String("NONE");` instead, students who enter anything -#: other than a string (e.g., a list of numbers) will receive an error message. +#: If, for example, there were no real roots, then set `$roots = List("NONE")` +#: so that students who enter a list of roots will not receive an error message +#: about entering the wrong type of answer. If `$roots = String("NONE")` were +#: used instead, students who enter anything other than a string (e.g., a list +#: of numbers) would receive an error message. #: -#: Similarly, if there were only one root at x=4, we would use -#: `$roots = List(4);` instead of $roots = Real(4); to avoid sending error -#: messages to students who enter multiple answers or NONE. +#: Similarly, if there were only one root at `x = 4`, use `$roots = List(4)` +#: instead of `$roots = Real(4)` to avoid sending error messages to students who +#: enter multiple answers or NONE. ($x0, $x1) = (non_zero_random(-6, 6), non_zero_random(-6, 6)); $factor1 = Compute("x-$x0")->reduce; $factor2 = Compute("x-$x1")->reduce; @@ -66,7 +66,6 @@ b) What are the roots of this equation? Roots = [__]{$roots} _(Enter both answers as a comma-separated list.)_ - END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Algebra/SolutionForEquation.pg b/tutorial/sample-problems/Algebra/SolutionForEquation.pg index d0c0889b59..bbda387238 100644 --- a/tutorial/sample-problems/Algebra/SolutionForEquation.pg +++ b/tutorial/sample-problems/Algebra/SolutionForEquation.pg @@ -14,17 +14,17 @@ #:% name = Solution for an Equation #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [algebra] +#:% categories = [equations] #:% section = preamble -#: The macro `parserSolutionFor.pl` must be loaded. +#: Load the macro PODLINK('parserSolutionFor.pl') for the `SolutionFor` method. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserSolutionFor.pl', 'PGcourse.pl'); #:% section = setup -#: We use `SolutionFor(equation, point)` to define this MathObject. For more -#: details and options, see PODLINK('parserSolutionFor.pl'). +#: `SolutionFor(equation, point)` is used to define the MathObject answer. For +#: more details and options, see PODLINK('parserSolutionFor.pl'). Context('Vector'); $r = random(3, 6); diff --git a/tutorial/sample-problems/Algebra/StringOrOtherType.pg b/tutorial/sample-problems/Algebra/StringOrOtherType.pg index 4b897c674f..cfeb428dfb 100644 --- a/tutorial/sample-problems/Algebra/StringOrOtherType.pg +++ b/tutorial/sample-problems/Algebra/StringOrOtherType.pg @@ -23,22 +23,28 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: There are several predefined strings, such as NONE, DNE, INF, INFINITY. If you -#: need another string added to the context, see PROBLINK('StringsInContext.pg'). +#: There are several predefined strings, such as `NONE`, `DNE`, `INF`, and +#: `INFINITY`. If another string is needed it will need to be added to the +#: context. See PROBLINK('StringsInContext.pg'). #: -#: When `$answer = Formula('2x')` and a student enters the string `NONE`, they will -#: not get any error message because when the answer checker expects a formula and -#: gets a string it is set up not to balk. However, when `$answer = String('none')` -#: and a student enters the formula `2x`, they will get an error message. This is -#: because the answer checker is expecting a string and gets a formula, and when -#: this happens it balks. We must use `typeMatch => Formula('x')` so that in the event -#: the answer is a string, no error message will appear. +#: When `$answer = Formula('2x')` and a student enters the string `NONE`, there +#: will not be an error message. This is because MathObject formula answers +#: are set up to accept string answers that are defined in the context. However, +#: when `$answer = String('none')` and a student enters the formula `2x`, they +#: will get an error message. This is because string answers do not also accept +#: formulas. So use `typeMatch => Formula('x')` so that in this case no error +#: message will appear. +#: +#: It is recommended that you do not use the technique demonstrated in this +#: sample problem anymore. Instead use the method demonstrated in +#: PROBLINK('NoSolution.pg'). That method is more intuitive for students, and +#: does not lead to an invalid statement for the answer such as `y = NONE`. $y = random(0, 4); if ($y < 4) { $answer = String('none')->cmp(typeMatch => Formula('x')); } else { - $answer = Formula('2*x')->cmp(typeMatch => Formula('x')); + $answer = Formula('2x'); } #:% section = statement diff --git a/tutorial/sample-problems/Algebra/TableOfValues.pg b/tutorial/sample-problems/Algebra/TableOfValues.pg index 4af120d27d..eadaa2fb31 100644 --- a/tutorial/sample-problems/Algebra/TableOfValues.pg +++ b/tutorial/sample-problems/Algebra/TableOfValues.pg @@ -17,47 +17,41 @@ #:% categories = [table] #:% section = preamble +#: The PODLINK('niceTables.pl') macro is implicitly used in this problem, but is +#: automatically loaded by the `PGML.pl` macro. DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'niceTables.pl', 'PGcourse.pl'); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: We create an empty array `@answer` and use a for loop to simplify filling it -#: with values. -#: -#: The `DataTable` is from PODLINK('niceTables.pl'). This builds a simple table. The options -#: `horizontalrules` and `texalignment` gives the borders around each of the cells. +#: Create the array `@answer` filled with values via a map. $f = Formula('3^(-x)'); -@answer = (); -for $i (0 .. 2) { - $answer[$i] = $f->eval(x => $i); -} - -$table = DataTable( - [ - [ '\(x\)', '\(f(x)\)' ], - [ '\(0\)', ans_rule(4) ], - [ '\(1\)', ans_rule(4) ], - [ '\(2\)', ans_rule(4) ], - ], - horizontalrules => 1, - texalignment => '|c|c|' -); +@answer = map { $f->eval(x => $_) } 0 .. 2; #:% section = statement +#: Create a PODLINK('niceTables.pl') `DataTable` via the PGML syntax. The first +#: row is declared to be a row of headers by adding the `headerrow => 1` option +#: to any cell in that row. Note that this option applies to the entire row. The +#: `horizontalrules` and `texalignment`options add borders around each of the +#: cells. The option `padding => [0.5, 0.5]` adds a bit more vertical space in +#: each cell to improve the appearance. The option `valign => 'middle'` gives +#: better vertical alignment for this table. BEGIN_PGML If [`f(x) = [$f]`], fill in the table of values with numbers. -[@ $table @]* -END_PGML - -#:% section = answer -#: Because the answer blanks are built with `ans_rule` inside the table, we -#: need to use the traditional `ANS` call here. -for $i (0 .. 2) { - ANS($answer[$i]->cmp); +[# + [.[`x`].] [.[`f(x)`].]*{ headerrow => 1 } + [.[`0`].] [.[_]{$answer[0]}.]* + [.[`1`].] [.[_]{$answer[1]}.]* + [.[`2`].] [.[_]{$answer[2]}.]* +#]{ + horizontalrules => 1, + texalignment => '|c|c|', + padding => [ 0.5, 0.5 ], + valign => 'middle' } +END_PGML #:% section = solution BEGIN_PGML_SOLUTION diff --git a/tutorial/sample-problems/Algebra/UnorderedAnswers.pg b/tutorial/sample-problems/Algebra/UnorderedAnswers.pg index 3c27baf3c1..5fbdc96b32 100644 --- a/tutorial/sample-problems/Algebra/UnorderedAnswers.pg +++ b/tutorial/sample-problems/Algebra/UnorderedAnswers.pg @@ -14,16 +14,17 @@ #:% name = Unordered Answers #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [answer] +#:% categories = [answers] #:% section = preamble -#: The macro `unorderedAnswer.pl` must be loaded. +#: The macro PODLINK('unorderedAnswer.pl') must be loaded. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'unorderedAnswer.pl', 'PGcourse.pl'); #:% section = setup -#: Because the answers have the variables x, y, and z, add the latter two. +#: The answers use the variables `x`, `y`, and `z`. The variable `x` is already +#: in the default `Numeric` context, but `y` and `z` need to be added. Context()->variables->add(y => 'Real', z => 'Real'); $a = random(2, 9); @@ -41,10 +42,10 @@ much as possible. END_PGML #:% section = answer -#: We use `UNORDERED_ANS(checker1, checker2, ...);` to evaluate the answers. It is -#: possible to withhold feedback and credit until everything is correct by using -#: the standard problem grader, which awards no partial credit and full credit -#: only when everything is correct. +#: The method `UNORDERED_ANS(checker1, checker2, ...)` is used to associate the +#: unordered answer evaluator with the answer rules defined in the problem. The +#: variable `$showPartialCorrectAnswers` is set to 0 to withhold feedback until +#: all answers are correct. $showPartialCorrectAnswers = 0; UNORDERED_ANS($answer1->cmp, $answer2->cmp, $answer3->cmp); diff --git a/tutorial/sample-problems/Complex/ComplexOperations.pg b/tutorial/sample-problems/Complex/ComplexOperations.pg index 7df510d8f6..3e119dd1a8 100644 --- a/tutorial/sample-problems/Complex/ComplexOperations.pg +++ b/tutorial/sample-problems/Complex/ComplexOperations.pg @@ -21,13 +21,12 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: To use complex numbers, we need to switch context with `Context('Complex')`. -#: There are many ways to create a complex number. Notice on the 4th one -#: `i` is defined and can be used naturally. +#: To use complex numbers, switch to the `Complex` context with +#: `Context('Complex')`. #: -#: Also, the standard operations go through as expected. -#: Notice that for the first two questions, we give the store the answer in -#: a variable. +#: Several ways to create a complex number that are demonstrated. Notice for the +#: 4th example that `i` is defined as a MathObject complex number that can be +#: used directly in Perl computations. Context('Complex'); $z0 = Complex(non_zero_random(-5, 4), non_zero_random(-5, 5)); @@ -41,22 +40,26 @@ $a1 = random(1, 5); $ans2 = Compute("$a0*$z1-$a1*$z2"); #:% section = statement -#: Note that in the last three answer blanks, the correct answer is -#: in the `{}` instead of stored as a variable, like the first two. -#: Either method is correct and it varies on which to use. -#: Recall that the perl power `**` is used in the last one. +#: For the last three answer rules, the correct answer is directly computed from +#: previously defined variables in the answer rule option braces `{...}` instead +#: of being stored in another variable, as in the first two answer rules. Either +#: method is correct. Usually you would only need store the answer in a variable +#: if it will be used in other places in the code as well which is not done even +#: for the first two answers rules in this case. +#: +#: Note that the `**` in the last answer is the Perl exponent operator. BEGIN_PGML -Let [`z_0=[$z0]`], [`z_1=[$z1]`], [`z_2=[$z2]`] and [`z_3=[$z3]`]. Find +Let [`z_0 = [$z0]`], [`z_1 = [$z1]`], [`z_2 = [$z2]`] and [`z_3 = [$z3]`]. Find -[`z_0+z_1=`] [___]{$ans1} +[`z_0+z_1 =`] [___]{$ans1} -[`[$a0]z_1-[$a1]z_2=`] [_____]{$ans2} +[`[$a0]z_1 - [$a1]z_2 =`] [_____]{$ans2} -[`z_1z_2=`] [___]{$z1*$z2} +[`z_1 z_2 =`] [___]{$z1*$z2} -[``\frac{z_3}{z_0}= ``] [___]{$z3/$z0} +[``\frac{z_3}{z_0} =``] [___]{$z3/$z0} -[`` z_2^2=``] [__]{$z2**2} +[``z_2^2 =``] [__]{$z2**2} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Complex/LimitedComplex.pg b/tutorial/sample-problems/Complex/LimitedComplex.pg index ccb8ba02ee..3eb63332f5 100644 --- a/tutorial/sample-problems/Complex/LimitedComplex.pg +++ b/tutorial/sample-problems/Complex/LimitedComplex.pg @@ -16,8 +16,8 @@ #:% subject = [complex] #:% section = preamble -#: This problems shows the capabilities of the `contextLimitedComplex.pl` macro -#: so it must be loaded. +#: This problems shows the capabilities of the +#: PODLINK('contextLimitedComplex.pl') macro, so it must be loaded. DOCUMENT(); loadMacros( @@ -26,18 +26,22 @@ loadMacros( ); #:% section = setup -#: If we ask students to do operations with complex numbers, often we don't -#: want those operations to be allowed in the answer. In this case we set the -#: `Context('LimitedComplex')`. If we define complex numbers, then perl operations -#: will be allowed, but not operations in `Compute` functions. +#: Often when students are asked to perform operations with complex numbers, +#: usually it is not desirable for them to be able to enter those operations in +#: their answer. One way to achieve this is by using the `LimitedComplex` +#: context via `Context('LimitedComplex')`. This context will only allow a +#: simplified complex number in either Cartesian or polar form to be entered. +#: Note that Perl operations on complex numbers are still allowed, but +#: operations in a string passed to the `Compute` call or in a student answer +#: are not. #: -#: `LimitedComplex` will allow a single number entered (technically only one -#: value of `i`) in either cartesian or polar form. This problem gives the -#: answer in polar to check that form. -#: -#: If you only want complex numbers to be entered in cartesian form you can use +#: If you only want complex numbers to be entered in Cartesian form you can use #: `Context('LimitedComplex-cartesian')` and if you only want students to #: enter numbers in polar form use `Context('LimitedComplex-polar')`. +#: +#: The final computation determines the polar form of the answer for use in the +#: solution. In the `LimitedComplex` context, most functions are disabled, so +#: the computations are performed on the real and imaginary components directly. Context('LimitedComplex'); $x0 = non_zero_random(-5, 5); @@ -51,8 +55,6 @@ $z1 = Complex($x1, $y1); $ans1 = $z0 + $z1; $ans2 = $z0 * $z1; -# Determine the polar form of the answer to give a hint. Since in -# LimitedComplex, most functions are diasbled, so we work on the components. $arg0 = atan($y0 / $x0) + ($x0 > 0 ? ($y0 > 0 ? 0 : 2 * pi) : pi); $arg1 = atan($y1 / $x1) + ($x1 > 0 ? ($y1 > 0 ? 0 : 2 * pi) : pi); $abs0 = sqrt($x0**2 + $y0**2); @@ -60,20 +62,23 @@ $abs1 = sqrt($x1**2 + $y1**2); #:% section = statement BEGIN_PGML -Let [`z_0=[$z0]`] and [`z_1=[$z1]`]. Find +Let [`z_0 = [$z0]`] and [`z_1 = [$z1]`]. Find -[`z_0+z_1=`] [___]{$ans1} +[`z_0 + z_1 =`] [___]{$ans1} -[`z_0z_1=`] [___]{$ans2} - -You may not enter operations between numbers for these answers. However, -if you want the polar form (the second answer is [`[@ $abs0*$abs1 @] e^{[@ $arg0+$arg1 @]i}`]) +[`z_0 z_1 =`] [___]{$ans2} +Give the answers in simplified Cartesian or polar form. END_PGML #:% section = solution +#: Note that a solution should do better than is demonstrated here. Don't just +#: give the answers. Show how to find the answers. BEGIN_PGML_SOLUTION -Solution explanation goes here. +The first answer in Cartesian form is [`[$ans1]`]. + +The second answer in polar form is +[`[@ Round($abs0 * $abs1, 4) @] e^{[@ Round($arg0 + $arg1, 4) @]i}`]. END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Complex/OtherOperations.pg b/tutorial/sample-problems/Complex/OtherOperations.pg index 422325521e..159fa7a650 100644 --- a/tutorial/sample-problems/Complex/OtherOperations.pg +++ b/tutorial/sample-problems/Complex/OtherOperations.pg @@ -22,14 +22,16 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: To use complex numbers, we need to switch context with `Context('Complex')`. -#: The problem PROBLINK('ComplexOperations.pg') showed different ways of +#: To use complex numbers, switch to the `Complex` context with +#: `Context('Complex')`. +#: +#: See the problem PROBLINK('ComplexOperations.pg') for different ways of #: creating complex numbers. #: -#: This shows the functions `Re` (real part), `Im` (imaginary part), `abs` -#: (absolute value or modulus -- distance from the origin), `arg` (the angle -#: the point is from the positive real axis) and `conj`, -#: (the complex conjugate) +#: This shows the usage of the functions `Re` (real part), `Im` (imaginary +#: part), `abs` (absolute value or modulus which gives the distance from the +#: origin), `arg` (the angle with the positive real axis), and `conj` (the +#: complex conjugate). Context('Complex'); $z0 = Complex(non_zero_random(-5, 4), non_zero_random(-5, 5)); @@ -37,21 +39,18 @@ $z1 = Complex(non_zero_random(-5, 4), non_zero_random(-5, 5)); $z2 = Complex(non_zero_random(-5, 4), non_zero_random(-5, 5)); #:% section = statement -#: All of the answers here are placed in the `{}` instead of making another -#: variable. BEGIN_PGML -Let [`z_0=[$z0]`], [`z_1=[$z1]`], and [`z_2=[$z2]`]. Find - -[`\text{Re}(z_0)=`] [___]{Re($z0)} +Let [`z_0 = [$z0]`], [`z_1 = [$z1]`], and [`z_2 = [$z2]`]. Find -[`\text{Im}(z_0)=`] [_____]{Im($z0)} +[`\text{Re}(z_0) =`] [___]{Re($z0)} -[`|z_1|=`] [___]{abs($z1)} +[`\text{Im}(z_0) =`] [_____]{Im($z0)} -[`\text{arg}(z_1)=`] [___]{arg($z1)} +[`|z_1| =`] [___]{abs($z1)} -[`\text{conj}(z_2)=`] [___]{conj($z2)} +[`\text{arg}(z_1) =`] [___]{arg($z1)} +[`\text{conj}(z_2) =`] [___]{conj($z2)} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/DiffCalc/AnswerWithUnits.pg b/tutorial/sample-problems/DiffCalc/AnswerWithUnits.pg index 7550e9806c..64c5ee7c0f 100644 --- a/tutorial/sample-problems/DiffCalc/AnswerWithUnits.pg +++ b/tutorial/sample-problems/DiffCalc/AnswerWithUnits.pg @@ -18,7 +18,8 @@ #:% categories = [units] #:% section = preamble -#: We load `parserNumberWithUnits.pl` and `parserFormulaWithUnits.pl`. +#: Load the PODLINK('parserNumberWithUnits.pl') and +#: PODLINK('parserFormulaWithUnits.pl') macros. DOCUMENT(); loadMacros( @@ -28,10 +29,8 @@ loadMacros( ); #:% section = setup -#: We use the differentiation operator `->D('t')` and the evaluation method `->eval()` to -#: construct the derivative and evaluate it as a function. If we were writing several -#: questions like this with different height functions, using the differentiation and -#: evaluation methods would really speed up the writing. +#: Use the differentiation operator `->D('t')` to compute the derivated, and the +#: evaluation method `->eval()` to evaluate it as a function. Context()->variables->are(t => 'Real'); $h = Formula('-16 t^2 + 16'); @@ -39,13 +38,14 @@ $v = $h->D('t'); $v1 = $v->eval(t => 1); $a = $v->D('t'); -$ans1 = FormulaWithUnits("$v", 'ft/s'); -$ans2 = NumberWithUnits("$v1", 'ft/s'); -$ans3 = FormulaWithUnits("$a", 'ft/s^2'); +$ans1 = FormulaWithUnits($v, 'ft/s'); +$ans2 = NumberWithUnits($v1, 'ft/s'); +$ans3 = FormulaWithUnits($a, 'ft/s^2'); #:% section = statement -#: Don't forget to use `helpLink('units')` so your students will have access to the -#: complete list of units that WeBWorK understands. +#: Give students access to help on entering units and the complete list of units +#: that WeBWorK understands by inserting the result of calling +#: `helpLink('units')` into the problem text. BEGIN_PGML Suppose the height of a falling object, in feet above the ground, is given by [`h(t) = [$h]`] for [`t \geq 0`], where time is measured in seconds. @@ -63,7 +63,7 @@ c. What is the acceleration of the object? Include units in your answer. [_]{$ans3}{15} -Note: use units in all answers. [@ helpLink('units') @]* +Note: Use units in all answers. [@ helpLink('units') @]* END_PGML #:% section = solution diff --git a/tutorial/sample-problems/DiffCalc/DifferenceQuotient.pg b/tutorial/sample-problems/DiffCalc/DifferenceQuotient.pg index d6913c3746..9af9ff780a 100644 --- a/tutorial/sample-problems/DiffCalc/DifferenceQuotient.pg +++ b/tutorial/sample-problems/DiffCalc/DifferenceQuotient.pg @@ -18,7 +18,7 @@ #:% categories = [difference quotient] #:% section = preamble -#: We need to include the macros file `parserDifferenceQuotient.pl`. +#: Include the macro file PODLINK('parserDifferenceQuotient.pl'). DOCUMENT(); loadMacros( @@ -27,8 +27,9 @@ loadMacros( ); #:% section = setup -#: The routine DifferenceQuotient('function', 'variable') takes the simplified function -#: and a variable name. If the variable is omitted, dx is used by default. +#: The `DifferenceQuotient` method takes a simplified function for its first +#: parameter, and a variable name for its second optional parameter. If the +#: variable name is omitted, then `x` is used by default. $limit = DifferenceQuotient('2 * x + h', 'h'); $fp = Compute('2 x'); diff --git a/tutorial/sample-problems/DiffCalc/DifferentiateFunction.pg b/tutorial/sample-problems/DiffCalc/DifferentiateFunction.pg index 2c831ebeed..502d1db2a3 100644 --- a/tutorial/sample-problems/DiffCalc/DifferentiateFunction.pg +++ b/tutorial/sample-problems/DiffCalc/DifferentiateFunction.pg @@ -22,38 +22,49 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: The partial differentiation operator is `->D('x')`. +#: The differentiation operator is `->D('x')`. #: -#: The main difference between `eval()` and `substitute()` is +#: The main difference between the `eval` and `substitute` methods is #: -#: - `eval()` returns a `Real` (a number) -#: - `substitute()` returns a `Formula` +#: - `eval` returns a `Real` (a number) +#: - `substitute` returns a `Formula` #: -#: Since plugging a particular number `$k` into the Formula `$f` returns a Formula `$k x`, -#: if we had used the eval method `$ans2 = $fx->eval(k => $k);` instead of the `substitute` -#: method, we would get errors because `$k x` is a Formula, not a Real. Note: You cannot -#: use eval or substitute to perform function composition, i.e., you can only plug in -#: numbers, not formulas. +#: For a constant answer either the `eval` method can be used in which case the +#: answer would be a `Real`, or the `substitute` method can be used in which +#: case the answer would be a constant `Formula`. #: -#: When the answer is a constant, we can use either the eval method, in which case the -#: answer would be a Real, or the substitute method, in which case the answer would -#: be a constant Formula. If you use the eval method, `$ans3 = $fx->eval(x => $a * pi, k => $k);` -#: the answer will be a Real and will display as a single number in decimal format. -#: If you use the substitute method instead, you have more control over how the answer -#: will be displayed. In particular, the context flag reduceConstants controls whether -#: the answer will be reduced to a single number in decimal format, the flag -#: reduceConstantFunctions controls whether or not expressions such as `4 + 5 * 2` are -#: reduced to 14, and setting the context flag `formatStudentAnswer => 'parsed'` will -#: prevent the student's answer from being reduced to a single number in decimal -#: format and will also display pi instead of 3.14159... +#: For example, the `eval` method could be used as in +#: `$ans3 = $fx->eval(x => $a * pi, k => $k)` to obtain a `Real` which will +#: display as a single number in decimal format. Note that the `eval` method +#: requires values for all variables in the formula. For example, calling +#: `$fx->eval(k => $k)` gives errors, because a value is not provided for the +#: variable `x`. #: -#: For more details, see eval versus substitute, formatting correct answers, and -#: constants in problems. +#: The substitute method can be used instead which gives more control over how +#: the answer will be displayed. In particular, the context flag +#: `reduceConstants` controls whether simple constant expressions like `2 * 4` +#: or `5 * pi` will be reduced to numbers in decimal format, the flag +#: `reduceConstantFunctions` controls whether or not expressions such as +#: `sqrt(3)` or `sin(3)` are evaluated to numbers or left as a function +#: evaluated at a number, and setting the context flag `formatStudentAnswer => +#: 'parsed'` will prevent the student's "Entered" answer from being reduced to a +#: single number in decimal format, and will display constants like `pi` instead +#: of an approximation being displayed. +#: +#: It is important to note that the above rules do not apply to the numbers +#: passed to the `substitute` method. So `$a * pi` will be converted into +#: decimal form before it is used by the `substitute` method. +#: +#: Note that neither the `eval` method or `substitute` method can be used to +#: perform function composition. Only numbers can be plugged in, not formulas. +#: +#: For more details see PROBLINK('EvalVersusSubstitute.pg') and +#: PROBLINK('ConstantsInProblems.pg'). Context()->variables->add(k => 'Real'); Context()->flags->set( - reduceConstants => 0, # no decimals - reduceConstantFunctions => 1, # simplify 4 + 5 * 2? - formatStudentAnswer => 'parsed', # no decimals + reduceConstants => 0, + reduceConstantFunctions => 0, + formatStudentAnswer => 'parsed', ); $a = random(6, 9); @@ -63,12 +74,8 @@ $f = Formula('k x^2'); $fx = $f->D('x'); $ans1 = $fx; - -$ans2 = $fx->substitute(k => $k); # formula -# $ans2 = $fx->eval(k => $k); # gives errors, must eval to real - -$ans3 = $fx->substitute(x => $a * pi, k => $k); # formula -# $ans3 = $fx->eval(x => $a * pi, k => $k); # real +$ans2 = $fx->substitute(k => $k); +$ans3 = $fx->substitute(x => $a * pi, k => $k); #:% section = statement BEGIN_PGML diff --git a/tutorial/sample-problems/DiffCalc/LinearApprox.pg b/tutorial/sample-problems/DiffCalc/LinearApprox.pg index d92ae3d8cb..8177d59be6 100644 --- a/tutorial/sample-problems/DiffCalc/LinearApprox.pg +++ b/tutorial/sample-problems/DiffCalc/LinearApprox.pg @@ -17,10 +17,10 @@ #:% categories = [linear approximation] #:% section = preamble -#: We load `parserAssignment.pl` to require students to enter their answer as an equation -#: of the form `y = ...` We load `answerHints.pl` to provide customized answer hints, -#: particularly for those students who enter the slope of the line instead of the -#: equation of the line. +#: Load PODLINK('parserAssignment.pl') to require students to enter their answer +#: as an equation of the form `y = ...`. Load PODLINK('answerHints.pl') to +#: provide customized answer hints, particularly for students who enter the +#: slope of the line instead of the equation of the line. DOCUMENT(); loadMacros( @@ -30,9 +30,9 @@ loadMacros( ); #:% section = setup -#: We have to tell the context that we are allowing the assignment of a variable to a formula. +#: Allow assignments in the context by calling `parser::Assignment->Allow`. #: -#: We use answer hints to remind students to enter an equation for a line, not +#: Answer hints are used to remind students to enter an equation for a line, not #: just the slope of the line. Context()->variables->add(y => 'Real'); parser::Assignment->Allow; @@ -43,19 +43,20 @@ $a2 = 2 * $a; $f = Compute('sqrt(x)'); -$answer = Compute("y = $a + (1/$a2) * (x-$aa)"); +$answer = Compute("y = $a + (1 / $a2) * (x - $aa)"); $cmp = $answer->cmp()->withPostFilter(AnswerHints( - [ Formula("1/$a2"), Formula("y=1/$a2") ] => [ + [ Formula("1 / $a2"), Formula("y = 1 / $a2") ] => [ 'Your answer should be an equation for a non-horizontal line.', replaceMessage => 1 ], )); #:% section = statement -#: The variable `$cmp` is used in the answer blank, which call the compare method -#: define in the setup section. +#: The variable `$cmp` is used for the answer associated with the answer rule. +#: It was defined by calling the `cmp` method of the `$answer` in the setup +#: section above. BEGIN_PGML -Find the linear approximation to [`f(x) = [$f]`] at [`x = [$aa]`]. Your +Find the linear approximation of [`f(x) = [$f]`] at [`x = [$aa]`]. Your answer should be an equation in the variables [`x`] and [`y`]. [_]{$cmp}{10} diff --git a/tutorial/sample-problems/DiffCalcMV/ContourPlot.pg b/tutorial/sample-problems/DiffCalcMV/ContourPlot.pg index c6b8ea3e44..872ff3bab6 100644 --- a/tutorial/sample-problems/DiffCalcMV/ContourPlot.pg +++ b/tutorial/sample-problems/DiffCalcMV/ContourPlot.pg @@ -17,8 +17,8 @@ #:% categories = [plots] #:% section = preamble -#: We will use `PGtikz.pl` for constructing the graph. The macro -#: `parserPopUp.pl` is used to include the popup. +#: The PODLINK('PGtikz.pl') macro is used for constructing the graph. The +#: PODLINK('parserPopUp.pl') macro is used for a drop down menu answer. DOCUMENT(); loadMacros( @@ -30,36 +30,38 @@ $showPartialCorrectAnswers = 0; #:% section = setup #: The contour plot is created with TikZ by overlaying circles with differing -#: shades of blue. See the POD and the [TikZ manual](https://tikz.dev/) for more -#: information. +#: shades of blue. See PODLINK('PGtikz.pl') and the +#: [TikZ manual](https://tikz.dev/) for more information. #: -#: If the colored contour plot is not desired, replace the `\filldraw` line -#: with the following: -#:```{#change-contour .perl} -#: \draw (0,0) circle [radius={sqrt(64-8*\n)}]; -#:``` +#: If a filled and colored contour plot is not desired, replace the `\filldraw` +#: line in the `\foreach` loop with +#: +#: ```{#change-contour .perl} +#: \draw (0,0) circle[radius = {sqrt(64 - 8 * \n)}]; +#: ``` $graph = createTikZImage(); $graph->tikzLibraries('arrows.meta'); $graph->BEGIN_TIKZ -\tikzset{>={Stealth[scale=2]}} +\tikzset{>={Stealth[scale = 2]}} \Large % Make the fonts a little bigger. \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-7,-7) rectangle (7,7); -\foreach \n in {0,...,7} { - \pgfmathsetmacro\k{100-\n*10} - \filldraw[fill=blue!\k!white,fill opacity=0.5] - (0,0) circle [radius={sqrt(64-8*\n)}]; + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-7, -7) rectangle (7, 7); +\foreach \n in {0, ..., 7} { + \pgfmathsetmacro\k{100 - \n * 10} + \filldraw[fill = blue!\k!white, fill opacity = 0.5] + (0,0) circle[radius = {sqrt(64 - 8 * \n)}]; } -\draw[->] (-7,0) -- (7,0) node[above left,outer sep=3pt] {\(x\)}; -\foreach \x in {-6,...,-1,1,2,...,6} - \draw(\x,5pt) -- (\x,-5pt) node [below] {\(\x\)}; -\draw[->] (0,-7) -- (0,7) node[below right,outer sep=3pt] {\(y\)}; -\foreach \y in {-6,...,-1,1,2,...,6} - \draw (5pt,\y) -- (-5pt,\y) node[left] {\(\y\)}; +\draw[->] (-7, 0) -- (7, 0) node[above left,outer sep = 3pt] {\(x\)}; +\foreach \x in {-6, ..., -1, 1, 2, ..., 6} + \draw (\x, 5pt) -- (\x, -5pt) node[below] {\(\x\)}; +\draw[->] (0, -7) -- (0, 7) node[below right, outer sep = 3pt] {\(y\)}; +\foreach \y in {-6, ..., -1, 1, 2, ..., 6} + \draw (5pt, \y) -- (-5pt, \y) node[left] {\(\y\)}; END_TIKZ $popup = DropDownTF('false', placeholder => 'Select One'); @@ -68,7 +70,7 @@ $popup = DropDownTF('false', placeholder => 'Select One'); BEGIN_PGML Determine if the following statement is true or false. -[_]{$popup} This could be a contour plot for [`f(x,y) = x^2 - y^2`]. +[_]{$popup} This could be a contour plot for [`f(x, y) = x^2 - y^2`]. >> [@ image($graph, width => 300, height => 300, tex_size => 450) @]* << END_PGML diff --git a/tutorial/sample-problems/DiffCalcMV/ImplicitPlane.pg b/tutorial/sample-problems/DiffCalcMV/ImplicitPlane.pg index de9d0066da..1ff35af988 100644 --- a/tutorial/sample-problems/DiffCalcMV/ImplicitPlane.pg +++ b/tutorial/sample-problems/DiffCalcMV/ImplicitPlane.pg @@ -17,9 +17,11 @@ #:% categories = [implicit function] #:% section = preamble -#: * The parserVectorUtils.pl macro is used for the non_zero_point3D function below. -#: * The parserImplicitPlane.pl macro includes the context and the ImplicitPlane -#: function to parse and create implicit planes. +#: * The PODLINK('parserVectorUtils.pl') macro defines the +#: `non_zero_point3D` and `non_zero_vector3D` functions. +#: * The PODLINK('parserImplicitPlane.pl') macro includes the 'ImplicitPlane' +#: context and the `ImplicitPlane` function used to parse and create implicit +#: planes. DOCUMENT(); loadMacros( @@ -29,16 +31,17 @@ loadMacros( ); #:% section = setup -#: The first answer is a standard mulitivariable calculus question. There are several -#: different ways to specify the input to `ImplicitPlane`, which are detailed in the POD -#: documentation. It is also possible to do some more complicated manipulations with -#: the vectors and points, which is detailed in the problem techniques section. +#: The first answer is a standard multivariable calculus question. There are +#: several different ways to specify the parameters for the `ImplicitPlane`, +#: which are detailed in the PODLINK('parserImplicitPlane.pl') documentation. +#: It is also possible to do more complicated manipulations with vectors and +#: points, which are detailed in the problem techniques section. #: -#: When the `ImplicitPlane` context has only two variables, it rephrases error messages -#: in terms of lines. If you want students to be able to enter an equation for a line -#: in the most general form, or if you have a vertical line to check (or just a -#: constant equation such as `x = 3`), you can use the `ImplicitPlane` context to reliably -#: check these answers. +#: When the `ImplicitPlane` context has only two variables, it rephrases error +#: messages in terms of lines. If you want students to be able to enter an +#: equation for a line in the most general form, or if you have a vertical line +#: to check (or just a constant equation such as `x = 3`), you can use the +#: `ImplicitPlane` context to check these answers. Context('ImplicitPlane'); Context()->variables->are(x => 'Real', y => 'Real', z => 'Real'); diff --git a/tutorial/sample-problems/DiffEq/GeneralSolutionODE.pg b/tutorial/sample-problems/DiffEq/GeneralSolutionODE.pg index f8003f090f..18078c8c88 100644 --- a/tutorial/sample-problems/DiffEq/GeneralSolutionODE.pg +++ b/tutorial/sample-problems/DiffEq/GeneralSolutionODE.pg @@ -25,28 +25,27 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserAssignment.pl', 'PGcourse.pl'); #:% section = setup -#: Add the arbitrary constants `c1, c2, c3` to the context as variables so that we can -#: evaluate them later. Set the domain of function evaluation on these variables to -#: something sensible. Use `parser::Assignment->Allow;` to allow equation answers of -#: the form `y = ...`. See PODLINK('parserAssignment.pl') for additional information. +#: Add the arbitrary constants `c1`, `c2`, and `c3` to the context as variables +#: so that they can be used in evaluations. #: -#: For the checker, we use my `$stu = Formula($student->{tree}{rop});` to get the right -#: side of the student answer (to get the left side, we could have used lop for the -#: left operand). Use `Formula($stu->D('c1')) == Formula(0)` to check that the student -#: actually has c1 in their answer. +#: Use `parser::Assignment->Allow` to allow equation answers of the form +#: `y = ...`. See PODLINK('parserAssignment.pl') for additional information. #: -#: We substitute numerical values that the student is unlikely to choose for `c1, c2, c3` -#: and then apply the Wronskian test for independence. Normally, we would check to see -#: if the Wronskian was zero, but zero level tolerance in WeBWorK is much more -#: stringent than non-zero level tolerance. So, we rearrange the terms of the -#: Wronskian == 0 equation so that there are nonzero terms on both sides of the -#: equation, and as a result we get a more reliable answer checker. +#: In the checker `my $stu = Formula($student->{tree}{rop});` is used to get +#: the right side of the assignment in the student answer (to get the left side +#: of the assignment use `lop`). Use `Formula($stu->D('c1')) == Formula(0)` to +#: verify that the student answer uses the variable `c1`. #: -#: Finally, we take several derivatives of the student answer and use them to check -#: that the student answer actually satisfies the differential equation. Again, instead -#: of checking (left side of ODE) == 0, we rearrange the terms of the differential -#: equation to be of the form (some nonzero function) == (some other nonzero function) -#: in order to get a more reliable answer checker. +#: Substitute numerical values for the variables `c1`, `c2`, and `c3` and apply +#: the Wronskian test for independence. Note that zero level tolerance in +#: WeBWorK is much more stringent that non-zero level tolerance. So the terms +#: of the Wronskian equation are rearranged so that there are nonzero terms on +#: both sides of the equation to give a more reliable check. +#: +#: Finally, several derivatives of the student answer are computed and used to +#: check that the student answer satisfies the differential equation. Again, +#: to check that differential equation, the terms are rearanged to have nonzero +#: terms on both sides, in order to give a more reliable check. Context()->variables->add( c1 => 'Real', c2 => 'Real', @@ -62,7 +61,7 @@ parser::Assignment->Allow; $a = list_random(2, 3, 5, 6, 7, 8); -# char poly (r-1)(r^2 + $a) +# The characteristic polynomial is (r - 1)(r^2 + $a). $answer = Compute("y = c1 e^x + c2 cos(sqrt($a) x) + c3 sin(sqrt($a) x)"); @@ -122,7 +121,8 @@ $cmp = $answer->cmp( ); #:% section = statement -#: Give students detailed instructions about the format of the answer that is expected. +#: Give students detailed instructions about the format of the answer that is +#: expected. BEGIN_PGML Find the general solution to [`y^{\,\prime\prime\prime} - y^{\,\prime\prime} + [$a] y^{\,\prime} - [$a] y = 0`]. diff --git a/tutorial/sample-problems/DiffEq/HeavisideStep.pg b/tutorial/sample-problems/DiffEq/HeavisideStep.pg index e4ba080d1d..baa13bf846 100644 --- a/tutorial/sample-problems/DiffEq/HeavisideStep.pg +++ b/tutorial/sample-problems/DiffEq/HeavisideStep.pg @@ -17,53 +17,61 @@ #:% categories = [heaviside-step] #:% section = preamble -#: We load `parserFunction.pl` to make adding a named function to the context easier. -#: Please see the POD documentation `parserFunction.pl`. +#: Load PODLINK('parserFunction.pl') for adding a named function to the context. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserFunction.pl', 'PGcourse.pl'); -#:% section=setup -#: We add the step function to the context with the name `step`. The function -#: `step(t)` is the Heaviside function and takes the value 1 when $t > 0$, and the value -#: 0 when $t \leq 0$. We will use the function `step` when evaluating the Heaviside function -#: to obtain an answer that is a number. +#:% section = setup +#: Add the step function to the context with the name `step`. The function +#: `step(t)` is the Heaviside function and takes the value 1 when $t > 0$, and +#: the value 0 when $t \leq 0$. The `step` function will be used when evaluating +#: the Heaviside function to obtain an answer that is a number. #: -#: For more details on adding the Heaviside function to the context, see the forum -#: discussion on the +#: For more details on adding the Heaviside function to the context, see the +#: forum discussion on the #: [Heaviside step function](https://webwork.maa.org/moodle/mod/forum/discuss.php?d=458). #: -#: For the second question, since answers are checked numerically by comparing the student -#: answer to the correct answer at several randomly points in the domain (the default is 5 points) -#: in an interval (the default is `[-1,1]`), the function `step(t) = u(t)` is not very robust when -#: checking answers using these defaults. For example, if a student types in the answer `u(t-0.1)` and -#: the correct answer is u(t), there is a good chance that the student's answer will be marked correct, -#: since the probability that a test point was chosen in the interval (0,0.1) is much less than 100%. -#: Also, if the correct answer were u(t-5), then a student could enter the answer 0 and be marked correct -#: because the correct answer is identically zero on the interval `[-1,1]`. +#: For the second question, since answers are checked numerically by comparing +#: the student answer to the correct answer at several random points in the +#: domain (the default is 5 points) in an interval (the default is `[-1,1 ]`), +#: the function `step(t) = u(t)` is not very robust when checking answers using +#: these defaults. For example, if a student types in the answer `u(t - 0.1)` +#: and the correct answer is u(t), there is a good chance that the student's +#: answer will be marked correct, since the probability that a test point was +#: chosen in the interval `(0, 0.1)` is much less than 100%. Also, if the +#: correct answer were `u(t - 5)`, then a student could enter the answer 0 and +#: be marked correct because the correct answer is identically zero on the +#: interval `[-1, 1]`. #: -#: To make the answer checking robust, in `$answer2` we specify a larger domain centered at `$a` using limits, -#: we require four of the test points always be used, and that there should be 10 test points total (the -#: four we specified and six others generated at random). Notice that we used the construction `$f->with(...)` -#: to do this (using `$f->{test_at} = [[1],[2]]` would generate an error because the functions we added to the -#: context aren't "blessed" with enough permissions to modify `$f` in that way). +#: To make the answer checking robust, in `$answer2` a larger domain centered at +#: `$a` is specified using limits, five test points are specified, and 10 test +#: points are required to be used (the five that were specified and five others +#: generated at random). Notice that the construction `$f->with(...)` +#: is used to do this (using `$f->{test_at} = [ [1], [2] ]` would generate an +#: error because the functions added to the context are not "blessed" with +#: enough permissions to modify `$f` in that way). #: -#: In part (b), since the students never actually see the values of the function u(t), -#: we could have defined the function as +#: In part (b), since the students never actually see the values of the function +#: `u(t)`, the function could be defined as #: -#:```{#parser-function-call .perl} -#: parserFunction("u(t)" => "1.5 * sin(e*t) + 5*pi/3 + arctan(t)"); -#:``` +#: ```{#parser-function-call .perl} +#: parserFunction("u(t)" => "1.5 * sin(e * t) + 5 * pi / 3 + arctan(t)"); +#: ``` #: -#: If we had defined `u(t)` this way, we would not have had to add the function `step(t)` to the context -#: and we could have used the defaults for the answer checker. Notice that the function `u(t)` is never -#: zero, is not constant, is differentiable, and takes moderately sized values, which makes its answer -#: checking very robust using the defaults for the answer checker. Further, because of the arctangent, -#: it is not periodic and so `u(t)-u(t-a)` should never be identically zero. Also, the formula for `u(t)` is -#: not something students are likely to input as an answer out of nowhere. The function `u(t)` is great as a -#: named function that stands in for the Heaviside function when the answer is a function. However, if the -#: answer is a number obtained by evaluating the Heaviside function, then step(t) should be used or the function -#: `u(t)` should be properly defined as the Heaviside function for obvious reasons. +#: If `u(t)` had been defined in this way, the function `step(t)` would not need +#: to be added to the context and the defaults for the answer checker could be +#: used. Notice that the function `u(t)` is never zero, is not constant, is +#: differentiable, and takes moderately sized values, which makes its answer +#: checking very robust using the defaults for the answer checker. Further, +#: because of the arctangent, it is not periodic and so `u(t) - u(t - a)` should +#: never be identically zero. Also, the formula for `u(t)` is not something +#: students are likely to input as an answer out of nowhere. The function `u(t)` +#: is great as a named function that stands in for the Heaviside function when +#: the answer is a function. However, if the answer is a number obtained by +#: evaluating the Heaviside function, then step(t) should be used or the +#: function `u(t)` should be properly defined as the Heaviside function for +#: obvious reasons. Context()->variables->are(t => 'Real'); Context()->functions->add( step => { diff --git a/tutorial/sample-problems/DiffEq/PrimesInFormulas.pg b/tutorial/sample-problems/DiffEq/PrimesInFormulas.pg index 890a05c774..0f16d51e69 100644 --- a/tutorial/sample-problems/DiffEq/PrimesInFormulas.pg +++ b/tutorial/sample-problems/DiffEq/PrimesInFormulas.pg @@ -17,9 +17,9 @@ #:% categories = [answers] #:% section = preamble -#: For this problem, we want to enter in differential equations, and the variables -#: will be `y', y''` and the resulting equation will be implicit, so the macro -#: `parserImplicitEquation.pl` is used. +#: For this problem a differential equation is the answer, the variables in +#: that equation will include `y'` and `y''`, and the resulting equation will be +#: implicit. So the macro PODLINK('parserImplicitEquation.pl') is used. DOCUMENT(); loadMacros( @@ -28,9 +28,11 @@ loadMacros( ); #:% section = setup -#: We switch the context to `ImplicitEquation` and then includes the variable `y, y', y''` -#: and `t`. The line `Context()->variables->{namePattern} = qr/[ty]'*/i;` is used to make sure -#: that primes can be included in the answer. +#: Switch to the `ImplicitEquation` context and then include the variables `y`, +#: `y'`, `y''`, and `t`. +#: +#: Setting `Context()->variables->{namePattern} = qr/[ty]'*/i` allows primes to +#: be included in the answer. Context('ImplicitEquation'); Context()->variables->{namePattern} = qr/[ty]'*/i; Context()->variables->are( diff --git a/tutorial/sample-problems/IntegralCalc/DoubleIntegral.pg b/tutorial/sample-problems/IntegralCalc/DoubleIntegral.pg index 4a7e296883..64030ae865 100644 --- a/tutorial/sample-problems/IntegralCalc/DoubleIntegral.pg +++ b/tutorial/sample-problems/IntegralCalc/DoubleIntegral.pg @@ -17,21 +17,24 @@ #:% categories = [double integral] #:% section = preamble -#: Since there are multiple answer blanks that are dependent upon each other, we use `parserMultiAnswer.pl`. +#: Since there are multiple answer blanks that are dependent upon each other the +#: PODLINK('parserMultiAnswer.pl') macro is used. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); #:% section = setup -#: There are two separate cases: integrating with respect to dx dy (which we call case -#: 0) or with respect to dy dx (which we call case 1). The zeroth and first entries -#: in each of the arrays `@id, @od, @A, @B, @C, @D` hold the values for case 0 and case 1, -#: respectively. We used constant limits of integration to keep this example easy to follow, -#: but we encourage you to write questions over non-rectangular regions. +#: There are two separate cases: integrating with respect to `dx dy` (case 0) or +#: with respect to `dy dx` (case 1). The zeroth and first entries in each of the +#: arrays `@id`, `@od`, `@A`, `@B`, `@C`, and `@D` hold the values for case 0 +#: and case 1, respectively. Constant limits of integration are used to keep +#: this example easy to follow, but you are encouraged to write questions over +#: non-rectangular regions. #: -#: The `$multians` object has been compartmentalized, so you shouldn't need to change it -#: unless you want to fiddle with the weighted score for each answer blank (by changing -#: the return values). The return values are set so that the percentages come out nicely. +#: The `$multians` object has been compartmentalized, so you shouldn't need to +#: change it unless you want to fiddle with the weighted score for each answer +#: blank (by changing the return values). The return values are set so that the +#: percentages come out nicely. Context()->variables->are( x => 'Real', dx => 'Real', @@ -74,8 +77,7 @@ $multians = MultiAnswer($f, $id[0], $od[0], $A[0], $B[0], $C[0], $D[0])->with( singleResult => 1, checker => sub { my ($correct, $student, $self) = @_; - my ($fstu, $idstu, $odstu, $Astu, $Bstu, $Cstu, $Dstu) = - @{$student}; + my ($fstu, $idstu, $odstu, $Astu, $Bstu, $Cstu, $Dstu) = @$student; if ( ( $f == $fstu @@ -153,17 +155,18 @@ $multians = MultiAnswer($f, $id[0], $od[0], $A[0], $B[0], $C[0], $D[0])->with( ); #:% section = statement -#: The only interesting thing to note here is that you must use `$multians` for each -#: answer blank (except the last one, which is independent.) +#: The only interesting thing to note here is that `$multians` must be used for +#: each answer rule (except the last one, which is independent.) BEGIN_PGML Set up a double integral in rectangular coordinates for calculating the volume of the solid under the graph of the function [`f(x,y) = [$f]`] over the region [`[$a] \leq x \leq [$b]`] and [`[$c] \leq y \leq [$d]`]. _Instructions:_ Please enter the integrand in the first answer box . Depending -on the order of integration you choose, enter _dx_ and _dy_ in either order into -the second and third answer boxes with only one _dx_ or _dy_ in each box . Then, -enter the limits of integration and evaluate the integral to find the volume. +on the order of integration you choose, enter [`dx`] and [`dy`] in either order +into the second and third answer boxes with only one [`dx`] or [`dy`] in each +box . Then, enter the limits of integration and evaluate the integral to find +the volume. [``\int_A^B \int_C^D``] [_]{$multians}{10} [_]{$multians}{5} [_]{$multians}{5} diff --git a/tutorial/sample-problems/IntegralCalc/GraphShading.pg b/tutorial/sample-problems/IntegralCalc/GraphShading.pg index 91e8682b10..79b9f4f6da 100644 --- a/tutorial/sample-problems/IntegralCalc/GraphShading.pg +++ b/tutorial/sample-problems/IntegralCalc/GraphShading.pg @@ -17,20 +17,21 @@ #:% categories = [plots] #:% section = preamble -#: The macro `PGikz.pl` is used to create the plot. +#: The macro PODLINK('PGikz.pl') is used to create the plot. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); #:% section = setup -#: We create a randomly transformed version of `sqrt(x)` as the function with a +#: A randomly transformed version of `sqrt(x)` is created as the function with a #: reasonably straightforward answer. #: #: The `nicestring` function is useful for printing polynomials in a nice way. #: -#: The graph is created with `PGtikz.pl`, a very robust plotting package. The -#: shading in done with the `\filldraw` command with the `fill opacity=0.5`. Also -#: note that one of the sides of the filled area is the square root function. +#: The graph is created with PODLINK('PGtikz.pl'), a robust plotting package. +#: The shading in done with the `\filldraw` command with the +#: `fill opacity = 0.5`. Also note that one of the sides of the filled area is +#: the square root function. $a = random(-4, 2); $b = random(1, 4); $g = Compute("$b + sqrt(x - $a)"); @@ -40,36 +41,45 @@ $ans = Compute("3*$b + 14/3"); $graph = createTikZImage(); $graph->tikzLibraries('arrows.meta'); $graph->BEGIN_TIKZ -\tikzset{>={Stealth[scale=1.5]}} +\tikzset{>={Stealth[scale = 1.5]}} \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, + draw = LightBlue, + fill = white, + rounded corners = 10pt, thick,use as bounding box -] (-6,-2) rectangle (6,8); +] (-6, -2) rectangle (6, 8); \begin{scope} - \clip[rounded corners=14pt] (-6,-2) rectangle (6,8); - \draw[ultra thin] (-6,-2) grid (6,8); + \clip[rounded corners = 14pt] (-6, -2) rectangle (6, 8); + \draw[ultra thin] (-6, -2) grid (6, 8); \end{scope} -\draw[->,thick] (-6,0) -- (6,0) node[above left,outer sep=3pt] {\(x\)}; -\foreach \x in {-5,...,-1,1,2,...,5} - \draw(\x,5pt) -- (\x,-5pt) node[below] {\(\x\)}; -\draw[->,thick] (0,-2) -- (0,8) node[below right,outer sep=3pt] {\(y\)}; -\foreach \y in {-1,1,2,...,7} - \draw (5pt,\y) -- (-5pt,\y) node[left]{\(\y\)}; -\filldraw[draw=blue,fill=blue!50!white,fill opacity=0.5] - ({$a+1},0) -- ({$a+1},{$b+1}) - -- plot[domain={$a+1}:{$a+4},samples=100] (\x,{$b+sqrt(\x-$a)}) - -- ({$a+4},0) -- cycle; -\draw[blue,ultra thick,->] - plot[domain={$a}:6,samples=100] (\x,{$b+sqrt(\x-$a)}); +\draw[->, thick] (-6, 0) -- (6, 0) node[above left, outer sep = 3pt] {\(x\)}; +\foreach \x in {-5, ..., -1, 1, 2, ..., 5} + \draw (\x, 5pt) -- (\x, -5pt) node[below] {\(\x\)}; +\draw[->, thick] (0, -2) -- (0, 8) node[below right, outer sep = 3pt] {\(y\)}; +\foreach \y in {-1, 1, 2, ..., 7} + \draw (5pt, \y) -- (-5pt, \y) node[left] {\(\y\)}; +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ({$a + 1}, 0) -- ({$a + 1}, {$b + 1}) + -- plot[domain = {$a + 1}:{$a + 4}, samples = 100] (\x, {$b + sqrt(\x - $a)}) + -- ({$a + 4}, 0) -- cycle; +\draw[blue, ultra thick, ->] + plot[domain = {$a}:6, samples = 100] (\x, {$b + sqrt(\x - $a)}); END_TIKZ +$altText = + "The graph of f(x) is shown starting at the point ($a, $b) and increasing " + . "to the right, sharply at first, and less sharply as it continues to the " + . "right. The region from x = " + . ($a + 1) + . " to x = " + . ($a + 4) + . " that is above the x-axis and below the function is shaded."; + #:% section = statement BEGIN_PGML Use the graph to find the area of the shaded region under [`f(x) = [$gtex]`]. ->>[@ image($graph, width => 400, tex_size => 800) @]*<< +>>[![$altText]!]{$graph}{400}<< >>Graph of [`y = f(x)`].<< diff --git a/tutorial/sample-problems/IntegralCalc/IndefiniteIntegrals.pg b/tutorial/sample-problems/IntegralCalc/IndefiniteIntegrals.pg index 6042d1c4c8..9a158bf19b 100644 --- a/tutorial/sample-problems/IntegralCalc/IndefiniteIntegrals.pg +++ b/tutorial/sample-problems/IntegralCalc/IndefiniteIntegrals.pg @@ -17,8 +17,8 @@ #:% categories = [antiderivatives] #:% section = preamble -#: The macro `parserFormulaUpToConstant.pl` will allow the entry of formula with a -#: general constant like the antiderivative. +#: The macro PODLINK('parserFormulaUpToConstant.pl') will allow the entry of +#: formula with a general constant like the antiderivative. DOCUMENT(); loadMacros( @@ -27,14 +27,12 @@ loadMacros( ); #:% section = setup -#: Examples of specific and general antiderivatives: +#: The specific antiderivative is an ordinary formula. When the `cmp` method is +#: called for this answer the `upToConstant` option will be passed to specify +#: that it be a formula evaluated up to a constant. #: -#: - Specific antiderivatives: `e^x, e^x + pi` -#: - General antiderivatives: `e^x + C`, `e^x + C - 3`, `e^x + K` -#: The specific antiderivative is an ordinary formula, and we check this answer, we -#: will specify that it be a formula evaluated up to a constant (see the first answer -#: blank in the section below). For the general antiderivative, we use the -#: `FormulaUpToConstant()` constructor provided by `parserFormulaUpToConstant.pl`. +#: For the general antiderivative the `FormulaUpToConstant` method provided by +#: PODLINK('parserFormulaUpToConstant.pl') is used. # Specific antiderivative: Marks correct e^x, e^x + pi, etc $specific = Formula('e^x'); @@ -43,11 +41,14 @@ $specific = Formula('e^x'); $general = FormulaUpToConstant('e^x'); #:% section = statement -#: In the first answer blank, we look for the answer with an additive constant -#: using the option `upToConstant => 1` in the `cmp` method. +#: The option `upToConstant => 1` is passed to the `cmp` method for the first +#: answer rule to specify that the any answer that differs from the given answer +#: only by a constant is to be accepted. #: -#: The second is a standard answer blank, but `$general` is created with. -#: `FormulaUpToConstant` +#: The second answer `$general` is created with `FormulaUpToConstant` which +#: means that an arbitrary additive constant must be included in the answer. +#: Any letter aside that is not a variable in the context or $e$ can be used to +#: represent the arbitrary constant. BEGIN_PGML a. Enter a specific antiderivative for [`e^x`]: [_]{ $specific->cmp(upToConstant => 1) }{10} diff --git a/tutorial/sample-problems/IntegralCalc/LimitsOfIntegration.pg b/tutorial/sample-problems/IntegralCalc/LimitsOfIntegration.pg index 5aaf2d8abb..30649550c1 100644 --- a/tutorial/sample-problems/IntegralCalc/LimitsOfIntegration.pg +++ b/tutorial/sample-problems/IntegralCalc/LimitsOfIntegration.pg @@ -1,5 +1,5 @@ ## DESCRIPTION -## Answer blanks in the limits of integration +## Answer rules in limits of integration ## ENDDESCRIPTION ## DBsubject(WeBWorK) @@ -11,15 +11,15 @@ ## MO(1) ## KEYWORDS('Integrals', 'answer blanks in limits of integration') -#:% name = Answer Blanks in Limits of Integration +#:% name = Answer Rules in Limits of Integration #:% type = Sample #:% subject = integral calculus #:% categories = [antiderivatives] #:% section = preamble -#: We use `niceTables.pl` for table formatting commands we will use to put -#: the answer blanks in the limits of integration. We use `answerHints.pl` to -#: help guide students toward the correct answer. +#: The PODLINK('niceTables.pl') macro is used to construct a table containing +#: the answer rules in the limits of integration. The PODLINK('answerHints.pl') +#: macro is used to help guide students toward the correct answer. DOCUMENT(); loadMacros( @@ -28,12 +28,13 @@ loadMacros( ); #:% section = setup -#: We define both `x` and `t` as variables as well as the differential `dx` -#: (which would be incorrect) and the correct `dt`. +#: The variables `x`, `t`, `dx`, and `dt` are added to the context. Note that +#: `dx` would be incorrect for a student to use in the answer and `dt` is the +#: correct differential. #: -#: The `LayoutTable` of PODLINK('niceTables.pl') is used to display the -#: definite integral. Note that the `align => 'rl'` is used to get the -#: formatting to look correct. +#: The `LayoutTable` of PODLINK('niceTables.pl') is used to display the definite +#: integral. Note that the option `align => 'rl'` is used to make the +#: formatting look right. Context()->variables->are( x => 'Real', dx => 'Real', @@ -45,7 +46,7 @@ $integral = LayoutTable( [ [ ' ', ans_rule(4) ], [ - '\(f(x)= \)' . ans_rule(10) . '\(+\)', + '\(f(x)= \) ' . ans_rule(10) . ' \(+\)', '\(\displaystyle \int \;\;\)' . ans_rule(10) ], [ ' ', ans_rule(4) ], @@ -54,31 +55,32 @@ $integral = LayoutTable( allcellcss => { padding => '3pt' } ); +$fx = Formula("sin(x)"); +$ft = Formula("sin(t)"); + #:% section = statement #: The integral is placed in the problem with the `[$integral]*` which displays #: the table. BEGIN_PGML -Find a formula for the function [`f(x)`] such that [`f '(x) = [$fpx]`] and -[`f(2) = 5`]. Use [`t`] as the variable of integration inside the integral. +Find a formula for the function [`f(x)`] such that [`f'(x) = [$fx]`] and +[`f(2) = 5`]. Use [`t`] for the variable of integration. [$integral]* END_PGML #:% section = answer -#: The answer blanks are written out as `ans_rule`, so we must use this style of -#: answer checking. We use `AnswerHints` to guide the students to the correct answer. -#: Note that we also include the incorrect answer with the `x` as the variable -#: and give the student feedback on this. -$fpx = Formula("sin(x)"); -$fpt = Formula("sin(t)"); - +#: The answer rules are inserted using `ans_rule`, so `ANS` must be called to +#: associate the answer evaluators to those answer rules. The `AnswerHints` +#: method is used to provide hints that guide the students to the correct +#: answer. Note that hints for the incorrect answers that use `x` for the +#: variable instead of `t` are cases that a hint is provided for. ANS(Compute('x')->cmp()); ANS(Compute('5')->cmp()); ANS( - Compute("$fpt * dt")->cmp()->withPostFilter(AnswerHints( - Formula("$fpx") => "Are you using the correct variable?", - Formula("$fpx*dx") => "Are you using the correct variable?", - Formula("$fpt") => "Don't forget the differential dt", + Compute("$ft * dt")->cmp()->withPostFilter(AnswerHints( + Formula("$fx") => "Are you using the correct variable?", + Formula("$fx*dx") => "Are you using the correct variable?", + Formula("$ft") => "Don't forget the differential \(dt\).", )) ); ANS(Compute('2')->cmp()); diff --git a/tutorial/sample-problems/IntegralCalc/RiemannSums.pg b/tutorial/sample-problems/IntegralCalc/RiemannSums.pg index 72f9039345..6928ade8aa 100644 --- a/tutorial/sample-problems/IntegralCalc/RiemannSums.pg +++ b/tutorial/sample-problems/IntegralCalc/RiemannSums.pg @@ -17,11 +17,12 @@ #:% categories = [Riemann sums] #:% section = preamble -#: The `weightedGrader.pl` macro is used because we want to give different parts -#: of the answer different weights, the `parserPopUp.pl` macro is used to create -#: drop down menus, and the `PGtikz.pl` macro is used to produce the graphs. +#: The PODLINK('weightedGrader.pl') macro is used to give different parts of the +#: answer different weights, the PODLINK('parserPopUp.pl') macro is used to +#: create drop down menus, and the PODLINK('PGtikz.pl') macro is used to produce +#: the graphs. #: -#: To use the weighted grader call `install_weighted_grader();`. +#: Call the `install_weighted_grader` method to use the weighted grader. DOCUMENT(); loadMacros( @@ -37,32 +38,36 @@ install_weighted_grader(); #: Note that you should be careful to choose ranges for the parameters such that #: all possibilities work well in the graphs. #: -#: Then compute the left and right Riemann sums, first by storing the x and y -#: values in arrays and summing over the arrays. +#: Then compute the left and right Riemann sums, first by storing the endpoints +#: of the intervals that partition `[$a, $b]` in the array `@x` and the +#: corresponding function values in the array `@y`, and then summing over the +#: arrays. #: #: Next, generate the graphs of the function with the rectangles whose areas are #: summed in the Riemann sums. #: -#: Finally, construct drop down menu answers that ask the student to relate the +#: Next, construct drop down menu answers that ask the student to relate the #: Riemann sum estimates to the area of the region specified in the problem. -$c = random(9, 13); # a constant for scaling the function -$f = Compute("x^2/$c"); -$a = random(2, 5); # left endpoint of interval -$b = $a + 2; # right endpoint of interval - -# Generate arrays of x and y values for the Riemann sum. -# There are n + 1 entries in each array so that we can use -# only one pair of arrays for both the left and the right -# endpoint Riemann sums. -$n = 4; # number of rectangles +#: +#: Finally, alternate texts are generated that describe the Riemann sum images +#: for unsighted users that are using a screen reader. +$c = random(9, 13); # a constant for scaling the function +$f = Compute("x^2/$c"); +$a = random(2, 5); # left endpoint of interval +$b = $a + 2; # right endpoint of interval +$n = 4; # number of rectangles $dx = ($b - $a) / $n; -for $k (0 .. $n) { + +# Generate arrays of interval endpoints and function values. +for my $k (0 .. $n) { $x[$k] = $a + $k * $dx; $y[$k] = $f->eval(x => $x[$k]); } + +# Compute the Riemann sums. $sumLeft = 0; $sumRight = 0; -for $k (0 .. $n - 1) { +for my $k (0 .. $n - 1) { $sumLeft += $y[$k] * $dx; $sumRight += $y[ $k + 1 ] * $dx; } @@ -71,54 +76,56 @@ for $k (0 .. $n - 1) { $graph1 = createTikZImage(); $graph1->tikzLibraries('arrows.meta'); $graph1->BEGIN_TIKZ -\tikzset{>={Stealth[scale=2]}} +\tikzset{>={Stealth[scale = 2]}} \Large % Make the fonts a little bigger. \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-1,-1) rectangle (9,9); -\draw[->] (-1,0) -- (9,0) node[above left,outer sep=3pt] {\(x\)}; -\foreach \x in {1,...,8} \draw(\x,5pt) -- (\x,-5pt) node [below] {\(\x\)}; -\draw[->] (0,-1) -- (0,9) node[below right,outer sep=3pt] {\(y\)}; -\foreach \y in {1,...,8} \draw (5pt,\y) -- (-5pt,\y) node[left] {\(\y\)}; -\draw[<->] plot[domain=-1:9] (\x,{\x*\x/$c}); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[0],0) rectangle ($x[1],$y[0]); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[1],0) rectangle ($x[2],$y[1]); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[2],0) rectangle ($x[3],$y[2]); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[3],0) rectangle ($x[4],$y[3]); + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-1, -1) rectangle (9, 9); +\draw[->] (-1, 0) -- (9, 0) node[above left, outer sep = 3pt] {\(x\)}; +\foreach \x in {1, ..., 8} \draw (\x, 5pt) -- (\x, -5pt) node [below] {\(\x\)}; +\draw[->] (0, -1) -- (0, 9) node[below right, outer sep = 3pt] {\(y\)}; +\foreach \y in {1, ..., 8} \draw (5pt, \y) -- (-5pt, \y) node[left] {\(\y\)}; +\draw[<->] plot[domain = -1:9] (\x, {\x * \x / $c}); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[0], 0) rectangle ($x[1], $y[0]); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[1], 0) rectangle ($x[2], $y[1]); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[2], 0) rectangle ($x[3], $y[2]); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[3], 0) rectangle ($x[4], $y[3]); END_TIKZ # Graph of the right Riemann sum rectangles $graph2 = createTikZImage(); $graph2->tikzLibraries('arrows.meta'); $graph2->BEGIN_TIKZ -\tikzset{>={Stealth[scale=2]}} +\tikzset{>={Stealth[scale = 2]}} \Large % Make the fonts a little bigger. \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-1,-1) rectangle (9,9); -\draw[->] (-1,0) -- (9,0) node[above left,outer sep=3pt] {\(x\)}; -\foreach \x in {1,...,8} \draw(\x,5pt) -- (\x,-5pt) node [below] {\(\x\)}; -\draw[->] (0,-1) -- (0,9) node[below right,outer sep=3pt] {\(y\)}; -\foreach \y in {1,...,8} \draw (5pt,\y) -- (-5pt,\y) node[left] {\(\y\)}; -\draw[<->] plot[domain=-1:9] (\x,{\x*\x/$c}); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[0],0) rectangle ($x[1],$y[1]); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[1],0) rectangle ($x[2],$y[2]); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[2],0) rectangle ($x[3],$y[3]); -\filldraw[draw=blue,fill=blue!50!white, fill opacity = 0.5] - ($x[3],0) rectangle ($x[4],$y[4]); + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-1, -1) rectangle (9, 9); +\draw[->] (-1, 0) -- (9, 0) node[above left, outer sep = 3pt] {\(x\)}; +\foreach \x in {1, ..., 8} \draw (\x, 5pt) -- (\x, -5pt) node [below] {\(\x\)}; +\draw[->] (0, -1) -- (0, 9) node[below right, outer sep = 3pt] {\(y\)}; +\foreach \y in {1,...,8} \draw (5pt, \y) -- (-5pt, \y) node[left] {\(\y\)}; +\draw[<->] plot[domain = -1:9] (\x, {\x * \x / $c}); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[0], 0) rectangle ($x[1], $y[1]); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[1], 0) rectangle ($x[2], $y[2]); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[2], 0) rectangle ($x[3], $y[3]); +\filldraw[draw = blue, fill = blue!50!white, fill opacity = 0.5] + ($x[3], 0) rectangle ($x[4], $y[4]); END_TIKZ $leftEstimateDropDown = DropDown( @@ -141,9 +148,32 @@ $rightEstimateDropDown = DropDown( 0 ); +$leftRiemannSumAltText = + "The graph of a parabola opening up with vertex at the point (0, 0) is " + . "shown. Rectangles above the intervals from $x[0] to $x[1], $x[1] to " + . "$x[2], $x[2] to $x[3], and $x[3] to $x[4] with heights $y[0], $y[1], " + . "$y[2], and $y[3], respectively are also shown."; + +$rightRiemannSumAltText = + "The graph of a parabola opening up with vertex at the point (0, 0) is " + . "shown. Rectangles above the intervals from $x[0] to $x[1], $x[1] to " + . "$x[2], $x[2] to $x[3], and $x[3] to $x[4] with heights $y[1], $y[2], " + . "$y[3], and $y[4], respectively are also shown."; + #:% section = statement #: The weights for the weighted grader are assigned by passing the `weight` flag #: to the `cmp` method. +#: +#: Note that there are two spaces at the end of the line +#: `>>[!left Riemann sum!]{$graph1}{250}<<` as well as at the end of the +#: equivalent line for the right Riemann sum (these may not be visible on this +#: page). Those spaces are a PGML line break, and make it so that the image +#: caption is closer to the image than a paragraph break (an empty line in PGML) +#: would give. +#: +#: It is also important to provide alternate texts for unsighted users that are +#: using a screen reader that give detailed information as to what is shown in +#: the image. BEGIN_PGML Suppose [``f(x) = \frac{x^2}{[$c]}``]. @@ -154,8 +184,7 @@ endpoint Riemann sum is [_]{Real($sumLeft)->cmp(weight => 45)}{5} and it is by [`y = f(x)`], the [`x`]-axis, and the vertical lines [`x = [$a]`] and [`x = [$b]`]. ->>[@ image($graph1, height => 250, width => 250, tex_size => 450) @]*<< - +>>[![$leftRiemannSumAltText]!]{$graph1}{250}<< >>Left endpoint Riemann sum<< b. The rectangles in the graph below illustrate a right endpoint Riemann sum for @@ -165,8 +194,7 @@ endpoint Riemann sum is [_]{ Real($sumRight)->cmp(weight => 45) }{5} and it is by [`y = f(x)`], the [`x`]-axis, and the vertical lines [`x = [$a]`] and [`x = [$b]`]. ->>[@ image($graph2, height => 250, width => 250, tex_size => 450) @]*<< - +>>[![$rightRiemannSumAltText]!]{$graph2}{250}<< >>Right endpoint Riemann sum<< END_PGML diff --git a/tutorial/sample-problems/IntegralCalc/VolumeOfRevolution.pg b/tutorial/sample-problems/IntegralCalc/VolumeOfRevolution.pg index ff166c790d..ca16ad4308 100644 --- a/tutorial/sample-problems/IntegralCalc/VolumeOfRevolution.pg +++ b/tutorial/sample-problems/IntegralCalc/VolumeOfRevolution.pg @@ -17,36 +17,38 @@ #:% categories = [volume, solid of revolution, disk method] #:% section = preamble -#: We load `weightedGrader.pl` and install it. We load `answerHints.pl` to give -#: student feedback on particular incorrect answers. We load `niceTables.pl` so -#: that we can construct tables in HTML mode that will make the answer blanks -#: for the limits of integration appear at the top and bottom of the integral -#: symbol. +#: Load the PODLINK('weightedGrader.pl') macro so that weights can be assigned +#: to the various answers in the problem. Load the PODLINK('answerHints.pl') +#: macro to give student feedback on particular incorrect answers. Note that the +#: PODLINK('niceTables.pl') macro is loaded via the `PGML.pl` macro, and is used +#: to construct a table that will make the answer rules for the limits of +#: integration appear at the top and bottom of the integral symbol. #: -#: If the weighted grader is to be used, the command -#: `install_weighted_grader();` must be called. +#: Then call the `install_weighted_grader` method to use the weighted grader. DOCUMENT(); loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'niceTables.pl', 'answerHints.pl', - 'weightedGrader.pl', 'PGcourse.pl' + 'PGstandard.pl', 'PGML.pl', + 'answerHints.pl', 'weightedGrader.pl', + 'PGcourse.pl' ); install_weighted_grader(); #:% section = setup -#: To keep the code that needs to be modified compartmentalized, we define the -#: functions involved, the limits of integration, the integrand, the volume, and -#: an array of weights (which sum to 100) for each of these answers. +#: First, the variables `x`, `dx`, `y`, and `dy` are added to the context. #: -#: The code for correctly displaying the answer blanks creates `$integral` which -#: will be displayed correctly both in TeX and HTML modes. Notice that it uses -#: `NAMED_ANS_RULE(name, width)` for all of the answer blanks instead of -#: `ans_rule(width)`. +#: Then the limits of integration, the integrand, and the volume are computed. #: -#: We define `$integral` to display the integral and answer -#: blanks correctly using a `LayoutTable` from PODLINK('niceTables.pl'). +#: Finally, answer names are generated for all of the answers in the problem. +#: Never use hard coded made up answer names in problems. Always obtain an +#: answer name using the `NEW_ANS_NAME` method. Problems that use hard coded +#: made up answer names will not work correctly in many situations. Also note +#: that answer names are not actually needed for this problem. They could be +#: removed entirely and it would still function correctly. However, they are +#: needed for the method to give full credit for a correct volume answer +#: if the answers given for the other answer rules are left blank (or are +#: correct) that is described in the statement section. Context()->variables->are(x => 'Real', dx => 'Real', y => 'Real', dy => 'Real'); $upper = Real('1'); @@ -54,90 +56,77 @@ $lower = Real('0'); $int = Compute('(pi x^2 - pi x^4) dx'); $vol = Compute('2pi/15'); -$integral = LayoutTable( - [ - [ ' ', NAMED_ANS_RULE('upperlimit', 4) ], - [ - '\(V= \)', - '\(\displaystyle \int \;\;\)' - . NAMED_ANS_RULE('integrand', 10) - . '\(\;=\; \)' - . NAMED_ANS_RULE('volume', 4) - ], - [ ' ', NAMED_ANS_RULE('lowerlimit', 4) ], - ], - align => 'rl', - valign => 'middle', - allcellcss => { padding => '3pt' } -); +$upperLimit = NEW_ANS_NAME(); +$lowerLimit = NEW_ANS_NAME(); +$integrand = NEW_ANS_NAME(); +$volume = NEW_ANS_NAME(); -@weights = (5, 5, 40, 50); #:% section = statement -#: Standard PGML and latex is used to describe the problem. The integral -#: that was formatted using a table above is inserted with `[$integral]*` +#: Standard PGML is used to describe the problem. #: -#: A note is added that specifies for the students how the -#: answer will be graded (the `weightedGrader.pl` macro does not do this -#: automatically, as some other graders do.) +#: Then the integral is formatted with a `LayoutTable` from the +#: PODLINK('niceTables.pl') macro using its `PGML` syntax. Note that each answer +#: rule is given the appropriate name from the names generated before in the +#: last answer rule option. In addition the weight for each answer is passed via +#: the `weight` option to the `cmp` method. +#: +#: Answer hints for various incorrect answers are provided for the integrand +#: answer by adding `->withPostFilter(AnswerHints(...))` to the `cmp` call. +#: +#: If you would like to give full credit for a correct volume answer if the +#: answers given for the other answer rules are left blank (or are correct), +#: then change +#: `[.[_]{$vol->cmp(weight => 50)}{4}{$volume}.]*` to +#: ```{.perl} +#: [. +#: [_]{ +#: $vol->cmp( +#: weight => 50, +#: credit => [ $upperLimit, $lowerLimit, $integrand ] +#: ) +#: }{4}{$volume} +#: .]* +#: ``` +#: +#: If you want to give equal credit for all answers, then remove the weight +#: options from the `cmp` calls. BEGIN_PGML Set up and evaluate an integral for the volume of the solid of revolution obtained by rotating the region bounded by [`y = x`] and [`y = x^2`] about the [`x`]-axis. -[$integral]* +[# + [. .] [.[_]{$upper->cmp(weight => 5)}{4}{$upperLimit}.]* -[@ MODES( - TeX => '', - HTML => << "END_HTML" -${BITALIC}${BBOLD}Note:${EBOLD} You can earn -$weights[0]${PERCENT} for the upper limit of integration, -$weights[1]${PERCENT} for the lower limit of integration, -$weights[2]${PERCENT} for the integrand, and -$weights[3]${PERCENT} for the finding the volume. -${EITALIC} -END_HTML -) @]* -END_PGML + [.[`V =`].] + [.[``\int``].] + [. + [_]{ + $int->cmp(weight => 40)->withPostFilter(AnswerHints( + Formula('pi x^2 - pi x^4 dx') => + "Don't forget to multiply every term in the integrand by dx", + Formula('pi(x^2 - x^4)') => "Don't forget the differential dx", + Formula('pi(x^4 - x^2)dx') => 'Is the parabola above the line?', + Formula('pi(x^4 - x^2)') => 'Is the parabola above the line?', + Formula('pi(x - x^2)') => 'Make sure you use the disk method.', + Formula('pi(x - x^2)dx') => 'Make sure you use the disk method.', + )) + }{10}{$integrand} + .] + [.[`\;=\;`].] + [.[_]{$vol->cmp(weight => 50)}{4}{$volume}.]* -#:% section = answer -#: To install the answer evaluator call -#: `NAMED_WEIGHTED_ANS(name => $answer->cmp()->withPostFilter(), weight)` -#: instead of using `ANS($answer->cmp()->withPostFilter())`. Providing -#: customized answer hints for students is a good idea, because the whole point -#: of this homework exercise it to learn how to set up this integral using -#: proper notation. If we just wanted to ask for the volume, we could have done -#: it using only one answer blank. -#: -#: If you would like to give full credit for the overall volume, you can -#: replace the last `NAMED_WEIGHTED_ANS` with -#:```{.perl} -#:CREDIT_ANS($vol->cmp, [ 'upperlimit', 'lowerlimit', 'integrand' ], $weights[3]); -#:``` -#: -#: Of course, if you want to give equal credit, then each of the -#: `NAMED_WEIGHTED_ANS` commands can be replaced with `ANS`, however -#: make sure that they listed in order. -NAMED_WEIGHTED_ANS(upperlimit => $upper->cmp, $weights[0]); -NAMED_WEIGHTED_ANS(lowerlimit => $lower->cmp, $weights[1]); -NAMED_WEIGHTED_ANS( - integrand => $int->cmp->withPostFilter(AnswerHints( - Formula('pi x^2 - pi x^4 dx') => - "Don't forget to multiply every term in the integrand by dx", - Formula('pi (x^2 - x^4)') => "Don't forget the differential dx", - Formula('pi(x^4 - x^2)dx') => 'Is the parabola above the line?', - Formula('pi(x^4 - x^2)') => 'Is the parabola above the line?', - Formula('pi(x - x^2)') => 'Make sure you use the disk method.', - Formula('pi(x - x^2)dx') => 'Make sure you use the disk method.', - )), - $weights[2] -); -NAMED_WEIGHTED_ANS('volume' => $vol->cmp, $weights[3]); + [. .] [.[_]{$lower->cmp(weight => 5)}{4}{$lowerLimit}.] +#]*{ + align => 'rl', + valign => 'middle', + allcellcss => { padding => '3pt' } +} +END_PGML #:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION -COMMENT('Weights each answer blank separately.'); - ENDDOCUMENT(); diff --git a/tutorial/sample-problems/LinearAlgebra/MatrixAnswer1.pg b/tutorial/sample-problems/LinearAlgebra/MatrixAnswer1.pg index f79ce10cdc..d381b8a6fa 100644 --- a/tutorial/sample-problems/LinearAlgebra/MatrixAnswer1.pg +++ b/tutorial/sample-problems/LinearAlgebra/MatrixAnswer1.pg @@ -22,14 +22,16 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: Use `Context('Matrix');`. MathObject matrices are constructed using the `Matrix()` -#: constructor. The matrix `A` has two rows and three columns, and is constructed by -#: `[[row 1 entries], [row 2 entries]]`, and this construction generalizes in the -#: obvious way. If a matrix has only one row, such as `B`, then it is entered as -#: `[row 1 entries]` and not as `[ [row 1 entries] ]`. If `$B = Matrix([a,b,c]);`, then -#: the matrix `$B->transpose` is equivalent to `Matrix([[a],[b],[c]]);` which has an outer -#: pair of brackets enclosing all of the rows, where each row encloses its single element -#: with brackets. +#: Call `Context('Matrix')` to switch to the `Matrix` context. MathObject +#: matrices are constructed using the `Matrix` method. The matrix `A` has two +#: rows and three columns, and is constructed by +#: `[ [row 1 entries], [row 2 entries] ]`, and this construction generalizes in +#: the obvious way. If a matrix has only one row, such as `B`, then the outer +#: brackets can be omitted. So it can be entered as `[row 1 entries]` or as +#: `[ [row 1 entries] ]`. If `$B = Matrix([a, b, c])`, then the matrix +#: `$B->transpose` is equivalent to `Matrix([ [a], [b], [c] ])` which has an +#: outer pair of brackets enclosing all of the rows, where each row encloses its +#: single element with brackets. Context('Matrix'); $A = Matrix([ diff --git a/tutorial/sample-problems/LinearAlgebra/MatrixAnswer2.pg b/tutorial/sample-problems/LinearAlgebra/MatrixAnswer2.pg index 21511bee51..1c90646e38 100644 --- a/tutorial/sample-problems/LinearAlgebra/MatrixAnswer2.pg +++ b/tutorial/sample-problems/LinearAlgebra/MatrixAnswer2.pg @@ -15,7 +15,7 @@ #:% name = Matrix Answer Alternative #:% type = Sample #:% subject = linear algebra -#:% categories = [matrix, answer] +#:% categories = [matrix, answers] #:% section = preamble DOCUMENT(); @@ -23,20 +23,39 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: Use `Context('Matrix');`. We construct a 2 by 3 matrix and extract its first column and first row. +#: Switch to the `Matrix` context by calling `Context('Matrix')`. +#: +#: Then construct a 2 by 3 matrix and extract its first column and first row. +#: These matrices will be answers in which the entire matrix will be expected to +#: be entered into a single answer rule. +#: +#: Finally, construct another 2 by 3 matrix which is a copy of the first. This +#: matrix will be an answer in which each entry of the matrix will have its own +#: answer rule. These answer rules are inserted via the `ans_array` method. The +#: answer rules are inserted into `PGML` with `[_]*`, and the asterisk tells +#: `PGML` to use `ans_array` instead of `ans_rule`. +#: +#: Note that it is important to use another matrix for `$example4` or a copy as +#: is done here. Do not attempt to use `$example1` for both a single answer rule +#: question and a answer array question (or even for multiple answers in +#: general). MathObjects change internal values when used for an answer, and +#: those changed internal values will cause conflicts if the matrix is used +#: again for another answer. Context('Matrix'); $example1 = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ] ]); $example2 = $example1->column(1); $example3 = $example1->row(1); +$example4 = $example1->copy; + #:% section = statement BEGIN_PGML The purpose of this question is to show you the syntax needed to enter matrices in WeBWorK when there is only one answer box for entering a matrix (which is not obvious) or when there are multiple answer boxes for entering a matrix (which is -obvious). The examples below should be self-explanatory, so you can jump to -them if you want; however, a detailed explanation follows if you want to read +obvious). The examples below should be self explanatory, so you can jump to +them if you want. However, a detailed explanation follows if you want to read more. Matrices use square brackets to enclose items in lists. A matrix with one row, @@ -53,38 +72,19 @@ answers may have spaces and line breaks in them, such as >> [| [ [1, 2, 3], |] << >> [| [4, 5, 6] ] |] << -+ Enter the matrix [``[$example1]``] as [@ $example1->string @]* -[@ ans_box(3,30) @]* - -+ Enter the column vector [``[$example2]``] as [@ $example2->string @]* -[@ ans_box(3,30) @]* - -+ Enter the row vector [``[$example3]``] as [@ $example3->string @]* -[@ ans_box(3,30) @]* -END_PGML - -#:% section = answer -#: Because an `ans_box` is used, we need to use the older style answer checkers. - -ANS($example1->cmp); -ANS($example2->cmp); -ANS($example3->cmp); ++ Enter the matrix [``[$example1]``] as [@ $example1->string @]*: +[_]{$example1}{20} -#:% section = setup2 -#: Reset the context because the matrix answer checker gets confused -#: when the `ans_box` and `ans_array` methods are co-mingled. -Context('Matrix'); ++ Enter the column vector [``[$example2]``] as [@ $example2->string @]*: +[_]{$example2}{10} -$example4 = Matrix([ [ 1, 2, 3 ], [ 4, 5, 6 ] ]); ++ Enter the row vector [``[$example3]``] as [@ $example3->string @]*: +[_]{$example3}{10} -#:% section = statement2 -#: This is the other method to entering matrices. -BEGIN_PGML -[$BR]* -+ Entering a matrix using multiple answer blanks is straightforward -- -just put each matrix entry into its own answer blank. -Enter the matrix [`` [$example4] ``] with one matrix entry per answer box. -[______]*{$example4} ++ Entering a matrix using multiple answer blanks is straightforward. Just put +each matrix entry into its own answer blank. +Enter the matrix [``[$example4]``] with one matrix entry per answer box. +[______]*{$example4}{4} END_PGML ENDDOCUMENT(); diff --git a/tutorial/sample-problems/LinearAlgebra/MatrixCustomAnswerChecker.pg b/tutorial/sample-problems/LinearAlgebra/MatrixCustomAnswerChecker.pg index 9e43abaffb..0fe858b31e 100644 --- a/tutorial/sample-problems/LinearAlgebra/MatrixCustomAnswerChecker.pg +++ b/tutorial/sample-problems/LinearAlgebra/MatrixCustomAnswerChecker.pg @@ -14,11 +14,11 @@ #:% name = Custom Matrix Answer Checker #:% type = Sample #:% subject = linear algebra -#:% categories = [answer, matrix] +#:% categories = [answers, matrix] #:% section = preamble -#: Since the answer will depend on the two matrices input, we need to use `parserMultiAnswer.pl`. - +#: Since the answer will depend on two matrix inputs at once, the +#: PODLINK('parserMultiAnswer.pl') macro must be loaded. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); @@ -26,14 +26,14 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); $showPartialCorrectAnswers = 0; #:% section = setup -#: Construct two matrices `$A` and `$B` that do not commute and therefore serve as a correct answer. -#: Use a $multians object with a custom answer checker subroutine. The answer checker uses -#: `my ( $correct, $student, $answerHash ) = @_;` to grab the inputs (the correct answer, -#: the student answer, and the answer hash table info). Then, put the student's -#: two answers into an array using `my @s = @{$student};`. Make sure the student's -#: first matrix `$s[0]` is converted to a `MathObject` matrix using `$s0 = Matrix($s[0]);` -#: and similarly for the student's second matrix. The return value, which is boolean, is -#: the truth value of the statement `$s0 * $s1 != $s1 * $s0`. +#: Construct two matrices `$A` and `$B` that do not commute. Use a `$multians` +#: object with a custom answer checker subroutine. The answer checker uses +#: `my ($c, $s, $ansHash) = @_` to extract the inputs (the correct answer, the +#: student answer, and the answer hash). The checker returns 1 if +#: `$s->[0] * $s->[1] != $s->[1] * $s->[0]` is true and 0 otherwise. Note that +#: `$s->[0]` is the MathObject representation of the student's answer for the +#: first matrix, and $s->[1] is the MathObject representation of the student's +#: answer for second matrix. Context('Matrix'); $A = Matrix([ [ 1, 1 ], [ 0, 1 ] ]); @@ -42,16 +42,14 @@ $B = Matrix([ [ 1, 0 ], [ 1, 1 ] ]); $multians = MultiAnswer($A, $B)->with( singleResult => 1, checker => sub { - my ($correct, $student, $answerHash) = @_; - my @s = @{$student}; - $s0 = Matrix($s[0]); - $s1 = Matrix($s[1]); - return $s0 * $s1 != $s1 * $s0; + my ($c, $s, $ansHash) = @_; + return $s->[0] * $s->[1] != $s->[1] * $s->[0] ? 1 : 0; } ); #:% section = statement -#: Make sure that both answer arrays are called as methods on the `$multians` object +#: Make sure that both answer arrays are called as methods on the `$multians` +#: object. BEGIN_PGML Give an example of two [`2 \times 2`] matrices [`A`] and [`B`] such that [`AB \ne BA`] . diff --git a/tutorial/sample-problems/LinearAlgebra/MatrixOperations.pg b/tutorial/sample-problems/LinearAlgebra/MatrixOperations.pg index 5b47b8dca9..6c575811a0 100644 --- a/tutorial/sample-problems/LinearAlgebra/MatrixOperations.pg +++ b/tutorial/sample-problems/LinearAlgebra/MatrixOperations.pg @@ -17,7 +17,7 @@ #:% categories = [matrix] #:% section = preamble -#: This uses `parserRadioMultiAnswer.pl`, so it needs to be loaded. +#: This uses PODLINK('parserRadioMultiAnswer.pl'), so it needs to be loaded. DOCUMENT(); loadMacros( @@ -28,18 +28,21 @@ loadMacros( #:% section = setup #: First, the two matrices are defined. #: -#: A `RadioMultiAnswer` produces a set of radio buttons for each of the given statements. -#: The format is -#:```{#radio-multi-answer-usage .perl} +#: The `RadioMultiAnswer` method produces a set of radio buttons for each of the +#: given statements. The format is +#: +#: ```{#radio-multi-answer-usage .perl} #: RadioMultiAnswer([ #: [statement1], #: [statement2], #: ... #: [last statement] #: ], correct index, options) -#:``` -#: Answer blanks can be added with the `%s` in the string and if a matrix is desired, -#: `%s*` should be used. See the POD for more details. +#: ``` +#: +#: Answer blanks can be added with the `%s` in the string and if a matrix is +#: desired, `%s*` should be used. See the PODLINK('parserRadioMultiAnswer.pl') +#: for more details. Context('Matrix'); $A = Matrix([ diff --git a/tutorial/sample-problems/LinearAlgebra/RowOperations.pg b/tutorial/sample-problems/LinearAlgebra/RowOperations.pg index 61173bc987..ed6a5ae0f9 100644 --- a/tutorial/sample-problems/LinearAlgebra/RowOperations.pg +++ b/tutorial/sample-problems/LinearAlgebra/RowOperations.pg @@ -21,13 +21,14 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: Construct a matrix with three distinct rows. Create a string `$op` of Tex code that -#: describes the row operation. Use `$A->row(i)` to extract the ith row of the matrix A -#: as a MathObject. Use `$A->row(1) + $k*$A->row(2)` to perform the row operation and -#: place it into the first row of the answer matrix. +#: Construct a matrix with three distinct rows. Create a string `$op` of Tex +#: code that describes the row operation. Use `$A->row(i)` to extract the ith +#: row of the matrix A as a MathObject. Use `$A->row(1) + $k * $A->row(2)` to +#: perform the row operation and place it into the first row of the answer +#: matrix. #: -#: The do-until loop ensures that no two rows are identical. This is not necessary -#: for this problem, but can be helpful in other situations. +#: The do/until loop ensures that no two rows are identical. This is not +#: necessary for this problem, but can be helpful in other situations. Context('Matrix'); do { @@ -46,7 +47,7 @@ $op = "R_{1} + $k R_{2} \rightarrow R_{1}"; $ans = Matrix([ $A->row(1) + $k * ($A->row(2)), $A->row(2), $A->row(3), ]); #:% section = statement -#: Remember when using a matrix answer blank in PGML, to append a * +#: Remember to append a `*` to use an array answer rule in PGML. BEGIN_PGML Give the result of applying the row operation [`[$op]`] to the given matrix. diff --git a/tutorial/sample-problems/Misc/ChemicalReaction.pg b/tutorial/sample-problems/Misc/ChemicalReaction.pg index 04530f796e..d2a97dd292 100644 --- a/tutorial/sample-problems/Misc/ChemicalReaction.pg +++ b/tutorial/sample-problems/Misc/ChemicalReaction.pg @@ -11,28 +11,25 @@ ## MO(1) ## KEYWORDS('chemical reaction', 'template') -# References: -# http://webwork.maa.org/pod/pg_TRUNK/macros/contextReaction.pl.html -# http://webwork.maa.org/moodle/mod/forum/discuss.php?d=449 - #:% name = Chemical Reaction #:% type = Sample #:% subject = chemistry #:% categories = [chemistry] #:% section = preamble -#: Load `contextReaction.pl` to put chemical reactions/equations in a `Compute` +#: Load PODLINK('contextReaction.pl)` to be able to use chemical reaction +#: equations in a `Compute` call. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'contextReaction.pl', 'PGcourse.pl'); #:% section = setup -#: We create a couple of arrays `@reactants` and `@products` and fill them with -#: some examples of balanced chemical equations. The second and third examples -#: show that groupings, such as for `(OH)_2` are necessary. The third example -#: shows how you could randomize a chemical reaction question. In particular, note -#: that `${b}_2` is needed instead of `$b_2` so that Perl interprets the variable -#: as `$b` with a subscript of 2 instead of a variable named `$b_2` with no subscript. +#: Create two arrays, `@reactants` and `@products`, and fill them with examples +#: of balanced chemical equations. The third example shows that groupings, such +#: as for `(PO_4)_2` are necessary. The fourth example shows how you could +#: randomize a chemical reaction question. In particular, note that `${b}_2` is +#: needed instead of `$b_2` so that Perl interprets the variable as `$b` with a +#: subscript of 2 instead of a variable named `$b_2` with no subscript. Context('Reaction'); @reactants = (); @@ -47,7 +44,7 @@ $products[1] = Formula('C_6 H_12 O_6 + 6 O_2'); $reactants[2] = Formula('3 Ca Cl_2 + 2 Na_3 PO_4'); $products[2] = Formula('Ca_3 (PO_4)_2 + 6 Na Cl'); -# variations on 2NaOH + MgCl_2 --> 2NaCl + Mg(OH)_2 +# Variations on 2NaOH + MgCl_2 --> 2NaCl + Mg(OH)_2 $a = list_random('Li', 'Na', 'K'); $b = list_random('F', 'Cl', 'Br'); @@ -62,13 +59,14 @@ $i = random(0, $#reactants, 1); #: This is a way to print out the four reactions in a for loop. for $i (0 .. 3) { BEGIN_PGML -[`[$reactants[$i]] \longrightarrow `] [_]{$products[$i]}{10} +[`[$reactants[$i]] \longrightarrow`] [_]{$products[$i]}{10} END_PGML } BEGIN_PGML -Enter a subscript using an underscore, such as [|H_2 O|]* for [`\mathrm{H_2 O}`]. +Enter a subscript using an underscore, such as [|H_2 O|]* for +[`\mathrm{H_2 O}`]. END_PGML ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Misc/DraggableProof.pg b/tutorial/sample-problems/Misc/DraggableProof.pg index 2cc57816ed..463451bdd8 100644 --- a/tutorial/sample-problems/Misc/DraggableProof.pg +++ b/tutorial/sample-problems/Misc/DraggableProof.pg @@ -17,39 +17,42 @@ #:% categories = [proof, draggable] #:% section = preamble -#: This problem uses the `draggableProof.pl` macro to display "buckets" that the -#: student can drag statements to and from. +#: This problem uses the PODLINK('draggableProof.pl') macro to display "buckets" +#: that the student can drag statements to and from. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'draggableProof.pl', 'PGcourse.pl'); #:% section = setup -#: The `DraggableProof` function takes an arrayref of correct statements, -#: followed (optionally) by extra statements. See -#: PODLINK('the Draggable Proof POD', 'draggableProof.pl') for more options. +#: The `DraggableProof` function takes an array reference of correct statements +#: in the correct order, followed (optionally) by another array reference of +#: extra statements. See +#: PODLINK('the draggable proof documentation', 'draggableProof.pl') for more +#: options. $proof = DraggableProof( # These are the correct statements of the proof in the correct order. [ - 'Assume \(\sqrt{2} = \frac{a}{b}\) where \(a,b\) are integers, with \(\text{gcd}(a,b) = 1\)', - '\(2 = \frac{a^2}{b^2}\)', - '\(a^2 = 2b^2\)', - 'if \(a^2\) is even, then \(a\) must be even', - 'Let \(a = 2k\) for \(k\) some integer', - 'We can then write \(2 = \frac{4k^2}{b^2}\) or \(b^2 = 2k^2\)', - 'Therefore \(b^2\) is even, so \(b\) is also even', - 'If \(a\) and \(b\) are both even, then the initial assumption that \(\text{gcd}(a,b) = 1\) is contradicted.', - '\(\sqrt{2}\) is therefore not rational.' + 'Assume \(\sqrt{2} = \frac{a}{b}\) where \(a\) and \(b\) are integers with \(\gcd(a,b) = 1\).', + 'Then \(2 = \frac{a^2}{b^2}\).', + 'So \(a^2 = 2b^2\).', + 'Thus \(a^2\) is even which implies that \(a\) must also be even.', + 'Let \(a = 2k\) for \(k\) some integer.', + 'We can then write \(2 = \frac{4k^2}{b^2}\) which implies that \(b^2 = 2k^2\).', + 'Thus \(b^2\) is even, and so \(b\) must also be even.', + 'Hence \(a\) and \(b\) are both even, and this contradicts the initial assumption that \(\gcd(a,b) = 1\).', + 'Therefore \(\sqrt{2}\) is not rational.' ], # These are extra statements that are not needed. [ - 'Then \(a\) is odd', - '\(b^2\) cannot be rational.', - 'therefore \(a = 2b\)' + 'Then \(a\) is odd.', + 'Thus \(b^2\) cannot be rational.', + 'Therefore \(a = 2b\).' ] ); #:% section = statement -#: The line `[_]{$proof}` prints the statement and options in the proof and sets up the answer rule. +#: The line `[_]{$proof}` prints the statement and options in the proof and sets +#: up the answer rule. BEGIN_PGML Prove that [`\sqrt{2}`] is irrational. diff --git a/tutorial/sample-problems/Misc/DynamicGraphPolygon.pg b/tutorial/sample-problems/Misc/DynamicGraphPolygon.pg index b589354cf1..0a82a80893 100644 --- a/tutorial/sample-problems/Misc/DynamicGraphPolygon.pg +++ b/tutorial/sample-problems/Misc/DynamicGraphPolygon.pg @@ -16,48 +16,49 @@ #:% see_also = [DynamicGraph.pg] #:% section = preamble -#: The dynamic graphs are generated with `PGtikz.pl`, so this is needed. +#: The dynamic graphs are generated with PODLINK('PGtikz.pl'), so load that +#: macro. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); #:% section = setup -#: See PROBLINK('DynamicGraph.pg') for basics of using tikz. +#: See PROBLINK('DynamicGraph.pg') for the basics of using TikZ. #: -#: Since we make three plots with the same setup, all of the commands that -#: are common to the graphs are defined in the same perl string. +#: Since three plots are created with the same setup, this common setup is +#: defined in the Perl string `$plot_setup`. #: #: Each of the plots uses the `\filldraw` command. Options for this are #: -#:* `fill`: the color of the fill region -#:* `draw`: the color of the boundary. -#:* `very thick`: the thickness of the boundary -#:* `opacity`: the opacity (between 0 and 1) of the fill region. +#: * `fill`: the color of the fill region +#: * `draw`: the color of the boundary. +#: * `very thick`: the thickness of the boundary +#: * `opacity`: the opacity (between 0 and 1) of the fill region. #: #: In the polygon and region under the curve, the `draw` method uses the -#: verticies (or the `plot` command which uses the curve itself) and -#: ends with `cycle` indicated that the region is closed. +#: vertices (or the `plot` command which uses the curve itself) and ends with +#: `cycle` indicating that the region is closed. #: -#: The colors are defined in the LaTeX LINK('xcolor package','https://mirrors.mit.edu/CTAN/macros/latex/contrib/xcolor/xcolor.pdf') -#: -# The setup for each plot is the same, so we'll use a perl block for this. +#: The colors are defined in the LaTeX +#: LINK('xcolor package','https://mirrors.mit.edu/CTAN/macros/latex/contrib/xcolor/xcolor.pdf') $plot_setup = qq/ -\tikzset{>={Stealth[scale=1.5]}} +\tikzset{>={Stealth[scale = 1.5]}} \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-5,5) rectangle (5,-5); -\draw[lightgray, dashed] (-5,-5) grid (5,5); -\draw (-5,0) -- (5,0) node [below left] {\(x\)}; -\foreach \x in {-4,...,-1,1,2,...,4} \draw(\x,-4.5) node {\(\x\)}; -\draw (0,-5) -- (0,5) node [below right] {\(y\)}; -\foreach \y in {-4,...,-1,1,2,...,4} \draw(-4.5,\y) node {\(\y\)}; + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-5, 5) rectangle (5, -5); +\draw[lightgray, dashed] (-5, -5) grid (5, 5); +\draw[->] (-5, 0) -- (5, 0) node[below left] {\(x\)}; +\foreach \x in {-4, ..., -1, 1, 2, ..., 4} \draw (\x, -4.5) node {\(\x\)}; +\draw[->] (0, -5) -- (0, 5) node[below right] {\(y\)}; +\foreach \y in {-4, ..., -1, 1, 2, ..., 4} \draw (-4.5, \y) node {\(\y\)}; /; -# The vertices of the triangle chosen randomly will be ($x0,$y0), ($x1,$y0) -# and ($x0,$y1). +# The vertices of the triangle chosen randomly will be ($x0, $y0), ($x1, $y0) +# and ($x0, $y1). $x0 = random(-3, -1); $x1 = random(1, 3); $y0 = random(-3, -1); @@ -67,24 +68,32 @@ $graph1 = createTikZImage(); $graph1->tikzLibraries('arrows.meta'); $graph1->BEGIN_TIKZ $plot_setup; -\filldraw[very thick, fill=LightGreen, draw=DarkGreen, opacity=0.5] ($x0,$y0) - -- ($x1,$y0) -- ($x0,$y1) -- cycle; +\filldraw[very thick, fill = LightGreen, draw = DarkGreen, opacity = 0.5] + ($x0, $y0) -- ($x1, $y0) -- ($x0, $y1) -- cycle; END_TIKZ +$graph1AltText = + "A filled triangle with vertices at ($x0, $y0), ($x1, $y0), and ($x0, $y1)."; + # A plot of 1+sqrt(x) and shade underneath the graph. $graph2 = createTikZImage(); $graph2->tikzLibraries('arrows.meta'); $graph2->BEGIN_TIKZ $plot_setup; - -\filldraw[fill=LightBlue, opacity=0.5, draw=blue] (1,1) - -- plot[domain=1:4, smooth] (\x,{1+sqrt(\x)}) - -- (4,0) -- (1,0) -- cycle; -\draw[very thick, DarkBlue] plot [domain=0:5, smooth] (\x,{1+sqrt(\x)}); +\filldraw[fill = LightBlue, opacity = 0.5, draw = blue] + (1,0) -- plot[domain = 1:4] (\x, {1 + sqrt(\x)}) -- (4,0) -- cycle; +\draw[very thick, DarkBlue] + plot [samples = 100, domain = 0:5, smooth] (\x, {1 + sqrt(\x)}); END_TIKZ -# A circle with center ($x0,$y0) +$graph2AltText = + 'The graph of a curve that starts at (0, 1) and increases to the right, ' + . 'increasing sharply at first, and less so as it continues to the right. ' + . 'The region above the x-axis and below the curve between ' + . 'x = 1 and x = 4 is filled.'; + +# A circle of radius 3 centered at ($x, $y). $x = random(-2, 2); $y = random(-2, 2); @@ -92,23 +101,22 @@ $graph3 = createTikZImage(); $graph3->tikzLibraries('arrows.meta'); $graph3->BEGIN_TIKZ $plot_setup; - -\filldraw[very thick, fill=LightSalmon, opacity=0.5, draw=DarkOrange] - circle[radius=3] ($x,$y); +\filldraw[very thick, fill = LightSalmon, opacity = 0.5, draw = DarkOrange] + ($x, $y) circle[radius = 3]; END_TIKZ +$graph3AltText = "A circle of radius 3 centered at ($x, $y)."; + #:% section = statement -#: Note that the tikz graph in `$graph1`, `$graph2` and `$graph3` to be shown -#: is placed in the `image` function -#: and since this is a function, it must go in a `[@ ... @]*` block. +#: Note that the TikZ graphs in `$graph1`, `$graph2` and `$graph3` are inserted +#: in the problem text via the `PGML` image syntax with their corresponding +#: alternate texts for screen reader users. BEGIN_PGML +[![$graph1AltText]!]{$graph1}{400} -[@ image($graph1, width => 400) @]* - -[@ image($graph2, width => 400) @]* - -[@ image($graph3, width => 400) @]* +[![$graph2AltText]!]{$graph2}{400} +[![$graph3AltText]!]{$graph3}{400} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Misc/EssayAnswer.pg b/tutorial/sample-problems/Misc/EssayAnswer.pg index fc75f21a1a..0e00701c65 100644 --- a/tutorial/sample-problems/Misc/EssayAnswer.pg +++ b/tutorial/sample-problems/Misc/EssayAnswer.pg @@ -11,72 +11,60 @@ ## MO(1) ## KEYWORDS('essay answer', 'template') -# References: -# http://webworkgoehle.blogspot.com/2012/09/essay-answers-in-webwork.html - #:% name = Essay Answer #:% type = [Sample, technique] #:% categories = [essay] #:% section = preamble -#: Use the `PGessaymacros.pl` for the essay answer -#: and `parserPopUp.pl` for the multiple choice drop -#: down menu. Setting `$showPartialCorrectAnswers = 0;` -#: means that students will not receive feedback on whether -#: their answers are correct. -#: The all-or-nothing problem grader (the standard problem grader) -#: is used in order to withhold assigning any credit when the student -#: submits an answer. This allows the professor to manually determine -#: what percentage the student should get. -#: If the standard problem grader was not used here, then the default -#: problem grader (the average problem grader) would award -#: 50 percent credit to students who answer the multiple choice -#: question correct. +#: This uses PODLINK('PGessaymacros.pl') for essay answers and +#: PODLINK('parserPopUp.pl') for multiple choice drop down menus. DOCUMENT(); + loadMacros( 'PGstandard.pl', 'PGML.pl', 'parserPopUp.pl', 'PGessaymacros.pl', 'PGcourse.pl' ); -$showPartialCorrectAnswers = 0; - #:% section = setup $popup = PopUp( [ 'Choose', 'True', 'False' ], # choices - 'False' # corect answer + 'False' # correct answer ); $a = random(2, 5); -$f1 = Compute("ln(x (x-$a))"); -$f2 = Compute("ln(x) + ln(x-$a)"); +$f1 = Compute("ln(x(x - $a))"); +$f2 = Compute("ln(x) + ln(x - $a)"); #:% section = statement -#: Clearly communicate to the student the expectations -#: of the problem and how it will be graded. The `essay_box(w, h)` -#: is resizable and takes inputs for initial width and height. +#: Clearly communicate to the student the expectations of the problem and how it +#: will be graded. The `essay_box(w, h)` method takes inputs for the initial +#: width and height of the text area answer rule. +#: +#: Note that `essay_cmp` is a method that is not associated with any object +#: (i.e., it is **not** `$essay->cmp`). +#: +#: An essay answer must be graded manually by the instructor. Hand grading is +#: done either #: -#: Note that `essay_cmp()` is not associated with any object -#: (i.e., it is **not** `$essay->cmp()`). +#: 1. by viewing the homework set and clicking the **Grade Problem** link in +#: the rightmost column of the problem list, or +#: 2. by clicking the **Show Problem Grader** button in the problem, or +#: 3. by clicking on **Statistics** in the Instructor Tools menu, selecting the +#: homework set, and clicking the **Manual Grader** link under the problem +#: number, or +#: 4. by clicking the **Grade Problem** link after the problem number on the +#: **Set Detail** page. #: -#: The essay answer must be graded manually by the instructor. Hand grading is -#: done either (1) by viewing the homework set and clicking the **Grade problem** -#: link in the rightmost column of the problem list, or (2) by checking the -#: **Problem Grader** checkbox in the problem and clicking -#: **Preview My Answers** or **Check Answers**, or (3) by clicking on -#: **Statistics** in Instructor Tools menu, selecting the homework set, and -#: clicking the **Manual Grader** link under the problem number, or (4) by -#: clicking the **Grade Problem** link after the problem number on the -#: **Set Detail** page. BEGIN_PGML -Answer the following true / false question and then explain your answer. Your -answers will be read and graded manually at a later time. +Determine if the following statement is true or false, and then explain your +answer. Your explanation will be graded manually at a later time. -[_]{$popup} For all real numbers [`x`], [`[$f1] = [$f2]`]. +[_]{$popup}: For all real numbers [`x`], [`[$f1] = [$f2]`]. Please explain your reasoning in the answer box below. -[@ ANS( essay_cmp() ); essay_box(8, 60) @]* +[@ ANS(essay_cmp()); essay_box(8, 60) @]* END_PGML ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Misc/FormulaAnswer.pg b/tutorial/sample-problems/Misc/FormulaAnswer.pg index 7602018506..2db15bee57 100644 --- a/tutorial/sample-problems/Misc/FormulaAnswer.pg +++ b/tutorial/sample-problems/Misc/FormulaAnswer.pg @@ -14,7 +14,7 @@ #:% name = Formula Answer #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [answer] +#:% categories = [answers] #:% section = preamble DOCUMENT(); @@ -22,11 +22,11 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl'); #:% section = setup -#: Use `do { $b = random(2, 9) } until ( $b != $a );` to generate distinct -#: random numbers. +#: Use `do { $b = random(2, 9) } until $b != $a` to generate distinct random +#: numbers. $a = non_zero_random(-9, 9); -do { $b = random(2, 9) } until ($b != $a); +do { $b = random(2, 9) } until $b != $a; $answer1 = Compute("$a"); $answer2 = Compute("($a x^($b) + $b)/x")->reduce(); diff --git a/tutorial/sample-problems/Misc/FormulaDomain.pg b/tutorial/sample-problems/Misc/FormulaDomain.pg index e9130f53d1..c000d6aae5 100644 --- a/tutorial/sample-problems/Misc/FormulaDomain.pg +++ b/tutorial/sample-problems/Misc/FormulaDomain.pg @@ -14,7 +14,7 @@ #:% name = Setting the Domain for Answer Checking #:% type = Sample #:% subject = [algebra, precalculus] -#:% categories = [domain, answer] +#:% categories = [domain, answers] #:% see_also = [FormulaTestPoints.pg] #:% section = preamble @@ -23,27 +23,19 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: Restrict the domain of function evaluation using `$ans1->{limits} = [ $a + 1, $a + 4 ];`, -#: which will choose test points at random in the interval `[$a + 1, $a + 4]`. -#: This will ensure that the test points are in the domain of the function. +#: Restrict the domain of function evaluation using +#: `$ans1->{limits} = [ $a + 1, $a + 4 ]`, which will choose test points at +#: random in the interval `[$a + 1, $a + 4]`. This will ensure that the test +#: points are in the domain of the function. #: -#: The domain for `$ans2` is all real numbers except for `0` and `$a`, and -#: we would like to stay away from these vertical asymptotes because answer evaluators -#: don't work well when the function values are very large or very small. Thus, we -#: explicitly list those test points in the domain that will be used when the function is evaluated. +#: The domain for `$ans2` is all real numbers except for `0` and `$a`, and the +#: test points need to be chosen so that they are not close to these vertical +#: asymptotes because answer evaluators don't work well when the function +#: values are very large or very small. Thus, the test points that are to be +#: used are explicitly listed. #: -#: It is possible to set the domain once for all of the functions within a particular -#: context. For more details, see PROBLINK('FormulaTestPoints.pg'). -#: -#: It is possible to get diagnostic information about the answer checker if one -#: replaces the `{$ans}` with `{$ans1->cmp(diagnostics => 1)}`. -#: When diagnostics are turned on and a student answer is submitted, you will get a graph -#: of the correct answer and the student answer on the same graph, as well as a table that -#: specifies which test points were used by the answer checker, and how much of a difference -#: there was between the student answer and the correct answer at these checkpoints. -#: To test the reliability of your answer checker, it is good to click the reload -#: button on your browser several times after a student answer has been submitted, -#: since reloading changes the test points used. +#: It is also possible to set the domain once for all of the functions within a +#: particular context. See PROBLINK('FormulaTestPoints.pg') for more details. $a = random(2, 5); $ans1 = Compute("sqrt(x - $a)"); @@ -53,6 +45,15 @@ $ans2 = Compute("ln(abs(x / (x - $a)))"); $ans2->{test_points} = [ [-5], [-4], [1], [ $a - 1 ], [7], [8] ]; #:% section = statement +#: It is possible to get diagnostic information about the answer checker by +#: replacing `{$ans1}` with `{$ans1->cmp(diagnostics => 1)}`. When diagnostics +#: are turned on and an answer is submitted, the correct answer and the student +#: answer will be shown on the same graph, as well as a table that specifies +#: which test points were used by the answer checker, and how much of a +#: difference there was between the student answer and the correct answer at +#: these test points. To test the reliability of your answer checker, it is good +#: to click the reload button on your browser several times after a student +#: answer has been submitted, since reloading changes the test points used. BEGIN_PGML a. Enter the answer [``[$ans1] =``] [_]{$ans1} diff --git a/tutorial/sample-problems/Misc/FormulaTestPoints.pg b/tutorial/sample-problems/Misc/FormulaTestPoints.pg index 5b854d1cac..345437da4a 100644 --- a/tutorial/sample-problems/Misc/FormulaTestPoints.pg +++ b/tutorial/sample-problems/Misc/FormulaTestPoints.pg @@ -21,40 +21,33 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: The first line sets the limits of evaluation for any problem in the context -#: to `[-1,1]`. +#: The first line sets the limits of evaluation for the variable `x` and for any +#: answer in the context to `[-1, 1]`. #: -#: Alternatively, the limits on the context can be set with -#:```{.perl} -#:Context()->flags->set(limits=>[2,5]); -#:``` +#: Alternatively, the limits for all variables in the context can be set with +#: `Context()->flags->set(limits => [-1, 1])`. #: -#: For the points for `$g`, note that the domain of `$g` is all values outside -#: of the interval `(-2,2)`. One way to handle this would be to set the `limits` -#: for the function to be outside this interval. Alteratively, as shown, -#: the points are set with the `test_points` field to be a set of points -#: that don't include `(-2,2)`. +#: For another alternative, the limits for the context could be left at their +#: default values, and instead the limits for a specific formula can be set +#: with `$f->{limits} = [-1, 1]`. +#: +#: For the test points of `$g`, note that the domain of `$g` is +#: `(-inf, -2] U [2,inf)`. One way to handle this would be to set the `limits` +#: for the function to be some interval contained in the domain. Alternatively, +#: as shown, the test points can be set with the `test_points` field to be a +#: specific set of values in the domain. Context()->variables->set(x => { limits => [ -1, 1 ] }); -# Alternately -Context()->flags->set(limits => [ 2, 5 ]); - -$f = Compute('sqrt(x+1)'); - -## Or, setting the limits only for the given -## formula, we don't need to reset the Context, -## and just include -# $func = Compute('sqrt(x-1)'); -# $func->{limits} = [2,5]; +$f = Compute('sqrt(x + 1)'); -$g = Compute("sqrt(x^2 - 4)"); +$g = Compute('sqrt(x^2 - 4)'); $g->{test_points} = [ [-3], [-2], [2], [3], [4] ]; #:% section = statement BEGIN_PGML -Enter [`[$f]`] [___]{$f} +Enter [`[$f]`]: [___]{$f} -Enter [`[$g]`] [___]{$g} +Enter [`[$g]`]: [___]{$g} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Misc/IframeEmbedding.pg b/tutorial/sample-problems/Misc/IframeEmbedding.pg index e3245df1b2..474a185e91 100644 --- a/tutorial/sample-problems/Misc/IframeEmbedding.pg +++ b/tutorial/sample-problems/Misc/IframeEmbedding.pg @@ -21,13 +21,13 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: We create a mode dependent variable `$slideshow` that displays a -#: Google slideshow inside an html `iframe` when in html mode, and -#: the message "An embedded Google slide show." in TeX mode. If you omit the -#: TeX mode stuff, then there will be errors when the pdf hardcopy is generated. -#: Similarly for the `$video`. (Searching the web for "YouTube embed -#: video" should bring up instructions for how to get the code to embed a YouTube -#: video into a webpage.) +#: Create a mode dependent variable `$slideshow` that displays a Google +#: slideshow inside an html `iframe` when in html mode, and the message +#: "An embedded Google slide show." in TeX mode. If you omit the TeX mode, then +#: there will be errors when the PDF hard copy is generated. Similarly for the +#: `$video`. (Searching the web for "YouTube embed video" should bring up +#: instructions for how to get the code to embed a YouTube video into a +#: webpage.) $slideshow = MODES( HTML => tag( @@ -52,7 +52,7 @@ $video = MODES( ); #:% section = statement -#: Include the `$slideshow` and `$video` wherever you like. +#: Include the `$slideshow` and `$video`. BEGIN_PGML # Embedded Google slides diff --git a/tutorial/sample-problems/Misc/ManyMultipleChoice.pg b/tutorial/sample-problems/Misc/ManyMultipleChoice.pg index 101639fa99..ded0c6671d 100644 --- a/tutorial/sample-problems/Misc/ManyMultipleChoice.pg +++ b/tutorial/sample-problems/Misc/ManyMultipleChoice.pg @@ -16,9 +16,9 @@ #:% categories = [multiple choice, misc] #:% section = preamble -#: The `PGchoicemacros.pl` macro is used to construct the list of multiple -#: choice items, and the custom problem grader fluid from `PGgraders.pl` is -#: used for incremental grading. +#: The PODLINK('PGchoicemacros.pl') macro is used to construct the list of +#: multiple choice items, and the custom problem grader fluid from +#: PODLINK('PGgraders.pl') is used for incremental grading. DOCUMENT(); loadMacros( @@ -30,14 +30,14 @@ loadMacros( #: Withhold feedback when answers are submitted by setting #: `$showPartialCorrectAnswers = 0;`. #: -#: This problem uses an incremental grader called the `custom_problem_grader_fluid`. -#: With this problem grader, the number of correct answers `[2, 4, 6]` -#: and their corresponding scores `[0.3, 0.6, 1]` must be specified. The last -#: entry in the `grader_numright` array must be the total number of questions -#: asked, and the last entry in the `grader_scores` array must be 1 -#: (otherwise nobody can earn full credit!). The grader message can also -#: be customized by setting the value of `grader_message` to the desired custom -#: message. +#: This problem uses an incremental grader called the +#: `custom_problem_grader_fluid`. With this problem grader, the number of +#: correct answers `[2, 4, 6]` and their corresponding scores `[0.3, 0.6, 1]` +#: must be specified. The last entry in the `grader_numright` array must be the +#: total number of questions asked, and the last entry in the `grader_scores` +#: array must be 1 (otherwise nobody can earn full credit!). The grader message +#: can also be customized by setting the value of `grader_message` to the +#: desired custom message. #: #: If a grader is desired that awards full credit when all questions are correct #: and no credit otherwise, use the commented out standard problem grader code diff --git a/tutorial/sample-problems/Misc/Matching.pg b/tutorial/sample-problems/Misc/Matching.pg index 6832fbf015..94c6a01e94 100644 --- a/tutorial/sample-problems/Misc/Matching.pg +++ b/tutorial/sample-problems/Misc/Matching.pg @@ -16,9 +16,9 @@ #:% categories = [multiple choice, misc] #:% section = preamble -#: The `PGchoicemacros.pl` macro is used to construct the list of multiple -#: choice items, and the custom problem grader fluid from `PGgraders.pl` is -#: used for incremental grading. +#: The PODLINK('PGchoicemacros.pl') macro is used to construct the list of +#: multiple choice items, and the custom problem grader fluid from +#: PODLINK('PGgraders.pl') is used for incremental grading. DOCUMENT(); loadMacros( @@ -31,14 +31,14 @@ loadMacros( #: Withhold feedback when answers are submitted by setting #: `$showPartialCorrectAnswers = 0;`. #: -#: This problem uses an incremental grader called the `custom_problem_grader_fluid`. -#: With this problem grader, the number of correct answers `[2, 4, 6]` -#: and their corresponding scores `[0.3, 0.6, 1]` must be specified. The last -#: entry in the `grader_numright` array must be the total number of questions -#: asked, and the last entry in the `grader_scores` array must be 1 -#: (otherwise nobody can earn full credit!). The grader message can also -#: be customized by setting the value of `grader_message` to the desired custom -#: message. +#: This problem uses an incremental grader called the +#: `custom_problem_grader_fluid`. With this problem grader, the number of +#: correct answers `[2, 4, 6]` and their corresponding scores `[0.3, 0.6, 1]` +#: must be specified. The last entry in the `grader_numright` array must be the +#: total number of questions asked, and the last entry in the `grader_scores` +#: array must be 1 (otherwise nobody can earn full credit!). The grader message +#: can also be customized by setting the value of `grader_message` to the +#: desired custom message. #: #: If a grader is desired that awards full credit when all questions are correct #: and no credit otherwise, use the commented out standard problem grader code @@ -46,7 +46,7 @@ loadMacros( #: #: Create a list of 6 questions and answers, 2 extra answers, and a #: 'None of the above' answer that will be made last with `makeLast`. -#: So the popup list must have 9 entries A through I. +#: So the pop up list must have 9 entries A through I. #: #: As an alternative, see PROBLINK('MatchingAlt.pg') for another way to write #: a matching problem. @@ -64,7 +64,7 @@ $ENV{grader_message} = # All or nothing grader # install_problem_grader(~~&std_problem_grader); -# Create a matching list and use popups +# Create a matching list and use pop ups $ml = new_match_list(); $ml->rf_print_q(~~&pop_up_list_print_q); $ml->ra_pop_up_list([ @@ -104,7 +104,8 @@ $ml->choose_extra(2); $ml->makeLast('None of the above'); #:% section = statement -#:The `ColumnMatchTable()` method is provided by the macro file `unionTables.pl`. +#: The `ColumnMatchTable` method is provided by the macro file +#: PODLINK('unionTables.pl'). BEGIN_PGML Match each question with its answer. @@ -115,12 +116,9 @@ END_PGML ANS(str_cmp($ml->ra_correct_ans)); #:% section = solution -#: Extract the correct answers from the MatchList object and reformat. -@correct = @{ $ml->ra_correct_ans() }; -$answerstring = join(', ', @correct); - +#: Extract the correct answers from the `MatchList` object and reformat. BEGIN_PGML_SOLUTION -The correct answers are [$answerstring]. +The correct answers are [@ join(', ', @{ $ml->ra_correct_ans() }) @]. END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Misc/MatchingAlt.pg b/tutorial/sample-problems/Misc/MatchingAlt.pg index c17ac7db0e..b1a5692309 100644 --- a/tutorial/sample-problems/Misc/MatchingAlt.pg +++ b/tutorial/sample-problems/Misc/MatchingAlt.pg @@ -15,9 +15,9 @@ #:% categories = [multiple choice, misc] #:% section = preamble -#: The `parserPopUp.pl` macro is used to create drop down menus for answers, and -#: the custom problem grader fluid from `PGgraders.pl` is used for incremental -#: grading. +#: The PODLINK('parserPopUp.pl') macro is used to create drop down menus for +#: answers, and the custom problem grader fluid from PODLINK('PGgraders.pl') is +#: used for incremental grading. DOCUMENT(); loadMacros( @@ -29,14 +29,14 @@ loadMacros( #: Withhold feedback when answers are submitted by setting #: `$showPartialCorrectAnswers = 0;`. #: -#: This problem uses an incremental grader called the `custom_problem_grader_fluid`. -#: With this problem grader, the number of correct answers `[2, 4, 6]` -#: and their corresponding scores `[0.3, 0.6, 1]` must be specified. The last -#: entry in the `grader_numright` array must be the total number of questions -#: asked, and the last entry in the `grader_scores` array must be 1 -#: (otherwise nobody can earn full credit!). The grader message can also -#: be customized by setting the value of `grader_message` to the desired custom -#: message. +#: This problem uses an incremental grader called the +#: `custom_problem_grader_fluid`. With this problem grader, the number of +#: correct answers `[2, 4, 6]` and their corresponding scores `[0.3, 0.6, 1]` +#: must be specified. The last entry in the `grader_numright` array must be the +#: total number of questions asked, and the last entry in the `grader_scores` +#: array must be 1 (otherwise nobody can earn full credit!). The grader message +#: can also be customized by setting the value of `grader_message` to the +#: desired custom message. #: #: If a grader is desired that awards full credit when all questions are correct #: and no credit otherwise, use the commented out `std_problem_grader` code @@ -100,22 +100,24 @@ push(@shuffle, scalar(@shuffle)); #: and the answers on the right. On narrow screens the answers will be below #: the questions. #: -#: In the problem text a `div` with the css class defined in the style snippet +#: In the problem text a `div` with the CSS class defined in the style snippet #: wraps the questions and answers. Inside that the questions are in the first #: inner `div`, and the answers in the second inner `div`. #: -#: When a hardcopy of the problem is generated two side by side parboxes are +#: When a hard copy of the problem is generated two side by side `\parboxes` are #: used instead. #: -#: Both the questions and answers are added as PGML parsed strings. +#: The HTML and TeX are inserted into the problem using the `PGML` tag syntax. +#: +#: Both the questions and answers are added as `PGML` parsed strings. HEADER_TEXT(MODES(TeX => '', HTML => < .two-column { display: flex; - flex-wrap: wrap; - gap: 2rem; - align-items: center; - justify-content: space-evenly; + flex-wrap: wrap; + gap: 2rem; + align-items: center; + justify-content: space-evenly; } END_STYLE @@ -123,25 +125,27 @@ END_STYLE BEGIN_PGML Match each question with its answer. -[@ MODES(TeX => '\\parbox{0.4\\linewidth}{', - HTML => '
') @]* -[@ join( - "\n\n", - map { - '[_]{$answer_dropdowns[' . $_ . ']} ' - . '*' . ($_ + 1) . '.* ' - . '[$q_and_a[' . $_ . '][0]]' - } 0 .. $#q_and_a -) @]** -[@ MODES(TeX => '}\\hfill\\parbox{0.4\\linewidth}{', - HTML => '
') @]* -[@ join( - "\n\n", - map { - '*' . $ALPHABET[($_)] . '.* [$answers[$shuffle[' . $_ . ']]]' - } 0 .. $#answers -) @]** -[@ MODES(TeX => '}', HTML => '
') @]* +[< + [< + [@ join( + "\n\n", + map { + '[_]{$answer_dropdowns[' . $_ . ']} ' + . '*' . ($_ + 1) . '.* ' + . '[$q_and_a[' . $_ . '][0]]' + } 0 .. $#q_and_a + ) @]** + >]{ [ 'div' ] }{ [ '\\parbox{0.55\\linewidth}{', '}' ] } + [@ MODES(TeX => '\\hfill', HTML => '') @]* + [< + [@ join( + "\n\n", + map { + '*' . $ALPHABET[($_)] . '.* [$answers[$shuffle[' . $_ . ']]]' + } 0 .. $#answers + ) @]** + >]{ [ 'div' ] }{ [ '\\parbox{0.25\\linewidth}{', '}' ] } +>]{ [ 'div', class => 'two-column' ] } END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Misc/MatchingGraphs.pg b/tutorial/sample-problems/Misc/MatchingGraphs.pg index e4475e5ef3..87751aefa9 100644 --- a/tutorial/sample-problems/Misc/MatchingGraphs.pg +++ b/tutorial/sample-problems/Misc/MatchingGraphs.pg @@ -17,35 +17,27 @@ #:% categories = [graph] #:% section = preamble -#: The dynamic graph is generated with `PGtikz.pl`, so this is needed. -#: The matching is done with popups, so `parserPopUp.pl` is need and lastly -#: a `LayoutTable` is used from `niceTables.pl`. +#: The dynamic graph is generated with PODLINK('PGtikz.pl'), so that macro is +#: loaded. Matching is done with pop ups, so PODLINK('parserPopUp.pl') is +#: loaded. Finally, a `LayoutTable` is used from PODLINK('niceTables.pl') which +#: is loaded by the `PGML.pl` macro. DOCUMENT(); loadMacros( 'PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'parserPopUp.pl', - 'niceTables.pl', 'PGcourse.pl' + 'PGcourse.pl' ); #:% section = setup -#: The array `@all_plots` contains the display form (f) of the function, -#: the functional form (form) of the function needed in tikz format, -#: the domain of the function and the alterative text. +#: The array `@all_plots` contains the display form `f` of the function, +#: the functional `form` of the function needed in TikZ format, +#: the `domain` of the function, and the alteratnive text (`alt`). #: -#: The graphs of all plots and then created by calling commands from `PGtikz.pl`. -#: See PROBLINK('DynamicGraph.pg') for a simpler example using tikz. Note -#: that alternate text is provided to the `image` command and for accessibility -#: should always be considered and this should be provided. +#: The graphs of all plots are then created by calling commands from +#: PODLINK('PGtikz.pl'). See PROBLINK('DynamicGraph.pg') for more information. +#: Note that alternate text for accessibility should always be provided. #: -#: The dropdowns are created in the `@dropdown` array which pulls all -#: options. -#: -#: The `LayoutTable` is used to make an accessible table that is nicely -#: laid out. -#: -#: Although this matching problem creates graphs dynamically, these can use -#: static images by changing the call to `image` to just pass in the -#: image names. +#: The dropdowns are created in the `@dropdown` array. @all_plots = ( { f => 'x^2', @@ -93,78 +85,81 @@ loadMacros( }, ); -for $i (0 .. $#all_plots) { +@plots = random_subset(4, @all_plots); + +for (@plots) { my $graph = createTikZImage(); $graph->tikzLibraries('arrows.meta'); $graph->BEGIN_TIKZ - \tikzset{>={Stealth[scale=1.5]}} + \tikzset{>={Stealth[scale = 1.5]}} \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box - ] (-7,-7) rectangle (7,7); - \draw[->,thick] (-6,0) -- (6,0) node[above left,outer sep=3pt] {\(x\)}; - \foreach \x in {-5,...,-1,1,2,...,5} - \draw(\x,5pt) -- (\x,-5pt) node [below] {\(\x\)}; - \draw[->,thick] (0,-6) -- (0,6) node[below right,outer sep=3pt] {\(y\)}; - \foreach \y in {-5,...,-1,1,2,...,5} - \draw (5pt,\y) -- (-5pt,\y) node[left] {\(\y\)}; - \draw[blue,ultra thick] plot[domain=$all_plots[$i]->{domain},smooth] (\x,{$all_plots[$i]->{form}}); + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box + ] (-6, -6) rectangle (6, 6); + \draw[->, thick] (-6, 0) -- (6, 0) + node[above left, outer sep = 3pt] {\(x\)}; + \foreach \x in {-5, ..., -1, 1, 2, ..., 5} + \draw (\x, 5pt) -- (\x, -5pt) node[below] {\(\x\)}; + \draw[->, thick] (0, -6) -- (0, 6) + node[below right, outer sep = 3pt] {\(y\)}; + \foreach \y in {-5, ..., -1, 1, 2, ..., 5} + \draw (5pt, \y) -- (-5pt, \y) node[left] {\(\y\)}; + \draw[blue, ultra thick] plot[domain = $_->{domain}, smooth] + (\x, {$_->{form}}); END_TIKZ - $all_plots[$i]->{graph} = $graph; + $_->{graph} = $graph; } -@plots = random_subset(4, @all_plots); - # sorted list of possible answers -$list = [ lex_sort(map {"$_->{f}"} @all_plots) ]; - -@dropdowns = map { DropDown($list, "$_->{f}") } @plots; - -$tab = LayoutTable( - [ - [ - map { - image( - $plots[$_]->{graph}, - width => 300, - tex_size => 400, - extra_html_tags => "alt = '$plots[$_]->{alt}'" - ) - } (0 .. 1) - ], - [ map { $dropdowns[$_]->menu } (0 .. 1) ], - [ - map { - image( - $plots[$_]->{graph}, - width => 300, - tex_size => 400, - extra_html_tags => "alt = '$plots[$_]->{alt}'" - ) - } (2 .. 3) - ], - [ map { $dropdowns[$_]->menu } (2 .. 3) ] - - ], - align => 'cc' -); +$list = [ lex_sort(map { $_->{f} } @all_plots) ]; + +@dropdowns = map { DropDown($list, $_->{f}) } @plots; $showPartialCorrectAnswers = 0; #:% section = statement +#: A `LayoutTable` from PODLINK('niceTables.pl') is used via its `PGML` syntax +#: to organize the graphs and pop ups nicely. +#: +#: Although this matching problem creates graphs dynamically, static images +#: could also be used by changing the image sources in the +#: `[!alt text!]{image source}` calls to the image file names. BEGIN_PGML -Match the graph with the formula for the graph (Click on image for a larger view.) - -[$tab]* +Match the graph with the formula for the graph +(Click on image for a larger view.) + +[# + [. + [![$plots[0]{alt}]!]{$plots[0]{graph}}{300}{ + image_options => { tex_size => 400 } + } + .] + [. + [![$plots[1]{alt}]!]{$plots[1]{graph}}{300}{ + image_options => { tex_size => 400 } + } + .]* + + [.[_]{$dropdowns[0]}.] [.[_]{$dropdowns[1]}.]* + + [. + [![$plots[2]{alt}]!]{$plots[2]{graph}}{300}{ + image_options => { tex_size => 400 } + } + .] + [. + [![$plots[3]{alt}]!]{$plots[3]{graph}}{300}{ + image_options => { tex_size => 400 } + } + .]* + + [.[_]{$dropdowns[2]}.] [.[_]{$dropdowns[3]}.]* +#]*{ align => 'cc' } END_PGML -#:% section = answer -#: Because the dropdowns are created in the older fashion, we use the `ANS` form -#: to check the answer -ANS($dropdowns[$_]->cmp) for (0 .. 3); - #:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. diff --git a/tutorial/sample-problems/Misc/MultipleChoiceCheckbox.pg b/tutorial/sample-problems/Misc/MultipleChoiceCheckbox.pg index 3fb2b1402d..8fe1167f08 100644 --- a/tutorial/sample-problems/Misc/MultipleChoiceCheckbox.pg +++ b/tutorial/sample-problems/Misc/MultipleChoiceCheckbox.pg @@ -17,26 +17,30 @@ #:% see_also = [MultipleChoiceRadio.pg, MultipleChoicePopup.pg, ManyMultipleChoice.pg] #:% section = preamble -#: Include `parserCheckboxList.pl` in the `loadMacros`. +#: The PODLINK('parserCheckboxList.pl') is used for check box answers. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserCheckboxList.pl', 'PGcourse.pl'); #:% section = setup -#: To setup, `CheckboxList` creates a new object. The format is -#:```{#constructor .perl} -#: $checks = CheckboxList([choices, ...], [correct_choices, ...], options); -#:``` -#: where the `correct_choices` can either match those in `choices` or be the indices -#: (starting at 0). +#: First, call `CheckboxList` to create a check box list answer object. The +#: format is #: -#: If we nest `choices` in another arrayref, then the order of the choices will be randomized. +#: ```{#constructor .perl} +#: CheckboxList([choices, ...], [correct_choices, ...], options); +#: ``` #: -#: Using the option `separator` and setting to `$SPACE x 10` results in -#: a horizontal checklist. Note that `$SPACE` should be used and not ` ` so that -#: this works in both html and hardcopy. +#: where the `correct_choices` can either match those in `choices` or be the +#: indices (starting at 0). #: -#: See PODLINK('the POD', 'parserCheckboxList.pl') for more options. +#: If we nest `choices` in another array reference, then the order of the +#: choices will be randomized. +#: +#: Setting the option `separator` to `$SPACE x 10` results in a horizontal +#: checklist. Note that `$SPACE` should be used and not ` ` so that +#: this works in both html and hard copy. +#: +#: See PODLINK('parserCheckboxList.pl') for more options. $checks1 = CheckboxList( [ "\(e^{x^2} e^{1/x}\)", @@ -78,11 +82,11 @@ There may be more than one correct answer. [_]{$checks1} -**Alternative with randomly ordered choices** +*Alternative with randomly ordered choices* [_]{$checks2} -**Alternative shown with horizontal spacing** +*Alternative shown with horizontal spacing* [_]{$checks3} END_PGML diff --git a/tutorial/sample-problems/Misc/MultipleChoicePopup.pg b/tutorial/sample-problems/Misc/MultipleChoicePopup.pg index 52823460d6..0669f27467 100644 --- a/tutorial/sample-problems/Misc/MultipleChoicePopup.pg +++ b/tutorial/sample-problems/Misc/MultipleChoicePopup.pg @@ -18,23 +18,28 @@ #:% see_also = [MultipleChoiceRadio.pg, MultipleChoiceCheckbox.pg, ManyMultipleChoice.pg] #:% section = preamble -#: The macro `parserPopUp.pl` must be loaded. +#: The macro PODLINK('parserPopUp.pl') is used for pop up (or drop down) menus. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserPopUp.pl', 'PGcourse.pl'); #:% section = setup -#: To create a radio object, use `$popup = PopUp([choices,...],correct);` -#: For details, see parserPopUp.pl The context is not really necessary, but multiple -#: choice questions are often follow-up questions, so we leave it in. +#: Call `PopUp` or `DropDown` to create a pop up answer object. The format is #: -#: The `parsePopUp.pl` macro has two methods `PopUp` and `DropDown`. The former requires -#: that a default `?` is coded in the first element. The latter will put that in -#: unless the `placeholder` option is there. +#: ```{#constructor .perl} +#: PopUp([choices, ...], correct, options); +#: DropDown([choices, ...], correct, options); +#: ``` #: -#: Note: setting the `$showPartialCorrectAnswers` to 0 is often desirable for -#: multiple choice problems so students don't know which part is incorrect and -#: could therefore just guess the answer. +#: The difference between the `PopUp` and `DropDown` methods is that the latter +#: will add an unselectable placeholder value. The value is `?` by default, but +#: can be customized with the `placeholder` option. Generally, you should use +#: the `DropDown` method. The `PopUp` method is considered deprecated. +#: +#: For details, see PODLINK('parserPopUp.pl'). +#: +#: Setting the `$showPartialCorrectAnswers` to 0 is often desirable for +#: multiple choice problems to prevent guessing of answers. $showPartialCorrectAnswers = 0; $popup = PopUp([ '?', 'Red', 'Blue', 'Green' ], 'Blue'); @@ -48,11 +53,11 @@ $dropdown2 = BEGIN_PGML Select my favorite color [_]{$popup} -**Same thing, but using DropDown** +*Same thing, but using DropDown* Select my favorite color [_]{$dropdown1} -**Same thing, but using DropDown with placeholder option** +*Same thing, but using DropDown with placeholder option* Select my favorite color [_]{$dropdown2} END_PGML diff --git a/tutorial/sample-problems/Misc/MultipleChoiceRadio.pg b/tutorial/sample-problems/Misc/MultipleChoiceRadio.pg index c0d53089d8..040aeca9e6 100644 --- a/tutorial/sample-problems/Misc/MultipleChoiceRadio.pg +++ b/tutorial/sample-problems/Misc/MultipleChoiceRadio.pg @@ -17,16 +17,23 @@ #:% see_also = [MultipleChoicePopup.pg, MultipleChoiceCheckbox.pg, ManyMultipleChoice.pg] #:% section = preamble color:blue -#:The macro `parserRadioButtons.pl` must be loaded. +#: The macro PODLINK('parserRadioButtons.pl') is used for radio button answers. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserRadioButtons.pl', 'PGcourse.pl'); #:% section = setup -#:To create a radio object, use `$radio = RadioButtons([choices,...],correct,options);` -#:For all options, see MultipleChoiceProblems and parserRadioButtons.pl. The context -#:is not really necessary, but multiple choice questions are often follow-up questions, -#:so we leave it in. +#: Call `RadioButtons` to create a radio button answer object. The format is +#: +#: ```{#constructor .perl} +#: RadioButtons([choices, ...], correct, options); +#: ``` +#: +#: For more details see PODLINK('parserRadioButtons.pl'). +#: +#: Setting the option `separator` to `$SPACE x 5` results in a horizontal +#: list of radio buttons. Note that `$SPACE` should be used and not ` ` so +#: that this works in both html and hard copy. $radio1 = RadioButtons( [ [ 'Red', 'Blue', 'Green' ], 'None of these' ], 'Blue', # correct answer @@ -35,7 +42,7 @@ $radio1 = RadioButtons( $radio2 = RadioButtons( [ [ 'Red', 'Blue', 'Green' ], 'None of these' ], 2, # correct answer as an index (starting at 0) - separator => $SPACE x 4 + separator => $SPACE x 5 ); #:% section = statement diff --git a/tutorial/sample-problems/Misc/RandomPerson.pg b/tutorial/sample-problems/Misc/RandomPerson.pg index 1fa4edf1f8..377b677763 100644 --- a/tutorial/sample-problems/Misc/RandomPerson.pg +++ b/tutorial/sample-problems/Misc/RandomPerson.pg @@ -21,25 +21,30 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'randomPerson.pl', 'PGcourse.pl'); #:% section = setup -#: The macro `randomPerson.pl` provides functionality to +#: The macro PODLINK('randomPerson.pl') provides functionality to #: #: * select a random name from a user-defined or default list with pronouns. #: * provide correct pronouns as well as verb conjugation. #: -#: The `randomPerson(n => 3)` subroutine selects 3 unique random persons. +#: Calling `randomPerson(n => 3)` selects 3 unique random persons. #: -#: See the `randomNamesPronouns.pl` POD for more information and example. +#: See the PODLINK('random person documentation', 'randomPerson.pl') for more +#: information. ($p1, $p2, $p3) = randomPerson(n => 3); $n = random(20, 30, 2); $c = random(4, 8, 2); #:% section = statement -#: The objects `$p1, $p2` and `$p3` are `Person` objects and we can call the methods -#: for name, and the pronouns `subject, possessive, possession` and `object` as well -#: as the capitalized versions of each of these. In addition, there is a `verb` -#: method to conjugate most verbs as well as some irregular ones, like `do` -#: which returns the correct conjugation of "to do". +#: The objects `$p1`, `$p2` and `$p3` are `Person` objects. A `Person` object's +#: `name` method can be called to obtain the person's name. A `Person` object is +#: "stringified" to the name as well. So `[$p1]` can be used in `PGML` to obtain +#: the name. The `subject`, `possessive`, `possession`, and `object` methods can +#: be called to obtain pronouns referring to the person in various parts of +#: speech. There are also capitalized versions of those methods to obtain +#: capitalized pronouns. In addition, there is a `verb` method to conjugate most +#: verbs as well as some irregular ones, like `do` which returns the correct +#: conjugation of "to do". BEGIN_PGML [$p1] has a ribbon of length [$n] cm. [$p1->Subject] [$p1->verb('cut')] [$c] cm off the ribbon and [$p1->verb('give')] the piece to [$p1->possessive] diff --git a/tutorial/sample-problems/Misc/Scaffolding.pg b/tutorial/sample-problems/Misc/Scaffolding.pg index 4926bea72e..546a3d7cbb 100644 --- a/tutorial/sample-problems/Misc/Scaffolding.pg +++ b/tutorial/sample-problems/Misc/Scaffolding.pg @@ -16,16 +16,19 @@ #:% categories = [misc] #:% section = preamble -#: Make sure that the `scaffold.pl` macro is loaded. +#: Load the PODLINK('scaffold.pl') macro to use scaffolding. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'scaffold.pl', 'PGcourse.pl'); -#:% section = setup - #:% section = statement -#: Each `Section::Begin()` and `Section::End()` block can have its own context, etc. -#: See the scaffold.pl macro file for Scaffold options. +#: Call `Scaffold::Begin()` to start scaffolding, and `Scaffold::End()` to end +#: the scaffold. +#: +#: Inside a scaffold block call `Section::Begin()` to start a scaffold section, +#: and `Section::End()` to end it. +#: +#: See the PODLINK('scaffold.pl') macro file for more details. Scaffold::Begin(); Section::Begin('Part 1: The first part'); diff --git a/tutorial/sample-problems/Parametric/ParametricEquationAnswers.pg b/tutorial/sample-problems/Parametric/ParametricEquationAnswers.pg index 3733c6d297..c628d49e3c 100644 --- a/tutorial/sample-problems/Parametric/ParametricEquationAnswers.pg +++ b/tutorial/sample-problems/Parametric/ParametricEquationAnswers.pg @@ -15,35 +15,35 @@ #:% name = Parametric Equation Answer Checker #:% type = [Sample, technique] #:% subject = [differential calculus] -#:% categories = [parametric, answer] +#:% categories = [parametric, answers] #:% section = preamble -#: Since there are multiple ways to parameterize, we use the `parserMultiAnswer.pl` -#: macro. +#: The PODLINK('parserMultiAnswer.pl') macro is used to check parameterization +#: equations and end points together. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); #:% section = setup -#: We use a `MultiAnswer()` answer checker that will verify that the students -#: answers satisfy the equation for the circle and have the required starting -#: and ending points. This answer checker will allow students to enter any -#: correct parametrization. For example, both -#: x = \cos(t), y = sin(t), 0 ≤ t ≤ pi/3 and x = cos(2t), y = sin(2t), -#: 0 ≤ t ≤ pi/6 will be marked correct. +#: A `MultiAnswer` is used to verify that the student answers satisfy the +#: equation for the circle and have the required starting and ending points. +#: This answer checker will allow students to enter any correct parametrization. +#: For example, both `x = \cos(t)`, `y = sin(t)`, `0 ≤ t ≤ pi/3` and +#: `x = cos(2t)`, `y = sin(2t)`, `0 ≤ t ≤ pi/6` will be marked correct. #: #: When evaluating student's answers, it is important not to use quotes. For -#: example, if the code were `$xstu->eval(t=>"$t1stu")` with quotes, then if a -#: student enters pi the answer checker will interpret it as the string "pi" -#: which will need to be converted to a MathObject Real and numerical error +#: example, if the code were `$xstu->eval(t => "$t1stu")` with quotes, then if a +#: student enters `pi` the answer checker will interpret it as the string "pi" +#: which will need to be converted to a MathObject `Real` and numerical error #: will be introduced in the conversion. The correct code to use is -#: `$xstu->eval(t=>$t1stu)` without quotes so that the answer is interpreted +#: `$xstu->eval(t => $t1stu)` without quotes so that the answer is interpreted #: without a conversion that may introduce error. #: -#: The first if statement is fully correct, that is the parametric functions -#: are on the unit circle and the initial and final points are correct. -#: The other three ifelse in the answer checker has either the second point, -#: first point or both points wrong. +#: The first `if` statement checks that the answer is fully correct, that is the +#: parametric functions are on the unit circle and the initial and final points +#: are correct. The next three `elsif` statements check the cases that the +#: parametric functions are on the unit circle, but one of the second point, +#: first point, or both points are incorrect. Context("Numeric")->variables->are(t => "Real"); Context()->variables->set(t => { limits => [ -5, 5 ] }); @@ -89,18 +89,17 @@ $multians = MultiAnswer($x, $y, $t0, $t1)->with( } ); #:% section = statement -#: Since the correct answer depends on all answer blanks, the MathObject -#: `$multians` is input into all answer blanks. +#: The `$multians` object is used for all answer rules. BEGIN_PGML Find a parametrization of the unit circle from the point -[` \big(1,0\big) `] to [` \big(\frac{1}{2},\frac{\sqrt{3}}{2}\big) `]. -Use [` t `] as the parameter for your answers. +[`\big(1, 0\big)`] to [`\big(\frac{1}{2}, \frac{\sqrt{3}}{2}\big)`]. +Use [`t`] as the parameter for your answers. -[` x(t) = `] [__]{$multians} +[`x(t) =`] [__]{$multians} -[` y(t) = `] [__]{$multians} +[`y(t) =`] [__]{$multians} -for [__]{$multians} to [__]{$multians}. +from [__]{$multians} to [__]{$multians}. END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Parametric/ParametricPlot.pg b/tutorial/sample-problems/Parametric/ParametricPlot.pg index 828d33f05f..00b234684d 100644 --- a/tutorial/sample-problems/Parametric/ParametricPlot.pg +++ b/tutorial/sample-problems/Parametric/ParametricPlot.pg @@ -16,45 +16,51 @@ #:% subject = parametric #:% section = preamble -#: We use `PGtikz.pl` to generate the graph. +#: The PODLINK('PGtikz.pl') macro is used to generate the graph. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); #:% section = setup -#: The package `PGtikz.pl` is used to produce the curve. Basics of such a plot -#: are described in PROBLINK('TikZImages.pg'). +#: The PODLINK('PGtikz.pl') macro is used to produce the curve. The basics of +#: such a plot are described in PROBLINK('TikZImages.pg'). #: -#: Most of the code for the plot produces the axes with the nice border. The -#: parametric plotting routine is the last function call starting with -#: `\draw[DarkBlue, very thick] plot [....]`. Note that +#: The initial TikZ code produces the axes and the nice border. The plot of the +#: parametric function is the last command starting with +#: `\draw[DarkBlue, very thick] plot [....]`. The options for the `plot` command +#: are as follows. #: -#: * `samples` is the number of points to create the plot. -#: * `domain` is the plotting domain. -#: * `variable` is the variable for the plot. `\x` is default. We switch to -#: * `\t` as is standard for parametric plots. -#: * The plot is in the `({}, {})` where the first slot is the `x` function and -#:the second is the `y` function. It is important that the functions are -#:wrapped in `{}` and the variable has the backslash. +#: * `samples` is the number of sample points used to create the plot. +#: * `domain` is the plot domain. +#: * `variable` is the variable for the plot. `\x` is default. `\t` is switched +#: to as it is standard for parametric plots. +#: * The function plotted is `({2 * sin(2 * \t r)}, {2 * sin(3 * \t r)})` where +#: the `x` function is in the first set of braces, and the `y` function is in +#: the second. It is important that the functions are wrapped in braces and +#: the variable has the backslash. Context()->variables->add(t => 'Real'); $graph = createTikZImage(); $graph->tikzLibraries('arrows.meta'); $graph->BEGIN_TIKZ -\tikzset{>={Stealth[scale=1.5]}} +\tikzset{>={Stealth[scale = 1.5]}} \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-3.5,-3.5) rectangle (3.5,3.5); -\draw[->] (-3.5,0) -- (3.5,0) node[above left,outer sep=3pt] {\(x\)}; -\foreach \x in {-3,-2,-1,1,2,3} \draw (\x,0.15) -- (\x,-0.15) node [below] {\x}; -\draw[->] (0,-3.5) -- (0,3.5) node[below right,outer sep=3pt] {\(y\)}; -\foreach \y in {-3,-2,-1,1,2,3} \draw (0.15,\y) -- (-0.15,\y) node [left] {\y}; -\draw[DarkBlue,very thick] - plot [samples=250,domain=0:{2*pi},variable=\t] - ({2*sin(2*\t r)},{2*sin(3*\t r)}); + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-3.5, -3.5) rectangle (3.5, 3.5); +\draw[->] (-3.5, 0) -- (3.5, 0) node[above left, outer sep = 3pt] {\(x\)}; +\foreach \x in {-3, -2, -1, 1, 2, 3} + \draw (\x, 0.15) -- (\x, -0.15) node[below] {\x}; +\draw[->] (0, -3.5) -- (0, 3.5) + node[below right, outer sep = 3pt] {\(y\)}; +\foreach \y in {-3, -2, -1, 1, 2, 3} + \draw (0.15, \y) -- (-0.15, \y) node[left] {\y}; +\draw[DarkBlue, very thick] + plot[samples = 100, domain = 0:{2 * pi}, variable = \t] + ({2 * sin(2 * \t r)}, {2 * sin(3 * \t r)}); END_TIKZ $x = Compute('2sin(2t)'); @@ -63,20 +69,23 @@ $y = Compute('2sin(3t)'); $y0 = $y->eval(t => 'pi/3'); $m = $y->D('t')->eval(t => 'pi/3') / $x->D('t')->eval(t => 'pi/3'); -$line = Compute("$m*(x-$x0)+$y0"); +$line = Compute("$m(x - $x0) + $y0"); -#:% section=statement +#:% section = statement +#: The alternate text provided in this example is not sufficient. I don't know +#: how one would describe a Lissajous curve to someone who can not see it. +#: Perhaps seek assistance from an expert in assistive technologies to find a +#: better description. BEGIN_PGML -Find the tangent line to the parametric curve: ->> [``x(t) = [$x], \qquad y(t) = [$y]``] << +Find the tangent line to the parametric curve -when [`t=\pi/3`]. The graph of the curve is +>> [``x(t) = [$x], \quad y(t) = [$y]``] << ->>[@ image($graph, width => 300) @]*<< +when [`t = \pi/3`]. The graph of the curve is -Tangent line in slope-intercept form +>>[!a Lissajous curve!]{$graph}{300}<< -[`y=`][_____]{$line} +Tangent line in slope-intercept form: [`y =`] [_____]{$line} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Parametric/PolarGraph.pg b/tutorial/sample-problems/Parametric/PolarGraph.pg index f0d2ba4a90..dd69cc979c 100644 --- a/tutorial/sample-problems/Parametric/PolarGraph.pg +++ b/tutorial/sample-problems/Parametric/PolarGraph.pg @@ -1,5 +1,5 @@ ## DESCRIPTION -## Graphing a polar curve given by r=f(theta) +## Graphing a polar curve given by r = f(theta) ## ENDDESCRIPTION ## DBsubject(WeBWorK) @@ -16,45 +16,49 @@ #:% subject = parametric #:% section = preamble -#: We use `PGtikz.pl` to generate the graph. +#: The PODLINK('PGtikz.pl') macro is used to generate the graph. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); #:% section = setup -#: The package `PGtikz.pl` is used to produce the curve. The plotting routine used in -#: this way plots parametrically and we plot the polar curve parametrically. +#: The polar curve is plotted parametrically. Context()->variables->are(t => 'Real'); $graph = createTikZImage(); $graph->tikzLibraries('arrows.meta'); $graph->BEGIN_TIKZ -\tikzset{>={Stealth[scale=1.5]}} +\tikzset{>={Stealth[scale = 1.5]}} \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-3.5,-3.5) rectangle (3.5,3.5); -\draw[->] (-3.5,0) -- (3.5,0) node[above left,outer sep=3pt] {\(x\)}; -\draw[->] (0,-3.5) -- (0,3.5) node[below right,outer sep=3pt] {\(y\)}; -\draw[DarkGreen,very thick] - plot [samples=250,domain=0:{2*pi},variable=\t] - ({3*cos(5*\t r)*cos(\t r)},{3*cos(5*\t r)*sin(\t r)}); -\fill[opacity=0.5,fill=DarkGreen] - (0,0) -- plot[samples=250,domain={-pi/10}:{pi/10},variable=\t] - ({3*cos(5*\t r)*cos(\t r)},{3*cos(5*\t r)*sin(\t r)}) -- cycle; + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-3.5, -3.5) rectangle (3.5, 3.5); +\draw[->] (-3.5, 0) -- (3.5, 0) node[above left, outer sep = 3pt] {\(x\)}; +\draw[->] (0, -3.5) -- (0, 3.5) node[below right,outer sep = 3pt] {\(y\)}; +\draw[DarkGreen, very thick] + plot [samples = 200, domain = 0:{2 * pi}, variable = \t] + ({3 * cos(5 * \t r) * cos(\t r)}, {3 * cos(5 * \t r) * sin(\t r)}); +\fill[opacity = 0.5, fill = DarkGreen] + (0, 0) -- plot[samples = 100, domain = {-pi / 10}:{pi / 10}, variable = \t] + ({3 * cos(5 * \t r) * cos(\t r)}, {3 * cos(5 * \t r) * sin(\t r)}) -- cycle; END_TIKZ +#:% section = statement +#: The alternate text provided in this example is not sufficient. I don't know +#: how one would describe a rose curve to someone who can not see it. Perhaps +#: seek assistance from an expert in assistive technologies to find a better +#: description. BEGIN_PGML Find the area enclosed by one petal of the rose curve [`r = f(\theta) = \cos(5\theta)`]. ->>[@ image($graph, width => 300) @]*<< - +>>[!a rose curve!]{$graph}{300}<< >>Graph of [`r = \cos(5\theta)`]<< -Area = [_____]{'pi/20'} +Area = [_____]{'pi / 20'} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Parametric/SpaceCurveGraph.pg b/tutorial/sample-problems/Parametric/SpaceCurveGraph.pg index a58be7ee6e..94df29c890 100644 --- a/tutorial/sample-problems/Parametric/SpaceCurveGraph.pg +++ b/tutorial/sample-problems/Parametric/SpaceCurveGraph.pg @@ -16,28 +16,34 @@ #:% subject = [parametric, graph] #:% section = preamble -#: The macro `plotly3D.pl` is used to produce the graph. +#: The macro PODLINK('plotly3D.pl') is used to produce the graph. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'plotly3D.pl', 'PGcourse.pl'); #:% section = setup -#: A `plotly3D` graph is created with the `Graph3D` function. There are many options as -#: decribed in PODLINK('the POD','plotly3D.pl'), but to get started include the `height` and `width`. +#: A `plotly3D` graph is created with the `Graph3D` function. There are many +#: options as decribed in PODLINK('plotly3D macro documentation','plotly3D.pl'). +#: In this example, only the `height`, `width`, and `title` are provided. #: -#: A parametric curve (space curve) is added to the graph with the `addCurve` method, -#: which takes an array ref of length 3. These are strings as javascript function in the -#: variable `t`. The second array ref is `[tmin, tmax, samples]`. +#: A parametric curve is added to the graph with the `addCurve` method, which +#: takes takes 2 array references with each array of length 3. The entries of +#: the first array will be used as the body of JavaScript functions with +#: parameter `t` whose values are the `x`, `y`, and `z` coordinates, +#: respectively, of a point on the curve. For example, `t * cos(t)` would be +#: converted into the JavaScript function `(t) => t * cos(t)` for the +#: `x`-coordinate. The entries of the second array are the minimum and maximum +#: values for the parameter `t`, and the number of samples to use for `t`. $graph = Graph3D( height => 300, width => 300, title => 'Spiral in 3D', ); -$graph->addCurve([ 't*cos(t)', 't*sin(t)', 't' ], [ 0, 6 * pi, 150 ]); +$graph->addCurve([ 't * cos(t)', 't * sin(t)', 't' ], [ 0, 6 * pi, 150 ]); #:% section = statement -#: This just prints the graph. No question is asked. +#: Insert the graph into the problem. BEGIN_PGML [@ $graph->Print @]* END_PGML diff --git a/tutorial/sample-problems/Parametric/Spacecurve.pg b/tutorial/sample-problems/Parametric/Spacecurve.pg index 0762b90b75..468939ba11 100644 --- a/tutorial/sample-problems/Parametric/Spacecurve.pg +++ b/tutorial/sample-problems/Parametric/Spacecurve.pg @@ -23,18 +23,22 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); $showPartialCorrectAnswers = 0; #:% section = setup -#: Because the answers can vary and are interdependent, we use the `MultiAnswer` to check -#: the results. +#: The PODLINK('MultiAnswer') is used to check the answers together since they +#: are interdependent. #: -#: We use `singleResult => 1` since it doesn't make sense to say that `x(t)` is correct but -#: `z(t)` is incorrect since they depend on one another. First, we check that the student -#: hasn't fed us a bogus constant solution such as `x=y=z=0` by requiring the x-coordinate -#: to be a formula (not a constant) via -#:```{.perl} -#:return 0 unless $xstu->isFormula; -#:``` -#: Then, we check -#: that the student's answers satisfy the parametric equation. +#: The option `singleResult => 1` is used since it doesn't make sense to say +#: that `x(t)` is correct but `z(t)` is incorrect since they depend on one +#: another. +#: +#: The checker first ensures that the student has not given a bogus constant +#: solution such as `x = y = z = 0` by requiring the `x`-coordinate to be a +#: non-constant formula via +#: +#: ```{.perl} +#: return 0 unless $xstu->isFormula; +#: ``` +#: +#: Then, it checks that the student answers satisfy the curve equation. Context()->variables->are(t => 'Real'); Context()->variables->set(t => { limits => [ 0, 10 ] }); @@ -47,18 +51,17 @@ $multians = MultiAnswer($x, $y, $z)->with( singleResult => 1, checker => sub { my ($correct, $student, $self) = @_; - my ($xstu, $ystu, $zstu) = @{$student}; + my ($xstu, $ystu, $zstu) = @$student; return 0 unless $xstu->isFormula; - return (($xstu == $a * $zstu**2) && ($ystu == 0)) ? 1 : 0; + return $xstu == $a * $zstu**2 && $ystu == 0 ? 1 : 0; } ); #:% section = statement -#: Notice that we use `$multians` in each answer blank because they results in the -#: three answers are dependent on each other. +#: Notice that `$multians` is used for the answer for all answer rules. BEGIN_PGML -Find a parametrization of the curve [`x = [$a] z^2`] in the [`xz`]-plane. Use -[`t`] as the parameter for all of your answers. +Find a parametrization of the curve [`x = [$a]z^2`] in the [`xz`]-plane. Use +[`t`] for the parameter. [`x(t) =`] [_]{$multians}{15} diff --git a/tutorial/sample-problems/Parametric/SurfaceGraph.pg b/tutorial/sample-problems/Parametric/SurfaceGraph.pg index 3aa5038840..17e8527081 100644 --- a/tutorial/sample-problems/Parametric/SurfaceGraph.pg +++ b/tutorial/sample-problems/Parametric/SurfaceGraph.pg @@ -16,20 +16,28 @@ #:% subject = parametric #:% section = preamble -#: The macro `plotly3D.pl` is used to produce the graph. +#: The macro PODLINK('plotly3D.pl') is used to produce the graph. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'plotly3D.pl', 'PGcourse.pl'); #:% section = setup -#: A `plotly3D` graph is created with the `Graph3D` function. There are many option -#: (see PODLINK('the POD','plotly3D.pl')), but to get started include the `height` and `width`. +#: A `plotly3D` graph is created with the `Graph3D` function. There are many +#: options as decribed in PODLINK('plotly3D macro documentation','plotly3D.pl'). +#: In this example, only the `height`, `width`, and `title` are provided. #: #: A parametric surface is added to the graph with the `addSurface` method, -#: which takes 3 array refs, each of length 3. -#: 1. These are strings as javascript function in the variables `u` and `v`. -#: 2. The parametric range in `u` or `[umin, umax, samples]`. -#: 3. The parametric range in `v` or `[vmin, vmax, samples]`. +#: which takes 3 array references with each array of length 3. These parameters +#: are described below. +#: +#: 1. The entries of the first array are strings that will be used as the body +#: of a JavaScript function with parameters `u` and `v` whose values are the +#: `x`, `y`, and `z` coordinates, respectively, of a point on the surface. +#: For example, `3 * sin(v) * cos(u)` becomes `(u, v) => 3 * sin(v) * cos(u)`. +#: 2. The entries of the second array are the minimum and maximum values for the +#: parameter `u`, and the number of samples to use for `u`. +#: 2. The entries of the third array are the minimum and maximum values for the +#: parameter `v`, and the number of samples to use for `v`. $graph = Graph3D( height => 300, width => 300, @@ -37,13 +45,13 @@ $graph = Graph3D( ); $graph->addSurface( - [ '3*sin(v)*cos(u)', '3*sin(v)*sin(u)', '3*cos(v)' ], - [ 0, 2 * pi, 30 ], - [ 0, pi, 30 ] + [ '3 * sin(v) * cos(u)', '3 * sin(v) * sin(u)', '3 * cos(v)' ], + [ 0, 2 * pi, 30 ], + [ 0, pi, 30 ] ); #:% section = statement -#: This just prints the graph. No question is asked. +#: Insert the graph into the problem. BEGIN_PGML [@ $graph->Print @]* END_PGML diff --git a/tutorial/sample-problems/Parametric/VectorParametricDerivative.pg b/tutorial/sample-problems/Parametric/VectorParametricDerivative.pg index 8e0b2455cc..c287157f8e 100644 --- a/tutorial/sample-problems/Parametric/VectorParametricDerivative.pg +++ b/tutorial/sample-problems/Parametric/VectorParametricDerivative.pg @@ -16,42 +16,61 @@ #:% subject = parametric #:% section = preamble -#: Although not necessary for the code below, we load `parserVectorUtils.pl` because you may -#: want to use some of its methods when you use this template file. +#: Although not necessary for the code demonstrated in this example, you might +#: want to load PODLINK('parserVectorUtils.pl'). It provides methods that are +#: useful for vector problems. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserVectorUtils.pl', 'PGcourse.pl'); #:% section = setup -#: We choose not to display the answer using ijk notation. Also, use `ijkAnyDimension => 1` to -#: require a dimension match between i,j,k -#: vectors and either the student or the correct answer when doing vector operations. +#: The context flag `ijk => 0` disables the display of correct answer vectors +#: using `ijk` form. Note that it does not prevent students from using `ijk` +#: form in answers, and if the student enters an answer in that form it will +#: also still be displayed in that form. Furthermore, the vector constants `i` +#: and `j` are still available in the context for the problem author to use (as +#: is done in the checker in this example). The vector constant `k` is also +#: available in the 3 dimensional `Vector` context. #: -#: The custom answer checker is used to check if the derivative matching the questioned asked. -#: Use dot products of the student answer with the vectors `Vector(1,0)` and `Vector(0,1)` to -#: get the components `$xstu` and `$ystu` of the student answer. Then, we can differentiate -#: the components just like any `MathObject` formula. +#: Setting the context flag `ijkAnyDimension => 1` means that trailing zero +#: vector entries are added or removed so that vector dimensions match when +#: vector comparisons are made. Although, this only has any effect if +#: `ijk => 1`, and is only here to demonstrate the availability of the option. +#: +#: Also note that the values of the flags described above are the default +#: values for these flags. So there is no need to set the flags in this problem +#: at all. This is only done here to demonstrate the flags and their meaning. +#: +#: The custom answer checker checks that the student answer has the correct +#: dimension, satisfies the equation for the curve, and that derivatives of the +#: components of the student answer equal the derivatives of the components of +#: the correct answer. The dot product of the student answer with the vectors +#: `i` and `j` is used to get the `x` and `y` components, `$xstu` and `$ystu` +#: respectively, of the student answer. Then, the components are differentiated +#: and compared to the derivatives of the components of the correct answer. Context('Vector2D'); Context()->variables->are(t => 'Real'); Context()->variables->set(t => { limits => [ 0, 5 ] }); Context()->flags->set(ijk => 0, ijkAnyDimension => 1); -$ans = Vector("<2t,(2t)^2>")->cmp( +$ans = Vector('<2t, 4t^2>')->cmp( checker => sub { my ($correct, $student, $ansHash) = @_; - my $xstu = $student . Vector(1, 0); - my $ystu = $student . Vector(0, 1); - return (($xstu->D('t') == Formula('2')) - && ($ystu->D('t') == Formula('8t'))) ? 1 : 0; + return 0 unless $student->length == $correct->length; + my $xstu = $student . i; + my $ystu = $student . j; + return + $ystu == $xstu**2 + && $xstu->D('t') == Formula('2') + && $ystu->D('t') == Formula('8t') ? 1 : 0; } ); #:% section = statement BEGIN_PGML -Find a vector parametric function [`\vec{r}(t)`] -for a bug that moves along the parabola [`y = x^2`] -with velocity [`\vec{v}(t) = \langle 2, 8t \rangle`] -for all [`t`]. +Find a vector parametric function [`\vec{r}(t)`] for a bug that moves along the +parabola [`y = x^2`] with velocity [`\vec{v}(t) = \langle 2, 8t \rangle`] for +all [`t`]. [`\vec{r}(t) =`] [_]{$ans}{15} END_PGML diff --git a/tutorial/sample-problems/Parametric/VectorParametricFunction.pg b/tutorial/sample-problems/Parametric/VectorParametricFunction.pg index f6a90c65b4..3bc37e8e23 100644 --- a/tutorial/sample-problems/Parametric/VectorParametricFunction.pg +++ b/tutorial/sample-problems/Parametric/VectorParametricFunction.pg @@ -16,8 +16,12 @@ #:% subject = parametric #:% section = preamble -#: Since it is a vector parametric curve, we will want vector utilities from `parserVectorUtils.pl`. -#: Since we will need to check multiple answer blanks that depend upon each other, we use `parserMultiAnswer.pl`. +#: Although not necessary for the code demonstrated in this example, you might +#: want to load PODLINK('parserVectorUtils.pl'). It provides methods that are +#: useful for vector problems. +#: +#: The PODLINK('parserMultiAnswer.pl') macro is used since the answers depend +#: upon each other. DOCUMENT(); loadMacros( 'PGstandard.pl', 'PGML.pl', @@ -26,45 +30,52 @@ loadMacros( ); #:% section = setup -#: The student's vector-valued function is stored in `$f`. To get the x- and y-components -#: of the students answer we dot it with the standard basis vectors using `$f . i` and `$f . j`. -#: Note: If you want to differentiate the component functions in the student's answer, you'll -#: need to use a different method as `($f . i)->D('t')` will generate errors since the dot -#: product does not get evaluated. Another problem given in this section describes how to -#: extract formulas from the components of the student's answer, which can then be differentiated. -#: Notice that we have given the students helpful feedback messages about which endpoints are incorrect. +#: In the checker the vector-valued function that the student enters is stored +#: in `$f`. It is dotted with the standard basis vectors `i` and `j` to obtain +#: the `x` and `y` components. +#: +#: Note that if you need to differentiate the component functions in the +#: student answer, you will need to use a different method. Attempting to +#: compute `($f . i)->D('t')` will generate errors since the dot product does +#: not get evaluated. See PROBLINK('VectorParametricDerivative.pg') for an +#: example of how to extract formulas from the components of the student answer, +#: which can then be differentiated. +#: +#: Notice that feedback messages are provided regarding which endpoints are +#: incorrect. Context('Vector2D'); -#Context('Vector'); # for 3D vectors +# Context('Vector'); # use for 3D vectors Context()->variables->are(t => 'Real'); -Context()->variables->set(t => { limits => [ 0, 5 ] }); -Context()->flags->set(ijk => 0); $a = random(2, 5); + +Context()->variables->set(t => { limits => [ 0, $a ] }); + $Q = Point($a, $a**2); $multians = MultiAnswer(Vector(""), 0, $a)->with( singleResult => 1, checker => sub { - my ($correct, $student, $self) = @_; # get the parameters - my ($f, $x1, $x2) = @{$student}; # extract student answers - if ((($f . i)**2 == ($f . j)) - && ($f->eval(t => $x1) == Vector("<0,0>")) - && ($f->eval(t => $x2) == Vector("<$a,$a**2>"))) + my ($f, $x1, $x2) = @$student; # extract student answers + + if (($f . i)**2 == $f . j + && $f->eval(t => $x1) == Vector("<0,0>") + && $f->eval(t => $x2) == Vector("<$a,$a**2>")) { return 1; - } elsif ((($f . i)**2 == ($f . j)) - && ($f->eval(t => $x1) == Vector("<0,0>"))) + } elsif (($f . i)**2 == $f . j + && $f->eval(t => $x1) == Vector("<0,0>")) { $self->setMessage(3, 'Your right endpoint is not correct.'); return 0; - } elsif ((($f . i)**2 == ($f . j)) - && ($f->eval(t => $x2) == Vector("<$a,$a**2>"))) + } elsif (($f . i)**2 == $f . j + && $f->eval(t => $x2) == Vector("<$a,$a**2>")) { $self->setMessage(2, 'Your left endpoint is not correct.'); return 0; - } elsif ((($f . i)**2 == ($f . j))) { + } elsif (($f . i)**2 == $f . j) { $self->setMessage(2, 'Your left endpoint is not correct.'); $self->setMessage(3, 'Your right endpoint is not correct.'); return 0; @@ -76,12 +87,11 @@ $multians = MultiAnswer(Vector(""), 0, $a)->with( #:% section = statement BEGIN_PGML -Find a vector parametric equation for the parabola -[`y = x^2`] from the origin to the point -[`[$Q]`] using [`t`] as a parameter. +Find a vector parametric equation for the parabola [`y = x^2`] from the origin +to the point [`[$Q]`] using [`t`] as a parameter. -[`\vec{r}(t) =`] [_]{$multians}{10} for [___]{$multians} -[`\leq t \leq`] [___]{$multians}. +[`\vec{r}(t) =`] [_]{$multians}{10} +for [___]{$multians} [`\leq t \leq`] [___]{$multians}. END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Parametric/VectorParametricLines.pg b/tutorial/sample-problems/Parametric/VectorParametricLines.pg index b02f2dfb15..5c2514415a 100644 --- a/tutorial/sample-problems/Parametric/VectorParametricLines.pg +++ b/tutorial/sample-problems/Parametric/VectorParametricLines.pg @@ -16,9 +16,10 @@ #:% subject = parametric #:% section = preamble -#: We load `parserVectorUtils.pl` which provides the `Line()` subroutine for a particular -#: parametrization of a line, as well as `parserParametricLine.pl` which provides a subroutine -#: `ParametricLine()` that allows students to enter any parametrization. +#: The PODLINK('parserVectorUtils.pl') macro is used which provides the +#: `non_zero_point3D`, `non_zero_vector3D`, and `Line` methods. The +#: PODLINK('parserParametricLine.pl') macro is also used which provides the +#: `ParametricLine` method that gives a MathObject form of a parametric line. DOCUMENT(); loadMacros( @@ -28,31 +29,35 @@ loadMacros( ); #:% section = setup -#: For the answer which is a particular parametrization through two points at times `t=0` and `t=1`, -#: we use `Line()`. To allow students to enter any equation for a parametric line through two points, -#: we use `ParametricLine()` The syntax is fairly self-explanatory. +#: The `non_zero_point3D` method returns a nonzero `Point` in the third +#: dimension with random coordinates that are the requested range. The accepted +#: arguments (all of which are optional) are the minimum coordinate value +#: (default -5), maximum coordinate value (default 5), and step size (default 1) +#: respectively. The `non_zero_vector3D` method is the same but returns a +#: `Vector`. +#: +#: The `ParametricLine` method is used for a general parameterization of the +#: line. +#: +#: The `Line` method is used for a particular parametrization through the two +#: points at `t = 0` and `t = 1`. Context('Vector')->variables->are(t => 'Real'); -$P = non_zero_point3D(-9, 9, 1); -$V = non_zero_vector3D(-9, 9, 1); - -$Q1 = Point($P + $V); -$Q2 = Point($P + 2 * $V); +$P = non_zero_point3D(-9, 9); +$V = non_zero_vector3D(-9, 9); $general = ParametricLine($P, $V); -$particular = Line($P, $V, '2t'); +$particular = Line($P, 2 * $V); #:% section = statement BEGIN_PGML -a. Find any vector parametric equation for the -line that goes through the points [`[$P]`] and -[`[$Q1]`]. +a. Find any vector parametric equation for the line that goes through the points +[`[$P]`] and [`[@ Point($P + $V) @]`]. [`\vec{L}(t) =`] [_]{$general}{20} -b. Find a vector parametric equation for the -line that goes through the point [`[$P]`] -when [`t = 0`] and the point [`[$Q2]`] when +b. Find a vector parametric equation for the line that goes through the point +[`[$P]`] when [`t = 0`] and the point [`[@ Point($P + 2 * $V) @]`] when [`t = 1`]. [`\vec{L}(t) =`] [_]{$particular}{20} diff --git a/tutorial/sample-problems/problem-techniques/AdaptiveParameters.pg b/tutorial/sample-problems/ProblemTechniques/AdaptiveParameters.pg similarity index 89% rename from tutorial/sample-problems/problem-techniques/AdaptiveParameters.pg rename to tutorial/sample-problems/ProblemTechniques/AdaptiveParameters.pg index 44dd744273..8abcd8f3d0 100644 --- a/tutorial/sample-problems/problem-techniques/AdaptiveParameters.pg +++ b/tutorial/sample-problems/ProblemTechniques/AdaptiveParameters.pg @@ -21,9 +21,10 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: This problem will utilize with adaptive parameters in the custom answer -#: checker. Define `$aSoln` to be the result of calling `cmp` on a MathObject -#: function with the `checker` option set. +#: This problem utilizes adaptive parameters in the custom answer checker. +#: +#: Define `$aSoln` to be the result of calling `cmp` on a MathObject function +#: with the `checker` option set. #: #: The general solution to the differential equation in this problem is #: $y = c e^x - 1$. The student will be asked to enter a specific solution like @@ -37,9 +38,7 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #: #: If it is desired to allow a solution to differ by an additive constant from #: the answer in the problem, this can be accomplished using existing -#: MathObjects methods, as discussed on the -#: [formulas up to constants](../problem-techniques/FormulasToConstants.html) -#: page. +#: MathObjects methods as discussed in PROBLINK('FormulasToConstants.pg'). #: #: If the answers may be a pure multiple of the correct answer without an #: additive constant, then `0` should not be considered correct. For example, @@ -78,7 +77,7 @@ END_PGML #:% section = solution BEGIN_PGML_SOLUTION - +Solution explanation goes here. END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/AnswerHints.pg b/tutorial/sample-problems/ProblemTechniques/AnswerHints.pg new file mode 100644 index 0000000000..39df65fd19 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/AnswerHints.pg @@ -0,0 +1,89 @@ +## DESCRIPTION +## Shows how to provide answer hints to students. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('answer', 'hints') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Answer Hints +#:% type = technique +#:% categories = [answers, knowls] +#:% section = preamble +#: Make sure the `answerHints.pl` macro is loaded. +DOCUMENT(); + +loadMacros('PGstandard.pl', 'PGML.pl', 'answerHints.pl', 'PGcourse.pl'); + +#:% section = setup +#: To generate specific, customized answer hints for particular answers, use +#: the 'AnswerHints' post-filter provided by PODLINK('answerHints.pl'). The +#: answer hints should be provided by a particular answer, followed by the hash +#: table association operator `=>`, followed by a string that will show up in +#: the messages portion of the answer preview and feedback box when students +#: submit their answers. You may include as many answer and hint pairs as you +#: like, and even use subroutines in them (see PODLINK('answerHints.pl') for +#: more details). +#: +#: If the same error message should be given for several different answers, use +#: a square bracketed list of those answers. For example, if the variables `x`, +#: `t`, and `u` were all in the context, and the correct answer is `6u`, then +#: you could use +#: +#: ```{#answer-hint-multiple .perl} +#: $ans->cmp->withPostFilter(AnswerHints( +#: [ Compute('6t'), Compute('6x') ] => 'Are you using the correct variable?' +#: )) +#: ``` +#: +#: If the MathObjects answer evaluator normally generates a message, the default +#: is not to change a message that is already in place. To override a message +#: generated by a MathObjects answer evaluator, set `replaceMessage => 1` as +#: below. +#: +#: ```{#answer-hint-single .perl} +#: $ans->cmp->withPostFilter(AnswerHints( +#: Compute('6u') => [ 'Good work!', replaceMessage => 1 ] +#: )) +#: ``` +Context()->variables->are(t => 'Real', u => 'Real'); + +$f = Formula('2t'); +$ans = Formula('6u')->cmp->withPostFilter(AnswerHints( + Formula('6t') => 'Are you using the correct variable?', + Formula('6u') => 'Good work!' +)); + +#:% section = statement +#: Knowls (little bits of knowledge) insert a button that will open modal dialog +#: containing the hint when activated. +BEGIN_PGML +If [`f(t) = [$f]`], then [`f(3u)`] = [____]{$ans} + +[@ knowlLink( + 'Hint', + value => 'Substitute \(3u\) wherever you see \(t\) in the formula for \(f\).' +) @]* +END_PGML + +#:% section = hint +#: This is a hint that is revealed to students only after a certain number of +#: submissions. The number of submissions before the hint is shown is specified +#: by the instructor in WeBWorK settings. +#: +#: These hints are added to the problem in the block of text between the +#: commands `BEGIN_PGML_HINT` and `END_PGML_HINT`, which must be on their own +#: lines. The hint will automatically generate the word "HINT" before the block +#: of text that is the hint, so you should not add it manually. +BEGIN_PGML_HINT +Substitute [`3u`] wherever you see [`t`] in the formula for [`f(t) = [$f]`]. +END_PGML_HINT + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/AnswerInExponent.pg b/tutorial/sample-problems/ProblemTechniques/AnswerInExponent.pg similarity index 76% rename from tutorial/sample-problems/problem-techniques/AnswerInExponent.pg rename to tutorial/sample-problems/ProblemTechniques/AnswerInExponent.pg index 4666033458..40888dba6e 100644 --- a/tutorial/sample-problems/problem-techniques/AnswerInExponent.pg +++ b/tutorial/sample-problems/ProblemTechniques/AnswerInExponent.pg @@ -15,7 +15,7 @@ #:% name = Answer in an Exponent #:% type = technique -#:% categories = [answer, exponent] +#:% categories = [answers, exponent] #:% section = preamble DOCUMENT(); @@ -32,18 +32,19 @@ $ans_rule1 = ans_rule(4); $ans_rule2 = ans_rule(4); #:% section = statement -#: To create an exponential, there is different code for both the TeX (hardcopy) -#: and HTML version. The `TeX` is as expected using the standard power `^`. -#: The HTML version creates a exponent using the `vertical-align` attribute. +#: To create an exponential, there is different code for both the TeX (hard +#: copy) and HTML version. The `TeX` is as expected using the standard power +#: `^`. The HTML version creates a exponent using the `vertical-align` +#: attribute. BEGIN_PGML Rewrite the following using a single exponent. [@ MODES( TeX => "\\( \\displaystyle a^{$n}b^{$n} = ( $ans_rule1)^{$ans_rule2} \\)", - HTML =>"
\\( \\displaystyle a^{$n}b^{$n} = (\\)$ans_rule1\\()\\)" . - " $ans_rule2
" + HTML =>"
\\( \\displaystyle a^{$n}b^{$n} = " + . "\\Big(\\) $ans_rule1 \\(\\Big)\\)" + . " $ans_rule2
" )@]* - END_PGML #:% section = answer diff --git a/tutorial/sample-problems/problem-techniques/AnswerIsSolutionToEquation.pg b/tutorial/sample-problems/ProblemTechniques/AnswerIsSolutionToEquation.pg similarity index 64% rename from tutorial/sample-problems/problem-techniques/AnswerIsSolutionToEquation.pg rename to tutorial/sample-problems/ProblemTechniques/AnswerIsSolutionToEquation.pg index 7ebb21f9b3..b3e4c458dc 100644 --- a/tutorial/sample-problems/problem-techniques/AnswerIsSolutionToEquation.pg +++ b/tutorial/sample-problems/ProblemTechniques/AnswerIsSolutionToEquation.pg @@ -15,27 +15,28 @@ #:% name = Answer is a Solution to an Equation #:% type = technique -#:% categories = [answer, exponent] +#:% categories = [answers, exponent] #:% section = preamble -#: We need to include the macros file `parserSolutionFor.pl` +#: Include the macro PODLINK('parserSolutionFor.pl'). DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserSolutionFor.pl', 'PGcourse.pl'); #:% section = setup -#: The function `SolutionFor('equation',point,options)` takes an equation, a point that -#: satisfies that equation, and options such as `vars=>['y','x']` in case you want to change -#: the order in which the variables appear in order pairs (the default is lexicographic ordering of the variables). +#: The function `SolutionFor(equation, point, options)` takes an equation, a +#: point that satisfies that equation, and options such as `vars => ['y', 'x']` +#: in case you want to change the order that the variables appear in ordered +#: pairs (the default is lexicographic ordering of the variables). Context('Vector')->variables->are(x => 'Real', y => 'Real'); -$f = SolutionFor("x^2 = cos(y)", "(1,0)"); +$f = SolutionFor("x^2 = cos(y)", "(1, 0)"); #$f = SolutionFor("x^2 - y = 0", [ 2, 4 ]); #$f = SolutionFor("x^2 - y = 0", Point(4, 2), vars => [ 'y', 'x' ]); #:% section = statement BEGIN_PGML -A solution to [`[@ $f->{f} @]`] is [`(x,y) =`] [___]{$f} +A solution to [`[@ $f->{f} @]`] is [`(x, y) =`] [___]{$f} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/problem-techniques/AnyAnswerMarkedCorrect.pg b/tutorial/sample-problems/ProblemTechniques/AnyAnswerMarkedCorrect.pg similarity index 67% rename from tutorial/sample-problems/problem-techniques/AnyAnswerMarkedCorrect.pg rename to tutorial/sample-problems/ProblemTechniques/AnyAnswerMarkedCorrect.pg index 64cf0e833f..70e44a96d9 100644 --- a/tutorial/sample-problems/problem-techniques/AnyAnswerMarkedCorrect.pg +++ b/tutorial/sample-problems/ProblemTechniques/AnyAnswerMarkedCorrect.pg @@ -15,24 +15,24 @@ #:% name = Any Answer is Marked Correct #:% type = technique -#:% categories = [answer] +#:% categories = [answers] #:% section = preamble -#: We need to include the macros file unionTables.pl DOCUMENT(); + loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); -#:% section=setup -#: We wrap the random command with a `Compute` to make `$a` a MathObject. +#:% section = setup +#: Wrap the random command with `Compute` to make `$a` a MathObject. #: -#: The checker then returns 1 which will make any answer correct. -$a = Compute(random(2, 9, 1)); +#: The checker simply returns 1 which will make any answer correct. +$a = Compute(random(2, 9)); $ans = $a->cmp(checker => sub { return 1; }); #:% section = statement BEGIN_PGML -Enter anything, e. g. [`[$a] `] and it will be marked correct: [__]{$ans} +Enter anything, e.g. [`[$a]`] and it will be marked correct: [__]{$ans} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/ProblemTechniques/CalculatingWithPoints.pg b/tutorial/sample-problems/ProblemTechniques/CalculatingWithPoints.pg new file mode 100644 index 0000000000..625d370fcf --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/CalculatingWithPoints.pg @@ -0,0 +1,70 @@ +## DESCRIPTION +## Perform calculations with Points. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('point') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Calculating with Points +#:% type = technique +#:% categories = [point] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Switch to the `Point` context, and generate two points with random +#: coordinates. +#: +#: Compute the difference between the points and call the `Point` `value` method +#: to retrieve an array of the coordinates of that difference. The coordinates +#: are unpacked from that array into the variables `$d1` and `$d2`. +#: +#: Note that if you want only one of the coordinates of a `Point`, you can use +#: the `extract` method, for example: `$x = $point->extract(1)` obtains the +#: first coordinate of `$point` and assigns it to the variable `$x`. +#: +#: Note that if `Context('Vector')` and `norm($point[0] - $point[1])` were +#: used then an answer like `|<5, 7> - <7, 8>|` would be accepted as correct, +#: and that is not desired in this problem. +#: +#: Note that to compute the `$length`, the parentheses around `$d1` and `$d2` +#: are needed in the `Compute` expression because if `$d1 = -6` and `$d2 = 1`, +#: then the string `"sqrt($d1^2 + $d2^2)"` first interpolates to +#: `"sqrt(-6^2 + 1^2)"` which will evaluate to `sqrt(-36 + 1)` instead of +#: `sqrt(36 + 1)` as desired. +Context('Point'); +$point1 = Point(random(1, 5, 1), random(-5, -1, 1)); +$point2 = Point(random(5, 10, 1), random(6, 11, 1)); + +# If $point1 = (x1,y1) and $point2 = (x2,y2), +# then the following makes $d1 = x1 - x2, $d2 = y1 - y2 +($d1, $d2) = ($point1 - $point2)->value; + +$length = Compute("sqrt(($d1)^2 + ($d2)^2)"); +$mid = ($point2 + $point1) / 2; + +#:% section = statement +BEGIN_PGML +Consider the two points [`[$point1]`] and [`[$point2]`]. + +a. The distance between them is: [_]{$length}{6} + +b. The midpoint of the line segment that joins them is: [_]{$mid}{10} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/ComposingFunctions.pg b/tutorial/sample-problems/ProblemTechniques/ComposingFunctions.pg similarity index 70% rename from tutorial/sample-problems/problem-techniques/ComposingFunctions.pg rename to tutorial/sample-problems/ProblemTechniques/ComposingFunctions.pg index 9cf0235ddd..c3fee68fd4 100644 --- a/tutorial/sample-problems/problem-techniques/ComposingFunctions.pg +++ b/tutorial/sample-problems/ProblemTechniques/ComposingFunctions.pg @@ -18,7 +18,7 @@ #:% categories = [composition] #:% section = preamble -#: We need to include the macros file `answerComposition.pl` +#: Include the macro PODLINK('answerComposition.pl'). DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'answerComposition.pl', 'PGcourse.pl'); @@ -27,8 +27,8 @@ Context()->variables->are(x => 'Real', y => 'Real', u => 'Real'); $a = random(2, 9); -$f = Formula("sqrt(u)"); -$g = Formula("x^2+$a"); +$f = Formula('sqrt(u)'); +$g = Formula("x^2 + $a"); #:% section = statement BEGIN_PGML @@ -41,8 +41,11 @@ of two simpler functions [`y = f(u)`] and [`u = g(x)`]. END_PGML #:% section = answer -#: This must be called with the method `COMPOSITION_ANS` with the arguments that -#: will test for `f(g(x))` +#: Call the `COMPOSITION_ANS` method with the arguments `$f` and `$g` to test +#: that the composition of the student answers for `f` and `g` is equal to the +#: composition of the correct answers. Note that the `COMPOSITION_ANS` checker +#: takes care to not allow certain trivial answers like `f(u) = u` and +#: `g(x) = sqrt(x^2 + $a)`. COMPOSITION_ANS($f, $g); #:% section = solution diff --git a/tutorial/sample-problems/ProblemTechniques/ConstantsInProblems.pg b/tutorial/sample-problems/ProblemTechniques/ConstantsInProblems.pg new file mode 100644 index 0000000000..281cfc2963 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/ConstantsInProblems.pg @@ -0,0 +1,58 @@ +## DESCRIPTION +## Provides constants in a PG problem. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('constants') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Constants in Problems +#:% type = technique +#:% categories = [constant] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Add the constant `k` with value `0.023431412` to the context. Choose a value +#: for `k` that is not easy to guess, because if the value were guessed it would +#: lead to unexpected results. For example, if a value of `1` were chosen, and a +#: student entered `1` for the answer in this problem, then it would be counted +#: as correct which is not desired. +#: +#: Note that in this case `constants->add` is used, so that the constant `k` is +#: added to existing constants in the context. If `constants->are` were used +#: instead, then all existing constants would be removed from the context and +#: `k` would be added as the only constant in the context. This is similar to +#: the use of `variables->add` and `variables->are` when defining variables in a +#: problem. +#: +#: One other tweak that might be desired is to set a context flag so that +#: student answers are not reduced to numerical values. This is done by setting +#: `formatStudentAnswer => 'parsed'` as shown. +Context()->constants->add(k => 0.023431412); + +Context()->flags->set(formatStudentAnswer => 'parsed'); + +$ans = Compute('k'); + +#:% section = statement +BEGIN_PGML +Let [`f(x) = x - k`] where [`k`] is a constant. Then [`f(x) = 0`] when +[`x =`] [___]{$ans}. +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/CustomAnswerCheckers.pg b/tutorial/sample-problems/ProblemTechniques/CustomAnswerCheckers.pg new file mode 100644 index 0000000000..4230d1064c --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/CustomAnswerCheckers.pg @@ -0,0 +1,70 @@ +## DESCRIPTION +## This provides a custom answer checker. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('answer', 'custom') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Custom Answer Checker +#:% type = technique +#:% categories = [answers, custom] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: This problem asks the student to enter a value of `x` such that +#: `cos(x) = cos($ans)`. A custom checker is needed since the particular correct +#: answer computed in the setup will only be one of the actual correct answers +#: for the problem. +#: +#: To set up the custom answer checker pass the custom checker subroutine for +#: the `checker` option to the `cmp` method. +#: +#: The custom checker subroutine checks that the cosine of the student answer is +#: equal to the cosine of the particular correct answer computed in the setup. +#: +#: An error message can be set in the answer checker by calling +#: `Value->Error("message")`. This will set the message that is displayed to the +#: student and exit the checker with an incorrect return value. +#: +#: Another tip for troubleshooting. To see all of the keys and values in the +#: `$ansHash` when the submit answers button is pressed, include the following +#: in the custom answer checker. +#: +#: ```{.perl} +#: for my $key (keys %$ansHash) { +#: warn "key: $key, value: $ansHash->{$key}"; +#: } +#: ``` +$ans = Compute('pi/3')->cmp( + checker => sub { + my ($correct, $student, $ansHash) = @_; + Value->Error('Try again.') + if cos($student) == sqrt(3) / 2 && !$ansHash->{isPreview}; + return cos($correct) == cos($student); + } +); + +#:% section = statement +BEGIN_PGML +Enter a value of [`x`] for which [`\cos(x) = [@ Compute('cos(pi/3)') @]`] + +[`x =`] [___]{$ans} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/CustomAnswerListChecker.pg b/tutorial/sample-problems/ProblemTechniques/CustomAnswerListChecker.pg new file mode 100644 index 0000000000..079e531646 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/CustomAnswerListChecker.pg @@ -0,0 +1,133 @@ +## DESCRIPTION +## This shows how to check an arbitrary list of student answers. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(Fitchburg State University) +## Author(Peter) +## MO(1) +## KEYWORDS('answer', 'custom', 'list') + +#:% name = Custom Answer List Checker +#:% type = technique +#:% categories = [answers, custom, list] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: The answer to this problem will be a list of points, so select the 'Point' +#: context. +#: +#: Then create a list of 3 points that will be shown as the correct answer. +#: +#: Since the given correct answer is a `List`, in order to check the answers in +#: the list the student enters as a whole (instead of a separate check for each +#: entry) a `list_checker` needs to be used instead of a `checker`. To set up +#: the custom list checker pass the custom checker subroutine for the +#: `list_checker` option to the `cmp` method. +#: +#: This custom list checker loops over the list of answers that the student has +#: entered, and checks that each answer is a point, is not the same as a +#: previous point already checked, and finally that the point satisfies the +#: requested constraint. A message is added to the `@error` array if the answer +#: is not correct. Otherwise the score is incremented. +#: +#: In this example, the `showLengthHints => 1` and `showHints => 0` options are +#: also passed to the `cmp` method. This means that if the correct answer is a +#: MathObject `List` of `Point`s, and a student does not give as many answers as +#: there are in the correct answer, then the message "There should be more +#: points in your list" will be shown. Also if the correct answer is a +#: MathObject `List` of `Point`s, and a student gives more answers than are in +#: the correct answer that are not all correct, then the message "There should +#: be fewer points in your list" will be shown. If it is desired to give your +#: own messages for those cases, then add code to the custom checker to look for +#: those cases and add appropriate messages, and to prevent the default messages +#: from being shown as well as the messages added by the custom list checker, +#: pass `showLengthHints => 0` to the `cmp` method instead. Note that by default +#: the values for both of those options are set to the value of +#: `$showPartialCorrectAnswers` (`$showPartialCorrectAnswers` is 1 by +#: default). +#: +#: The return value of a `list_checker` should be a list consisting of the +#: numeric score followed by any messages. Any messages returned after the score +#: will be displayed in feedback to the student. The score that will be given +#: for the list answer is the numeric score returned divided by the maximum of +#: the number of entries in the correct answer list and the number of entries in +#: the student answer. So for this problem if a student gives four distinct +#: points that satisfy constraint, then the score will be 1 even though more +#: entries were given than were asked for. However, if `partialCredit => 0` is +#: passed to the `cmp` method, then the score for the list answer will be 1 if +#: the numeric score returned is equal to the maximum of of entries in the +#: correct answer list and the number of entries in the student answer, and 0 +#: otherwise. Note that the default value for the `partialCredit` option is the +#: value of `$showPartialCorrectAnswers`. +Context('Point'); + +$c = random(4, 8); +$ans = List("(0, $c), ($c, 0), ($c - 1, 1)")->cmp( + list_checker => sub { + my ($correct, $student, $ansHash, $value) = @_; + return 0 if $ansHash->{isPreview}; + + my $n = scalar(@$student); # number of student answers + my $score = 0; # number of correct student answers + my @errors; # stores error messages + + # Loop though the student answers. + STUDENT_ANS: + for my $i (0 .. $n - 1) { + my $ith = Value::List->NameForNumber($i + 1); + my $p = $student->[$i]; # ith student answer + + # Check that the student's answer is a point. + if ($p->type ne 'Point') { + push(@errors, "Your $ith entry is not a point."); + next; + } + + # Check that the point hasn't been given before. If it is the same + # as a previous answer, then skip to the next student answer. + for (my $j = 0; $j < $i; ++$j) { + if ($student->[$j]->type eq 'Point' && $student->[$j] == $p) + { + push(@errors, + "Your $ith point is the same as a previous one."); + next STUDENT_ANS; + } + } + + # Check that it satisfies the equation and increase the score if so. + my ($a, $b) = $p->value; + if ($a + $b == $c) { + ++$score; + } else { + push(@errors, "Your $ith point is not correct."); + } + } + + return ($score, @errors); + }, + showLengthHints => 1, + showHints => 0 +); + +#:% section = statement +BEGIN_PGML +Enter three distinct points [`(x, y)`] that satisfy the equation +[`x + y = [$c]`]. + +[_]{$ans}{15} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Any three points who's coordinates sum to [`[$c]`] are valid. For example, +[`([$c], 0), (0, [$c]), (1, [$c - 1])`]. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/DataTables.pg b/tutorial/sample-problems/ProblemTechniques/DataTables.pg new file mode 100644 index 0000000000..cadb8fd4f9 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/DataTables.pg @@ -0,0 +1,117 @@ +## DESCRIPTION +## This shows how to present and format a table for data. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2023) +## Institution(Fitchburg State University) +## Author(Peter Staab) +## MO(1) +## KEYWORDS('tutorial', 'table') + +#:% name = Data Table +#:% type = [technique] +#:% categories = table + +#:% section = preamble +#: This shows how to use the `DataTable` function in PODLINK('niceTables.pl'). +#: Note that the PODLINK('niceTables.pl') macro is automatically loaded by +#: `PGML.pl`, so it is not necessary to load it explicitly. +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: A `DataTable` is meant to display data in a table. It is not meant to be used +#: for layout. If a layout is needed use `LayoutTable` instead. See +#: PROBLINK('LayoutTable.pg') for an example of the usage of the `LayoutTable` +#: method. +#: +#: Typically the `PGML` syntax for a `DataTable` should be used as is done in +#: this example. However, the result of calling `DataTable` could also be used. +#: It is called as follows. +#: +#: ```{#datatable .perl} +#: $table = DataTable( +#: [ +#: [row1], +#: [row2], +#: ... +#: [rowN] +#: ], +#: options +#: ); +#: ``` +#: +#: where the data goes in as an array reference of array references. The first +#: row can (and is often) used for a header row. Some of the `DataTable` options +#: are demonstrated here, but the full list of options and explanation of there +#: usage is in PODLINK('the niceTables.pl documentation','niceTables.pl') +#: +#: Then the table would be inserted into the problem text by adding `[$table]*` +#: in the desired location inside the `BEGIN_PGML/END_PGML` block. +#: +#: The first table in this example will display randomly generated data stored +#: in the `@rows` array that is generated in the setup. +#: +#: Note that a reference to the `@rows` array needs to be passed to the +#: `DataTable` method, and `\@rows` is used to obtain the array reference. +#: However, if this reference were not created inside the `BEGIN_PGML/END_PGML` +#: block then `~~@rows` would need to be used instead to obtain this array +#: reference due to PG translation. +#: +#: The second table in this example will ask the student to complete a table of +#: values for the function that is defined at the end of the setup. +@rows = map { [ $_, random(1, 10) ] } 1 .. 5; + +$a = non_zero_random(-4, 4); +$f = Compute("x / (x - [$a])")->reduce; + +#:% section = statement +#: The `PGML` syntax for inserting a `DataTable` is `[# ... #]{ options }`. In +#: between `[#` and `#]` any number of cells can be added with `[. ... .]`. The +#: end of a row is marked by adding a `*` to the end of the last cell in the +#: row (the `*` can be omitted on the last row). Inside of a cell started with +#: `[.` and ended with `.]` all of the usual `PGML` syntax can be used. +#: +#: Options can also be passed to a cell with `[. ... .]{ options }`. Note that +#: some cell options apply to the entire row that contains the cell. This is the +#: case for the `headerrow => 1` option. +#: +#: One special cell option is the `rows` option used in the first table example. +#: It can only be used with a cell that has no contents, and its option must be +#: an array reference of array references (the same as the usual first argument +#: for the `DataTable` method. In this case the rows defined in the array +#: reference will be added to the table. +BEGIN_PGML +Here's some data: + +[# + [.[`x`].] [.[`y`].]*{ headerrow => 1 } + [. .]{ rows => \@rows } +#] + +Complete the following table of values for [``f(x) = [$f]``]. + +[$tab2]* +[# + [.[`x`].] [.[`f(x)`].]*{ headerrow => 1 } + [.[$a - 2].] [.[_]{$f->eval(x => $a - 2)}.]* + [.[$a - 1].] [.[_]{$f->eval(x => $a - 1)}.]* + [.[$a + 1].] [.[_]{$f->eval(x => $a + 1)}.]* + [.[$a + 2].] [.[_]{$f->eval(x => $a + 2)}.] +#]{ + align => '|r|c|', + valign => 'middle', + padding => [ 0.5, 0.5 ], + horizontalrules => 1 +} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/AddingFunctions.pg b/tutorial/sample-problems/ProblemTechniques/DefiningFunctions.pg similarity index 51% rename from tutorial/sample-problems/problem-techniques/AddingFunctions.pg rename to tutorial/sample-problems/ProblemTechniques/DefiningFunctions.pg index 68a9faae46..e0d8090793 100644 --- a/tutorial/sample-problems/problem-techniques/AddingFunctions.pg +++ b/tutorial/sample-problems/ProblemTechniques/DefiningFunctions.pg @@ -13,33 +13,33 @@ # updated to full problem by Peter Staab (06/01/2023) -#:% name = Adding Functions to a Context +#:% name = Defining Functions in a Context #:% type = technique #:% categories = [numbers, tolerance] #:% section = preamble -#: We need to load the `parserFunction.pl` macro, and then use one of its -#: routines to define a new function that students may type in their answers. +#: Load the PODLINK('parserFunction.pl') macro which is used to add a new +#: function to the context. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserFunction.pl', 'PGcourse.pl'); #:% section = setup -#: First, we define a function `f(x,y)`. The actual function doesn't matter but shouldn't -#: be easily guessed, since a student could put in `sin(x*y)-exp(y)` and get the answer correct. -#: -#: This is a useful technique for any problem that the question is about a generic function +#: Define a function `f(x,y)`. The actual function doesn't matter but shouldn't +#: be easily guessed, since a student could enter it and get the answer correct. +#: This is a useful technique for any problem adking is about a generic function #: rather than a specific one. Context()->variables->are(x => 'Real', y => 'Real'); -parserFunction('f(x,y)' => 'sin(x*y)-exp(y)'); -$ans = Compute('f(x-4,3)'); +parserFunction('f(x,y)' => 'sin(x * y) - exp(y)'); +$ans = Compute('f(x - 4, 3)'); #:% section = statement BEGIN_PGML -Given a surface [`z = f(x,y)`], what is the equation for the [`z`] -coordinate -of the surface along a line having [`y=3`], shifted four units to the right? +Given a surface defined by [`z = f(x, y)`], what is the equation for the +[`z`]-coordinate of the surface along the line [`y = 3`], shifted four units to +the right? -[`z = `] [_____]{$ans} +[`z =`] [_____]{$ans} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/ProblemTechniques/DifferentiatingFormulas.pg b/tutorial/sample-problems/ProblemTechniques/DifferentiatingFormulas.pg new file mode 100644 index 0000000000..02234a8e0c --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/DifferentiatingFormulas.pg @@ -0,0 +1,62 @@ +## DESCRIPTION +## This shows how to check "arbitrary" conditions on the student's answer. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('differentiate') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Differentiating Formulas +#:% type = technique +#:% categories = [derivative] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: The `Numeric` context includes the variable `x` by default, but the variable +#: `y` must be added to the context. +#: +#: The differentiation operator `D(variable name)` is used to take a partial +#: derivative with respect to the variable give for `variable name`. Note that +#: the differentaion operator returns a MathObject `Formula`. +#: +#: The `substitute` method is used to evaluate the `Formula` for the partial +#: derivative with respect to `x` at a particular value. +Context()->variables->add(y => 'Real'); + +$a = random(2, 4); +$f = Formula('x^2y'); + +$fx = $f->D('x'); +$fxa = $fx->substitute(x => $a); +$fy = $f->D('y'); +$fyx = $fy->D('x')->reduce; + +#:% section = statement +BEGIN_PGML +Suppose [`f(x) = [$f]`]. Then + +a. [``\frac{\partial f}{\partial x} =``] [____]{$fx} + +b. [`f_x ([$a],y) =`] [____]{$fxa} + +c. [`f_y(x, y) =`] [____]{$fy} + +d. [`f_{yx}(x, y) =`] [___]{$fyx} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/DigitsTolType.pg b/tutorial/sample-problems/ProblemTechniques/DigitsTolType.pg new file mode 100644 index 0000000000..4dcf602044 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/DigitsTolType.pg @@ -0,0 +1,98 @@ +## DESCRIPTION +## This describes an alternative way for determining +## the tolerance type based on the number of digits. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('answer', 'tolerance') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Number of Digits and Tolerance in Answers +#:% type = technique +#:% categories = [answers, tolerance] +#:% see_also = [NumericalTolerance.pg] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Several context flags are set for this problem. The meaning of these flags +#: are decribed in the following list. +#: +#: * The flag `tolType => 'digits'` switches from the default `'relative'` +#: tolerance type to the `'digits'` tolerance type. +#: * The flag `tolerance => 3` sets the number of digits to check to 3. The +#: default value is the same default as for other tolerance types, `0.001`. +#: Any tolerance that is between 0 and 1 is converted via `log10` and rounding +#: to an integer. So `0.001` would be converted to 3. +#: * The `tolTruncation` flag is either 1 (true) or 0 (false). Its default is 1. +#: Details are explained below. +#: * The `tolExtraDigits` flag sets the number of extra digits to examine beyond +#: the first tolerance digits. Its default value is 1. This is explained +#: below. +#: +#: If the student enters additional digits, the first additional +#: `tolExtraDigits` digits are examined in the same manner. For example, if the +#: correct answer is `pi = 3.1415926...` and default flag values are used, the +#: student can answer with 3.14, 3.141, 3.142, 3.1415, and even 3.1418 since +#: that 8 is beyond the extra digits checking. But for example 3.143 is not +#: accepted, since the first extra digit is not right. (And if `tolTruncation` +#: is false, 3.141 would not be accepted either.) +#: +#: The goal is that the student must enter at least the first tolerance digits +#: correctly. The last digits that they enter might be rounded (always accepted) +#: or truncated (only accepted if `tolTruncation` is true). For example, if the +#: correct answer is e = 2.7182818... and tolerance is 3, the student can answer +#: with 2.72. Or they can answer with 2.71 if `tolTruncation` is true. But for +#: example 2.7 and 2.73 are not accepted. +#: +#: Warning: This tolerance type also applies to formula comparisons. For example +#: if the answer is `2^x` and a student enters `e^(0.69x)`, this will probably +#: not be accepted. Random test values will be used for x to make that +#: comparison. For example if one of the test values is `x = 2`, the correct +#: output is 4 and the student's output would be 3.9749... and this would be +#: declared as not a match, since the first three digits to not agree. +#: +#: Warning: this article is about using this tolerance type for comparison of +#: correct answers to student answers. But if this tolerance type is activated +#: for a context, it also applies to comparisons you might make in problem setup +#: code. It may be important to understand that it is not symmetric. For +#: example, under default conditions, `Real(4) == Real(3.995)` is false, while +#: `Real(3.995) == Real(4)` is true. The left operand is viewed as the "correct" +#: value. With `Real(4) == Real(3.995)`, that "5" violates the `tolExtraDigits` +#: checking. But with `Real(3.995) == Real(4)`, it is as if the student entered +#: 4.00 and has the first 3 digits correct accounting for rounding. (Note that +#: the default tolerance type relative is similarly asymmetric, but the effect +#: is more subtle. You can see it with `Real(4) == Real(3.996001)` versus +#: `Real(3.996001) == Real(4)`.) +Context()->flags->set( + tolType => 'digits', + tolerance => 3, + tolTruncation => 1, + tolExtraDigits => 1 +); +$ans = Real('pi'); + +#:% section = statement +BEGIN_PGML +The option [|tolTruncation|]* set to true (1) for this problem. The exact answer +is [`\pi`]. Enter [`3.14`], [`3.15`], [`3.141`], or [`3.142`] and see which +answers are accepted. + +[`\pi =`] [_]{$ans} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/DisableFunctions.pg b/tutorial/sample-problems/ProblemTechniques/DisableFunctions.pg new file mode 100644 index 0000000000..8296508c47 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/DisableFunctions.pg @@ -0,0 +1,102 @@ +## DESCRIPTION +## This shows how to disable functions allowed in student answers. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('answer') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Disabling Functions in Student Answers +#:% type = technique +#:% categories = [answers] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Specific operations in the context can be disabled with +#: `Context()->operations->undefine`. The predefined operations are `*` +#: (multiplication), `/` (division), `+` (addition), `-` (subtraction), `!` +#: (factorial), `><` (cross product), `U` (union), `^` (exponent), `**` +#: (exponent), `.` (dot product), and `,` (list creation). +#: +#: After disabling an operation, it can be re-enabled with +#: `Context()->operators->redefine`, e.g., +#: `Context()->operators->redefine('^')`. +#: +#: Operators can be removed from the context with +#: `Context()->operators->remove`, but this is not recommended as it makes it +#: completely unknown in the context so that students will not get helpful error +#: messages if they try to use them. +#: +#: Specific functions can be disabled in the context with +#: `Context()->functions->undefine`. The predefined functions are `sin`, `cos`, +#: `tan`, `sec`, `csc`, `cot`, `asin`, `acos`, `atan`, `asec`, `acsc`, `acot`, +#: `sinh`, `cosh`, `tanh`, `sech`, `csch`, `coth`, `asinh`, `acosh`, `atanh`, +#: `asech`, `csch`, `acoth`, `ln`, `log`, `log10`, `exp`, `sqrt`, `abs`, `int`, +#: `sgn`, `atan2`, `norm`, `unit`, `arg`, `mod`, `Re`, `Im`, and `conj`. +#: +#: In addition, classes of functions can be disabled by passing one of the +#: following arguments to `Context()->functions->disable`: +#: +#: * `Trig`: disables all trig functions listed in both `SimpleTrig` and +#: `InverseTrig` functions +#: * `SimpleTrig`: disables `sin`, `cos`, `tan`, `sec`, `csc`, `cot` +#: * `InverseTrig`: disables `asin`, `acos`, `atan`, `asec`, `acsc`, `acot`, +#: `atan2` +#: * `Hyperbolic`: disables all hyperbolic functions in both `SimpleHyperbolic` +#: and `InverseHyperbolic` functions +#: * `SimpleHyperbolic`: disables `sinh`, `cosh`, `tanh`, `sech`, `csch`, `coth` +#: * `InverseHyperbolic`: disables `asinh`, `acosh`, `atanh`, `asech`, `acsch`, +#: `acoth` +#: * `Numeric`: disables `ln`, `log`, `log10`, `exp`, `sqrt`, `abs`, `int`, +#: `sgn` +#: * `Vector`: disables `norm`, `unit` +#: * `Complex`: disables `arg`, `mod`, `Re`, `Im`, `conj` +#: * `All`: diables all predefined functions +#: +#: Alternatively, the following syntax can be used. +#: +#: ```{#disable-functions .perl} +#: Parser::Context::Functions::Disable('All'); +#: ``` +# Disable exponents in the context. +Context()->operators->undefine('^', '**'); + +# Disable the 'sin', 'cos', 'tan', 'abs', and 'sqrt' functions. +Context()->functions->undefine('sin', 'cos', 'tan', 'abs', 'sqrt'); + +# Remove the '|' parenthesis operator which starts and ends an absolute value. +# This needs to be done in addition to disabling the 'abs' function to fully +# disable absolute values. +Context()->parens->remove('|'); + +# Give consistent error messages if the '|' absolute value parenthesis operator +# is attempted to be used by the student. +Context()->{error}{convert} = sub { + my $message = shift; + $message =~ s/Unexpected character '~~|'/Absolute value is not allowed/; + return $message; +}; + +$ans = Compute('1/2'); + +#:% section = statement +BEGIN_PGML +Find the numerical value: [`\sin^2(\pi / 4) =`] [____]{$ans} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/DraggableSubsets.pg b/tutorial/sample-problems/ProblemTechniques/DraggableSubsets.pg similarity index 55% rename from tutorial/sample-problems/problem-techniques/DraggableSubsets.pg rename to tutorial/sample-problems/ProblemTechniques/DraggableSubsets.pg index 6f1de8f20c..09e781728c 100644 --- a/tutorial/sample-problems/problem-techniques/DraggableSubsets.pg +++ b/tutorial/sample-problems/ProblemTechniques/DraggableSubsets.pg @@ -23,25 +23,31 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'draggableSubsets.pl', 'PGcourse.pl'); #:% section = setup -#: The DraggableSubsets call visually creates a set of regions called buckets that -#: labelled boxes can be dragged between. The general form is -#:```{#draggable-subsets-usage .perl} +#: The `DraggableSubsets` method creates a drag and drop object that when +#: inserted into the problem text creates regions called buckets representing +#: sets that contain draggable elements that can be dragged between the buckets +#: (or sets). The syntax for calling it is +#: +#: ```{#draggable-subsets-usage .perl} #: $draggable = DraggableSubsets( -#: $full_set, -#: $answer_sets, -#: %options -#: ); -#:``` +#: elements, +#: correct sets, +#: options +#: ); +#: ``` #: -#: where `$full_set` is the set of all labelled boxes. The `$answer_sets` is a nested -#: array reference of distribution of the correct subsets. There are many options. The -#: example here shows the use of `DefaultSubsets` which shows how to label and initialize -#: the buckets. The `AllowNewBuckets` option allows the student in add a new bucket (1) -#: or not (0). The `OrderedSubsets` option requires that the subsets in the student -#: answer be the same as in the correct answer. +#: where `elements` is a reference to an array of strings which are textual +#: representations of all elements contained in the sets and `correct sets` is a +#: reference to an array of array references representing the correct subsets +#: with the elements listed by 0-based index from the `elements` list. There are +#: several options that can be passed. The `DefaultSubsets` option is used to +#: label and initialize the buckets, and determine where elements are initially. +#: If the `AllowNewBuckets` option is set to 1, then students can add new +#: buckets. If the `OrderedSubsets` option is set to 1, then the subsets in the +#: student answers are required to be the in the same order as the subsets in +#: the correct answer. #: -#: See the [DraggableProofs](../Misc/DraggableProof.html) for an example of -#: how to create drag and drop proof problems. +#: See PODLINK('draggableSubsets.pl') for other options and more details. $draggable = DraggableSubsets( [ 'mouse', 'ebola bacteria', @@ -52,7 +58,6 @@ $draggable = DraggableSubsets( 'blue whale', 'eagle' ], [ [], [ 0, 4, 6, 7, 8, 9, 10 ], [ 5, 11 ], [ 1, 2, 3 ] ], - # ['mouse','house cat','coyote','tapir','hippopatamus','elephant'] DefaultSubsets => [ { label => 'Animals', indices => [ 0 .. 11 ] }, { label => 'Mammals', indices => [] }, diff --git a/tutorial/sample-problems/ProblemTechniques/EquationEvaluators.pg b/tutorial/sample-problems/ProblemTechniques/EquationEvaluators.pg new file mode 100644 index 0000000000..1f126ff915 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/EquationEvaluators.pg @@ -0,0 +1,103 @@ +## DESCRIPTION +## This code shows how to check student answers that are equations. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('answer', 'custom') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Implicit Equations Evaluators +#:% type = technique +#:% categories = [implicit] + +#:% section = preamble +#: Include the macro PODLINK('parserImplicitEquation.pl'). +DOCUMENT(); +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'parserImplicitEquation.pl', 'PGcourse.pl' +); + +#:% section = setup +#: Select the `ImplicitEquation` context, and define the equation for the +#: answer. Note that the `ImplicitEquation` context contains the variables `x` +#: and `y` by default. If other variables are needed, they will need to be +#: added. +#: +#: Its often important to set the limits for variables when working with +#: equations. In this case the limits for the variable `x` are set to the +#: interval `[-2, 2]` and the limits for the variable `y` are set to the +#: interval `[0, 4]`. +#: +#: The `ImplicitEquation` checker attempts to locate the solutions for the given +#: equation using a random search. This search can fail in some cases which will +#: result in correct answers being marked incorrect, or incorrect answers being +#: marked correct. Thus it is generally a good idea to specify some particular +#: solutions. This can be done in the `ImplicitEquation` call, by passing the +#: `solutions` option as is done in this example. +#: +#: Also, for this type of answer checking it is likely that the student will +#: represent the function in a form that exceeds the default problem checking +#: tolerances, and so be marked as incorrect. To correct for this it may be +#: necessary to specify a tolerance. An absolute tolerance can be set in the +#: `ImplicitEquation` call, e.g., +#: +#: ```{#set-tolerance .perl} +#: $eqn = ImplicitEquation("y = (x-1)^2", tolerance => 0.0001); +#: ``` +#: +#: It is possible to remove the error message "Can't find any solutions to your +#: equation" by remapping it to another error message. The message has to be +#: non-empty, but it can be the string containing one space `" "`, as in +#: +#: ```{#error-message .perl} +#: Context()->{error}{msg}{"Can't find any solutions to your equation"} = " "; +#: ``` +#: +#: However, this is not recommended as it can be confusing. Although no error +#: message will be shown, the answer feedback button will still show a dot +#: indicating a message is present. +#: +#: A better way to remove the error message would be to use a post-filter to +#: remove the message after the answer has been graded. The +#: PODLINK('answerHints.pl') file provides one way to do this. +#: A post-filter can also be added manually for this as in the following +#: example. +#: +#: ```{#post-filter .perl} +#: $ans = ImplicitEquation('y = (x-1)^2')->cmp->withPostFilter(sub { +#: my $ans = shift; +#: delete $ans->{ans_message} +#: if $ans->{ans_message} eq "Can't find any solutions to your equation"; +#: return $ans; +#: }); +#: ``` +Context('ImplicitEquation'); +Context()->variables->set( + x => { limits => [ -2, 2 ] }, + y => { limits => [ 0, 4 ] } +); + +$ans = ImplicitEquation('y = (x-1)^2'); + +#:% section = statement +BEGIN_PGML +Give the equation of a shift of the parabola [`y = x^2`] that opens upward and +has its vertex at [`(1, 0)`]. + +Equation: [___]{$ans} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/EquationsDefiningFunctions.pg b/tutorial/sample-problems/ProblemTechniques/EquationsDefiningFunctions.pg similarity index 60% rename from tutorial/sample-problems/problem-techniques/EquationsDefiningFunctions.pg rename to tutorial/sample-problems/ProblemTechniques/EquationsDefiningFunctions.pg index e89ed34913..8f8b31ccc3 100644 --- a/tutorial/sample-problems/problem-techniques/EquationsDefiningFunctions.pg +++ b/tutorial/sample-problems/ProblemTechniques/EquationsDefiningFunctions.pg @@ -18,34 +18,34 @@ #:% name = Equations Defining Functions (Not Implicit) #:% type = technique -#:% categories = [equation] +#:% categories = [equations] #:% section = preamble -#: In the initialization section, we need to include the macros file -#: `parserAssignment.pl`. +#: Include the macro PODLINK('parserAssignment.pl'). DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserAssignment.pl', 'PGcourse.pl'); -#:% section=setup -#: We must allow assignment, and declare any function names we wish to use. -#: For more details and examples in other `MathObjects` contexts, see -#: PODLINK('parserAssignment.pl'). -Context("Numeric")->variables->are(x => "Real", y => "Real"); +#:% section = setup +#: Set the variables in the context to be `x` and `y`, enable assignments in the +#: context, and declare that the function `f` may be used on the left side of an +#: assignment. +#: +#: For more details see PODLINK('parserAssignment.pl'). +Context('Numeric')->variables->are(x => 'Real', y => 'Real'); parser::Assignment->Allow; -parser::Assignment->Function("f"); +parser::Assignment->Function('f'); -$eqn = Formula("y=5x+2"); -$f = Formula("f(x)=5x+2"); +$eqn = Formula('y = 5x + 2'); +$f = Formula('f(x) = 5x + 2'); -#:% section=statement +#:% section = statement BEGIN_PGML -Enter [`y = 5x+2`] [___]{$eqn} - -Enter [`f(x) = 5x+2`] [___]{$f} +Enter [`y = 5x + 2`]: [___]{$eqn} +Enter [`f(x) = 5x + 2`]: [___]{$f} END_PGML -#:% section=solution +#:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION diff --git a/tutorial/sample-problems/problem-techniques/ErrorMessageCustomization.pg b/tutorial/sample-problems/ProblemTechniques/ErrorMessageCustomization.pg similarity index 77% rename from tutorial/sample-problems/problem-techniques/ErrorMessageCustomization.pg rename to tutorial/sample-problems/ProblemTechniques/ErrorMessageCustomization.pg index e57c71a97f..601352ecfe 100644 --- a/tutorial/sample-problems/problem-techniques/ErrorMessageCustomization.pg +++ b/tutorial/sample-problems/ProblemTechniques/ErrorMessageCustomization.pg @@ -23,20 +23,19 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: To update the error message, the string in the `Context()->{error}{msg}` -#: hash must match exactly and then is replaced with the customized -#: version. +#: To change an error message the string in the `Context()->{error}{msg}` hash +#: must match exactly, and then is replaced with the customized message. Context()->{error}{msg}{"Missing operand after '-'"} = "Enter '-1' instead of '-'"; $ans1 = Real(-1); -$ans2 = Formula("x-2"); +$ans2 = Formula('x - 2'); #:% section = statement BEGIN_PGML -Factor [`-1`] from [`-x+2`] +Factor [`-1`] from [`-x + 2`] -[`-x+2 =`] [__]{$ans1} [`\cdot \big(`] [__]{$ans2} [`\big)`] +[`-x + 2 =`] [__]{$ans1} [`\cdot \big(`] [__]{$ans2} [`\big)`] END_PGML #:% section = solution diff --git a/tutorial/sample-problems/ProblemTechniques/EvalVersusSubstitute.pg b/tutorial/sample-problems/ProblemTechniques/EvalVersusSubstitute.pg new file mode 100644 index 0000000000..5ebe7f9098 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/EvalVersusSubstitute.pg @@ -0,0 +1,93 @@ +## DESCRIPTION +## Shows the difference between eval and substitute for MathObject Formulas +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('formula', 'eval', 'substitute') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Eval Versus Substitute +#:% type = technique +#:% categories = [formula] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: First, consider `$f = Compute('sqrt(3x + 1)')` defined in the default context +#: with the default context flags. +#: +#: * The `eval` method returns a number, which is a `Value::Real` +#: * The `substitute` method returns a Formula which is a `Value::Formula` +#: +#: Note that the The Perl command `ref` used in the problem text returns the +#: type of a blessed object. In this case the objects are MathObjects with type +#: `Value::Real` or `Value::Formula`. +#: +#: Next add the variable `y` to the context, and consider the function +#: `$g = Compute('7xy')`. +#: +#: For the function `$g`, `$g->eval(x => 3)` throws an error because when the +#: `eval` method is used a value must be provided for all variables used in the +#: function, and a value for `y` is not provided. +#: +#: The next section shows the effect of changing the context flag +#: `reduceConstants` to 0. Notice that there is no effect on `eval`, the result +#: is the same number as before, however with `substitute` the value 3 is +#: substituted for `x` but left within the formula, which is not reduced. +#: +#: Lastly, to show the effect of `reduceConstantFunctions`, if `reduceConstants` +#: is set to 1 and `reduceConstantFunctions` to 0, then the inside of the square +#: root is reduced (because it is constant), but the square root remains. +$f = Compute('sqrt(3x + 1)'); +$f1 = $f->eval(x => 3); +$f2 = $f->substitute(x => 3); + +Context()->variables->add(y => 'Real'); +$g = Compute('7xy'); +# This next line is an error. +# $g1 = $g->eval(x => 3); +$g2 = $g->substitute(x => 3); +$g3 = $g->eval(x => 3, y => -1); + +Context()->flags->set(reduceConstants => 0); +$f3 = $f->eval(x => 3); +$f4 = $f->substitute(x => 3); + +Context()->flags->set(reduceConstantFunctions => 0, reduceConstants => 1); +$f5 = $f->substitute(x => 3); + +#:% section = statement +BEGIN_PGML +This shows the difference between [|eval|] and [|substitute|]. Consider the +function [|$f|] [`= [$f]`]. Then + +* [|$f->eval(x => 3)|] returns [$f1] and the type is [@ ref $f1 @] +* [|$f->substitute(x => 3)|] returns [$f2] and the type is [@ ref $f2 @] + +Next, consider the function [|$g|] [`= [$g]`]. Then + +* [|$g->eval(x => 3)|] throws an error. +* [|$g->substitute(x => 3)|] returns [$g2] and the type is [@ ref $g2 @] +* [|$g->eval(x => 3, y => -1)|] returns [$g3] and the type is [@ ref $g3 @] + +If [|reduceConstants|] is set to 0 in the flags, then + +* [|$f->eval(x => 3)|] returns [$f3] +* [|$f->substitute(x => 3)|] returns [$f4] + +If [|reducedConstants|] is set back to 1 and [|reduceConstantFunctions|] is +set to 0, then + +* [|$f->substitute(x => 3)|] returns [$f5] +END_PGML + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/ExtractingCoordinatesFromPoint.pg b/tutorial/sample-problems/ProblemTechniques/ExtractingCoordinatesFromPoint.pg new file mode 100644 index 0000000000..84c82d3322 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/ExtractingCoordinatesFromPoint.pg @@ -0,0 +1,85 @@ +## DESCRIPTION +## This problem shows how to extract the coordinates from a point. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('point', 'coordinates') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Extracting Coordinates from a Point +#:% type = technique +#:% categories = [point] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Defined two `Point`s with randomly generated coordinates. +#: +#: To extract a particular coordinate of a `Point`, you can use the extract +#: method. For example, `$point->extract(1)`. This returns the first coordinate +#: of `$point`. This is demonstrated in the problem text. +#: +#: Next, the difference of the two points is computed. Note that this returns +#: another `Point` object whose `x`-coordinate is the difference of the +#: `x`-coordinates of the two points, and whose `y`-coordinate is the difference +#: of the `y`-coordinates of the two points. The coordinates of the "difference" +#: point that is returned are then extracted into the variables, `$d1` and `$d2` +#: by calling the `value` method of the "difference" `Point`. +#: +#: Next, the length of the line segment between the two points is computed from +#: the extracted difference coordinates. Note that the parentheses around `$d1` +#: and `$d2` in this formula are necessary. For if `$d1 = -6` then the string +#: `"$d1^2"` would first interpolate to `'-6^2'`, and the `Compute` call would +#: evaluate that to `-36`. However, the string `"($d1)^2"` would be interpolated +#: to `'(-6)^2'` which of course would be correctly evaluated to `36` by the +#: compute call. +#: +#: If it were desired to allow students to use vector computations in answers, +#: then `Context('Vector')` could be used, and the length could be defined by +#: `$length = norm($point1 - $point2)`. Then an answer like `|<5, 7> - <7, 8>|` +#: would be allowed. +#: +#: Finally, the midpoint of the line segment joining the two points is computed. +#: Note that an answer like `((2, -3) + (-7, 8)) / 2` would be allowed in the +#: `Point` context. The addition operation could be disabled in the context to +#: prevent this type of answer. Although, that would also prevent answers like +#: `((2 + (-7)) / 2, (-3 + 8) / 2)` from being accepted. +Context('Point'); + +$point1 = Point(random(1, 5), random(-5, -1)); +$point2 = Point(random(-10, -6), random(6, 10)); + +($d1, $d2) = ($point1 - $point2)->value; + +$length = Compute("sqrt(($d1)^2 + ($d2)^2)"); +$mid = ($point2 + $point1) / 2; + +BEGIN_PGML +Consider the two points [`[$point1]`] and [`[$point2]`]. + +The [`x`]-coordinate of the first point is [`[$point1->extract(1)]`], and the +[`y`]-coordinate of the first point is [`[$point1->extract(2)]`]. + +The [`x`]-coordinate of the second point is [`[$point2->extract(1)]`], and the +[`y`]-coordinate of the second point is [`[$point2->extract(2)]`]. + +The distance between the two points is: [___]{$length} + +The midpoint of the line segment that joins the two points is: [___]{$mid} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/FactoringAndExpanding.pg b/tutorial/sample-problems/ProblemTechniques/FactoringAndExpanding.pg new file mode 100644 index 0000000000..f5fc783b95 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/FactoringAndExpanding.pg @@ -0,0 +1,106 @@ +## DESCRIPTION +## This shows how to check answers that require students to factor or expand +## a polynomial expression. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('answer', 'custom') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Factoring and Expanding Polynomials +#:% type = technique +#:% categories = [polynomials, factoring, expanding] + +#:% section = preamble +#: The macros PODLINK('contextLimitedPolynomial.pl'), +#: PODLINK('contextPolynomialFactors.pl'), and +#: PODLINK('contextLimitedPowers.pl') are needed for this example. +DOCUMENT(); +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'contextLimitedPolynomial.pl', 'contextPolynomialFactors.pl', + 'contextLimitedPowers.pl', 'PGcourse.pl' +); + +#:% section = setup +#: Randomly generate `$a` and `$b`. The quadratic in this problem will be +#: `(x + $a)(x - $b)`. +#: +#: Then compute the vertex form `(a(x - h)^2 + k)`, expanded form +#: `(ax^2 + bx + c)`, and factored form `(x + $a)(x - $b)` of the quadratic in +#: different contexts. +#: +#: The vertex form is computed in the default `Numeric` context. This form is +#: used for display only and will not be used as an answer. So particular care +#: with specialized contexts is not needed. +#: +#: The expanded form is computed in the `LimitedPolynomial-Strict` context. +#: The coefficients `$p[0]` and `$p[1]` are constructed as Perl reals, and then +#: the `$expandedform` computed using these coefficients. This is because the +#: `LimitedPolynomial-Strict` context does not allow computations in the +#: coefficients. +#: +#: The factored form is computed in the `PolynomialFactors-Strict` context. The +#: context is further restricted to allow only the exponents of 0 or 1 using +#: `LimitedPowers::OnlyIntegers`. Note that restricting all exponents to 0 or 1 +#: means that repeated factors will have to be entered in the form +#: `k(ax + b)(ax + b)` instead of `k(ax + b)^2`. This also means that the +#: polynomial must factor as a product of linear factors (no irreducible +#: quadratic factors can appear). Of course, the exponents of 0, 1, or 2 could +#: be allowed, but then students would be allowed to enter reducible quadratic +#: factors. There are no restrictions on the coefficients, i.e., the quadratic +#: could have any nonzero leading coefficient. The context flags +#: `singleFactors => 0` is set so that repeated, non-simplified factors do not +#: generate errors. +Context('Numeric'); + +$a = random(2, 5); +$b = ($a + 2 * random(2, 5)); + +# Vertex form +$h = ($b - $a) / 2; +$k = $h**2 + $a * $b; +$vertexform = Compute("(x - $h)^2 - $k"); + +# Expanded form +Context('LimitedPolynomial-Strict'); +$p0 = $h**2 - $k; +$p1 = 2 * $h; +$expandedform = Formula("x^2 - $p1 x + $p0")->reduce; + +# Factored form +Context('PolynomialFactors-Strict'); +Context()->flags->set(singleFactors => 0); +LimitedPowers::OnlyIntegers( + minPower => 0, + maxPower => 1, + message => 'either 0 or 1', +); +$factoredform = Compute("(x + $a)(x - $b)"); + +#:% section = statement +BEGIN_PGML +The quadratic expression [`[$vertexform]`] is written in vertex form. + +a. Write the expression in expanded form [`ax^2 + bx + c`]. + + [_]{$expandedform}{15} + +b. Write the expression in factored form [`k(ax + b)(cx + d)`]. + + [_]{$factoredform}{15} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/FormattingDecimals.pg b/tutorial/sample-problems/ProblemTechniques/FormattingDecimals.pg new file mode 100644 index 0000000000..5a6e220263 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/FormattingDecimals.pg @@ -0,0 +1,93 @@ +## DESCRIPTION +## Formatting decimals and using logarithmic functions. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('formatting decimals', 'logarithm') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Formatting Decimals +#:% type = technique +#:% categories = [formatting decimals, logarithm] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Since the domain of a logarithmic function is all positive real numbers, the +#: domain for function evaluation with the variable `x` is set to `[0.1, 4]`. +#: +#: This example used to use Perl's `sprintf(format, number)` command to format +#: the decimal. The format `'%0.3f'` rounds to 3 decimal places and ensures +#: precisely 3 decimal places with 0 padding. However, Perl rounding via +#: `sprintf` is not entirely reliable due to finite precision representation of +#: floating point numbers. For example, `sprintf('%0.2f', 100.255)` gives +#: `100.25` (this is because the binary representation of `100.255` actually +#: rounds to `100.254999999999995` accurate to 15 decimal places). So this +#: example now uses `Round` from PODLINK('PGauxiliaryFunctions.pl') (which is +#: loaded by PODLINK('PGstandard.pl')) for more reliable rounding. The format +#: for calling `Round` is `Round(number, number of decimal places)`. Be aware +#: that if further calculations are performed with a number that has been +#: rounded, numerical error may still be an issue. +#: +#: So in short, do not use `sprintf` for rounding in problems as was previously +#: done in this example. +#: +#: The logarithmic change of base formula +#: `log10(a) = log(a) / log(10) = ln(a) / ln(10)` can be used to compute a +#: logarithm with base 10. The functions `log10` and `logten` are also defined +#: which can be used for this. +#: +#: Note that if `Context()->flags->set(useBaseTenLog => 1)` is called, then the +#: `log` function will be the logarithm with base 10. By default the +#: `useBaseTenLog` flag is 0, and the `log` function is the natural logarithm. +#: +#: If a function for log base 2 (or another base) is needed see +#: PROBLINK('AddingFunctions.pg') for how to define and add a new function +#: to the context so that students can enter it in their answers. +Context()->variables->set(x => { limits => [ 0.1, 4 ] }); + +$a = random(3, 7); + +$ans1 = Real(Round(ln($a), 3)); + +$ans2 = Formula('ln(x)')->eval(x => $a); + +$ans3 = Real(Round(ln($a) / ln(10), 3)); + +$ans4 = Formula('ln(x) / ln(10)')->eval(x => $a); + +#:% section = statement +#: Notice the difference in decimal formatting when "Show Correct Answers" +#: is clicked versus when "Submit Answers" is clicked. +BEGIN_PGML +Notice the formatting and rounding differences between [`[$ans1]`] and +[`[$ans2]`]. + +Try entering [`\ln([$a])`], [`\log([$a])`], [`\ln([$a]) / \ln(10)`], +[`\log([$a]) / \log(10)`], [`\mathrm{logten}([$a])`], or +[`\mathrm{log10}([$a]) `]. + +1. [`\ln([$a]) =`] [_]{$ans1}{10} + +2. [`\ln([$a]) =`] [_]{$ans2}{10} + +3. [`\log_{10}([$a]) =`] [_]{$ans3}{10} + +4. [`\log_{10}([$a]) =`] [_]{$ans4}{10} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/FormulasToConstants.pg b/tutorial/sample-problems/ProblemTechniques/FormulasToConstants.pg similarity index 52% rename from tutorial/sample-problems/problem-techniques/FormulasToConstants.pg rename to tutorial/sample-problems/ProblemTechniques/FormulasToConstants.pg index 7d07ffd92f..69ec646be6 100644 --- a/tutorial/sample-problems/problem-techniques/FormulasToConstants.pg +++ b/tutorial/sample-problems/ProblemTechniques/FormulasToConstants.pg @@ -15,16 +15,17 @@ #:% categories = [constant] #:% section = preamble -#: There are two types of comparison demonstrated here. One is "an -#: antiderivative of f(x)", and the other is "the most general antiderivative of -#: f(x)". The former requires that the student answers F(x), F(x) + 1, -#: F(x) - sqrt(8), etc., all be marked correct, and the latter, that F(x) + C, -#: F(x) + 5 - k, etc., all be marked correct. +#: There are two types of answers demonstrated here. One is "a particular +#: antiderivative of f(x)", and the other is "all antiderivatives of f(x)". The +#: former requires that a student enter an answer like `F(x)`, `F(x) + 1`, or +#: `F(x) - sqrt(8)`, and the latter, that a student enter an answer like +#: `F(x) + C` or `F(x) + 5 - k`. #: -#: To check the most general antiderivative of a function, that is, a formula up +#: To check the all antiderivatives of a function, that is, a formula up #: to an arbitrary additive constant, the `parserFormulaUpToConstant.pl` macro -#: is used. To evaluate an antiderivative of a function, that is, a formula that -#: is unique up to a (specified) additive constant, this macro is not needed. +#: is used. To check a particular antiderivative of a function, that is, a +#: formula that is unique up to a specific additive constant, this macro is +#: not needed. DOCUMENT(); loadMacros( @@ -33,21 +34,23 @@ loadMacros( ); #:% section = setup -#: Define an antiderivative function `$func` and the most general antiderivative -#: function `$gfunc`. For the latter is is not required to include `+ C`. It -#: would be equivalent to specify `$gfunc = FormulaUpToConstant('sin(x)')`. +#: Define a particular antiderivative `$func` and all antiderivatives `$gfunc`. +#: For the latter it is not required to include `+ C`. It would be equivalent to +#: specify `$gfunc = FormulaUpToConstant('sin(x)')`. $func = Formula('sin(x)'); $gfunc = FormulaUpToConstant('sin(x) + C'); #:% section = statement -#: Call the MathObjects `cmp()` method and specify the `upToConstant => 1` flag. -#: This allows the student's answer to differ from the correct answer by any +#: Call the MathObject `cmp` method and specify the `upToConstant => 1` option. +#: This allows the student answer to differ from the correct answer by any #: constant. Both `sin(x)` and `sin(x) + 5` would be marked correct, but #: `sin(x) + C` is not correct since it is a family of answers and not a #: specific antiderivative. Note that for the formula up to an arbitrary #: constant the comparison will correctly mark student's answers that have #: different arbitrary constants. Thus, a student answer to the second question -#: of `sin(x) + k` will be marked correct as will `sin(x) + c`. +#: of `sin(x) + k` will be marked correct as will `sin(x) + c`. Any letter can +#: be used for the constant that is not a variable or predefined constant (like +#: `e`) in the context. BEGIN_PGML An antiderivative of [`\cos(x)`] is [_]{$func->cmp(upToConstant => 1)} diff --git a/tutorial/sample-problems/ProblemTechniques/GraphsInTables.pg b/tutorial/sample-problems/ProblemTechniques/GraphsInTables.pg new file mode 100644 index 0000000000..007338f737 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/GraphsInTables.pg @@ -0,0 +1,140 @@ +## DESCRIPTION +## Creating a set of graphs and displaying the options in a table. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(PGML tutorial 2015) +## Date(06/01/2015) +## Institution(Hope College) +## Author(Paul Pearson) +## MO(1) +## KEYWORDS('parametric', 'graph') + +#:% name = Graphs in a Table +#:% types = [Sample, technique] + +#:% section = preamble +#: The PODLINK('PGtikz.pl') macro is used to generate the graph, +#: the PODLINK('parserPopUp.pl') macro is used for a pop up (select) answer, +#: the PODLINK('niceTables.pl') macro (loaded automatically via `PGML.pl`) is +#: used for layout, and the PODLINK('PGchoicemacros.pl') macro provides the +#: `shuffle` and `invert` functions. +DOCUMENT(); + +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'PGtikz.pl', 'parserPopUp.pl', + 'PGchoicemacros.pl', 'PGcourse.pl' +); + +#:% section = setup +#: First, `@eqn_plot`, `@eqn`, `@alt_text`, and `@graph` arrays are defined +#: containing the form of each equation for the TikZ graph, the TeX form of each +#: equation, alternate text descriptions of the graphs for accessibility, and +#: the TikZ graph for each equation. Because the graphs are shuffled and the +#: correct graph picked randomly, the four arrays must be in the same order. + +#: Then one equation is picked at random (using `$k`) to be the correct choice. +#: The graphs are shuffled using a permutation, and the inverse permutation used +#: to recall the correct answer in the answer evaluation section. +#: +#: If, instead, there were six graphs and it were desired to organize the graphs +#: into three columns the `tex_size` would need to be adjusted. However, you are +#: strongly discouraged from using more than three columns of graphs, because +#: the graphs would need to be scaled down so much that they would become +#: unreadable (especially in TeX mode). +@eqn_plot = ('-exp(\x)', 'exp(-\x)', '-exp(-\x)', 'exp(\x)'); + +# The tex form of the functions. +@eqn = ('y = -e^{x}', 'y = e^{-x}', 'y = -e^{-x}', 'y = e^{x}'); + +# Alternate text for each image. +@alt_text = ( + 'A graph that starts close to and below the x-axis on the left ' + . 'and decreases to the right, decreasing more rapidly as ' + . 'it continues to the right.', + 'A graph that starts in the extreme upper left and decreases toward the ' + . 'x-axis, decreasing less rapidly as it continues to the right.', + 'A graph that starts in the extreme lower left and increases toward the ' + . 'x-axis, increasing less rapidly as it continues to the right.', + 'A graph that starts close to and above the x-axis on the left, ' + . 'and increases to the right, increasing more rapidly as ' + . 'it continues to the right.', +); + +for my $i (0 .. 3) { + $graph[$i] = createTikZImage(); + $graph[$i]->tikzLibraries('arrows.meta'); + $graph[$i]->BEGIN_TIKZ +\tikzset{>={Stealth[scale = 1.5]}} +\filldraw[ + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-3, -3) rectangle (3, 3); +\draw[->] (-3, 0) -- (3, 0) node[above left, outer sep = 3pt] {\(x\)}; +\draw[->] (0, -3) -- (0, 3) node[below right, outer sep = 3pt] {\(y\)}; +\draw[DarkBlue, thick] plot [smooth, domain = -3:3] + (\x, {$eqn_plot[$i]}); +END_TIKZ +} + +$k = random(0, 3); + +@perm = shuffle(4); +@graph = @graph[@perm]; +@alt_text = @alt_text[@perm]; +@inv = invert(@perm); + +@letters = ('A', 'B', 'C', 'D'); +$popup = DropDown(~~@letters, $letters[ $inv[$k] ]); + +#:% section = statement +#: The `LayoutTable` method provided by PODLINK('niceTables.pl') is used via its +#: `PGML` syntax to organize the graphs into columns. See +#: PROBLINK('LayoutTable.pg') for a more detailed example of how to do this. +BEGIN_PGML +Consider the exponential equation [`[$eqn[$k]]`]. Sketch a graph of this +equation on paper without using a calculator. + +Which graph below most closely matches the graph you drew? [_]{$popup} + +[# + [. + [# + [.A.]* + [.[![$alt_text[0]]!]{$graph[0]}.] + #]*{ padding => [ 0, 0.1 ] } + .] + [. + [# + [.B.]* + [.[![$alt_text[1]]!]{$graph[1]}.] + #]*{ padding => [ 0, 0.1 ] } + .]* + + [. + [# + [.C.]* + [.[![$alt_text[2]]!]{$graph[2]}.] + #]*{ padding => [ 0, 0.1 ] } + .] + [. + [# + [.D.]* + [.[![$alt_text[3]]!]{$graph[3]}.] + #]*{ padding => [ 0, 0.1 ] } + .] +#]*{ align => 'cc' } +>>(Click on a graph to enlarge it.)<< +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/HtmlLinks.pg b/tutorial/sample-problems/ProblemTechniques/HtmlLinks.pg new file mode 100644 index 0000000000..4bc88a8336 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/HtmlLinks.pg @@ -0,0 +1,72 @@ +## DESCRIPTION +## This shows how to make an html link in a PG problem. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('link') + +#:% name = HTML Links +#:% type = [snippet, technique] + +#:% section = preamble +#: An example below uses units, so the PODLINK('parserNumberWithUnits.pl') is +#: loaded. +DOCUMENT(); +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'parserNumberWithUnits.pl', 'PGcourse.pl' +); + +#:% section = setup +$ans = NumberWithUnits('4', 'ft'); + +#:% section = statement +#: The `htmlLink` function is used to insert links in the first three examples. +#: The page to load is given is the first argument to `htmlLink`, and the text +#: to display for the link is the second argument. +#: +#: The first example is a link to a general URL. +#: +#: The second example shows how to link to a page that is in the same directory +#: on the WeBWorK server as the PG file. The alias function creates the correct +#: link to this file. Setting the target to be _blank will open a new (blank) +#: window or tab. +#: +#: The third example shows how to link to a page that is under the html +#: subdirectory of a course's main directory. In this example, `plotter.html` is +#: a file that has been placed in the course's html directory. The course's html +#: directory can be linked using `${htmlURL}` as in the example given or by +#: using `alias("${htmlDirectory}plotter.html")`. Note that this will not work +#: unless this problem is used in a course and a `plotter.html` file placed into +#: the course's `html` directory. +#: +#: The fourth example uses the `helpLink` method defined in +#: PODLINK('PGbasicmacros.pl'). The following is a list of all help topics: +#: `angle`, `decimal`, `equation`, `exponent`, `formula`, `fraction`, +#: `inequality`, `limit`, `log`, `matrix`, `number`, `point`, `vector`, +#: `interval`, `unit`, and `syntax`. +BEGIN_PGML +The answer to all questions is on +[@ htmlLink('http://www.google.com/', 'this page') @]*. + +A link to a +[@ htmlLink(alias('local.html'), 'local file', 'TARGET="_blank"') @]*. + +It helps to [@htmlLink( + "${htmlURL}plotter.html", + 'sketch the graph', + 'TARGET="plotter"' +)@]*. + +Enter 4 feet: [__]{$ans} + +Don't forget to enter [@ helpLink('units') @]*. +END_PGML + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/Images.pg b/tutorial/sample-problems/ProblemTechniques/Images.pg new file mode 100644 index 0000000000..2f48576c47 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/Images.pg @@ -0,0 +1,97 @@ +## DESCRIPTION +## Inserting images in PGML. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2023) +## Institution(Fitchburg State University) +## Author(Peter Staab) +## MO(1) +## KEYWORDS('images') + +#:% name = Inserting Images in PGML +#:% type = [technique] + +#:% section = preamble +#: The PODLINK('PGgraphmacros.pl'), PODLINK('PGtikz.pl'), +#: PODLINK('PGlateximage.pl') and PODLINK('parserGraphTool.pl') macros are used +#: to create the images. +DOCUMENT(); + +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'PGgraphmacros.pl', 'PGtikz.pl', + 'PGlateximage.pl', 'parserGraphTool.pl', + 'PGcourse.pl' +); + +#:% section = setup +#: A WWPlot using PODLINK('PGgraphmacros.pl'), a TikZ plot using +#: PODLINK('PGtikz.pl'), LaTeX image using PODLINK('PGlateximage.pl'), and +#: Graphtool using PODLINK('parserGraphTool.pl') are made. +$WWPlot = init_graph(-1, -1, 4, 4); +add_functions($WWPlot, 'x^2 / 4 for x in <-1,4> using color:blue and weight:2'); + +$TikZ = createTikZImage(); +$TikZ->BEGIN_TIKZ +\draw (0,0) circle[radius = 1.5]; +END_TIKZ + +$LaTeXImage = createLaTeXImage(); +$LaTeXImage->texPackages([ [ 'xy', 'all' ] ]); +$LaTeXImage->BEGIN_LATEX_IMAGE +\xymatrix{ A \ar[r] & B \ar[d] \\\\ + D \ar[u] & C \ar[l] } +END_LATEX_IMAGE + +$LaTeXImageAltText = + 'A graph with four nodes labeled A, B, C, and D. ' + . 'There are directed edges from node A to node B, node B to node C, ' + . 'node C to node D, and node D to node A'; + +$gt = GraphTool('{circle, solid, (1, 1), (2, 2)}'); + +#:% section = statement +#: In each of these cases, the PGML syntax for images is used. +#: +#: ``` +#: [!alt text!]{image}{width (optional)}{height (optional)} +#: ``` +#: +#: * The image can be a string (either a local image file or a URL), a +#: WWPlot, TikZ plot, LaTeXImage, or GraphTool plot. The local file should be +#: in the same directory as the problem. +#: +#: * An alternate text for accessibility should always be included. +#: +#: * If the `width` is not included, the width is 100 (in pixels). +#: +#: * If the `height` is not included, the image's natural height relative to the +#: width is used. +#: +#: * The `tex_size` will be computed by `width * 1000 / 600`. +#: +#: Note that the `GraphTool` is not generally intended for displaying static +#: graphs. It is intended to be used to show an interactive graphing tool in +#: which student's are expected to graph requested objects, in this case the +#: circle that has center `(1, 1)` and passes through the point `(2, 2)`. +#: However, static images can be diplayed as is shown in this example. The +#: intent for this usage is to show the correct graph in a solution. +BEGIN_PGML +* A static image: [!Graph of an exponential!]{'image.png'}{120} + +* A static image from an external link (Note: This does not work for hardcopy.) +[!Runestone Logo!]{'https://runestone.academy/runestone/static/images/RAIcon.png'}{120} + +* A WWplot graph [!A simple parabola plot!]{$WWPlot}{120} + +* A TikZ graph [!A circle!]{$TikZ}{120} + +* A LaTeXImage: [![$LaTeXImageAltText]!]{$LaTeXImage}{120} + +* A graphtool plot [!A graphtool plot with a circle!]{$gt}{120} +END_PGML + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/InequalityEvaluators.pg b/tutorial/sample-problems/ProblemTechniques/InequalityEvaluators.pg new file mode 100644 index 0000000000..1d01ef1290 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/InequalityEvaluators.pg @@ -0,0 +1,66 @@ +## DESCRIPTION +## This shows how to use inqualities in a problem. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('interval') + +# created as a full problem by Peter Staab 2023.06.02 + +#:% name = Inequality Evaluator +#:% type = [technique] +#:% categories = interval +#:% subject = algebra +#:% see_also = [IntervalEvaluators.pg] + +#:% section = preamble +#: Load the PODLINK('contextInequalities.pl') macro. +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'contextInequalities.pl', 'PGcourse.pl'); + +#:% section = setup +#: Select the `Inequalities-Only` context. In this context, an inequality such +#: as `-16 <= y <= 9` will be accepted, but the equivalent interval `[-16, 9]` +#: would not be. If the `Inequalities` context were used instead, both the +#: inequality and the interval would be accepted. +#: +#: Uncommenting the lines containing `EmptySet` creates an empty set in the +#: context. Students could then enter `EmptySet` for an answer. +#: +#: Uncommenting `Context()->flags->set(ignoreEndpointTypes => 1)` would also +#: mark the student answers `-16 < y < 9` or `-16 <= y < 9` or `-16 < y <= 9` +#: correct. +Context('Inequalities-Only'); +Context()->variables->add(y => 'Real'); +# Context()->constants->add(EmptySet => Set()); +# Context()->flags->set(noneWord => 'EmptySet'); +# Context()->flags->set(ignoreEndpointTypes => 1); + +# f(x) = x^2 - 16 on -1 <= x <= 5 +$f = Formula('x^2 - 16'); + +$range = Compute('-16 <= y <= 9'); + +Context()->variables->remove('x'); + +#:% section = statement +BEGIN_PGML +What is the range of [`y = f(x) = [$f]`] on the domain [`-1 \leq x \leq 5`]? + +Range: [___]{$range} + +Enter your answer using inequalities (not intervals). +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/IntervalEvaluators.pg b/tutorial/sample-problems/ProblemTechniques/IntervalEvaluators.pg new file mode 100644 index 0000000000..408b8301dc --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/IntervalEvaluators.pg @@ -0,0 +1,60 @@ +## DESCRIPTION +## This shows how to use intervals in a problem. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('interval') + +# created as a full problem by Peter Staab 2023.06.02 + +#:% name = Interval Evaluator +#:% type = [technique] +#:% categories = interval +#:% subject = algebra +#:% see_also = [InequalityEvaluators.pg] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Select the `Interval` context. +#: +#: Once the `Interval` context is selected, intervals can be defined. The +#: constant `inf` (with alias `infinity`) is defined in the context, and can be +#: used for the end points of an interval. For example, +#: +#: ```{#interval .perl} +#: $int2 = Compute('(-inf, 1]'); +#: ``` +#: This would give the interval from negative infinity to 1, including the point +#: at one. +#: +#: The context flag `ignoreEndpointTypes` can be set to 1 to ignore inclusion or +#: exclusion of end points in intervals. However, this does not apply to +#: infinite end points. The interval `(-5, inf]` is still invalid. +Context('Interval'); +# To allow open or closed intervals, uncomment the following line. +#Context()->flags->set(ignoreEndpointTypes => 1); + +$int = Compute('(1, 3)'); + +#:% section = statement +BEGIN_PGML +On what interval is the parabola [`y = (1 - x)(x - 3)`] above the [`x`]-axis? + +For [`x`] in [_____]{$int} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/Knowls.pg b/tutorial/sample-problems/ProblemTechniques/Knowls.pg new file mode 100644 index 0000000000..761edea591 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/Knowls.pg @@ -0,0 +1,56 @@ +## DESCRIPTION +## This shows how to use intervals in a problem. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('interval') + +# created as a full problem by Peter Staab 2023.06.02 + +#:% name = Knowls +#:% type = [technique, snippet] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = statement +#: Knowls can be added to a text section of the problem file. +#: +#: You can specify a value, as in the first example, which gives the text to +#: appear in the knowl. +#: +#: Math can be included in the text of a knowl as shown in the second example. +#: +#: You can instead specify a URL for the knowl, as shown in the third example. +#: Note that the URL must be for a file located on the same server. A URL for +#: another server will result in a cross-origin request which will generally be +#: blocked. Generally, this should only be used in local problems to show +#: custom content, and should not be used for problems in the OPL. +BEGIN_PGML +Here is a knowl +[@ knowlLink( + 'click me', + value => 'This is the inside of a knowl. If you click again, I will go away' +) @]* + +Here is a +[@ knowlLink( + 'math knowl', + value => 'the sine function is \\(\\frac{2}{3}\\)' +) @]* + +Here is another knowl +[@ knowlLink( + 'click me', + url => '/webwork2_files/helpFiles/Entering-Angles.html' +) @]* +END_PGML + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/LayoutTable.pg b/tutorial/sample-problems/ProblemTechniques/LayoutTable.pg new file mode 100644 index 0000000000..995502527e --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/LayoutTable.pg @@ -0,0 +1,131 @@ +## DESCRIPTION +## This shows how to use LayoutTable for layout. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2023) +## Institution(Fitchburg State University) +## Author(Peter Staab) +## MO(1) +## KEYWORDS('tutorial', 'table') + +#:% name = Layout Table +#:% type = [technique] +#:% categories = table +#:% see_also = [DataTables.pg] + +#:% section = preamble +#: This shows how to use the `LayoutTable` function in PODLINK('niceTables.pl'). +#: Note that the PODLINK('niceTables.pl') macro is automatically loaded by +#: `PGML.pl`, so it is not necessary to load it explicitly. +#: +#: The PODLINK('PGtikz.pl') macro is also used in this example to demonstrate +#: inserting a dynamically generated image into a table. +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); + +#:% section = setup +#: A `LayoutTable` is meant to be used for layout, i.e., to organize various +#: HTML elements nicely for display. It is not meant to be used to display +#: organized data. If a table that displays organized data is needed use +#: `DataTable` instead. See PROBLINK('DataTables.pg') for an example of the +#: usage of the `DataTable` method. +#: +#: Typically the `PGML` syntax for a `DataTable` should be used as is done in +#: this example. However, the result of calling `DataTable` could also be used. +#: It is called as follows. +#: +#: ```{#datatable .perl} +#: $table = LayoutTable( +#: [ +#: [row1], +#: [row2], +#: ... +#: [rowN] +#: ], +#: options +#: ); +#: ``` +#: +#: where the data goes in as an array reference of array references. Some of the +#: `DataTable` options are demonstrated here, but the full list of options and +#: explanation of there usage is in +#: PODLINK('the niceTables.pl documentation','niceTables.pl') +#: +#: Then the table would be inserted into the problem text by adding `[$table]*` +#: in the desired location inside the `BEGIN_PGML/END_PGML` block. +#: +#: In this example a table with one row and two columns is created. The primary +#: text of the problem and answer will be in the left column, and the image +#: will be displayed in the right column. In the setup the answer and the image +#: are defined. +$a = random(0, 3); +$ans = Compute("x^2 + $a")->reduce; + +$graph = createTikZImage(); +$graph->tikzLibraries('arrows.meta'); +$graph->BEGIN_TIKZ +\tikzset{>={Stealth[scale = 1.5]}} +\filldraw[ + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-6, 7) rectangle (6, -1); +\draw[->, thick] (-6, 0) -- (6, 0) node[above left, outer sep = 3pt] {\(x\)}; +\foreach \x in {-5, ..., -1, 1, 2, ..., 5} + \draw(\x, 5pt) -- (\x, -5pt) node[below] {\(\x\)}; +\draw[->, thick] (0, -1) -- (0, 7) node[below right, outer sep = 3pt] {\(y\)}; +\foreach \y in {1, ..., 6} + \draw (5pt, \y) -- (-5pt, \y) node[left] {\(\y\)}; +\draw[blue, ultra thick] plot[domain = -2.5:2.5, smooth] (\x, {\x * \x + $a}); +END_TIKZ + +$altText = + "graph of a parabola with vertex at (0, $a) and " + . 'passing through the point (1, ' + . ($a + 1) . ')'; + +#:% section = statement +#: The `PGML` syntax for inserting a `LayoutTable` is `[# ... #]*{ options }`. +#: Note that it is the `*` after `*]` that makes this a `LayoutTable` as opposed +#: to a `DataTable`. In between `[#` and `#]*` any number of cells can be added +#: with `[. ... .]`. The end of a row is marked by adding a `*` to the end of +#: the last cell in the row (the `*` can be omitted on the last row and since +#: this example only has one row, none of these are needed). Inside of a cell +#: started with `[.` and ended with `.]` all of the usual `PGML` syntax can be +#: used. +#: +#: Options can also be passed to a cell with `[. ... .]{ options }`. This +#: example does not use any cell options. +BEGIN_PGML +[# + [. + A common situation is that there is a problem with a graph and + the problem is in the left column and the graph is in the right + column. + + This even works with an equation like [`e^{i\pi} + 1 = 0`]. + + Answer rules can also be added. + + A formula for the function graphed on the right is + [`f(x) =`] [_]{$ans}{10}. + .] + [.[![$altText]!]{$graph}{400}{ image_options => { tex_size => 600 } }.] +#]*{ align => 'lc' } +END_PGML + +#:% section = answer +#: Since `ans_rule` is used to produce answer blanks, this in needed. +ANS($ans->cmp); + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/Multianswer.pg b/tutorial/sample-problems/ProblemTechniques/Multianswer.pg new file mode 100644 index 0000000000..0712a191fa --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/Multianswer.pg @@ -0,0 +1,107 @@ +## DESCRIPTION +## A simple multianswer problem. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('tolerance') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Multianswer Problem +#:% type = [technique, sample] +#:% subject = [algebra, precalculus] +#:% categories = [multianswer] + +#:% section = preamble +#: Load the PODLINK('parserMultianswer.pl') which provides the `MultiAnswer` +#: method. +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); + +#:% section = setup +#: This problem is shown as an example of using PODLINK('parserMultiAnswer.pl'). +#: Another approach for this type of problem is shown in +#: PROBLINK('FactoredPolynomial.pg'). +#: +#: Define a `MultiAnswer` object that takes two answers and check that they are +#: correct (in either order). +#: +#: The `singleResult => 0` option indicates that the different answers in the +#: problem will be evaluated as separate answers, rather than as a single unit. +#: Other useful flags include `allowBlankAnswers`, `checkTypes`, `separator` and +#: `tex_separator`. These are noted below. +#: +#: The `checker => sub { ... }` option defines a subroutine to check the answer. +#: Its inputs are a reference to an array of correct answers, a reference to an +#: array of student answers, and a reference to the `MultiAnswer` object itself. +#: (There is a fourth input, as well. It is the answer hash, but that is not +#: needed in this example.) +#: +#: The checker routine then returns a reference to a list of scores for the +#: answers. In this case there are two answer blanks, so there are two return +#: values. All return values should be a number from `0` to `1`. Of course `1` +#: means the answer is fully correct, `0` means it is completely incorrect, and +#: a number between means it is partially correct. Note that if +#: `singleResult => 1` were set then only one return value needed and must +#: be a number from 0 and 1. +#: +#: It is possible to set an answer message that will be displayed for an answer +#: part. For example, a message could be shown for answers that result from a +#: common mistake that is made in working the problem. For example the following +#: code could be used in the checker. +#: +#: ```{.perl} +#: if ($f1 == $f1stu || $f2 == $f1stu) { +#: $self->setMessage(1, 'This is correct.'); +#: $self->setMessage(2, 'Check your answer by using FOIL.'); +#: return [ 1, 0 ]; +#: } elsif ($f1 == $f1stu || $f2 == $f2stu) { +#: $self->setMessage(1, 'Check your answer by using FOIL.'); +#: $self->setMessage(2, 'This is correct.'); +#: return [ 0, 1 ]; +#: } else { +#: return [0, 0]; +#: } +#: ``` +$fac1 = Formula("(1 - x)"); +$fac2 = Formula("(1 + x)"); + +$multians = MultiAnswer($fac1, $fac2)->with( + singleResult => 0, + checker => sub { + my ($correct, $student, $self) = @_; + my ($f1stu, $f2stu) = @$student; + my ($f1, $f2) = @$correct; + if (($f1 == $f1stu && $f2 == $f2stu) + || ($f1 == $f2stu && $f2 == $f1stu)) + { + return [ 1, 1 ]; + } else { + if ($f1 == $f1stu || $f2 == $f1stu) { + return [ 1, 0 ]; + } elsif ($f1 == $f2stu || $f2 == $f2stu) { + return [ 0, 1 ]; + } else { + return [ 0, 0 ]; + } + } + } +); + +BEGIN_PGML +Factor: +[`1-x^2 = \big(`] [___]{$multians} [`\big)\big(`] [___]{$multians} [`\big)`] +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/NumericalTolerance.pg b/tutorial/sample-problems/ProblemTechniques/NumericalTolerance.pg new file mode 100644 index 0000000000..d0ecf10a8d --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/NumericalTolerance.pg @@ -0,0 +1,89 @@ +## DESCRIPTION +## Explains the difference in tolerance type and numerical tolerance. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('tolerance') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Numerical Tolerance +#:% type = technique +#:% categories = [numbers, tolerance] +#:% see_also = [DigitsTolType.pg] + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: This shows three different ways of setting the `toltype` and `tolerance` of +#: the answer. The `tolType` can be `absolute` (specifying a decimal distance +#: from the correct answer that will be allowed) or `relative` (specifying a +#: percent error that will be allowed). +#: +#: Thus if the correct answer is 17, a tolerance of 0.01 will mean that the +#: student answer must be in the interval `(16.99,17.01)` if the `tolType` is +#: `absolute`, and in the interval `(16.83,17.17)` if `tolType` is `relative` +#: (which is the default). +#: +#: 1. The `cmp` method is called directly on the MathObject returned by the +#: `Compute` call, and the `tolerance` and `tolType` are passed as options to +#: the `cmp` call. Note that this answer is in the current context which in +#: this case would be the default `Numeric` context since no other context +#: has been specified. +#: +#: 2. The `cmp` call is called on the MathObject returned by the `Compute` call, +#: expect that is done when the answer is specified in the `PGML` problem +#: statement. Note that this answer is also in the current default `Numeric` +#: context. Also note that this is really the same as the first case. It is +#: just a different time that the `cmp` call is made. +#: +#: 3. The `tolerance` and `toltype` context flags are set. This is useful if +#: the desired `toltype` or `tolerance` are the same for many answers. +#: Typically this would be done at the beginning of the setup section. +#: However, note that calling `Context('Numeric')` as is done in this example +#: creates a new copy of the unmodified default `Numeric` context (not a +#: copy of any `Numeric` contexts previously created and used in the +#: problem). So the context flags specified here do not apply to any answers +#: created before this. Hence it is also useful to do this later in the setup +#: for unrelated answers. +$ans1 = Compute('1.5708')->cmp( + tolType => 'absolute', + tolerance => .0001, +); + +$ans2 = Compute('1.5708'); + +Context('Numeric')->flags->set( + tolerance => 0.0001, + tolType => 'absolute', +); + +$ans3 = Compute('1.5708'); +#:% section = statement +BEGIN_PGML +For each of the following Enter your answer accurate to four decimal places . + +1. Enter [``\frac{\pi}{2} =``] [____]{$ans1} + +2. Enter [``\frac{\pi}{2} =``] [____]{$ans2->cmp( + tolType => 'absolute', + tolerance => .0001, +)} + +3. Enter [``\frac{\pi}{2} =``] [____]{$ans3} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/ProblemTechniques/OtherVariables.pg b/tutorial/sample-problems/ProblemTechniques/OtherVariables.pg new file mode 100644 index 0000000000..2aa28539f2 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/OtherVariables.pg @@ -0,0 +1,60 @@ +## DESCRIPTION +## Add variables to the context. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(Problem Techniques) +## Date(06/01/2008) +## Institution(University of Michigan) +## Author(Gavin LaRose) +## MO(1) +## KEYWORDS('variables') + +# updated to full problem by Peter Staab (06/01/2023) + +#:% name = Adding Variables to the Context +#:% type = technique +#:% categories = [variables] + +#:% section = preamble + +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = setup +#: Typically the variables for the answers in the problem are set at the +#: beginning of the setup section. In this case, the variables `y`, `z` and `t` +#: are added as `Real` numbers. +#: +#: The variable `rho` is also added as a `Real` number. Its TeX representation +#: is specified to be `\rho` which means that if an answer that uses the +#: variable is displayed in math mode, then the variable will appear as the +#: Greek letter rho. +#: +#: If the variables were set with `Context()->variables->are` instead, then all +#: other variables in the context would be deleted, and if a removed variable +#: were used in a student answer the message "Variable '?' is not defined in +#: this context" would be shown. +Context()->variables->add(t => 'Real', y => 'Real', z => 'Real'); +$f = Compute('-16 t^2 + 5 t + 4'); +$g = Compute('x^2 + y^2 + z^2'); + +Context()->variables->add(rho => [ 'Real', TeX => '\rho' ]); +$h = Compute("sqrt(1 + rho^2)"); + +#:% section = statement +BEGIN_PGML +Enter the following formulas: + +* [`[$f] =`] [____]{$f} +* [`[$g] =`] [____]{$g} +* [`[$h] =`] [____]{$h} +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/Percent.pg b/tutorial/sample-problems/ProblemTechniques/Percent.pg similarity index 57% rename from tutorial/sample-problems/problem-techniques/Percent.pg rename to tutorial/sample-problems/ProblemTechniques/Percent.pg index 5bbbc5d6b0..c02499b445 100644 --- a/tutorial/sample-problems/problem-techniques/Percent.pg +++ b/tutorial/sample-problems/ProblemTechniques/Percent.pg @@ -15,20 +15,24 @@ #:% type = [technique] #:% section = preamble -#: The `contextPercent.pl` must be loaded. +#: The PODLINK('contextPercent.pl') macro must be loaded. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'contextPercent.pl', 'PGcourse.pl'); #:% section = setup -#: The `Percent` context must be loaded. +#: Select the `Percent` context, generate a random percentage, and `Compute` the +#: percent MathObject for the answer. Note that the `%` symbol or the word +#: `percent` must be included in the `Compute` call for the answer to be +#: interpreted as a percent. Context('Percent'); -$p = random(5, 95, 5); +$p = random(5, 95, 5); +$ans = Compute("$p %"); #:% section = statement -#: The answer can be entered with a `%` or with the work `percent`. +#: The answer can be entered with a `%` symbol or with the word `percent`. BEGIN_PGML -Enter [$p]% [__]{Real($p)} +Enter [`[$ans]`]: [__]{$ans} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/problem-techniques/RandomFunction.pg b/tutorial/sample-problems/ProblemTechniques/RandomFunction.pg similarity index 59% rename from tutorial/sample-problems/problem-techniques/RandomFunction.pg rename to tutorial/sample-problems/ProblemTechniques/RandomFunction.pg index 1c0b36f547..beda462f3d 100644 --- a/tutorial/sample-problems/problem-techniques/RandomFunction.pg +++ b/tutorial/sample-problems/ProblemTechniques/RandomFunction.pg @@ -19,17 +19,19 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: First, there are some random numbers generated as well as an array of -#: functions using those values. The statement -#: `random(0,$#funs)` generates a random number between 0 and (in this case 4, -#: but in general 1 less than the length of the array) -#: and then that element of the array is selected. -# Define some random values and functions +#: First, generate the random numbers needed as well as an array of functions +#: that depend on the random numbers generated. +#: +#: The statement `$functions[ random(0, $#functions) ]` generates a random +#: number between 0 and the index of the last element of the @functions array +#: (in this case 4), and selects that element of the @functions array. The +#: selected element is then converted to a MathObject `Formula` which will be +#: the answer for this problem. $a = non_zero_random(-8, 8); $b = random(1, 8); $n = random(2, 4); -@funs = ( +@functions = ( "1 + $a*x + $b x^2", "$a / (1 + $b x)", "$a x^3 + $b", @@ -38,11 +40,11 @@ $n = random(2, 4); ); # This select one of the functions at random. -$f = Formula($funs[ random(0, $#funs) ])->reduce; +$f = Formula($functions[ random(0, $#functions) ])->reduce; #:% section = statement BEGIN_PGML -Enter [``[$f]``] [____]{$f} +Enter [``[$f]``]: [____]{$f} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/ProblemTechniques/RestrictAnswerToFraction.pg b/tutorial/sample-problems/ProblemTechniques/RestrictAnswerToFraction.pg new file mode 100644 index 0000000000..e7d893a79b --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/RestrictAnswerToFraction.pg @@ -0,0 +1,83 @@ +## DESCRIPTION +## Restricting answers that should reduce to a fraction. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(PGML tutorial 2015) +## Date(06/01/2015) +## Institution(Fitchburg State University) +## Author(Peter Staab) +## MO(1) +## KEYWORDS('answer', 'fraction') + +#:% name = Restrict Answers to a Fraction +#:% type = [technique, sample] +#:% subject = [answer] +#:% see_also = [RestrictingFunctions.pg] + +#:% section = preamble +#: Load the PODLINK('contextFraction.pl') for the fraction contexts that it +#: provides. +DOCUMENT(); + +loadMacros('PGstandard.pl', 'PGML.pl', 'contextFraction.pl', 'PGcourse.pl'); + +#:% section = setup +#: The `Fractions-NoDecimals` context is selected which requires that answers +#: be fractions and not decimals. +#: +#: To ensure that students simplify answers, the operators other than division +#: are undefined. Note that since these operators have been undefined for all +#: MathObjects, the answer can not be defined as +#: `$frac = Compute("$b / ($c + $a^2)")`. The operators `+` and `^` are +#: undefined, so they are not available for the problem author either. So +#: do the calculation of the denominator using Perl first, and then use the +#: MathObject to create the answer. +#: +#: Also note that by default a Fraction will be reduced to lowest terms. This +#: can be changed by setting the context flag `reduceFractions => 0`. +#: +#: The option `studentsMustReduceFractions => 1` that is passed to the `cmp` +#: method means that fractions entered by students must be reduced in order to +#: be accepted. +#: +#: The option `strictFractions => 1` that is passed to the `cmp` method means +#: that division is only allowed between integers. Note that the options +#: `strictMinus => 1` and `strictMultiplication => 1` are also needed to make +#: the `strictFractions` option really work. +#: +#: See PODLINK('contextFraction.pl') for more details. +Context("Fraction-NoDecimals"); +Context()->operators->undefine('+', '-', '*', '*', '**', '^'); + +$a = random(2, 4); +$b = random(1, 9); +$c = random(1, 9); +$den = $c + $a * $a; +$frac = Compute("$b / $den"); + +$ans = $frac->cmp( + studentsMustReduceFractions => 1, + strictFractions => 1, + strictMinus => 1, + strictMultiplication => 1 +); + +#:% section = statement +BEGIN_PGML +Find and simplify the value of [`f([$a])`] if +[`` f(x) = \frac{[$b]}{[$c] + x^2}. ``] + +[`f([$a]) = `] [__]{$ans} + +_(Simplify your answer as much as possible, and enter a fraction instead of a +decimal.)_ +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/RestrictingFunctions.pg b/tutorial/sample-problems/ProblemTechniques/RestrictingFunctions.pg similarity index 67% rename from tutorial/sample-problems/problem-techniques/RestrictingFunctions.pg rename to tutorial/sample-problems/ProblemTechniques/RestrictingFunctions.pg index 72758d57fb..cab2cbf11f 100644 --- a/tutorial/sample-problems/problem-techniques/RestrictingFunctions.pg +++ b/tutorial/sample-problems/ProblemTechniques/RestrictingFunctions.pg @@ -22,11 +22,12 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: Here we've turned off type warnings in the answer checking, so that a -#: student entering an un-simplified answer (e.g., -#: `2 sin(x) cos(x) + 2 cos(x) (-sin(x))`) will have it marked wrong -#: (but not get feedback that says "you should have entered a number"). -$expr = Formula("sin(x)^2 + cos(x)^2"); +#: Pass the `showTypeWarnings => 0` option to the `cmp` method to turn off type +#: warnings in answer checking. Then if a student enters an unsimplified answer +#: (e.g., `2 sin(x) cos(x) + 2 cos(x) (-sin(x))`), it will be marked incorrect +#: but there will not be the feedback message that says "Your answer isn't a +#: number (it looks like a formula that returns a number)". +$expr = Formula('sin(x)^2 + cos(x)^2'); $deriv = Compute(0)->cmp(showTypeWarnings => 0); #:% section = statement diff --git a/tutorial/sample-problems/problem-techniques/SimplePopUp.pg b/tutorial/sample-problems/ProblemTechniques/SimplePopUp.pg similarity index 71% rename from tutorial/sample-problems/problem-techniques/SimplePopUp.pg rename to tutorial/sample-problems/ProblemTechniques/SimplePopUp.pg index af098659ea..43ee21d347 100644 --- a/tutorial/sample-problems/problem-techniques/SimplePopUp.pg +++ b/tutorial/sample-problems/ProblemTechniques/SimplePopUp.pg @@ -15,7 +15,7 @@ #:% type = technique #:% section = preamble -#: We need to load `parserPopUp.pl` for this feature. +#: Load PODLINK('parserPopUp.pl') for pop up (or drop down) menu answers. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserPopUp.pl', 'PGcourse.pl'); @@ -23,17 +23,17 @@ loadMacros('PGstandard.pl', 'PGML.pl', 'parserPopUp.pl', 'PGcourse.pl'); #:% section = setup #: This shows a number of ways to use either `PopUp` (the legacy version) or #: `DropDown` (a more flexible version). Both create an HTML select object. -#: The `PopUp` takes a array reference of option and the correct answer and +#: The `PopUp` takes a array reference of options and the correct answer and #: creates the options. Notice in `Popup` the first element is shown, but #: selectable, whereas in `DropDown`, the first either defaults to `?` or -#: whatever is in the `placeholder` option. In `Dropdown`, the first element -#: is not selectable. +#: whatever is set in the `placeholder` option. In `Dropdown`, the first +#: element is not selectable. #: #: Similar to other `parser` objects, inserting another array reference, #: randomizes those options. #: -#: Lastly, the `DropDownTF` creates a true/false dropdown for simplicity. -$popup = PopUp([ "?", "one", "two", "three" ], "three"); +#: Lastly, the `DropDownTF` creates a true/false dropdown. +$popup = PopUp([ '?', 'one', 'two', 'three' ], 'three'); $dropdown1 = DropDown([ 'one', 'two', 'three' ], 'two'); $dropdown2 = DropDown([ 'one', 'two', 'three' ], @@ -45,13 +45,15 @@ $tf = DropDownTF('T'); #:% section = statement BEGIN_PGML +- [_]{$popup} (Answer: 'three') -- [_]{$popup} (ans: 'three') -- [_]{$dropdown1} (ans: 'two') -- [_]{$dropdown2} (ans: 'one') -- [_]{$dropdown3} (ans: 'six') -- [_]{$tf} (ans: 'True') +- [_]{$dropdown1} (Answer: 'two') +- [_]{$dropdown2} (Answer: 'one') + +- [_]{$dropdown3} (Answer: 'six') + +- [_]{$tf} (Answer: 'True') END_PGML #:% section = solution diff --git a/tutorial/sample-problems/ProblemTechniques/StaticImages.pg b/tutorial/sample-problems/ProblemTechniques/StaticImages.pg new file mode 100644 index 0000000000..fc219df6d1 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/StaticImages.pg @@ -0,0 +1,55 @@ +## DESCRIPTION +## Show a static image. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(PGML tutorial 2015) +## Date(06/01/2015) +## Institution(Hope College) +## Author(Paul Pearson) +## MO(1) +## KEYWORDS('parametric', 'graph') + +#:% name = Graphic Images, Static +#:% types = technique + +#:% section = preamble +DOCUMENT(); +loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); + +#:% section = statement +#: Use the `PGML` image syntax to include static images. For accessibility you +#: should always include an alternate text describing the image in detail. +#: +#: For each PG problem with static images, both the PG file and the image files +#: should be place into a separate subdirectory. This subdirectory should be +#: located somewhere under the course templates directory and have the same root +#: name as the PG file. For example, if you have a PG file called +#: `Contour-plots.pg` which uses static graphic files `Contour-plot-01.png`and +#: `Contour-plot-02.png`, you should create a subdirectory somewhere under the +#: course templates directory called `Contour-plots` and put the PG file and all +#: the PNG files in it. Putting a PG file and all of its graphics files into +#: their own separate subdirectory like this makes it easier to find the +#: graphics files that go with each PG file, thereby making the problem easier +#: to maintain. Another reason for having the subdirectory and the root name of +#: the PG file be the same is that when the library is browsed via directories, +#: the library browser in WeBWorK is configured to recognize that when a +#: subdirectory has the same name as the root name of the only PG file in that +#: subdirectory, the subdirectory and PG file should be treated as a single +#: entity. +#: +#: Image options such as the `tex_size` can be set as shown. If the `tex_size` +#: option is set, then dividing by 10 gives the percentage of the +#: available line width used by the graphic. So for `tex_size => 600` as shown +#: in this example, the image will occupy 60 percent of the line width. Usually +#: the available space is constrained by the width of one column of a two-column +#: printed page. + +BEGIN_PGML +[!graph of a decreasing exponential function!]{'image.png'}{400}{ + image_options => { tex_size => 600 } +} +END_PGML + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/StringsInContext.pg b/tutorial/sample-problems/ProblemTechniques/StringsInContext.pg similarity index 51% rename from tutorial/sample-problems/problem-techniques/StringsInContext.pg rename to tutorial/sample-problems/ProblemTechniques/StringsInContext.pg index 3248dea7b6..09a49e25f8 100644 --- a/tutorial/sample-problems/problem-techniques/StringsInContext.pg +++ b/tutorial/sample-problems/ProblemTechniques/StringsInContext.pg @@ -24,33 +24,42 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: Add the strings that are to be allowed in answers to the Context. Note that the -#: add call has the form `string => { options }`. The most common use of options is to allow -#: "aliases", i.e., strings that are marked the same as others, e.g., -#:```{.perl} -#: Context()->strings->add(none => {}, N => { alias => "none" }); -#:``` -#: (Which would allow "N" to be used instead of "none".) +#: Note that this example is only here to demonstrate how to add strings to a +#: context. However, using strings for this type of problem is not recommended. +#: See PROBLINK('NoSolution.pg') for a better approach for dealing with this +#: sort of problem. #: -#: By default, strings are case-insensitive. To make them case sensitive, include this -#: as an option in the Context call. +#: Add the strings that are to be allowed in answers to the context. #: -#: There are some shortcuts available if you need to add many allowable strings all at once. -#: See PODLINK('parserAutoStrings.pl'). +#: Note that the add call has the form `string => { options }`. The most common +#: use of options is to allow "aliases", i.e., strings that are marked the same +#: as others, e.g., +#: +#: ```{.perl} +#: Context()->strings->add(none => {}, N => { alias => 'none' }); +#: ``` +#: +#: which would allow 'N' to be used instead of 'none'. +#: +#: By default, strings are case-insensitive. To make them case sensitive, +#: include the option `caseSensitive => 1`. +#: +#: There are some shortcuts available if you need to add many allowable strings +#: at once. See PODLINK('parserAutoStrings.pl'). Context()->strings->add(none => {}); -# or, if we wanted a case-sensitive string, -# we would instead use -# Context()->strings->add(none=>{caseSensitive=>1}); +# If a case-sensitive string is desired, the following could be used instead. +# Context()->strings->add(none => { caseSensitive => 1 }); #:% section = statement -#: It's usually a good idea to include some indication of what strings are expected or allowed in the answer. +#: It's usually a good idea to include some indication of what strings are +#: expected or allowed in the answer. BEGIN_PGML Enter the positive real value of [`x`] for which [`x^2 = -2`] : [`x = `] [___]{'none'} -_(Enter **none** if there are no values that satisfy the equation .) _ +_(Enter *none* if there are no values that satisfy the equation .)_ END_PGML #:% section = solution diff --git a/tutorial/sample-problems/ProblemTechniques/TikZImages.pg b/tutorial/sample-problems/ProblemTechniques/TikZImages.pg new file mode 100644 index 0000000000..4c213fe162 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/TikZImages.pg @@ -0,0 +1,102 @@ +## DESCRIPTION +## Create a graph using tikz. +## ENDDESCRIPTION + +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(PGML tutorial 2015) +## Date(06/01/2015) +## Institution(Hope College) +## Author(Paul Pearson) +## MO(1) +## KEYWORDS('graph', 'tikz') + +#:% name = Graphic Images, TikZ +#:% types = [Sample, technique] +#:% subject = parametric + +#:% section = preamble +#: PODLINK('PGtikz.pl') is used to generate the graph, +DOCUMENT(); + +loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); + +#:% section = setup +#: The `createTikZImage` function creates an image to be built using TikZ. +#: +#: An `svg` image will be generated by default which will generally work better +#: than a `png` image due to being scalable. In rare cases the `svg` creation +#: methods do not give the correct output, and so in those cases a `png` image +#: may be generated instead by adding `$graph_image->ext('png')`. +#: +#: The `$graph_image->tikzLibraries("arrows.meta")` will load the `arrows.meta` +#: Tikz library. +#: +#: The variables `$a` and `$b` are defined for use in the TikZ code that +#: follows. +#: +#: The actual TikZ image is built between `$graph_image->BEGIN_TIKZ` and +#: `END_TIKZ` +#: +#: The command `\tikzset{>={Stealth[scale = 1.5]}}` scales the arrows by a +#: factor of 1.5. +#: +#: The `\filldraw` command creates a nice background for the graph that provides +#: contrast with the problem background color. +#: +#: The `\draw` commands that follow draw the `x` and `y` axis and labels. +#: +#: The `\foreach` loops `\draw` ticks and tick labels on the axes. +#: +#: Finally, the function is plotted with the `\draw plot` command. +$graph_image = createTikZImage(); +$graph_image->tikzLibraries("arrows.meta"); + +# Randomization +$a = non_zero_random(-6, 6); # horizonatal translation +$b = random(-4, 4); # vertical translation + +$graph_image->BEGIN_TIKZ +\tikzset{>={Stealth[scale = 1.5]}} +\filldraw[ + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-11,-11) rectangle (11,11); +\huge +\draw[<->, thick] (-11, 0) -- (11, 0) node[above left,outer sep = 4pt]{\(x\)}; +\draw[<->, thick] (0, -11) -- (0, 11) node[below right, outer sep = 4pt]{\(y\)}; +\foreach \x in {-10, -8, ..., -2, 2, 4, ..., 10} + \draw[thin] (\x, 5pt) -- (\x, -5pt) node[below]{\(\x\)}; +\foreach \y in {-10, -8, ..., -2, 2, 4, ..., 10} + \draw[thin] (5pt, \y) -- (-5pt, \y) node[left]{\(\y\)}; +\draw[<->, Blue, thick] + plot[domain = -11:11, samples=50, smooth] (\x, {(\x - $a)^2 + $b}); +END_TIKZ + +#:% section = statement +#: Insert the TikZ image using the `PGML` image syntax. +#: +#: * The width in pixels for display in HTML is set in the first option argument +#: (in this case 400). +#: +#: * The `tex_size` option determines the size of the image for hard copy and +#: can be set using the `image_options`. The `tex_size` option is the scale +#: factor for hardcopy where 1000 is the full width of either the page or the +#: column. This image will be 60% of the page width. +#: +#: If the problem times out then often there may be a problem with the TikZ +#: code. Troubleshooting is often needed by running the same code in +#: a latex file and compiling it. +BEGIN_PGML +>>[!TODO!]{$graph_image}{400}{ image_options => { tex_size => 600 } }<< +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Solution explanation goes here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/WeightedGrader.pg b/tutorial/sample-problems/ProblemTechniques/WeightedGrader.pg similarity index 55% rename from tutorial/sample-problems/problem-techniques/WeightedGrader.pg rename to tutorial/sample-problems/ProblemTechniques/WeightedGrader.pg index e57fa63def..817f5288cf 100644 --- a/tutorial/sample-problems/problem-techniques/WeightedGrader.pg +++ b/tutorial/sample-problems/ProblemTechniques/WeightedGrader.pg @@ -11,33 +11,38 @@ ## MO(1) ## KEYWORDS('weighted grader') -# created as a full problem by Peter Staab 2023.06.02 +# created as a full problem by Peter Staab 2023.06.02 #:% name = Weighted Grader #:% type = [technique] #:% categories = [grader] #:% section = preamble +#: Load the PODLINK('weightedGrader.pl') macro. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'weightedGrader.pl', 'PGcourse.pl'); -#:% section=setup -#: Call `install_weighted_grader();` so that the weighted grader is used. +#:% section = setup +#: Call `install_weighted_grader` so that the weighted grader is used. install_weighted_grader(); -#:% section=statement -#: Assign weights to answers by passing the `weight` via `cmp_options`. The +#:% section = statement +#: Assign weights to answers by passing the `weight` via `cmp_options`. The #: example here gives weights as percents that sum to 100, but weights of #: (2, 5, 3), (4, 10, 6), or (0.2, 0.5, 0.3) would give the same weighting. +#: +#: Note that if an answer is created as a MathObject in the problem setup, then +#: the weight can also be assigned by passing the `weight` option to the `cmp` +#: method directly as in `$ans->cmp(weight => 20)`. BEGIN_PGML -* This answer is worth 20%. Enter 1 [___]{1}{ cmp_options => { weight => 20 } } +* This answer is worth 20%. Enter 1: [___]{1}{ cmp_options => { weight => 20 } } -* This answer is worth 50%. Enter 3 [___]{3}{ cmp_options => { weight => 50 } } +* This answer is worth 50%. Enter 3: [___]{3}{ cmp_options => { weight => 50 } } -* This answer is worth 30%. Enter 7 [___]{7}{ cmp_options => { weight => 30 } } +* This answer is worth 30%. Enter 7: [___]{7}{ cmp_options => { weight => 30 } } END_PGML -#:% section=solution +#:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION diff --git a/tutorial/sample-problems/problem-techniques/image.png b/tutorial/sample-problems/ProblemTechniques/image.png similarity index 100% rename from tutorial/sample-problems/problem-techniques/image.png rename to tutorial/sample-problems/ProblemTechniques/image.png diff --git a/tutorial/sample-problems/ProblemTechniques/local.html b/tutorial/sample-problems/ProblemTechniques/local.html new file mode 100644 index 0000000000..dcbfe3e039 --- /dev/null +++ b/tutorial/sample-problems/ProblemTechniques/local.html @@ -0,0 +1,5 @@ + +

Local File

+

This is an example of a local html file.

+ + diff --git a/tutorial/sample-problems/README.md b/tutorial/sample-problems/README.md index 558b45025b..64deacc459 100644 --- a/tutorial/sample-problems/README.md +++ b/tutorial/sample-problems/README.md @@ -81,7 +81,7 @@ directory of pg. There are the following options (and many are required): - `out_dir` or `o`: The directory where the resulting documentation files (HTML) will be located. - `pod_root` or `p`: The URL where the POD is located. This is needed to -correctly link POD documentation from the sample problems. +correctly link POD from the sample problems. - `pg_doc_home` or `h`: The URL of the directory for `out_dir`. This is needed for correct linking. - `verbose` or `v`: verbose mode. diff --git a/tutorial/sample-problems/Sequences/AnswerOrderedList.pg b/tutorial/sample-problems/Sequences/AnswerOrderedList.pg index 8ea33bf3f5..2e4bfe69a2 100644 --- a/tutorial/sample-problems/Sequences/AnswerOrderedList.pg +++ b/tutorial/sample-problems/Sequences/AnswerOrderedList.pg @@ -14,7 +14,7 @@ #:% name = Ordered List #:% type = Sample #:% subject = Sequences and Series -#:% categories = [sequences, answer] +#:% categories = [sequences, answers] #:% section = preamble DOCUMENT(); @@ -22,20 +22,16 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: We create the array `@seq` with the first two entries. The rest is filled -#: with a `for` loop. Since the entries in the array `@seq` do not have commas between -#: them, we create a Perl string `$answer` that joins the entries of the array `@seq` by -#: a comma followed by a space ', '. Then, we make this string a MathObject by -#: putting `Compute()` around it. -#: -#: Since the answer is a MathObject `List`, which is by default unordered, we must -#: specify that the answer checker use `ordered=>1`. +#: Create an array `@seq` with the first two elements of the sequence that will +#: be the answer. The next 5 elements are added in a `for` loop. Then construct +#: a MathObject `List` by calling `List` on that array. Specify that this +#: `List` is ordered by passing the option `ordered => 1` to the `cmp` method. @seq = (1, 1); -for $i (2 .. 6) { - $seq[$i] = $seq[ $i - 1 ] + $seq[ $i - 2 ]; +for (2 .. 6) { + $seq[$_] = $seq[ $_ - 1 ] + $seq[ $_ - 2 ]; } -$answer_cmp = Compute(join(', ', @seq))->cmp(ordered => 1); +$answer_cmp = List(@seq)->cmp(ordered => 1); #:% section = statement BEGIN_PGML diff --git a/tutorial/sample-problems/Sequences/ExplicitSequence.pg b/tutorial/sample-problems/Sequences/ExplicitSequence.pg index 0c880da25f..2d18161cef 100644 --- a/tutorial/sample-problems/Sequences/ExplicitSequence.pg +++ b/tutorial/sample-problems/Sequences/ExplicitSequence.pg @@ -23,33 +23,21 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: We set the test points to be positive integers to avoid errors when evaluating -#: the answer. Even if you expect students to enter answers such as `cos(pi * n) / n!`, -#: you should still restrict the domain to positive integers, because some students -#: may simplify this to `(-1)^n / n!` and receive errors because the answer checker -#: is substituting things such as n=0.5 into their formula. +#: The answer is a formula involving factorials that are only defined for +#: positive integers. So set the test points to be positive integers to avoid +#: errors when evaluating the answer. #: -#: For more explanation on the `test_points` see PROBLINK('FormulaTestPoints.pg') +#: For more explanation on `test_points` see PROBLINK('FormulaTestPoints.pg') Context()->variables->are(n => 'Real'); $answer = Compute('(-1)^n / n!'); $answer->{test_points} = [ [1], [2], [3], [4], [5], [6] ]; -@seq = ( - "a_0 = 1", - "a_1 = -1", - "a_2 = \frac{1}{2}", - "a_3 = -\frac{1}{6}", - "a_4 = \frac{1}{24}", - "a_5 = -\frac{1}{120}", - "\ldots" -); - -$sequence = join(', ', @seq); - #:% section = statement BEGIN_PGML -Find a formula for [`n^{th}`] term of the sequence [`[$sequence]`]. +Find a formula for [`n^{th}`] term of the sequence [`a_0 = 1`], [`a_1 = -1`]. +[`a_2 = \frac{1}{2}`], [`a_3 = -\frac{1}{6}`], [`a_4 = \frac{1}{24}`], +[`a_5 = -\frac{1}{120}`], [`\ldots`]. [`a_n =`] [_]{$answer}{20} END_PGML diff --git a/tutorial/sample-problems/Sequences/RecursiveSequence.pg b/tutorial/sample-problems/Sequences/RecursiveSequence.pg index 33c5612c04..c967ab954e 100644 --- a/tutorial/sample-problems/Sequences/RecursiveSequence.pg +++ b/tutorial/sample-problems/Sequences/RecursiveSequence.pg @@ -18,35 +18,33 @@ #:% categories = [sequences] #:% section = preamble -#: We will be defining a new named function and adding it to the context, and the -#: easiest way to do this is using parserFunction.pl. There is a more basic way to -#: add functions to the context, which is explained in example 2 at AddingFunctions +#: A new named function will be defined and added to the context. This can be +#: done using PODLINK('parserFunction.pl'). DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserFunction.pl', 'PGcourse.pl'); #:% section = setup -#: We define a new named function `f` as something the student is unlikely to guess. -#: The named function `f` is, in some sense, just a placeholder since the student will -#: enter expressions involving `f(n-1)`, WeBWorK will interpret it internally as -#: `sin(pi^(n-1))+e*n^2`, and the only thing the student sees is `f(n-1)`. -#: If the recursion -#: has an closed-form solution (e.g., the Fibonacci numbers are given by -#: `f(n) = (a^n - (1-a)^n)/sqrt(5)` where `a = (1+sqrt(5))/2)` and you want to allows -#: students to enter the closed-form solution, it would be good to define `f` using -#: that explicit solution in case the student tries to answer the question by writing -#: out the explicit solution `(a^n - (1-a)^n)/sqrt(5)` instead of using the shorthand `f(n)`. +#: Define a new named function `f` as something the student is unlikely to +#: guess. The named function `f` is just a placeholder since the student will +#: enter expressions involving `f(n - 1)`. It will be interpreted internally as +#: defined here, and the only thing the student sees is `f(n - 1)`. +#: +#: If the recursion has a closed-form solution (e.g., the Fibonacci numbers are +#: given by `f(n) = (a^n - (1 - a)^n) / sqrt(5)` where `a = (1 + sqrt(5)) / 2`) +#: and you want to allow students to enter the closed-form solution, it would be +#: good to define `f` using that explicit solution in case the student tries to +#: answer the question by entering the explicit solution. Context()->variables->are(n => 'Real'); parserFunction(f => 'sin(pi^n) + e * n^2'); $fn = Formula('3 f(n - 1) + 2'); #:% section = statement -#: We should tell students to use function notation rather than subscript notation -#: so that they aren't confused about syntax. BEGIN_PGML -The current value [`f(n)`] is three times the previous value, plus two. Find a -recursive definition for [`f(n)`]. Enter [`f_{n-1}`] as [`f(n-1)`]. +If [`f(n)`] defines a sequence for all integegers [`n \geq 0`] that satisfies +the property that [`f(n)`] is two more than three times the previous value. +Find a recursive definition for [`f(n)`]. [`f(n) =`] [_]{$fn}{15} END_PGML diff --git a/tutorial/sample-problems/Sequences/SeriesTest.pg b/tutorial/sample-problems/Sequences/SeriesTest.pg index b2bfde3de2..11cbdcdd0a 100644 --- a/tutorial/sample-problems/Sequences/SeriesTest.pg +++ b/tutorial/sample-problems/Sequences/SeriesTest.pg @@ -17,24 +17,39 @@ #:% categories = [sequences, series] #:% section = preamble -#: We load `niceTables.pl` to create a table in which answer blanks are stacked on top -#: of each other to form a fraction. We use `PGgraders.pl` to give partial credit -#: incrementally. We use `parserMultiAnswer.pl` for the fraction answer so that we can -#: accept two correct answers, depending on how much a student has simplified their answer. +#: The PODLINK('parserMultiAnswer.pl') macro is used for the fraction answer so +#: that the numerator and denominator can be checked together. +#: +#: The PODLINK('parserRadioMultiAnswer.pl') macro is used for a better way for +#: students to enter an answer that might not exist than telling students to +#: enter DNE. +#: +#: The PODLINK('niceTables.pl') macro which is loaded by the `PGML.pl` macro is +#: used to create a table in which answer blanks are stacked on top of each +#: other to form a fraction. +#: +#: The PODLINK('PGgraders.pl') macro is used to give incremental partial credit +#: (although that is a poor choice for this problem). DOCUMENT(); loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'niceTables.pl', 'parserPopUp.pl', - 'PGgraders.pl', 'parserMultiAnswer.pl', + 'PGstandard.pl', 'PGML.pl', + 'parserPopUp.pl', 'PGgraders.pl', + 'parserMultiAnswer.pl', 'parserRadioMultiAnswer.pl', 'PGcourse.pl' ); #:% section = setup -#: We use the `MultiAnswer` object `$multians` to allow students to enter one of two -#: correct answers. We could have also accomplished this using two custom answer checkers. +#: Create `$multians` as a `MultiAnswer` with the two answers `$num1` and +#: `$den1`. An alternate form of the correct answer is `$num2 / $den2`. This +#: alternate form is also checked for in the `MultiAnswer` checker. +#: +#: The value of the limit in the limit comparison test is created as a +#: `RadioMultiAnswer`. This allows a clear way for students to enter a +#: non-existent limit, and is better than telling students to enter DNE and +#: results in the invalid statement `lim ... = DNE`. #: -#: We display the answerblanks nicely as a fraction in HTML and TeX modes by how we constructed `$showfraction`. +#: Also create a drop down answer for asking about convergence of the series. Context()->variables->are(n => 'Real'); $a = random(2, 9); @@ -43,18 +58,12 @@ $c = random(5, 20); $d = random(3, 9); $e = random(2, 9); -$dm1 = $d - 1; -$dm2 = $d - 2; - -# TeX -$series = "\sum_{n=$c}^{\infty} \frac{$a n + $b}{$c n^{$d} + $e}"; -$fraction = "\lim_{n\to\infty} \frac{a_n}{b_n} = \lim_{n\to\infty}"; - -$num1 = Formula("$a n^$d + $b n^$dm1"); +$num1 = Formula("$a n^$d + $b n^" . ($d - 1)); $den1 = Formula("$c n^$d + $e"); -$num2 = Formula("$a + $b/n"); -$den2 = Formula("$c + $e/(n^$d)"); +# Alternate form of the correct answer +$num2 = Formula("$a + $b / n"); +$den2 = Formula("$c + $e / (n^$d)"); $multians = MultiAnswer($num1, $den1)->with( singleResult => 0, @@ -82,46 +91,70 @@ $multians = MultiAnswer($num1, $den1)->with( } ); -$limit = Formula("$a/$c"); -$popup = - PopUp([ 'Choose', 'Converges', 'Diverges', 'Inconclusive' ], 'Converges'); - -# Display the fraction and answer blanks nicely -$frac = LayoutTable( - [ [ [ ans_rule(10), rowbottom => 1 ] ], [ ans_rule(10) ] ], - center => 0, - allcellcss => { padding => '4pt' } +$limitRMA = RadioMultiAnswer( + [ + [ + '\(\displaystyle\lim_{n \to \infty}\frac{a_n}{b_n} =\) %s', + Formula("$a / $c") + ], + ['The limit does not exist, and is not infinite.'] + ], + 0 ); +$popup = DropDown([ 'Converges', 'Diverges', 'Inconclusive' ], 'Converges'); + #:% section = statement -#: Most of this is standard latex markup in a PGML block. Note that to -#: display the fraction above, we use `[$frac]*` followed by the -#: PGML codeblock `[@ ANS($multians->cmp); '' @]` which does the -#: answer checking using the multianswer described above. There is a -#: `''` at the tend of the codeblock to return an empty string instead of -#: a HASHREF which we get from the ANS method. +#: Display the answer rules nicely as a fraction in HTML and TeX modes by using +#: the `PGML` syntax for a `LayoutTable` from PODLINK('niceTables.pl'). BEGIN_PGML Use the limit comparison test to determine whether -[``\sum_{n=[$c]}^{\infty} a_n = \sum_{n=[$c]}^{\infty} \frac{[$a] n + [$b]}{[$c] n^{[$d]} + [$e]}``] +[``\sum_{n = [$c]}^{\infty} a_n + = \sum_{n = [$c]}^{\infty} \frac{[$a] n + [$b]}{[$c] n^{[$d]} + [$e]}``] converges or diverges. -a. Choose a series [``\sum_{n=[$c]}^\infty b_n``] with terms of the form -[``b_n = \frac{1}{n^p}``] and apply the limit comparison test. Write your -answer as a fully reduced fraction. For [``n \geq [$c]``], -[```\frac{\lim_{n \to \infty} a_n}{\lim_{n \to \infty} b_n}```][$frac]* [@ ANS($multians->cmp); '' @] +Choose a series [``\sum_{n = [$c]}^\infty b_n``] with terms of the form +[``b_n = \frac{1}{n^p}``] to use in the limit comparison test. + +a. Simplify the fraction [``\frac{a_n}{b_n}``] in the application of the limit +comparison test shown. Give your answer as a fully reduced fraction. +[# + [. + For [`n \geq [$c]`], + [``\lim_{n \to \infty}\frac{a_n}{b_n} = \lim_{n \to \infty}``] + .] + [. + [# + [.[_]{$multians}{10}.]*{ bottom => 1 } + [.[_]{$multians}{10}.] + #]*{ padding => [ 0.5, 0 ] } + .] +#]*{ + center => 0, + valign => 'middle', + allcellcss => { padding => '4pt' } +} -b. Evaluate the limit in the previous part. Enter [` \infty `] as _infinity_ -and [` -\infty `] as _-infinity_. If the limit does not exist, enter _DNE_. +b. Evaluate the limit in the previous part. Enter [|infinity|]* for [` \infty `] +and [|-infinity|]* for [`-\infty`]. -[``\lim_{n\to\infty} \frac{a_{n}}{b_{n}} =``] [_]{$limit}{15} + [_]{$limitRMA}{10} c. By the limit comparison test, does the series converge, diverge, or is the -test inconclusive? [_]{$popup} +test inconclusive? [_]{$popup} END_PGML #:% section = answer -#: We use the problem grader fluid to give partial credit incrementally: 0% for 0-1 -#: correct answers, 40% for 2-3 correct answers, and full credit for 4 correct answers. +#: The problem grader fluid is used to give partial credit incrementally. +#: +#: * 0% is awarded for 0-1 correct answers, +#: * 40% is awarded for 2-3 correct answers, and +#: * full credit is awarded for 4 correct answers. +#: +#: This is only here to demonstrate the usage of the fluid problem grader, and +#: because that is how this problem has been. It would be better to use the +#: weighted grader for a problem like this. The parts of this problem are not +#: equal, so the fluid problem grader is not a good choice. install_problem_grader(~~&custom_problem_grader_fluid); $ENV{grader_numright} = [ 2, 4 ]; diff --git a/tutorial/sample-problems/snippets/CommentsForInstructors.pg b/tutorial/sample-problems/Snippets/CommentsForInstructors.pg similarity index 67% rename from tutorial/sample-problems/snippets/CommentsForInstructors.pg rename to tutorial/sample-problems/Snippets/CommentsForInstructors.pg index 149d99a42b..64c4442053 100644 --- a/tutorial/sample-problems/snippets/CommentsForInstructors.pg +++ b/tutorial/sample-problems/Snippets/CommentsForInstructors.pg @@ -15,14 +15,17 @@ #:% name = Comment for Instructors #:% type = snippet +#:% categories = [comments] +#:% section = preamble DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); -#:% section = comment -#: Include the `COMMENT();` just before the `ENDDOCUMENT();` Comments are only visible -#: when the PG file is viewed in the Library Browser, so students will not see them. +#:% section = statement +#: Call `COMMENT` to add a comment that is visible when the PG file is viewed in +#: the Library Browser or the PG problem editor. Students do not see these +#: comments. COMMENT('This problem is not randomized.'); ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Statistics/LinearRegression.pg b/tutorial/sample-problems/Statistics/LinearRegression.pg new file mode 100644 index 0000000000..2bb03618db --- /dev/null +++ b/tutorial/sample-problems/Statistics/LinearRegression.pg @@ -0,0 +1,66 @@ +## DESCRIPTION +## Find the mean and standard deviation of a list of numbers. +## ENDDESCRIPTION +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(WeBWorK tutorial) +## Institution(Fitchburg State University) +## Author(Peter Staab) +## KEYWORDS('statistic', 'linear regression', 'correlation coefficient') + +#:% name = Linear Regression +#:% subject = [statistics] +#:% type = sample + +#:% section = preamble +#: The PODLINK('PGstatisticsmacros.pl') macro provides the `sample_correlation` +#: and `linear_regression` methods. +DOCUMENT(); + +loadMacros("PGstandard.pl", "PGML.pl", 'PGstatisticsmacros.pl', "PGcourse.pl"); + +#:% section = setup +#: Generate random numbers, and then use the `sample_correlation` and +#: `linear_regression` methods from PODLINK('PGstatisticsmacros.pl'). + +# Generate a random slope and intercept. +$m = random(0.1, 0.75, 0.05); +$b = random(0.5, 5, 0.25); + +$x = []; +$y = []; + +# Create some random data +for (0 .. 9) { + $x->[$_] = random(2.5, 7.5, 0.5); + $y->[$_] = $m * $x->[$_] + $b; +} + +@rows = map { [ $x->[$_], $y->[$_] ] } 0 .. $#$x; + +$corr = sample_correlation($x, $y); +($m, $b) = linear_regression($x, $y); + +#:% section = statement +BEGIN_PGML +Consider the following data: + +[# + [.[`x`].] [.[`y`].]*{ headerrow => 1 } + [. .]{ rows => \@rows } +#]{ horizontalrules => 1, align => '|c|c|' } + +Find the correlation coefficient and the linear regression line: + +a) correlation coefficient: [__]{$corr} + +b) linear regression line [`\hat{y} =`] [__]{Formula("$m x + $b")} + +END_PGML + +#:% section = solution +BEGIN_PGML_SOLUTION +Provide a solution here. +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Statistics/MeanStdDev.pg b/tutorial/sample-problems/Statistics/MeanStdDev.pg new file mode 100644 index 0000000000..a86773c91a --- /dev/null +++ b/tutorial/sample-problems/Statistics/MeanStdDev.pg @@ -0,0 +1,73 @@ +## DESCRIPTION +## Find the mean and standard deviation of a list of numbers. +## ENDDESCRIPTION +## DBsubject(WeBWorK) +## DBchapter(WeBWorK tutorial) +## DBsection(WeBWorK tutorial) +## Institution(Fitchburg State University) +## Author(Peter Staab) +## KEYWORDS('statistic', 'mean', 'standard deviation') + +#:% name = Mean and Standard Deviation +#:% subject = [statistics] +#:% type = sample + +#:% section = preamble +#: The PODLINK('PGstatisticsmacros.pl') macro provides the `stats_mean`, +#: `stats_sd`, and `stats_SX_SXX` methods. +DOCUMENT(); + +loadMacros("PGstandard.pl", "PGML.pl", 'PGstatisticsmacros.pl', "PGcourse.pl"); + +#:% section = setup +#: Generate random numbers and then calculate the mean and standard deviation +#: using the `stats_mean` and `stats_sd` methods from +#: PODLINK('PGstatisticsmacros.pl'). +@x = map { random(1, 10) } 0 .. 7; + +$mean = stats_mean(@x); +$sd = stats_sd(@x); + +#:% section = statement +BEGIN_PGML +Find the mean and standard deviation of the following list of numbers: +[@ join(', ', @x) @] + +a) Mean: [_]{$mean}{8} + +b) Standard Deviation: [_]{$sd}{8} +END_PGML + +#:% section = solution +#: The `stats_SX_SXX` method from `PGstatisticsmacros.pl` returns the sum and +#: sum of squares which are used in the solution. +#: +#: Note that when a long list of equalities are strung together in an equation +#: it is a good idea to use the `aligned` environment to break the equalities +#: onto separate lines. If left on a single line, the equation may extend +#: outside of the solution container and look quite bad. Particularly on narrow +#: screens. +($sum_x, $sum_sq) = stats_SX_SXX(@x); +$var = $sum_sq - ($sum_x**2) / 8; + +BEGIN_PGML_SOLUTION +The mean is + +[``\bar{x} = \frac{1}{n} \sum_{i = 1}^n x_i = \frac{[$sum_x]}{8} = [$mean]``] + +For the standard deviation, first, find the variance. + +[`` + \begin{aligned} + s^2 &= \frac{1}{n - 1} \left( + \sum_{i = 1}^n x_i^2 - \frac{1}{n}\left(\sum_{i = 1}^n x_i\right)^2 + \right) \\ + &= \frac{1}{7} \left( [$sum_sq] - \frac{1}{8} ([$sum_x])^2\right) \\ + &= \frac{[$var]}{7} + \end{aligned} +``] + +Taking the square root gives [`s \approx [$sd]`] +END_PGML_SOLUTION + +ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Statistics/linearRegression.pg b/tutorial/sample-problems/Statistics/linearRegression.pg deleted file mode 100644 index d316f12e9b..0000000000 --- a/tutorial/sample-problems/Statistics/linearRegression.pg +++ /dev/null @@ -1,68 +0,0 @@ -## DESCRIPTION -## Find the mean and standard deviation of a list of numbers. -## ENDDESCRIPTION -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(WeBWorK tutorial) -## Institution(Fitchburg State University) -## Author(Peter Staab) -## KEYWORDS('statistic', 'linear regression', 'correlation coefficient') - -#:% name = Linear Regression -#:% subject = [statistics] -#:% type = sample - -#:% section = preamble -#: Statistics functions mean and standard deviation are used so we load -#: `PGstatisticsmacros.pl`. We use the `DataTable` method from the -#: `niceTables.pl` macro. -DOCUMENT(); - -loadMacros( - "PGstandard.pl", "PGML.pl", - 'PGstatisticsmacros.pl', 'niceTables.pl', - "PGcourse.pl" -); - -#:% section = setup -#: First, generate random numbers and then use the methods -#: `sample_correlation` and `linear_regression` -#: from the macro PODLINK('PGstatisticsmacros.pl'). - -# produce an approximate slope and intercept -$m = random(0.1, 0.75, 0.05); -$b = random(0.5, 5, 0.25); - -# Create some random data -for $i (0 .. 9) { - $x[$i] = random(2.5, 7.5, 0.5); - $y[$i] = $m * $x[$i] + $b; -} - -@rows = ([ '\(x\)', '\(y\)' ]); -push(@rows, [ $x[$_], $y[$_] ]) for (0 .. $#x); - -$corr = sample_correlation(~~@x, ~~@y); -($m, $b) = sample_correlation(~~@x, ~~@y); - -#:% section = statement -BEGIN_PGML -Consider the following data: - -[@ DataTable(\@rows, - padding => [0.25, 0.25], horizontalrules => 1, align => '|c|c|' ) @]* - -Find the correlation coefficient and the linear regression line: - -a) correlation coefficient: [__]{$corr} - -b) linear regression line [`\hat{y}=`] [__]{Formula("$m x + $b")} - -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Provide a solution here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Statistics/meanStdDev.pg b/tutorial/sample-problems/Statistics/meanStdDev.pg deleted file mode 100644 index 037e10fe15..0000000000 --- a/tutorial/sample-problems/Statistics/meanStdDev.pg +++ /dev/null @@ -1,64 +0,0 @@ -## DESCRIPTION -## Find the mean and standard deviation of a list of numbers. -## ENDDESCRIPTION -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(WeBWorK tutorial) -## Institution(Fitchburg State University) -## Author(Peter Staab) -## KEYWORDS('statistic', 'mean', 'standard deviation') - -#:% name = Mean and Standard Deviation -#:% subject = [statistics] -#:% type = sample - -#:% section = preamble -#: Statistics functions mean and standard deviation are used so we load -#: `PGstatisticsmacros.pl`. -DOCUMENT(); - -loadMacros("PGstandard.pl", "PGML.pl", 'PGstatisticsmacros.pl', "PGcourse.pl"); - -#:% section = setup -#: First, generate random numbers and then calculate the mean and standard -#: deviation using the methods `stats_mean` and `stats_sd` from the macro -#: PODLINK('PGstatisticsmacros.pl'). -for $i (0 .. 7) { - $x[$i] = random(1, 10); -} - -$mean = stats_mean(@x); -$sd = stats_sd(@x); - -#:% section = statement -BEGIN_PGML -Find the mean and standard deviation of the following list of numbers: -[@ join(', ', @x) @] - -a) Mean = [_____]{$mean} - -b) Standard Deviation = [___]{$sd} -END_PGML - -#:% section = solution -#: We use the method `stats_SX_SXX` of the `PGstatisticsmacros.pl` which -#: returns the sum and sum of squares which aids in the solution. -($sum_x, $sum_sq) = stats_SX_SXX(@x); -$var = $sum_sq - ($sum_x**2) / 8; - -BEGIN_PGML_SOLUTION -For the mean, use the formula - -[``\bar{x} = \frac{1}{n} \sum_{i=1}^n x_i = \frac{[$sum_x]}{8} = [$mean]``] - -For the standard deviation, first, find the variance - -[``s^2 = \frac{1}{n-1} \left(\sum_{i=1}^n x_i^2 - \frac{1}{n}(\sum_{i=1}^n x_i)^2\right) = -\frac{1}{7} \left( [$sum_sq] - \frac{1}{8} ([$sum_x])^2\right) = \frac{[$var]}{7}``] - -and taking the square root - -[``s = [$sd]``] -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Trig/DisableFunctionsTrig.pg b/tutorial/sample-problems/Trig/DisableFunctionsTrig.pg index 567f0a7154..348d2e4273 100644 --- a/tutorial/sample-problems/Trig/DisableFunctionsTrig.pg +++ b/tutorial/sample-problems/Trig/DisableFunctionsTrig.pg @@ -14,45 +14,33 @@ #:% name = Disabling Functions #:% type = [Sample, technique] #:% subject = [trigonometry, precalculus] -#:% categories = [trigonometry] +#:% categories = [functions, exact] #:% section = preamble -#: The `contextFraction.pl` is loaded since we used the `Fraction-NoDecimals` context. +#: The PODLINK('contextFraction.pl') is loaded for the `Fraction-NoDecimals` +#: context. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'contextFraction.pl', 'PGcourse.pl'); #:% section = setup -#: We choose a context that requires fractions as answers and does not allow decimals. -#: After constructing the formulas involving trig functions, we disable all functions -#: and re-enable the `sqrt()` function. This means that students are allowed to type in -#: fractions and square roots, but not much else (e.g., they'll get an error message -#: if they type in a trig function). +#: Select the `Fraction-NoDecimals` context which requires fractions as answers +#: and does not allow decimals. #: -#: Note that `$f1` and `$f2` are MathObject Formulas, which do not get reduced since `pi` -#: is set to keep its name. If `$f1` and `$f2` used `Compute` instead, then the results -#: would be -1 and 0.866... instead of cos(\pi) and sin(\pi/3) as desired. +#: After constructing the formulas involving trig functions, disable all +#: functions and re-enable the `sqrt` function. This means fractions and square +#: roots can be entered, but an error message will be shown if any other +#: function is entered. Context('Fraction-NoDecimals'); -# Prevent pi from becoming 3.1415... and cos(pi) from -# becoming -1. -Context()->constants->set(pi => { keepName => 1 }); - -# The next context changes are not necessary to -# prevent cos(pi) from becoming -1, but they cannot hurt. -Context()->flags->set( - reduceConstants => 0, - reduceConstantFunctions => 0 -); - $f1 = Formula('cos(pi)'); -$f2 = Formula('sin(pi/3)'); +$f2 = Formula('sin(pi / 3)'); Context()->functions->disable('All'); Context()->functions->enable('sqrt'); $answer1 = Compute('-1'); -$answer2 = Compute('sqrt(3)/2'); +$answer2 = Compute('sqrt(3) / 2'); #:% section = statement BEGIN_PGML @@ -63,9 +51,15 @@ Enter your answers as simplified fractions. + [`[$f2] =`] [_]{$answer2}{15} END_PGML +#:% section = hint +#: A hint is provided. +BEGIN_PGML_HINT +The cosine of an angle is zero when the angle is an integer multiple of [`\pi`]. +END_PGML_HINT + #:% section = solution BEGIN_PGML_SOLUTION -The cosine of an angle is zero when the angle is an integer multiple of [`\pi`]. +Provide a solution here. END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Trig/DraggableIdentity.pg b/tutorial/sample-problems/Trig/DraggableIdentity.pg index 4b5807efc4..742fde95ec 100644 --- a/tutorial/sample-problems/Trig/DraggableIdentity.pg +++ b/tutorial/sample-problems/Trig/DraggableIdentity.pg @@ -14,42 +14,41 @@ #:% name = Draggable Trigonometry Identity #:% type = Sample #:% subject = [proof, trigonometry] -#:% categories = [trigonometry, draggable] +#:% categories = [draggable] #:% section = preamble -#: This problem uses the `draggableProof.pl` macro to display "buckets" that the -#: student can drag statements to and from. +#: This problem uses the PODLINK('draggableProof.pl`) macro to display "buckets" +#: that the student can drag statements to and from. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'draggableProof.pl', 'PGcourse.pl'); #:% section = setup -#: The `DraggableProof` function takes an arrayref of correct statements, -#: followed (optionally) by extra statements. See -#: PODLINK('the Draggable Proof POD', 'draggableProof.pl') for more options. +#: The `DraggableProof` function takes a reference to an array of correct +#: statements, followed (optionally) by a reference to an array of extra +#: statements. See PODLINK('draggableProof.pl') for more details. #: #: This shows how other identities could be structured. You probably want #: some incorrect statements to make the problem a little bit harder. $proof = DraggableProof( # These are the correct statements of the proof in the correct order. [ - '\(\sin(\pi-\theta) = \sin(\pi)\cos(\theta)-\cos(\pi)\sin(\theta) \)', - '\(\sin(\pi-\theta) = 0 \cdot \cos(\theta) - (-1)\cdot \sin(\theta)\)', - '\(\sin(\pi-\theta) = 0+1\sin(\theta)\)', - '\(\sin(\pi-\theta) = \sin(\theta)\)', + '\(\sin(\pi - \theta) = \sin(\pi)\cos(\theta) - \cos(\pi)\sin(\theta)\)', + '\(\sin(\pi - \theta) = (0)\cos(\theta) - (-1)\sin(\theta)\)', + '\(\sin(\pi - \theta) = 0 + (1)\sin(\theta)\)', + '\(\sin(\pi - \theta) = \sin(\theta)\)', ], # These are extra statements that are not needed. [ - '\(\sin(\pi-\theta) = \cos(\pi)\cos(\theta)-\sin(\pi)\sin(\theta) \)', - '\(\sin(\pi-\theta) = 0\cdot\cos(\theta)-(-1)\sin(\theta) \)', - '\(\sin(\pi-\theta) = \sin(\pi)\cos(\theta)+\cos(\pi)\sin(\theta) \)', - '\(\sin(\pi-\theta) = 0\cdot\cos(\theta)+1 \cdot\sin(\theta) \)', - '\(\sin(\pi-\theta) = 1\cdot\cos(\theta)+0 \cdot\sin(\theta) \)', + '\(\sin(\pi - \theta) = \cos(\pi)\cos(\theta) - \sin(\pi)\sin(\theta)\)', + '\(\sin(\pi - \theta) = \sin(\pi)\cos(\theta) + \cos(\pi)\sin(\theta)\)', + '\(\sin(\pi - \theta) = (1)\cos(\theta) + (0)\sin(\theta)\)', ] ); #:% section = statement -#: The line `[_]{$proof}` prints the statement and options in the proof and sets up the answer rule. +#: The line `[_]{$proof}` inserts the drag and drop "buckets" containing the +#: proof statements that are to be selected and ordered. BEGIN_PGML Prove the trigonmetric identity [`\sin(\pi-\theta) = \sin(\theta)`]. diff --git a/tutorial/sample-problems/Trig/PeriodicAnswers.pg b/tutorial/sample-problems/Trig/PeriodicAnswers.pg index 99963be619..1bf2b540a3 100644 --- a/tutorial/sample-problems/Trig/PeriodicAnswers.pg +++ b/tutorial/sample-problems/Trig/PeriodicAnswers.pg @@ -14,7 +14,7 @@ #:% name = Periodic Answers #:% type = Sample #:% subject = [trigonometry, precalculus] -#:% categories = [trigonometry] +#:% categories = [periodic] #:% section = preamble DOCUMENT(); @@ -22,9 +22,9 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: This allows any answer of the form `pi/2+n pi` for integer `n` to be -#: accepted. -$answer = Real('pi/2')->with(period => pi); +#: This allows any answer of the form `pi / 2 + n pi` where `n` is a particular +#: integer to be accepted. +$answer = Real('pi / 2')->with(period => pi); #:% section = statement BEGIN_PGML @@ -33,10 +33,16 @@ Enter a solution to [`\cos(\theta) = 0`]. [`\theta =`] [_]{$answer}{15} END_PGML +#:% section = hint +#: A hint is provided. +BEGIN_PGML_HINT +The cosine of an angle is zero when the angle is [`\frac{2n + 1}{2}\pi`] for +any integer [`n`]. +END_PGML_HINT + #:% section = solution BEGIN_PGML_SOLUTION -The cosine of an angle is zero when the angle is [`(n + 1 / 2)\pi`] for any -integer [`n`]. +Solution explanation goes here. END_PGML_SOLUTION ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Trig/ProvingTrigIdentities.pg b/tutorial/sample-problems/Trig/ProvingTrigIdentities.pg index 8a4abb58bd..67026d74c1 100644 --- a/tutorial/sample-problems/Trig/ProvingTrigIdentities.pg +++ b/tutorial/sample-problems/Trig/ProvingTrigIdentities.pg @@ -15,45 +15,43 @@ #:% name = Proving Identities #:% type = Sample #:% subject = [trigonometry, precalculus] -#:% categories = [trigonometry, proof] +#:% categories = [proof] #:% section = preamble -#: This is a scaffolded problem, so load `scaffold.pl`. +#: This is a scaffolded problem, so load PODLINK('scaffold.pl'). DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'scaffold.pl', 'PGcourse.pl'); #:% section = setup -#: We cleverly redefine the sine function so that when the student enters `sin(t)`, -#: it is interpreted and evaluated internally as `exp(pi*t)` but displayed to -#: the student as `sin(t)`. This prevents the entering of the orginally -#: expression as the answer. An alternative method to doing this is in -#: PROBLINK('TrigIdentities.pg'). +#: The sine function is cleverly redefined so that when the student enters +#: `sin(t)`, it is interpreted and evaluated internally as `exp(pi * t)` but +#: displayed to the student as `sin(t)`. This prevents the original expression +#: from being entered as the answer in the last step of the proof. Context()->variables->are(t => 'Real'); # Redefine sin(x) to be e^(pi x). Context()->functions->remove('sin'); -package NewFunc; +package AltSin; # The next line makes the function a function from reals to reals. our @ISA = qw(Parser::Function::numeric); sub sin { shift; my $x = shift; - return CORE::exp($x * 3.1415926535); + return CORE::exp($x * $pi); } package main; -# Make it work on formulas as well as numbers -# sub cos { Parser::Function->call('cos', @_) } # if uncommented, this line will generate error messages + # Add the new functions to the Context. -Context()->functions->add(sin => { class => 'NewFunc', TeX => '\sin' },); +Context()->functions->add(sin => { class => 'AltSin', TeX => '\sin' },); #:% section = statement BEGIN_PGML -This problem has three parts. A part may be open if it is correct or if it is -the first incorrect part. Clicking on the heading for a part toggles whether it +This problem has three parts. A part may be open if it is correct or if it is +the first incorrect part. Clicking on the heading for a part toggles whether it is displayed. In this multi-part problem, we will use algebra to verify the identity @@ -66,16 +64,16 @@ Scaffold::Begin(is_open => 'correct_or_first_incorrect'); Section::Begin('Part 1'); BEGIN_PGML - -First, using algebra we may rewrite the equation above as -[`\displaystyle \sin(t) = \left( \frac{1 + \cos(t)}{\sin(t)} \right) \cdot \Big(`] +First, the equation above can be rewritten as +[`\displaystyle \sin(t) + = \left( \frac{1 + \cos(t)}{\sin(t)} \right) \cdot \Big(`] [_]{'1 - cos(t)'}{15} [` \Big) `]. END_PGML Section::End(); Section::Begin('Part 2'); BEGIN_PGML -Using algebra we may rewrite the equation as +Next, the equation can be rewritten as [`\sin(t) \cdot \big(`] [_]{'sin(t)'}{15} [`\big) = \big(1 + \cos(t)\big) \cdot \big(1 - \cos(t)\big)`]. END_PGML @@ -83,20 +81,14 @@ Section::End(); Section::Begin('Part 3'); BEGIN_PGML -Finally, using algebra we may rewrite the equation as +Finally, the equation can be rewritten as [`\sin^2(t) =`] [_]{'1-(cos(t))^2'}{15}, which is true since -[`\cos^2(t) + \sin^2(t) = 1`]. Thus, the original identity can be derived by -reversing these steps. +[`\cos^2(t) + \sin^2(t) = 1`]. + +Thus, the original identity can be derived by reversing these steps. END_PGML Section::End(); Scaffold::End(); -COMMENT( - 'This is a multi-part problem -in which the next part is revealed only after the previous -part is correct. Prevents students from entering trivial -identities (entering what they were given). Uses PGML.' -); - ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Trig/SpecialTrigValues.pg b/tutorial/sample-problems/Trig/SpecialTrigValues.pg index f3240a1fff..282a85db01 100644 --- a/tutorial/sample-problems/Trig/SpecialTrigValues.pg +++ b/tutorial/sample-problems/Trig/SpecialTrigValues.pg @@ -16,23 +16,23 @@ #:% subject = trigonometry #:% section = preamble -#: We load the `specialTrigValues.pl` macro to use exact values on the -#: unit circle. +#: Load the PODLINK('specialTrigValues.pl') macro for the `specialRadical` and +#: `specialAngle` methods. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'specialTrigValues.pl', 'PGcourse.pl'); #:% section = setup #: The `random_coprime` function selects two random numbers that are coprime -#: from the list. This will give fractions with denominators of 2,3,4 or 6. +#: from the list. This will give fractions with denominators of 2, 3, 4 or 6. #: -#: The `specialRadical` function returns a MathObject in the form `a sqrt(b)/c` -#: were b, c come from a list of integers (defaults to `[1,2,3]`). +#: The `specialRadical` function returns a `Formula` in the form `a sqrt(b) / c` +#: were `b` and `c` come from a list of integers (defaults to `[1, 2, 3]`). #: -#: It is noted that `specialRadical` has a complex form as well. +#: Note that the `specialRadical` function has a complex form as well. #: -#: The `specialAngle` function returns a MathObject in the form `a pi/c` where -#: a in an integer and `c` comes from a list (defaults to `[1,2,3,4,6]`). +#: The `specialAngle` function returns a MathObject in the form `a pi / c` where +#: `a` in an integer and `c` comes from a list (defaults to `[1, 2, 3, 4, 6]`). ($d, $n) = random_coprime([ 2, 3, 4, 6 ], [ 1 .. 12 ]); $r = random(2, 3); @@ -50,13 +50,13 @@ $z = specialRadical("$r exp($n pi i/$d)"); BEGIN_PGML Evaluate the following: -a) [`[$r] \cos([$n] \pi/[$d])=`] [_]{$c} +a) [`[$r] \cos([$n] \pi/[$d]) =`] [_]{$c} -b) [`[$r] \sin([$n] \pi/[$d])=`] [_]{$s} +b) [`[$r] \sin([$n] \pi/[$d]) =`] [_]{$s} -c) [`[$r] \exp([$n] \pi/[$d])=`] [_]{$z} +c) [`[$r] \exp([$n] \pi/[$d]) =`] [_]{$z} -d) [`\arcsin([$x])=`] [_]{$a} +d) [`\arcsin([$x]) =`] [_]{$a} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/Trig/TrigDegrees.pg b/tutorial/sample-problems/Trig/TrigDegrees.pg index 0c5c151356..b766672052 100644 --- a/tutorial/sample-problems/Trig/TrigDegrees.pg +++ b/tutorial/sample-problems/Trig/TrigDegrees.pg @@ -14,40 +14,44 @@ #:% name = Degrees in Trigonometric Functions #:% type = [technique, sample] #:% subject = [trigonometry, precalculus] -#:% categories = [trigonometry, degrees] +#:% categories = [degrees] #:% section = preamble -#: We load the `contextTrigDegrees.pl` macro to help with trig functions with degrees. +#: Load the PODLINK('contextTrigDegrees.pl') macro for the `TrigDegrees` +#: context. Also load the PODLINK('parserNumberWithUnits.pl') so that answers +#: can be asked for with the degree symbol. DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'contextTrigDegrees.pl', 'PGcourse.pl'); +loadMacros( + 'PGstandard.pl', 'PGML.pl', + 'contextTrigDegrees.pl', 'parserNumberWithUnits.pl', + 'PGcourse.pl' +); #:% section = setup -#: To override the WeBWorK default of evaluating trig functions in radians, use the `TrigDegrees` context, -#: which redefines the standard trig functions to be in degrees, both in any formulas that appear -#: later in the PG code and in any formulas that students enter in answer blanks. +#: Select the `TrigDegrees` context to which evaluates trig functions in +#: degrees. This applies to any formulas or student answers in this context. #: -#: These redefined functions allow students to enter inverse functions using syntax such as -#: `atan(x)`, or `arctan(x)`, or `tan^(-1)(x)`. +#: The second answers is constructed with the `NumberWithUnits` method and the +#: degree symbol is specified as the unit. Degree measures should always be +#: given with the degree symbol. Context('TrigDegrees'); -$ans1 = Compute("sin(30)"); -$ans2 = Compute("arcsin(0.5)"); +$ans1 = Compute('sin(30)'); +$ans2 = NumberWithUnits('arcsin(1 / 2)', 'degrees'); #:% section = statement -#: Since this is in degrees, you should tell the students this. +#: Inform the students to give angle measurements in degrees. BEGIN_PGML -1. [`\sin(30^{\circ})=`] [___]{$ans1} -2. [`\arcsin(1/2)=`] [___]{$ans2} +Evaluate the following. Give angles in degrees. -Interpret arguments of the sine and arcsine in terms of degrees. +1. [`\sin(30^{\circ}) =`] [_]{$ans1}{5} +2. [`\arcsin(1 / 2) =`] [_]{$ans2}{5} END_PGML -#:% section=solution +#:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION -COMMENT("Redefines trig functions to be in degrees (not radians)."); - ENDDOCUMENT(); diff --git a/tutorial/sample-problems/Trig/TrigIdentities.pg b/tutorial/sample-problems/Trig/TrigIdentities.pg index d4293d8cd7..40ba98ce69 100644 --- a/tutorial/sample-problems/Trig/TrigIdentities.pg +++ b/tutorial/sample-problems/Trig/TrigIdentities.pg @@ -14,7 +14,7 @@ #:% name = Trigonometric Identities #:% type = Sample #:% subject = [trigonometry, precalculus] -#:% categories = [trigonometry, custom] +#:% categories = [custom] #:% section = preamble DOCUMENT(); @@ -22,20 +22,27 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); #:% section = setup -#: To prevent the student from just entering the given expression, we create -#: a custom answer checker, which 1) performs a `->reduce` which will do some -#: small simplification, 2) returns an error if the original expression -#: was put in and 3) then checks if the answer is correct. +#: To prevent the student from just entering the given expression, a custom +#: answer checker is used, which 1) calls `reduce` on the student answer which +#: will do some small simplification, 2) returns an error if the original +#: expression is entered and 3) then checks if the answer is correct. #: -#: An alternative method to doing this is in -#: PROBLINK('ProvingTrigIdentities.pg'). +#: A better method for doing this is demonstrated in +#: PROBLINK('ProvingTrigIdentities.pg'). Don't use the method demonstrated in +#: this example as it will fail in many cases. A student can enter +#: `tan(x)*cos(x)*2/2` and it will be counted as correct because the `reduce` +#: call does not simplify that to `tan(x)*cos(x)`. Instead it reduces it to +#: `[2*tan(x)*cos(x)]/2`. In general using string comparison is not what you +#: should do with MathObjects. It completely subverts what MathObjects were +#: designed to do. $ans = Compute('sin(x)')->cmp( checker => sub { my ($correct, $student, $ansHash) = @_; my $stu_ans = $student->reduce; Value->Error('There is a simpler answer') - if $stu_ans->string eq 'cos(x)*tan(x)'; - return $student == $correct; + if $stu_ans->string eq 'cos(x)*tan(x)' + || $stu_ans->string eq 'tan(x)*cos(x)'; + return $student == $correct ? 1 : 0; } ); @@ -43,7 +50,7 @@ $ans = Compute('sin(x)')->cmp( BEGIN_PGML Simplify the expression as much as possible. -[`\tan(x) \cos(x) =`] [_]{$ans}{15} +[`\tan(x)\cos(x) =`] [_]{$ans}{15} END_PGML #:% section = solution @@ -51,8 +58,4 @@ BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION -COMMENT( - 'Prevents students from entering trivial identities (entering what they ' - . 'were given)'); - ENDDOCUMENT(); diff --git a/tutorial/sample-problems/VectorCalc/CylindricalGraph3D.pg b/tutorial/sample-problems/VectorCalc/CylindricalGraph3D.pg index 34663c4084..31d7a2973e 100644 --- a/tutorial/sample-problems/VectorCalc/CylindricalGraph3D.pg +++ b/tutorial/sample-problems/VectorCalc/CylindricalGraph3D.pg @@ -18,54 +18,74 @@ #:% categories = [graph] #:% section = preamble -#: The dynamic graph is generated with `plotly3D.pl`, -#: so this is needed. +#: The dynamic graph is generated with PODLINK('plotly3D.pl'). DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'plotly3D.pl', 'PGcourse.pl'); -#:% section=setup -#: We generate the plot parametrically for the radial functions `z=cos(r^2/4)` -#: and `z=r*sin^2(t)`. +#:% section = setup +#: To add a plotly graph to a problem, first create a `Graph3D` object by +#: calling the `Graph3D` method. Note that there are several options that can be +#: passed to the `Graph3D` method that affect the display of the graph. This +#: example uses the defaults for all of the options. Then add objects to the +#: graph by calling the `Graph3D` object methods. This example demonstrates the +#: usage of the `addSurface` method. Its syntax is #: -#: This occurs with `x=u*cos(v)` and `y=u*sin(v)`, where `u` and `v` are -#: used as the radial and angular variables respectively. These are the -#: default variables used. +#: ```{#addsurface-usage .perl} +#: $graph->addSurface( +#: [xFunction, yFunction, zFunction], +#: [uMin, uMax, uCount], +#: [vMin, vMax, vCount], +#: options +#: ); +#: ``` #: -#: The second plot changes the variables to `r` and `t` and shows a function -#: with a non-rotational symmetric plot. +#: where `xFunction`, `yFunction`, and `zFunction` are the parametric functions +#: for the `x`, `y`, and `z` coordinates, respectively, `uMin` and `uMax` are +#: the maximum and minimum values for the parameter `u` and `uCount` is the +#: number of values in that range to use, `vMin` and `vMax` are the maximum and +#: minimum values for the parameter `v` and `vCount` is the number of values in +#: that range to use, and the `options` are additional options that can be set +#: using the format `option => value`. Note that `uCount` and `vCount` are +#: optional and both default to 20 if not given. #: -#: The `addSurface` is very flexible, but if you are plotting a function in -#: cylindrical coordinates, then the first two functions should remain the -#: same. +#: First, generate the graph for the function parameterized by `x = u * cos(v)`, +#: `y = u * sin(v)`, and `z = $a * cos(u^2 / 4)` is generated. This graph uses +#: the default variables `u` and `v`. #: -#: See PODLINK('plotly3D.pl') for more information on options. +#: Second, generate the graph for the function parameterized by `x = r * cos(t)`, +#: `y = r * sin(t)`, and `z = r * sin(t)^2`. Since the variables `r` and `t` are +#: used and are not the default variables, those must be specified. +#: +#: See PODLINK('plotly3D.pl') for more details on the usage of the `Graph3D` +#: object and its `addSurface` method. $a = random(2, 5); -$gr1 = Graph3D(); -$gr1->addSurface( - [ 'u*cos(v)', 'u*sin(v)', "$a*cos(u^2/4)" ], - [ 0, 6, 30 ], - [ 0, 2 * pi, 30 ] +$graph1 = Graph3D(); +$graph1->addSurface( + [ 'u * cos(v)', 'u * sin(v)', "$a * cos(u^2 / 4)" ], + [ 0, 6, 30 ], + [ 0, 2 * pi, 30 ] ); -$gr2 = Graph3D(); -$gr2->addSurface( - [ 'r*cos(t)', 'r*sin(t)', 'r*sin(t)^2' ], - [ 0, 6, 30 ], - [ 0, 2 * pi, 30 ], +$graph2 = Graph3D(); +$graph2->addSurface( + [ 'r * cos(t)', 'r * sin(t)', 'r * sin(t)^2' ], + [ 0, 6, 30 ], + [ 0, 2 * pi, 30 ], variables => [ 'r', 't' ] ); -#:% section=statement -#: This shows how to add a plot to the problem. +#:% section = statement +#: If `$graph` is a `Graph3D` object, then insert its graph into the problem +#: with `[@ $graph->Print @]*`. BEGIN_PGML -This just shows the two plots side by side. +[@ $graph1->Print @]* -[@ $gr1->Print @]* [@ $gr2->Print @]* +[@ $graph2->Print @]* END_PGML -#:% section=solution +#:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION diff --git a/tutorial/sample-problems/VectorCalc/DirectionField.pg b/tutorial/sample-problems/VectorCalc/DirectionField.pg index da85cb3ce7..9f329000e7 100644 --- a/tutorial/sample-problems/VectorCalc/DirectionField.pg +++ b/tutorial/sample-problems/VectorCalc/DirectionField.pg @@ -17,54 +17,61 @@ #:% categories = [graph] #:% see_also = [VectorFieldGraph2D.pg] -#:% section=preamble -#: The macro `PGtikz.pl` is used to produced the direction field. +#:% section = preamble +#: The macro PODLINK('PGtikz.pl') is used to produced the direction field graph. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); -#:% section=setup +#:% section = setup #: A direction field is a vector field where the length of the vectors are -#: constant. We use the same technique as PROBLINK('VectorFieldGraph2D.pg'). +#: constant. Techniques similar to those in PROBLINK('VectorFieldGraph2D.pg') +#: are used. #: -#: The vector field is used and then when the vector is drawn is -#: scaled by its length or `sqrt(x^2+y^2)`. Since this is not defined at -#: the origin, we don't draw the vector there and use the `ifthen` package -#: to load the `\ifthenelse` latex command. +#: The vector field `` is used and when the vector is drawn its length is +#: scaled by the factor `1 / sqrt(x^2 + y^2)`. Since this is not defined at the +#: origin, the `ifthen` package is loaded and the `\ifthenelse` latex command +#: from that package used to skip this point. #: -#: If you want a slope field, where only the slope is draw with no arrow -#: delete the `->` in the option of the `\draw` command inside the `\foreach` -#: loops. +#: Delete `->` in the option of the `\draw` command inside the `\foreach` loops +#: if the arrows on the vectors are not desired. $graph = createTikZImage(); $graph->texPackages(['ifthen']); $graph->BEGIN_TIKZ \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-6,-6) rectangle (6,6); -\draw[dotted] (-5,-5) grid (5,5); -\draw[->] (-5,0) -- (5.25,0) node[above right] {\(x\)}; -\foreach \x in {-5,...,-1,1,2,...,5} \draw(\x,-5) node [below] {\x}; -\draw[->] (0,-5) -- (0,5.25) node[above right] {\(y\)}; -\foreach \y in {-5,...,-1,1,2,...,5} \draw(-5,\y) node [left] {\y}; -\foreach \x in {-4.5,-4,...,4.5} { - \foreach \y in {-4.5,-4,...,4.5} { - \ifthenelse{\equal{\x}{0} \AND \equal{\y}{0}}{}{ - \draw[thick, blue,->] (\x,\y) -- - ({\x+0.4*\y/sqrt(\x*\x+\y*\y)},{\y-0.4*\x/sqrt(\x*\x+\y*\y)}); - } - } - } + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-6, -6) rectangle (6, 6); +\draw[dotted] (-5, -5) grid (5, 5); +\draw[->] (-5, 0) -- (5.25, 0) node[above right] {\(x\)}; +\foreach \x in {-5, ..., -1, 1, 2, ..., 5} \draw(\x, -5) node[below] {\x}; +\draw[->] (0, -5) -- (0, 5.25) node[above right] {\(y\)}; +\foreach \y in {-5, ..., -1, 1, 2, ..., 5} \draw(-5, \y) node[left] {\y}; +\foreach \x in {-4.5, -4, ..., 4.5} { + \foreach \y in {-4.5, -4, ..., 4.5} { + \ifthenelse{\equal{\x}{0} \AND \equal{\y}{0}}{}{ + \draw[thick, blue, ->] + (\x, \y) -- + ( + {\x + 0.4 * \y / sqrt(\x * \x + \y * \y)}, + {\y - 0.4 * \x / sqrt(\x * \x + \y * \y)} + ); + } + } +} END_TIKZ #:% section = statement -#: This shows the vector field graph. +#: Insert the graph into the problem text using the `PGML` image syntax. +#: +#: Note that the alternate text given here is not sufficient to appropriately +#: describe the image for screen reader users. BEGIN_PGML -This is a direction field for -[``\vec{v} = \left< y, -x \right>``] +This is a direction field for [`\vec{v} = \left`]. ->> [@ image($graph, width=> 400) @]* << +>>[!a direction field!]{$graph}{400}<< END_PGML #:% section = solution diff --git a/tutorial/sample-problems/VectorCalc/RectangularGraph3D.pg b/tutorial/sample-problems/VectorCalc/RectangularGraph3D.pg index 62c30ba1c1..9412334734 100644 --- a/tutorial/sample-problems/VectorCalc/RectangularGraph3D.pg +++ b/tutorial/sample-problems/VectorCalc/RectangularGraph3D.pg @@ -18,39 +18,40 @@ #:% categories = [graph] #:% section = preamble -#: The dynamic graph is generated with `plotly3D.pl`, -#: so this is needed. +#: The dynamic graph is generated with PODLINK('plotly3D.pl'). DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'plotly3D.pl', 'PGcourse.pl'); -#:% section=setup -#: We generate the plot parametrically for the function `z=x^2+y^2` with the -#: `addSurface` function. Since `x` and `y` are generally used with -#: rectangular coordinates, we set them with the -#:```{#vars .perl} -#:variables => ['x','y'] -#:``` +#:% section = setup +#: Generate the plot parametrically for the function `z = x^2 + y^2` with the +#: `addSurface` function. The variables `x` and `y` are used as is customary for +#: rectangular coordinates. Since `x` and `y` are not the default variables used +#: by a graph created with the `addSurface` method, the variables must be +#: specified. #: -#: See PODLINK('plotly3D.pl') for more information on options. +#: See PODLINK('plotly3D.pl') for more information, as well as +#: PROBLINK('CylindricalGraph3D.pg') for details on using the `addSurface` +#: method of a `Graph3D` object. -$gr = Graph3D(); -$gr->addSurface( - [ 'x', 'y', 'x^2+y^2' ], +$graph = Graph3D(); +$graph->addSurface( + [ 'x', 'y', 'x^2 + y^2' ], [ -3, 3, 30 ], [ -3, 3, 30 ], variables => [ 'x', 'y' ] ); -#:% section=statement -#: This shows how to add a plot to the problem. +#:% section = statement +#: If `$graph` is a `Graph3D` object, then insert its graph into the problem +#: with `[@ $graph->Print @]*`. BEGIN_PGML -The following is the plot of [`z=x^2+y^2`] +The following is the plot of [`z = x^2 + y^2`] -[@ $gr->Print @]* +[@ $graph->Print @]* END_PGML -#:% section=solution +#:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION diff --git a/tutorial/sample-problems/VectorCalc/VectorFieldGraph2D.pg b/tutorial/sample-problems/VectorCalc/VectorFieldGraph2D.pg index bea1fa6801..bbd1837968 100644 --- a/tutorial/sample-problems/VectorCalc/VectorFieldGraph2D.pg +++ b/tutorial/sample-problems/VectorCalc/VectorFieldGraph2D.pg @@ -17,50 +17,59 @@ #:% categories = [graph] #:% see_also = [DirectionField.pg] -#:% section=preamble -#: The macro `PGtikz.pl` is used to produced the vector field. +#:% section = preamble +#: The macro PODLINK('PGtikz.pl') is used to produced the vector field graph. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); -#:% section=setup -#: The vector field is created directly. The vector field is -#: which is not defined at the origin. Therefore, -#: we don't draw the vector there and use the `ifthen` package to load the `\ifthenelse` -#: latex command. +#:% section = setup +#: The graph of the vector field is created. The vector field is +#: `` which is not defined at the origin. +#: The `ifthen` package is loaded and the `\ifthenelse` command from that +#: package used to skip the vector at the origin. #: #: The vector is created using the `\draw` command. $graph = createTikZImage(); $graph->texPackages(['ifthen']); $graph->BEGIN_TIKZ \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-6,-6) rectangle (6,6); -\draw[dotted] (-5,-5) grid (5,5); -\draw[->] (-5,0) -- (5.25,0) node[above right] {\(x\)}; -\foreach \x in {-5,...,-1,1,2,...,5} \draw(\x,-5) node [below] {\x}; -\draw[->] (0,-5) -- (0,5.25) node[above right] {\(y\)}; -\foreach \y in {-5,...,-1,1,2,...,5} \draw(-5,\y) node [left] {\y}; -\foreach \x in {-4,...,4} { - \foreach \y in {-4,...,4} { - \ifthenelse{\equal{\x}{0} \AND \equal{\y}{0}}{}{ - \draw[thick, blue,->] (\x,\y) -- ({\x+\x/(\x*\x+\y*\y)},{\y+\y/(\x*\x+\y*\y)}); - } - } + draw = LightBlue, + fill = white, + rounded corners = 10pt, + thick, + use as bounding box +] (-6, -6) rectangle (6, 6); +\draw[dotted] (-5, -5) grid (5, 5); +\draw[->] (-5, 0) -- (5.25, 0) node[above right] {\(x\)}; +\foreach \x in {-5, ..., -1, 1, 2, ..., 5} \draw (\x, -5) node[below] {\x}; +\draw[->] (0, -5) -- (0, 5.25) node[above right] {\(y\)}; +\foreach \y in {-5, ..., -1, 1, 2, ..., 5} \draw (-5, \y) node[left] {\y}; +\foreach \x in {-4, ..., 4} { + \foreach \y in {-4, ..., 4} { + \ifthenelse{\equal{\x}{0} \AND \equal{\y}{0}}{}{ + \draw[thick, blue, ->] + (\x, \y) -- + ( + {\x + \x / (\x * \x + \y * \y)}, + {\y + \y / (\x * \x + \y * \y)} + ); + } + } } END_TIKZ -#:% section=statement -#: This shows the vector field graph. +#:% section = statement +#: Insert the graph into the problem text using the `PGML` image syntax. +#: +#: Note that the alternate text given here is not sufficient to appropriately +#: describe the image for screen reader users. BEGIN_PGML This is a velocity vector field for an explosion at the origin that decreases in speed the farther the distance is from the origin. [``\vec{v} = \left< \frac{x}{x^2+y^2}, \frac{y}{x^2+y^2} \right>``] ->> [@ image($graph, width=> 400) @]* << +>>[!a vector field!]{$graph}{400}<< END_PGML #:% section = solution diff --git a/tutorial/sample-problems/VectorCalc/VectorFieldGraph3D/VectorFieldGraph3D1.pg b/tutorial/sample-problems/VectorCalc/VectorFieldGraph3D/VectorFieldGraph3D1.pg index e3d1d3e6d0..b8d6869353 100644 --- a/tutorial/sample-problems/VectorCalc/VectorFieldGraph3D/VectorFieldGraph3D1.pg +++ b/tutorial/sample-problems/VectorCalc/VectorFieldGraph3D/VectorFieldGraph3D1.pg @@ -16,25 +16,30 @@ #:% type = Sample #:% categories = [graph] -#:% section=preamble -#: We need to include the macros file `LiveGraphicsVectorField3D.pl`. +#:% section = preamble +#: Include the PODLINK('LiveGraphicsVectorField3D.pl') macro. Note that the +#: PODLINK('LiveGraphics3D.pl') macro is loaded by that macro and provides the +#: `Live3Ddata` method. DOCUMENT(); loadMacros( 'PGstandard.pl', 'PGML.pl', 'LiveGraphicsVectorField3D.pl', 'PGcourse.pl' ); -#:% section=setup -#: The `VectorField3D()` routine returns a string of plot data consisting of a list of line -#: segments (the vectors) along with other plot options. The arguments `RGBColor[a,b,c]` are -#: numbers a, b, and c between 0 and 1 inclusive. You can uniformly scale all of the vectors -#: in the vector field by the same amount using vectorscale. The outputtype feature controls -#: how much of the string of plot data is generated, and setting it equal to 4 generates all -#: of the plot information necessary to be displayed. +#:% section = setup +#: The `VectorField3D` method returns a string of plot data suitable for use +#: with the `Live3Ddata` method consisting of a list of line segments (the +#: vectors) along with other plot options. The arguments `a`, `b`, and `c` in +#: `RGBColor[a, b, c]` are numbers from 0 to 1. You can uniformly scale all of +#: the vectors in the vector field by the same amount using `vectorscale`. #: -#: Setting outputtype to something other than 4 will require you to read the source code of -#: `LiveGraphicsVectorField3D.pl` and familiarize yourself with the details of the LiveGraphics3D -#: javascript applet. +#: The `outputtype` feature controls how much of the string of plot data is +#: generated. Setting it equal to 4 generates all of the plot information +#: necessary to generate a single graph. Setting `outputtype` to something other +#: than 4 is used to combine output from multiple live graphics 3D methods into +#: a single graph. +#: +#: See PODLINK('LiveGraphicsVectorField3D.pl') for more details. Context()->variables->are(x => 'Real', y => 'Real', z => 'Real'); $plot = VectorField3D( @@ -57,30 +62,29 @@ $plot = VectorField3D( xaxislabel => 'X', yaxislabel => 'Y', zaxislabel => 'Z', - vectorcolor => "RGBColor[0.0,0.0,1.0]", + vectorcolor => 'RGBColor[0.0, 0.0, 1.0]', vectorscale => 0.2, vectorthickness => 0.01, outputtype => 4, ); -#:% section=statement -#: This just shows the plot. +#:% section = statement +#: Show the plot. BEGIN_PGML - -The following is the 3D vector field give by ->> [`` \vec{v} = \left ``] << +The following is the 3D vector field given by +>> [``\vec{v} = \left``] << >> [@ Live3Ddata( $plot, - image => alias("exploding-vector-field.png"), - size => [400,400], - tex_size => 600, + image => alias('exploding-vector-field.png'), + size => [ 400, 400 ], + tex_size => 600, tex_center => 1, - scale => 1.25, -); @]* << + scale => 1.25, +) @]* << END_PGML -#:% section=solution +#:% section = solution BEGIN_PGML_SOLUTION Solution explanation goes here. END_PGML_SOLUTION diff --git a/tutorial/sample-problems/VectorCalc/VectorLineSegment1.pg b/tutorial/sample-problems/VectorCalc/VectorLineSegment1.pg index ac8d3976d3..996b4bf5b3 100644 --- a/tutorial/sample-problems/VectorCalc/VectorLineSegment1.pg +++ b/tutorial/sample-problems/VectorCalc/VectorLineSegment1.pg @@ -13,16 +13,16 @@ # created as a full problem by Peter Staab 2023.06.02 -#:% name = Vector-valued Parametric Line Segment--General +#:% name = Vector-valued Parametric Line Segment (General) #:% type = [technique, sample] #:% categories = vector #:% subject = Vector Calculus #:% section = preamble -#: The macro -#: `parseParametricLine.pl` provides the `ParametricLine` function which -#: will be the answer. The `parserMultiAnswer.pl` is needed since the -#: answer blanks are interdependent. +#: The PODLINK('parseParametricLine.pl') macro provides the `ParametricLine` +#: function which us used to check the answer. The +#: PODLINK('parserMultiAnswer.pl') macro is needed since the answers are +#: interdependent. DOCUMENT(); loadMacros( 'PGstandard.pl', 'PGML.pl', @@ -31,41 +31,42 @@ loadMacros( ); #:% section = setup -#: We create a MutiAnswer answer checker that will evaluate the students -#: vector parametric equation at the starting and ending times provided -#: by the student. For example, both of the student answers `(4,0) + t<-4,2>` -#: for `t` between `0` and `1`, and `(4,0) + t<-2,1>` for t between `0` and `2` -#: will be marked correct. -Context("Vector"); -Context()->variables->are(t => "Real"); +#: Create a `MutiAnswer` object that is used to evaluate the student's vector +#: equation at the starting and ending times provided by the student. For +#: example, both of the student answers `<4, 0> + t<-4, 2>` for `t` between `0` +#: and `1`, and `<4,0> + t<-2,1>` for t between `0` and `2` will be marked +#: correct. +Context('Vector'); +Context()->variables->are(t => 'Real'); $P = Point(4, 0); $Q = Point(0, 2); $V = Vector(-4, 2); -$t = Formula("t"); +$t = Formula('t'); $line = Vector("$P + $t * $V"); -$multians = MultiAnswer($line, Real("0"), Real("1"))->with( +$multians = MultiAnswer($line, Real(0), Real(1))->with( singleResult => 0, checker => sub { my ($correct, $student, $ansHash) = @_; - my ($linestu, $astu, $bstu) = @{$student}; - my ($linecor, $acor, $bcor) = @{$correct}; + + my ($linecor, $acor, $bcor) = @$correct; + my ($linestu, $astu, $bstu) = @$student; if ((ParametricLine("$line") == $linestu) - && ($linestu->eval(t => $astu) == $line->eval(t => "0")) - && ($linestu->eval(t => $bstu) == $line->eval(t => "1"))) + && ($linestu->eval(t => $astu) == $line->eval(t => 0)) + && ($linestu->eval(t => $bstu) == $line->eval(t => 1))) { return [ 1, 1, 1 ]; } elsif ((ParametricLine("$line") == $linestu) - && ($linestu->eval(t => $astu) == $line->eval(t => "0"))) + && ($linestu->eval(t => $astu) == $line->eval(t => 0))) { return [ 1, 1, 0 ]; } elsif ((ParametricLine("$line") == $linestu) - && ($linestu->eval(t => $bstu) == $line->eval(t => "1"))) + && ($linestu->eval(t => $bstu) == $line->eval(t => 1))) { return [ 1, 0, 1 ]; @@ -80,19 +81,13 @@ $multians = MultiAnswer($line, Real("0"), Real("1"))->with( ); #:% section = statement -#: Since the three answer blanks depend on each other, we use `$multians` -#: for each answer blank. +#: Use `$multians` for each answer. BEGIN_PGML -Find a vector parametric equation for the line -segment from the point [`P = [$P]`] -to [`Q = [$Q]`]. - -[` \vec{r}(t) = `] [__]{$multians} +Find a vector equation for the line segment from the point [`P = [$P]`] to +[`Q = [$Q]`]. -for -[__]{$multians} -[` \leq t \leq `] -[__]{$multians} +[`\vec{r}(t) =`] [__]{$multians}{15} +for [_]{$multians}{4} [` \leq t \leq `] [_]{$multians}{4} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/VectorCalc/VectorLineSegment2.pg b/tutorial/sample-problems/VectorCalc/VectorLineSegment2.pg index 21e8f450b8..51f18ed6e1 100644 --- a/tutorial/sample-problems/VectorCalc/VectorLineSegment2.pg +++ b/tutorial/sample-problems/VectorCalc/VectorLineSegment2.pg @@ -13,39 +13,44 @@ # created as a full problem by Peter Staab 2023.06.02 -#:% name = Vector-valued Parametric Line Segment--Specific +#:% name = Vector-valued Parametric Line Segment (Specific) #:% type = [technique, sample] #:% categories = vector #:% subject = Vector Calculus #:% section = preamble -#: The macro `parseVectorUtils.pl` provides random points and vectors. +#: The PODLINK('parseVectorUtils.pl') macro is loaded for the +#: `non_zero_point3D` and `non_zero_vector3D` methods. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserVectorUtils.pl', 'PGcourse.pl'); #:% section = setup -#: In this case, there is only a single answer, so we can just enter the -#: correct expression for the vector-valued function. +#: A random point is generated with the `non_zero_point3D` method, a random +#: displacement vector is generated with the `non_zero_vector3D` method, and a +#: random speed is generated. +#: +#: Then the answer is computed from those values. Context("Vector"); Context()->variables->are(t => "Real"); $P = non_zero_point3D(); $disp = non_zero_vector3D(); -$Q = Point($P + $disp); $speed = random(3, 9, 1); -$ans = Compute("$P + $speed *t * $disp/norm($disp)"); +$ans = Compute("$P + $speed * t * $disp / norm($disp)"); #:% section = statement BEGIN_PGML -A particle starts at the point [` P = [$P] `] -when [` t = 0 `] and moves along a straight line -toward [` Q = [$Q] `] at a speed of [` [$speed] `] -cm/sec. Assume that [`x, y,`] and [`z`] are measured -in cm. Do not enter units with your answers. +A particle starts at the point [`P = [$P]`] when [`t = 0`] and moves along a +straight line toward [`Q = [@ Point($P + $disp) @]`] at a speed of [`[$speed]`] +centimeters per second. Assume that [`x`], [`y`], and [`z`] are measured in +centimeters. + +Find a vector equation for the position of the object. -Find the vector parametric equation for the position of the object. [` \vec{r}(t) = `] [____]{$ans} + +_Do not include units in your answer._ END_PGML #:% section = solution diff --git a/tutorial/sample-problems/VectorCalc/VectorOperations.pg b/tutorial/sample-problems/VectorCalc/VectorOperations.pg index ce8c1f403f..deb55c7559 100644 --- a/tutorial/sample-problems/VectorCalc/VectorOperations.pg +++ b/tutorial/sample-problems/VectorCalc/VectorOperations.pg @@ -15,20 +15,29 @@ #:% subject = Vector Calculus #:% type = Sample -#:% section=preamble -#: We load `parserVectorUtils.pl` to have access to functions on vectors like norm and unit. +#:% section = preamble +#: Load PODLINK('parserVectorUtils.pl') for the `non_zero_vector3D` method. DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'parserVectorUtils.pl', 'PGcourse.pl'); -#:% section=setup -#: We use `non_zero_vector3D(low,high,increment)` to randomly generate some vectors. Calling `$U->value` -#: returns a (Perl) array of numbers. (Note that `->value` does not work on a vector whose -#: components are non-constant formulas.) MathObjects defines the operators `.` and `x` to be the -#: dot product and cross product when they occur between two vectors (that is, these operations -#: are overloaded). The functions norm and unit calculate the length of a vector and a unit -#: vector in the same direction. We undefine the dot and cross product as well as the functions -#: norm and unit so that students cannot enter them in their answers. +#:% section = setup +#: Use `non_zero_vector3D(low, high, increment)` to randomly generate two +#: vectors. +#: +#: Calling `$U->value` returns a (Perl) array of numbers which are the +#: components of the vector `$U`. (Note that `->value` does not work on a vector +#: whose components are non-constant formulas.) +#: +#: The operators `.` and `x` are defined to be the dot product and cross +#: product, respectively, if the operands are vectors. +#: +#: The `norm` function computes the length of a vector, and the `unit` function +#: computes the unit vector in the same direction as a vector. +#: +#: The dot and cross products as well as all `Vector` functions (including +#: `norm` and `unit`) are undefined so that students cannot enter them in +#: answers. Context('Vector'); $U = non_zero_vector3D(-9, 9, 1); @@ -44,28 +53,29 @@ $Vlength = norm($V); $Vunit = unit($V); # Prevent students from entering the dot and cross products, -# and the vector functions norm and unit. - -Context()->operators->undefine('.', "><"); +# and using the vector functions such as norm and unit. +Context()->operators->undefine('.', '><'); Context()->functions->disable('Vector'); BEGIN_PGML -Suppose [` \vec{u} = [$U] `] and [` \vec{v} = [$V] `]. +Suppose [`\vec{u} = [$U]`] and [`\vec{v} = [$V]`]. -a. The second component of [` \vec{u} `] is [___________]{$Ucomp2} +a. The second component of [`\vec{u}`] is [_]{$Ucomp2}{5} -b. [` \vec{u} \cdot \vec{v} = `] [___________]{$UdotV} +b. [`\vec{u} \cdot \vec{v} =`] [_]{$UdotV}{5} -c. [` \vec{u} \times \vec{v} = `] [________________]{$UcrossV} +c. [`\vec{u} \times \vec{v} =`] [_]{$UcrossV}{15} -d. [` \left\| \vec{v} \right\| = `] [___________]{$Vlength} +d. [`\left\|\vec{v}\right\| =`] [_]{$Vlength}{10} -e. Enter a unit vector in the direction of [` \vec{v} `]. [______________]{$Vunit} +e. Enter a unit vector in the direction of [`\vec{v}`]. + [_]{$Vunit}{15} -f. Enter a vector parallel to [` \vec{v} `]. [________________]{$V->cmp( parallel=>1 )} +f. Enter a vector parallel to [`\vec{v}`]. + [_]{$V->cmp(parallel => 1)}{15} -g. Enter a vector in the same direction as [` \vec{v} `]. -[__________]{$V->cmp( parallel=>1, sameDirection=>1 )} +g. Enter a vector in the same direction as [`\vec{v}`]. + [_]{$V->cmp( parallel => 1, sameDirection => 1 )}{15} END_PGML #:% section = solution diff --git a/tutorial/sample-problems/VectorCalc/VectorParametricLine.pg b/tutorial/sample-problems/VectorCalc/VectorParametricLine.pg index 0a326d124f..2e1cce8997 100644 --- a/tutorial/sample-problems/VectorCalc/VectorParametricLine.pg +++ b/tutorial/sample-problems/VectorCalc/VectorParametricLine.pg @@ -19,9 +19,9 @@ #:% subject = Vector Calculus #:% section = preamble -#: The macro `parseVectorUtils.pl` provides random points. The macro -#: `parseParametricLine.pl` provides the `ParametricLine` function which -#: will be the answer. +#: The PODLINK('parseVectorUtils.pl') macro provides the `non_zero_point3D` and +#: `non_zero_vector3D` methods. The macro PODLINK('parseParametricLine.pl') +#: provides the `ParametricLine` function used for the answer. DOCUMENT(); loadMacros( 'PGstandard.pl', 'PGML.pl', @@ -30,24 +30,26 @@ loadMacros( ); #:% section = setup -#: We randomize two points in three-dimensional space, `P` and `Q`, a -#: displacement vector between them, and a speed to travel between them. +#: Generate a random three dimensional initial point `$P` using the +#: `non_zero_point3D` method, and a random three dimensional displacement vector +#: `$disp` using the `non_zero_vector3D` method. Use the `ParametricLine` method +#: to create the answer which is the parametric line through `$P` in the +#: direction of `$disp`. Context('Vector'); Context()->variables->are(t => 'Real'); $P = non_zero_point3D(); $disp = non_zero_vector3D(); -$Q = Point($P + $disp); -$line = ParametricLine("$P+t *$disp"); +$line = ParametricLine("$P + t * $disp"); #:% section = statement BEGIN_PGML -Find a vector parametric equation for the -line through points [` P = [$P] `] and [` Q = [$Q] `]. +Find a vector parametric equation for the line through points [`P = [$P]`] and +[`Q = [@ Point($P + $disp) @]`]. -[` \vec{r}(t) = `] [___]{$line} +[`\vec{r}(t) =`] [_]{$line}{15} -A vector should be entered like . +Give A vector in the form [||]*. END_PGML #:% section = solution diff --git a/tutorial/sample-problems/VectorCalc/Vectors.pg b/tutorial/sample-problems/VectorCalc/Vectors.pg index 9640916fb2..e02ee9a039 100644 --- a/tutorial/sample-problems/VectorCalc/Vectors.pg +++ b/tutorial/sample-problems/VectorCalc/Vectors.pg @@ -22,60 +22,60 @@ DOCUMENT(); loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); -#:% section=setup -#: We indicate that we are working in a vector context by setting -#: `Context('Vector')`. If we want to have vectors displayed, by default, as a -#: sum of `i,j,k` components, we can set the `ijk` flag in the `Context`. This is -#: commented out here; uncommenting it would result in the vector `$v1` here -#: being shown as `$v1 = i + 3j` instead of `$v1 = <1,3>`, etc. Similarly, -#: if we wanted to change the default display of the `i, j` and `k` vectors, -#: say to have them display with overset arrows, we can redefine the TeX -#: formatting of those constants, as shown in the second comment. +#:% section = setup +#: Select the `Vector` context to work with vectors. #: -#: Then, we can define vectors as we might expect: either with the `Vector` or -#: `Compute` constructors, or by using the predefined vector constants -#: `i, j` and `k`. Any vector constructed with `i, j` and/or `k` will be -#: three-dimensional vectors even if the vector `k` is not used. That is -#: `Vector('<1,2>')` is not equal to `i+2j`. +#: If vectors are to be displayed as a sum of `i`, `j`, `k` components, we can +#: set the `ijk` context flag. This would result in the vector `$v1` in this +#: example being shown as `$v1 = i + 3j` instead of `$v1 = <1, 3>`, for example. #: -#: Also if we define the vector using the constants i, j and k, as in the -#: definition of `$v3 `here, then the default display of that vector will be in -#: `i,j,k `format even if we don't set the corresponding Context flag. +#: If it is desired to change how the `i`, `j`, and `k` vectors are displayed, +#: say to have them displayed with over set arrows instead of bold, the TeX +#: formatting of those constants can be redefined as shown in the second comment. +#: +#: Vectors are defined either with the `Vector` or `Compute` constructors, or by +#: using the predefined vector constants `i, j` and `k`. Any vector constructed +#: with `i`, `j`, and `k` will be a three-dimensional vector even if the vector +#: `k` is not used. That means `Vector('<1, 2>')` is not equal to `i + 2j`. +#: +#: If a vector is defined using the constants `i`, `j` and `k`, as in the +#: definition of `$v3`, then the default display of that vector will be in +#: `i`, `j`, `k` format even if the `ijk` context flag is not set. #: #: To explicitly require that the vectors be two-dimensional rather than -#: three-dimensional, we would use `Context('Vector2D')` instead of -#: `Context('Vector')`. +#: three-dimensional, use the `Vector2D` context instead of the `Vector` +#: context. +#: +#: The components of vectors are available as an array returned by `$v->value`. +#: Thus, the three components of the vector `$v3` can be stored in the array +#: `@v3comp` by calling `@v3comp = $v3->value`. The first component can then be +#: accessed using `$v3comp[0]`, the second component using `$v3comp[1]`, and the +#: third component using `$v3comp[2]`. #: -#: The components of MathObjects vectors are available as an array from -#: `$v->value;` thus, we could save the three components of the vector `$v3` -#: in the array `@v3comp` using `@v3comp = $v3->value`. -#: Then, we can access the first component using -#: `$v3comp[0]`, the second component using `$v3comp[1]`, etc. -#: Better still, to get the first component of the vector `$v3` we could -#: use `$v3->extract(1)` instead of `($v3->value)[0]`. Note that the -#: index of `extract` starts at 1, not 0. +#: To obtain a single component of a vector use the `extract` method. For +#: example, to obtain the first component of `$v3` use `$v3->extract(1)`. Note +#: that the index of `extract` starts at 1, not 0. #: -#: Lastly, there is other functionality associated with Vectors. See the -#: WeBWorK wiki page on -#: [vectors](https://webwork.maa.org/wiki/Vector_(MathObject_Class)) +#: See the [vector](https://webwork.maa.org/wiki/Vector_(MathObject_Class)) +#: wiki page for more information about MathObject vectors. Context('Vector'); -## display vectors in ijk format -# Context()->flags->set( ijk=>1 ); -## set the appearance of the ijk vectors -## this sets them to be overset with -## vector arrows, instead of boldface -# Context()->constants->set( -# i => {TeX => "\mathit{\vec i}"}, -# j => {TeX => "\mathit{\vec j}"}, -# k => {TeX => "\mathit{\vec k}"}, -# ); - -$v1 = Vector("<1,3>"); -$v2 = Compute("<-3,1>"); +# Uncomment to display vectors in ijk format. +#Context()->flags->set(ijk => 1); + +# Uncomment to change the appearance of the ijk vectors. This sets them to be +# overset with vector arrows, instead of bold. +#Context()->constants->set( +# i => { TeX => "\mathit{\vec i}" }, +# j => { TeX => "\mathit{\vec j}" }, +# k => { TeX => "\mathit{\vec k}" }, +#); + +$v1 = Vector('<1, 3>'); +$v2 = Compute('<-3, 1>'); $v3 = 3 * i + 2 * j - 4 * k; $v4 = Vector(1, 1, 0); -# create an array of the components of $v3 +# Create an array of the components of $v3. @v3comp = $v3->value; $a = 3 * i + j; @@ -87,28 +87,28 @@ $v6 = $v3 x $v4; # cross product $v3->isParallel($v4); # returns 1 if parallel, 0 if skew BEGIN_PGML -[`\vec{v}_1=[$v1]`] +[`\vec{v}_1 = [$v1]`] -[`\vec{v}_2=[$v2]`] +[`\vec{v}_2 = [$v2]`] -[`\vec{v}_3=[$v3]`] +[`\vec{v}_3 = [$v3]`] -[`\vec{v}_4=[$v4]`] +[`\vec{v}_4 = [$v4]`] [`\vec{a} = [$a]`] -[`\vec{v}_1+\vec{v}_2 = [$v1+$v2]`] +[`\vec{v}_1 + \vec{v}_2 = [$v1 + $v2]`] [`||\vec{v}_3|| = [$c]`] [`[$v5]`] is a unit vector in the same direction as [`[$v3]`] -[`\vec{v}_1 \cdot \vec{v}_2=[$d]`] +[`\vec{v}_1 \cdot \vec{v}_2 = [$d]`] -[`\vec{v}_3 \times \vec{v}_4=[$v6]`] +[`\vec{v}_3 \times \vec{v}_4 = [$v6]`] -[`\vec{v}_3`] [@ $v3->isParallel($v4) ? 'is' : 'is not' @] -parallel to [`\vec{v}_4`] +[`\vec{v}_3`] [@ $v3->isParallel($v4) ? 'is' : 'is not' @] parallel to +[`\vec{v}_4`] The first element of [`\vec{v}_3`] is [@ $v3->extract(1) @] diff --git a/tutorial/sample-problems/problem-techniques/AnswerHints.pg b/tutorial/sample-problems/problem-techniques/AnswerHints.pg deleted file mode 100644 index 67a3a9a749..0000000000 --- a/tutorial/sample-problems/problem-techniques/AnswerHints.pg +++ /dev/null @@ -1,90 +0,0 @@ -## DESCRIPTION -## Shows how to provide answer hints to students. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('answer', 'hints') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Answer Hints -#:% type=technique -#:% categories=[answer, knowls] -#:% section=preamble -#: Make sure the `answerHints.pl` macro is loaded. -DOCUMENT(); - -loadMacros('PGstandard.pl', 'PGML.pl', 'answerHints.pl', 'PGcourse.pl'); - -#:% section = setup -#: To generate specific, customized answer hints for particular answers, we use -#: the `AnswerHints` postfilter provided by `answerHints.pl`. The answer hints -#: should be provided by a particular answer, followed by the hash table -#: association operator `=>`, followed by a string that will show up in the -#: messages portion of the answer preview and feedback box when students submit -#: their answers. You may include as many answer and hint pairs as you like, and -#: even use subroutines in them (see answerHints.pl for more on this). -#: -#: If the same error message should be given for several different answers, you -#: should make a square bracketed list of those answers. For example, if the -#: variables x, t, and u were all in the context, we could use -#: -#:```{#answer-hint-multiple .perl} -#: $ans->cmp->withPostFilter(AnswerHints( -#: [ Compute('6 t'), Compute('6 x') ] => -#: 'Are you using the correct variable?' -#: )) -#:``` -#: -#: If the MathObjects answer evaluator normally generates a message, the default -#: is not to change a message that is already in place. To override a message -#: generated by a MathObjects answer evaluator, you should set -#: `replaceMessage => 1` as below. -#: -#:```{#answer-hint-single .perl} -#: $ans->cmp->withPostFilter(AnswerHints( -#: Compute('6 u') => [ 'Good work!', replaceMessage => 1 ] -#: )) -#:``` -Context()->variables->are(t => 'Real', u => 'Real'); - -$f = Formula('2 t'); -$ans = Formula('6 u')->cmp->withPostFilter(AnswerHints( - Formula('6 t') => 'Are you using the correct variable?', - Formula('6 u') => 'Good work!' -)); - -#:% section = statement -#: Knowls (little bits of knowledge) provide an always present link to a hint -#: that expand to reveal the hint when clicked on, and collapse to hide the hint -#: when clicked on again. -BEGIN_PGML -If [`f(t) = [$f]`], then [`f(3u)`] = [____]{$ans} - -[@ knowlLink( - 'Click for a hint', - value => 'Substitute \(3u\) wherever you see \(t\) in the formula for \(f\).' -) @]* -END_PGML - -#:% section = hint -#: This is a hint that is revealed to students only after a certain number of -#: submissions. The number of submissions before the hint is shown is specified -#: by the instructor in webwork settings. -#: -#: These hints are added to the problem in the block of text -#: between the commands `BEGIN_PGML_HINT` and `END_PGML_HINT`, which must be on -#: their own lines and at the start of a line. The hint will automatically -#: generate the word HINT: before the block of text that is the hint, so you -#: should not add it manually. -BEGIN_PGML_HINT -Substitute [`3u`] wherever you see [`t`] in the formula for [`f(t) = [$f]`]. -END_PGML_HINT - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/CalculatingWithPoints.pg b/tutorial/sample-problems/problem-techniques/CalculatingWithPoints.pg deleted file mode 100644 index 8d64940136..0000000000 --- a/tutorial/sample-problems/problem-techniques/CalculatingWithPoints.pg +++ /dev/null @@ -1,72 +0,0 @@ -## DESCRIPTION -## Perform calculations with Points. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('point') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Calculating with Points -#:% type = technique -#:% categories = [point] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: In the problem setup section of the file, we put the value of the subtraction -#: of two Points in two variables, `$d1`, the x coordinate, and `$d2`, the y -#: coordinate. This is achieved by calling `Point`'s value method, as shown. -#: -#: Alternative method: If you want to get only one of the coordinates of a -#: Point, you can use the extract method, for example: `$x = $point->extract(1);`. -#: This gets the first coordinate of `$point` and assigns it to the variable `$x`. -#: -#: We don't use `Context("Vector");` and `norm( $point[0] - $point[1] )` here -#: to determine length because we don't want to accept an answer like `|<5,7>-<7,8>|`. -#: -#: Alternative method: You can use `$length=norm( $point[0] - $point[1] );` -#: with `Context("Vector");` if you want to accept answers that are valid in -#: the `Vector` context (such as the absolute value of a vector). -#: -#: We need to put parentheses around `$d1` and `$d2` in the Compute expression -#: because if `$d1 = -6`, then `-6^2 = -36`, not `36`, as desired. However, if the -#: code is `($d1)^2` then that evaluates as `(-6)^2 = 36`, as desired. -Context("Point"); -@points = ( - Point(random(1, 5, 1), random(-5, -1, 1)), - Point(random(5, 10, 1), random(6, 11, 1)) -); - -# If $point[0] = (x1,y1) and $point[1] = (x2,y2), -# then the following makes $d1 = x1 - x2, $d2 = y1 - y2 -($d1, $d2) = ($points[0] - $points[1])->value; - -$length = Compute("sqrt( ($d1)^2+($d2)^2 )"); -$mid = ($points[1] + $points[0]) / 2; - -#:% section=statement -BEGIN_PGML -Consider the two points [` [$points[0]] `] -and [` [$points[1]] `]. - -a. The distance between them is: [_______]{$length} - -b. The midpoint of the line segment that joins them is: -[______]{$mid} -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/ConstantsInProblems.pg b/tutorial/sample-problems/problem-techniques/ConstantsInProblems.pg deleted file mode 100644 index 061eb91b8e..0000000000 --- a/tutorial/sample-problems/problem-techniques/ConstantsInProblems.pg +++ /dev/null @@ -1,57 +0,0 @@ -## DESCRIPTION -## Provides constants in a PG problem. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('constants') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Constants in Problems -#:% type = technique -#:% categories = [constant] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section = setup -#: No changes are needed in the tagging and description or initialization sections of the problem -#: file. In the problem set-up section, we add to the Context the constants we're going to use. -#: Here we define a constant `k`, and assign it a value that will be used when expressions involving -#: k are evaluated. Do not set `k=1`, because if you do, then `kx `and `x/k` are equivalent, for example. -#: Obviously, do not set `k=0.` -#: -#: In this case we specified `constants->add()`, so that the constant k is added to existing -#: constants in the problem. If we had used `constants->are()`, we would also remove all predefined -#: constants from the Context (in a manner similar to the use of `variables->add()` and -#: `variables->are()` when defining variables in a problem. -#: -#: One other tweak that we might want to put in here is to reset a Context flag so that students' -#: answers are not reduced to numerical values when they are previewed or submitted. This is done -#: by setting the formatStudentAnswer flag, as shown. -Context()->constants->add(k => 0.01); - -# This means that student answers are not reduced to the numerical value -# specified in the Context -Context()->flags->set(formatStudentAnswer => 'parsed'); - -$ans = Compute('k'); - -#:% section = statement -BEGIN_PGML -[`f(x) = x - k`] (where [`k > 0`] is constant) is zero when [`x =`] [___]{$ans} -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/CustomAnswerCheckers.pg b/tutorial/sample-problems/problem-techniques/CustomAnswerCheckers.pg deleted file mode 100644 index 1630b8e588..0000000000 --- a/tutorial/sample-problems/problem-techniques/CustomAnswerCheckers.pg +++ /dev/null @@ -1,76 +0,0 @@ -## DESCRIPTION -## This provides a custom answer checker. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('answer', 'custom') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Custom Answer Checker -#:% type = technique -#:% categories = [answer, custom] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section = setup -#: To set up the custom answer checker we will override the answer checker routine for the `MathObject` -#: that we're using to check the answer. Thus our answer object should be of the same type (e.g., `Real`, -#: `Formula`, etc.) as what we want the student to be entering. For example, here we're going to ask for a -#: value of x such that `cos(x) = cos($ans)`. Thus we set up the answer to be a real number. -#: -#: The `$ans` object overrides the `checker` part of the MathObject. This is a subroutine -#: which checks that the cosine of the student answer equals the cosine of the correct -#: answer. This also values like `pi/3`, `-pi/3` or `pi/3 + 2pi` to be considered correct. -#: -#: We can set an error message in the answer checker by using Value->Error("message"). This will -#: set the message that is displayed to the student and exit the checker with an incorrect -#: return value. For example: -#: -#:```{#value-error .perl} -#: $ans = Compute('pi/3')->cmp( -#: checker => sub { -#: my ($correct, $student, $ansHash) = @_; -#: Value->Error("Try again") if cos($student) == sqrt(3) / 2; -#: return cos($correct) == cos($student); -#: } -#: ); -#:``` -#: -#: Another handle tip for troubleshooting. To see all of the keys and values in the -#: `$ansHash` when the submit answers button is pressed, include this in your custom answer checker: -#: -#:```{.perl} -#:for my $key ( keys %{$ansHash} ) { -#: warn "key: $key, value: $ansHash->{$key}"; -#:} -#:``` -$ans = Compute('pi/3')->cmp( - checker => sub { - my ($correct, $student, $ansHash) = @_; - return cos($correct) == cos($student); - } -); -$val = Compute('cos(pi/3)'); - -#:% section = statement -BEGIN_PGML -Enter a value of [`x`] for which [`\cos(x) = [$val]`] - -[`x=`] [___]{$ans} -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/CustomAnswerListChecker.pg b/tutorial/sample-problems/problem-techniques/CustomAnswerListChecker.pg deleted file mode 100644 index 44b3b07f76..0000000000 --- a/tutorial/sample-problems/problem-techniques/CustomAnswerListChecker.pg +++ /dev/null @@ -1,106 +0,0 @@ -## DESCRIPTION -## This shows how to check an arbitrary list of student answers. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(Fitchburg State University) -## Author(Peter) -## MO(1) -## KEYWORDS('answer', 'custom', 'list') - -# Adapted from https://webwork.maa.org/wiki/Custom_Answer_Checkers_for_Lists - -#:% name = Custom Answer List Checker -#:% type = technique -#:% categories = [answer, custom, list] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section = setup -#: We expect the student answers to be points so we'll use the 'Point' context. -#: Provide a list of 3 points that will be shown as the correct answer. -#: -#: The important part of this problem is that we customize the `cmp` function of -#: the answer and since this is a list, we use a `list_checker` as shown. -#: -#: Most of the custom list checker is spent giving errors to specific situations -#: in the student answers. The part of the checker toward the bottom which -#: checks if the two coordinates and if they add to `$c`, then increase the -#: the `$score`. -#: -#: And lastly the checker ensures that the right number of points (3) is -#: entered. -Context('Point'); - -$c = random(4, 8); -$ans = List("(0,$c),($c,0),($c-1,1)")->cmp( - list_checker => sub { - my ($correct, $student, $ansHash, $value) = @_; - my $n = scalar(@$student); # number of student answers - my $score = 0; # number of correct student answers - my @errors; # stores error messages - - # Loop though the student answers - for my $i (0 .. $n - 1) { - my $ith = Value::List->NameForNumber($i + 1); - my $p = $student->[$i]; # i-th student answer - - # Check that the student's answer is a point - if ($p->type ne "Point") { - push(@errors, "Your $ith entry is not a point"); - next; - } - - # Check that the point hasn't been given before - for (my $j = 0, $used = 0; $j < $i; $j++) { - if ($student->[$j]->type eq "Point" && $student->[$j] == $p) - { - push(@errors, - "Your $ith point is the same as a previous one") - unless $ansHash->{isPreview}; - $used = 1; - last; - } - } - - # If not already used, check that it satisfies the equation - # and increase the score if so. - if (!$used) { - my ($a, $b) = $p->value; - if ($a + $b == $c) { - $score++; - } else { - push(@errors, "Your $ith point is not correct") - unless $ansHash->{isPreview}; - } - } - } - - # Check that there are the right number of answers - if (!$ansHash->{isPreview}) { - push(@errors, "You need to provide more points") if $n < 3; - push(@errors, "You have given too many points") if $n > 3; - } - return ($score, @errors); - } -); - -#:% section = statement -BEGIN_PGML -Enter three distinct points [`(x,y)`] that satisfy the equation [`x+y=[$c]`]: - -[____]{$ans} -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Any three points who's coordinates sum to [`[$c]`] are valid. For example -[`([$c],0),(0,[$c]),(1,[@ $c-1 @])`] -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/DataTables.pg b/tutorial/sample-problems/problem-techniques/DataTables.pg deleted file mode 100644 index f3b78865d4..0000000000 --- a/tutorial/sample-problems/problem-techniques/DataTables.pg +++ /dev/null @@ -1,91 +0,0 @@ -## DESCRIPTION -## This shows how to present and format a table for data. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2023) -## Institution(Fitchburg State University) -## Author(Peter Staab) -## MO(1) -## KEYWORDS('tutorial', 'table') - -#:% name = Data Table -#:% type = [technique] -#:% categories = table - -#:% section = preamble -#: This shows how to use the `DataTable` function in `niceTables.pl`. -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'niceTables.pl', 'PGcourse.pl'); - -#:% section=setup -#: We use the `DataTable` function from `niceTables.pl` to demonstrate some -#: of it's features. A `DataTable` is meant to display data in a -#: tabular format. -#: -#: The basic form of a `DataTable` is -#:```{#datatable .perl} -#:$table = DataTable([ -#: [row1], -#: [row2], -#: ... -#: [rowN] -#: ], -#: options); -#:``` -# where the data goes in as an array ref of array refs. The first row can -#: (and is often) used for a header row. We will show some of the options -#: here, but the full listing and explantion is in -#: PODLINK('the niceTables.pl POD','niceTables.pl') -#: -#: The first use is meant to show some tabular data and the data is filled -#: in randomly and stored in the `@rows` array. Also, note that since -#: a `DataTable` needs an array ref and typically in perl `\@rows` would be -#: the arrayref, however, in a PG problem, to get a `\`, we use `~~` -#: -#: The second table uses `ans_rule` for answer blanks. This example has -#: rows hardcoded instead of in a loop. -@rows = ([ '\(x\)', '\(y\)' ]); -for $i (1 .. 5) { - push(@rows, [ $i, random(1, 10) ]); -} - -$tab1 = DataTable(~~@rows); - -$a = non_zero_random(-4, 4); -$f = Compute("x/(x-[$a])")->reduce; - -$tab2 = DataTable( - [ - [ '\(x\)', '\(f(x)\)' ], - [ $a - 2, ans_rule(10) ], - [ $a - 1, ans_rule(10) ], - [ $a + 1, ans_rule(10) ], - [ $a + 2, ans_rule(10) ] - ], - align => '|r|l|', - horizontalrules => 1 -); - -BEGIN_PGML -Here's some data: - -[$tab1]* - -Fill out the following table for [``f(x)=[$f]``]. - -[$tab2]* -END_PGML - -for $i (-2, -1, 1, 2) { - ANS($f->eval(x => $a + $i)->cmp); -} - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/DifferentiatingFormulas.pg b/tutorial/sample-problems/problem-techniques/DifferentiatingFormulas.pg deleted file mode 100644 index dfa79873f9..0000000000 --- a/tutorial/sample-problems/problem-techniques/DifferentiatingFormulas.pg +++ /dev/null @@ -1,58 +0,0 @@ -## DESCRIPTION -## This shows how to check "arbitrary" conditions on the student's answer. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('differentiate') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Differentiating Formulas -#:% type = technique -#:% categories = [derivative] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: The `Numeric` context automatically defines `x` to be a variable, so we add the variable `y` -#: to the context. Then, we use the partial differentiation operator `D('var_name') ` -#: to take a partial derivative with respect to that variable. We can use the evaluate -#: feature as expected. -Context()->variables->add(y => "Real"); - -$a = random(2, 4, 1); -$f = Formula("x*y^2"); - -$fx = $f->D('x'); -$fxa = $fx->substitute(x => "$a"); -$fy = $f->D('y'); -$fyx = $fy->D('x')->reduce; - -#:% section=statement -BEGIN_PGML -Suppose [` f(x) = [$f] `]. Then - -a. [`` \frac{\partial f}{\partial x} ``] = [____]{$fx} - -b. [`f_x ([$a],y)= `] [____]{$fxa} - -c. [` f_y(x,y)=`] [____]{$fy} - -d. [`f_{yx} (x,y)= `] [___]{$fyx} - -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/DigitsTolType.pg b/tutorial/sample-problems/problem-techniques/DigitsTolType.pg deleted file mode 100644 index abd1654d36..0000000000 --- a/tutorial/sample-problems/problem-techniques/DigitsTolType.pg +++ /dev/null @@ -1,88 +0,0 @@ -## DESCRIPTION -## This describes an alternative way for determining the tolerance type based on the number of digits. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('answer', 'tolerance') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Number of Digits and Tolerance in Answers -#:% type = technique -#:% categories = [answer, tolerance] -#:% see_also = [NumericalTolerance.pg] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: * The `tolType => 'digits'` switches from the default `'relative' `tolerance -#: type to the `'digits'` tolerance type. -#: * The `tolerance => 3` sets the number of digits to check to 3. The default -#: value is acutally the default for other tolerance types, `0.001`, but any -#: tolerance that is between 0 and 1 is converted via `log10` and rounding to an -#: integer (in this case, to 3). -#: * The `tolTruncation` parameter is either 1 (true) or 0 (false). Its default -#: is 1. Details are explained below. -#: * The `tolExtraDigits` parameter sets the number of extra digits to examine -#: beyond the first tolerance digits. Its default value is 1. -#: This is explained below. -#: * The goal is that the student must enter at least the first tolerance -#: digits correctly. The last digits that they enter might be rounded -#: (always accepted) or truncated (only accepted if `tolTruncation` is true). -#: For example, if the correct answer is e=2.7182818... and tolerance is 3, -#: the student can answer with 2.72. Or they can answer with 2.71 if -#: `tolTruncation` is true. But for example 2.7 and 2.73 are not accepted. -#: -#: If the student enters additional digits, the first additional `tolExtraDigits` -#: digits are examined in the same manner. For example, if the correct answer -#: is `pi=3.1415926...` and default flag values are used, the student can answer -#: with 3.14, 3.141, 3.142, 3.1415, and even 3.1418 since that 8 is beyond the -#: extra digits checking. But for example 3.143 is not accepted, since the first -#: extra digit is not right. (And if `tolTruncation` is false, 3.141 would not be -#: accepted either.) -#: -#: Warning: this tolerance type also applies to formula comparisons. -#: For example if the answer is `2^x` and a student enters `e^(0.69x)`, this -#: will probably not be accepted. Random test values will be used for x to -#: make that comparison. For example if one of the test values is `x=2`, the -#: correct output is 4 and the student's output would be 3.9749... and this -#: would be declared as not a match, since the first three digits to not agree. -#: -#: Warning: this article is about using this tolerance type for comparison of -#: correct answers to student answers. But if this tolerance type is activated -#: for a context, it also applies to comparisons you might make in problem -#: setup code. It may be important to understand that it is not symmetric. -#: For example, under default conditions, `Real(4) == Real(3.995)` is false, -#: while `Real(3.995) == Real(4)` is true. The left operand is viewed as the -#: "correct" value. With `Real(4) == Real(3.995)`, that "5" violates the -#: `tolExtraDigits` checking. But with `Real(3.995) == Real(4)`, it is as if the -#: student entered 4.00 and has the first 3 digits correct accounting for -#: rounding. (Note that the default tolerance type relative is similarly -#: asymmetric, but the effect is more subtle. You can see it with -#: `Real(4) == Real(3.996001)` versus `Real(3.996001) == Real(4)`.) -Context()->flags->set(tolType => 'digits', tolerance => 3, tolTruncation => 1); -$ans = Real("pi"); -#:% section=statement -BEGIN_PGML - -This section is with [|tolTruncation|] set to true (1). The exact answer -is [`\pi`]. Enter 3.14, 3.15, 3.141, 3.142 to see if it accepts the answer. - -[`\pi=`][_]{$ans} - -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/DisableFunctions.pg b/tutorial/sample-problems/problem-techniques/DisableFunctions.pg deleted file mode 100644 index 8929f1487c..0000000000 --- a/tutorial/sample-problems/problem-techniques/DisableFunctions.pg +++ /dev/null @@ -1,93 +0,0 @@ -## DESCRIPTION -## This shows how to disable functions allowed in student answers. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('answer') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Disabling Functions in Student Answers -#:% type = technique -#:% categories = [answer] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: We can disable specific operations in the Context: in general, predefined -#: operations are `* / + - ! >< U ^ ** . ,`, for multiplication, division, -#: addition, subtraction, the factorial operation, the cross-product ><, -#: set union, exponentiation (both `^` and `**` give exponentiation), the dot -#: product, and list creation (,). -#: `` -#: After disabling the operation, they can be re-enabled with -#: `operators->redefine()`, e.g., `Context()->operators->redefine("^")`. We can -#: also remove operators with `operators->remove()`, but this is not recommended, -#: as it makes it completely unknown in the Context so that students -#: won't get helpful error messages if they try to use them. -#: -#: To disable specific functions in the Context, we similarly undefine them -#: from the predefined functions. The predefined functions are `sin, cos, tan, sec, -#: csc, cot, asin, acos, atan, asec, acsc, acot, sinh, cosh, tanh, sech, csch, -#: coth, asinh, acosh, atanh, asech, csch, acoth, ln, log, log10, exp, sqrt, -#: abs, int, sgn, atan2, norm, unit, arg, mod, Re, Im, conj`. -#: -#: In addition, classes of functions can be disabled with functions->disable(): -#: -#: * `Context()->functions->disable("Trig");` (disables all trig functions in both SimpleTrig and InverseTrig functions) -#: * `Context()->functions->disable("SimpleTrig");` (disables sin, cos, tan, sec, csc, cot) -#: * `Context()->functions->disable("InverseTrig");` (disables asin, acos, atan, asec, acsc, acot, atan2) -#: * `Context()->functions->disable("Hyperbolic");` (disables all hyperbolic functions in both SimpleHyperbolic and InverseHyperbolic functions) -#: * `Context()->functions->disable("SimpleHyperbolic");` (disables sinh, cosh, tanh, sech, csch, coth) -#: * `Context()->functions->disable("InverseHyperbolic");` (disables asinh, acosh, atanh, asech, acsch, acoth) -#: * `Context()->functions->disable("Numeric");` (disables ln, log, log10, exp, sqrt, abs, int, sgn) -#: * `Context()->functions->disable("Vector");` (disables norm, unit) -#: * `Context()->functions->disable("Complex");` (disables arg, mod, Re, Im, conj) -#: * `Context()->functions->disable("All");` -#: Alternatively, we could use the following syntax. -#: -#:```{#disable-functions .perl} -#: Parser::Context::Functions::Disable('All'); -#:``` -#: -# To disable specific operations in student answers, use the undefine -# method for the operations: -Context()->operators->undefine("^", "**"); - -# We can similarly disable specific functions with the following -Context()->functions->undefine("sin", "cos", "tan", "sqrt"); - -$ans = Compute("1/2"); - -# To disallow absolute value, disable abs(), -# sqrt and exponentiation (for sqrt(x^2) and (x^4)^(1/4)), and -# the parentheses | |, and give consistent -# error messages -Context()->functions->disable("abs", "sqrt"); -Context()->operators->undefine("^", "**"); -Context()->parens->remove("|"); -Context()->{error}{convert} = sub { - my $message = shift; - $message =~ s/Unexpected character '~~|'/Absolute value is not allowed/; - return $message; -}; - -#:% section=statement -BEGIN_PGML -Find the numerical value: [` \sin^2(\pi/4) = `] [____]{$ans} -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/EquationEvaluators.pg b/tutorial/sample-problems/problem-techniques/EquationEvaluators.pg deleted file mode 100644 index 4fa497bbe0..0000000000 --- a/tutorial/sample-problems/problem-techniques/EquationEvaluators.pg +++ /dev/null @@ -1,109 +0,0 @@ -## DESCRIPTION -## This code shows how to check student answers that are equations. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('answer', 'custom') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Implicit Equations Evaluators -#:% type = technique -#:% categories = [implicit] - -#:% section = preamble -#: In the initialization section, we need to include the macros file -#: `parserImplicitEquation.pl`. -DOCUMENT(); -loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'parserImplicitEquation.pl', 'PGcourse.pl' -); - -#:% section=setup -#: We specify that the `Context` should be `ImplicitEquation`, and define the -#: answer to be an equation. It's worth noting that there are a number of -#: `Context` settings that may be specifying for equation answers. -#: In particular, it's often important to pay attention to the limits -#: used by the answer checker. -#: -#: By default, the `ImplicitEquation` context defines the variables x and y. -#: To include other variables, it may be necessary to modify the context. -#: -#: Two other notes: if it's possible that a student's solution may evaluate -#: to true for the test points that are used in the answer checker, it may -#: be a good idea to specify what (x,y) solution values are used to check the -#: answer. This can be done in the `ImplicitEquation` initialization call, e.g., -#: -#:```{#specify-solutions .perl} -#: $eqn = ImplicitEquation("y = (x-1)^2", -#: solutions=>[[0,0],[1,1],[-1,1],[2,4],[-2,4]]); -#:``` -#: -#: And, for this type of answer checking it is more likely than for regular -#: formulas that the student will represent the function in a form that exceeds -#: the default problem checking tolerances, and so be marked as incorrect. To -#: correct this, it may be necessary to specify a tolerance; an absolute -#: tolerance can be set in the `ImplicitEquation` call, e.g., -#: -#:```{#set-tolerance .perl} -#: $eqn = ImplicitEquation("y = (x-1)^2", -#: tolerance=>0.0001); -#:``` -#: -#: It is possible to remove the error message -#: "Can't find any solutions to your equation" by remapping it to another -#: error message. The message has to be non-empty, but it can be just a -#: blank " ", as in -#: -#:```{#error-message .perl} -#: Context()->{error}{msg}{"Can't find any solutions to your equation"} = " "; -#:``` -#: -#: This will eliminate the error message (though the "messages" column will -#: be displayed in the answer table at the top of the problem, but no -#: message will be there). -#: -#: Another way to remove the error message "Can't find any solutions to your -#: equation" would be to use a post-filter to remove the message after the -#: answer has been graded. The answerHints.pl file provides one way to do this, -#: but it is probably just as easy to do it manually, as replacing the -#: `$ans` line with the following: -#: -#:```{#post-filter .perl} -#: $ans = ImplicitEquation("y = (x-1)^2")->cmp->withPostFilter(sub { -#: my $ans = shift; -#: $ans->{ans_message} = " " if $ans->{ans_message} eq -#: "Can't find any solutions to your equation"; -#: return $ans; -#: })); -#:``` -Context("ImplicitEquation"); -Context()->variables->set( - x => { limits => [ -2, 2 ] }, - y => { limits => [ 0, 4 ] } -); - -$ans = ImplicitEquation("y = (x-1)^2"); - -#:% section=statement -BEGIN_PGML -Give the equation of a shift of the -parabola [`y = x^2`] which is upward -opening and has its vertex at [`(1,0)`]. - -equation: [___]{$ans} -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/EvalVersusSubstitute.pg b/tutorial/sample-problems/problem-techniques/EvalVersusSubstitute.pg deleted file mode 100644 index 732ca19d70..0000000000 --- a/tutorial/sample-problems/problem-techniques/EvalVersusSubstitute.pg +++ /dev/null @@ -1,89 +0,0 @@ -## DESCRIPTION -## Shows the difference between eval and substitute for MathObject Formulas -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('formula', 'eval', 'substitute') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Eval Versus Substitute -#:% type = technique -#:% categories = [formula] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: First, we start with a formula using `Compute`. -#: -#: * `eval` returns a number, which is a `Value::Real` -#: * `substitute` returns a Formula which is a `Value::Formula` -#: -#: The `ref` command is a perl command to determine the reference type. In -#: these cases they are MathObjects with type `Value::Real` and `Value::Formula` -#: -#: For the function `$g`, the line `$g->eval(x => '3');` throws an error -#: because it is expected to return a number (`Value::Real`). -#: -#: The next section shows the effect of changing `reduceConstants` to 0 (false). -#: Notice that there is no effect on `eval`, the result is the same number -#: as before, however with `substitute` the value 3 is substituted in but left -#: within the formula, which is not reduced. -#: -#: Lastly, to show the effect of `reduceConstantFunctions`, if we set -#: `reduceConstants` back to 1 and `reduceConstantFunctions` to 0, we -#: see that the inside of the square root is reduced (because they are -#: constants), but the square root remains. -$f = Compute("sqrt(3x + 1)"); -$f1 = $f->eval(x => "3"); -$f2 = $f->substitute(x => "3"); - -Context()->variables->add(y => 'Real'); -$g = Compute('7xy'); -# This next line is an error. -# $g1 = $g->eval(x => '3'); -$g2 = $g->substitute(x => '3'); -$g3 = $g->eval(x => 3, y => -1); - -Context()->flags->set(reduceConstants => 0); -$f3 = $f->eval(x => 3); -$f4 = $f->substitute(x => 3); - -Context()->flags->set(reduceConstantFunctions => 0, reduceConstants => 1); -$f5 = $f->substitute(x => 3); - -#:% section=statement -BEGIN_PGML -This shows the difference between [|eval|] and [|substitute|]. First, we start -with a function [`[$f]`]: - -* [|$f->eval(x=>'3')|] returns [$f1] and the type is [@ ref $f1@] -* [|$f->substitute(x=>'3')|] returns [$f2] and the type is [@ ref $f2 @] - -Next, we do the same with the function [`[$g]`] - -* [|$g->eval(x=>'3')|] throws an error. -* [|$g->substitute(x=>'3')|] returns [$g2] and the type is [@ ref $g2 @] -* [|$g->eval(x=>'3', y => -1)|] returns [$g3] and the type is [@ ref $g3 @] - -If [|reduceConstants|] is set to 0 (False) in the flags, we get - -* [|$f->eval(x => 3)|] returns [$f3] -* [|$f->substitute(x => 3)|] returns [$f4] - -If [|reducedConstants|] is set back to 1 and [|reduceConstantFunctions|] is -set to 0, then - -* [|$f->substitute(x => 3)|] returns [$f5] - -END_PGML - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/ExtractingCoordinatesFromPoint.pg b/tutorial/sample-problems/problem-techniques/ExtractingCoordinatesFromPoint.pg deleted file mode 100644 index 72051da989..0000000000 --- a/tutorial/sample-problems/problem-techniques/ExtractingCoordinatesFromPoint.pg +++ /dev/null @@ -1,72 +0,0 @@ -## DESCRIPTION -## This problem shows how to extract the coordinates from a point. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('point', 'coordinates') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Extracting Coordinates from a Point -#:% type = technique -#:% categories = [point] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section = setup -#: In the problem setup section of the file, we put the value of the subtraction -#: of two `Points` in two variables, `$d1`, the `x`-coordinate, and `$d2`, -#: the `y`-coordinate. This is achieved by calling Point's value method, as shown. -#: -#: Alternative method: If you want to get only one of the coordinates of a -#: `Point`, you can use the extract method, for example: -#: `$x = $point->extract(1);`. This gets the first coordinate of `$point` -#: (x) and assigns it to the variable `$x`. -#: -#: We don't use `Context('Vector')`; and `norm( $point[0] - $point[1] )` here to -#: determine length because we don't want to accept an answer like `|<5,7>-<7,8>|`. -#: -#: Alternative method: You can use `$length=norm( $point[0] - $point[1] );` -#: with `Context('Vector');` if you want to accept answers that are valid in -#: the `Vector` context (such as the absolute value of a vector). -#: -#: We need to put parentheses around `$d1` and `$d2` in the `Compute` expression -#: because if `$d1 = -6`, then `-6^2 = -36`, not `36`, as desired. However, if -#: the code is `($d1)^2` then that evaluates as `(-6)^2 = 36`, as desired. -Context('Point'); - -push(@point, Point(random(1, 5), random(-5, -1))); -push(@point, Point(random(5, 10), random(6, 11))); - -# now we have two points, $point[0] = (x1,y1) -# and $point[1] = (x2,y2). -# the following makes $d1 = x1 - x2, $d2 = y1 - y2 -($d1, $d2) = ($point[0] - $point[1])->value; - -$length = Compute("sqrt( ($d1)^2+($d2)^2 )"); -$mid = ($point[1] + $point[0]) / 2; - -BEGIN_PGML -Consider the two points [`[$point[0]]`] and [`[$point[1]]`]. - -The distance between them is: [___]{$length} - -The midpoint of the line segment that joins them is: -[___]{$mid} - -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/FactoringAndExpanding.pg b/tutorial/sample-problems/problem-techniques/FactoringAndExpanding.pg deleted file mode 100644 index 25d52d4aa0..0000000000 --- a/tutorial/sample-problems/problem-techniques/FactoringAndExpanding.pg +++ /dev/null @@ -1,100 +0,0 @@ -## DESCRIPTION -## This shows how to check answers that require students to factor or expand -## a polynomial expression. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('answer', 'custom') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Factoring and Expanding Polynomials -#:% type = technique -#:% categories = [polynomials, factoring, expanding] - -#:% section = preamble -#: In the initialization section, we need to include the macros file -#: `contextLimitedPolynomial.pl`, `contextPolynomialFactors.pl` and -#: `contextLimitedPowers.pl`. -DOCUMENT(); -loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'contextLimitedPolynomial.pl', 'contextPolynomialFactors.pl', - 'contextLimitedPowers.pl', 'PGcourse.pl' -); - -#:% section=setup -#: To construct this quadratic, we choose a nice factored form `(x+$a)(x-$b)` and -#: from it we construct its vertex form `(a(x-h)^2+k)` and expanded form -#: `(ax^2+bx+c)`. -#: -#: For the expanded form we use the `LimitedPolynomial-Strict` context, -#: construct the coefficients `$p[0]` and `$p[1]` as Perl reals, and then -#: construct `$expandedform` using these pre-computed coefficients. This is -#: because the `LimitedPolynomial-Strict` context balks at answers that are -#: not already simplified completely. -#: -#: For the factored form we need to change to the `PolynomialFactors-Strict` -#: context and restrict the allowed powers to either 0 or 1 using the -#: `LimitedPowers::OnlyIntegers` block of code. Note: restricting all exponents -#: to 0 or 1 means that repeated factors will have to be entered in the form -#: `k(ax+b)(ax+b)` instead of `k(ax+b)^2`. Also, restricting all exponents to -#: 0 or 1 means that the polynomial must factor as a product of linear -#: factors (no irreducible quadratic factors can appear). Of course, -#: we could allow exponents to be 0, 1, or 2, but then students would be -#: allowed to enter reducible quadratic factors. There are no restrictions -#: on the coefficients, i.e., the quadratic could have any nonzero leading c -#: oefficient. We set `singleFactors=>0` so that repeated, non-simplified -#: factors do not generate errors. - -# Vertex form -Context("Numeric"); -$n = list_random(4, 6); -$a = random(2, 4, 1); -$b = ($a + $n); -$h = ($b - $a) / 2; -$k = $h**2 + $a * $b; -$vertexform = Compute("(x-$h)^2-$k"); - -# Expanded form -Context("LimitedPolynomial-Strict"); -$p0 = $h**2 - $k; -$p1 = 2 * $h; -$expandedform = Formula("x^2 - $p1 x + $p0")->reduce; - -# Factored form -Context("PolynomialFactors-Strict"); -Context()->flags->set(singleFactors => 0); -LimitedPowers::OnlyIntegers( - minPower => 0, - maxPower => 1, - message => "either 0 or 1", -); -$factoredform = Compute("(x+$a)(x-$b)"); - -#:% section=statement -BEGIN_PGML - -The quadratic expression [` [$vertexform] `] is written in vertex form. - -a. Write the expression in expanded form [` ax^2 + bx + c `]. - - [_____]{$expandedform} - -b. Write the expression in factored form [` k(ax+b)(cx+d) `]. - - [_____]{$factoredform} -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/FormattingDecimals.pg b/tutorial/sample-problems/problem-techniques/FormattingDecimals.pg deleted file mode 100644 index 56faf42ad2..0000000000 --- a/tutorial/sample-problems/problem-techniques/FormattingDecimals.pg +++ /dev/null @@ -1,94 +0,0 @@ -## DESCRIPTION -## We show how to use format decimals, and, conveniently also how to use logarithmic functions in PG problems. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('formatting decimals', 'logarithm') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Formatting Decimals -#:% type = technique -#:% categories = [formatting decimals, logarithm] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: Since the domain of a logarithmic function is all positive real numbers, -#: we should set the domain of function evaluation to `[2,4]` in order to avoid -#: vertical asymptotes and places where a logarithmic function takes values -#: close to zero. -#: -#: Use perl's `sprintf( format, number );` command to format the decimal. The -#: `"%0.3f"` portion truncates after 3 decimal places and uses zeros (not spaces) -#: to right-justify. For answers involving money, you should set `"%0.2f"` for -#: two decimal places and zero filling (for example, `sprintf("%0.2f",0.5);` -#: returns `0.50`). You can do a web search for more options to perl's sprintf, -#: and also for WeBWorK's PODLINK('contextCurrency.pl'). If you do further -#: calculations with `$b`, be aware that numerical error may be an -#: issue since you've reduced the number of decimal places. -#: -#: We used the logarithm change of base formula -#: `log10(a) = log(a) / log(10) = ln(a) / ln(10)` to get a logarithm base 10. -#: -#: It is possible to set a context flag that will use the base 10 log via -#: `Context()->flags->set(useBaseTenLog=>1);` The default is that this is set to zero. -#: -#: If you would like to define log base 2 (or another base) see -#: PROBLINK('AddingFunctions.pg') for how to define and add a new function -#: to the context so that students can enter it in their answers. -Context("Numeric"); -Context()->variables->set(x => { limits => [ 2, 4 ] }); - -$a = random(3, 7, 1); - -# both ln and log are natural log (base e) -$b = sprintf("%0.3f", ln($a)); -$ans1 = Real("$b"); - -$f = Formula("ln(x)"); # or log(x) -$ans2 = $f->eval(x => $a); - -# log base 10 is log10, logten, -# ln(x)/ln(10), or log(x)/log(10) - -$c = sprintf("%0.3f", ln($a) / ln(10)); # or log($a)/log(10) -$ans3 = Real("$c"); - -$g = Formula("ln(x)/ln(10)"); # or log(x)/log(10) -$ans4 = $g->eval(x => $a); - -#:% section=statement -#: Notice the difference in decimal formatting when "Show Correct Answers" -#: is checked and you click "Submit Answers". -BEGIN_PGML -Notice the formatting and rounding differences -between [` [$ans1] `] and [` [$ans2] `]. - -Try entering [` \ln([$a]), \log([$a]), \ln([$a])/\ln(10), \log([$a])/\log(10), -\mathrm{logten}([$a]), \mathrm{log10}([$a]) `]. - - -1. [` \ln([$a]) = `] [_____]{$ans1} - -2. [` \ln([$a]) = `] [_____]{$ans2} - -3. [` \log_{10}([$a]) = `] [_____]{$ans3} - -4. [` \log_{10}([$a]) = `] [_____]{$ans4} -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/GraphsInTables.pg b/tutorial/sample-problems/problem-techniques/GraphsInTables.pg deleted file mode 100644 index cb37e408c4..0000000000 --- a/tutorial/sample-problems/problem-techniques/GraphsInTables.pg +++ /dev/null @@ -1,141 +0,0 @@ -## DESCRIPTION -## Creating a set of graphs and displaying the options in a table. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(PGML tutorial 2015) -## Date(06/01/2015) -## Institution(Hope College) -## Author(Paul Pearson) -## MO(1) -## KEYWORDS('parametric', 'graph') - -#:% name = Graphs in a Table -#:% types = [Sample, technique] - -#:% section = preamble -#: We use `PGtikz.pl` to generate the graph, `parserPopUp.pl` for the -#: popup (select), `niceTables.pl` for layout and `PGchoicemacros.pl` -#: for the `shuffle` and `invert` functions. -DOCUMENT(); - -loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'PGtikz.pl', 'parserPopUp.pl', - 'niceTables.pl', 'PGchoicemacros.pl', - 'PGcourse.pl' -); - -#:% section=setup -#: Pick one equation and graph at random (using `$k`) to be the correct choice. -#: Shuffle the graphs using a permutation, and use the inverse permutation to -#: recall the correct answer in the answer evaluation section. -#: -#: For accessibility, there are `alt_text` statements describing each graph. Because -#: the graphs are shuffled and the correct one picked randomly, the `alt_text` -#: must be in an array in the same order as the functions. -# The form of the functions for the tikz plotting. -#: -#: If, instead, we had six graphs and desired a three-column array of graphs, -#: we would want to change `tex_size=>310` as in the following bit of code. -#: -#:```{#for-loop .perl} -#: for $i (0..5) { -#: # create the plots -#: -#: $fig[$i]=image($graph[$i], width => 200, tex_size => 310); -#: } -#:``` -#: You are strongly discouraged from using more than three columns in an array of -#: graphs because otherwise you have to scale the graphs down so much that they -#: become unreadable (especially in TeX mode). -#: -#: The `LayoutTable` is part of PODLINK('niceTables.pl') and is useful for -#: laying out elements. -#: -#: Toward the bottom the line `[$tab]*` inserts the table. Since the result -#: is HTML, the `*` at the end formats the result correctly. -#: -@eqn_plot = ('-exp(\x)', 'exp(-\x)', '-exp(-\x)', 'exp(\x)'); - -# The tex form of the functions. -@eqn = ( - "\( y = -e^{x} \)", - "\( y = e^{-x} \)", - "\( y = -e^{-x} \)", - "\( y = e^{x} \)" -); - -# Alternate text for each image. -@alt_text = ( - 'A graph starting near the negative x-axis on the left, decreasing to the lower left in a concave down manner', - 'A graph in the upper left and decreasing to the x-axis in a concave up manner', - 'A graph in the lower left and increasing to the x-axis in a concave down manner', - 'A graph starting near the negative x-axis on the left, increasing to the upper right in a concave up manner', -); - -for $i (0 .. 3) { - $graph[$i] = createTikZImage(); - $graph[$i]->tikzLibraries('arrows.meta'); - $graph[$i]->BEGIN_TIKZ - \tikzset{>={Stealth[scale=1.5]}} - \filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box - ] (-3.5,-3.5) rectangle (3.5,3.5); - \draw[->] (-3.5,0) -- (3.5,0) node[above left,outer sep=3pt] {\(x\)}; - \draw[->] (0,-3.5) -- (0,3.5) node[below right,outer sep=3pt] {\(y\)}; - \draw[DarkBlue,very thick] plot [samples=150,domain=-3:3] - (\x,{$eqn_plot[$i]}); -END_TIKZ - $fig[$i] = image( - $graph[$i], - width => 200, - tex_size => 450, - extra_html_tags => "alt='$alt_text[$i]'" - ); -} - -$k = random(0, 3); - -@perm = shuffle(4); -@fig = @fig[@perm]; -@inv = invert(@perm); - -@letter = ("A", "B", "C", "D"); - -$popup = PopUp([ "?", "A", "B", "C", "D" ], $letter[ $inv[$k] ]); - -$tab = LayoutTable( - [ - [ 'A', 'B' ], - [ $fig[0], $fig[1] ], - [ 'C', 'D' ], - [ $fig[2], $fig[3] ], - ], - texalignment => 'cc' -); - -#:% section = statement -BEGIN_PGML -Consider the exponential equation [$eqn[$k]]. -Without using a calculator, sketch a -graph of this equation on paper. - -Which graph A-D below most closely matches -the graph you drew? [___]{$popup} - -[$tab]* - -(Click on a graph to enlarge it.) -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/HtmlLinks.pg b/tutorial/sample-problems/problem-techniques/HtmlLinks.pg deleted file mode 100644 index 854fae5543..0000000000 --- a/tutorial/sample-problems/problem-techniques/HtmlLinks.pg +++ /dev/null @@ -1,69 +0,0 @@ -## DESCRIPTION -## This shows how to make an html link in a PG problem. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('link') - -#:% name = HTML Links -#:% type = [snippet, technique] - -#:% section = preamble -#: An example below uses units, so we load `parserNumberWithUnits.pl`. -DOCUMENT(); -loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'parserNumberWithUnits.pl', 'PGcourse.pl' -); - -#:% section=setup -$ans = NumberWithUnits('4', 'ft'); - -#:% section=statement -#: We need no additions to the PG file except in the text section, where we use -#: the `htmlLink` function to insert the link. There are four examples here in -#: all, the page to load is given as the first argument to `htmlLink`, and the -#: text to display as the link as the second argument. -#: -#: 1. This is a link to a general URL. -#: -#: 2. The second example shows how to link to a page that is in the same directory -#: on the WeBWorK server as the PG file: the alias function puts in the correct -#: link for this file. Setting the target to be _blank will open a new (blank) -#: window or tab. -#: -#: 3. The third example shows how to link to a page that is under the html -#: subdirectory of a course's main directory. In this example, which is taken -#: from the problem `Library/Rochester/setDiffEQ6AutonomousStability/ur_de_6_3.pg`, -#: phaseplaneplotters is a subdirectory that has been added under the course's -#: html directory. The course's html directory can be linked using `${htmlURL}` -#: as in the example given or by using -#: `alias("${htmlDirectory}phaseplaneplotters/index.html")`. -#: -#: 4. The fourth example uses the built-in `helpLink` feature of WeBWorK. The -#: following is a list of all help topics: angle, decimal, equation, exponent -#: formula, fraction, inequality, limit, log, matrix, number, point, vector, -#: interval, unit, syntax. -BEGIN_PGML -The answer to all questions is on -[@ htmlLink( "http://www.google.com/", "this page" ) @]* - -A link to a -[@ htmlLink( alias('local.html'), "local problem", "TARGET='_blank'" ) @]* - -Click [@htmlLink("${htmlURL}phaseplaneplotters/index.html", - "sketch the graph.", "TARGET='plotter'" )@]* to use xFunctions for -plotting. - -Enter 4 feet: [__]{$ans} - -Don't forget to enter [@ helpLink("units") @]* -END_PGML - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/Images.pg b/tutorial/sample-problems/problem-techniques/Images.pg deleted file mode 100644 index 4ea85d77c0..0000000000 --- a/tutorial/sample-problems/problem-techniques/Images.pg +++ /dev/null @@ -1,84 +0,0 @@ -## DESCRIPTION -## Inserting images in PGML. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2023) -## Institution(Fitchburg State University) -## Author(Peter Staab) -## MO(1) -## KEYWORDS('images') - -#:% name = Inserting Images in PGML -#:% type = [technique] - -#:% section = preamble -#: We include `PGgraphmacros.pl`, `PGtikz.pl`, `PGlateximage.pl` and -#: `parserGraphTool.pl` to create images to be shown. -DOCUMENT(); - -loadMacros( - 'PGstandard.pl', 'PGML.pl', - 'PGgraphmacros.pl', 'PGtikz.pl', - 'PGlateximage.pl', 'parserGraphTool.pl', - 'PGcourse.pl' -); - -#:% section = setup -#: A WWPlot (from `PGgraphmacros.pl`), a TikZ plot, LaTeXImage, and -#: Graphtool is made. -$WWPlot = init_graph(-1, -1, 4, 4); -add_functions($WWPlot, "x^2/4 for x in <-1,4> using color:blue and weight:2"); - -$TikZ = createTikZImage(); -$TikZ->BEGIN_TIKZ -\draw (0,0) circle[radius=1.5]; -END_TIKZ - -$LaTeXImage = createLaTeXImage(); -$LaTeXImage->texPackages([ [ 'xy', 'all' ] ]); -$LaTeXImage->BEGIN_LATEX_IMAGE -\xymatrix{ A \ar[r] & B \ar[d] \\\\ - D \ar[u] & C \ar[l] } -END_LATEX_IMAGE - -$gt = GraphTool("{circle, solid, (1, 1), (2, 2)}"); - -#:% section = statement -#: In each of these cases, we use the PGML syntax -#:``` -#:[!alt text!]{image}{width (optional)}{height (optional)} -#:``` -#: -#: * The image can be a string (either a local image file or a URL), a -#: WWPlot, TikZ plot, LaTeXImage, or graphtool plot. The local file should be -#: in the same directory as the problem. -#: -#: * You should always include some alternate text for accessibility. -#: -#: * If the `width` is not included, the width is 100 (in pixels) -#: -#: * If the `height` is not included, it takes on the natural value (not to -#: stretch the image) -#: -#: * The `tex_size` will be computed by `width * 1000/600` -BEGIN_PGML - -* A static image: [!Graph of an exponential!]{'image.png'}{120} - -* A static image from an external link (note: this does not work for hardcopy) -[!Runestone Logo!]{"https://runestone.academy/runestone/static/images/RAIcon.png"}{120} - -* A WWplot graph [!A simple parabola plot!]{$WWPlot}{120} - -* A TikZ graph [!A circle!]{$TikZ}{120} - -* A LaTeXImage: [!A graph with node A going to node B going to node C, going to node D, and back to node A!]{$LaTeXImage}{120} - -* A graphtool plot [!A graphtool plot with a circle!]{$gt}{120} - -END_PGML - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/InequalityEvaluators.pg b/tutorial/sample-problems/problem-techniques/InequalityEvaluators.pg deleted file mode 100644 index b69ac982dd..0000000000 --- a/tutorial/sample-problems/problem-techniques/InequalityEvaluators.pg +++ /dev/null @@ -1,67 +0,0 @@ -## DESCRIPTION -## This shows how to use inqualities in a problem. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('interval') - -# created as a full problem by Peter Staab 2023.06.02 - -#:% name = Inequality Evaluator -#:% type = [technique] -#:% categories = interval -#:% subject = algebra -#:% see_also = [IntervalEvaluators.pg] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'contextInequalities.pl', 'PGcourse.pl'); - -#:% section=setup -#: Using `Context('Inequalities-Only')`, if the student enters the inequality -#: `-16 <= y <= 9` their answer will be marked correct, but the equivalent -#: interval `[-16,9]` would be incorrect. If we had used -#: `Context('Inequalities')` instead, both the inequality and the interval -#: would be marked correct. -#: -#: Uncommenting the lines containing `EmptySet` creates an empty set as a named -#: constant and uses that name. -#: -#: Uncommenting `Context()->flags->set(ignoreEndpointTypes=>1);` would also mark -#: the student answers `-16 < y < 9` or `-16 <= y < 9` or `-16 < y <= 9` correct. -Context("Inequalities-Only"); -Context()->variables->add(y => "Real"); -# Context()->constants->add(EmptySet => Set()); -# Context()->flags->set(noneWord=>"EmptySet"); -# Context()->flags->set(ignoreEndpointTypes=>1); - -# f(x) = x^2 - 16 on -1 <= x <= 5 -$f = Formula("x^2 - 16"); - -$range = Compute("-16 <= y <= 9"); - -Context()->variables->remove("x"); - -#:% section=statement -BEGIN_PGML -What is the range of -[`y = f(x) = [$f] `] on the domain [` -1 \leq x \leq 5 `]? - - -Range: [___]{$range} - -Enter your answer using inequalities (not intervals). -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/IntervalEvaluators.pg b/tutorial/sample-problems/problem-techniques/IntervalEvaluators.pg deleted file mode 100644 index e8d7c0ea99..0000000000 --- a/tutorial/sample-problems/problem-techniques/IntervalEvaluators.pg +++ /dev/null @@ -1,59 +0,0 @@ -## DESCRIPTION -## This shows how to use intervals in a problem. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('interval') - -# created as a full problem by Peter Staab 2023.06.02 - -#:% name = Interval Evaluator -#:% type = [technique] -#:% categories = interval -#:% subject = algebra -#:% see_also = [InequalityEvaluators.pg] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: In the problem set-up section of the file, we set the `Context` to be the -#: `Interval` context. Note that we can relax checking of endpoints in the -#: `Context` or in the actual answer checking, as noted below. -#: -#: Once we're in the `Interval` context, we can define intervals as we'd expect: -#: as shown here, or with limits at infinity: -#: -#:```{#interval .perl} -#: $int2 = Compute('(-inf,1]'); -#:``` -#: This would give the interval from negative infinity to 1, including -#: the point at one. Note the Context flag to make endpoint checking "fuzzy." -Context('Interval'); -# to allow open or closed intervals, uncomment -# the following line. -# Context()->flags->set(ignoreEndpointTypes=>1); - -$int = Compute('(1,3)'); - -#:% section=statement -BEGIN_PGML -On what interval is the parabola [`y = (1-x)(x-3)`] -above the [`x`]-axis? - -For [`x`] in [_____]{$int} -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/Knowls.pg b/tutorial/sample-problems/problem-techniques/Knowls.pg deleted file mode 100644 index cdbefc13b8..0000000000 --- a/tutorial/sample-problems/problem-techniques/Knowls.pg +++ /dev/null @@ -1,46 +0,0 @@ -## DESCRIPTION -## This shows how to use intervals in a problem. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('interval') - -# created as a full problem by Peter Staab 2023.06.02 - -#:% name = Knowls -#:% type = [technique, snippet] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=statement -#: Knowls appear in the text section of the problem file. You can specify -#: a value, as in the first example, which gives the text to appear in the -#: Knowl, or the URL of a file with the HTML content for the knowl, as -#: shown in the second example here. -#: -#: To include math text in the knowl, it is necessary to pipe the text -#: through EV3P and escapeSolution HTML, as shown in the third example. -BEGIN_PGML -Here is a knowl -[@ knowlLink("click me", value => - 'This is the inside of a knowl. If you click again, I will go away') @]* - -Here is another knowl -[@ knowlLink("click me", - url=>'https://openwebwork.org') @]* - - -[@ knowlLink("a math knowl", -value=>escapeSolutionHTML(EV3P("the sine function is \\(\\frac{2}{3}\\)")), base64=>1); -@]* -END_PGML - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/LayoutTable.pg b/tutorial/sample-problems/problem-techniques/LayoutTable.pg deleted file mode 100644 index dcdcddd889..0000000000 --- a/tutorial/sample-problems/problem-techniques/LayoutTable.pg +++ /dev/null @@ -1,103 +0,0 @@ -## DESCRIPTION -## This shows how to use LayoutTable for layout. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2023) -## Institution(Fitchburg State University) -## Author(Peter Staab) -## MO(1) -## KEYWORDS('tutorial', 'table') - -#:% name = Layout Table -#:% type = [technique] -#:% categories = table -#:% see_also = [DataTables.pg] - -#:% section = preamble -#: This shows how to use the `LayoutTable` function in `niceTables.pl` to -#: give a nice two column format. -#: -#: Note the `LayoutTable` does the right thing for accessibility and -#: webpage responsiveness. -DOCUMENT(); -loadMacros( - 'PGstandard.pl', 'PGML.pl', 'niceTables.pl', 'PGtikz.pl', - 'PGcourse.pl' -); - -#:% section=setup -#: We use the `LayoutTable` function from `niceTables.pl` to demonstrate some -#: of it's features. -#: -#: The basic form of a `LayoutTable` is identical to that of `DataTable` or -#:```{#datatable .perl} -#:$table = LayoutTable([ -#: [row1], -#: [row2], -#: ... -#: [rowN] -#: ], -#: options); -#:``` -#: where the data goes in as an array ref of array refs. However, if using -#: a table for layout purposes, `LayoutTable` has more appropriate default -#: options. See -#: PODLINK('the niceTables.pl POD','niceTables.pl') for more details. -#: -#: Notice in this example that we make two columns. The left column is -#: written using older style PG, with `$PAR` as a paragraph break. -$a = random(0, 3); -$ans = Compute("x^2+$a")->reduce; - -$left = qq| -A common situation is that there is a problem with a graph and -the problem is on the left column and the graph is on the right -column. -$PAR -This even works if we add an equation like \(e^{i\pi}+1 = 0\) -$PAR -Or if we add an answer blank. -$PAR -A formula for the function graphed on the right is \(f(x)=\)| . ans_rule(10); - -$graph = createTikZImage(); -$graph->tikzLibraries('arrows.meta'); -$graph->BEGIN_TIKZ -\tikzset{>={Stealth[scale=1.5]}} -\filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-6,7) rectangle (6,-1); -\draw[->,thick] (-6,0) -- (6,0) node[above left,outer sep=3pt] {\(x\)}; -\foreach \x in {-5,...,-1,1,2,...,5} - \draw(\x,5pt) -- (\x,-5pt) node [below] {\(\x\)}; -\draw[->,thick] (0,-1) -- (0,7) node[below right,outer sep=3pt] {\(y\)}; -\foreach \y in {1,...,6} - \draw (5pt,\y) -- (-5pt,\y) node[left] {\(\y\)}; -\draw[blue,ultra thick] plot[domain=-2.5:2.5,smooth] (\x,{\x*\x+$a}); -END_TIKZ - -#:% section=statement -#: Since the only output is the table, we use this line to output the -#: problem as the two columns. This is the `LayoutTable` with only -#: one row. -TEXT(LayoutTable( - [ [ $left, image($graph, width => 400, tex_size => 600) ] ], - align => 'lc' -)); - -#:% section = answer -#: Since `ans_rule` is used to produce answer blanks, this in needed. -ANS($ans->cmp); - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/Multianswer.pg b/tutorial/sample-problems/problem-techniques/Multianswer.pg deleted file mode 100644 index 4a69c57920..0000000000 --- a/tutorial/sample-problems/problem-techniques/Multianswer.pg +++ /dev/null @@ -1,110 +0,0 @@ -## DESCRIPTION -## A simple multianswer problem. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('tolerance') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Multianswer Problem -#:% type = [technique, sample] -#:% subject = [algebra, precalculus] -#:% categories = [multianswer] - -#:% section = preamble -#: Since we are using the Multianswer technique, `parserMultianswer.pl` -#: must be loaded. -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'parserMultiAnswer.pl', 'PGcourse.pl'); - -#:% section = setup -#: This problem is shown as an example of using `parserMultiAnswer.pl`. -#: A better solution for this type of problem is shown in -#: PROBLINK('FactoredPolynomial.pg'). -#: -#: In the setup section of the file we define a `MultiAnswer` object that -#: knows how to deal with the problem. Here we define an object that will take two -#: answers and check that they are correct (in either order). -#: -#: First, the `singleResult=>0` line indicates that the different answers in the -#: problem will be evaluated as separate answers, rather than as a single unit. -#: Other useful flags include `allowBlankAnswers`, `checkTypes`, `separator` and -#: `tex_separator`. These are noted below. -#: -#: Then, the `checker=> section` defines a subroutine to evaluate the problem. -#: It will always have as input a reference to an array of correct answers, a -#: reference to an array of student answers, and a reference to the object -#: itself. (There is a fourth input, too, an answer hash, but we don't need -#: that here.) -#: -#: The checker routine then returns a reference to a list of results for the -#: problem. In this case there are two answer blanks, so there are two return -#: values. All return values should be `0` or `1`, according to whether the -#: answer for that answer blank is correct or not. Note that if we made -#: this an "all or nothing" problem (that is, we set `singleResult=>1`), -#: then there is only one return value needed, so that we could just -#: `return 0` or `return 1`. -#: -#: It is possible to set an answer message that will be displayed when the -#: problem is checked, too. For example, if we wanted to set a message when -#: one of the parts was wrong, we could replace the section of the checker -#: code that deals with incorrect answers with: -#: -#:```{.perl} -#: if ($f1 == $f1stu || $f2 == $f1stu) { -#: $self->setMessage(1,"This is correct."); -#: $self->setMessage(2,"Check your answer " . -#: "by using FOIL."); -#: return [1,0]; -#: } elsif ($f1 == $f1stu || $f2 == $f2stu) { -#: $self->setMessage(1,"Check your answer " . -#: "by using FOIL."); -#: $self->setMessage(2,"This is correct."); -#: return [0,1]; -#: } else { -#: return [0,0]; -#: } -#:``` -$fac1 = Formula("(1 - x)"); -$fac2 = Formula("(1 + x)"); - -$multians = MultiAnswer($fac1, $fac2)->with( - singleResult => 0, - checker => sub { - my ($correct, $student, $self) = @_; - my ($f1stu, $f2stu) = @{$student}; - my ($f1, $f2) = @{$correct}; - if (($f1 == $f1stu && $f2 == $f2stu) - || ($f1 == $f2stu && $f2 == $f1stu)) - { - return [ 1, 1 ]; - } else { - if ($f1 == $f1stu || $f2 == $f1stu) { - return [ 1, 0 ]; - } elsif ($f1 == $f2stu || $f2 == $f2stu) { - return [ 0, 1 ]; - } else { - return [ 0, 0 ]; - } - } - } -); - -BEGIN_PGML -Factor: [`1-x^2 = \big(`] [___]{$multians} -[`\big)\big(`] [___]{$multians} [`\big)`] -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/NumericalTolerance.pg b/tutorial/sample-problems/problem-techniques/NumericalTolerance.pg deleted file mode 100644 index 18648e2997..0000000000 --- a/tutorial/sample-problems/problem-techniques/NumericalTolerance.pg +++ /dev/null @@ -1,76 +0,0 @@ -## DESCRIPTION -## Explains the difference in tolerance type and numerical tolerance. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('tolerance') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Numerical Tolerance -#:% type = technique -#:% categories = [numbers, tolerance] -#:% see_also = [DigitsTolType.pg] - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section = setup -#: This shows three different ways of setting the toltype and tolerance of the answer. The -#: `tolType` can be `absolute` (specifying a decimal distance from the correct answer that -#: will be allowed) or `relative` (specifying a percent error that will be allowed). -#: -#: Thus if the correct answer is 17, a tolerance of 0.01 will mean that the student answer -#: must be in the interval `(16.99,17.01)` if the `tolType` is `absolute`, and in the interval -#: `(16.83,17.17)` if `tolType` is `relative` (or omitted, as relative tolerance is the default). -#: -#: 1. The default `Context('Numeric')` is used (but not needed) and within the Compute call, -#: the `tolerance` type and level is set. -#: -#: 2. The `tolerance` and `toltype` is set on the answer check with the cmp call. See the -#: problem statement below. -#: -#: 3. The `tolerance` and `toltype` can be set on the `Context`. This is useful if the desired -#: toltype and/or tolerance is the same for many answer. Typically this would go at the -#: top of the setup section. -$ans1 = Compute('1.5708')->cmp( - tolType => 'absolute', - tolerance => .0001, -); - -$ans2 = Compute('1.5708'); - -Context('Numeric')->flags->set( - tolerance => 0.0001, - tolType => 'absolute', -); - -$ans3 = Compute('1.5708'); -#:% section = statement -BEGIN_PGML -For each of the following Enter your answer accurate to four decimal places . - -1. Enter [`` \frac{\pi}{2}= ``] [____]{$ans1} - -2. Enter [`` \frac{\pi}{2}= ``] [____]{$ans2->cmp( - tolType => 'absolute', - tolerance => .0001, -)} - -3. Enter [`` \frac{\pi}{2}= ``] [____]{$ans3} - -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/OtherVariables.pg b/tutorial/sample-problems/problem-techniques/OtherVariables.pg deleted file mode 100644 index 16938547c1..0000000000 --- a/tutorial/sample-problems/problem-techniques/OtherVariables.pg +++ /dev/null @@ -1,54 +0,0 @@ -## DESCRIPTION -## Add variables to the context. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(Problem Techniques) -## Date(06/01/2008) -## Institution(University of Michigan) -## Author(Gavin LaRose) -## MO(1) -## KEYWORDS('variables') - -# updated to full problem by Peter Staab (06/01/2023) - -#:% name = Adding Variables to the Context -#:% type = technique -#:% categories = [variables] - -#:% section = preamble - -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section=setup -#: Typically you either add or set the variables for the entire problem at the -#: top of the setup section. In this case, we add y, z and t, all real numbers. -#: -#: This allows us to use a greek letter as a variable. Note that we have used -#: add for this. If we set this with `are`, then the other variables will be -#: deleted upon answer checked and the student will get a `variable not defined -#: in this context` error. -Context()->variables->add(t => 'Real', y => 'Real', z => 'Real'); -$f = Compute('-16 t^2 + 5 t + 4'); -$g = Compute('x^2+y^2+z^2'); - -Context()->variables->add(rho => [ 'Real', TeX => '\rho' ]); -$h = Compute("sqrt(1+rho^2)"); - -#:% section=statement -BEGIN_PGML -Enter the following formulas: - -* [`[$f]=`] [____]{$f} -* [`[$g]=`] [____]{$g} -* [`[$h]=`] [____]{$h} -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/RestrictAnswerToFraction.pg b/tutorial/sample-problems/problem-techniques/RestrictAnswerToFraction.pg deleted file mode 100644 index 742278821f..0000000000 --- a/tutorial/sample-problems/problem-techniques/RestrictAnswerToFraction.pg +++ /dev/null @@ -1,68 +0,0 @@ -## DESCRIPTION -## Restricting answers that should reduce to a fraction. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(PGML tutorial 2015) -## Date(06/01/2015) -## Institution(Fitchburg State University) -## Author(Peter Staab) -## MO(1) -## KEYWORDS('answer', 'fraction') - -#:% name = Restrict Answers to a Fraction -#:% type = [technique, sample] -#:% subject = [answer] -#:% see_also = [RestrictingFunctions.pg] - -#:% section = preamble -DOCUMENT(); - -loadMacros('PGstandard.pl', 'PGML.pl', 'contextFraction.pl', 'PGcourse.pl'); - -#:% section = setup -#: Here we specify that we are using the `Fractions-NoDecimals` Context, which -#: requires that answers be fractions and not decimals. To ensure that -#: students do the simplification rather than typing the answer without -#: expanding it, we undefine operators other than division (see Restricting -#: Functions and Operators). -#: -#: Note that because we've undefined these operators for all MathObjects, -#: we can't define the answer as `$frac=Compute("$b/($c + $a^2)");`. -#: The operators + and ^ are undefined, so we don't have them available. -#: In this case we do the calculation of the denominator using Perl first, -#: and then use the MathObject to create the answer. -#: -#: Also note that by default a Fraction will be reduced to lowest terms. -Context("Fraction-NoDecimals"); -Context()->operators->undefine('+', '-', '*', '*', '**', '^'); - -$a = random(2, 4); -$b = random(1, 9); -$c = random(1, 9); -$den = $c + $a * $a; -$frac = Compute("$b/$den"); -$ans = $frac->cmp( - studentsMustReduceFractions => 1, - strictFractions => 1, - strictMinus => 1, - strictMultiplication => 1 -); -#:% section = statement -BEGIN_PGML -Find and simplify completely the value of -[`f([$a])`] if [`` f(x) = \frac{[$b]}{[$c] + x^2}. ``] - -[`f([$a]) = `] [__]{$ans} - -_(Simplify your answer as much as possible, and enter a fraction instead of -a decimal.)_ -END_PGML - -#:% section = solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/StaticImages.pg b/tutorial/sample-problems/problem-techniques/StaticImages.pg deleted file mode 100644 index c76d2c586c..0000000000 --- a/tutorial/sample-problems/problem-techniques/StaticImages.pg +++ /dev/null @@ -1,69 +0,0 @@ -## DESCRIPTION -## Show a static image. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(PGML tutorial 2015) -## Date(06/01/2015) -## Institution(Hope College) -## Author(Paul Pearson) -## MO(1) -## KEYWORDS('parametric', 'graph') - -#:% name = Graphic Images, Static -#:% types = technique - -#:% section = preamble -DOCUMENT(); -loadMacros('PGstandard.pl', 'PGML.pl', 'PGcourse.pl'); - -#:% section = statement -#: Just use the `image` macro in the main section of the -#: problem to include the image. The images to be included should be GIF or -#: PNG or JPG files. PNG files are recommended since they generally look -#: better when rescaled. In order for PDF hardcopy generation to work properly, -#: the names of image files must have only one period in them (imagename.png -#: works but image.name.png does not). PDF files will work on hardcopy but do -#: not immediately render in the browser without first clicking on the image -#: box. -#: -#: If using PGML, the `image` command must be surrounded by the `[@ @]*` tags -#: which calls the `image` function. The `*` is needed to format the result -#: correctly. The `image` function returns HTML. -#: -#: For accessibility you should always add the option -#: `extra_html_tags = 'alt_text = "..."'` describing in detail the image. -#: -#: For each PG problem with static images, you should put both the PG file -#: and the image files into their own separate subdirectory. This subdirectory -#: should be located somewhere under the course templates directory and have -#: the same root name as the PG file. For example, if you have a PG file -#: called `Contour-plots.pg` which uses static graphic files -#: `Contour-plot-01.gif`and `Contour-plot-02.gif`, you should create a -#: subdirectory somewhere under the course templates directory called -#: `Contour-plots` and put the PG file and all the GIF files in it. Putting a -#: PG file and all of its graphics files into their own separate subdirectory -#: like this makes it easier to find the graphics files that go with each PG -#: file, thereby making the problem easier to maintain. The reason for having -#: the subdirectory and the root name of the PG file be the same is as follows. -#: When the library is browsed via directories, the library browser in WeBWorK -#: is configured to recognize that when a subdirectory has the same name as -#: the root name of the only PG file in that subdirectory, the subdirectory -#: and PG file should be treated as a single entity. -#: -#: We should always, of course, include options such as specifying the tex_size, -#: etc., in this call, as shown in the including dynamic images code snippet. -#: Taking the `tex_size => "667"` and dividing by 10 results in the percentage -#: of the available space used by the graphic -- in this case 66.7 percent. -#: Usually the available space is constrained by the width of one column of -#: a two-column printed page. - -BEGIN_PGML -[@ image('image.png') @]* - -[@ image('image.png', width => 400, tex_size => 600, - extra_html_tags => 'An graph of a decreasing exponential function.') @]* -END_PGML - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/TikZImages.pg b/tutorial/sample-problems/problem-techniques/TikZImages.pg deleted file mode 100644 index a390ee7bef..0000000000 --- a/tutorial/sample-problems/problem-techniques/TikZImages.pg +++ /dev/null @@ -1,89 +0,0 @@ -## DESCRIPTION -## Create a graph using tikz. -## ENDDESCRIPTION - -## DBsubject(WeBWorK) -## DBchapter(WeBWorK tutorial) -## DBsection(PGML tutorial 2015) -## Date(06/01/2015) -## Institution(Hope College) -## Author(Paul Pearson) -## MO(1) -## KEYWORDS('graph', 'tikz') - -#:% name = Graphic Images, TikZ -#:% types = [Sample, technique] -#:% subject = parametric - -#:% section = preamble -#: We use `PGtikz.pl` to generate the graph, -DOCUMENT(); - -loadMacros('PGstandard.pl', 'PGML.pl', 'PGtikz.pl', 'PGcourse.pl'); - -#:% section=setup -#: * The `createTikZImage()` function creates an image to be built using tikz. -#: By default an `svg` image will be generated, which will generally look better -#: than a `png` image. -#: * In certain cases the `svg` creation methods do not give the correct output, -#: and so in those cases a `png` image may be generated instead by adding -#: `$graph_image->ext('png');`. -#: * The command `\tikzset{>={Stealth[scale=1.5]}}` scales the arrows by a -#: factor of 1.5. -#: * The command that starts with `\filldraw` creates a nice background of -#: the graph with contrast to the rest of the problem page. -#: * The `$graph_image->tikzLibraries("arrows.meta");` will load the `arrows.meta` -#: Tikz library. -#: * The variables `$a` and `$b` are defined for use in the TikZ code that -#: follows. If the TikZ code references non-existent pg variables the -#: image creation fails silently. -#: * The actual tikz image is built between `$graph_image->BEGIN_TIKZ` and -#: `END_TIKZ` -$graph_image = createTikZImage(); -$graph_image->tikzLibraries("arrows.meta"); - -# Randomization -$a = non_zero_random(-6, 6); # horizonatal translation -$b = random(-4, 4); # vertical translation - -$graph_image->BEGIN_TIKZ -\tikzset{>={Stealth[scale=1.5]}} -\filldraw[ - draw=LightBlue, - fill=white, - rounded corners=10pt, - thick,use as bounding box -] (-11.5,-11.5) rectangle (11.5,11.5); -\draw[<->,thick] (-11,0) -- (11,0) node[above left,outer sep=4pt]{\(x\)}; -\draw[<->,thick] (0,-11) -- (0,11) node[below right,outer sep=4pt]{\(y\)}; -\foreach \x in {-10,-8,...,-2,2,4,...,10} - \draw[thin] (\x,5pt) -- (\x,-5pt) node[below]{\(\x\)}; -\foreach \y in {-10,-8,...,-2,2,4,...,10} - \draw[thin] (5pt,\y) -- (-5pt,\y) node[left]{\(\y\)}; -\draw[<->,red] plot[domain={-3.2+$a}:{3.2+$a}] (\x,{pow(\x-$a,2)+$b}); -END_TIKZ - -#:% section=statement -#: This is how to insert the tikz image. Note the `width` and `tex_size` -#: parameters can change the size of the image on the web and as hardcopy. -#: -#: * the `width` option is the size in pixels of the image on the screen. -#: * the `tex_size` option is the scale factor for hardcopy where 1000 is -#: the full width of either the page or the column. This image will be -#: 60% of the page width. -#: -#: If the problem times out then often there is a problem with the tikz -#: commands. Troubleshooting is often needed by running the same code in -#: a latex file and compiling it. -BEGIN_PGML - ->> [@ image($graph_image, width => 400, tex_size => 600) @]* << - -END_PGML - -#:% section=solution -BEGIN_PGML_SOLUTION -Solution explanation goes here. -END_PGML_SOLUTION - -ENDDOCUMENT(); diff --git a/tutorial/sample-problems/problem-techniques/local.html b/tutorial/sample-problems/problem-techniques/local.html deleted file mode 100644 index de828e8fc0..0000000000 --- a/tutorial/sample-problems/problem-techniques/local.html +++ /dev/null @@ -1,4 +0,0 @@ - -

Local File

-

This is an example of a local html file.

- \ No newline at end of file