Parent Directory
|
Revision Log
answer rules can not be placed within equations in dvipng mode
1 #!/usr/local/bin/webwork-perl 2 3 #################################################################### 4 # Copyright @ 1995-1998 University of Rochester 5 # All Rights Reserved 6 #################################################################### 7 8 =head1 NAME 9 10 PGbasicmacros.pl --- located in the courseScripts directory 11 12 =head1 SYNPOSIS 13 14 15 16 =head1 DESCRIPTION 17 18 19 20 =cut 21 22 # this is equivalent to use strict, but can be used within the Safe compartment. 23 BEGIN{ 24 be_strict; 25 } 26 27 28 my $displayMode=$main::displayMode; 29 30 my ($PAR, 31 $BR, 32 $LQ, 33 $RQ, 34 $BM, 35 $EM, 36 $BDM, 37 $EDM, 38 $LTS, 39 $GTS, 40 $LTE, 41 $GTE, 42 $BEGIN_ONE_COLUMN, 43 $END_ONE_COLUMN, 44 $SOL, 45 $SOLUTION, 46 $HINT, 47 $US, 48 $SPACE, 49 $BBOLD, 50 $EBOLD, 51 $BITALIC, 52 $EITALIC, 53 $BCENTER, 54 $ECENTER, 55 $HR, 56 $LBRACE, 57 $RBRACE, 58 $LB, 59 $RB, 60 $DOLLAR, 61 $PERCENT, 62 $CARET, 63 $PI, 64 $E, 65 @ALPHABET, 66 ); 67 68 sub _PGbasicmacros_init { 69 $displayMode =$main::displayMode; 70 $main::PAR = PAR(); 71 $main::BR = BR(); 72 $main::LQ = LQ(); 73 $main::RQ = RQ(); 74 $main::BM = BM(); 75 $main::EM = EM(); 76 $main::BDM = BDM(); 77 $main::EDM = EDM(); 78 $main::LTS = LTS(); 79 $main::GTS = GTS(); 80 $main::LTE = LTE(); 81 $main::GTE = GTE(); 82 $main::BEGIN_ONE_COLUMN = BEGIN_ONE_COLUMN(); 83 $main::END_ONE_COLUMN = END_ONE_COLUMN(); 84 $main::SOL = SOLUTION_HEADING(); 85 $main::SOLUTION = SOLUTION_HEADING(); 86 $main::HINT = HINT_HEADING(); 87 $main::US = US(); 88 $main::SPACE = SPACE(); 89 $main::BBOLD = BBOLD(); 90 $main::EBOLD = EBOLD(); 91 $main::BITALIC = BITALIC(); 92 $main::EITALIC = EITALIC(); 93 $main::BCENTER = BCENTER(); 94 $main::ECENTER = ECENTER(); 95 $main::HR = HR(); 96 $main::LBRACE = LBRACE(); 97 $main::RBRACE = RBRACE(); 98 $main::LB = LB(); 99 $main::RB = RB(); 100 $main::DOLLAR = DOLLAR(); 101 $main::PERCENT = PERCENT(); 102 $main::CARET = CARET(); 103 $main::PI = PI(); 104 $main::E = E(); 105 @main::ALPHABET = ('A'..'ZZ'); 106 107 $PAR = PAR(); 108 $BR = BR(); 109 $LQ = LQ(); 110 $RQ = RQ(); 111 $BM = BM(); 112 $EM = EM(); 113 $BDM = BDM(); 114 $EDM = EDM(); 115 $LTS = LTS(); 116 $GTS = GTS(); 117 $LTE = LTE(); 118 $GTE = GTE(); 119 $BEGIN_ONE_COLUMN = BEGIN_ONE_COLUMN(); 120 $END_ONE_COLUMN = END_ONE_COLUMN(); 121 $SOL = SOLUTION_HEADING(); 122 $SOLUTION = SOLUTION_HEADING(); 123 $HINT = HINT_HEADING(); 124 $US = US(); 125 $SPACE = SPACE(); 126 $BBOLD = BBOLD(); 127 $EBOLD = EBOLD(); 128 $HR = HR(); 129 $LBRACE = LBRACE(); 130 $RBRACE = RBRACE(); 131 $LB = LB(); 132 $RB = RB(); 133 $DOLLAR = DOLLAR(); 134 $PERCENT = PERCENT(); 135 $CARET = CARET(); 136 $PI = PI(); 137 $E = E(); 138 @ALPHABET = ('A'..'ZZ'); 139 140 141 142 } 143 144 =head2 Answer blank macros: 145 146 These produce answer blanks of various sizes or pop up lists or radio answer buttons. 147 The names for the answer blanks are 148 generated implicitly. 149 150 ans_rule( width ) 151 tex_ans_rule( width ) 152 ans_radio_buttons(value1=>label1, value2,label2 => value3,label3=>...) 153 pop_up_list(@list) # list consists of (value => label, PR => "Product rule",...) 154 155 To indicate the checked position of radio buttons put a '%' in front of the value: C<ans_radio_buttons(1, 'Yes','%2','No')> 156 will have 'No' checked. C<tex_ans_rule> works inside math equations in C<HTML_tth> mode. It does not work in C<Latex2HTML> mode 157 since this mode produces gif pictures. 158 159 160 The following method is defined in F<PG.pl> for entering the answer evaluators corresponding 161 to answer rules with automatically generated names. The answer evaluators are matched with the 162 answer rules in the order in which they appear on the page. 163 164 ANS(ans_evaluator1, ans_evaluator2,...); 165 166 These are more primitive macros which produce answer blanks for specialized cases when complete 167 control over the matching of answers blanks and answer evaluators is desired. 168 The names of the answer blanks must be generated manually, and it is best if they do NOT begin 169 with the default answer prefix (currently AnSwEr). 170 171 labeled_ans_rule(name, width) # an alias for NAMED_ANS_RULE where width defaults to 20 if omitted. 172 173 NAMED_ANS_RULE(name, width) 174 NAMED_ANS_BOX(name, rows, cols) 175 NAMED_ANS_RADIO(name, value,label,) 176 NAMED_ANS_RADIO_EXTENSION(name, value,label) 177 NAMED_ANS_RADIO_BUTTONS(name,value1,label1,value2,label2,...) 178 check_box('-name' =>answer5,'-value' =>'statement3','-label' =>'I loved this course!' ) 179 NAMED_POP_UP_LIST($name, @list) # list consists of (value => tag, PR => "Product rule",...) 180 181 (Name is the name of the variable, value is the value given to the variable when this option is selected, 182 and label is the text printed next to the button or check box. Check box variables can have multiple values.) 183 184 NAMED_ANS_RADIO_BUTTONS creates a sequence of NAMED_ANS_RADIO and NAMED_ANS_RADIO_EXTENSION items which 185 are output either as an array or, in scalar context, as the array glued together with spaces. It is 186 usually easier to use this than to manually construct the radio buttons by hand. However, sometimes 187 extra flexibility is desiredin which case: 188 189 When entering radio buttons using the "NAMED" format, you should use NAMED_ANS_RADIO button for the first button 190 and then use NAMED_ANS_RADIO_EXTENSION for the remaining buttons. NAMED_ANS_RADIO requires a matching answer evalutor, 191 while NAMED_ANS_RADIO_EXTENSION does not. The name used for NAMED_ANS_RADIO_EXTENSION should match the name 192 used for NAMED_ANS_RADIO (and the associated answer evaluator). 193 194 195 The following method is defined in F<PG.pl> for entering the answer evaluators corresponding 196 to answer rules with automatically generated names. The answer evaluators are matched with the 197 answer rules in the order in which they appear on the page. 198 199 NAMED_ANS(name1 => ans_evaluator1, name2 => ans_evaluator2,...); 200 201 These auxiliary macros are defined in PG.pl 202 203 204 NEW_ANS_NAME( number ); # produces a new answer blank name from a number by adding a prefix (AnSwEr) 205 # and registers this name as an implicitly labeled answer 206 # Its use is paired with each answer evaluator being entered using ANS() 207 208 ANS_NUM_TO_NAME(number); # adds the prefix (AnSwEr) to the number, but does nothing else. 209 210 RECORD_ANS_NAME( name ); # records the order in which the answer blank is rendered 211 # This is called by all of the constructs above, but must 212 # be called explicitly if an input blank is constructed explictly 213 # using HTML code. 214 215 These are legacy macros: 216 217 ANS_RULE( number, width ); # equivalent to NAMED_ANS_RULE( NEW_ANS_NAME(number), width) 218 ANS_BOX( question_number,height, width ); # equivalent to NAMED_ANS_BOX( NEW_ANS_NAME(number), height, width) 219 ANS_RADIO( question_number, value,tag ); # equivalent to NAMED_ANS_RADIO( NEW_ANS_NAME(number), value,tag) 220 ANS_RADIO_OPTION( question_number, value,tag ); # equivalent to NAMED_ANS_RADIO_EXTENSION( ANS_NUM_TO_NAME(number), value,tag) 221 222 223 =cut 224 225 sub labeled_ans_rule { # syntactic sugar for NAMED_ANS_RULE 226 my($name,$col) = @_; 227 $col = 20 unless defined($col); 228 NAMED_ANS_RULE($name,$col); 229 } 230 231 sub NAMED_ANS_RULE { 232 my($name,$col) = @_; 233 my $len = 0.07*$col; 234 my $answer_value = ''; 235 $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name}); 236 if ($answer_value =~ /\0/ ) { 237 my @answers = split("\0", $answer_value); 238 $answer_value = shift(@answers); # use up the first answer 239 $main::rh_sticky_answers{$name}=\@answers; # store the rest 240 $answer_value= '' unless defined($answer_value); 241 } elsif (ref($answer_value) eq 'ARRAY') { 242 my @answers = @{ $answer_value}; 243 $answer_value = shift(@answers); # use up the first answer 244 $main::rh_sticky_answers{$name}=\@answers; # store the rest 245 $answer_value= '' unless defined($answer_value); 246 } 247 248 $answer_value =~ tr/$@//d; ## make sure student answers can not be interpolated by e.g. EV3 249 $name = RECORD_ANS_NAME($name); 250 MODES( 251 TeX => "\\mbox{\\parbox[t]{10pt}{\\hrulefill}}\\hrulefill\\quad ", 252 Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!, 253 HTML => "<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"$answer_value\">\n" 254 ); 255 } 256 257 sub NAMED_ANS_RULE_OPTION { # deprecated 258 &NAMED_ANS_RULE_EXTENSION; 259 } 260 261 sub NAMED_ANS_RULE_EXTENSION { 262 my($name,$col) = @_; 263 my $len = 0.07*$col; 264 my $answer_value = ''; 265 $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name}); 266 if ( defined($main::rh_sticky_answers{$name}) ) { 267 $answer_value = shift( @{$main::rh_sticky_answers{$name}}); 268 $answer_value = '' unless defined($answer_value); 269 } 270 $answer_value =~ tr/$@//d; ## make sure student answers can not be interpolated by e.g. EV3 271 MODES( 272 TeX => '\\hrulefill\\quad ', 273 Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!, 274 HTML => qq!<INPUT TYPE=TEXT SIZE=$col NAME = "$name" VALUE = "$answer_value">\n! 275 ); 276 } 277 278 sub ANS_RULE { #deprecated 279 my($number,$col) = @_; 280 my $name = NEW_ANS_NAME($number); 281 NAMED_ANS_RULE($name,$col); 282 } 283 284 285 sub NAMED_ANS_BOX { 286 my($name,$row,$col) = @_; 287 $row = 10 unless defined($row); 288 $col = 80 unless defined($col); 289 $name = RECORD_ANS_NAME($name); 290 my $len = 0.07*$col; 291 my $height = .07*$row; 292 my $answer_value = ''; 293 $answer_value = $main::inputs_ref->{$name} if defined( $main::inputs_ref->{$name} ); 294 $answer_value =~ tr/$@//d; ## make sure student answers can not be interpolated by e.g. EV3 295 my $out = M3( 296 qq!\\vskip $height in \\hrulefill\\quad !, 297 qq!\\begin{rawhtml}<TEXTAREA NAME="$name" ROWS="$row" COLS="$col" 298 WRAP="VIRTUAL">$answer_value</TEXTAREA>\\end{rawhtml}!, 299 qq!<TEXTAREA NAME="$name" ROWS="$row" COLS="$col" 300 WRAP="VIRTUAL">$answer_value</TEXTAREA>! 301 ); 302 $out; 303 } 304 305 sub ANS_BOX { #deprecated 306 my($number,$row,$col) = @_; 307 my $name = NEW_ANS_NAME($number); 308 NAMED_ANS_BOX($name,$row,$col); 309 } 310 311 sub NAMED_ANS_RADIO { 312 my $name = shift; 313 my $value = shift; 314 my $tag =shift; 315 $name = RECORD_ANS_NAME($name); 316 my $checked = ''; 317 if ($value =~/^\%/) { 318 $value =~ s/^\%//; 319 $checked = 'CHECKED' 320 } 321 if (defined($main::inputs_ref->{$name}) ) { 322 if ($main::inputs_ref->{$name} eq $value) { 323 $checked = 'CHECKED' 324 } else { 325 $checked = ''; 326 } 327 328 } 329 330 MODES( 331 TeX => qq!\\item{$tag}\n!, 332 Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!, 333 HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag! 334 ); 335 336 } 337 338 sub NAMED_ANS_RADIO_OPTION { #deprecated 339 &NAMED_ANS_RADIO_EXTENSION; 340 } 341 342 sub NAMED_ANS_RADIO_EXTENSION { 343 my $name = shift; 344 my $value = shift; 345 my $tag =shift; 346 347 348 my $checked = ''; 349 if ($value =~/^\%/) { 350 $value =~ s/^\%//; 351 $checked = 'CHECKED' 352 } 353 if (defined($main::inputs_ref->{$name}) ) { 354 if ($main::inputs_ref->{$name} eq $value) { 355 $checked = 'CHECKED' 356 } else { 357 $checked = ''; 358 } 359 360 } 361 362 MODES( 363 TeX => qq!\\item{$tag}\n!, 364 Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!, 365 HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag! 366 ); 367 368 } 369 370 sub NAMED_ANS_RADIO_BUTTONS { 371 my $name =shift; 372 my $value = shift; 373 my $tag = shift; 374 375 376 my @out = (); 377 push(@out, NAMED_ANS_RADIO($name, $value,$tag)); 378 my @buttons = @_; 379 while (@buttons) { 380 $value = shift @buttons; $tag = shift @buttons; 381 push(@out, NAMED_ANS_RADIO_OPTION($name, $value,$tag)); 382 } 383 (wantarray) ? @out : join(" ",@out); 384 } 385 sub ANS_RADIO { 386 my $number = shift; 387 my $value = shift; 388 my $tag =shift; 389 my $name = NEW_ANS_NAME($number); 390 NAMED_ANS_RADIO($name,$value,$tag); 391 } 392 393 sub ANS_RADIO_OPTION { 394 my $number = shift; 395 my $value = shift; 396 my $tag =shift; 397 398 399 my $name = ANS_NUM_TO_NAME($number); 400 NAMED_ANS_RADIO_OPTION($name,$value,$tag); 401 } 402 sub ANS_RADIO_BUTTONS { 403 my $number =shift; 404 my $value = shift; 405 my $tag = shift; 406 407 408 my @out = (); 409 push(@out, ANS_RADIO($number, $value,$tag)); 410 my @buttons = @_; 411 while (@buttons) { 412 $value = shift @buttons; $tag = shift @buttons; 413 push(@out, ANS_RADIO_OPTION($number, $value,$tag)); 414 } 415 (wantarray) ? @out : join(" ",@out); 416 } 417 418 sub NAMED_ANS_CHECKBOX { 419 my $name = shift; 420 my $value = shift; 421 my $tag =shift; 422 $name = RECORD_ANS_NAME($name); 423 424 my $checked = ''; 425 if ($value =~/^\%/) { 426 $value =~ s/^\%//; 427 $checked = 'CHECKED' 428 } 429 430 if (defined($main::inputs_ref->{$name}) ) { 431 if ($main::inputs_ref->{$name} eq $value) { 432 $checked = 'CHECKED' 433 } 434 else { 435 $checked = ''; 436 } 437 438 } 439 440 MODES( 441 TeX => qq!\\item{$tag}\n!, 442 Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!, 443 HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag! 444 ); 445 446 } 447 448 sub NAMED_ANS_CHECKBOX_OPTION { 449 my $name = shift; 450 my $value = shift; 451 my $tag =shift; 452 453 my $checked = ''; 454 if ($value =~/^\%/) { 455 $value =~ s/^\%//; 456 $checked = 'CHECKED' 457 } 458 459 if (defined($main::inputs_ref->{$name}) ) { 460 if ($main::inputs_ref->{$name} eq $value) { 461 $checked = 'CHECKED' 462 } 463 else { 464 $checked = ''; 465 } 466 467 } 468 469 MODES( 470 TeX => qq!\\item{$tag}\n!, 471 Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!, 472 HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag! 473 ); 474 475 } 476 477 sub NAMED_ANS_CHECKBOX_BUTTONS { 478 my $name =shift; 479 my $value = shift; 480 my $tag = shift; 481 482 my @out = (); 483 push(@out, NAMED_ANS_CHECKBOX($name, $value,$tag)); 484 485 my @buttons = @_; 486 while (@buttons) { 487 $value = shift @buttons; $tag = shift @buttons; 488 push(@out, NAMED_ANS_CHECKBOX_OPTION($name, $value,$tag)); 489 } 490 491 (wantarray) ? @out : join(" ",@out); 492 } 493 494 sub ANS_CHECKBOX { 495 my $number = shift; 496 my $value = shift; 497 my $tag =shift; 498 my $name = NEW_ANS_NAME($number); 499 500 NAMED_ANS_CHECKBOX($name,$value,$tag); 501 } 502 503 sub ANS_CHECKBOX_OPTION { 504 my $number = shift; 505 my $value = shift; 506 my $tag =shift; 507 my $name = ANS_NUM_TO_NAME($number); 508 509 NAMED_ANS_CHECKBOX_OPTION($name,$value,$tag); 510 } 511 512 sub ANS_CHECKBOX_BUTTONS { 513 my $number =shift; 514 my $value = shift; 515 my $tag = shift; 516 517 my @out = (); 518 push(@out, ANS_CHECKBOX($number, $value, $tag)); 519 520 my @buttons = @_; 521 while (@buttons) { 522 $value = shift @buttons; $tag = shift @buttons; 523 push(@out, ANS_CHECKBOX_OPTION($number, $value,$tag)); 524 } 525 526 (wantarray) ? @out : join(" ",@out); 527 } 528 529 sub ans_rule { 530 my $len = shift; # gives the optional length of the answer blank 531 $len = 20 unless $len ; 532 my $name = NEW_ANS_NAME(++$main::ans_rule_count); 533 NAMED_ANS_RULE($name ,$len); 534 } 535 sub ans_rule_extension { 536 my $len = shift; 537 $len = 20 unless $len ; 538 my $name = NEW_ANS_NAME($main::ans_rule_count); # don't update the answer name 539 NAMED_ANS_RULE($name ,$len); 540 } 541 sub ans_radio_buttons { 542 my $name = NEW_ANS_NAME(++$main::ans_rule_count); 543 my @radio_buttons = NAMED_ANS_RADIO_BUTTONS($name, @_); 544 545 if ($displayMode eq 'TeX') { 546 $radio_buttons[0] = "\n\\begin{itemize}\n" . $radio_buttons[0]; 547 $radio_buttons[$#radio_buttons] .= "\n\\end{itemize}\n"; 548 } 549 550 (wantarray) ? @radio_buttons: join(" ", @radio_buttons); 551 } 552 553 #added 6/14/2000 by David Etlinger 554 sub ans_checkbox { 555 my $name = NEW_ANS_NAME( ++$main::ans_rule_count ); 556 my @checkboxes = NAMED_ANS_CHECKBOX_BUTTONS( $name, @_ ); 557 558 if ($displayMode eq 'TeX') { 559 $checkboxes[0] = "\n\\begin{itemize}\n" . $checkboxes[0]; 560 $checkboxes[$#checkboxes] .= "\n\\end{itemize}\n"; 561 } 562 563 (wantarray) ? @checkboxes: join(" ", @checkboxes); 564 } 565 566 567 ## define a version of ans_rule which will work inside TeX math mode or display math mode -- at least for tth mode. 568 ## This is great for displayed fractions. 569 ## This will not work with latex2HTML mode since it creates gif equations. 570 571 sub tex_ans_rule { 572 my $len = shift; 573 $len = 20 unless $len ; 574 my $name = NEW_ANS_NAME(++$main::ans_rule_count); 575 my $answer_rule = NAMED_ANS_RULE($name ,$len); # we don't want to create three answer rules in different modes. 576 my $out = MODES( 577 'TeX' => $answer_rule, 578 'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', 579 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 580 'HTML_dpng' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', 581 'HTML' => $answer_rule 582 ); 583 584 $out; 585 } 586 sub tex_ans_rule_extension { 587 my $len = shift; 588 $len = 20 unless $len ; 589 my $name = NEW_ANS_NAME($main::ans_rule_count); 590 my $answer_rule = NAMED_ANS_RULE($name ,$len); # we don't want to create three answer rules in different modes. 591 my $out = MODES( 592 'TeX' => $answer_rule, 593 'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}', 594 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 595 'HTML_dpng' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 596 'HTML' => $answer_rule 597 ); 598 599 $out; 600 } 601 # still needs some cleanup. 602 sub NAMED_TEX_ANS_RULE { 603 my $name = shift; 604 my $len = shift; 605 $len = 20 unless $len ; 606 my $answer_rule = NAMED_ANS_RULE($name ,$len); # we don't want to create three answer rules in different modes. 607 my $out = MODES( 608 'TeX' => $answer_rule, 609 'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', 610 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 611 'HTML_dpng' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 612 'HTML' => $answer_rule 613 ); 614 615 $out; 616 } 617 sub NAMED_TEX_ANS_RULE_EXTENSION { 618 my $name = shift; 619 my $len = shift; 620 $len = 20 unless $len ; 621 my $answer_rule = NAMED_ANS_RULE_EXTENSION($name ,$len); # we don't want to create three answer rules in different modes. 622 my $out = MODES( 623 'TeX' => $answer_rule, 624 'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}', 625 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 626 'HTML_dpng' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 627 'HTML' => $answer_rule 628 ); 629 630 $out; 631 } 632 sub ans_box { 633 my $row = shift; 634 my $col =shift; 635 $row = 5 unless $row; 636 $col = 80 unless $col; 637 my $name = NEW_ANS_NAME(++$main::ans_rule_count); 638 NAMED_ANS_BOX($name ,$row,$col); 639 } 640 641 #this is legacy code; use ans_checkbox instead 642 sub checkbox { 643 my %options = @_; 644 qq!<INPUT TYPE="checkbox" NAME="$options{'-name'}" VALUE="$options{'-value'}">$options{'-label'}! 645 } 646 647 648 sub NAMED_POP_UP_LIST { 649 my $name = shift; 650 my @list = @_; 651 $name = RECORD_ANS_NAME($name); # record answer name 652 my $answer_value = ''; 653 $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name}); 654 my $out = ""; 655 if ($displayMode eq "HTML" or $displayMode eq "HTML_tth" or 656 $displayMode eq "HTML_dpng") { 657 $out = qq!<SELECT NAME = "$name" SIZE=1> \n!; 658 my $i; 659 foreach ($i=0; $i< @list; $i=$i+2) { 660 my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : ""; 661 $out .= qq!<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1] </OPTION>\n!; 662 }; 663 $out .= " </SELECT>\n"; 664 } elsif ( $displayMode eq "Latex2HTML") { 665 $out = qq! \\begin{rawhtml}<SELECT NAME = "$name" SIZE=1> \\end{rawhtml} \n !; 666 my $i; 667 foreach ($i=0; $i< @list; $i=$i+2) { 668 my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : ""; 669 $out .= qq!\\begin{rawhtml}<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1] </OPTION>\\end{rawhtml}\n!; 670 }; 671 $out .= " \\begin{rawhtml}</SELECT>\\end{rawhtml}\n"; 672 } elsif ( $displayMode eq "TeX") { 673 $out .= "\\fbox{?}"; 674 } 675 676 } 677 678 sub pop_up_list { 679 my @list = @_; 680 my $name = NEW_ANS_NAME(++$main::ans_rule_count); # get new answer name 681 NAMED_POP_UP_LIST($name, @list); 682 } 683 684 # end answer blank macros 685 686 =head2 Hints and solutions macros 687 688 solution('text','text2',...); 689 SOLUTION('text','text2',...); # equivalent to TEXT(solution(...)); 690 691 hint('text', 'text2', ...); 692 HINT('text', 'text2',...); # equivalent to TEXT("$BR$HINT" . hint(@_) . "$BR") if hint(@_); 693 694 Solution prints its concatenated input when the check box named 'ShowSol' is set and 695 the time is after the answer date. The check box 'ShowSol' is visible only after the 696 answer date or when the problem is viewed by a professor. 697 698 $envir{'displaySolutionsQ'} is set to 1 when a solution is to be displayed. 699 700 Hints are shown only after the number of attempts is greater than $:showHint 701 ($main::showHint defaults to 1) and the check box named 'ShowHint' is set. The check box 702 'ShowHint' is visible only after the number of attempts is greater than $main::showHint. 703 704 $envir{'displayHintsQ'} is set to 1 when a hint is to be displayed. 705 706 707 =cut 708 709 710 711 # solution prints its input when $displaySolutionsQ is set. 712 # use as TEXT(solution("blah, blah"); 713 # \$solutionExists 714 # is passed to processProblem which displays a "show Solution" button 715 # when a solution is available for viewing 716 717 718 sub solution { 719 my @in = @_; 720 my $out = ''; 721 $main::solutionExists =1; 722 if ($envir{'displaySolutionsQ'}) {$out = join(' ',@in);} 723 $out; 724 } 725 726 727 sub SOLUTION { 728 TEXT( solution(@_)) ; 729 } 730 731 732 733 sub hint { 734 my @in = @_; 735 my $out = ''; 736 737 $main::hintExists =1; 738 $main::numOfAttempts = 0 unless defined($main::numOfAttempts); 739 740 if ($main::displayMode eq 'TeX') { 741 $out = ''; # do nothing since hints are not available for download 742 } elsif (($envir{'displayHintsQ'}) and ($main::numOfAttempts >= $main::showHint)) 743 744 ## the second test above prevents a hint being shown if a doctored form is submitted 745 746 {$out = join(' ',@in);} # show hint 747 748 $out ; 749 } 750 751 752 sub HINT { 753 TEXT("$main::BR" . hint(@_) . "$main::BR") if hint(@_); 754 } 755 756 757 758 # End hints and solutions macros 759 ################################# 760 761 # Produces a random number between $begin and $end with increment 1. 762 # You do not have to worry about integer or floating point types. 763 764 =head2 Pseudo-random number generator 765 766 Usage: 767 random(0,5,.1) # produces a random number between 0 and 5 in increments of .1 768 non_zero_random(0,5,.1) # gives a non-zero random number 769 770 list_random(2,3,5,6,7,8,10) # produces random value from the list 771 list_random(2,3, (5..8),10) # does the same thing 772 773 SRAND(seed) # resets the main random generator -- use very cautiously 774 775 776 SRAND(time) will create a different problem everytime it is called. This makes it difficult 777 to check the answers :-). 778 779 SRAND($envir{'inputs_ref'}->{'key'} ) will create a different problem for each login session. 780 This is probably what is desired. 781 782 =cut 783 784 785 sub random { 786 my ($begin, $end, $incr) = @_; 787 $main::PG_random_generator->random($begin,$end,$incr); 788 } 789 790 791 sub non_zero_random { ##gives a non-zero random number 792 my (@arguments)=@_; 793 my $a=0; 794 my $i=100; #safety counter 795 while ($a==0 && ( 0 < $i-- ) ) { 796 $a=random(@arguments); 797 } 798 $a; 799 } 800 801 sub list_random { 802 my(@li) = @_; 803 return $li[random(1,scalar(@li))-1]; 804 } 805 806 sub SRAND { # resets the main random generator -- use cautiously 807 my $seed = shift; 808 $main::PG_random_generator -> srand($seed); 809 } 810 811 # display macros 812 813 =head2 Display Macros 814 815 These macros produce different output depending on the display mode being used to show 816 the problem on the screen, or whether the problem is being converted to TeX to produce 817 a hard copy output. 818 819 MODES ( TeX => "Output this in TeX mode", 820 HTML => "output this in HTML mode", 821 HTML_tth => "output this in HTML_tth mode", 822 HTML_dpng => "output this in HTML_dpng mode", 823 Latex2HTML => "output this in Latex2HTML mode", 824 ) 825 826 TEX (tex_version, html_version) #obsolete 827 828 M3 (tex_version, latex2html_version, html_version) #obsolete 829 830 831 832 =cut 833 834 835 sub TEX { 836 my ($tex, $html ) = @_; 837 MODES(TeX => $tex, HTML => $html, HTML_tth => $html, HTML_dpng => $html); 838 } 839 840 841 sub M3 { 842 my($tex,$l2h,$html) = @_; 843 MODES(TeX => $tex, Latex2HTML => $l2h, HTML => $html, HTML_tth => $html, HTML_dpng => $html); 844 } 845 846 # This replaces M3. You can add new modes at will to this one. 847 848 sub MODES { 849 my %options = @_; 850 return $options{$displayMode} 851 if defined( $options{$displayMode} ); 852 853 # default searches. 854 if ($displayMode eq "Latex2HTML") { 855 return $options{TeX} 856 if defined( $options{TeX} ); 857 return $options{HTML} 858 if defined( $options{HTML} ); 859 die " ERROR in using MODES: 'HTML' and 'TeX' options not defined for 'Latex2HTML'"; 860 } 861 862 if ($displayMode eq "HTML_tth") { 863 return $options{HTML} 864 if defined( $options{HTML} ); 865 die " ERROR in using MODES: 'HTML' option not defined for HTML_tth"; 866 867 } 868 869 if ($displayMode eq "HTML_dpng") { 870 return $options{HTML_tth} 871 if defined( $options{HTML_tth} ); 872 return $options{HTML} 873 if defined( $options{HTML} ); 874 die " ERROR in using MODES: 'HTML' option not defined for HTML_dpng"; 875 876 } 877 878 # trap undefined errors 879 die "ERROR in defining MODES: Can't find |$displayMode| among 880 available options:" . join(" ", keys(%options) ) 881 . " file " . __FILE__ ." line " . __LINE__."\n\n"; 882 883 } 884 885 886 # end display macros 887 888 889 =head2 Display constants 890 891 @ALPHABET ALPHABET() capital letter alphabet -- ALPHABET[0] = 'A' 892 $PAR PAR() paragraph character (\par or <p>) 893 $BR BR() line break character 894 $LQ LQ() left double quote 895 $RQ RQ() right double quote 896 $BM BM() begin math 897 $EM EM() end math 898 $BDM BDM() begin display math 899 $EDM EDM() end display math 900 $LTS LTS() strictly less than 901 $GTS GTS() strictly greater than 902 $LTE LTE() less than or equal 903 $GTE GTE() greater than or equal 904 $BEGIN_ONE_COLUMN BEGIN_ONE_COLUMN() begin one-column mode 905 $END_ONE_COLUMN END_ONE_COLUMN() end one-column mode 906 $SOL SOLUTION_HEADING() solution headline 907 $SOLUTION SOLUTION_HEADING() solution headline 908 $HINT HINT_HEADING() hint headline 909 $US US() underscore character 910 $SPACE SPACE() space character (tex and latex only) 911 $BBOLD BBOLD() begin bold typeface 912 $EBOLD EBOLD() end bold typeface 913 $BITALIC BITALIC() begin italic typeface 914 $EITALIC EITALIC() end italic typeface 915 $BCENTER BCENTER() begin centered environment 916 $ECENTER ECENTER() end centered environment 917 $HR HR() horizontal rule 918 $LBRACE LBRACE() left brace 919 $LB LB () left brace 920 $RBRACE RBRACE() right brace 921 $RB RB () right brace 922 $DOLLAR DOLLAR() a dollar sign 923 $PERCENT PERCENT() a percent sign 924 $CARET CARET() a caret sign 925 $PI PI() the number pi 926 $E E() the number e 927 928 =cut 929 930 931 932 933 934 # A utility variable. Notice that "B"=$ALPHABET[1] and 935 # "ABCD"=@ALPHABET[0..3]. 936 937 sub ALPHABET { 938 ('A'..'ZZ')[@_]; 939 } 940 941 ############################################################### 942 # Some constants which are different in tex and in HTML 943 # The order of arguments is TeX, Latex2HTML, HTML 944 sub PAR { MODES( TeX => '\\par ',Latex2HTML => '\\par ',HTML => '<P>' ); }; 945 sub BR { MODES( TeX => '\\par\\noindent ',Latex2HTML => '\\par\\noindent ',HTML => '<BR>'); }; 946 sub LQ { MODES( TeX => "``", Latex2HTML => '"', HTML => '"' ); }; 947 sub RQ { MODES( TeX => "''", Latex2HTML => '"', HTML => '"' ); }; 948 sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML => ''); }; # begin math mode 949 sub EM { MODES(TeX => '\\)', Latex2HTML => '\\)', HTML => ''); }; # end math mode 950 sub BDM { MODES(TeX => '\\[', Latex2HTML => '\\[', HTML => '<P ALIGN=CENTER>'); }; #begin displayMath mode 951 sub EDM { MODES(TeX => '\\]', Latex2HTML => '\\]', HTML => '</P>'); }; #end displayMath mode 952 sub LTS { MODES(TeX => ' < ', Latex2HTML => ' \\lt ', HTML => '<'); }; 953 sub GTS {MODES(TeX => ' > ', Latex2HTML => ' \\gt ', HTML => '>'); }; 954 sub LTE { MODES(TeX => ' \\le ', Latex2HTML => ' \\le ', HTML => '<=' ); }; 955 sub GTE { MODES(TeX => ' \\ge ', Latex2HTML => ' \\ge ', HTML => '>'); }; 956 sub BEGIN_ONE_COLUMN { MODES(TeX => " \\end{multicols}\n", Latex2HTML => " ", HTML => " "); }; 957 sub END_ONE_COLUMN { MODES(TeX => 958 " \\begin{multicols}{2}\n\\columnwidth=\\linewidth\n", 959 Latex2HTML => ' ', HTML => ' '); 960 961 }; 962 sub SOLUTION_HEADING { MODES( TeX => '\\par {\\bf Solution:}', 963 Latex2HTML => '\\par {\\bf Solution:}', 964 HTML => '<P><B>Solution:</B>'); 965 }; 966 sub HINT_HEADING { MODES( TeX => "\\par {\\bf Hint:}", Latex2HTML => "\\par {\\bf Hint:}", HTML => "<P><B>Hint:</B>"); }; 967 sub US { MODES(TeX => '\\_', Latex2HTML => '\\_', HTML => '_');}; # underscore, e.g. file${US}name 968 sub SPACE { MODES(TeX => '\\ ', Latex2HTML => '\\ ', HTML => ' ');}; # force a space in latex, doesn't force extra space in html 969 sub BBOLD { MODES(TeX => '{\\bf ', Latex2HTML => '{\\bf ', HTML => '<B>'); }; 970 sub EBOLD { MODES( TeX => '}', Latex2HTML => '}',HTML => '</B>'); }; 971 sub BITALIC { MODES(TeX => '{\\it ', Latex2HTML => '{\\it ', HTML => '<I>'); }; 972 sub EITALIC { MODES(TeX => '} ', Latex2HTML => '} ', HTML => '</I>'); }; 973 sub BCENTER { MODES(TeX => '\\begin{center} ', Latex2HTML => ' \\begin{rawhtml} <div align="center"> \\end{rawhtml} ', HTML => '<div align="center">'); }; 974 sub ECENTER { MODES(TeX => '\\end{center} ', Latex2HTML => ' \\begin{rawhtml} </div> \\end{rawhtml} ', HTML => '</div>'); }; 975 sub HR { MODES(TeX => '\\par\\hrulefill\\par ', Latex2HTML => '\\begin{rawhtml} <HR> \\end{rawhtml}', HTML => '<HR>'); }; 976 sub LBRACE { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '\{' , HTML_tth=> '\\lbrace' ); }; 977 sub RBRACE { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '\}' , HTML_tth=> '\\rbrace',); }; 978 sub LB { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '\{' , HTML_tth=> '\\lbrace' ); }; 979 sub RB { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '\}' , HTML_tth=> '\\rbrace',); }; 980 sub DOLLAR { MODES( TeX => '\\$', Latex2HTML => '\\$', HTML => '$' ); }; 981 sub PERCENT { MODES( TeX => '\\%', Latex2HTML => '\\%', HTML => '%' ); }; 982 sub CARET { MODES( TeX => '\\verb+^+', Latex2HTML => '\\verb+^+', HTML => '^' ); }; 983 sub PI {4*atan2(1,1);}; 984 sub E {exp(1);}; 985 986 ############################################################### 987 ## Evaluation macros 988 989 990 =head2 TEXT macros 991 992 Usage: 993 TEXT(@text); 994 995 This is the simplest way to print text from a problem. The strings in the array C<@text> are concatenated 996 with spaces between them and printed out in the text of the problem. The text is not processed in any other way. 997 C<TEXT> is defined in PG.pl. 998 999 Usage: 1000 BEGIN_TEXT 1001 text..... 1002 END_TEXT 1003 1004 This is the most common way to enter text into the problem. All of the text between BEGIN_TEXT and END_TEXT 1005 is processed by the C<EV3> macro described below and then printed using the C<TEXT> command. The two key words 1006 must appear on lines by themselves. The preprocessing that makes this construction work is done in F<PGtranslator.pm>. 1007 See C<EV3> below for details on the processing. 1008 1009 1010 =cut 1011 1012 =head2 Evaluation macros 1013 1014 =head3 EV3 1015 1016 TEXT(EV3("This is a formulat \( \int_0^5 x^2 \, dx \) "); 1017 TEXT(EV3(@text)); 1018 1019 TEXT(EV3(<<'END_TEXT')); 1020 text stuff... 1021 END_TEXT 1022 1023 1024 The BEGIN_TEXT/END_TEXT construction is translated into the construction above by PGtranslator.pm. END_TEXT must appear 1025 on a line by itself and be left justified. (The << construction is known as a "here document" in UNIX and in PERL.) 1026 1027 The single quotes around END_TEXT mean that no automatic interpolation of variables takes place in the text. 1028 Using EV3 with strings which have been evaluated by double quotes may lead to unexpected results. 1029 1030 1031 The evaluation macro E3 first evaluates perl code inside the braces: C<\{ code \}>. 1032 Any perl statment can be put inside the braces. The 1033 result of the evaluation (i.e. the last statement evaluated) replaces the C<\{ code \}> construction. 1034 1035 Next interpolation of all variables (e.g. C<$var or @array> ) is performed. 1036 1037 Then mathematical formulas in TeX are evaluated within the 1038 C<\( tex math mode \)> and 1039 C<\[ tex display math mode \] > 1040 constructions, in that order: 1041 1042 =head3 FEQ 1043 1044 FEQ($string); # processes and outputs the string 1045 1046 1047 The mathematical formulas are run through the macro C<FEQ> (Format EQuations) which performs 1048 several substitutions (see below). 1049 In C<HTML_tth> mode the resulting code is processed by tth to obtain an HTML version 1050 of the formula. (In the future processing by WebEQ may be added here as another option.) 1051 The Latex2HTML mode does nothing 1052 at this stage; it creates the entire problem before running it through 1053 TeX and creating the GIF images of the equations. 1054 1055 The resulting string is output (and usually fed into TEXT to be printed in the problem). 1056 1057 Usage: 1058 1059 $string2 = FEQ($string1); 1060 1061 This is a filter which is used to format equations by C<EV2> and C<EV3>, but can also be used on its own. It is best 1062 understood with an example. 1063 1064 $string1 = "${a}x^2 + ${b}x + {$c:%.1f}"; $a = 3;, $b = -2; $c = -7.345; 1065 1066 when interpolated becomes: 1067 1068 $string1 = '3x^2 + -2x + {-7.345:%0.1f} 1069 1070 FEQ first changes the number of decimal places displayed, so that the last term becomes -7.3 Then it removes the 1071 extraneous plus and minus signs, so that the final result is what you want: 1072 1073 $string2 = '3x^2 - 2x -7.3'; 1074 1075 (The %0.1f construction 1076 is the same formatting convention used by Perl and nearly identical to the one used by the C printf statement. Some common 1077 usage: %0.3f 3 decimal places, fixed notation; %0.3e 3 significant figures exponential notation; %0.3g uses either fixed 1078 or exponential notation depending on the size of the number.) 1079 1080 Two additional legacy formatting constructions are also supported: 1081 1082 C<?{$c:%0.3f} > will give a number with 3 decimal places and a negative 1083 sign if the number is negative, no sign if the number is positive. 1084 1085 C<!{$c:%0.3f}> determines the sign and prints it 1086 whether the number is positive or negative. 1087 1088 =head3 EV2 1089 1090 TEXT(EV2(@text)); 1091 1092 TEXT(EV2(<<END_OF_TEXT)); 1093 text stuff... 1094 END_OF_TEXT 1095 1096 This is a precursor to EV3. In this case the constants are interpolated first, before the evaluation of the \{ ...code...\} 1097 construct. This can lead to unexpected results. For example C<\{ join(" ", @text) \}> with C<@text = ("Hello","World");> becomes, 1098 after interpolation, C<\{ join(" ",Hello World) \}> which then causes an error when evaluated because Hello is a bare word. 1099 C<EV2> can still be useful if you allow for this, and in particular it works on double quoted strings, which lead to 1100 unexpected results with C<EV3>. Using single quoted strings with C<EV2> may lead to unexpected results. 1101 1102 The unexpected results have to do with the number of times backslashed constructions have to be escaped. It is quite messy. For 1103 more details get a good Perl book and then read the code. :-) 1104 1105 1106 1107 1108 =cut 1109 1110 1111 sub ev_substring { 1112 my $string = shift; 1113 my $start_delim = shift; 1114 my $end_delim = shift; 1115 my $actionRef = shift; 1116 my ($eval_out,$PG_eval_errors,$PG_full_error_report)=(); 1117 my $out = ""; 1118 while ($string) { 1119 if ($string =~ /\Q$start_delim\E/s) { 1120 #print "$start_delim $end_delim evaluating_substring=$string<BR>"; 1121 $string =~ s/^(.*?)\Q$start_delim\E//s; # get string up to next \{ ---treats string as a single line, ignoring returns 1122 $out .= $1; 1123 #print "$start_delim $end_delim substring_out=$out<BR>"; 1124 $string =~ s/^(.*?)\Q$end_delim\E//s; # get perl code up to \} ---treats string as a single line, ignoring returns 1125 #print "$start_delim $end_delim evaluate_string=$1<BR>"; 1126 ($eval_out,$PG_eval_errors,$PG_full_error_report) = &$actionRef($1); 1127 $eval_out = "$start_delim $eval_out $end_delim" if $PG_full_error_report; 1128 $out = $out . $eval_out; 1129 #print "$start_delim $end_delim new substring_out=$out<BR><p><BR>"; 1130 $out .="$main::PAR ERROR $0 in ev_substring, PGbasicmacros.pl:$main::PAR <PRE> $@ </PRE>$main::PAR" if $@; 1131 } 1132 else { 1133 $out .= $string; # flush the last part of the string 1134 last; 1135 } 1136 1137 } 1138 $out; 1139 } 1140 sub safe_ev { 1141 my ($out,$PG_eval_errors,$PG_full_error_report) = &old_safe_ev; # process input by old_safe_ev first 1142 $out =~s/\\/\\\\/g; # protect any new backslashes introduced. 1143 ($out,$PG_eval_errors,$PG_full_error_report) 1144 } 1145 1146 sub old_safe_ev { 1147 my $in = shift; 1148 my ($out,$PG_eval_errors,$PG_full_error_report) = PG_restricted_eval("$in;"); 1149 # the addition of the ; seems to provide better error reporting 1150 if ($PG_eval_errors) { 1151 my @errorLines = split("\n",$PG_eval_errors); 1152 #$out = "<PRE>$main::PAR % ERROR in $0:old_safe_ev, PGbasicmacros.pl: $main::PAR % There is an error occuring inside evaluation brackets \\{ ...code... \\} $main::BR % somewhere in an EV2 or EV3 or BEGIN_TEXT block. $main::BR % Code evaluated:$main::BR $in $main::BR % $main::BR % $errorLines[0]\n % $errorLines[1]$main::BR % $main::BR % $main::BR </PRE> "; 1153 warn " ERROR in old_safe_ev, PGbasicmacros.pl: <PRE> 1154 ## There is an error occuring inside evaluation brackets \\{ ...code... \\} 1155 ## somewhere in an EV2 or EV3 or BEGIN_TEXT block. 1156 ## Code evaluated: 1157 ## $in 1158 ##" .join("\n ", @errorLines). " 1159 ##</PRE>$main::BR 1160 "; 1161 $out ="$main::PAR $main::BBOLD $in $main::EBOLD $main::PAR"; 1162 1163 1164 } 1165 1166 ($out,$PG_eval_errors,$PG_full_error_report); 1167 } 1168 1169 sub FEQ { # Format EQuations 1170 my $in = shift; 1171 # formatting numbers -- the ?{} and !{} constructions 1172 $in =~s/\?\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &sspf($1,$2) )}/g; 1173 $in =~s/\!\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &spf($1,$2) )}/g; 1174 1175 # more formatting numbers -- {number:format} constructions 1176 $in =~ s/\{(\s*[\+\-\d\.]+[eE]*[\+\-]*\d*):(\%\d*.\d*\w)}/${ \( &spf($1,$2) )}/g; 1177 $in =~ s/\+\s*\-/ - /g; 1178 $in =~ s/\-\s*\+/ - /g; 1179 $in =~ s/\+\s*\+/ + /g; 1180 $in =~ s/\-\s*\-/ + /g; 1181 $in; 1182 } 1183 1184 sub math_ev3 { 1185 my $in = shift; 1186 return general_math_ev3($in, "inline"); 1187 } 1188 1189 sub display_math_ev3 { 1190 my $in = shift; 1191 return general_math_ev3($in, "display"); 1192 } 1193 1194 1195 sub general_math_ev3 { 1196 my $in = shift; 1197 my $mode = shift || "inline"; 1198 1199 $in = FEQ($in); 1200 $in =~ s/%/\\%/g; 1201 my $in_delim; 1202 1203 if($mode eq "inline") { 1204 $in_delim = "\\($in\\)"; 1205 } else { # assuming displayed math 1206 $in_delim = "\\[$in\\]"; 1207 } 1208 1209 my $out; 1210 if($displayMode eq "HTML_tth") { 1211 $out = tth($in_delim); 1212 } elsif ($displayMode eq "HTML_dpng") { 1213 $out = $envir{'imagegen'}->add($in_delim); 1214 } elsif ($displayMode eq "HTML_img") { 1215 $out = math2img($in, $mode); 1216 } else { # Maybe we don't have to do anything! 1217 $out = $in_delim; 1218 } 1219 return $out; 1220 } 1221 1222 1223 sub EV2 { 1224 my $string = join(" ",@_); 1225 # evaluate code inside of \{ \} (no nesting allowed) 1226 $string = ev_substring($string,"\\{","\\}",\&old_safe_ev); 1227 $string = ev_substring($string,"\\<","\\>",\&old_safe_ev); 1228 $string = ev_substring($string,"\\(","\\)",\&math_ev3); 1229 $string = ev_substring($string,"\\[","\\]",\&display_math_ev3); 1230 # macros for displaying math 1231 $string =~ s/\\\(/$main::BM/g; 1232 $string =~ s/\\\)/$main::EM/g; 1233 $string =~ s/\\\[/$main::BDM/g; 1234 $string =~ s/\\\]/$main::EDM/g; 1235 $string; 1236 } 1237 1238 sub EV3{ 1239 my $string = join(" ",@_); 1240 # evaluate code inside of \{ \} (no nesting allowed) 1241 $string = ev_substring($string,"\\\\{","\\\\}",\&safe_ev); # handles \{ \} in single quoted strings of PG files 1242 # interpolate variables 1243 my ($evaluated_string,$PG_eval_errors,$PG_full_errors) = PG_restricted_eval("<<END_OF_EVALUATION_STRING\n$string\nEND_OF_EVALUATION_STRING\n"); 1244 if ($PG_eval_errors) { 1245 my @errorLines = split("\n",$PG_eval_errors); 1246 $string =~ s/</</g; $string =~ s/>/>/g; 1247 $evaluated_string = "<PRE>$main::PAR % ERROR in $0:EV3, PGbasicmacros.pl: $main::PAR % There is an error occuring in the following code:$main::BR $string $main::BR % $main::BR % $errorLines[0]\n % $errorLines[1]$main::BR % $main::BR % $main::BR </PRE> "; 1248 $@=""; 1249 } 1250 $string = $evaluated_string; 1251 $string = ev_substring($string,"\\(","\\)",\&math_ev3); 1252 $string = ev_substring($string,"\\[","\\]",\&display_math_ev3); 1253 $string; 1254 } 1255 1256 =head2 Formatting macros 1257 1258 beginproblem() # generates text listing number and the point value of 1259 # the problem. It will also print the file name containing 1260 # the problem for users listed in the PRINT_FILE_NAMES_FOR PG_environment 1261 # variable. 1262 OL(@array) # formats the array as an Ordered List ( <OL> </OL> ) enumerated by letters. 1263 1264 htmlLink($url, $text) 1265 # Places a reference to the URL with the specified text in the problem. 1266 # A common usage is \{ htmlLink(alias('prob1_help.html') \}, 'for help') 1267 # where alias finds the full address of the prob1_help.html file in the same directory 1268 # as the problem file 1269 appletLink($url, $parameters) 1270 # For example 1271 # appletLink(q! archive="http: //webwork.math.rochester.edu/gage/xFunctions/xFunctions.zip" 1272 code="xFunctionsLauncher.class" width=100 height=14!, 1273 " parameter text goes here") 1274 # will link to xFunctions. 1275 1276 low level: 1277 1278 spf($number, $format) # prints the number with the given format 1279 sspf($number, $format) # prints the number with the given format, always including a sign. 1280 protect_underbar($string) # protects the underbar (class_name) in strings which may have to pass through TeX. 1281 1282 =cut 1283 1284 sub beginproblem { 1285 my $out = ""; 1286 my $TeXFileName = protect_underbar($main::fileName); 1287 my $l2hFileName = protect_underbar($main::fileName); 1288 my %inlist; 1289 my $points ='pts'; 1290 $points = 'pt' if $main::problemValue == 1; 1291 ## Prepare header for the problem 1292 grep($inlist{$_}++,@{ $envir{'PRINT_FILE_NAMES_FOR'} }); 1293 if ( defined($inlist{$main::studentLogin}) and ($inlist{$main::studentLogin} > 0) ) { 1294 $out = &M3("\n\n\\medskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.{\\footnotesize ($main::problemValue $points) $TeXFileName}}\\newline ", 1295 " \\begin{rawhtml} ($main::problemValue $points) <B>$l2hFileName</B><BR>\\end{rawhtml}", 1296 "($main::problemValue $points) <B>$main::fileName</B><BR>" 1297 ); 1298 } else { 1299 $out = &M3("\n\n\\smallskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.}($main::problemValue $points) ", 1300 "($main::problemValue $points) ", 1301 "($main::problemValue $points) " 1302 ); 1303 } 1304 $out; 1305 1306 } 1307 1308 # kludge to clean up path names 1309 ## allow underscore character in set and section names and also allows line breaks at / 1310 sub protect_underbar { 1311 my $in = shift; 1312 if ($displayMode eq 'TeX') { 1313 1314 $in =~ s|_|\\\_|g; 1315 $in =~ s|/|\\\-/|g; # allows an optional hyphenation of the path (in tex) 1316 } 1317 $in; 1318 } 1319 1320 1321 # An example of a macro which prints out a list (with letters) 1322 sub OL { 1323 my(@array) = @_; 1324 my $i = 0; 1325 my $out= &M3( 1326 "\\begin{enumerate}\n", 1327 " \\begin{rawhtml} <OL TYPE=\"A\" VALUE=\"1\"> \\end{rawhtml} ", 1328 "<OL TYPE=\"A\" VALUE=\"1\">\n" 1329 ) ; 1330 my $elem; 1331 foreach $elem (@array) { 1332 $out .= &M3( 1333 "\\item[$main::ALPHABET[$i].] $elem\n", 1334 " \\begin{rawhtml} <LI> \\end{rawhtml} $elem ", 1335 "<LI> $elem\n" 1336 ) ; 1337 $i++; 1338 } 1339 $out .= &M3( 1340 "\\end{enumerate}\n", 1341 " \\begin{rawhtml} </OL>\n \\end{rawhtml} ", 1342 "</OL>\n" 1343 ) ; 1344 } 1345 1346 sub htmlLink { 1347 my $url = shift; 1348 my $text = shift; 1349 my $options = shift; 1350 $options = "" unless defined($options); 1351 return "${main::BBOLD}[ broken link: $text ] ${main::EBOLD}" unless defined($url); 1352 M3( "{\\bf \\underline{$text} }", 1353 "\\begin{rawhtml} <A HREF=\"$url\" $options> $text </A>\\end{rawhtml}", 1354 "<A HREF=\"$url\" $options> $text </A>" 1355 ); 1356 } 1357 sub appletLink { 1358 my $url = shift; 1359 my $options = shift; 1360 $options = "" unless defined($options); 1361 M3( "{\\bf \\underline{APPLET} }", 1362 "\\begin{rawhtml} <APPLET $url> $options </APPLET>\\end{rawhtml}", 1363 "<APPLET $url> $options </APPLET>" 1364 ); 1365 } 1366 sub spf { 1367 my($number,$format) = @_; # attention, the order of format and number are reversed 1368 $format = "%4.3g" unless $format; # default value for format 1369 sprintf($format, $number); 1370 } 1371 sub sspf { 1372 my($number,$format) = @_; # attention, the order of format and number are reversed 1373 $format = "%4.3g" unless $format; # default value for format 1374 my $sign = $number>=0 ? " + " : " - "; 1375 $number = $number>=0 ? $number : -$number; 1376 $sign .sprintf($format, $number); 1377 } 1378 1379 =head2 Sorting and other list macros 1380 1381 1382 1383 Usage: 1384 lex_sort(@list); # outputs list in lexigraphic (alphabetical) order 1385 num_sort(@list); # outputs list in numerical order 1386 uniq( @list); # outputs a list with no duplicates. Order is unspecified. 1387 1388 PGsort( \&sort_subroutine, @list); 1389 # &sort_subroutine defines order. It's output must be -1,0 or 1. 1390 1391 =cut 1392 1393 # uniq gives unique elements of a list: 1394 sub uniq { 1395 my (@in) =@_; 1396 my %temp = (); 1397 while (@in) { 1398 $temp{shift(@in)}++; 1399 } 1400 my @out = keys %temp; # sort is causing trouble with Safe.?? 1401 @out; 1402 } 1403 1404 sub lex_sort { 1405 PGsort sub {$_[0] cmp $_[1]}, @_; 1406 } 1407 sub num_sort { 1408 PGsort sub {$_[0] <=> $_[1]}, @_; 1409 } 1410 1411 1412 =head2 Macros for handling tables 1413 1414 Usage: 1415 begintable( number_of_columns_in_table) 1416 row(@dataelements) 1417 endtable() 1418 1419 Example of useage: 1420 1421 BEGIN_TEXT 1422 This problem tests calculating new functions from old ones:$BR 1423 From the table below calculate the quantities asked for:$BR 1424 \{begintable(scalar(@firstrow)+1)\} 1425 \{row(" \(x\) ",@firstrow)\} 1426 \{row(" \(f(x)\) ", @secondrow)\} 1427 \{row(" \(g(x)\) ", @thirdrow)\} 1428 \{row(" \(f'(x)\) ", @fourthrow)\} 1429 \{row(" \(g'(x)\) ", @fifthrow)\} 1430 \{endtable()\} 1431 1432 (The arrays contain numbers which are placed in the table.) 1433 1434 END_TEXT 1435 1436 =cut 1437 1438 sub begintable { 1439 my ($number)=shift; #number of columns in table 1440 my %options = @_; 1441 warn "begintable(cols) requires a number indicating the number of columns" unless defined($number); 1442 my $out = ""; 1443 if ($displayMode eq 'TeX') { 1444 $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{" . "|c" x $number . "|} \\hline\n"; 1445 } 1446 elsif ($displayMode eq 'Latex2HTML') { 1447 $out .= "\n\\begin{rawhtml} <TABLE , BORDER=1>\n\\end{rawhtml}"; 1448 } 1449 elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng') { 1450 $out .= "<TABLE BORDER=1>\n" 1451 } 1452 else { 1453 $out = "Error: PGchoicemacros: begintable: Unknown displayMode: $displayMode.\n"; 1454 } 1455 $out; 1456 } 1457 1458 sub endtable { 1459 my $out = ""; 1460 if ($displayMode eq 'TeX') { 1461 $out .= "\n\\end {tabular}\\end{center}\\par\\smallskip\n"; 1462 } 1463 elsif ($displayMode eq 'Latex2HTML') { 1464 $out .= "\n\\begin{rawhtml} </TABLE >\n\\end{rawhtml}"; 1465 } 1466 elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng') { 1467 $out .= "</TABLE>\n"; 1468 } 1469 else { 1470 $out = "Error: PGchoicemacros: endtable: Unknown displayMode: $displayMode.\n"; 1471 } 1472 $out; 1473 } 1474 1475 1476 sub row { 1477 my @elements = @_; 1478 my $out = ""; 1479 if ($displayMode eq 'TeX') { 1480 while (@elements) { 1481 $out .= shift(@elements) . " &"; 1482 } 1483 chop($out); # remove last & 1484 $out .= "\\\\ \\hline \n"; 1485 # carriage returns must be added manually for tex 1486 } 1487 elsif ($displayMode eq 'Latex2HTML') { 1488 $out .= "\n\\begin{rawhtml}\n<TR>\n\\end{rawhtml}\n"; 1489 while (@elements) { 1490 $out .= " \n\\begin{rawhtml}\n<TD> \n\\end{rawhtml}\n" . shift(@elements) . " \n\\begin{rawhtml}\n</TD> \n\\end{rawhtml}\n"; 1491 } 1492 $out .= " \n\\begin{rawhtml}\n</TR> \n\\end{rawhtml}\n"; 1493 } 1494 elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng') { 1495 $out .= "<TR>\n"; 1496 while (@elements) { 1497 $out .= "<TD>" . shift(@elements) . "</TD>"; 1498 } 1499 $out .= "\n</TR>\n"; 1500 } 1501 else { 1502 $out = "Error: PGchoicemacros: row: Unknown displayMode: $main::displayMode.\n"; 1503 } 1504 $out; 1505 } 1506 1507 =head2 Macros for displaying static images 1508 1509 Usage: 1510 $string = image($image, width => 100, height => 100, tex_size => 800) 1511 $string = image([$image1, $image2], width => 100, height => 100, tex_size => 800) 1512 $string = caption($string); 1513 $string = imageRow([$image1, $image2 ], [$caption1, $caption2]); 1514 # produces a complete table with rows of pictures. 1515 1516 1517 =cut 1518 1519 # More advanced macros 1520 sub image { 1521 my $image_ref = shift; 1522 my @opt = @_; 1523 unless (scalar(@opt) % 2 == 0 ) { 1524 warn "ERROR in image macro. A list of macros must be inclosed in square brackets."; 1525 } 1526 my %in_options = @opt; 1527 my %known_options = ( width => 100, 1528 height => 100, 1529 tex_size => 800 1530 ); 1531 # # handle options 1532 my %out_options = %known_options; 1533 foreach my $opt_name (keys %in_options) { 1534 if ( exists( $known_options{$opt_name} ) ) { 1535 $out_options{$opt_name} = $in_options{$opt_name} if exists( $in_options{$opt_name} ) ; 1536 } else { 1537 die "Option $opt_name not defined for image. " . 1538 "Default options are:<BR> ", display_options2(%known_options); 1539 1540 } 1541 } 1542 my $width = $out_options{width}; 1543 my $height = $out_options{height}; 1544 my $tex_size = $out_options{tex_size}; 1545 my $width_ratio = $tex_size*(.001); 1546 my @image_list = (); 1547 1548 if (ref($image_ref) =~ /ARRAY/ ) { 1549 @image_list = @{$image_ref}; 1550 } else { 1551 push(@image_list,$image_ref); 1552 } 1553 my @output_list = (); 1554 while(@image_list) { 1555 1556 my $imageURL = alias(shift @image_list); 1557 my $out=""; 1558 1559 1560 if ($main::displayMode eq 'TeX') { 1561 $out = qq!\\includegraphics[width=$width_ratio\\linewidth]{$imageURL}\n ! 1562 } 1563 elsif ($main::displayMode eq 'Latex2HTML') { 1564 $out = qq!\\begin{rawhtml}\n<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL" WIDTH="$width" HEIGHT="$height"></A>\n 1565 \\end{rawhtml}\n ! 1566 } 1567 elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth' || $displayMode eq 'HTML_dpng') { 1568 $out = qq!<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL" WIDTH="$width" HEIGHT="$height"></A> 1569 ! 1570 } 1571 else { 1572 $out = "Error: PGchoicemacros: image: Unknown displayMode: $main::displayMode.\n"; 1573 } 1574 push(@output_list, $out); 1575 } 1576 wantarray ? @output_list : $output_list[0] ; 1577 } 1578 1579 # This is legacy code. 1580 sub images { 1581 my @in = @_; 1582 my @outlist = (); 1583 while (@in) { 1584 push(@outlist,&image( shift(@in) ) ); 1585 } 1586 @outlist; 1587 } 1588 1589 1590 sub caption { 1591 my ($out) = @_; 1592 $out = " $out \n" if $main::displayMode eq 'TeX'; 1593 $out = " $out " if $main::displayMode eq 'HTML'; 1594 $out = " $out " if $main::displayMode eq 'HTML_tth'; 1595 $out = " $out " if $main::displayMode eq 'HTML_dpng'; 1596 $out = " $out " if $main::displayMode eq 'Latex2HTML'; 1597 $out; 1598 } 1599 1600 sub captions { 1601 my @in = @_; 1602 my @outlist = (); 1603 while (@in) { 1604 push(@outlist,&caption( shift(@in) ) ); 1605 } 1606 @outlist; 1607 } 1608 1609 sub imageRow { 1610 1611 my $pImages = shift; 1612 my $pCaptions=shift; 1613 my $out = ""; 1614 my @images = @$pImages; 1615 my @captions = @$pCaptions; 1616 my $number = @images; 1617 # standard options 1618 my %options = ( 'tex_size' => 200, # width for fitting 4 across 1619 'height' => 100, 1620 'width' => 100, 1621 @_ # overwrite any default options 1622 ); 1623 1624 if ($main::displayMode eq 'TeX') { 1625 $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{" . "|c" x $number . "|} \\hline\n"; 1626 while (@images) { 1627 $out .= &image( shift(@images),%options ) . '&'; 1628 } 1629 chop($out); 1630 $out .= "\\\\ \\hline \n"; 1631 while (@captions) { 1632 $out .= &caption( shift(@captions) ) . '&'; 1633 } 1634 chop($out); 1635 $out .= "\\\\ \\hline \n\\end {tabular}\\end{center}\\par\\smallskip\n"; 1636 } elsif ($main::displayMode eq 'Latex2HTML'){ 1637 1638 $out .= "\n\\begin{rawhtml} <TABLE BORDER=1><TR>\n\\end{rawhtml}\n"; 1639 while (@images) { 1640 $out .= "\n\\begin{rawhtml} <TD>\n\\end{rawhtml}\n" . &image( shift(@images),%options ) 1641 . "\n\\begin{rawhtml} </TD>\n\\end{rawhtml}\n" ; 1642 } 1643 1644 $out .= "\n\\begin{rawhtml}</TR><TR>\\end{rawhtml}\n"; 1645 while (@captions) { 1646 $out .= "\n\\begin{rawhtml} <TH>\n\\end{rawhtml}\n".&caption( shift(@captions) ) 1647 . "\n\\begin{rawhtml} </TH>\n\\end{rawhtml}\n" ; 1648 } 1649 1650 $out .= "\n\\begin{rawhtml} </TR> </TABLE >\n\\end{rawhtml}"; 1651 } elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth' || $main::displayMode eq 'HTML_dpng'){ 1652 $out .= "<P>\n <TABLE BORDER=2 CELLPADDING=3 CELLSPACING=2 ><TR ALIGN=CENTER VALIGN=MIDDLE>\n"; 1653 while (@images) { 1654 $out .= " \n<TD>". &image( shift(@images),%options ) ."</TD>"; 1655 } 1656 $out .= "</TR>\n<TR>"; 1657 while (@captions) { 1658 $out .= " <TH>". &caption( shift(@captions) ) ."</TH>"; 1659 } 1660 $out .= "\n</TR></TABLE></P>\n" 1661 } 1662 else { 1663 $out = "Error: PGchoicemacros: imageRow: Unknown languageMode: $main::displayMode.\n"; 1664 warn $out; 1665 } 1666 $out; 1667 } 1668 1669 1670 ########### 1671 # Auxiliary macros 1672 1673 sub display_options2{ 1674 my %options = @_; 1675 my $out_string = ""; 1676 foreach my $key (keys %options) { 1677 $out_string .= " $key => $options{$key},<BR>"; 1678 } 1679 $out_string; 1680 } 1681 1682 1683 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |