## WeBWorK Main Forum

by Patrick Spencer -
Number of replies: 5
I was wondering if there was a way to combine PopUpLists with MultiAnswer choices? The problem we're working on goes like this: the student is presented with a graph of a polynomial and they have to find the value and multiplicity of each zero. They can enter the zero's in any order they please. We would like the multiplicity choice for each zero to be a PopUpList containing either "even" or "odd". I've attached a photo of part of the problem to this message.

I thought about switching to the unorderedAnswers.pl helper functions but I didn't know how to group the pairs of solutions togethers.

I've also included the code we've been using below.

Thank you!
Patrick Spencer
University of Missouri

DOCUMENT();
"MathObjects.pl",
"PeriodicRerandomization.pl",
"PGgraphmacros.pl",
"PGstandard.pl",
"parserPopUp.pl",
);

TEXT(beginproblem());
PeriodicRerandomization("3");
$refreshCachedImages = 1;$showPartialCorrectAnswers = 1;

#################################################################
# setup contexts and variables
#################################################################
Context("Numeric");
"odd"=>{}, "o"=>{alias=>"odd"},
"positive"=>{}, "pos"=>{alias=>"positive"}, "p"=>{alias=>"positive"},
"negative"=>{}, "neg"=>{alias=>"negative"}, "n"=>{alias=>"negative"});
Context()->{error}{msg}{"Operands of '*' can't be words"} = " ";
#Context()->{error}{msg}{"Can't convert an Empty Value to a Real Number"} = " ";
$numzeros = random(2,5); do { # posszeros = possible zeros? @posszeros = (-10..10); # zeros = actual zeros? @zeros = (1..$numzeros);
for ($i = 0;$i < $numzeros;$i++) {
$zeros[$i] = list_random(@posszeros);
if ($numzeros == 2 ||$numzeros == 3) {
$powers[$i] = random(2,3);
} else {
$powers[$i] = random(1,2);
}
$removed = 0;$count = 0;
do {
if ($posszeros[$count] == $zeros[$i]) {
$tmp =$posszeros[$count];$posszeros[$count] =$posszeros[scalar(@posszeros)-1];
$posszeros[scalar(@posszeros)-1] =$tmp;
pop(@posszeros);
$removed = 1; }$count++;
} until ($removed ||$count == scalar(@posszeros));
$removed = 0;$count = 0;
do {
if ($posszeros[$count] == $zeros[$i]-1) {
$tmp =$posszeros[$count];$posszeros[$count] =$posszeros[scalar(@posszeros)-1];
$posszeros[scalar(@posszeros)-1] =$tmp;
pop(@posszeros);
$removed = 1; }$count++;
} until ($removed ||$count == scalar(@posszeros));
$removed = 0;$count = 0;
do {
if ($posszeros[$count] == $zeros[$i]+1) {
$tmp =$posszeros[$count];$posszeros[$count] =$posszeros[scalar(@posszeros)-1];
$posszeros[scalar(@posszeros)-1] =$tmp;
pop(@posszeros);
$removed = 1; }$count++;
} until ($removed ||$count == scalar(@posszeros));
}

$yint = 1; for ($i = 0; $i <$numzeros; $i++) {$yint = $yint*((-$zeros[$i])**$powers[$i]); } # this sub routine represents what is to be the graphed polynomial sub poly { my$x = shift();
$prod = 1; for ($i = 0; $i <$numzeros; $i++) {$prod = $prod*(($x - $zeros[$i])**$powers[$i]);
}
return $prod; }; # sort zeros from low to high;$swapped = 1;
$count = 0; while ($swapped) {
$swapped = 0;$count++;
for ($i = 0;$i < $numzeros -$count; $i++) { if ($zeros[$i] >$zeros[$i+1]) {$tmp = $zeros[$i];
$zeros[$i] = $zeros[$i+1];
$zeros[$i+1] = $tmp;$swapped = 1;
}
}
}

# this next bit is to be sure that the gap between the low or high point
# between zeros and the x-axis is not too small
for ($i = 0;$i < $numzeros-1;$i++) {
$maxs[$i] = 0;
}

