[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 36 - (download) (as text) (annotate)
Wed Jun 20 20:01:26 2001 UTC (11 years, 11 months ago) by gage
File size: 50486 byte(s)
Simplified some of the scripts.  Using forking there is no need
to evaluate $main:: everytime, since it remains the same for both
the parent (where the script is compiled) and in the child where
the script is executed.

There were other minor fixes to work around bugs in 5.6.0 which were
fixed in 5.6.1

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9