|
| 1 | +# A group of macros used in the Alfred problem library. |
| 2 | + |
| 3 | +sub _Alfredmacros_init {} #don't reload these macros. |
| 4 | + |
| 5 | + |
| 6 | +# Given a point (x,y) this macro computes the angle with respect to x-axis. The angle will be between 0 and 2pi. |
| 7 | +sub inversetrig |
| 8 | +{my $refangle = arctan(abs($_[1]/$_[0])); |
| 9 | + if($_[0] == 0) |
| 10 | + {0} |
| 11 | + elsif ($_[0]>0) |
| 12 | + {if($_[1] == 0) |
| 13 | + {0} |
| 14 | + elsif($_[1]>0) |
| 15 | + {$refangle;} |
| 16 | + else |
| 17 | + {2*pi-$refangle;} |
| 18 | + } |
| 19 | + else |
| 20 | + {if($_[1] == 0) |
| 21 | + {pi} |
| 22 | + elsif($_[1]>0) |
| 23 | + {pi-$refangle;} |
| 24 | + else |
| 25 | + {pi+$refangle} |
| 26 | + } |
| 27 | +} |
| 28 | + |
| 29 | +## Compute the max and min of an array of numbers |
| 30 | + |
| 31 | + |
| 32 | + |
| 33 | +#This macro prevents students from double clicking in an answer box. This macro #is necessary for multiple integral problems where the answer box is typeset |
| 34 | +#into the integration symbols. |
| 35 | +sub doubleclickprevent |
| 36 | +{TEXT(MODES( |
| 37 | + TeX => "", |
| 38 | + HTML => "<SCRIPT>if (window.jsMath) {jsMath.Click.DblClick = function () {}}</SCRIPT>" |
| 39 | + )); |
| 40 | +} |
| 41 | + |
| 42 | +#The problems that have the answer box in the limits must be displayed in JS |
| 43 | +#math mode. This macro warns the user to use JS math mode if they are not. |
| 44 | +sub jsMathwarn |
| 45 | +{TEXT(MODES( |
| 46 | + TeX => '', |
| 47 | + HTML_jsMath => '', |
| 48 | + HTML => $HR."Warning: to use this problem, you need to". |
| 49 | + "select jsMath mode in the Display Options panel at the left".$HR, |
| 50 | +)); |
| 51 | +} |
| 52 | + |
| 53 | +sub jsmathmode |
| 54 | +{ |
| 55 | +TEXT(MODES( |
| 56 | + TeX => '', |
| 57 | + HTML_jsMath => '', |
| 58 | + HTML => $HR."Warning: to use this problem, you need to ". |
| 59 | + "select jsMath mode in the Display Options panel at the left".$HR, |
| 60 | +)); |
| 61 | +TEXT(MODES( |
| 62 | + TeX => "", |
| 63 | + HTML => "<SCRIPT>if (window.jsMath) {jsMath.Click.DblClick = function () {}}</SCRIPT>" |
| 64 | + )); |
| 65 | + |
| 66 | +} |
| 67 | + |
| 68 | +sub mathjaxmode |
| 69 | +{TEXT(MODES( |
| 70 | + TeX => '', |
| 71 | + HTML_MathJax => '', |
| 72 | + HTML => $HR."Warning: to use this problem, you need to ". |
| 73 | + "select MathJax mode in the Display Options panel at the left".$HR, |
| 74 | +)); |
| 75 | +} |
| 76 | + |
| 77 | +#This subroutine includes the Strang's textbook into a problem, you have to |
| 78 | +#feed it the chapter and section. It is assumed that the book is in the course |
| 79 | +#directory and is labeled strangtextbook. The parameters are |
| 80 | +#strang(chapter,section,(optional) section title. |
| 81 | +#example \{&strang(16,5,"surface integrals")\}. |
| 82 | +$wwstrang = "http://webwork.alfred.edu/webwork2_course_files/strangcalculus"; |
| 83 | +sub strang |
| 84 | +{ |
| 85 | +htmlLink(qq!$wwstrang/Strang-$_[0]-$_[1].pdf!,"$_[0].$_[1] $_[2] from Gilbert Strang's Calculus",q/TARGET="new_window"/) |
| 86 | +} |
| 87 | + |
| 88 | +#Inserts a link to a trig table in the problem. |
| 89 | +#example \{&trig_table()\} |
| 90 | +sub trig_table |
| 91 | +{ |
| 92 | +htmlLink(qq!$wwstrang/trig_identities.pdf!,"Trig Identities",q/TARGET="new_window"/) |
| 93 | +} |
| 94 | + |
| 95 | +sub strang_index |
| 96 | +{ |
| 97 | +htmlLink(qq!$wwstrang/Index.pdf!,"Index",q/TARGET="new_window"/) |
| 98 | +} |
| 99 | + |
| 100 | +sub strang_TOC |
| 101 | +{ |
| 102 | +htmlLink(qq!$wwstrang/TOC.pdf!,"Table of Contents",q/TARGET="new_window"/) |
| 103 | +} |
| 104 | + |
| 105 | +sub product_Rule_cmp { |
| 106 | +my ( $correct, $student, $self ) = @_; |
| 107 | + my ( $f1stu, $f2stu,$f3stu,$f4stu ) = @{$student}; |
| 108 | + my ( $f1, $f2, $f3, $f4 ) = @{$correct}; |
| 109 | + my @fgrade = (0,0,0,0); |
| 110 | + my @fstu = ($f1stu,$f2stu,$f3stu,$f4stu); |
| 111 | + my @fcorrect = ($f1,$f2,$f3,$f4); |
| 112 | + #we will associate each student answer with a prime number, noting which student answer is in which blank. This allows us to make use of the fundamental theorem of arithmetic. |
| 113 | + my @prime = (2,3,5,7); |
| 114 | + my @answerblank = (0,0,0,0); |
| 115 | + |
| 116 | + for($i=0;$i<4;$i++){ |
| 117 | + for($j=0;$j<4;$j++){ |
| 118 | + if(($fcorrect[$i]==$fstu[$j])&&($answerblank[$j]==0)){ |
| 119 | + {$answerblank[$j] = $prime[$i]; |
| 120 | + $j = 4 # you have to terminate the inner loop in this case for the special case of f=e^x where f and f' are the same. |
| 121 | + } |
| 122 | + } |
| 123 | + } |
| 124 | + }; |
| 125 | + for($i=0;$i<4;$i++){ |
| 126 | + if(!$answerblank[$i]){ |
| 127 | + $self->setMessage($i+1,"All of your answers should be $f, $g, or a derivative of one of these functions"); |
| 128 | + } |
| 129 | + } |
| 130 | +#now we rely on the fact that products of primes are unique. First we check to see if all of the blanks are correct |
| 131 | + if ((($answerblank[0]*$answerblank[1] == 6)&&($answerblank[2]*$answerblank[3] == 35))||(($answerblank[0]*$answerblank[1] == 35)&&($answerblank[2]*$answerblank[3] == 6))){ |
| 132 | + @fgrade = (1,1,1,1); |
| 133 | + } |
| 134 | +#now check to see if the first pair of blanks is correct, knowing one pair is not |
| 135 | + elsif ($answerblank[0]*$answerblank[1] == 6){ |
| 136 | + if (($answerblank[2] == 5)||($answerblank[2] == 7)){ |
| 137 | + @fgrade = (1,1,1,0); |
| 138 | + } |
| 139 | + elsif (($answerblank[3] == 5)||($answerblank[3] == 7)){ |
| 140 | + @fgrade = (1,1,0,1); |
| 141 | + } |
| 142 | + else {@fgrade = (1,1,0,0);} |
| 143 | + } |
| 144 | + elsif ($answerblank[0]*$answerblank[1] == 35){ |
| 145 | + if (($answerblank[2] == 2)||($answerblank[2] == 3)){ |
| 146 | + @fgrade = (1,1,1,0); |
| 147 | + } |
| 148 | + elsif (($answerblank[3] == 2)||($answerblank[3] == 3)){ |
| 149 | + @fgrade = (1,1,0,1); |
| 150 | + } |
| 151 | + else {@fgrade = (1,1,0,0);} |
| 152 | + } |
| 153 | +#if both sets are not correct, and the first set is not correct, check to see if the last pair are |
| 154 | + elsif ($answerblank[2]*$answerblank[3] == 6){ |
| 155 | + if (($answerblank[0] == 5)||($answerblank[0] == 7)){ |
| 156 | + @fgrade = (1,0,1,1); |
| 157 | + } |
| 158 | + elsif (($answerblank[1] == 5)||($answerblank[1] == 7)){ |
| 159 | + @fgrade = (0,1,1,1); |
| 160 | + } |
| 161 | + else {@fgrade = (0,0,1,1);} |
| 162 | + } |
| 163 | + elsif ($answerblank[2]*$answerblank[3] == 35){ |
| 164 | + if (($answerblank[0] == 2)||($answerblank[0] == 3)){ |
| 165 | + @fgrade = (1,0,1,1); |
| 166 | + } |
| 167 | + elsif (($answerblank[1] == 2)||($answerblank[1] == 3)){ |
| 168 | + @fgrade = (0,1,1,1); |
| 169 | + } |
| 170 | + else {@fgrade = (0,0,1,1);} |
| 171 | + } |
| 172 | +#at this point they don't have a matched set of blanks correct. look for a single function in each pair that is right. You have to make sure you only get one for each pair of answer blanks. |
| 173 | + else{ |
| 174 | + if (($answerblank[0])&&($answerblank[2])&&($answerblank[0]*$answerblank[2] !=6)&&($answerblank[0]*$answerblank[2] !=35)&&($answerblank[0]!=$answerblank[2])){ |
| 175 | + @fgrade = (1,0,1,0); |
| 176 | + } |
| 177 | + elsif (($answerblank[0])&&($answerblank[3])&&($answerblank[0]*$answerblank[3] !=6)&&($answerblank[0]*$answerblank[3] !=35)&&($answerblank[0]!=$answerblank[3])){ |
| 178 | + @fgrade = (1,0,0,1); |
| 179 | + } |
| 180 | + elsif (($answerblank[1])&&($answerblank[2])&&($answerblank[1]*$answerblank[2] !=6)&&($answerblank[1]*$answerblank[2] !=35)&&($answerblank[1]!=$answerblank[2])){ |
| 181 | + @fgrade = (0,1,1,0); |
| 182 | + } |
| 183 | + elsif (($answerblank[1])&&($answerblank[3])&&($answerblank[1]*$answerblank[3] !=6)&&($answerblank[1]*$answerblank[3] !=35)&&($answerblank[1]!=$answerblank[3])){ |
| 184 | + @fgrade = (0,1,0,1); |
| 185 | + } |
| 186 | + elsif ($answerblank[0]){@fgrade = (1,0,0,0)} |
| 187 | + elsif ($answerblank[1]){@fgrade = (0,1,0,0)} |
| 188 | + elsif ($answerblank[2]){@fgrade = (0,0,1,0)} |
| 189 | + elsif ($answerblank[3]){@fgrade = (0,0,0,1)} |
| 190 | + }; |
| 191 | + return [@fgrade]; |
| 192 | +} |
| 193 | + |
| 194 | + |
| 195 | + |
| 196 | +sub check_boundary_conditions { |
| 197 | +my ( $correct, $student, $self ) = @_; |
| 198 | + return product_Rule_cmp(@_) ; |
| 199 | + } |
| 200 | + |
| 201 | +sub Snxy(){ |
| 202 | + my %args = @_; |
| 203 | + my @x = @{$args{inputs}}; |
| 204 | + my @y = @{$args{outputs}}; |
| 205 | + my $m = $args{m}; |
| 206 | + my $n = $args{n}; |
| 207 | + my $i = 0; |
| 208 | + my $sum = 0; |
| 209 | + if ($#x == $#y){ |
| 210 | + for ($i=0;$i <= $#x;$i++){ |
| 211 | + $sum = $sum + ($x[$i])**($n)*($y[$i])**($m); |
| 212 | + } |
| 213 | + } |
| 214 | + else {$sum = 0}; |
| 215 | + return $sum; |
| 216 | +} |
| 217 | + |
| 218 | +### To use the macros your problem must include unionTables.pl, and of course Alfredmacros.pl |
| 219 | +### Table integral returns a string that can be included in a Table to output an integral whose upper and lower limits |
| 220 | +### of integration can be answer blanks. There are several optional parameters: |
| 221 | +### width - change the width of the answer blanks. defaults to 3. |
| 222 | +### lowerwidth - change the width of the lower answer blank. defaults to width |
| 223 | +### upperwidth - change the width of the upper answer blank. defaults to width. |
| 224 | +### upper - the uppper limit of integration, does not have to be an answer blank, defaults to answer blank with width "width" |
| 225 | +### lower - the lower limit of integration, does not have to be an answer blank, defaults to answer blank with width "width" |
| 226 | +### limits - boolean, if 1 puts the limits of integration above and below the integral symbol, if 0 puts them after the integral symbol. |
| 227 | +### default is 1. |
| 228 | +### Your code must include unionTables.pl, and of course Alfredmacros.pl |
| 229 | +### An example: |
| 230 | +### \{BeginTable(center=>0). |
| 231 | +### Row([tableintegral(), |
| 232 | +### ],separation=>2). |
| 233 | +### EndTable(); |
| 234 | +### \} |
| 235 | +### which will print an integral with answer blank on the upper and lower limits with the default length of 3 |
| 236 | +### |
| 237 | +### This example prints out a double integral, the first integral with answer blanks with width 10, the second integral |
| 238 | +### has 0 for the lower limit of integration and an answer blank with width 5 for the upper limit of integration. |
| 239 | +### The default limits of integratin are answer blanks with width 3, in this case the default width was overridden to 5 |
| 240 | +### and the default lower limit was changed to a zero. |
| 241 | +### \{BeginTable(center=>0). |
| 242 | +### Row([tableintegral(width=>10,limits=>'\(0\)'),tableintegral(width=>5,lower=>'\(0\)',limits=>0), |
| 243 | +### ],separation=>2). |
| 244 | +### EndTable(); |
| 245 | +### \} |
| 246 | +### An example where the width of the upper and lower answer blanks have different widths. |
| 247 | +### \{BeginTable(center=>0). |
| 248 | +### Row([tableintegral(lowerwidth=>10,upperwidth=>1) |
| 249 | +### ],separation=>2). |
| 250 | +### EndTable(); |
| 251 | +### \} |
| 252 | + |
| 253 | +sub tableintegral{ |
| 254 | + my %arg = @_; |
| 255 | + my $width = delete $arg{width} // 3; |
| 256 | + my $lowerwidth = delete $arg{lowerwidth} // $width; |
| 257 | + my $upperwidth = delete $arg{upperwidth} // $width; |
| 258 | + my $lower = delete $arg{lower} // ans_rule($lowerwidth); |
| 259 | + my $upper = delete $arg{upper} // ans_rule($upperwidth); |
| 260 | + my $limits = delete $arg{limits} // 1; |
| 261 | + if ($limits == 1){ |
| 262 | + return $upper.$BR.'\(\displaystyle\int\)'.$BR.$lower |
| 263 | + } |
| 264 | + else { |
| 265 | + return '\(\displaystyle\int\)',$upper.$BR.$BR.$lower |
| 266 | + } |
| 267 | +}; |
| 268 | + |
| 269 | +# a sum with answer blanks for the summation variable, lower limit, and upper limit |
| 270 | +#\{ BeginTable(center=>0). |
| 271 | +# Row([tablesum(width=>10), |
| 272 | +# ],separation=>2). |
| 273 | +# EndTable(); |
| 274 | +#\} |
| 275 | +# a sum with answer blanks for the upper and lower limits, and the summation variable is i |
| 276 | +#\{ BeginTable(center=>0). |
| 277 | +# Row([tablesum(width=>10,sumvariable=>'i'), |
| 278 | +# ],separation=>2). |
| 279 | +# EndTable(); |
| 280 | +#\} |
| 281 | +# a sum with answer blanks for the upper and lower limits, and summation variable is not used. |
| 282 | +#\{ BeginTable(center=>0). |
| 283 | +# Row([tablesum(width=>10,usesumvariable=>0), |
| 284 | +# ],separation=>2). |
| 285 | +# EndTable(); |
| 286 | +#\} |
| 287 | +# sum from n = 1 to infinity |
| 288 | +#\{ BeginTable(center=>0). |
| 289 | +# Row([tablesum(sumvariable=>'\(n\)',lower=>'\(1\)', upper=>'\(\hskip 3pt\infty\)') ],separation=>2). |
| 290 | +# EndTable(); |
| 291 | +#\} |
| 292 | + |
| 293 | +sub tablesum{ |
| 294 | + my %arg = @_; |
| 295 | + my $width = delete $arg{width} // 3; |
| 296 | + my $lowerwidth = delete $arg{lowerwidth} // $width; |
| 297 | + my $upperwidth = delete $arg{upperwidth} // $width; |
| 298 | + my $lower = delete $arg{lower} // ans_rule($lowerwidth); |
| 299 | + my $upper = delete $arg{upper} // ans_rule($upperwidth); |
| 300 | + my $limits = delete $arg{limits} // 1; |
| 301 | + my $sumvariable = delete $arg{sumvariable} // ans_rule($width); |
| 302 | + my $usesumvariable = delete $arg{usesumvariable} // 1; |
| 303 | + if ($usesumvariable == 0){ |
| 304 | + if ($limits == 1){ |
| 305 | + return $upper.$BR.'\(\displaystyle\sum\)'.$BR.$lower |
| 306 | + } |
| 307 | + else { |
| 308 | + return '\(\displaystyle\int\)',$upper.$BR.$BR.$lower |
| 309 | + } |
| 310 | + } |
| 311 | + else { |
| 312 | + if ($limits == 1){ |
| 313 | + return $upper.$BR.'\(\displaystyle\sum\)'.$BR.$sumvariable.'\( = \)'.$lower |
| 314 | + } |
| 315 | + else { |
| 316 | + return '\(\displaystyle\int\)',$upper.$BR.$BR.$sumvariable.'\( = \)'.$lower |
| 317 | + } |
| 318 | + } |
| 319 | +}; |
| 320 | + |
| 321 | + |
| 322 | +### Create a vertical bar with an upper and lower limit. |
| 323 | +sub tableevaluate{ |
| 324 | + my %arg = @_; |
| 325 | + my $width = delete $arg{width} // 3; |
| 326 | + my $lowerwidth = delete $arg{lowerwidth} // $width; |
| 327 | + my $upperwidth = delete $arg{upperwidth} // $width; |
| 328 | + my $lower = delete $arg{lower} // ans_rule($lowerwidth); |
| 329 | + my $upper = delete $arg{upper} // ans_rule($upperwidth); |
| 330 | + return |
| 331 | + '\(\Bigg\vert\)',$upper.$BR.$BR.$lower |
| 332 | +}; |
| 333 | + |
| 334 | +### Create a subscripted character |
| 335 | +sub tablesubscript{ |
| 336 | + my %arg = @_; |
| 337 | + my $width = delete $arg{width} // 3; |
| 338 | + my $lower = delete $arg{lower} // ans_rule($width); |
| 339 | + my $variable = delete $arg{variable} // 'c'; |
| 340 | + return |
| 341 | + $variable,$BR.$BR.$lower |
| 342 | +}; |
| 343 | + |
| 344 | +### Create a superscripted character |
| 345 | +sub tablesuperscript{ |
| 346 | + my %arg = @_; |
| 347 | + my $width = delete $arg{width} // 3; |
| 348 | + my $upper = delete $arg{upper} // ans_rule($width); |
| 349 | + my $variable = delete $arg{variable} // 'c'; |
| 350 | + return |
| 351 | + $variable,$upper.$BR.$BR.$BR |
| 352 | +}; |
| 353 | + |
| 354 | + |
| 355 | + |
| 356 | +### A fraction |
| 357 | +sub tablefrac{ |
| 358 | + my %arg = @_; |
| 359 | + my $width = delete $arg{width} // 3; |
| 360 | + my $lower = delete $arg{lower} // ans_rule($width); |
| 361 | + my $upper = delete $arg{upper} // ans_rule($width); |
| 362 | + my $barwidth = delete $arg{barwidth} // 10+$width; |
| 363 | + my $divisionbar = ""; |
| 364 | + for ($count = 1;$count <= $barwidth; $count++){ |
| 365 | + $divisionbar = $divisionbar."-"; |
| 366 | + } |
| 367 | + return $upper.$BR.$divisionbar.$BR.$lower |
| 368 | +}; |
| 369 | + |
0 commit comments