[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 37 - (download) (as text) (annotate)
Thu Jun 21 01:03:14 2001 UTC (11 years, 11 months ago) by apizer
File size: 50003 byte(s)
Hints and Solutions now use basically the same mechanism.  Fixed hugs in hints
and minor bugs in solutions.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9