for ($j = 0;$j < $numzeros-1;$j++) {
$steps = 100;$stepsize = ($zeros[$j+1] - $zeros[$j])/$steps; for ($k = 0; $k <$steps; $k++) {$tmp = abs(poly($zeros[$j]+$k*$stepsize));
if ($tmp >$maxs[$j]) {$maxs[$j] =$tmp;
}
}
}

$min = min(@maxs);$max = max(@maxs);
$scale = random(5,9);$a = random(-1,1,2)*$scale/max($max);
$tmp1 = abs($min*$a);$tmp2 = abs($max*$a);
} until (abs($tmp2/$tmp1) < 3*$scale/2);$expn = sub {
my $x = shift();$prod = $a; for ($i = 0; $i <$numzeros; $i++) {$prod = $prod*(($x - $zeros[$i])**$powers[$i]);
}
return $prod; };$minx = min($zeros[0],0)-3;$miny = -10;
$maxx = max($zeros[$numzeros-1],0)+3;$maxy = 10;
$gsize = 400;$graph = init_graph($minx,$miny, $maxx,$maxy, size=>[$gsize,$gsize]);
for ($i =$minx; $i <=$maxx; $i++) {$graph->moveTo($i,$miny);
$graph->lineTo($i,$maxy,'gray',1); } for ($i = $miny;$i <= $maxy;$i++) {
$graph->moveTo($minx,$i);$graph->lineTo($maxx,$i,'gray',1);
}
$graph->moveTo($minx, 0);
$graph->arrowTo($maxx, 0, 'black', 2);
$graph->arrowTo($minx, 0, 'black', 2);
$graph->moveTo(0,$miny);
$graph->arrowTo(0,$maxy, 'black', 2);
$graph->arrowTo(0,$miny, 'black', 2);
$graph->lb(new Label($maxx-0.1, 0.4, 'x', 'black', 'bottom', 'right'));
$graph->lb(new Label(0.4,$maxy-0.1, 'y', 'black', 'top', 'left'));

for ($i =$minx+1; $i <$maxx; $i++) { if ($i != 0 && $i % 5 == 0 ||$i == 1) {
$graph->lb(new Label($i,-0.1,$i, 'black', 'top','center')); } } for ($i = $miny+1;$i < $maxy;$i++) {
if ($i != 0 &&$i % 5 == 0 || $i == 1) {$graph->lb(new Label(-0.1,$i,$i, 'black', 'middle', 'right'));
}
}

for ($i = 0;$i < $numzeros;$i++) {
$graph->stamps(closed_circle($zeros[$i],0,'blue')); }$fun = new Fun($expn,$graph);
$fun->steps(1500);$fun->domain($minx,$maxx);

$zerosans = List(@zeros); for ($i = 0; $i <$numzeros; $i++) { if ($powers[$i] % 2 == 0) {$mltpy[$i] = "even"; } else {$mltpy[$i] = "odd"; } } for ($i = 0; $i <$numzeros; $i++) {$answers[2*$i] =$zeros[$i];$answers[2*$i+1] =$mltpy[$i] }$ma = MultiAnswer(@answers)->with(
singleResult => 0,
checker => sub {
$num = scalar(@answers); my ($correct, $student,$self) = @_;
my @ac = @{$correct}; my @st = @{$student};
my @ans = (0..scalar(@ac)/2);
for ($i = 0;$i < $num;$i++) {
$returnans[$i] = 0;
}
for ($i = 0;$i < $num/2;$i++) {
$hasbeenused[$i] = 0;
}
for ($i = 0;$i < scalar(@ac)/2; $i++) {$ans[$i] = [$ac[2*$i],$ac[2*$i+1]]; } for ($i = 0; $i < scalar(@ac)/2;$i++) {
for ($j = 0;$j < scalar(@ac)/2; $j++) { if ($ans[$i][0] ==$st[2*$j] &&$ans[$i][1] ==$st[2*$j+1] &&$hasbeenused[$i] != 1) {$returnans[0][2*$j] = 1;$returnans[0][2*$j+1] = 1;$hasbeenused[$i] = 1; } } } for ($i = 0; $i <$num; $i++) {$reans[0][$i] =$returnans[0][$i]; } return @reans; } );$degree = $powers[0]; for ($i = 1; $i <$numzeros; $i++) {$degree = $degree +$powers[$i]; } if ($degree % 2 == 0) {
$degreeans = "even"; } else {$degreeans = "odd";
}

if ($a < 0) {$leadcoeffans = "negative";
} else {
$leadcoeffans = "positive"; }$dispzeros = "$zeros[0]";$dispmults = "$mltpy[0]"; for ($i = 1; $i <$numzeros; $i++) {$dispzeros = ($dispzeros).", ".($zeros[$i]);$dispmults = ($dispmults).", ".($mltpy[$i]); }$degree = PopUp([" ","even", "odd"], "$degreeans");$lead_coef_sgn = PopUp([" ","positive", "negative"], "$leadcoeffans"); ################################################################# # state the problem ################################################################# Context()->texStrings; BEGIN_TEXT$PAR
$BCENTER \{ image(insertGraph($graph), width=>$gsize, height=>$gsize, tex_size=>300 ); \}
$ECENTER$PAR

Find the following information pertaining to the polynomial, $$f(x)$$, graphed above.
$PAR (a) The zeros of $$f(x)$$ are $$x =$$ \{ans_rule(15)\} (separate by commas) and END_TEXT for ($i = 0; $i <$numzeros; $i++) { if ($i == $numzeros-2) { BEGIN_TEXT$PAR
$SPACE$SPACE $SPACE$SPACE  the zero $$x =$$ \{$ma->ans_rule(5)\} has \{$ma->ans_rule(5)\} multiplicity and,
$PAR END_TEXT } elsif ($i == $numzeros-1) { BEGIN_TEXT$PAR
$SPACE$SPACE $SPACE$SPACE  the zero $$x =$$ \{$ma->ans_rule(5)\} has \{$ma->ans_rule(5)\} multiplicity.
$PAR END_TEXT } else { BEGIN_TEXT$PAR
$SPACE$SPACE $SPACE$SPACE  the zero $$x =$$ \{$ma->ans_rule(5)\} has \{$ma->ans_rule(5)\} multiplicity,
$PAR END_TEXT } } BEGIN_TEXT$PAR
(b) The degree of $$f(x)$$ is \{$degree->menu()\}.$PAR
(c) The leading coefficient of $$f(x)$$ is \{$lead_coef_sgn->menu()\}. END_TEXT ################################################################# # check the answer ################################################################# ANS(Compute($zerosans)->cmp());
ANS($ma->cmp()); ANS($degree->cmp());
ANS($lead_coef_sgn->cmp()); ################################################################# # use PeriodicRerandomization to write the answer and generate a new # version of the problem ################################################################# if ($attempts_modp == 0 && $actualAttempts != 0) { BEGIN_TEXT$PAR
${BBOLD}Answer:${EBOLD}
$PAR (a) The zeros are $$x = dispzeros$$ with$dispmults multiplicity, respectively.
$PAR (b) The degree is$degreeans.
$PAR (c) The leading coefficient is$leadcoeffans.
$PAR END_TEXT } else { BEGIN_TEXT$PAR
${BBOLD}Help:${EBOLD} Type ${BBOLD}even${EBOLD} or ${BBOLD}odd${EBOLD} for multiplicities and the degree. Type ${BBOLD}pos${EBOLD} for positive or ${BBOLD}neg${EBOLD} for negative for the leading coefficient. You must have the corresponding multiplicity correct for each zero in order to receive credit!
$BR END_TEXT } Context()->normalStrings; PeriodicStatus(); ENDDOCUMENT(); In reply to Patrick Spencer ### Re: Adding pop-up lists to MultiAnswers by Davide Cervone - In fact, here is a version that uses PopUp's in the way that I think you are suggesting: DOCUMENT(); loadMacros( "AnswerFormatHelp.pl", "MathObjects.pl", "PeriodicRerandomization.pl", "PGgraphmacros.pl", "PGstandard.pl", "parserMultiAnswer.pl", "parserPopUp.pl", ); TEXT(beginproblem()); PeriodicRerandomization("3");$refreshCachedImages = 1;
$showPartialCorrectAnswers = 1; ################################################################# # setup contexts and variables ################################################################# Context("Numeric"); Context()->strings->add("even"=>{}, "e"=>{alias=>"even"}, "odd"=>{}, "o"=>{alias=>"odd"}, "positive"=>{}, "pos"=>{alias=>"positive"}, "p"=>{alias=>"positive"}, "negative"=>{}, "neg"=>{alias=>"negative"}, "n"=>{alias=>"negative"}); Context()->{error}{msg}{"Operands of '*' can't be words"} = " "; #Context()->{error}{msg}{"Can't convert an Empty Value to a Real Number"} = " ";$numzeros = random(2,5);
do {
# posszeros = possible zeros?
@posszeros = (-10..10);
# zeros = actual zeros?
@zeros = (1..$numzeros); for ($i = 0; $i <$numzeros; $i++) {$zeros[$i] = list_random(@posszeros); if ($numzeros == 2 || $numzeros == 3) {$powers[$i] = random(2,3); } else {$powers[$i] = random(1,2); }$removed = 0;
$count = 0; do { if ($posszeros[$count] ==$zeros[$i]) {$tmp = $posszeros[$count];
$posszeros[$count] = $posszeros[scalar(@posszeros)-1];$posszeros[scalar(@posszeros)-1] = $tmp; pop(@posszeros);$removed = 1;
}
$count++; } until ($removed || $count == scalar(@posszeros));$removed = 0;
$count = 0; do { if ($posszeros[$count] ==$zeros[$i]-1) {$tmp = $posszeros[$count];
$posszeros[$count] = $posszeros[scalar(@posszeros)-1];$posszeros[scalar(@posszeros)-1] = $tmp; pop(@posszeros);$removed = 1;
}
$count++; } until ($removed || $count == scalar(@posszeros));$removed = 0;
$count = 0; do { if ($posszeros[$count] ==$zeros[$i]+1) {$tmp = $posszeros[$count];
$posszeros[$count] = $posszeros[scalar(@posszeros)-1];$posszeros[scalar(@posszeros)-1] = $tmp; pop(@posszeros);$removed = 1;
}
$count++; } until ($removed || $count == scalar(@posszeros)); }$yint = 1;
for ($i = 0;$i < $numzeros;$i++) {
$yint =$yint*((-$zeros[$i])**$powers[$i]);
}

# this sub routine represents what is to be the graphed polynomial
sub poly {
my $x = shift();$prod = 1;
for ($i = 0;$i < $numzeros;$i++) {
$prod =$prod*(($x -$zeros[$i])**$powers[$i]); } return$prod;
};

# sort zeros from low to high;
$swapped = 1;$count = 0;
while ($swapped) {$swapped = 0;
$count++; for ($i = 0; $i <$numzeros - $count;$i++) {
if ($zeros[$i] > $zeros[$i+1]) {
$tmp =$zeros[$i];$zeros[$i] =$zeros[$i+1];$zeros[$i+1] =$tmp;
$swapped = 1; } } } # this next bit is to be sure that the gap between the low or high point # between zeros and the x-axis is not too small for ($i = 0; $i <$numzeros-1; $i++) {$maxs[$i] = 0; } for ($j = 0; $j <$numzeros-1; $j++) {$steps = 100;
$stepsize = ($zeros[$j+1] -$zeros[$j])/$steps;
for ($k = 0;$k < $steps;$k++) {
$tmp = abs(poly($zeros[$j]+$k*$stepsize)); if ($tmp > $maxs[$j]) {
$maxs[$j] = $tmp; } } }$min = min(@maxs);
$max = max(@maxs);$scale = random(5,9);
$a = random(-1,1,2)*$scale/max($max);$tmp1 = abs($min*$a);
$tmp2 = abs($max*$a); } until (abs($tmp2/$tmp1) < 3*$scale/2);

