[system] / trunk / webwork / system / courseScripts / PGbasicmacros.pl Repository:
ViewVC logotype

Annotation of /trunk/webwork/system/courseScripts/PGbasicmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 5 - (view) (download) (as text)

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9