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