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