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