Parent Directory
|
Revision Log
Added new randomization function, list_random, which takes a list of values and picks one from the list.
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' => $answer_rule 581 ); 582 583 $out; 584 } 585 sub tex_ans_rule_extension { 586 my $len = shift; 587 $len = 20 unless $len ; 588 my $name = NEW_ANS_NAME($main::ans_rule_count); 589 my $answer_rule = NAMED_ANS_RULE($name ,$len); # we don't want to create three answer rules in different modes. 590 my $out = MODES( 591 'TeX' => $answer_rule, 592 'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}', 593 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 594 'HTML' => $answer_rule 595 ); 596 597 $out; 598 } 599 # still needs some cleanup. 600 sub NAMED_TEX_ANS_RULE { 601 my $name = shift; 602 my $len = shift; 603 $len = 20 unless $len ; 604 my $answer_rule = NAMED_ANS_RULE($name ,$len); # we don't want to create three answer rules in different modes. 605 my $out = MODES( 606 'TeX' => $answer_rule, 607 'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', 608 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 609 'HTML' => $answer_rule 610 ); 611 612 $out; 613 } 614 sub NAMED_TEX_ANS_RULE_EXTENSION { 615 my $name = shift; 616 my $len = shift; 617 $len = 20 unless $len ; 618 my $answer_rule = NAMED_ANS_RULE_EXTENSION($name ,$len); # we don't want to create three answer rules in different modes. 619 my $out = MODES( 620 'TeX' => $answer_rule, 621 'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}', 622 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 623 'HTML' => $answer_rule 624 ); 625 626 $out; 627 } 628 sub ans_box { 629 my $row = shift; 630 my $col =shift; 631 $row = 5 unless $row; 632 $col = 80 unless $col; 633 my $name = NEW_ANS_NAME(++$main::ans_rule_count); 634 NAMED_ANS_BOX($name ,$row,$col); 635 } 636 637 #this is legacy code; use ans_checkbox instead 638 sub checkbox { 639 my %options = @_; 640 qq!<INPUT TYPE="checkbox" NAME="$options{'-name'}" VALUE="$options{'-value'}">$options{'-label'}! 641 } 642 643 644 sub NAMED_POP_UP_LIST { 645 my $name = shift; 646 my @list = @_; 647 $name = RECORD_ANS_NAME($name); # record answer name 648 my $answer_value = ''; 649 $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name}); 650 my $out = ""; 651 if ($displayMode eq "HTML" or $displayMode eq "HTML_tth") { 652 $out = qq!<SELECT NAME = "$name" SIZE=1> \n!; 653 my $i; 654 foreach ($i=0; $i< @list; $i=$i+2) { 655 my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : ""; 656 $out .= qq!<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1] </OPTION>\n!; 657 }; 658 $out .= " </SELECT>\n"; 659 } elsif ( $displayMode eq "Latex2HTML") { 660 $out = qq! \\begin{rawhtml}<SELECT NAME = "$name" SIZE=1> \\end{rawhtml} \n !; 661 my $i; 662 foreach ($i=0; $i< @list; $i=$i+2) { 663 my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : ""; 664 $out .= qq!\\begin{rawhtml}<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1] </OPTION>\\end{rawhtml}\n!; 665 }; 666 $out .= " \\begin{rawhtml}</SELECT>\\end{rawhtml}\n"; 667 } elsif ( $displayMode eq "TeX") { 668 $out .= "\\fbox{?}"; 669 } 670 671 } 672 673 sub pop_up_list { 674 my @list = @_; 675 my $name = NEW_ANS_NAME(++$main::ans_rule_count); # get new answer name 676 NAMED_POP_UP_LIST($name, @list); 677 } 678 679 # end answer blank macros 680 681 =head2 Hints and solutions macros 682 683 solution('text','text2',...); 684 SOLUTION('text','text2',...); # equivalent to TEXT(solution(...)); 685 686 hint('text', 'text2', ...); 687 HINT('text', 'text2',...); # equivalent to TEXT("$BR$HINT" . hint(@_) . "$BR") if hint(@_); 688 689 Solution prints its concatenated input when the check box named 'ShowSol' is set and 690 the time is after the answer date. The check box 'ShowSol' is visible only after the 691 answer date or when the problem is viewed by a professor. 692 693 $envir{'displaySolutionsQ'} is set to 1 when a solution is to be displayed. 694 695 Hints are shown only after the number of attempts is greater than $:showHint 696 ($main::showHint defaults to 1) and the check box named 'ShowHint' is set. The check box 697 'ShowHint' is visible only after the number of attempts is greater than $main::showHint. 698 699 $envir{'displayHintsQ'} is set to 1 when a hint is to be displayed. 700 701 702 =cut 703 704 705 706 # solution prints its input when $displaySolutionsQ is set. 707 # use as TEXT(solution("blah, blah"); 708 # \$solutionExists 709 # is passed to processProblem which displays a "show Solution" button 710 # when a solution is available for viewing 711 712 713 sub solution { 714 my @in = @_; 715 my $out = ''; 716 $main::solutionExists =1; 717 if ($envir{'displaySolutionsQ'}) {$out = join(' ',@in);} 718 $out; 719 } 720 721 722 sub SOLUTION { 723 TEXT( solution(@_)) ; 724 } 725 726 727 728 sub hint { 729 my @in = @_; 730 my $out = ''; 731 732 $main::hintExists =1; 733 $main::numOfAttempts = 0 unless defined($main::numOfAttempts); 734 735 if ($main::displayMode eq 'TeX') { 736 $out = ''; # do nothing since hints are not available for download 737 } elsif (($envir{'displayHintsQ'}) and ($main::numOfAttempts >= $main::showHint)) 738 739 ## the second test above prevents a hint being shown if a doctored form is submitted 740 741 {$out = join(' ',@in);} # show hint 742 743 $out ; 744 } 745 746 747 sub HINT { 748 TEXT("$main::BR" . hint(@_) . "$main::BR") if hint(@_); 749 } 750 751 752 753 # End hints and solutions macros 754 ################################# 755 756 # Produces a random number between $begin and $end with increment 1. 757 # You do not have to worry about integer or floating point types. 758 759 =head2 Pseudo-random number generator 760 761 Usage: 762 random(0,5,.1) # produces a random number between 0 and 5 in increments of .1 763 non_zero_random(0,5,.1) # gives a non-zero random number 764 765 list_random(2,3,5,6,7,8,10) # produces random value from the list 766 list_random(2,3, (5..8),10) # does the same thing 767 768 SRAND(seed) # resets the main random generator -- use very cautiously 769 770 771 SRAND(time) will create a different problem everytime it is called. This makes it difficult 772 to check the answers :-). 773 774 SRAND($envir{'inputs_ref'}->{'key'} ) will create a different problem for each login session. 775 This is probably what is desired. 776 777 =cut 778 779 780 sub random { 781 my ($begin, $end, $incr) = @_; 782 $main::PG_random_generator->random($begin,$end,$incr); 783 } 784 785 786 sub non_zero_random { ##gives a non-zero random number 787 my (@arguments)=@_; 788 my $a=0; 789 my $i=100; #safety counter 790 while ($a==0 && ( 0 < $i-- ) ) { 791 $a=random(@arguments); 792 } 793 $a; 794 } 795 796 sub list_random { 797 my(@li) = @_; 798 return $li[random(1,scalar(@li))-1]; 799 } 800 801 sub SRAND { # resets the main random generator -- use cautiously 802 my $seed = shift; 803 $main::PG_random_generator -> srand($seed); 804 } 805 806 # display macros 807 808 =head2 Display Macros 809 810 These macros produce different output depending on the display mode being used to show 811 the problem on the screen, or whether the problem is being converted to TeX to produce 812 a hard copy output. 813 814 MODES ( TeX => "Output this in TeX mode", 815 HTML => "output this in HTML mode", 816 HTML_tth => "output this in HTML_tth mode", 817 Latex2HTML => "output this in Latex2HTML mode", 818 ) 819 820 TEX (tex_version, html_version) #obsolete 821 822 M3 (tex_version, latex2html_version, html_version) #obsolete 823 824 825 826 =cut 827 828 829 sub TEX { 830 my ($tex, $html ) = @_; 831 MODES(TeX => $tex, HTML => $html, HTML_tth => $html); 832 } 833 834 835 sub M3 { 836 my($tex,$l2h,$html) = @_; 837 MODES(TeX => $tex, Latex2HTML => $l2h, HTML => $html, HTML_tth => $html); 838 } 839 840 # This replaces M3. You can add new modes at will to this one. 841 842 sub MODES { 843 my %options = @_; 844 return $options{$displayMode} 845 if defined( $options{$displayMode} ); 846 847 # default searches. 848 if ($displayMode eq "Latex2HTML") { 849 return $options{TeX} 850 if defined( $options{TeX} ); 851 return $options{HTML} 852 if defined( $options{HTML} ); 853 die " ERROR in using MODES: 'HTML' and 'TeX' options not defined for 'Latex2HTML'"; 854 } 855 856 if ($displayMode eq "HTML_tth") { 857 return $options{HTML} 858 if defined( $options{HTML} ); 859 die " ERROR in using MODES: 'HTML' option not defined for HTML_tth"; 860 861 } 862 863 # trap undefined errors 864 die "ERROR in defining MODES: Can't find |$displayMode| among 865 available options:" . join(" ", keys(%options) ) 866 . " file " . __FILE__ ." line " . __LINE__."\n\n"; 867 868 } 869 870 871 # end display macros 872 873 874 =head2 Display constants 875 876 @ALPHABET ALPHABET() capital letter alphabet -- ALPHABET[0] = 'A' 877 $PAR PAR() paragraph character (\par or <p>) 878 $BR BR() line break character 879 $LQ LQ() left double quote 880 $RQ RQ() right double quote 881 $BM BM() begin math 882 $EM EM() end math 883 $BDM BDM() begin display math 884 $EDM EDM() end display math 885 $LTS LTS() strictly less than 886 $GTS GTS() strictly greater than 887 $LTE LTE() less than or equal 888 $GTE GTE() greater than or equal 889 $BEGIN_ONE_COLUMN BEGIN_ONE_COLUMN() begin one-column mode 890 $END_ONE_COLUMN END_ONE_COLUMN() end one-column mode 891 $SOL SOLUTION_HEADING() solution headline 892 $SOLUTION SOLUTION_HEADING() solution headline 893 $HINT HINT_HEADING() hint headline 894 $US US() underscore character 895 $SPACE SPACE() space character (tex and latex only) 896 $BBOLD BBOLD() begin bold typeface 897 $EBOLD EBOLD() end bold typeface 898 $BITALIC BITALIC() begin italic typeface 899 $EITALIC EITALIC() end italic typeface 900 $BCENTER BCENTER() begin centered environment 901 $ECENTER ECENTER() end centered environment 902 $HR HR() horizontal rule 903 $LBRACE LBRACE() left brace 904 $LB LB () left brace 905 $RBRACE RBRACE() right brace 906 $RB RB () right brace 907 $DOLLAR DOLLAR() a dollar sign 908 $PERCENT PERCENT() a percent sign 909 $CARET CARET() a caret sign 910 $PI PI() the number pi 911 $E E() the number e 912 913 =cut 914 915 916 917 918 919 # A utility variable. Notice that "B"=$ALPHABET[1] and 920 # "ABCD"=@ALPHABET[0..3]. 921 922 sub ALPHABET { 923 ('A'..'ZZ')[@_]; 924 } 925 926 ############################################################### 927 # Some constants which are different in tex and in HTML 928 # The order of arguments is TeX, Latex2HTML, HTML 929 sub PAR { MODES( TeX => '\\par ',Latex2HTML => '\\par ',HTML => '<P>' ); }; 930 sub BR { MODES( TeX => '\\par\\noindent ',Latex2HTML => '\\par\\noindent ',HTML => '<BR>'); }; 931 sub LQ { MODES( TeX => "``", Latex2HTML => '"', HTML => '"' ); }; 932 sub RQ { MODES( TeX => "''", Latex2HTML => '"', HTML => '"' ); }; 933 sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML => ''); }; # begin math mode 934 sub EM { MODES(TeX => '\\)', Latex2HTML => '\\)', HTML => ''); }; # end math mode 935 sub BDM { MODES(TeX => '\\[', Latex2HTML => '\\[', HTML => '<P ALIGN=CENTER>'); }; #begin displayMath mode 936 sub EDM { MODES(TeX => '\\]', Latex2HTML => '\\]', HTML => '</P>'); }; #end displayMath mode 937 sub LTS { MODES(TeX => ' < ', Latex2HTML => ' \\lt ', HTML => '<'); }; 938 sub GTS {MODES(TeX => ' > ', Latex2HTML => ' \\gt ', HTML => '>'); }; 939 sub LTE { MODES(TeX => ' \\le ', Latex2HTML => ' \\le ', HTML => '<=' ); }; 940 sub GTE { MODES(TeX => ' \\ge ', Latex2HTML => ' \\ge ', HTML => '>'); }; 941 sub BEGIN_ONE_COLUMN { MODES(TeX => " \\end{multicols}\n", Latex2HTML => " ", HTML => " "); }; 942 sub END_ONE_COLUMN { MODES(TeX => 943 " \\begin{multicols}{2}\n\\columnwidth=\\linewidth\n", 944 Latex2HTML => ' ', HTML => ' '); 945 946 }; 947 sub SOLUTION_HEADING { MODES( TeX => '\\par {\\bf Solution:}', 948 Latex2HTML => '\\par {\\bf Solution:}', 949 HTML => '<P><B>Solution:</B>'); 950 }; 951 sub HINT_HEADING { MODES( TeX => "\\par {\\bf Hint:}", Latex2HTML => "\\par {\\bf Hint:}", HTML => "<P><B>Hint:</B>"); }; 952 sub US { MODES(TeX => '\\_', Latex2HTML => '\\_', HTML => '_');}; # underscore, e.g. file${US}name 953 sub SPACE { MODES(TeX => '\\ ', Latex2HTML => '\\ ', HTML => ' ');}; # force a space in latex, doesn't force extra space in html 954 sub BBOLD { MODES(TeX => '{\\bf ', Latex2HTML => '{\\bf ', HTML => '<B>'); }; 955 sub EBOLD { MODES( TeX => '}', Latex2HTML => '}',HTML => '</B>'); }; 956 sub BITALIC { MODES(TeX => '{\\it ', Latex2HTML => '{\\it ', HTML => '<I>'); }; 957 sub EITALIC { MODES(TeX => '} ', Latex2HTML => '} ', HTML => '</I>'); }; 958 sub BCENTER { MODES(TeX => '\\begin{center} ', Latex2HTML => ' \\begin{rawhtml} <div align="center"> \\end{rawhtml} ', HTML => '<div align="center">'); }; 959 sub ECENTER { MODES(TeX => '\\end{center} ', Latex2HTML => ' \\begin{rawhtml} </div> \\end{rawhtml} ', HTML => '</div>'); }; 960 sub HR { MODES(TeX => '\\par\\hrulefill\\par ', Latex2HTML => '\\begin{rawhtml} <HR> \\end{rawhtml}', HTML => '<HR>'); }; 961 sub LBRACE { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '\{' , HTML_tth=> '\\lbrace' ); }; 962 sub RBRACE { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '\}' , HTML_tth=> '\\rbrace',); }; 963 sub LB { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '\{' , HTML_tth=> '\\lbrace' ); }; 964 sub RB { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '\}' , HTML_tth=> '\\rbrace',); }; 965 sub DOLLAR { MODES( TeX => '\\$', Latex2HTML => '\\$', HTML => '$' ); }; 966 sub PERCENT { MODES( TeX => '\\%', Latex2HTML => '\\%', HTML => '%' ); }; 967 sub CARET { MODES( TeX => '\\verb+^+', Latex2HTML => '\\verb+^+', HTML => '^' ); }; 968 sub PI {4*atan2(1,1);}; 969 sub E {exp(1);}; 970 971 ############################################################### 972 ## Evaluation macros 973 974 975 =head2 TEXT macros 976 977 Usage: 978 TEXT(@text); 979 980 This is the simplest way to print text from a problem. The strings in the array C<@text> are concatenated 981 with spaces between them and printed out in the text of the problem. The text is not processed in any other way. 982 C<TEXT> is defined in PG.pl. 983 984 Usage: 985 BEGIN_TEXT 986 text..... 987 END_TEXT 988 989 This is the most common way to enter text into the problem. All of the text between BEGIN_TEXT and END_TEXT 990 is processed by the C<EV3> macro described below and then printed using the C<TEXT> command. The two key words 991 must appear on lines by themselves. The preprocessing that makes this construction work is done in F<PGtranslator.pm>. 992 See C<EV3> below for details on the processing. 993 994 995 =cut 996 997 =head2 Evaluation macros 998 999 =head3 EV3 1000 1001 TEXT(EV3("This is a formulat \( \int_0^5 x^2 \, dx \) "); 1002 TEXT(EV3(@text)); 1003 1004 TEXT(EV3(<<'END_TEXT')); 1005 text stuff... 1006 END_TEXT 1007 1008 1009 The BEGIN_TEXT/END_TEXT construction is translated into the construction above by PGtranslator.pm. END_TEXT must appear 1010 on a line by itself and be left justified. (The << construction is known as a "here document" in UNIX and in PERL.) 1011 1012 The single quotes around END_TEXT mean that no automatic interpolation of variables takes place in the text. 1013 Using EV3 with strings which have been evaluated by double quotes may lead to unexpected results. 1014 1015 1016 The evaluation macro E3 first evaluates perl code inside the braces: C<\{ code \}>. 1017 Any perl statment can be put inside the braces. The 1018 result of the evaluation (i.e. the last statement evaluated) replaces the C<\{ code \}> construction. 1019 1020 Next interpolation of all variables (e.g. C<$var or @array> ) is performed. 1021 1022 Then mathematical formulas in TeX are evaluated within the 1023 C<\( tex math mode \)> and 1024 C<\[ tex display math mode \] > 1025 constructions, in that order: 1026 1027 =head3 FEQ 1028 1029 FEQ($string); # processes and outputs the string 1030 1031 1032 The mathematical formulas are run through the macro C<FEQ> (Format EQuations) which performs 1033 several substitutions (see below). 1034 In C<HTML_tth> mode the resulting code is processed by tth to obtain an HTML version 1035 of the formula. (In the future processing by WebEQ may be added here as another option.) 1036 The Latex2HTML mode does nothing 1037 at this stage; it creates the entire problem before running it through 1038 TeX and creating the GIF images of the equations. 1039 1040 The resulting string is output (and usually fed into TEXT to be printed in the problem). 1041 1042 Usage: 1043 1044 $string2 = FEQ($string1); 1045 1046 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 1047 understood with an example. 1048 1049 $string1 = "${a}x^2 + ${b}x + {$c:%.1f}"; $a = 3;, $b = -2; $c = -7.345; 1050 1051 when interpolated becomes: 1052 1053 $string1 = '3x^2 + -2x + {-7.345:%0.1f} 1054 1055 FEQ first changes the number of decimal places displayed, so that the last term becomes -7.3 Then it removes the 1056 extraneous plus and minus signs, so that the final result is what you want: 1057 1058 $string2 = '3x^2 - 2x -7.3'; 1059 1060 (The %0.1f construction 1061 is the same formatting convention used by Perl and nearly identical to the one used by the C printf statement. Some common 1062 usage: %0.3f 3 decimal places, fixed notation; %0.3e 3 significant figures exponential notation; %0.3g uses either fixed 1063 or exponential notation depending on the size of the number.) 1064 1065 Two additional legacy formatting constructions are also supported: 1066 1067 C<?{$c:%0.3f} > will give a number with 3 decimal places and a negative 1068 sign if the number is negative, no sign if the number is positive. 1069 1070 C<!{$c:%0.3f}> determines the sign and prints it 1071 whether the number is positive or negative. 1072 1073 =head3 EV2 1074 1075 TEXT(EV2(@text)); 1076 1077 TEXT(EV2(<<END_OF_TEXT)); 1078 text stuff... 1079 END_OF_TEXT 1080 1081 This is a precursor to EV3. In this case the constants are interpolated first, before the evaluation of the \{ ...code...\} 1082 construct. This can lead to unexpected results. For example C<\{ join(" ", @text) \}> with C<@text = ("Hello","World);> becomes, 1083 after interpolation, C<\{ join(" ",Hello World) \}> which then causes an error when evaluated because Hello is a bare word. 1084 C<EV2> can still be useful if you allow for this, and in particular it works on double quoted strings, which lead to 1085 unexpected results with C<EV3>. Using single quoted strings with C<EV2> may lead to unexpected results. 1086 1087 The unexpected results have to do with the number of times backslashed constructions have to be escaped. It's quite messy. For 1088 more details get a good Perl book and then read the code. :-) 1089 1090 1091 1092 1093 =cut 1094 1095 1096 sub ev_substring { 1097 my $string = shift; 1098 my $start_delim = shift; 1099 my $end_delim = shift; 1100 my $actionRef = shift; 1101 my ($eval_out,$PG_eval_errors,$PG_full_error_report)=(); 1102 my $out = ""; 1103 while ($string) { 1104 if ($string =~ /\Q$start_delim\E/s) { 1105 #print "$start_delim $end_delim evaluating_substring=$string<BR>"; 1106 $string =~ s/^(.*?)\Q$start_delim\E//s; # get string up to next \{ ---treats string as a single line, ignoring returns 1107 $out .= $1; 1108 #print "$start_delim $end_delim substring_out=$out<BR>"; 1109 $string =~ s/^(.*?)\Q$end_delim\E//s; # get perl code up to \} ---treats string as a single line, ignoring returns 1110 #print "$start_delim $end_delim evaluate_string=$1<BR>"; 1111 ($eval_out,$PG_eval_errors,$PG_full_error_report) = &$actionRef($1); 1112 $eval_out = "$start_delim $eval_out $end_delim" if $PG_full_error_report; 1113 $out = $out . $eval_out; 1114 #print "$start_delim $end_delim new substring_out=$out<BR><p><BR>"; 1115 $out .="$main::PAR ERROR $0 in ev_substring, PGbasicmacros.pl:$main::PAR <PRE> $@ </PRE>$main::PAR" if $@; 1116 } 1117 else { 1118 $out .= $string; # flush the last part of the string 1119 last; 1120 } 1121 1122 } 1123 $out; 1124 } 1125 sub safe_ev { 1126 my ($out,$PG_eval_errors,$PG_full_error_report) = &old_safe_ev; # process input by old_safe_ev first 1127 $out =~s/\\/\\\\/g; # protect any new backslashes introduced. 1128 ($out,$PG_eval_errors,$PG_full_error_report) 1129 } 1130 1131 sub old_safe_ev { 1132 my $in = shift; 1133 my ($out,$PG_eval_errors,$PG_full_error_report) = PG_restricted_eval("$in;"); 1134 # the addition of the ; seems to provide better error reporting 1135 if ($PG_eval_errors) { 1136 my @errorLines = split("\n",$PG_eval_errors); 1137 #$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> "; 1138 warn " ERROR in old_safe_ev, PGbasicmacros.pl: <PRE> 1139 ## There is an error occuring inside evaluation brackets \\{ ...code... \\} 1140 ## somewhere in an EV2 or EV3 or BEGIN_TEXT block. 1141 ## Code evaluated: 1142 ## $in 1143 ##" .join("\n ", @errorLines). " 1144 ##</PRE>$main::BR 1145 "; 1146 $out ="$main::PAR $main::BBOLD $in $main::EBOLD $main::PAR"; 1147 1148 1149 } 1150 1151 ($out,$PG_eval_errors,$PG_full_error_report); 1152 } 1153 1154 sub FEQ { # Format EQuations 1155 my $in = shift; 1156 # formatting numbers -- the ?{} and !{} constructions 1157 $in =~s/\?\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &sspf($1,$2) )}/g; 1158 $in =~s/\!\s*\{([.\-\$\w\d]+):?([%.\da-z]*)\}/${ \( &spf($1,$2) )}/g; 1159 1160 # more formatting numbers -- {number:format} constructions 1161 $in =~ s/\{(\s*[\+\-\d\.]+[eE]*[\+\-]*\d*):(\%\d*.\d*\w)}/${ \( &spf($1,$2) )}/g; 1162 $in =~ s/\+\s*\-/ - /g; 1163 $in =~ s/\-\s*\+/ - /g; 1164 $in =~ s/\+\s*\+/ + /g; 1165 $in =~ s/\-\s*\-/ + /g; 1166 $in; 1167 } 1168 1169 sub math_ev3 { 1170 my $in = shift; #print "in=$in<BR>"; 1171 my ($out,$PG_eval_errors,$PG_full_error_report); 1172 $in = FEQ($in); 1173 $in =~ s/%/\\%/g; # % causes trouble in TeX and HTML_tth it usually (always?) indicates an error, not comment 1174 return("$main::BM $in $main::EM") unless ($displayMode eq 'HTML_tth'); 1175 $in = "\\(" . $in . "\\)"; 1176 $out = tth($in); 1177 ($out,$PG_eval_errors,$PG_full_error_report); 1178 1179 } 1180 1181 sub display_math_ev3 { 1182 my $in = shift; #print "in=$in<BR>"; 1183 my ($out,$PG_eval_errors,$PG_full_error_report); 1184 $in = FEQ($in); 1185 $in =~ s/%/\\%/g; 1186 return("$main::BDM $in $main::EDM") unless $displayMode eq 'HTML_tth' ; 1187 $in = "\\[" . $in . "\\]"; 1188 $out =tth($in); 1189 ($out,$PG_eval_errors,$PG_full_error_report); 1190 } 1191 1192 sub EV2 { 1193 my $string = join(" ",@_); 1194 # evaluate code inside of \{ \} (no nesting allowed) 1195 $string = ev_substring($string,"\\{","\\}",\&old_safe_ev); 1196 $string = ev_substring($string,"\\<","\\>",\&old_safe_ev); 1197 $string = ev_substring($string,"\\(","\\)",\&math_ev3); 1198 $string = ev_substring($string,"\\[","\\]",\&display_math_ev3); 1199 # macros for displaying math 1200 $string =~ s/\\\(/$main::BM/g; 1201 $string =~ s/\\\)/$main::EM/g; 1202 $string =~ s/\\\[/$main::BDM/g; 1203 $string =~ s/\\\]/$main::EDM/g; 1204 $string; 1205 } 1206 1207 sub EV3{ 1208 my $string = join(" ",@_); 1209 # evaluate code inside of \{ \} (no nesting allowed) 1210 $string = ev_substring($string,"\\\\{","\\\\}",\&safe_ev); # handles \{ \} in single quoted strings of PG files 1211 # interpolate variables 1212 my ($evaluated_string,$PG_eval_errors,$PG_full_errors) = PG_restricted_eval("<<END_OF_EVALUATION_STRING\n$string\nEND_OF_EVALUATION_STRING\n"); 1213 if ($PG_eval_errors) { 1214 my @errorLines = split("\n",$PG_eval_errors); 1215 $string =~ s/</</g; $string =~ s/>/>/g; 1216 $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> "; 1217 $@=""; 1218 } 1219 $string = $evaluated_string; 1220 $string = ev_substring($string,"\\(","\\)",\&math_ev3); 1221 $string = ev_substring($string,"\\[","\\]",\&display_math_ev3); 1222 $string; 1223 } 1224 1225 =head2 Formatting macros 1226 1227 beginproblem() # generates text listing number and the point value of 1228 # the problem. It will also print the file name containing 1229 # the problem for users listed in the PRINT_FILE_NAMES_FOR PG_environment 1230 # variable. 1231 OL(@array) # formats the array as an Ordered List ( <OL> </OL> ) enumerated by letters. 1232 1233 htmlLink($url, $text) 1234 # Places a reference to the URL with the specified text in the problem. 1235 # A common usage is \{ htmlLink(alias('prob1_help.html') \}, 'for help') 1236 # where alias finds the full address of the prob1_help.html file in the same directory 1237 # as the problem file 1238 appletLink($url, $parameters) 1239 # For example 1240 # appletLink(q! archive="http: //webwork.math.rochester.edu/gage/xFunctions/xFunctions.zip" 1241 code="xFunctionsLauncher.class" width=100 height=14!, 1242 " parameter text goes here") 1243 # will link to xFunctions. 1244 1245 low level: 1246 1247 spf($number, $format) # prints the number with the given format 1248 sspf($number, $format) # prints the number with the given format, always including a sign. 1249 protect_underbar($string) # protects the underbar (class_name) in strings which may have to pass through TeX. 1250 1251 =cut 1252 1253 sub beginproblem { 1254 my $out = ""; 1255 my $TeXFileName = protect_underbar($main::fileName); 1256 my $l2hFileName = protect_underbar($main::fileName); 1257 my %inlist; 1258 my $points ='pts'; 1259 $points = 'pt' if $main::problemValue == 1; 1260 ## Prepare header for the problem 1261 grep($inlist{$_}++,@{ $envir{'PRINT_FILE_NAMES_FOR'} }); 1262 if ( defined($inlist{$main::studentLogin}) and ($inlist{$main::studentLogin} > 0) ) { 1263 $out = &M3("\n\n\\medskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.{\\footnotesize ($main::problemValue $points) $TeXFileName}}\\newline ", 1264 " \\begin{rawhtml} ($main::problemValue $points) <B>$l2hFileName</B><BR>\\end{rawhtml}", 1265 "($main::problemValue $points) <B>$main::fileName</B><BR>" 1266 ); 1267 } else { 1268 $out = &M3("\n\n\\smallskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.}($main::problemValue $points) ", 1269 "($main::problemValue $points) ", 1270 "($main::problemValue $points) " 1271 ); 1272 } 1273 $out; 1274 1275 } 1276 1277 # kludge to clean up path names 1278 ## allow underscore character in set and section names and also allows line breaks at / 1279 sub protect_underbar { 1280 my $in = shift; 1281 if ($displayMode eq 'TeX') { 1282 1283 $in =~ s|_|\\\_|g; 1284 $in =~ s|/|\\\-/|g; # allows an optional hyphenation of the path (in tex) 1285 } 1286 $in; 1287 } 1288 1289 1290 # An example of a macro which prints out a list (with letters) 1291 sub OL { 1292 my(@array) = @_; 1293 my $i = 0; 1294 my $out= &M3( 1295 "\\begin{enumerate}\n", 1296 " \\begin{rawhtml} <OL TYPE=\"A\" VALUE=\"1\"> \\end{rawhtml} ", 1297 "<OL TYPE=\"A\" VALUE=\"1\">\n" 1298 ) ; 1299 my $elem; 1300 foreach $elem (@array) { 1301 $out .= &M3( 1302 "\\item[$main::ALPHABET[$i].] $elem\n", 1303 " \\begin{rawhtml} <LI> \\end{rawhtml} $elem ", 1304 "<LI> $elem\n" 1305 ) ; 1306 $i++; 1307 } 1308 $out .= &M3( 1309 "\\end{enumerate}\n", 1310 " \\begin{rawhtml} </OL>\n \\end{rawhtml} ", 1311 "</OL>\n" 1312 ) ; 1313 } 1314 1315 sub htmlLink { 1316 my $url = shift; 1317 my $text = shift; 1318 my $options = shift; 1319 $options = "" unless defined($options); 1320 return "${main::BBOLD}[ broken link: $text ] ${main::EBOLD}" unless defined($url); 1321 M3( "{\\bf \\underline{$text} }", 1322 "\\begin{rawhtml} <A HREF=\"$url\" $options> $text </A>\\end{rawhtml}", 1323 "<A HREF=\"$url\" $options> $text </A>" 1324 ); 1325 } 1326 sub appletLink { 1327 my $url = shift; 1328 my $options = shift; 1329 $options = "" unless defined($options); 1330 M3( "{\\bf \\underline{APPLET} }", 1331 "\\begin{rawhtml} <APPLET $url> $options </APPLET>\\end{rawhtml}", 1332 "<APPLET $url> $options </APPLET>" 1333 ); 1334 } 1335 sub spf { 1336 my($number,$format) = @_; # attention, the order of format and number are reversed 1337 $format = "%4.3g" unless $format; # default value for format 1338 sprintf($format, $number); 1339 } 1340 sub sspf { 1341 my($number,$format) = @_; # attention, the order of format and number are reversed 1342 $format = "%4.3g" unless $format; # default value for format 1343 my $sign = $number>=0 ? " + " : " - "; 1344 $number = $number>=0 ? $number : -$number; 1345 $sign .sprintf($format, $number); 1346 } 1347 1348 =head2 Sorting and other list macros 1349 1350 1351 1352 Usage: 1353 lex_sort(@list); # outputs list in lexigraphic (alphabetical) order 1354 num_sort(@list); # outputs list in numerical order 1355 uniq( @list); # outputs a list with no duplicates. Order is unspecified. 1356 1357 PGsort( \&sort_subroutine, @list); 1358 # &sort_subroutine defines order. It's output must be -1,0 or 1. 1359 1360 =cut 1361 1362 # uniq gives unique elements of a list: 1363 sub uniq { 1364 my (@in) =@_; 1365 my %temp = (); 1366 while (@in) { 1367 $temp{shift(@in)}++; 1368 } 1369 my @out = keys %temp; # sort is causing trouble with Safe.?? 1370 @out; 1371 } 1372 1373 sub lex_sort { 1374 PGsort sub {$_[0] cmp $_[1]}, @_; 1375 } 1376 sub num_sort { 1377 PGsort sub {$_[0] <=> $_[1]}, @_; 1378 } 1379 1380 1381 =head2 Macros for handling tables 1382 1383 Usage: 1384 begintable( number_of_columns_in_table) 1385 row(@dataelements) 1386 endtable() 1387 1388 Example of useage: 1389 1390 BEGIN_TEXT 1391 This problem tests calculating new functions from old ones:$BR 1392 From the table below calculate the quantities asked for:$BR 1393 \{begintable(scalar(@firstrow)+1)\} 1394 \{row(" \(x\) ",@firstrow)\} 1395 \{row(" \(f(x)\) ", @secondrow)\} 1396 \{row(" \(g(x)\) ", @thirdrow)\} 1397 \{row(" \(f'(x)\) ", @fourthrow)\} 1398 \{row(" \(g'(x)\) ", @fifthrow)\} 1399 \{endtable()\} 1400 1401 (The arrays contain numbers which are placed in the table.) 1402 1403 END_TEXT 1404 1405 =cut 1406 1407 sub begintable { 1408 my ($number)=shift; #number of columns in table 1409 my %options = @_; 1410 warn "begintable(cols) requires a number indicating the number of columns" unless defined($number); 1411 my $out = ""; 1412 if ($displayMode eq 'TeX') { 1413 $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{" . "|c" x $number . "|} \\hline\n"; 1414 } 1415 elsif ($displayMode eq 'Latex2HTML') { 1416 $out .= "\n\\begin{rawhtml} <TABLE , BORDER=1>\n\\end{rawhtml}"; 1417 } 1418 elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth') { 1419 $out .= "<TABLE BORDER=1>\n" 1420 } 1421 else { 1422 $out = "Error: PGchoicemacros: begintable: Unknown displayMode: $displayMode.\n"; 1423 } 1424 $out; 1425 } 1426 1427 sub endtable { 1428 my $out = ""; 1429 if ($displayMode eq 'TeX') { 1430 $out .= "\n\\end {tabular}\\end{center}\\par\\smallskip\n"; 1431 } 1432 elsif ($displayMode eq 'Latex2HTML') { 1433 $out .= "\n\\begin{rawhtml} </TABLE >\n\\end{rawhtml}"; 1434 } 1435 elsif ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth') { 1436 $out .= "</TABLE>\n"; 1437 } 1438 else { 1439 $out = "Error: PGchoicemacros: endtable: Unknown displayMode: $displayMode.\n"; 1440 } 1441 $out; 1442 } 1443 1444 1445 sub row { 1446 my @elements = @_; 1447 my $out = ""; 1448 if ($displayMode eq 'TeX') { 1449 while (@elements) { 1450 $out .= shift(@elements) . " &"; 1451 } 1452 chop($out); # remove last & 1453 $out .= "\\\\ \\hline \n"; 1454 # carriage returns must be added manually for tex 1455 } 1456 elsif ($displayMode eq 'Latex2HTML') { 1457 $out .= "\n\\begin{rawhtml}\n<TR>\n\\end{rawhtml}\n"; 1458 while (@elements) { 1459 $out .= " \n\\begin{rawhtml}\n<TD> \n\\end{rawhtml}\n" . shift(@elements) . " \n\\begin{rawhtml}\n</TD> \n\\end{rawhtml}\n"; 1460 } 1461 $out .= " \n\\begin{rawhtml}\n</TR> \n\\end{rawhtml}\n"; 1462 } 1463 elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') { 1464 $out .= "<TR>\n"; 1465 while (@elements) { 1466 $out .= "<TD>" . shift(@elements) . "</TD>"; 1467 } 1468 $out .= "\n</TR>\n"; 1469 } 1470 else { 1471 $out = "Error: PGchoicemacros: row: Unknown displayMode: $main::displayMode.\n"; 1472 } 1473 $out; 1474 } 1475 1476 =head2 Macros for displaying static images 1477 1478 Usage: 1479 $string = image($image, width => 100, height => 100, tex_size => 800) 1480 $string = image([$image1, $image2], width => 100, height => 100, tex_size => 800) 1481 $string = caption($string); 1482 $string = imageRow([$image1, $image2 ], [$caption1, $caption2]); 1483 # produces a complete table with rows of pictures. 1484 1485 1486 =cut 1487 1488 # More advanced macros 1489 sub image { 1490 my $image_ref = shift; 1491 my @opt = @_; 1492 unless (scalar(@opt) % 2 == 0 ) { 1493 warn "ERROR in image macro. A list of macros must be inclosed in square brackets."; 1494 } 1495 my %in_options = @opt; 1496 my %known_options = ( width => 100, 1497 height => 100, 1498 tex_size => 800 1499 ); 1500 # # handle options 1501 my %out_options = %known_options; 1502 foreach my $opt_name (keys %in_options) { 1503 if ( exists( $known_options{$opt_name} ) ) { 1504 $out_options{$opt_name} = $in_options{$opt_name} if exists( $in_options{$opt_name} ) ; 1505 } else { 1506 die "Option $opt_name not defined for image. " . 1507 "Default options are:<BR> ", display_options2(%known_options); 1508 1509 } 1510 } 1511 my $width = $out_options{width}; 1512 my $height = $out_options{height}; 1513 my $tex_size = $out_options{tex_size}; 1514 my $width_ratio = $tex_size*(.001); 1515 my @image_list = (); 1516 1517 if (ref($image_ref) =~ /ARRAY/ ) { 1518 @image_list = @{$image_ref}; 1519 } else { 1520 push(@image_list,$image_ref); 1521 } 1522 my @output_list = (); 1523 while(@image_list) { 1524 1525 my $imageURL = alias(shift @image_list); 1526 my $out=""; 1527 1528 1529 if ($main::displayMode eq 'TeX') { 1530 $out = qq!\\includegraphics[width=$width_ratio\\linewidth]{$imageURL}\n ! 1531 } 1532 elsif ($main::displayMode eq 'Latex2HTML') { 1533 $out = qq!\\begin{rawhtml}\n<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL" WIDTH="$width" HEIGHT="$height"></A>\n 1534 \\end{rawhtml}\n ! 1535 } 1536 elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') { 1537 $out = qq!<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL" WIDTH="$width" HEIGHT="$height"></A> 1538 ! 1539 } 1540 else { 1541 $out = "Error: PGchoicemacros: image: Unknown displayMode: $main::displayMode.\n"; 1542 } 1543 push(@output_list, $out); 1544 } 1545 wantarray ? @output_list : $output_list[0] ; 1546 } 1547 1548 # This is legacy code. 1549 sub images { 1550 my @in = @_; 1551 my @outlist = (); 1552 while (@in) { 1553 push(@outlist,&image( shift(@in) ) ); 1554 } 1555 @outlist; 1556 } 1557 1558 1559 sub caption { 1560 my ($out) = @_; 1561 $out = " $out \n" if $main::displayMode eq 'TeX'; 1562 $out = " $out " if $main::displayMode eq 'HTML'; 1563 $out = " $out " if $main::displayMode eq 'HTML_tth'; 1564 $out = " $out " if $main::displayMode eq 'Latex2HTML'; 1565 $out; 1566 } 1567 1568 sub captions { 1569 my @in = @_; 1570 my @outlist = (); 1571 while (@in) { 1572 push(@outlist,&caption( shift(@in) ) ); 1573 } 1574 @outlist; 1575 } 1576 1577 sub imageRow { 1578 1579 my $pImages = shift; 1580 my $pCaptions=shift; 1581 my $out = ""; 1582 my @images = @$pImages; 1583 my @captions = @$pCaptions; 1584 my $number = @images; 1585 # standard options 1586 my %options = ( 'tex_size' => 200, # width for fitting 4 across 1587 'height' => 100, 1588 'width' => 100, 1589 @_ # overwrite any default options 1590 ); 1591 1592 if ($main::displayMode eq 'TeX') { 1593 $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{" . "|c" x $number . "|} \\hline\n"; 1594 while (@images) { 1595 $out .= &image( shift(@images),%options ) . '&'; 1596 } 1597 chop($out); 1598 $out .= "\\\\ \\hline \n"; 1599 while (@captions) { 1600 $out .= &caption( shift(@captions) ) . '&'; 1601 } 1602 chop($out); 1603 $out .= "\\\\ \\hline \n\\end {tabular}\\end{center}\\par\\smallskip\n"; 1604 } elsif ($main::displayMode eq 'Latex2HTML'){ 1605 1606 $out .= "\n\\begin{rawhtml} <TABLE BORDER=1><TR>\n\\end{rawhtml}\n"; 1607 while (@images) { 1608 $out .= "\n\\begin{rawhtml} <TD>\n\\end{rawhtml}\n" . &image( shift(@images),%options ) 1609 . "\n\\begin{rawhtml} </TD>\n\\end{rawhtml}\n" ; 1610 } 1611 1612 $out .= "\n\\begin{rawhtml}</TR><TR>\\end{rawhtml}\n"; 1613 while (@captions) { 1614 $out .= "\n\\begin{rawhtml} <TH>\n\\end{rawhtml}\n".&caption( shift(@captions) ) 1615 . "\n\\begin{rawhtml} </TH>\n\\end{rawhtml}\n" ; 1616 } 1617 1618 $out .= "\n\\begin{rawhtml} </TR> </TABLE >\n\\end{rawhtml}"; 1619 } elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth'){ 1620 $out .= "<P>\n <TABLE BORDER=2 CELLPADDING=3 CELLSPACING=2 ><TR ALIGN=CENTER VALIGN=MIDDLE>\n"; 1621 while (@images) { 1622 $out .= " \n<TD>". &image( shift(@images),%options ) ."</TD>"; 1623 } 1624 $out .= "</TR>\n<TR>"; 1625 while (@captions) { 1626 $out .= " <TH>". &caption( shift(@captions) ) ."</TH>"; 1627 } 1628 $out .= "\n</TR></TABLE></P>\n" 1629 } 1630 else { 1631 $out = "Error: PGchoicemacros: imageRow: Unknown languageMode: $main::displayMode.\n"; 1632 warn $out; 1633 } 1634 $out; 1635 } 1636 1637 1638 ########### 1639 # Auxiliary macros 1640 1641 sub display_options2{ 1642 my %options = @_; 1643 my $out_string = ""; 1644 foreach my $key (keys %options) { 1645 $out_string .= " $key => $options{$key},<BR>"; 1646 } 1647 $out_string; 1648 } 1649 1650 1651 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |