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