$expn = sub { my$x = shift();
$prod =$a;
for ($i = 0;$i < $numzeros;$i++) {
$prod =$prod*(($x -$zeros[$i])**$powers[$i]); } return$prod;
};

$minx = min($zeros[0],0)-3;
$miny = -10;$maxx = max($zeros[$numzeros-1],0)+3;
$maxy = 10;$gsize = 400;

$graph = init_graph($minx, $miny,$maxx, $maxy, size=>[$gsize,$gsize]); for ($i = $minx;$i <= $maxx;$i++) {
$graph->moveTo($i,$miny);$graph->lineTo($i,$maxy,'gray',1);
}
for ($i =$miny; $i <=$maxy; $i++) {$graph->moveTo($minx,$i);
$graph->lineTo($maxx,$i,'gray',1); }$graph->moveTo($minx, 0);$graph->arrowTo($maxx, 0, 'black', 2);$graph->arrowTo($minx, 0, 'black', 2);$graph->moveTo(0, $miny);$graph->arrowTo(0, $maxy, 'black', 2);$graph->arrowTo(0, $miny, 'black', 2);$graph->lb(new Label($maxx-0.1, 0.4, 'x', 'black', 'bottom', 'right'));$graph->lb(new Label(0.4, $maxy-0.1, 'y', 'black', 'top', 'left')); for ($i = $minx+1;$i < $maxx;$i++) {
if ($i != 0 &&$i % 5 == 0 || $i == 1) {$graph->lb(new Label($i,-0.1,$i, 'black', 'top','center'));
}
}

