Skip to content

Commit

Permalink
Lots of clean up and issue fixing for the sample problems.
Browse files Browse the repository at this point in the history
Fix warnings and errors in many of the sample problems, and clean up the
categories. Note that algebra and trigonometry should not be
categories. Those are subject areas.

The documentation in the problems has been updated for consistency,
readability, and correctness.

Cases where new PGML constructs could be used have been implemented
(tables, images, tags, and such).
  • Loading branch information
drgrice1 committed Dec 12, 2024
1 parent ba2f562 commit c185f7d
Show file tree
Hide file tree
Showing 178 changed files with 4,913 additions and 4,402 deletions.
2 changes: 1 addition & 1 deletion lib/SampleProblemParser.pm
Original file line number Diff line number Diff line change
Expand Up @@ -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*'(.*)')?\)/) {
Expand Down
112 changes: 56 additions & 56 deletions tutorial/sample-problems/Algebra/AlgebraicFractionAnswer.pg
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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))
Expand All @@ -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 ];
Expand All @@ -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.
Expand Down
32 changes: 18 additions & 14 deletions tutorial/sample-problems/Algebra/AnswerBlankInExponent.pg
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,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);
Expand All @@ -39,30 +40,33 @@ $base = Formula("a*b");
$exponent = Formula("$n");

# Display exponents nicely
$exp = MODES(
HTML => "<span>\(\displaystyle $expression= \Big(\)"
if ($displayMode eq 'TeX') {
$exp =
"\( \displaystyle $expression = ("
. ans_rule(4) . ")^{"
. ans_rule(4) . "}\)";
} else {
$exp =
"<span>\(\displaystyle $expression = \Big(\)"
. ans_rule(4)
. '\(\Big)\)</span><span style="vertical-align: 12pt;">'
. ans_rule(4)
. '</span>',
TeX => "\( \displaystyle $expression = ("
. ans_rule(4) . ")^{"
. ans_rule(4) . "}\)"
);
. '</span>';
}

#:% section = statement
#: We insert exponential stored as `$exp`.
#: Insert the exponential stored as `$exp`.
BEGIN_PGML
Rewrite the following using a single exponent.

[$exp]*
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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 {
Expand Down
56 changes: 28 additions & 28 deletions tutorial/sample-problems/Algebra/DomainRange.pg
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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`].

Expand All @@ -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
Expand Down
Loading

0 comments on commit c185f7d

Please sign in to comment.