From: Sam H. v. a. <we...@ma...> - 2007-09-28 00:41:00
|
Log Message: ----------- added MathObjects examples from webwork2/doc/parser Added Files: ----------- pg/doc/MathObjects/extensions: 1-function.pg 2-function.pg 3-operator.pg 4-list.pg 5-operator.pg 6-precedence.pg 7-context.pg 8-answer.pg pg/doc/MathObjects/macros: Differentiation.pl DifferentiationDefs.pl parserTables.pl parserUtils.pl unionImage.pl unionTables.pl pg/doc/MathObjects/problems: sample01.pg sample02.pg sample03.pg sample04.pg sample05.pg sample06.pg sample07.pg sample08.pg sample09.pg sample10.pg sample11.pg sample12.pg sample13.pg sample14.pg sample15.pg sample16.pg sample17.pg sample18.pg sample19.pg sample20.pg sample21.pg sample22.pg Revision Data ------------- --- /dev/null +++ doc/MathObjects/extensions/2-function.pg @@ -0,0 +1,83 @@ +########################################################## +# +# Example showing how to add a new two-variable function to the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# Use standard numeric mode +# +Context('Numeric'); + +############################################# +# +# Create a "Combinations" function +# + +package MyFunction2; +our @ISA = qw(Parser::Function::numeric2); # this is what makes it R^2 -> R + +sub C { + shift; my ($n,$r) = @_; my $C = 1; + $r = $n-$r if ($r > $n-$r); # find the smaller of the two + for (1..$r) {$C = $C*($n-$_+1)/$_} + return $C +} + +package main; + +# +# Make it work on formulas as well as numbers +# +sub C {Parser::Function->call('C',@_)} + +# +# Add the new functions into the Parser +# + +Context()->functions->add(C => {class => 'MyFunction2'}); + +$x = Formula('x'); + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new function to the Parser: ${BTT}C(n,r)${ETT}. +(Edit the code to see how this is done). +$PAR +Assuming that ${BTT}${DOLLAR}x = Formula('x')${ETT}, it can be used as follows: +$PAR + +\{ParserTable( + 'Formula("C(x,3)")', + 'C(6,2)', + 'C($x,3)', + 'Formula("C(x,3)")->eval(x=>6)', + '(C($x,2))->eval(x=>6)', + 'Formula("C(x)")', + 'Formula("C(1,2,3)")', + 'C(1)', + 'C(1,2,3)', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/extensions/4-list.pg @@ -0,0 +1,106 @@ +########################################################## +# +# Example showing how to add a new list-type object +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Define our own [n,r] notation for n choose r +# + +package MyChoose; +our @ISA = qw(Parser::List); # subclass of List + +# +# Check that two numbers are given +# +sub _check { + my $self = shift; + $self->{type}{list} = 0; # our result is a single number, not really a list + $self->Error("You need two numbers within '[' and ']'") + if ($self->{type}{length} < 2); + $self->Error("Only two numbers can appear within '[' and ']'") + if ($self->{type}{length} > 2); + my ($n,$r) = @{$self->{coords}}; + $self->Error("The arguments for '[n,r]' must be numbers") + unless ($n->type eq 'Number' && $r->type eq 'Number'); + $self->{type} = $Value::Type{number}; +} + +# +# Compute n choose r +# +sub _eval { + shift; my ($n,$r) = @_; my $C = 1; + $r = $n-$r if ($r > $n-$r); # find the smaller of the two + for (1..$r) {$C = $C*($n-$_+1)/$_} + return $C +} + +# +# Non-standard TeX output +# +sub TeX { + my $self = shift; + return '{'.$self->{coords}[0]->TeX.' \choose '.$self->{coords}[1]->TeX.'}'; +} + +# +# Non-standard perl output +# +sub perl { + my $self = shift; + return '(MyChoose->_eval('.$self->{coords}[0]->perl.','.$self->{coords}[1]->perl.'))'; +} + + +package main; + +########################################################## +# +# Add the new list to the context +# + +Context()->lists->add(Choose => {class => 'MyChoose'}); +Context()->parens->replace('[' => {close => ']', type => 'Choose'}); + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new list to the Parser: ${BTT}[n,r]${ETT}, +which returns \(n\choose r\). +$PAR + +\{ParserTable( + 'Formula("[x,3]")', + 'Formula("[5,3]")', + 'Formula("[x,3]")->eval(x=>5)', + '$C = Formula("[x,y]"); $C->substitute(x=>5)', + 'Formula("[x,y]")->perlFunction("C"); C(5,3)', + 'Formula("[x,y,3]")', + 'Formula("[x]")', + 'Formula("[x,[y,2]]")', + 'Formula("[x,<1,2>]")', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/extensions/7-context.pg @@ -0,0 +1,86 @@ +########################################################## +# +# Example showing how to switch different contexts +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we compare formulas in complex and vector contexts. +Note the difference between how ${BTT}i${ETT} is treated in the two +contexts. Note that 'Number' comprises both real and complex numbers. +$PAR + +Assuming that ${BTT}${DOLLAR}x = Formula('x')${ETT}, it can be used as follows: +$PAR + +END_TEXT + +$x = Formula('x'); + +########################################################## +# +# Use Complex context +# + +Context('Complex'); + +BEGIN_TEXT +\{Title("The Complex context:")\} +$PAR +\{ParserTable( + 'i', + 'Formula("1+3i")', + 'Formula("x+3i")', + '1 + 3*i', + '$x + 3*i', + '$z = tan(2*i)', + 'Formula("sinh(zi)")', + 'Formula("3i+4j-k")', + 'Formula("3i+4j-k")->eval', + '3*i + 4*j - k', +)\} +$PAR$BR +END_TEXT + + +########################################################## +# +# Use Vector context +# + +Context('Vector'); + +BEGIN_TEXT +\{Title("The Vector context:")\} +$PAR +\{ParserTable( + 'i', + 'Formula("1+3i")', + 'Formula("x+3i")', + '1 + 3*i', + '$x + 3*i', + '$z = tan(2*i)', + 'Formula("sinh(zi)")', + 'Formula("3i+4j-k")', + 'Formula("3i+4j-k")->eval', + '3*i + 4*j - k', +)\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/extensions/5-operator.pg @@ -0,0 +1,89 @@ +########################################################## +# +# Example of how to implement equalities in the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Define our own operator for equality +# + +package Equality; +our @ISA = qw(Parser::BOP); # subclass of Binary OPerator + +# +# Check that the operand types are numbers. +# +sub _check { + my $self = shift; my $name = $self->{bop}; + $self->Error("Only one equality is allowed in an equation") + if ($self->{lop}->class eq 'Equality' || $self->{rop}->class eq 'Equality') ; + $self->Error("Operands of '$name' must be Numbers") unless $self->checkNumbers(); + $self->{type} = Value::Type('Equality',1); # Make it not a number, to get errors with other operations. +} + +# +# Determine if the two sides are equal +# +sub _eval {return ($_[1] == $_[2])? 1: 0} + +package main; + +# +# Add the operator into the current context +# + +$prec = Context()->operators->get(',')->{precedence} + .25; + +Context()->operators->add( + '=' => { + class => 'Equality', + precedence => $prec, # just above comma + associativity => 'left', # computed left to right + type => 'bin', # binary operator + string => '=', # output string for it + perl => '==', # perl string + } +); + + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new operator to the Parser: ${BTT} a += b${ETT}, for equality. +$PAR + +\{ParserTable( + 'Formula("x + y = 0")', + 'Formula("x + y = 0")->{tree}->class', + 'Formula("x + y = 0")->{tree}{lop}', + 'Formula("x + y = 0")->{tree}{rop}', + 'Formula("x + y = 0")->eval(x=>2,y=>3)', + 'Formula("x + y = 0")->eval(x=>2,y=>-2)', + 'Formula("x + y = 0 = z")', + 'Formula("(x + y = 0) + 5")', + 'Formula("x + y = 0, 3x-y = 4")', # you CAN get a list of equalities + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/extensions/8-answer.pg @@ -0,0 +1,112 @@ +########################################################## +# +# Example showing an answer checker that uses the parser +# to evaluate the student (and professor's) answers. +# +# This is now obsolete, as the paser's ->cmp method +# can be used to produce an answer checker for any +# of the parser types. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Use Vector context +# + +Context('Vector'); + +########################################################## +# +# Make the answer checker +# +sub vector_cmp { + my $v = shift; + die "vector_cmp requires a vector argument" unless defined $v; + my $v = Vector($v); # covert to vector if it isn't already + my $ans = new AnswerEvaluator; + $ans->ans_hash(type => "vector",correct_ans => $v->string, vector=>$v); + $ans->install_evaluator(~~&vector_cmp_check); + return $ans; +} + +sub vector_cmp_check { + my $ans = shift; my $v = $ans->{vector}, + $ans->score(0); # assume failure + my $f = Parser::Formula($ans->{student_ans}); + my $V = Parser::Evaluate($f); + if (defined $V) { + $V = Formula($V) unless Value::isValue($V); # make sure we can call Value methods + $ans->{preview_latex_string} = $f->TeX; + $ans->{preview_text_string} = $f->string; + $ans->{student_ans} = $V->string; + if ($V->type eq 'Vector') { + $ans->score(1) if ($V == $v); # Let the overloaded == do the check + } else { + $ans->{ans_message} = $ans->{error_message} = + "Your answer doesn't seem to be a Vector (it looks like ".Value::showClass($V).")" + unless $inputs_ref->{previewAnswers}; + } + } else { + # + # Student answer evaluation failed. + # Report the error, with formatting, if possible. + # + my $context = Context(); + my $message = $context->{error}{message}; + if ($context->{error}{pos}) { + my $string = $context->{error}{string}; + my ($s,$e) = @{$context->{error}{pos}}; + $message =~ s/; see.*//; # remove the position from the message + $ans->{student_ans} = protectHTML(substr($string,0,$s)) . + '<SPAN CLASS="parsehilight">' . + protectHTML(substr($string,$s,$e-$s)) . + '</SPAN>' . + protectHTML(substr($string,$e)); + } + $ans->{ans_message} = $ans->{error_message} = $message; + } + return $ans; +} + +########################################################## +# +# The problem text +# + +$V = Vector(1,2,3); + +Context()->flags->set(ijk=>0); +Context()->constants->add(a=>1,b=>1,c=>1); + +$ABC = Formula("<a,b,c>"); + +BEGIN_TEXT +Enter the vector \(\{$V->TeX\}\) in any way you like: \{ans_rule(20)\}. +$PAR +You can use either \(\{$ABC->TeX\}\) or \(\{$ABC->ijk\}\) notation,$BR +and can perform vector operations to produce your answer. +$PAR +${BBOLD}Note:${EBOLD} This problem is obsolete. +END_TEXT + +########################################################### +# +# The answer +# + +ANS(vector_cmp($V)); + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/extensions/6-precedence.pg @@ -0,0 +1,84 @@ +########################################################## +# +# Example of the non-standard precedences as a possible alternative +# that makes it possible to write "sin 2x" and get "sin(2x)" +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Use standard precedences for multiplication +# + +Context()->usePrecedence("Standard"); + +$standard = ParserTable( + 'Formula("sin 2xy/3")', + 'Formula("sin 2x y/3")', + 'Formula("sin 2x y / 3")', + 'Formula("sin 2x+5")', + 'Formula("sin x(x+1)")', + 'Formula("sin x (x+1)")', + 'Formula("1/2xy")', + 'Formula("1/2 xy")', + 'Formula("1/2x y")', + 'Formula("sin^2 x")', + 'Formula("sin^(-1) x")', + 'Formula("x^2x")', +); + +Context()->usePrecedence("Non-Standard"); + +$nonstandard = ParserTable( + 'Formula("sin 2xy/3")', + 'Formula("sin 2x y/3")', + 'Formula("sin 2x y / 3")', + 'Formula("sin 2x+5")', + 'Formula("sin x(x+1)")', + 'Formula("sin x (x+1)")', + 'Formula("1/2xy")', + 'Formula("1/2 xy")', + 'Formula("1/2x y")', + 'Formula("sin^2 x")', + 'Formula("sin^(-1) x")', + 'Formula("x^2x")', +); + + + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we compare the standard and non-standard precedences for +multiplication. +$PAR + +\{Title("The Non-Standard precedences:")\} +$PAR +$nonstandard +$PAR$BR + +\{Title("The Standard precedences:")\} +$PAR +$standard + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/extensions/3-operator.pg @@ -0,0 +1,113 @@ +########################################################## +# +# Example showing how to add new operators to the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# Define our own binary operator +# + +package MyOperator; +our @ISA = qw(Parser::BOP); # subclass of Binary OPerator + +# +# Check that the operand types are numbers. +# +sub _check { + my $self = shift; my $name = $self->{bop}; + return if $self->checkNumbers(); + $self->Error("Operands of '$name' must be Numbers"); +} + +# +# Compute the value of n choose r. +# +sub _eval { + shift; my ($n,$r) = @_; my $C = 1; + $r = $n-$r if ($r > $n-$r); # find the smaller of the two + for (1..$r) {$C = $C*($n-$_+1)/$_} + return $C +} + +# +# Non-standard TeX output +# +sub TeX { + my $self = shift; + return '{'.$self->{lop}->TeX.' \choose '.$self->{rop}->TeX.'}'; +} + +# +# Non-standard perl output +# +sub perl { + my $self = shift; + return '(MyOperator->_eval('.$self->{lop}->perl.','.$self->{rop}->perl.'))'; +} + +package main; + +########################################################## +# +# Add the operator into the current context +# + +$prec = Context()->operators->get('+')->{precedence} - .25; + +Context()->operators->add( + '#' => { + class => 'MyOperator', + precedence => $prec, # just below addition + associativity => 'left', # computed left to right + type => 'bin', # binary operator + string => ' # ', # output string for it + TeX => '\mathop{\#}', # TeX version (overridden above, but just an example) + } +); + + +$CHOOSE = MODES(TeX => '\#', HTML => '#'); + + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new operator to the Parser: ${BTT}n $CHOOSE r${ETT}, +which returns \(n\choose r\). +$PAR + +\{ParserTable( + 'Formula("x # y")', + 'Formula("x+1 # 5")', + 'Formula("x # 5")->eval(x=>7)', + 'Formula("(x#5)+(x#4)")', + 'Formula("x#5+x#4")', + 'Formula("x # y")', + 'Formula("x # y")->substitute(x=>5)', + 'Formula("x # y")->eval(x=>5,y=>2)', + 'Formula("x # y")->perlFunction(~~'C~~'); C(5,2)', + 'Formula("1 # <x,3>")', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/extensions/1-function.pg @@ -0,0 +1,83 @@ +########################################################## +# +# Example showing how to add a new single-variable function to the Parser +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserTables.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# Use standard numeric mode +# +Context('Numeric'); + +############################################# +# +# Create a 'log2' function to the Parser for log base 2 +# + +package MyFunction1; +our @ISA = qw(Parser::Function::numeric); # this is what makes it R -> R + +sub log2 { + shift; my $x = shift; + return CORE::log($x)/CORE::log(2); +} + +package main; + +# +# Make it work on formulas as well as numbers +# +sub log2 {Parser::Function->call('log2',@_)} + +# +# Add the new functions into the Parser +# + +Context()->functions->add( + log2 => {class => 'MyFunction1', TeX => '\log_2'}, # fancier TeX output +); + +$x = Formula('x'); + +########################################################### +# +# The problem text +# +BEGIN_TEXT +$BEGIN_ONE_COLUMN + +In this problem, we have added a new function to the Parser: ${BTT}log2(x)${ETT}. +(Edit the code to see how this is done.) +$PAR +Assuming that ${BTT}${DOLLAR}x = Formula('x')${ETT}, it can be used as follows: +$PAR + +\{ParserTable( + 'Formula("log2(x)")', + 'log2(8)', + 'log2($x+1)', + 'Formula("log2(x)")->eval(x=>16)', + '(log2($x))->eval(x=>16)', + 'Formula("log2()")', + 'Formula("log2(1,x)")', + 'log2()', + 'log2(1,3)', + )\} + +$END_ONE_COLUMN +END_TEXT + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/macros/parserTables.pl @@ -0,0 +1,87 @@ +loadMacros("parserUtils.pl"); + +############################################# +# +# For Parser example tables: +# + +$BTT = MODES(TeX=>'{\tt ', Latex2HTML => $bHTML.'<TT>'.$eHTML, HTML => '<TT>'); +$ETT = MODES(TeX=>'}', Latex2HTML => $bHTML.'</TT>'.$eHTML, HTML => '</TT>'); + +$BC = MODES( + TeX=>'{\small\it ', + Latex2HTML => $bHTML.'<SMALL><I>'.$eHTML, + HTML => '<SMALL><I>' +); +$EC = MODES( + TeX=>'}', + Latex2HTML => $bHTML.'</I></SMALL>'.$eHTML, + HTML => '</I></SMALL>' +); + +$LT = MODES(TeX => "<", Latex2HTML => "<", HTML => '<'); +$GT = MODES(TeX => ">", Latex2HTML => ">", HTML => '>'); + +$TEX = MODES(TeX => '{\TeX}', HTML => 'TeX', HTML_dpng => '\(\bf\TeX\)'); + +@rowOptions = ( + indent => 0, + separation => 0, + align => 'LEFT" NOWRAP="1', # alignment hack to get NOWRAP +); + +sub ParserRow { + my $f = shift; my $t = ''; + Context()->clearError; + my ($s,$err) = PG_restricted_eval($f); + if (defined $s) { + my $ss = $s; + if (ref($s) && \&{$s->string}) { + $t = '\('.$s->TeX.'\)'; + $s = $s->string; + } elsif ($s !~ m/^[a-z]+$/i) { + $t = '\('.Formula($s)->TeX.'\)'; + $s = Formula($s)->string; + } + $s =~ s/</$LT/g; $s =~ s/>/$GT/g; + if (ref($ss) && \&{$ss->class}) { + if ($ss->class eq 'Formula') { + $s .= ' '.$BC.'(Formula returning '.$ss->showType.')'.$EC; + } else { + $s .= ' '.$BC.'('.$ss->class.' object)'.$EC; + } + } + } else { + $s = $BC. (Context()->{error}{message} || $err) . $EC; + $t = ''; + } + $f =~ s/</$LT/g; $f =~ s/>/$GT/g; + if ($displayMode eq 'TeX') { + $f =~ s/\^/\\char`\\^/g; $s =~ s/\^/\\char`\\^/g; + $f =~ s/#/\\#/g; $s =~ s/#/\\#/g; + } + my $row = Row([$BTT.$f.$ETT,$BTT.$s.$ETT,$t],@rowOptions); + $row =~ s/\$/\${DOLLAR}/g; + return $row; +} + +sub ParserTable { + my $table = + BeginTable(border=>1, padding=>20). + Row([$BBOLD."Perl Code".$EBOLD, + $BBOLD."Result".$EBOLD, + $BBOLD.$TEX.' version'.$EBOLD],@rowOptions); + foreach my $f (@_) {$table .= ParserRow($f)} + $table .= EndTable(); + return $table; +} + +sub Title { + my $title = shift; + + MODES( + TeX => "\\par\\centerline{\\bf $title}\\par\\nobreak\n", + Latex2HTML => $bHTML.'<CENTER><H2>'.$title.'</H2></CENTER>'.$eHTML, + HTML => '<CENTER><H2>'.$title.'</H2></CENTER>' + ); +} --- /dev/null +++ doc/MathObjects/macros/Differentiation.pl @@ -0,0 +1,20 @@ +# +# Example of how to add new functionality to the Parser. +# +# Here we load new methods for the Parser object classes. Note, however, +# that these are PERSISTANT when used with webwork2 (mod_perl), and so we +# need to take care not to load them more than once. We look for the +# variable $Parser::Differentiation::loaded, which is defined in the +# differentiation package, in order to tell. +# +# DifferentiationDefs.pl is really just a copy of the +# Parser::Differentiation.pm file, and you really could just preload the +# latter instead by uncommenting the 'use Parser::Differentiation' line at +# the bottom of Parser.pm. (This file is really just a sample). The way +# it's done here will load it the first time it gets used, then will keep +# it around, so not much overhead even this way. +# + +loadMacros("DifferentiationDefs.pl") unless $Parser::Differentiation::loaded; + +1; --- /dev/null +++ doc/MathObjects/macros/DifferentiationDefs.pl @@ -0,0 +1,635 @@ +# +# Extend differentiation to multiple variables +# Check differentiation for complex functions +# Do derivatives for norm and unit. +# +# Make shortcuts for getting numbers 1, 2, and sqrt, etc. +# + +################################################## +# +# Differentiate the formula in terms of the given variable +# +sub Parser::D { + my $self = shift; my $x = shift; + if (!defined($x)) { + my @vars = keys(%{$self->{variables}}); + my $n = scalar(@vars); + if ($n == 0) { + return $self->new('0') if $self->{isNumber}; + $x = 'x'; + } else { + $self->Error("You must specify a variable to differentiate by") unless $n ==1; + $x = $vars[0]; + } + } else { + return $self->new('0') unless defined $self->{variables}{$x}; + } + return $self->new($self->{tree}->D($x)); +} + +sub Item::D { + my $self = shift; + my $type = ref($self); $type =~ s/.*:://; + $self->Error("Differentiation for '$type' is not implemented",$self->{ref}); +} + + +######################################################################### + +sub Parser::BOP::comma::D {Item::D(shift)} +sub Parser::BOP::union::D {Item::D(shift)} + +sub Parser::BOP::add::D { + my $self = shift; my $x = shift; + $self = Parser::BOP->new( + $self->{equation},$self->{bop}, + $self->{lop}->D($x),$self->{rop}->D($x) + ); + return $self->reduce; +} + + +sub Parser::BOP::subtract::D { + my $self = shift; my $x = shift; + $self = Parser::BOP->new( + $self->{equation},$self->{bop}, + $self->{lop}->D($x),$self->{rop}->D($x) + ); + return $self->reduce; +} + +sub Parser::BOP::multiply::D { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + $self = + Parser::BOP->new($equation,'+', + Parser::BOP->new($equation,$self->{bop}, + $self->{lop}->D($x),$self->{rop}->copy($equation)), + Parser::BOP->new($equation,$self->{bop}, + $self->{lop}->copy($equation),$self->{rop}->D($x)) + ); + return $self->reduce; +} + +sub Parser::BOP::divide::D { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + $self = + Parser::BOP->new($equation,$self->{bop}, + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'*', + $self->{lop}->D($x),$self->{rop}->copy($equation)), + Parser::BOP->new($equation,'*', + $self->{lop}->copy($equation),$self->{rop}->D($x)) + ), + Parser::BOP->new($equation,'^', + $self->{rop},Parser::Number->new($equation,2) + ) + ); + return $self->reduce; +} + +sub Parser::BOP::power::D { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + my $vars = $self->{rop}->getVariables; + if (defined($vars->{$x})) { + $vars = $self->{lop}->getVariables; + if (defined($vars->{$x})) { + $self = + Parser::Function->new($equation,'exp', + [Parser::BOP->new($equation,'*',$self->{rop}->copy($equation), + Parser::Function->new($equation,'log',[$self->{lop}->copy($equation)],0))]); + return $self->D($x); + } + $self = Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'log',[$self->{lop}->copy($equation)],0), + Parser::BOP->new($equation,'*', + $self->copy($equation),$self->{rop}->D($x)) + ); + } else { + $self = + Parser::BOP->new($equation,'*', + Parser::BOP->new($equation,'*', + $self->{rop}->copy($equation), + Parser::BOP->new($equation,$self->{bop}, + $self->{lop}->copy($equation), + Parser::BOP->new($equation,'-', + $self->{rop}->copy($equation), + Parser::Number->new($equation,1) + ) + ) + ), + $self->{lop}->D($x) + ); + } + return $self->reduce; +} + +sub Parser::BOP::cross::D {Item::D(shift)} +sub Parser::BOP::dot::D {Item::D(shift)} +sub Parser::BOP::underscore::D {Item::D(shift)} + +######################################################################### + +sub Parser::UOP::plus::D { + my $self = shift; my $x = shift; + return $self->{op}->D($x) +} + +sub Parser::UOP::minus::D { + my $self = shift; my $x = shift; + $self = Parser::UOP->new($self->{equation},'u-',$self->{op}->D($x)); + return $self->reduce; +} + +sub Parser::UOP::factorial::D {Item::D(shift)} + +######################################################################### + +sub Parser::Function::D { + my $self = shift; + $self->Error("Differentiation of '$self->{name}' not implemented",$self->{ref}); +} + +sub Parser::Function::D_chain { + my $self = shift; my $x = $self->{params}[0]; + my $name = "D_" . $self->{name}; + $self = Parser::BOP->new($self->{equation},'*',$self->$name($x->copy),$x->D(shift)); + return $self->reduce; +} + +############################# + +sub Parser::Function::trig::D {Parser::Function::D_chain(@_)} + +sub Parser::Function::trig::D_sin { + my $self = shift; my $x = shift; + return Parser::Function->new($self->{equation},'cos',[$x]); +} + +sub Parser::Function::trig::D_cos { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::Function->new($equation,'sin',[$x]) + ); +} + +sub Parser::Function::trig::D_tan { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'sec',[$x]), + Parser::Number->new($equation,2) + ); +} + +sub Parser::Function::trig::D_cot { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'csc',[$x]), + Parser::Number->new($equation,2) + ) + ); +} + +sub Parser::Function::trig::D_sec { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'sec',[$x]), + Parser::Function->new($equation,'tan',[$x]) + ); +} + +sub Parser::Function::trig::D_csc { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'csc',[$x]), + Parser::Function->new($equation,'cot',[$x]) + ) + ); +} + +sub Parser::Function::trig::D_asin { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x,Parser::Number->new($equation,2) + ) + )] + ) + ); +} + +sub Parser::Function::trig::D_acos { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x,Parser::Number->new($equation,2) + ) + )] + ) + ) + ); +} + +sub Parser::Function::trig::D_atan { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ); +} + +sub Parser::Function::trig::D_acot { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ) + ); +} + +sub Parser::Function::trig::D_asec { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'abs',[$x]), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ), + Parser::Number->new($equation,1) + )] + ) + ) + ); +} + +sub Parser::Function::trig::D_acsc { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'abs',[$x]), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ), + Parser::Number->new($equation,1) + )] + ) + ) + ) + ); +} + + +############################# + +sub Parser::Function::hyperbolic::D {Parser::Function::D_chain(@_)} + +sub Parser::Function::hyperbolic::D_sinh { + my $self = shift; my $x = shift; + return Parser::Function->new($self->{equation},'cosh',[$x]); +} + +sub Parser::Function::hyperbolic::D_cosh { + my $self = shift; my $x = shift; + return Parser::Function->new($self->{equation},'sinh',[$x]); +} + +sub Parser::Function::hyperbolic::D_tanh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'sech',[$x]), + Parser::Number->new($equation,2) + ); +} + +sub Parser::Function::hyperbolic::D_coth { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'^', + Parser::Function->new($equation,'csch',[$x]), + Parser::Number->new($equation,2) + ) + ); +} + +sub Parser::Function::hyperbolic::D_sech { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'sech',[$x]), + Parser::Function->new($equation,'tanh',[$x]) + ) + ); +} + +sub Parser::Function::hyperbolic::D_csch { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'csch',[$x]), + Parser::Function->new($equation,'coth',[$x]) + ) + ); +} + +sub Parser::Function::hyperbolic::D_asinh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + )] + ) + ); +} + +sub Parser::Function::hyperbolic::D_acosh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ), + Parser::Number->new($equation,1) + )] + ) + ); +} + +sub Parser::Function::hyperbolic::D_atanh { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ); +} + +sub Parser::Function::hyperbolic::D_acoth { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + ) + ); +} + +sub Parser::Function::hyperbolic::D_asech { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + $x, + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'-', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + )] + ) + ) + ) + ); +} + +sub Parser::Function::hyperbolic::D_acsch { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::UOP->new($equation,'u-', + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Function->new($equation,'abs',[$x]), + Parser::Function->new($equation,'sqrt',[ + Parser::BOP->new($equation,'+', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'^', + $x, Parser::Number->new($equation,2) + ) + )] + ) + ) + ) + ); +} + + +############################# + +sub Parser::Function::numeric::D {Parser::Function::D_chain(@_)} + +sub Parser::Function::numeric::D_log { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return Parser::BOP->new($equation,'/',Parser::Number->new($equation,1),$x); +} + +sub Parser::Function::numeric::D_log10 { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Number->new($equation,CORE::log(10)), $x + ) + ); +} + +sub Parser::Function::numeric::D_exp { + my $self = shift; my $x = shift; + return $self->copy(); +} + +sub Parser::Function::numeric::D_sqrt { + my $self = shift; my $x = shift; + my $equation = $self->{equation}; + return + Parser::BOP->new($equation,'/', + Parser::Number->new($equation,1), + Parser::BOP->new($equation,'*', + Parser::Number->new($equation,2), + $self->copy + ) + ); +} + +sub Parser::Function::numeric::D_abs {Parser::Function::D(@_)} +sub Parser::Function::numeric::D_int {Parser::Function::D(@_)} +sub Parser::Function::numeric::D_sgn {Parser::Function::D(@_)} + +######################################################################### + +sub Parser::List::D { + my $self = shift; my $x = shift; + $self = $self->copy($self->{equation}); + foreach my $f (@{$self->{coords}}) {$f = $f->D($x)} + return $self->reduce; +} + + +sub Parser::List::Interval::D { + my $self = shift; + $self->Error("Can't differentiate intervals",$self->{ref}); +} + +sub Parser::List::AbsoluteValue::D { + my $self = shift; + $self->Error("Can't differentiate absolute values",$self->{ref}); +} + + +######################################################################### + +sub Parser::Number::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +sub Parser::Complex::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +sub Parser::Constant::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +sub Parser::Value::D { + my $self = shift; my $x = shift; my $equation = $self->{equation}; + return Parser::Value->new($equation,$self->{value}->D($x,$equation)); +} + +sub Value::D { + my $self = shift; my $x = shift; my $equation = shift; + return 0 if $self->isComplex; + my @coords = @{$self->{data}}; + foreach my $n (@coords) + {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x,$equation)->data}} + return $self->new([@coords]); +} + +sub Value::List::D { + my $self = shift; my $x = shift; my $equation = shift; + my @coords = @{$self->{data}}; + foreach my $n (@coords) + {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x)}} + return $self->new([@coords]); +} + +sub Value::Interval::D { + shift; shift; my $self = shift; + $self->Error("Can't differentiate intervals",$self->{ref}); +} + +sub Value::Union::D { + shift; shift; my $self = shift; + $self->Error("Can't differentiate unions",$self->{ref}); +} + +######################################################################### + +sub Parser::Variable::D { + my $self = shift; my $x = shift; + my $d = ($self->{name} eq $x)? 1: 0; + return Parser::Number->new($self->{equation},$d); +} + +######################################################################### + +sub Parser::String::D {Parser::Number->new(shift->{equation},0)} + +######################################################################### + +package Parser::Differentiation; +our $loaded = 1; + +######################################################################### + +1; --- /dev/null +++ doc/MathObjects/macros/unionImage.pl @@ -0,0 +1,75 @@ +###################################################################### +# +# A routine to make including images easier to control +# +# Usage: Image(name,options) +# +# where name is the name of an image file or a reference to a +# graphics object (or a reference to a pair of one of these), +# and options are taken from among the following: +# +# size => [w,h] the size of the image in the HTML page +# (default is [150,150]) +# +# tex_size => r the size to use in TeX mode (as a percentage +# of the line width times 10). E.g., 500 is +# half the width, etc. (default is 200.) +# +# link => 0 or 1 whether to include a link to the original +# image (default is 0, unless there are +# two images given) +# +# border => 0 or 1 size of image border in HTML mode +# (defaults to 2 or 1 depending on whether +# there is a link or not) +# +# align => placement vertical alignment for image in HTML mode +# (default is "BOTTOM") +# +# tex_center => 0 or 1 whether to center the image horizontally +# in TeX mode (default is 0) +# +# The image name can be one of a number of different things. It can be +# the name of an image file, or an alias to one produce by the alias() +# command. It can be a graphics object reference created by init_graph(). +# Or it can be a pair of these (in square brackets). The first is the +# image for the HTML file, and the second is the image that it will be +# linked to. +# +# Examples: Image("graph.gif", size => [200,200]); +# Image(["graph.gif","graph-large.gif"]); +# +# The alias() and insertGraph() functions will be called automatically +# when needed. +# +sub Image { + my $image = shift; my $ilink; + my %options = ( + size => [150,150], tex_size => 200, + link => 0, align => "BOTTOM", tex_center => 0, @_); + my ($w,$h) = @{$options{size}}; + my ($ratio,$link) = ($options{tex_size}*(.001),$options{link}); + my ($border,$align) = ($options{border},$options{align}); + my ($tcenter) = $options{tex_center}; + my $HTML; my $TeX; + ($image,$ilink) = @{$image} if (ref($image) eq "ARRAY"); + $image = alias(insertGraph($image)) if (ref($image) eq "WWPlot"); + $image = alias($image) unless ($image =~ m!^/!i); + if ($ilink) { + $ilink = alias(insertGraph($ilink)) if (ref($ilink) eq "WWPlot"); + $ilink = alias($ilink) unless ($ilink =~ m!^/!i); + } else {$ilink = $image} + $border = (($link || $ilink ne $image)? 2: 1) unless defined($border); + $HTML = '<IMG SRC="'.$image.'" WIDTH="'.$w. + '" HEIGHT="'.$h.'" BORDER="'.$border.'" ALIGN="'.$align.'">'; + $HTML = '<A HREF="'.$ilink.'">'.$HTML.'</A>' if $link or $ilink ne $image; + $TeX = '\includegraphics[width='.$ratio.'\linewidth]{'.$image.'}'; + $TeX = '\centerline{'.$TeX.'}' if $tcenter; + MODES( + TeX => $TeX."\n", + Latex2HTML => $bHTML.$HTML.$eHTML, + HTML => $HTML + ); +} + +1; --- /dev/null +++ doc/MathObjects/macros/unionTables.pl @@ -0,0 +1,253 @@ +###################################################################### +## +## Functions for creating tables of various kinds +## +## ColumnTable() Creates a two-column display in HTML, +## but only one column in TeX. +## +## ColumnMatchTable() Does a side-by-side match table +## +## BeginTable() Begin a borderless HTML table +## Row() Create a row in the table +## AlignedRow() Create a row with alignment in each column +## TableSpace() Insert extra vertical space in the table +## EndTable() End the table +## + +###################################################################### +# +# Make a two-column table in HTML and Latex2HTML modes +# +# Usage: ColumnTable(col1,col2,[options]) +# +# Options can be taken from: +# +# indent => n the width to indent the first column +# (default is 0) +# +# separation => n the width of the separating gutter +# (default is 50) +# +# valign => type set the vertical alignment +# (default is "MIDDLE") +# +sub ColumnTable { + my $col1 = shift; my $col2 = shift; + my %options = (indent => 0, separation => 50, valign => "MIDDLE", @_); + my ($ind,$sep) = ($options{"indent"},$options{"separation"}); + my $valign = $options{"valign"}; + + my ($bhtml,$ehtml) = ('\begin{rawhtml}','\end{rawhtml}'); + ($bhtml,$ehtml) = ('','') unless ($displayMode eq "Latex2HTML"); + + my $HTMLtable = qq { + $bhtml<TABLE BORDER="0"><TR VALIGN="$valign"> + <TD WIDTH="$ind"> </TD><TD>$ehtml + $col1 + $bhtml</TD><TD WIDTH="$sep"> </TD><TD>$ehtml + $col2 + $bhtml</TD></TR></TABLE>$ehtml + }; + + MODES( + TeX => '\par\medskip\hbox{\qquad\vtop{'. + '\advance\hsize by -3em '.$col1.'}}'. + '\medskip\hbox{\qquad\vtop{'. + '\advance\hsize by -3em '.$col2.'}}\medskip', + Latex2HTML => $HTMLtable, + HTML => $HTMLtable + ); +} + +# +# Use columns for a match-list output +# +# Usage: ColumnMatchTable($ml,options) +# +# where $ml is a math list reference and options are those +# allowed for ColumnTable above. +# +sub ColumnMatchTable { + my $ml = shift; + + ColumnTable($ml->print_q,$ml->print_a,@_); +} + + +# +# Command for tables with no borders. +# +# Usage: BeginTable(options); +# +# Options are taken from: +# +# border => n value for BORDER attribute (default 0) +# spacing => n value for CELLSPACING attribute (default 0) +# padding => n value for CELLPADDING attribute (default 0) +# tex_spacing => dimen value for spacing between columns in TeX +# (e.g, tex_spacing => 2em) (default 1em) +# tex_border => dimen value for left- and right border in TeX (0pt) +# center => 0 or 1 center table or not (default 1) +# +sub BeginTable { + my %options = (border => 0, padding => 0, spacing => 0, center => 1, + tex_spacing => "1em", tex_border => "0pt", @_); + my ($bd,$pd,$sp) = ($options{border},$options{padding},$options{spacing}); + my ($tsp,$tbd) = ($options{tex_spacing},$options{tex_border}); + my ($center,$tcenter) = (' ALIGN="CENTER"','\centerline'); + ($center,$tcenter) = ('','') if (!$options{center}); + my $table = + qq{<TABLE BORDER="$bd" CELLPADDING="$pd" CELLSPACING="$sp"$center>}; + + MODES( + TeX => '\par\medskip'.$tcenter.'{\kern '.$tbd. + '\vbox{\halign{#\hfil&&\kern '.$tsp.' #\hfil', + Latex2HTML => $bHTML.$table.$eHTML."\n", + HTML => $table."\n" + ); +} + +# +# Usage: EndTable(options) +# +# where options are taken from: +# +# tex_border => dimen extra vertical space in TeX mode (default 0pt) +# +sub EndTable { + my %options = (tex_border => "0pt", @_); + my $tbd = $options{tex_border}; + MODES( + TeX => '\cr}}\kern '.$tbd.'}\medskip'."\n", + Latex2HTML => $bHTML.'</TABLE>'.$eHTML."\n", + HTML => '</TABLE>'."\n" + ); +} + +# +# Creates a row in the table +# +# Usage: Row([item1,item2,...],options); +# +# Each item appears as a separate entry in the table. +# +# Options control how the row is displayed: +# +# indent => num Specifies size of blank column on the left +# (default: indent => 0) +# +# separation => num Specifies separation of columns +# (default: spearation => 30) +# +# align => "type" Specifies alignment of initial column +# (default: align => "LEFT") +# +# valign => "type" Specified vertical alignment of row +# (default: valign => "MIDDLE") +# +sub Row { + my $rowref = shift; my @row = @{$rowref}; + my %options = ( + indent => 0, separation => 30, + align => "LEFT", valign => "MIDDLE", + @_ + ); + my ($cind,$csep) = ($options{indent},$options{separation}); + my ($align,$valign) = ($options{align},$options{valign}); + my $sep = '<TD WIDTH="'.$csep.'"> </TD>'; $sep = '' if ($csep < 1); + my $ind = '<TD WIDTH="'.$cind.'"> </TD>'; $ind = '' if ($cind < 1); + my $fill = ''; + $fill = '\hfil' if (uc($align) eq "CENTER"); + $fill = '\hfill' if (uc($align) eq "RIGHT"); + + MODES( + TeX => "\\cr\n". $fill . join('& ',@row), + Latex2HTML => + $bHTML."<TR VALIGN=\"$valign\">$ind<TD ALIGN=\"$align\">".$eHTML . + join($bHTML."</TD>$sep<TD>".$eHTML,@row) . + $bHTML.'</TD></TR>'.$eHTML."\n", + HTML => "<TR VALIGN=\"$valign\">$ind<TD ALIGN=\"$align\">" . + join("</TD>$sep<TD>",@row) . '</TD></TR>'."\n" + ); +} + +# +# AlignedRow([item1,item2,...],options); +# +# Options control how the row is displayed: +# +# indent => num Specifies size of blank column on the left +# (default: indent => 0) +# +# separation => num Specifies separation of columns +# (default: spearation => 30) +# +# align => "type" Specifies alignment of all columns +# (default: align => "CENTER") +# +# valign => "type" Specified vertical alignment of row +# (default: valign => "MIDDLE") +# +sub AlignedRow { + my $rowref = shift; my @row = @{$rowref}; + my %options = ( + indent => 0, separation => 30, + align => "CENTER", valign => "MIDDLE", + @_ + ); + my ($cind,$csep) = ($options{indent},$options{separation}); + my ($align,$valign) = ($options{align},$options{valign}); + my $sep = '<TD WIDTH="'.$csep.'"> </TD>'; $sep = '' if ($csep < 1); + my $ind = '<TD WIDTH="'.$cind.'"> </TD>'; $ind = '' if ($cind < 1); + my $fill = ''; + $fill = '\hfil ' if (uc($align) eq "CENTER"); + $fill = '\hfill ' if (uc($align) eq "RIGHT"); + + MODES( + TeX => "\\cr\n". $fill . join('&'.$fill,@row), + Latex2HTML => + $bHTML."<TR VALIGN=\"$valign\">$ind<TD ALIGN=\"$align\">".$eHTML . + join($bHTML."</TD>$sep<TD ALIGN=\"$align\">".$eHTML,@row) . + $bHTML.'</TD></TR>'.$eHTML."\n", + HTML => "<TR VALIGN=\"$valign\">$ind<TD ALIGN=\"$align\">" . + join("</TD>$sep<TD ALIGN=\"$align\">",@row) . '</TD></TR>'."\n" + ); +} + +# +# Add extra space between rows of a table +# +# Usage: TableSpace(pixels,points) +# +# where pixels is the number of pixels of space in HTML mode and +# points is the number of points to use in TeX mode. +# +sub TableSpace { + my $rsep = shift; + my $tsep = shift; + $rsep = $tsep if (defined($tsep) && $main::displayMode eq "TeX"); + return "" if ($rsep < 1); + MODES( + TeX => '\vadjust{\kern '.$rsep.'pt}' . "\n", + Latex2HTML => + $bHTML.'<TR><TD HEIGHT="'.$rsep.'"><!></TD></TR>'.$eHTML."\n", + HTML => '<TR><TD HEIGHT="'.$rsep.'"></TD></TR>'."\n", + ); +} + +# +# A horizontal rule within a table. (Could have been a variable, +# but all the other table commands are subroutines, so kept it +# one to be consistent.) +# +sub TableLine { + MODES( + TeX => '\vadjust{\kern2pt\hrule\kern2pt}', + Latex2HTML => $bHTML. + '<TR><TD COLSPAN="10"><HR NOSHADE SIZE="1"></TD></TR>'. + $eHTML."\n", + HTML =>'<TR><TD COLSPAN="10"><HR NOSHADE SIZE="1"></TD></TR>'."\n" + ); +} + +1; --- /dev/null +++ doc/MathObjects/macros/parserUtils.pl @@ -0,0 +1,61 @@ +loadMacros( + "unionImage.pl", + "unionTables.pl", +); + +$bHTML = '\begin{rawhtml}'; +$eHTML = '\end{rawhtml}'; + +# HTML(htmlcode) +# HTML(htmlcode,texcode) +# +# Insert $html in HTML mode or \begin{rawhtml}$html\end{rawhtml} in +# Latex2HTML mode. In TeX mode, insert nothing for the first form, and +# $tex for the second form. +# +sub HTML { + my ($html,$tex) = @_; + return('') unless (defined($html) && $html ne ''); + $tex = '' unless (defined($tex)); + MODES(TeX => $tex, Latex2HTML => $bHTML.$html.$eHTML, HTML => $html); +} + +# +# Begin and end <TT> mode +# +$BTT = HTML('<TT>','\texttt{'); +$ETT = HTML('</TT>','}'); + +# +# Begin and end <SMALL> mode +# +$BSMALL = HTML('<SMALL>','{\small '); +$ESMALL = HTML('</SMALL>','}'); + +# +# Block quotes +# +$BBLOCKQUOTE = HTML('<BLOCKQUOTE>','\hskip3em '); +$EBLOCKQUOTE = HTML('</BLOCKQUOTE>'); + +# +# Smart-quotes in TeX mode, regular quotes in HTML mode +# +$LQ = MODES(TeX => '``', Latex2HTML => '"', HTML => '"'); +$RQ = MODES(TeX => "''", Latex2HTML => '"', HTML => '"'); + +# +# make sure all characters are displayed +# +sub protectHTML { + my $string = shift; + $string =~ s/&/\&/g; + $string =~ s/</\</g; + $string =~ s/>/\>/g; + $string; +} + +sub _parserUtils_init {} + +1; + --- /dev/null +++ doc/MathObjects/problems/sample12.pg @@ -0,0 +1,62 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context("Interval"); + +$a = non_zero_random(-5,5,1); +$f = Formula("(x^2+1)/(x-$a)")->reduce; +$R = Union("(-inf,$a) U ($a,inf)"); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(\displaystyle f(x) = $f\). +$PAR +Then \(f\) is defined on the region \{ans_rule(30)\}. +$PAR +${BCENTER} +${BSMALL} +Several intervals can be combined using the +set union symbol, ${LQ}${BTT}U${ETT}${RQ}.$BR +Use ${LQ}${BTT}infinity${ETT}${RQ} for ${LQ}\(\infty\)${RQ} and +${LQ}${BTT}-infinity${ETT}${RQ} for ${LQ}\(-\infty\)${RQ}. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS($R->cmp); +$showPartialCorrectAnswers=1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/problems/sample19.pg @@ -0,0 +1,67 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Interval')->variables->add(a=>'Real'); +$x = Formula('x'); $a = Formula('a'); + +$f = log($x-$a); +$I = Formula("(-infinity,a]"); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(x) = $f\). +$PAR +Then \(f\) is undefined for \(x\) in the interval(s) +\{ans_rule(20)\}. +$PAR +${BCENTER} +${BSMALL} +To enter more than one interval, separate them by commas.$BR +Use ${LQ}${BTT}infinity${ETT}${RQ} for ${LQ}\(\infty\)${RQ} and +${LQ}${BTT}-infinity${ETT}${RQ} for ${LQ}\(-\infty\)${RQ}.$BR +Enter ${LQ}${BTT}NONE${ETT}${RQ} if the function is always defined. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS(List($I)->cmp( + list_type => 'a list of intervals', # override these names to avoid + entry_type => "an interval", # 'formula returning ...' messages +)); +Context()->variables->remove('x'); # error if 'x' is used in answer + +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/problems/sample16.pg @@ -0,0 +1,63 @@ +########################################################### +# +# Example showing how to use the Parser's function +# answer checker. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "Differentiation.pl", +); + +TEXT(beginproblem()); + +########################################################### +# +# The setup +# +Context('Numeric'); +$x = Formula('x'); # used to construct formulas below. + +# +# Define a function and its derivative and make them pretty +# +$a = random(1,8,1); +$b = random(-8,8,1); +$c = random(-8,8,1); + +$f = ($a*$x**2 + $b*$x + $c) -> reduce; +$df = $f->D('x'); + +$x = random(-8,8,1); + +########################################################### +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(f(x) = $f\). +$PAR +Then \(f'(x)=\) \{ans_rule(20)\},$BR +and \(f'($x)=\) \{ans_rule(20)\}. + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answers +# +ANS($df->cmp); +ANS($df->eval(x=>$x)->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/problems/sample15.pg @@ -0,0 +1,59 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros.pl", + "Parser.pl", + "parserUtils.pl", +); + +TEXT(beginproblem()); + +########################################################## +# +# The setup +# + +Context("Numeric"); + +$a = random(1,5,1); +$f = Formula("(x^2-$a)/(x^2+$a)"); + +########################################################## +# +# The problem text +# + +Context()->texStrings; +BEGIN_TEXT + +Suppose \(\displaystyle f(x) = $f\). +$PAR +Then \(f\) is defined for all \(x\) except for \{ans_rule(30)\}. +$PAR +${BCENTER} +${BSMALL} +To enter more than one value, separate them by commas.$BR +Enter ${LQ}${BTT}NONE${ETT}${RQ} if there are no such values. +${ESMALL} +${ECENTER} + +END_TEXT +Context()->normalStrings; + +########################################################### +# +# The answer +# + +ANS(List("NONE")->cmp); +$showPartialCorrectAnswers = 1; + +########################################################### + +ENDDOCUMENT(); # This should be the last executable line in the problem. --- /dev/null +++ doc/MathObjects/problems/sample14.pg @@ -0,0 +1,59 @@ +########################################################## +# +# Example showing how to use the built-in answer checker for parsed values. +# + +DOCUMENT(); # This should be the first executable line in the problem. + +loadMacros( + "PGbasicmacros.pl", + "PGanswermacros... [truncated message content] |