[system] / trunk / pg / macros / PGbasicmacros.pl Repository:
ViewVC logotype

View of /trunk/pg/macros/PGbasicmacros.pl

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1758 - (download) (as text) (annotate)
Mon Jan 26 16:36:29 2004 UTC (15 years, 10 months ago) by jj
File size: 62406 byte(s)
If LaTeX formula ends in a backslash after removing training spaces,
then we removed too much space, so we add a space to the end of it so
it ends '\ \)'.

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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9