for ($i =$miny+1; $i <$maxy; $i++) { if ($i != 0 && $i % 5 == 0 ||$i == 1) {
$graph->lb(new Label(-0.1,$i,$i, 'black', 'middle', 'right')); } } for ($i = 0; $i <$numzeros; $i++) {$graph->stamps(closed_circle($zeros[$i],0,'blue'));
}

$fun = new Fun($expn, $graph);$fun->steps(1500);
$fun->domain($minx,$maxx);$zerosans = List(@zeros);

for ($i = 0;$i < $numzeros;$i++) {
if ($powers[$i] % 2 == 0) {
$mltpy[$i] = "even";
} else {
$mltpy[$i] = "odd";
}
}

for ($i = 0;$i < $numzeros;$i++) {
$answers[2*$i] = $zeros[$i];
$answers[2*$i+1] = PopUp([" ","even","odd"],$mltpy[$i]);
}

$ma = MultiAnswer(@answers)->with( singleResult => 0, allowBlankAnswers => 1, checker => sub {$num = scalar(@answers);
my ($correct,$student, $self) = @_; my @ac = @{$correct};
my @st = @{$student}; my @ans = (0..scalar(@ac)/2); for ($i = 0; $i <$num; $i++) {$returnans[$i] = 0; } for ($i = 0; $i <$num/2; $i++) {$hasbeenused[$i] = 0; } for ($i = 0; $i < scalar(@ac)/2;$i++) {
$ans[$i] = [$ac[2*$i],$ac[2*$i+1]];
}
for ($i = 0;$i < scalar(@ac)/2; $i++) { for ($j = 0; $j < scalar(@ac)/2;$j++) {
if ($ans[$i][0] == $st[2*$j] && $ans[$i][1] == $st[2*$j+1] && $hasbeenused[$i] != 1) {
$returnans[0][2*$j] = 1;
$returnans[0][2*$j+1] = 1;
$hasbeenused[$i] = 1;
}
}
}
for ($i = 0;$i < $num;$i++) {
$reans[0][$i] = $returnans[0][$i];
}
return @reans;
}
);

$degree =$powers[0];
for ($i = 1;$i < $numzeros;$i++) {
$degree =$degree + $powers[$i];
}
if ($degree % 2 == 0) {$degreeans = "even";
} else {
$degreeans = "odd"; } if ($a < 0) {
$leadcoeffans = "negative"; } else {$leadcoeffans = "positive";
}

