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