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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 11 - (download) (as text) (annotate)
Mon Jun 18 15:21:51 2001 UTC (11 years, 11 months ago) by sam
File size: 49614 byte(s)
another setup script test (changed #! lines)

    1 #!/usr/local/bin/webwork-perl
    2 
    3 ####################################################################
    4 # Copyright @ 1995-1998 University of Rochester
    5 # All Rights Reserved
    6 ####################################################################
    7 
    8 =head1 NAME
    9 
   10     PGbasicmacros.pl --- located in the courseScripts directory
   11 
   12 =head1 SYNPOSIS
   13 
   14 
   15 
   16 =head1 DESCRIPTION
   17 
   18 
   19 
   20 =cut
   21 
   22 # this is equivalent to use strict, but can be used within the Safe compartment.
   23 BEGIN{
   24   be_strict;
   25 }
   26 
   27 _PGbasicmacros_init();  # initialize constants
   28 
   29 =head2  Answer blank macros:
   30 
   31 These produce answer blanks of various sizes or pop up lists or radio answer buttons.
   32 The names for the answer blanks are
   33 generated implicitly.
   34 
   35   ans_rule( width )
   36   tex_ans_rule( width )
   37   ans_radio_buttons(value1=>label1, value2,label2 => value3,label3=>...)
   38   pop_up_list(@list)  # list consists of (value => label,  PR => "Product rule",...)
   39 
   40 To indicate the checked position of radio buttons put a '%' in front of the value: C<ans_radio_buttons(1, 'Yes','%2','No')>
   41 will have 'No' checked.  C<tex_ans_rule> works inside math equations in C<HTML_tth> mode.  It does not work in C<Latex2HTML> mode
   42 since this mode produces gif pictures.
   43 
   44 
   45 The following method is defined in F<PG.pl> for entering the answer evaluators corresponding
   46 to answer rules with automatically generated names.  The answer evaluators are matched with the
   47 answer rules in the order in which they appear on the page.
   48 
   49   ANS(ans_evaluator1, ans_evaluator2,...);
   50 
   51 These are more primitive macros which produce answer blanks for specialized cases when complete
   52 control over the matching of answers blanks and answer evaluators is desired.
   53 The names of the answer blanks must be generated manually, and it is best if they do NOT begin
   54 with the default answer prefix (currently AnSwEr).
   55 
   56 
   57   NAMED_ANS_RULE(name, width)
   58   NAMED_ANS_BOX(name, rows, cols)
   59   NAMED_ANS_RADIO(name, value,label,)
   60   NAMED_ANS_RADIO_OPTION(name, value,label)
   61   NAMED_ANS_RADIO_BUTTONS(name,value1,label1,value2,label2,...)
   62   check_box('-name' =>answer5,'-value' =>'statement3','-label' =>'I loved this course!'   )
   63   NAMED_POP_UP_LIST($name, @list) # list consists of (value => tag,  PR => "Product rule",...)
   64 
   65 (Name is the name of the variable, value is the value given to the variable when this option is selected,
   66 and label is the text printed next to the button or check box.    Check box variables can have multiple values.)
   67 
   68 NAMED_ANS_RADIO_BUTTONS creates a sequence of NAMED_ANS_RADIO and NAMED_ANS_RADIO_OPTION  items which
   69 are  output either as an array or, in scalar context, as the array glued together with spaces.  It is
   70 usually easier to use this than to manually construct the radio buttons by hand.  However, sometimes
   71  extra flexibility is desiredin which case:
   72 
   73 When entering radio buttons using the "NAMED" format, you should use NAMED_ANS_RADIO button for the first button
   74 and then use NAMED_ANS_RADIO_OPTION for the remaining buttons.  NAMED_ANS_RADIO requires a matching answer evalutor,
   75 while NAMED_ANS_RADIO_OPTION does not. The name used for NAMED_ANS_RADIO_OPTION should match the name
   76 used for NAMED_ANS_RADIO (and the associated answer evaluator).
   77 
   78 
   79 
   80 The following method is defined in in F<PG.pl> for entering the answer evaluators corresponding
   81 to answer rules with automatically generated names.  The answer evaluators are matched with the
   82 answer rules in the order in which they appear on the page.
   83 
   84       NAMED_ANS(name1 => ans_evaluator1, name2 => ans_evaluator2,...);
   85 
   86 These auxiliary macros are defined in PG.pl
   87 
   88 
   89   NEW_ANS_NAME( number );   # produces a new answer blank name from a number by adding a prefix (AnSwEr)
   90                             # and registers this name as an implicitly labeled answer
   91                             # Its use is paired with each answer evaluator being entered using ANS()
   92 
   93     ANS_NUM_TO_NAME(number);  # adds the prefix (AnSwEr) to the number, but does nothing else.
   94 
   95   RECORD_ANS_NAME( name );  # records the order in which the answer blank  is rendered
   96                             # This is called by all of the constructs above, but must
   97                             # be called explicitly if an input blank is constructed explictly
   98                             # using HTML code.
   99 
  100 These are legacy macros:
  101 
  102   ANS_RULE( number, width );                  # equivalent to NAMED_ANS_RULE( NEW_ANS_NAME(number), width)
  103   ANS_BOX( question_number,height, width );       # equivalent to NAMED_ANS_BOX( NEW_ANS_NAME(number), height, width)
  104   ANS_RADIO( question_number, value,tag );        # equivalent to NAMED_ANS_RADIO( NEW_ANS_NAME(number), value,tag)
  105   ANS_RADIO_OPTION( question_number, value,tag );   # equivalent to NAMED_ANS_RADIO_OPTION( ANS_NUM_TO_NAME(number), value,tag)
  106 
  107 
  108 =cut
  109 
  110 
  111 sub NAMED_ANS_RULE {
  112   my($name,$col) = @_;
  113   my $len = 0.07*$col;
  114   my $answer_value = '';
  115   $answer_value = ${$main::inputs_ref}{$name} if    defined(${$main::inputs_ref}{$name});
  116     if ($answer_value =~ /\0/ ) {
  117       my @answers = split("\0", $answer_value);
  118       $answer_value = shift(@answers);  # use up the first answer
  119       $main::rh_sticky_answers{$name}=\@answers;  # store the rest
  120       $answer_value= '' unless defined($answer_value);
  121   }
  122   $name = RECORD_ANS_NAME($name);
  123   MODES(
  124     TeX => "\\mbox{\\parbox[t]{10pt}{\\hrulefill}}\\hrulefill\\quad ",
  125     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!,
  126     HTML => "<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"$answer_value\">\n"
  127   );
  128 }
  129 
  130 sub NAMED_ANS_RULE_OPTION {
  131   &NAMED_ANS_RULE_EXTENSION;
  132 }
  133 
  134 sub NAMED_ANS_RULE_EXTENSION {
  135   my($name,$col) = @_;
  136   my $len = 0.07*$col;
  137   my $answer_value = '';
  138   $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name});
  139   if ( defined($main::rh_sticky_answers{$name}) ) {
  140     $answer_value = shift( @{$main::rh_sticky_answers{$name}});
  141     $answer_value = '' unless defined($answer_value);
  142   }
  143   MODES(
  144     TeX => '\\hrulefill\\quad ',
  145     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=TEXT SIZE=$col NAME=\"$name\" VALUE = \"\">\n\\end{rawhtml}\n!,
  146     HTML => qq!<INPUT TYPE=TEXT SIZE=$col NAME = "$name" VALUE = "$answer_value">\n!
  147   );
  148 }
  149 
  150 sub ANS_RULE {
  151   my($number,$col) = @_;
  152   my $name = NEW_ANS_NAME($number);
  153     NAMED_ANS_RULE($name,$col);
  154 }
  155 
  156 
  157 sub  NAMED_ANS_BOX {
  158   my($name,$row,$col) = @_;
  159   $row = 10 unless defined($row);
  160   $col = 80 unless defined($col);
  161   $name = RECORD_ANS_NAME($name);
  162   my $len = 0.07*$col;
  163   my $height = .07*$row;
  164   my $answer_value = '';
  165   $answer_value = $main::inputs_ref->{$name} if defined( $main::inputs_ref->{$name} );
  166   my $out = M3(
  167        qq!\\vskip $height in \\hrulefill\\quad !,
  168        qq!\\begin{rawhtml}<TEXTAREA NAME="$name" ROWS="$row" COLS="$col"
  169                WRAP="VIRTUAL">$answer_value</TEXTAREA>\\end{rawhtml}!,
  170          qq!<TEXTAREA NAME="$name" ROWS="$row" COLS="$col"
  171                WRAP="VIRTUAL">$answer_value</TEXTAREA>!
  172          );
  173   $out;
  174 }
  175 
  176 sub  ANS_BOX {
  177   my($number,$row,$col) = @_;
  178   my $name = NEW_ANS_NAME($number);
  179     NAMED_ANS_BOX($name,$row,$col);
  180 }
  181 
  182 sub NAMED_ANS_RADIO {
  183   my $name = shift;
  184   my $value = shift;
  185     my $tag =shift;
  186     $name = RECORD_ANS_NAME($name);
  187     my $checked = '';
  188     if ($value =~/^\%/) {
  189       $value =~ s/^\%//;
  190       $checked = 'CHECKED'
  191     }
  192   if (defined($main::inputs_ref->{$name}) ) {
  193     if ($main::inputs_ref->{$name} eq $value) {
  194       $checked = 'CHECKED'
  195     } else {
  196       $checked = '';
  197     }
  198 
  199     }
  200 
  201   MODES(
  202     TeX => qq!\\item{$tag}\n!,
  203     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  204     HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag!
  205   );
  206 
  207 }
  208 
  209 sub NAMED_ANS_RADIO_OPTION {
  210   my $name = shift;
  211   my $value = shift;
  212   my $tag =shift;
  213 
  214 
  215     my $checked = '';
  216     if ($value =~/^\%/) {
  217       $value =~ s/^\%//;
  218       $checked = 'CHECKED'
  219     }
  220   if (defined($main::inputs_ref->{$name}) ) {
  221     if ($main::inputs_ref->{$name} eq $value) {
  222       $checked = 'CHECKED'
  223     } else {
  224       $checked = '';
  225     }
  226 
  227     }
  228 
  229   MODES(
  230     TeX => qq!\\item{$tag}\n!,
  231     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  232     HTML => qq!<INPUT TYPE=RADIO NAME="$name" VALUE="$value" $checked>$tag!
  233   );
  234 
  235 }
  236 
  237 sub NAMED_ANS_RADIO_BUTTONS {
  238     my $name  =shift;
  239     my $value = shift;
  240     my $tag = shift;
  241 
  242 
  243   my @out = ();
  244   push(@out, NAMED_ANS_RADIO($name, $value,$tag));
  245   my @buttons = @_;
  246   while (@buttons) {
  247     $value = shift @buttons;  $tag = shift @buttons;
  248     push(@out, NAMED_ANS_RADIO_OPTION($name, $value,$tag));
  249   }
  250   (wantarray) ? @out : join(" ",@out);
  251 }
  252 sub ANS_RADIO {
  253   my $number = shift;
  254   my $value = shift;
  255   my $tag =shift;
  256     my $name = NEW_ANS_NAME($number);
  257   NAMED_ANS_RADIO($name,$value,$tag);
  258 }
  259 
  260 sub ANS_RADIO_OPTION {
  261   my $number = shift;
  262   my $value = shift;
  263   my $tag =shift;
  264 
  265 
  266     my $name = ANS_NUM_TO_NAME($number);
  267   NAMED_ANS_RADIO_OPTION($name,$value,$tag);
  268 }
  269 sub ANS_RADIO_BUTTONS {
  270     my $number  =shift;
  271     my $value = shift;
  272     my $tag = shift;
  273 
  274 
  275   my @out = ();
  276   push(@out, ANS_RADIO($number, $value,$tag));
  277   my @buttons = @_;
  278   while (@buttons) {
  279       $value = shift @buttons; $tag = shift @buttons;
  280     push(@out, ANS_RADIO_OPTION($number, $value,$tag));
  281   }
  282   (wantarray) ? @out : join(" ",@out);
  283 }
  284 
  285 sub NAMED_ANS_CHECKBOX {
  286   my $name = shift;
  287   my $value = shift;
  288     my $tag =shift;
  289     $name = RECORD_ANS_NAME($name);
  290 
  291     my $checked = '';
  292     if ($value =~/^\%/) {
  293       $value =~ s/^\%//;
  294       $checked = 'CHECKED'
  295     }
  296 
  297   if (defined($main::inputs_ref->{$name}) ) {
  298     if ($main::inputs_ref->{$name} eq $value) {
  299       $checked = 'CHECKED'
  300     }
  301     else {
  302       $checked = '';
  303     }
  304 
  305     }
  306 
  307   MODES(
  308     TeX => qq!\\item{$tag}\n!,
  309     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  310     HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag!
  311   );
  312 
  313 }
  314 
  315 sub NAMED_ANS_CHECKBOX_OPTION {
  316   my $name = shift;
  317   my $value = shift;
  318   my $tag =shift;
  319 
  320     my $checked = '';
  321     if ($value =~/^\%/) {
  322       $value =~ s/^\%//;
  323       $checked = 'CHECKED'
  324     }
  325 
  326   if (defined($main::inputs_ref->{$name}) ) {
  327     if ($main::inputs_ref->{$name} eq $value) {
  328       $checked = 'CHECKED'
  329     }
  330     else {
  331       $checked = '';
  332     }
  333 
  334     }
  335 
  336   MODES(
  337     TeX => qq!\\item{$tag}\n!,
  338     Latex2HTML => qq!\\begin{rawhtml}\n<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>\\end{rawhtml}$tag!,
  339     HTML => qq!<INPUT TYPE=CHECKBOX NAME="$name" VALUE="$value" $checked>$tag!
  340   );
  341 
  342 }
  343 
  344 sub NAMED_ANS_CHECKBOX_BUTTONS {
  345     my $name  =shift;
  346     my $value = shift;
  347     my $tag = shift;
  348 
  349   my @out = ();
  350   push(@out, NAMED_ANS_CHECKBOX($name, $value,$tag));
  351 
  352   my @buttons = @_;
  353   while (@buttons) {
  354     $value = shift @buttons;  $tag = shift @buttons;
  355     push(@out, NAMED_ANS_CHECKBOX_OPTION($name, $value,$tag));
  356   }
  357 
  358   (wantarray) ? @out : join(" ",@out);
  359 }
  360 
  361 sub ANS_CHECKBOX {
  362   my $number = shift;
  363   my $value = shift;
  364   my $tag =shift;
  365     my $name = NEW_ANS_NAME($number);
  366 
  367   NAMED_ANS_CHECKBOX($name,$value,$tag);
  368 }
  369 
  370 sub ANS_CHECKBOX_OPTION {
  371   my $number = shift;
  372   my $value = shift;
  373   my $tag =shift;
  374     my $name = ANS_NUM_TO_NAME($number);
  375 
  376   NAMED_ANS_CHECKBOX_OPTION($name,$value,$tag);
  377 }
  378 
  379 sub ANS_CHECKBOX_BUTTONS {
  380     my $number  =shift;
  381     my $value = shift;
  382     my $tag = shift;
  383 
  384   my @out = ();
  385   push(@out, ANS_CHECKBOX($number, $value, $tag));
  386 
  387   my @buttons = @_;
  388   while (@buttons) {
  389     $value = shift @buttons;  $tag = shift @buttons;
  390     push(@out, ANS_CHECKBOX_OPTION($number, $value,$tag));
  391   }
  392 
  393   (wantarray) ? @out : join(" ",@out);
  394 }
  395 
  396 sub ans_rule {
  397   my $len = shift;     # gives the optional length of the answer blank
  398   $len    = 20 unless $len ;
  399   my $name = NEW_ANS_NAME(++$main::ans_rule_count);
  400   NAMED_ANS_RULE($name ,$len);
  401 }
  402 sub ans_rule_extension {
  403   my $len = shift;
  404     $len    = 20 unless $len ;
  405   my $name = NEW_ANS_NAME($main::ans_rule_count);  # don't update the answer name
  406   NAMED_ANS_RULE($name ,$len);
  407 }
  408 sub ans_radio_buttons {
  409   my $name  = NEW_ANS_NAME(++$main::ans_rule_count);
  410   my @radio_buttons = NAMED_ANS_RADIO_BUTTONS($name, @_);
  411 
  412   if ($main::displayMode eq 'TeX') {
  413     $radio_buttons[0] = "\n\\begin{itemize}\n" . $radio_buttons[0];
  414     $radio_buttons[$#radio_buttons] .= "\n\\end{itemize}\n";
  415   }
  416 
  417   (wantarray) ? @radio_buttons: join(" ", @radio_buttons);
  418 }
  419 
  420 #added 6/14/2000 by David Etlinger
  421 sub ans_checkbox {
  422   my $name = NEW_ANS_NAME( ++$main::ans_rule_count );
  423   my @checkboxes = NAMED_ANS_CHECKBOX_BUTTONS( $name, @_ );
  424 
  425   if ($main::displayMode eq 'TeX') {
  426     $checkboxes[0] = "\n\\begin{itemize}\n" . $checkboxes[0];
  427     $checkboxes[$#checkboxes] .= "\n\\end{itemize}\n";
  428   }
  429 
  430   (wantarray) ? @checkboxes: join(" ", @checkboxes);
  431 }
  432 
  433 
  434 ## define a version of ans_rule which will work inside TeX math mode or display math mode -- at least for tth mode.
  435 ## This is great for displayed fractions.
  436 ## This will not work with latex2HTML mode since it creates gif equations.
  437 
  438 sub tex_ans_rule {
  439   my $len = shift;
  440   $len    = 20 unless $len ;
  441     my $name = NEW_ANS_NAME(++$main::ans_rule_count);
  442     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  443     my $out = MODES(
  444                      'TeX' => $answer_rule,
  445                      'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  446                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  447                      'HTML'     => $answer_rule
  448                    );
  449 
  450     $out;
  451 }
  452 sub tex_ans_rule_extension {
  453   my $len = shift;
  454   $len    = 20 unless $len ;
  455     my $name = NEW_ANS_NAME($main::ans_rule_count);
  456     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  457     my $out = MODES(
  458                      'TeX' => $answer_rule,
  459                      'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  460                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  461                      'HTML'     => $answer_rule
  462                    );
  463 
  464     $out;
  465 }
  466 # still needs some cleanup.
  467 sub NAMED_TEX_ANS_RULE {
  468     my $name = shift;
  469   my $len = shift;
  470   $len    = 20 unless $len ;
  471     my $answer_rule = NAMED_ANS_RULE($name ,$len);  # we don't want to create three answer rules in different modes.
  472     my $out = MODES(
  473                      'TeX' => $answer_rule,
  474                      'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}',
  475                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  476                      'HTML'     => $answer_rule
  477                    );
  478 
  479     $out;
  480 }
  481 sub NAMED_TEX_ANS_RULE_EXTENSION {
  482   my $name = shift;
  483   my $len = shift;
  484   $len    = 20 unless $len ;
  485     my $answer_rule = NAMED_ANS_RULE_EXTENSION($name ,$len);  # we don't want to create three answer rules in different modes.
  486     my $out = MODES(
  487                      'TeX' => $answer_rule,
  488                      'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}',
  489                      'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}',
  490                      'HTML'     => $answer_rule
  491                    );
  492 
  493     $out;
  494 }
  495 sub ans_box {
  496   my $row = shift;
  497   my $col =shift;
  498   $row = 5 unless $row;
  499   $col = 80 unless $col;
  500   my $name = NEW_ANS_NAME(++$main::ans_rule_count);
  501   NAMED_ANS_BOX($name ,$row,$col);
  502 }
  503 
  504 #this is legacy code; use ans_checkbox instead
  505 sub checkbox {
  506   my %options = @_;
  507   qq!<INPUT TYPE="checkbox" NAME="$options{'-name'}" VALUE="$options{'-value'}">$options{'-label'}!
  508 }
  509 
  510 
  511 sub NAMED_POP_UP_LIST {
  512     my $name = shift;
  513   my @list = @_;
  514   $name = RECORD_ANS_NAME($name);   # record answer name
  515     my $answer_value = '';
  516   $answer_value = ${$main::inputs_ref}{$name} if defined(${$main::inputs_ref}{$name});
  517   my $out = "";
  518   if ($main::displayMode eq "HTML" or $main::displayMode eq "HTML_tth") {
  519     $out = qq!<SELECT NAME = "$name" SIZE=1> \n!;
  520     my $i;
  521     foreach ($i=0; $i< @list; $i=$i+2) {
  522       my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : "";
  523       $out .= qq!<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1]  </OPTION>\n!;
  524     };
  525     $out .= " </SELECT>\n";
  526   } elsif ( $main::displayMode eq "Latex2HTML") {
  527     $out = qq! \\begin{rawhtml}<SELECT NAME = "$name" SIZE=1> \\end{rawhtml} \n !;
  528     my $i;
  529     foreach ($i=0; $i< @list; $i=$i+2) {
  530       my $select_flag = ($list[$i] eq $answer_value) ? "SELECTED" : "";
  531       $out .= qq!\\begin{rawhtml}<OPTION $select_flag VALUE ="$list[$i]" > $list[$i+1]  </OPTION>\\end{rawhtml}\n!;
  532     };
  533     $out .= " \\begin{rawhtml}</SELECT>\\end{rawhtml}\n";
  534   } elsif ( $main::displayMode eq "TeX") {
  535       $out .= "\\fbox{?}";
  536   }
  537 
  538 }
  539 
  540 sub pop_up_list {
  541   my @list = @_;
  542   my $name = NEW_ANS_NAME(++$main::ans_rule_count);  # get new answer name
  543   NAMED_POP_UP_LIST($name, @list);
  544 }
  545 
  546 # end answer blank macros
  547 
  548 =head2 Hints and solutions macros
  549 
  550   solution('text','text2',...);
  551   SOLUTION('text','text2',...);   # equivalent to TEXT(solution(...));
  552 
  553   hint('text', 'text2', ...);
  554   HINT('text', 'text2',...);      # equivalent to TEXT("$main::BR$main::HINT" . hint(@_) . "$main::BR") if hint(@_);
  555 
  556 Solution prints its concatenated input when the check box named 'ShowSol' is set and
  557 the time is after the answer date. (The variable C<$envir{'inputs_ref'}->>C<{'ShowSol'}> is set.)
  558 
  559 PG_FLAGS{'solutionExists'} is set to 1 when a solution is available for viewing.
  560 
  561 Hints are shown only after the number of attempts is greater than $main::showHint.
  562 ($main::showHint defaults to 1.)
  563 
  564 =cut
  565 
  566 
  567 
  568 #   solution prints its input when $displaySolutionsQ is set.
  569 #   use as TEXT(solution("blah, blah");
  570 #   \$solutionExists
  571 #   is passed to processProblem which displays a "show Solution" button
  572 #   when a solution is available for viewing
  573 
  574 
  575 sub solution {
  576   my @in = @_;
  577   my $out = '';
  578   $main::solutionExists =1;
  579   if ($envir{'inputs_ref'}->{'ShowSol'}) {
  580     my %inlist;
  581     grep($inlist{$_}++,@{ $envir{'PRINT_FILE_NAMES_FOR'} });
  582     if  ( defined($inlist{$main::studentLogin}) and ($inlist{$main::studentLogin} > 0) ) {
  583       $out = join(' ',@in);
  584     } elsif ($envir{'answerDate'} < time ){
  585       $out = join(' ',@in);
  586     }
  587   }
  588 
  589     $out;
  590 }
  591 
  592 
  593 
  594 sub SOLUTION {
  595   TEXT( solution(@_)) ;
  596 }
  597 
  598 
  599 
  600 sub hint {
  601     my @in = @_;
  602   my $out = '';
  603 
  604     $main::showHint = 1 unless defined($main::showHint);
  605     $main::numOfAttempts = 0 unless defined($main::numOfAttempts);
  606   if ($main::displayMode eq 'TeX')   {
  607     $out = '';  # do nothing since hints are not available for download
  608 
  609   } elsif ($main::numOfAttempts >= $main::showHint ) { #numOfAttempts is only defined in interactive mode
  610     if ( ${$main::inputs_ref}{'ShowHint'} ) {
  611         $out = join(' ',@in);     # show hint
  612     } elsif ($main::displayMode eq 'HTML' or $main::displayMode eq 'HTML_tth') {
  613       $out =  checkbox(-name=>'ShowHint',
  614               -value=>1,
  615               -label=>"Show Hint",
  616               -override => 1
  617               ) ;
  618     } elsif ($main::displayMode eq 'Latex2HTML')  {
  619       $out =  '\\begin{rawhtml}' . checkbox(-name=>'ShowHint',
  620                   -value=>1,
  621                   -label=>"Show Hint",
  622                   -override => 1
  623                   ) . '\\end{rawhtml}' ;
  624     } else {
  625       warn "Error: The subroutine hints doesn't know what to do in display mode $main::displayMode.";
  626       $out = '';
  627     }
  628   } else {
  629     # do nothing since the $numOf Attempts is not yet large enough.
  630     $out = '';
  631   }
  632   $out ;
  633 }
  634 
  635 
  636 
  637 sub HINT {
  638     TEXT("$main::BR$main::HINT" . hint(@_) . "$main::BR") if hint(@_);
  639 }
  640 
  641 
  642 
  643 # End hints and solutions macros
  644 #################################
  645 
  646 # Produces a random number between $begin and $end with increment 1.
  647 # You do not have to worry about integer or floating point types.
  648 
  649 =head2 Pseudo-random number generator
  650 
  651   Usage:
  652   random(0,5,.1)      # produces a random number between 0 and 5 in increments of .1
  653   non_zero_random(0,5,.1) # gives a non-zero random number
  654 
  655   SRAND(seed)     # resets the main random generator -- use very cautiously
  656 
  657 
  658 SRAND(time) will create a different problem everytime it is called.  This makes it difficult
  659 to check the answers :-).
  660 
  661 SRAND($envir{'inputs_ref'}->{'key'} ) will create a different problem for each login session.
  662 This is probably what is desired.
  663 
  664 =cut
  665 
  666 
  667 sub random  {
  668   my ($begin, $end, $incr) = @_;
  669   $main::PG_random_generator->random($begin,$end,$incr);
  670 }
  671 
  672 
  673 sub non_zero_random { ##gives a non-zero random number
  674   my (@arguments)=@_;
  675   my $a=0;
  676   my $i=100; #safety counter
  677   while ($a==0 && ( 0 < $i-- ) ) {
  678     $a=random(@arguments);
  679   }
  680   $a;
  681 }
  682 
  683 sub SRAND { # resets the main random generator -- use cautiously
  684     my $seed = shift;
  685   $main::PG_random_generator -> srand($seed);
  686 }
  687 
  688 # display macros
  689 
  690 =head2 Display Macros
  691 
  692 These macros produce different output depending on the display mode being used to show
  693 the problem on the screen, or whether the problem is being converted to TeX to produce
  694 a hard copy output.
  695 
  696   MODES   ( TeX =>        "Output this in TeX mode",
  697             HTML =>       "output this in HTML mode",
  698             HTML_tth =>   "output this in HTML_tth mode",
  699             Latex2HTML => "output this in Latex2HTML mode",
  700            )
  701 
  702   TEX     (tex_version, html_version) #obsolete
  703 
  704   M3      (tex_version, latex2html_version, html_version) #obsolete
  705 
  706 
  707 
  708 =cut
  709 
  710 
  711 sub TEX {
  712   my ($tex, $html ) = @_;
  713   MODES(TeX => $tex, HTML => $html, HTML_tth => $html);
  714 }
  715 
  716 
  717 sub M3 {
  718   my($tex,$l2h,$html) = @_;
  719   MODES(TeX => $tex, Latex2HTML => $l2h, HTML => $html, HTML_tth => $html);
  720 }
  721 
  722 # This replaces M3.  You can add new modes at will to this one.
  723 
  724 sub MODES {
  725   my %options = @_;
  726   return $options{$main::displayMode}
  727              if defined( $options{$main::displayMode} );
  728 
  729   # default searches.
  730   if ($main::displayMode eq "Latex2HTML") {
  731     return $options{TeX}
  732              if defined( $options{TeX} );
  733       return $options{HTML}
  734              if defined( $options{HTML} );
  735       die " ERROR in using MODES: 'HTML' and 'TeX' options not defined for 'Latex2HTML'";
  736   }
  737 
  738   if ($main::displayMode eq "HTML_tth") {
  739     return $options{HTML}
  740              if defined( $options{HTML} );
  741       die " ERROR in using MODES: 'HTML' option not defined for HTML_tth";
  742 
  743   }
  744 
  745   # trap undefined errors
  746   die "ERROR in defining MODES:  Can't find |$main::displayMode| among
  747            available options:" . join(" ", keys(%options) ) . "\n\n";
  748 
  749 }
  750 
  751 
  752 # end display macros
  753 
  754 
  755 =head2  Display constants
  756 
  757   @ALPHABET       ALPHABET()      capital letter alphabet -- ALPHABET[0] = 'A'
  758   $PAR        PAR()       paragraph character (\par or <p>)
  759   $BR             BR()        line break character
  760   $LQ         LQ()        left double quote
  761   $RQ         RQ()        right double quote
  762   $BM         BM()        begin math
  763   $EM         EM()        end math
  764   $BDM        BDM()       begin display math
  765   $EDM        EDM()       end display math
  766   $LTS        LTS()       strictly less than
  767   $GTS        GTS()       strictly greater than
  768   $LTE        LTE()       less than or equal
  769   $GTE        GTE()       greater than or equal
  770   $BEGIN_ONE_COLUMN BEGIN_ONE_COLUMN()  begin one-column mode
  771   $END_ONE_COLUMN   END_ONE_COLUMN()  end one-column mode
  772   $SOL        SOLUTION_HEADING()  solution headline
  773   $HINT       HINT_HEADING()    hint headline
  774   $US         US()        underscore character
  775   $SPACE        SPACE()       space character (tex and latex only)
  776   $BBOLD        BBOLD()       begin bold typeface
  777   $EBOLD        EBOLD()       end bold typeface
  778   $HR         HR()        horizontal rule
  779   $LBRACE       LBRACE()      left brace
  780   $LB         LB ()       left brace
  781   $RBRACE       RBRACE()      right brace
  782   $RB         RB ()       right brace
  783   $DOLLAR       DOLLAR()      a dollar sign
  784   $PERCENT      PERCENT()     a percent sign
  785   $CARET        CARET()       a caret sign
  786   $PI         PI()        the number pi
  787   $E          E()         the number e
  788 
  789 =cut
  790 
  791 
  792 sub _PGbasicmacros_init {
  793   $main::PAR        = PAR();
  794   $main::BR       = BR();
  795   $main::LQ       = LQ();
  796   $main::RQ       = RQ();
  797   $main::BM       = BM();
  798   $main::EM       = EM();
  799   $main::BDM        = BDM();
  800   $main::EDM        = EDM();
  801   $main::LTS        = LTS();
  802   $main::GTS        = GTS();
  803   $main::LTE        = LTE();
  804   $main::GTE        = GTE();
  805   $main::BEGIN_ONE_COLUMN = BEGIN_ONE_COLUMN();
  806   $main::END_ONE_COLUMN = END_ONE_COLUMN();
  807   $main::SOL        = SOLUTION_HEADING();
  808   $main::HINT       = HINT_HEADING();
  809   $main::US       = US();
  810   $main::SPACE      = SPACE();
  811   $main::BBOLD      = BBOLD();
  812   $main::EBOLD      = EBOLD();
  813   $main::HR       = HR();
  814   $main::LBRACE     = LBRACE();
  815   $main::RBRACE     = RBRACE();
  816   $main::LB       = LB();
  817   $main::RB       = RB();
  818   $main::DOLLAR     = DOLLAR();
  819   $main::PERCENT      = PERCENT();
  820   $main::CARET      = CARET();
  821   $main::PI       = PI();
  822   $main::E        = E();
  823   @main::ALPHABET     = ('A'..'ZZ');
  824 }
  825 
  826 # A utility variable.  Notice that "B"=$ALPHABET[1] and
  827 # "ABCD"=@ALPHABET[0..3].
  828 
  829 sub ALPHABET  {
  830   ('A'..'ZZ')[@_];
  831 }
  832 
  833 ###############################################################
  834 # Some constants which are different in tex and in HTML
  835 # The order of arguments is TeX, Latex2HTML, HTML
  836 sub PAR { MODES( TeX => '\\par ',Latex2HTML => '\\par ',HTML => '<P>' ); };
  837 sub BR { MODES( TeX => '\\par\\noindent ',Latex2HTML => '\\par\\noindent ',HTML => '<BR>'); };
  838 sub LQ { MODES( TeX => "``", Latex2HTML =>   '"',  HTML =>  '&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                   # appletLink(q!  archive="http: //webwork.math.rochester.edu/gage/xFunctions/xFunctions.zip"
 1144                                   code="xFunctionsLauncher.class"  width=100 height=14!,
 1145                   " parameter text goes here")
 1146                   # will link to xFunctions.
 1147 
 1148   low level:
 1149 
 1150   spf($number, $format)   # prints the number with the given format
 1151   sspf($number, $format)  # prints the number with the given format, always including a sign.
 1152   protect_underbar($string) # protects the underbar (class_name) in strings which may have to pass through TeX.
 1153 
 1154 =cut
 1155 
 1156 sub beginproblem {
 1157   my $out = "";
 1158     my $TeXFileName = protect_underbar($main::fileName);
 1159     my $l2hFileName = protect_underbar($main::fileName);
 1160   my %inlist;
 1161   my $points ='pts';
 1162   $points = 'pt' if $main::problemValue == 1;
 1163   ##    Prepare header for the problem
 1164   grep($inlist{$_}++,@{ $envir{'PRINT_FILE_NAMES_FOR'} });
 1165   if ( defined($inlist{$main::studentLogin}) and ($inlist{$main::studentLogin} > 0) ) {
 1166     $out = &M3("\n\n\\medskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.{\\footnotesize ($main::problemValue $points) $TeXFileName}}\\newline ",
 1167     " \\begin{rawhtml} ($main::problemValue $points) <B>$l2hFileName</B><BR>\\end{rawhtml}",
 1168      "($main::problemValue $points) <B>$main::fileName</B><BR>"
 1169        );
 1170   } else {
 1171     $out = &M3("\n\n\\smallskip\\hrule\\smallskip\\par{\\bf ${main::probNum}.}($main::problemValue $points) ",
 1172     "($main::problemValue $points) ",
 1173      "($main::problemValue $points) "
 1174        );
 1175   }
 1176   $out;
 1177 
 1178 }
 1179 
 1180 # kludge to clean up path names
 1181             ## allow underscore character in set and section names and also allows line breaks at /
 1182 sub protect_underbar {
 1183     my $in = shift;
 1184     if ($main::displayMode eq 'TeX')  {
 1185 
 1186         $in =~ s|_|\\\_|g;
 1187         $in =~ s|/|\\\-/|g;  # allows an optional hyphenation of the path (in tex)
 1188     }
 1189     $in;
 1190 }
 1191 
 1192 
 1193 # An example of a macro which prints out a list (with letters)
 1194 sub OL {
 1195   my(@array) = @_;
 1196   my $i = 0;
 1197   my  $out=   &M3(
 1198           "\\begin{enumerate}\n",
 1199           " \\begin{rawhtml} <OL TYPE=\"A\" VALUE=\"1\"> \\end{rawhtml} ",
 1200           "<OL TYPE=\"A\" VALUE=\"1\">\n"
 1201           ) ;
 1202   my $elem;
 1203   foreach $elem (@array) {
 1204     $out .= &M3(
 1205           "\\item[$main::ALPHABET[$i].] $elem\n",
 1206           " \\begin{rawhtml} <LI> \\end{rawhtml} $elem  ",
 1207           "<LI> $elem\n"
 1208           ) ;
 1209     $i++;
 1210   }
 1211   $out .= &M3(
 1212         "\\end{enumerate}\n",
 1213         " \\begin{rawhtml} </OL>\n \\end{rawhtml} ",
 1214         "</OL>\n"
 1215         ) ;
 1216 }
 1217 
 1218 sub htmlLink {
 1219   my $url = shift;
 1220   my $text = shift;
 1221   my $options = shift;
 1222   $options = "" unless defined($options);
 1223   M3( "{\\bf \\underline{$text}  }",
 1224       "\\begin{rawhtml} <A HREF=\"$url\" $options> $text </A>\\end{rawhtml}",
 1225       "<A HREF=\"$url\" $options> $text </A>"
 1226       );
 1227 }
 1228 sub appletLink {
 1229   my $url = shift;
 1230   my $options = shift;
 1231   $options = "" unless defined($options);
 1232   M3( "{\\bf \\underline{APPLET}  }",
 1233       "\\begin{rawhtml} <APPLET $url> $options </APPLET>\\end{rawhtml}",
 1234       "<APPLET $url> $options </APPLET>"
 1235       );
 1236 }
 1237 sub spf {
 1238   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1239   $format = "%4.3g" unless $format;   # default value for format
 1240   sprintf($format, $number);
 1241   }
 1242 sub sspf {
 1243   my($number,$format) = @_;  # attention, the order of format and number are reversed
 1244   $format = "%4.3g" unless $format;   # default value for format
 1245   my $sign = $number>=0 ? " + " : " - ";
 1246   $number = $number>=0 ? $number : -$number;
 1247   $sign .sprintf($format, $number);
 1248   }
 1249 
 1250 =head2  Sorting and other list macros
 1251 
 1252 
 1253 
 1254   Usage:
 1255   lex_sort(@list);   # outputs list in lexigraphic (alphabetical) order
 1256   num_sort(@list);   # outputs list in numerical order
 1257   uniq( @list);      # outputs a list with no duplicates.  Order is unspecified.
 1258 
 1259   PGsort( \&sort_subroutine, @list);
 1260   # &sort_subroutine defines order. It's output must be -1,0 or 1.
 1261 
 1262 =cut
 1263 
 1264 #  uniq gives unique elements of a list:
 1265  sub uniq {
 1266    my (@in) =@_;
 1267    my %temp = ();
 1268    while (@in) {
 1269           $temp{shift(@in)}++;
 1270       }
 1271    my @out =  keys %temp;  # sort is causing trouble with Safe.??
 1272    @out;
 1273 }
 1274 
 1275 sub lex_sort {
 1276   PGsort sub {$_[0] cmp $_[1]}, @_;
 1277 }
 1278 sub num_sort {
 1279   PGsort sub {$_[0] <=> $_[1]}, @_;
 1280 }
 1281 
 1282 
 1283 =head2 Macros for handling tables
 1284 
 1285   Usage:
 1286   begintable( number_of_columns_in_table)
 1287   row(@dataelements)
 1288   endtable()
 1289 
 1290 Example of useage:
 1291 
 1292   BEGIN_TEXT
 1293     This problem tests calculating new functions from old ones:$BR
 1294     From the table below calculate the quantities asked for:$BR
 1295     \{begintable(scalar(@firstrow)+1)\}
 1296     \{row(" \(x\) ",@firstrow)\}
 1297     \{row(" \(f(x)\) ", @secondrow)\}
 1298     \{row(" \(g(x)\) ", @thirdrow)\}
 1299     \{row(" \(f'(x)\) ", @fourthrow)\}
 1300     \{row(" \(g'(x)\) ", @fifthrow)\}
 1301     \{endtable()\}
 1302 
 1303    (The arrays contain numbers which are placed in the table.)
 1304 
 1305   END_TEXT
 1306 
 1307 =cut
 1308 
 1309 sub begintable {
 1310   my ($number)=shift;   #number of columns in table
 1311   my %options = @_;
 1312   warn "begintable(cols) requires a number indicating the number of columns" unless defined($number);
 1313   my $out = "";
 1314   if ($main::displayMode eq 'TeX') {
 1315     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 1316     }
 1317   elsif ($main::displayMode eq 'Latex2HTML') {
 1318     $out .= "\n\\begin{rawhtml} <TABLE , BORDER=1>\n\\end{rawhtml}";
 1319     }
 1320   elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1321     $out .= "<TABLE BORDER=1>\n"
 1322   }
 1323   else {
 1324     $out = "Error: PGchoicemacros: begintable: Unknown displayMode: $main::displayMode.\n";
 1325     }
 1326   $out;
 1327   }
 1328 
 1329 sub endtable {
 1330   my $out = "";
 1331   if ($main::displayMode eq 'TeX') {
 1332     $out .= "\n\\end {tabular}\\end{center}\\par\\smallskip\n";
 1333     }
 1334   elsif ($main::displayMode eq 'Latex2HTML') {
 1335     $out .= "\n\\begin{rawhtml} </TABLE >\n\\end{rawhtml}";
 1336     }
 1337   elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1338     $out .= "</TABLE>\n";
 1339     }
 1340   else {
 1341     $out = "Error: PGchoicemacros: endtable: Unknown displayMode: $main::displayMode.\n";
 1342     }
 1343   $out;
 1344   }
 1345 
 1346 
 1347 sub row {
 1348   my @elements = @_;
 1349   my $out = "";
 1350   if ($main::displayMode eq 'TeX') {
 1351     while (@elements) {
 1352       $out .= shift(@elements) . " &";
 1353       }
 1354      chop($out); # remove last &
 1355      $out .= "\\\\ \\hline \n";
 1356      # carriage returns must be added manually for tex
 1357     }
 1358   elsif ($main::displayMode eq 'Latex2HTML') {
 1359     $out .= "\n\\begin{rawhtml}\n<TR>\n\\end{rawhtml}\n";
 1360     while (@elements) {
 1361       $out .= " \n\\begin{rawhtml}\n<TD> \n\\end{rawhtml}\n" . shift(@elements) . " \n\\begin{rawhtml}\n</TD> \n\\end{rawhtml}\n";
 1362       }
 1363     $out .= " \n\\begin{rawhtml}\n</TR> \n\\end{rawhtml}\n";
 1364   }
 1365   elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1366     $out .= "<TR>\n";
 1367     while (@elements) {
 1368       $out .= "<TD>" . shift(@elements) . "</TD>";
 1369       }
 1370     $out .= "\n</TR>\n";
 1371   }
 1372   else {
 1373     $out = "Error: PGchoicemacros: row: Unknown displayMode: $main::displayMode.\n";
 1374     }
 1375   $out;
 1376 }
 1377 
 1378 =head2 Macros for displaying static images
 1379 
 1380   Usage:
 1381   $string = image($image, width => 100, height => 100, tex_size => 800)
 1382   $string = image([$image1, $image2], width => 100, height => 100, tex_size => 800)
 1383   $string = caption($string);
 1384   $string = imageRow([$image1, $image2 ], [$caption1, $caption2]);
 1385            # produces a complete table with rows of pictures.
 1386 
 1387 
 1388 =cut
 1389 
 1390 #   More advanced macros
 1391 sub image {
 1392   my $image_ref  = shift;
 1393     my @opt = @_;
 1394     unless (scalar(@opt) % 2 == 0 ) {
 1395       warn "ERROR in image macro.  A list of macros must be inclosed in square brackets.";
 1396     }
 1397     my %in_options = @opt;
 1398     my %known_options = ( width   => 100,
 1399                           height  => 100,
 1400                           tex_size  => 800
 1401                          );
 1402 #   # handle options
 1403     my %out_options = %known_options;
 1404   foreach my $opt_name (keys %in_options) {
 1405     if ( exists( $known_options{$opt_name} ) ) {
 1406       $out_options{$opt_name} = $in_options{$opt_name} if exists( $in_options{$opt_name} ) ;
 1407     } else {
 1408       die "Option $opt_name not defined for image. " .
 1409       "Default options are:<BR> ", display_options2(%known_options);
 1410 
 1411     }
 1412   }
 1413   my $width     = $out_options{width};
 1414   my $height    = $out_options{height};
 1415   my $tex_size  = $out_options{tex_size};
 1416   my $width_ratio = $tex_size*(.001);
 1417   my @image_list = ();
 1418 
 1419   if (ref($image_ref) =~ /ARRAY/ ) {
 1420     @image_list = @{$image_ref};
 1421   } else {
 1422     push(@image_list,$image_ref);
 1423   }
 1424   my @output_list = ();
 1425     while(@image_list) {
 1426 
 1427     my $imageURL = alias(shift @image_list);
 1428     my $out="";
 1429 
 1430 
 1431     if ($main::displayMode eq 'TeX') {
 1432       $out = qq!\\includegraphics[width=$width_ratio\\linewidth]{$imageURL}\n !
 1433       }
 1434     elsif ($main::displayMode eq 'Latex2HTML') {
 1435       $out = qq!\\begin{rawhtml}\n<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>\n
 1436       \\end{rawhtml}\n !
 1437       }
 1438     elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth') {
 1439       $out = qq!<A HREF= "$imageURL" TARGET="ZOOM"><IMG SRC="$imageURL"  WIDTH="$width" HEIGHT="$height"></A>
 1440       !
 1441       }
 1442     else {
 1443       $out = "Error: PGchoicemacros: image: Unknown displayMode: $main::displayMode.\n";
 1444       }
 1445     push(@output_list, $out);
 1446   }
 1447   wantarray ? @output_list : $output_list[0] ;
 1448 }
 1449 
 1450 # This is legacy code.
 1451 sub images {
 1452   my @in = @_;
 1453   my @outlist = ();
 1454   while (@in) {
 1455      push(@outlist,&image( shift(@in) ) );
 1456    }
 1457   @outlist;
 1458 }
 1459 
 1460 
 1461 sub caption {
 1462   my ($out) = @_;
 1463   $out = " $out \n" if $main::displayMode eq 'TeX';
 1464   $out = " $out  " if $main::displayMode eq 'HTML';
 1465   $out = " $out  " if $main::displayMode eq 'HTML_tth';
 1466   $out = " $out  " if $main::displayMode eq 'Latex2HTML';
 1467     $out;
 1468 }
 1469 
 1470 sub captions {
 1471   my @in = @_;
 1472   my @outlist = ();
 1473   while (@in) {
 1474      push(@outlist,&caption( shift(@in) ) );
 1475   }
 1476   @outlist;
 1477 }
 1478 
 1479 sub imageRow {
 1480 
 1481   my $pImages = shift;
 1482   my $pCaptions=shift;
 1483   my $out = "";
 1484   my @images = @$pImages;
 1485   my @captions = @$pCaptions;
 1486   my $number = @images;
 1487   # standard options
 1488   my %options = ( 'tex_size' => 200,  # width for fitting 4 across
 1489                   'height' => 100,
 1490                   'width' => 100,
 1491                   @_            # overwrite any default options
 1492                 );
 1493 
 1494   if ($main::displayMode eq 'TeX') {
 1495     $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{"  .  "|c" x $number .  "|} \\hline\n";
 1496     while (@images) {
 1497       $out .= &image( shift(@images),%options ) . '&';
 1498     }
 1499     chop($out);
 1500     $out .= "\\\\ \\hline \n";
 1501     while (@captions) {
 1502       $out .= &caption( shift(@captions) ) . '&';
 1503     }
 1504     chop($out);
 1505     $out .= "\\\\ \\hline \n\\end {tabular}\\end{center}\\par\\smallskip\n";
 1506   } elsif ($main::displayMode eq 'Latex2HTML'){
 1507 
 1508     $out .= "\n\\begin{rawhtml} <TABLE  BORDER=1><TR>\n\\end{rawhtml}\n";
 1509     while (@images) {
 1510       $out .= "\n\\begin{rawhtml} <TD>\n\\end{rawhtml}\n" . &image( shift(@images),%options )
 1511               . "\n\\begin{rawhtml} </TD>\n\\end{rawhtml}\n" ;
 1512     }
 1513 
 1514     $out .= "\n\\begin{rawhtml}</TR><TR>\\end{rawhtml}\n";
 1515     while (@captions) {
 1516       $out .= "\n\\begin{rawhtml} <TH>\n\\end{rawhtml}\n".&caption( shift(@captions) )
 1517               . "\n\\begin{rawhtml} </TH>\n\\end{rawhtml}\n" ;
 1518     }
 1519 
 1520     $out .= "\n\\begin{rawhtml} </TR> </TABLE >\n\\end{rawhtml}";
 1521   } elsif ($main::displayMode eq 'HTML' || $main::displayMode eq 'HTML_tth'){
 1522     $out .= "<P>\n <TABLE BORDER=2 CELLPADDING=3 CELLSPACING=2 ><TR ALIGN=CENTER    VALIGN=MIDDLE>\n";
 1523     while (@images) {
 1524       $out .= " \n<TD>". &image( shift(@images),%options ) ."</TD>";
 1525     }
 1526     $out .= "</TR>\n<TR>";
 1527     while (@captions) {
 1528       $out .= " <TH>". &caption( shift(@captions) ) ."</TH>";
 1529     }
 1530     $out .= "\n</TR></TABLE></P>\n"
 1531   }
 1532   else {
 1533     $out = "Error: PGchoicemacros: imageRow: Unknown languageMode: $main::displayMode.\n";
 1534     warn $out;
 1535   }
 1536   $out;
 1537 }
 1538 
 1539 
 1540 ###########
 1541 # Auxiliary macros
 1542 
 1543 sub display_options2{
 1544   my %options = @_;
 1545   my $out_string = "";
 1546   foreach my $key (keys %options) {
 1547     $out_string .= " $key => $options{$key},<BR>";
 1548   }
 1549   $out_string;
 1550 }
 1551 
 1552 
 1553 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9