$dispzeros = "$zeros[0]";
$dispmults = "$mltpy[0]";
for ($i = 1;$i < $numzeros;$i++) {
$dispzeros = ($dispzeros).", ".($zeros[$i]);
$dispmults = ($dispmults).", ".($mltpy[$i]);
}

$degree = PopUp([" ","even", "odd"], "$degreeans");
$lead_coef_sgn = PopUp([" ","positive", "negative"], "$leadcoeffans");

#################################################################
# state the problem
#################################################################

Context()->texStrings;
BEGIN_TEXT

$PAR$BCENTER
\{ image(insertGraph($graph), width=>$gsize, height=>$gsize, tex_size=>300 ); \}$ECENTER
$PAR Find the following information pertaining to the polynomial, $$f(x)$$, graphed above.$PAR
(a) The zeros of $$f(x)$$ are $$x =$$ \{ans_rule(15)\} (separate by commas) and
END_TEXT

for ($i = 0;$i < $numzeros;$i++) {
if ($i ==$numzeros-2) {
BEGIN_TEXT
$PAR$SPACE $SPACE$SPACE $SPACE the zero $$x =$$ \{$ma->ans_rule(5)\} has \{$ma->ans_rule(5)\} multiplicity and,$PAR
END_TEXT
} elsif ($i ==$numzeros-1) {
BEGIN_TEXT
$PAR$SPACE $SPACE$SPACE $SPACE the zero $$x =$$ \{$ma->ans_rule(5)\} has \{$ma->ans_rule(5)\} multiplicity.$PAR
END_TEXT
} else {
BEGIN_TEXT
$PAR$SPACE $SPACE$SPACE $SPACE the zero $$x =$$ \{$ma->ans_rule(5)\} has \{$ma->ans_rule(5)\} multiplicity,$PAR
END_TEXT
}
}

BEGIN_TEXT
$PAR (b) The degree of $$f(x)$$ is \{$degree->menu()\}.
$PAR (c) The leading coefficient of $$f(x)$$ is \{$lead_coef_sgn->menu()\}.
END_TEXT

#################################################################
#################################################################
ANS(Compute($zerosans)->cmp()); ANS($ma->cmp());
ANS($degree->cmp()); ANS($lead_coef_sgn->cmp());

#################################################################
# use PeriodicRerandomization to write the answer and generate a new
# version of the problem
#################################################################
if ($attempts_modp == 0 &&$actualAttempts != 0) {
BEGIN_TEXT
$PAR${BBOLD}Answer:${EBOLD}$PAR
(a) The zeros are $$x = dispzeros$$ with $dispmults multiplicity, respectively.$PAR
(b) The degree is $degreeans.$PAR
(c) The leading coefficient is $leadcoeffans.$PAR
END_TEXT
} else {
BEGIN_TEXT
$PAR${BBOLD}Help:${EBOLD} Type${BBOLD}even${EBOLD} or${BBOLD}odd${EBOLD} for multiplicities and the degree. Type${BBOLD}pos${EBOLD} for positive or${BBOLD}neg${EBOLD} for negative for the leading coefficient. You must have the corresponding multiplicity correct for each zero in order to receive credit!$BR
END_TEXT
}
Context()->normalStrings;
PeriodicStatus();

ENDDOCUMENT();


by Davide Cervone -
You might also try using something like
     singleResult => 1,
format => "%s is %s, %s is %s, %s is %s",
tex_format => "%s\text{ is }%s,\ %s\text{ is }%s,\ %s \text{ is } %s",

with the MultiAnswer object to reduce the size of the results table when a student enters their answers.

by Patrick Spencer -
Davide,

I'm a little confused about what was the part in your first reply you were suggesting I implement. I didn't see a reference to the \$ma = MultiAnswer(@answers) object.

I think we are just going to leave it as it is because the chance of messing something up is too much.

Again thanks for the reply! There's a lot of good perl code I hadn't known about and can use later.

Best,
Patrick