diff --git a/LICENSE b/LICENSE index 98bc5d4468..8df1667545 100644 --- a/LICENSE +++ b/LICENSE @@ -2,7 +2,7 @@ Online Homework Delivery System Version 2.* - Copyright 2000-2017, The WeBWorK Project + Copyright 2000-2018, The WeBWorK Project All rights reserved. This program is free software; you can redistribute it and/or modify diff --git a/VERSION b/VERSION index 2fe7285f81..2650dc99cd 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ -$PG_VERSION ='PG-2.13'; -$PG_COPYRIGHT_YEARS = '1996-2017'; +$PG_VERSION ='PG-2.14'; +$PG_COPYRIGHT_YEARS = '1996-2018'; 1; diff --git a/lib/AnswerHash.pm b/lib/AnswerHash.pm index 711c304dfa..a2dee9e85e 100755 --- a/lib/AnswerHash.pm +++ b/lib/AnswerHash.pm @@ -20,7 +20,7 @@ AnswerHash -- this class stores information related to the student's answer. It is little more than a standard perl hash with - a special name, butit does have some access and + a special name, but it does have some access and manipulation methods. More of these may be added as it becomes necessary. diff --git a/lib/Applet.pm b/lib/Applet.pm index 80fecf5bf7..6e251b6948 100644 --- a/lib/Applet.pm +++ b/lib/Applet.pm @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/lib/Label.pm b/lib/Label.pm index 193020427e..140b8fa57c 100644 --- a/lib/Label.pm +++ b/lib/Label.pm @@ -18,14 +18,18 @@ This module defines labels for the graph objects (WWPlot). =head2 Usage - $label1 = new Label($x_value, $y_value, $label_string, $label_color, @justification) - $justification = one of ('left', 'center', 'right) and ('bottom', 'center', 'top') - describes the position of the ($x_value, $y_value) within the string. - The default is 'left', 'top' + $label1 = new Label($x_value, $y_value, $label_string, $label_color, @options) + $options is an array with (*'d defaults) + - one of 'left'*, 'center', 'right' (horizontal alignment) + - one of 'bottom', 'center', 'top'* (verical alignment) + - one of 'horizontal'*, 'vertical' (orientation) + - one of 'small', 'large', 'mediumbold'*, 'tiny', 'giant' (which gd font to use) + Note the alignment specifications are relative to the English reading of the string, + even when the orientation is vertical. -=head2 Example +=head2 Example: $new_label = new Label ( 0,0, 'origin','red','left', 'top') @labels = $graph->lb($new_label); @@ -51,14 +55,15 @@ use strict; @Label::ISA = qw(WWPlot); my %fields =( - 'x' => 0, - 'y' => 0, - color => 'black', - font => GD::gdMediumBoldFont, #gdLargeFont - # constants from GD need to be addressed fully, they have not been imported. - str => "", - lr_nudge => 0, #justification parameters - tb_nudge => 0, + 'x' => 0, + 'y' => 0, + color => 'black', + font => GD::gdMediumBoldFont, #gdLargeFont + # constants from GD need to be addressed fully, they have not been imported. + str => "", + lr_nudge => 0, #justification parameters + tb_nudge => 0, + orientation => 'horizontal', ); @@ -74,31 +79,46 @@ sub new { } sub _initialize { - my $self = shift; - my ($x,$y,$str,$color,@justification) = @_; - $self -> x($x); - $self -> y($y); - $self -> str($str); - $self -> color($color) if defined($color); - my $j; - foreach $j (@justification) { - $self->lr_nudge( - length($self->str) ) if $j eq 'right'; - $self->tb_nudge( - 1 ) if $j eq 'bottom'; - $self->lr_nudge( - ( length($self->str) )/2)if $j eq 'center'; - $self->tb_nudge(-0.5) if $j eq 'middle'; -# print "\njustification=$j",$self->lr_nudge,$self->tb_nudge,"\n"; - } + my $self = shift; + my ($x,$y,$str,$color,@justification) = @_; + $self -> x($x); + $self -> y($y); + $self -> str($str); + $self -> color($color) if defined($color); + my $j; + foreach $j (@justification) { + if ($j eq 'right') {$self->lr_nudge( - length($self->str) ); } + elsif ($j eq 'bottom') {$self->tb_nudge( - 1 ); } + elsif ($j eq 'center') {$self->lr_nudge( - ( length($self->str) )/2); } + elsif ($j eq 'middle') {$self->tb_nudge(-0.5); } + elsif ($j eq 'vertical') {$self->orientation($j); } + #there are only five avialble fonts: http://search.cpan.org/~rurban/GD-2.68/lib/GD.pm#Font_Utilities + elsif ($j eq 'small') {$self->font(GD::gdSmallFont); } + elsif ($j eq 'large') {$self->font(GD::gdLargeFont); } + elsif ($j eq 'tiny') {$self->font(GD::gdTinyFont); } + elsif ($j eq 'giant') {$self->font(GD::gdGiantFont); } + } } sub draw { - my $self = shift; - my $g = shift; #the containing graph - $g->im->string( $self->font, - $g->ii($self->x)+int( $self->lr_nudge*($self->font->width) ), - $g->jj($self->y)+int( $self->tb_nudge*($self->font->height) ), - $self->str, - ${$g->colors}{$self->color} - ); - + my $self = shift; + my $g = shift; #the containing graph + if ($self->orientation eq 'horizontal') { + $g->im->string( $self->font, + $g->ii($self->x)+int( $self->lr_nudge*($self->font->width) ), + $g->jj($self->y)+int( $self->tb_nudge*($self->font->height) ), + $self->str, + ${$g->colors}{$self->color} + ); + } + elsif ($self->orientation eq 'vertical') { + $g->im->stringUp( $self->font, + $g->ii($self->x)+int( $self->tb_nudge*($self->font->height) ), + $g->jj($self->y)-int( $self->lr_nudge*($self->font->width) ), + $self->str, + ${$g->colors}{$self->color} + ); + + } } sub AUTOLOAD { @@ -214,6 +234,22 @@ sub tb_nudge { return $self->{tb_nudge} } } + +sub orientation { + my $self = shift; + my $type = ref($self) || die "$self is not an object"; + unless (exists $self->{orientation} ) { + die "Can't find orientation field in object of class $type"; + } + + if (@_) { + return $self->{orientation} = shift; + } else { + return $self->{orientation} + } +} + + sub DESTROY { # doing nothing about destruction, hope that isn't dangerous } diff --git a/lib/Matrix.pm b/lib/Matrix.pm index 18c8093ff9..3bb7586c94 100644 --- a/lib/Matrix.pm +++ b/lib/Matrix.pm @@ -3,6 +3,12 @@ Matrix - Matrix of Reals Implements overrides for MatrixReal.pm for WeBWorK +In general it is better to use MathObjects Matrices (Value::Matrix) +in writing PG problem. The answer checking is much superior with better +error messages for syntax errors in student entries. Some of the +subroutines in this file are still used behind the scenes +by Value::Matrix to perform calculations, +such as decompose_LR(). =head1 DESCRIPTION @@ -68,8 +74,23 @@ sub _stringify { return($s); } -# obtain the Left Right matrices of the decomposition and the two pivot permutation matrices -# the original is M = PL*L*R*PR +=head3 Accessor functions + + (these are deprecated for direct use. Use the covering Methods + provided by MathObject Matrices instead.) + + L($matrix) - return matrix L of the LR decomposition + R($matrix) - return matrix R of the LR decomposition + PL($matrix) - return permutation matrix + PR($matrix) - return permutation matrix + Original matrix is PL * L * R *PR = M + +Obtain the Left Right matrices of the decomposition +and the two pivot permutation matrices +the original is M = PL*L*R*PR + +=cut + sub L { my $matrix = shift; my $rows = $matrix->[1]; @@ -83,6 +104,7 @@ sub L { } $L_matrix; } + sub R { my $matrix = shift; my $rows = $matrix->[1]; @@ -117,12 +139,14 @@ sub PR { # use this permuation on the right PL*L*R*PR =M $PR_matrix; } -# obtain the Left Right matrices of the decomposition and the two pivot permutation matrices -# the original is M = PL*L*R*PR + + =head4 Method $matrix->rh_options +Meant for internal use when dealing with MatrixReal1 + =cut sub rh_options { @@ -137,9 +161,13 @@ sub rh_options { Method $matrix->trace Returns: scalar which is the trace of the matrix. + + Used by MathObject Matrices for calculating the trace. + Deprecated for direct use in PG questions. =cut + sub trace { my $self = shift; my $rows = $self->[1]; @@ -152,9 +180,12 @@ sub trace { $sum; } + =head4 - Method $matrix->new_from_array_ref + Method $new_matrix = $matrix->new_from_array_ref ([[a,b,c],[d,e,f]]) + + Deprecated in favor of using creation tools for MathObject Matrices =cut @@ -172,6 +203,8 @@ sub new_from_array_ref { # this will build a matrix or a row vector from [a, b Method $matrix->array_ref +Converts Matrix from an ARRAY to an ARRAY reference. + =cut sub array_ref { @@ -183,6 +216,8 @@ sub array_ref { Method $matrix->list +Converts a Matrix column vector to an ARRAY (list). + =cut sub list { # this is used only for column vectors @@ -196,29 +231,14 @@ sub list { # this is used only for column vectors @list; } -=head4 - - Method $matrix->new_from_list - -=cut - -sub new_from_list { # this builds a row vector from an array - my $class = shift; - my @list = @_; - my $cols = @list; - my $rows = 1; - my $matrix = new Matrix($rows, $cols); - my $i=1; - while(@list) { - my $elem = shift(@list); - $matrix->assign($i++,1, $elem); - } - $matrix; -} =head4 Method $matrix->new_row_matrix + + Deprecated -- there are better tools for MathObject Matrices. + +Create a row 1 by n matrix from a list. This subroutine appears to be broken =cut @@ -239,7 +259,9 @@ sub new_row_matrix { # this builds a row vector from an array =head4 Method $matrix->proj - + Provides behind the scenes calculations for MathObject Matrix->proj + Deprecated for direct use in favor of methods of MathObject matrix + =cut sub proj{ @@ -251,6 +273,8 @@ sub proj{ =head4 Method $matrix->proj_coeff + Provides behind the scenes calculations for MathObject Matrix->proj_coeff + Deprecated for direct use in favor of methods of MathObject matrix =cut @@ -271,6 +295,8 @@ sub proj_coeff{ Method $matrix->new_column_matrix + Create column matrix from an ARRAY reference (list reference) + =cut sub new_column_matrix { @@ -293,6 +319,8 @@ sub new_column_matrix { vectors. Method $matrix->new_from_col_vecs + + Deprecated: The tools for creating MathObjects Matrices are simpler =cut @@ -343,8 +371,8 @@ sub new_from_col_vecs =head4 - Method $matrix->new_from_col_vecs - + Function: cp() + Provides ability to use complex numbers. =cut sub cp { # MEG makes new copies of complex number @@ -462,6 +490,8 @@ sub transpose Method $matrix->decompose_LR + Used by MathObjects Matrix for LR decomposition + Deprecated for direct use in PG problems. =cut sub decompose_LR diff --git a/lib/PGUtil.pm b/lib/PGUtil.pm index 9973195253..06dcee6c75 100644 --- a/lib/PGUtil.pm +++ b/lib/PGUtil.pm @@ -1,6 +1,6 @@ ############################################################################### # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/lib/PGcore.pm,v 1.6 2010/05/25 22:47:52 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/lib/PGalias.pm b/lib/PGalias.pm index dcbbabe921..8b4a5f8a87 100644 --- a/lib/PGalias.pm +++ b/lib/PGalias.pm @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/lib/PGalias.pm,v 1.6 2010/05/15 18:41:23 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under @@ -265,7 +265,7 @@ sub make_alias { or $ext eq 'js' or $ext eq 'nb' ) { - if ($displayMode =~ /^HTML/ ) { + if ($displayMode =~ /^HTML/ or $displayMode eq 'PTX') { $adr_output=$self->alias_for_html($aux_file_id, $ext); } elsif ($displayMode eq 'TeX') { ################################################################################ diff --git a/lib/PGanswergroup.pm b/lib/PGanswergroup.pm index 6616f545e3..14a8eff61c 100644 --- a/lib/PGanswergroup.pm +++ b/lib/PGanswergroup.pm @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/lib/PGanswergroup.pm,v 1.1 2010/05/14 11:39:02 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/lib/PGcore.pm b/lib/PGcore.pm index cc041d9fe1..5927faeb59 100755 --- a/lib/PGcore.pm +++ b/lib/PGcore.pm @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/lib/PGcore.pm,v 1.6 2010/05/25 22:47:52 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under @@ -527,7 +527,6 @@ sub store_persistent_data { # will store strings only (so far) my $self = shift; my $label = shift; my @content = @_; - $self->internal_debug_message("PGcore::store_persistent_data: storing $label in PERSISTENCE_HASH"); if (defined($self->{PERSISTENCE_HASH}->{$label}) ) { warn "can' overwrite $label in persistent data"; } else { diff --git a/lib/PGresource.pm b/lib/PGresource.pm index 3859c96a6d..8f318cd616 100644 --- a/lib/PGresource.pm +++ b/lib/PGresource.pm @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/lib/PGalias.pm,v 1.6 2010/05/15 18:41:23 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/lib/PGresponsegroup.pm b/lib/PGresponsegroup.pm index 334982b0f0..27d093efb7 100644 --- a/lib/PGresponsegroup.pm +++ b/lib/PGresponsegroup.pm @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/lib/PGresponsegroup.pm,v 1.2 2010/05/25 22:13:52 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/lib/Parser/BOP.pm b/lib/Parser/BOP.pm index 0950f64b01..ba3aa95ad2 100644 --- a/lib/Parser/BOP.pm +++ b/lib/Parser/BOP.pm @@ -175,6 +175,23 @@ sub checkMatrixSize { } else {$self->Error("Matrices are too deep to be multiplied")} } +# +# Check if a matrix is square +# +sub checkMatrixSquare { + my $self = shift; + my $m = shift; my $type = $m->{entryType}; + if ($type->{entryType}{name} eq 'Number') { + my ($r,$c) = ($m->{length},$type->{length}); + if ($r == $c) { + my $rowType = Value::Type('Matrix',$r,$Value::Type{number},formMatrix=>1); + $self->{type} = Value::Type('Matrix',$r,$rowType,formMatrix=>1); + return 1; + } + } + return 0; +} + # # Promote point operands to vectors or matrices. # diff --git a/lib/Parser/BOP/power.pm b/lib/Parser/BOP/power.pm index 78c1ca6403..78eb745395 100644 --- a/lib/Parser/BOP/power.pm +++ b/lib/Parser/BOP/power.pm @@ -17,7 +17,9 @@ sub _check { return if $self->checkNumbers(); my ($ltype,$rtype) = $self->promotePoints('Matrix'); if ($rtype->{name} eq 'Number') { - if ($ltype->{name} eq 'Matrix') {$self->checkMatrixSize($ltype,$ltype)} + if ($ltype->{name} eq 'Matrix') { + $self->Error("Only square matrices can be raised to a power") if !$self->checkMatrixSquare($ltype); + } elsif ($self->context->flag("allowBadOperands")) {$self->{type} = $Value::Type{number}} else {$self->Error("You can only raise a Number to a power")} } @@ -49,8 +51,12 @@ sub _reduce { if (($self->{rop}{isZero} && !$self->{lop}{isZero} && $reduce->{'x^0'}) || ($self->{lop}{isOne} && $reduce->{'1^x'})); return $self->{lop} if $self->{rop}{isOne} && $reduce->{'x^1'}; - if ($self->{rop}->isNeg && $self->{rop}->string eq '-1' && $reduce->{'x^(-1)'}) { - $self = $self->Item("BOP")->new($equation,'/',$self->Item("Number")->new($equation,1),$self->{lop}); + if ($self->{rop}->isNeg && $self->{rop}{isConstant} && + $self->{lop}->typeRef->{name} ne "Matrix" && $reduce->{'x^(-a)'}) { + my $copy = $self->copy($equation); + $copy->{rop} = $self->Item("Number")->new($equation,-($copy->{rop}->eval)); + $self = $self->Item("BOP")->new($equation,'/',$self->Item("Number")->new($equation,1), + ($copy->{rop}->string eq '1' ? $copy->{lop} : $copy)); $self = $self->reduce; } return $self; @@ -58,7 +64,7 @@ sub _reduce { $Parser::reduce->{'x^0'} = 1; $Parser::reduce->{'1^x'} = 1; -$Parser::reduce->{'x^(-1)'} = 1; +$Parser::reduce->{'x^(-a)'} = 1; $Parser::reduce->{'x^1'} = 1; diff --git a/lib/Parser/Function/numeric.pm b/lib/Parser/Function/numeric.pm index cfd2b842c4..18bf0fa461 100644 --- a/lib/Parser/Function/numeric.pm +++ b/lib/Parser/Function/numeric.pm @@ -52,6 +52,28 @@ sub log { CORE::log($_[0]); } +# +# Handle reduction of ln(e) and ln(e^x) +# +sub _reduce { + my $self = shift; + my $context = $self->context; + my $base10 = $context->flag('useBaseTenLog'); + my $reduce = $context->{reduction}; + if ($self->{name} eq 'ln' || ($self->{name} eq 'log' && !$base10)) { + my $arg = $self->{params}[0]; + if ($reduce->{'ln(e^x)'}) { + return $arg->{rop} if $arg->isa('Parser::BOP::power') && $arg->{lop}->string eq 'e'; + return $arg->{params}[0] if $arg->isa('Parser::Function') && $arg->{name} eq 'exp'; + } + return $self->Item('Value')->new($self->{equation}, [$self->eval]) + if $context->flag('reduceConstantFunctions') && $arg->string eq 'e'; + } + return $self; +} + +$Parser::reduce->{'ln(e^x)'} = 1; + # # Handle absolute values as a special case # diff --git a/lib/Parser/Legacy/NumberWithUnits.pm b/lib/Parser/Legacy/NumberWithUnits.pm index 22052634fa..49247724f9 100644 --- a/lib/Parser/Legacy/NumberWithUnits.pm +++ b/lib/Parser/Legacy/NumberWithUnits.pm @@ -59,8 +59,6 @@ sub new { } } - - Value::Error("You must provide a ".$self->name) unless defined($num); ($num,$units) = splitUnits($num) unless $units; Value::Error("You must provide units for your ".$self->name) unless $units; @@ -71,6 +69,8 @@ sub new { $num->{units} = $units; $num->{units_ref} = \%Units; $num->{isValue} = 1; + $num->{correct_ans} .= ' '.$units if defined $num->{correct_ans}; + $num->{correct_ans_latex_string} .= ' '.TeXunits($units) if defined $num->{correct_ans_latex_string}; bless $num, $class; } @@ -133,12 +133,14 @@ sub getUnits { # # Convert units to TeX format # (fix superscripts, put terms in \rm, +# escape percent, # and make a \frac out of fractions) # sub TeXunits { my $units = shift; $units =~ s/\^\(?([-+]?\d+)\)?/^{$1}/g; $units =~ s/\*/\\,/g; + $units =~ s/%/\\%/g; return '{\rm '.$units.'}' unless $units =~ m!^(.*)/(.*)$!; my $displayMode = WeBWorK::PG::Translator::PG_restricted_eval(q!$main::displayMode!); return '{\textstyle\frac{'.$1.'}{'.$2.'}}' if ($displayMode eq 'HTML_tth'); diff --git a/lib/Parser/List/Vector.pm b/lib/Parser/List/Vector.pm index 6b2458a33b..99378494d4 100644 --- a/lib/Parser/List/Vector.pm +++ b/lib/Parser/List/Vector.pm @@ -27,6 +27,7 @@ sub _check { sub ijk { my $self = shift; my $context = $self->context; my $method = shift || ($context->flag("StringifyAsTeX") ? 'TeX': 'string'); + my $precedence = shift || 0; my @coords = @{$self->{coords}}; $self->Error("Method 'ijk' can only be used on vectors in three-space") unless (scalar(@coords) <= 3); @@ -47,19 +48,20 @@ sub ijk { } } $string = $ijk[3] if $string eq ''; + $string = '('.$string.')' if $string =~ m/[-+]/ && $precedence > $context->operators->get('+')->{precedence}; return $string; } sub TeX { my $self = shift; - return $self->ijk("TeX") + return $self->ijk("TeX",@_) if $self->{ijk} || $self->{equation}{ijk} || $self->{equation}{context}->flag("ijk"); return $self->SUPER::TeX; } sub string { my $self = shift; - return $self->ijk("string") + return $self->ijk("string",@_) if $self->{ijk} || $self->{equation}{ijk} || $self->{equation}{context}->flag("ijk"); return $self->SUPER::string; } diff --git a/lib/Units.pm b/lib/Units.pm index 0a076c5c83..60b6582492 100644 --- a/lib/Units.pm +++ b/lib/Units.pm @@ -97,6 +97,9 @@ our %known_units = ('m' => { 'factor' => 1, 'cd' => 1, }, + '%' => { + 'factor' => 0.01, + }, # ANGLES # deg -- degrees # sr -- steradian, a mesure of solid angle @@ -105,6 +108,22 @@ our %known_units = ('m' => { 'factor' => 0.0174532925, 'rad' => 1 }, + 'degree' => { + 'factor' => 0.0174532925, + 'rad' => 1 + }, + 'degrees' => { + 'factor' => 0.0174532925, + 'rad' => 1 + }, + 'radian' => { + 'factor' => 1, + 'rad' => 1 + }, + 'radians' => { + 'factor' => 1, + 'rad' => 1 + }, 'sr' => { 'factor' => 1, 'rad' => 2 @@ -118,6 +137,18 @@ our %known_units = ('m' => { # yr -- years -- 365 days in a year # fortnight -- (FFF system) 2 weeks # + 'sec' => { + 'factor' => 1, + 's' => 1 + }, + 'second' => { + 'factor' => 1, + 's' => 1 + }, + 'seconds' => { + 'factor' => 1, + 's' => 1 + }, 'ms' => { 'factor' => 0.001, 's' => 1 @@ -126,10 +157,30 @@ our %known_units = ('m' => { 'factor' => 60, 's' => 1 }, + 'minute' => { + 'factor' => 60, + 's' => 1 + }, + 'minutes' => { + 'factor' => 60, + 's' => 1 + }, 'hr' => { 'factor' => 3600, 's' => 1 }, + 'hour' => { + 'factor' => 3600, + 's' => 1 + }, + 'hours' => { + 'factor' => 3600, + 's' => 1 + }, + 'h' => { + 'factor' => 3600, + 's' => 1 + }, 'day' => { 'factor' => 86400, 's' => 1 @@ -194,10 +245,26 @@ our %known_units = ('m' => { 'factor' => 0.0254, 'm' => 1 }, + 'inch' => { + 'factor' => 0.0254, + 'm' => 1 + }, + 'inches' => { + 'factor' => 0.0254, + 'm' => 1 + }, 'ft' => { 'factor' => 0.3048, 'm' => 1 }, + 'feet' => { + 'factor' => 0.3048, + 'm' => 1 + }, + 'foot' => { + 'factor' => 0.3048, + 'm' => 1 + }, 'mi' => { 'factor' => 1609.344, 'm' => 1 @@ -237,10 +304,18 @@ our %known_units = ('m' => { 'factor' => 1E-6, 'm' => 3, }, - 'dL' => { + 'dL' => { 'factor' => 0.0001, 'm' => 3 }, + 'cup' => { + 'factor' => 0.000236588, + 'm' => 3 + }, + 'cups' => { + 'factor' => 0.000236588, + 'm' => 3 + }, # VELOCITY # knots -- nautical miles per hour # c -- speed of light @@ -255,6 +330,11 @@ our %known_units = ('m' => { 'm' => 1, 's' => -1 }, + 'mph' => { + 'factor' => 0.44704, + 'm' => 1, + 's' => -1 + }, # MASS # mg -- miligrams # g -- grams diff --git a/lib/Value/List.pm b/lib/Value/List.pm index 66ead376c3..79994a2990 100644 --- a/lib/Value/List.pm +++ b/lib/Value/List.pm @@ -38,7 +38,7 @@ sub new { return $self->formula($p) if $isFormula; my $list = bless {data => $p, type => $type, context=>$context}, $class; $list->{correct_ans} = $p->[0]{correct_ans} - if $isSingleton && defined scalar(@{$p}) && defined $p->[0]{correct_ans}; + if $isSingleton && scalar(@{$p}) && defined $p->[0]{correct_ans}; if (scalar(@{$p}) == 0) { $list->{open} = $def->{nestedOpen}; $list->{close} = $def->{nestedClose}; diff --git a/lib/Value/Matrix.pm b/lib/Value/Matrix.pm index f2253c72a7..0d73cc32ec 100644 --- a/lib/Value/Matrix.pm +++ b/lib/Value/Matrix.pm @@ -3,6 +3,124 @@ # Implements the Matrix class. # # @@@ Still needs lots of work @@@ + +=head1 Value::Matrix class + + +References: + +MathObject Matrix methods: L +MathObject Contexts: L +CPAN RealMatrix docs: L + +Allowing Matrices in Fractions: +L + + Context()->parens->set("[" => {formMatrix => 1}); + +Files interacting with Matrices: + +L L + +L + +L -- checking whether vectors form a basis + +L -- tools for row reduction via elementary matrices + +L -- Generates unimodular matrices with real entries + +L + +L + +L + +L + +L + +L + +Contexts + + Matrix -- allows students to enter [[3,4],[3,6]] + -- formMatrix =>1 also allows this? + Complex-Matrix -- allows complex entries + +Creation methods + + $M1 = Matrix([1,2],[3,4]); + $M2 = Matrix([5,6],[7,8]); + $v = Vector(9,10); + $w = ColumnVector(9,10); # differs in how it is printed + +Commands added in Value::matrix + + Conversion + $matrix->values produces [[3,4,5],[1,3,4]] recursive array references of numbers (not MathObjects) + $matrix->wwMatrix produces CPAN MatrixReal1 matrix, used for computation subroutines + + Information + $matrix->dimension: ARRAY + + Access values + + row : MathObjectMatrix + column : MathObjectMatrix + element : Real or Complex value + + Assign values + + these need to be added: + +see C in MatrixReduce and L + + Advanced + $matrix->data: ARRAY reference (internal data) of MathObjects (Real,Complex, Fractions) + stored at each location. + + +Passthrough methods covering subroutines in Matrix.pm which overrides or +augment CPAN's MatrixReal1.pm. Matrix is a specialized subclass of MatrixReal1.pm + +The actual calculations for these methods are done in C + + trace + proj + proj_coeff + L + R + PL + PR + +Passthrough methods covering subroutines in C +(this has been modified to handle complex numbers) +The actual calculations are done in C subroutines +The commands below are Value::Matrix B unless otherwise noted. + + + + condition + det + inverse + is_symmetric + decompose_LR + dim + norm_one + norm_max + kleene + normalize + solve_LR($v) - LR decomposition + solve($M,$v) - function version of solve_LR + order_LR - order of LR decomposition matrix (number of non-zero equations)(also order() ) + order($M) - function version of order_LR + solve_GSM + solve_SSM + solve_RM + +=cut + # package Value::Matrix; my $pkg = 'Value::Matrix'; @@ -17,7 +135,7 @@ our @ISA = qw(Value); # a point, vector or matrix object, a matrix-valued formula, or a string # that evaluates to a matrix # -sub new { +sub new { #internal my $self = shift; my $class = ref($self) || $self; my $context = (Value::isContext($_[0]) ? shift : $self->context); my $M = shift; $M = [] unless defined $M; $M = [$M,@_] if scalar(@_) > 0; @@ -38,7 +156,7 @@ sub new { # (Recursively) make a matrix from a list of array refs # and report errors about the entry types # -sub matrixMatrix { +sub matrixMatrix { #internal my $self = shift; my $class = ref($self) || $self; my $context = shift; my ($x,$m); my @M = (); my $isFormula = 0; @@ -62,7 +180,7 @@ sub matrixMatrix { # Form a 1 x n matrix from a list of numbers # (could become a row of an m x n matrix) # -sub numberMatrix { +sub numberMatrix { #internal my $self = shift; my $class = ref($self) || $self; my $context = shift; my @M = (); my $isFormula = 0; @@ -209,7 +327,7 @@ sub mult { # # Constant multiplication # - if (Value::matchNumber($r) || Value::isComplex($r)) { + if (Value::isNumber($r)) { my @coords = (); foreach my $x (@{$l->data}) {push(@coords,$x*$r)} return $self->make(@coords); @@ -225,7 +343,7 @@ sub mult { if (scalar(@dl) == 1) {@dl = (1,@dl); $l = $self->make($l)} if (scalar(@dr) == 1) {@dr = (@dr,1); $r = $self->make($r)->transpose} Value::Error("Can only multiply 2-dimensional matrices") if scalar(@dl) > 2 || scalar(@dr) > 2; - Value::Error("Matices of dimensions %dx%d and %dx%d can't be multiplied",@dl,@dr) + Value::Error("Matrices of dimensions %dx%d and %dx%d can't be multiplied",@dl,@dr) unless ($dl[1] == $dr[0]); # # Do matrix multiplication @@ -247,8 +365,7 @@ sub mult { sub div { my ($l,$r,$flag) = @_; my $self = $l; Value::Error("Can't divide by a Matrix") if $flag; - Value::Error("Matrices can only be divided by Numbers") - unless (Value::matchNumber($r) || Value::isComplex($r)); + Value::Error("Matrices can only be divided by Numbers") unless Value::isNumber($r); Value::Error("Division by zero") if $r == 0; my @coords = (); foreach my $x (@{$l->data}) {push(@coords,$x/$r)} @@ -260,7 +377,10 @@ sub power { Value::Error("Can't use Matrices in exponents") if $flag; Value::Error("Only square matrices can be raised to a power") unless $l->isSquare; $r = Value::makeValue($r,context=>$context); - if ($r->isNumber && $r =~ m/^-\d+$/) {$l = $l->inverse; $r = -$r} + if ($r->isNumber && $r =~ m/^-\d+$/) { + $l = $l->inverse; $r = -$r; + $self->Error("Matrix is not invertible") unless defined($l); + } Value::Error("Matrix powers must be non-negative integers") unless $r->isNumber && $r =~ m/^\d+$/; return $context->Package("Matrix")->I($l->length,$context) if $r == 0; my $M = $l; foreach my $i (2..$r) {$M = $M*$l} @@ -321,9 +441,10 @@ sub I { Value::Error("You must provide a dimension for the Identity matrix") unless defined $d; Value::Error("Dimension must be a positive integer") unless $d =~ m/^[1-9]\d*$/; my @M = (); my @Z = split('',0 x $d); + my $REAL = $context->Package('Real'); foreach my $i (0..$d-1) { my @row = @Z; $row[$i] = 1; - push(@M,$self->make($context,@row)); + push(@M,$self->make($context, map {$REAL->new($_)} @row)); } return $self->make($context,@M); } @@ -435,7 +556,8 @@ sub det { sub inverse { my $self = shift; $self->wwMatrixLR; Value->Error("Can't take inverse of non-square matrix") unless $self->isSquare; - return $self->new($self->{lrM}->invert_LR); + my $I = $self->{lrM}->invert_LR; + return (defined($I) ? $self->new($I) : $I); } sub decompose_LR { @@ -476,7 +598,9 @@ sub solve_LR { my $self = shift; my $v = $self->wwColumnVector(shift); my ($d,$b,$M) = $self->wwMatrixLR->solve_LR($v); - return ($d,$self->new($b),$self->new($M)); + $b = $self->new($b) if defined($b); + $M = $self->new($M) if defined($M); + return ($d,$b,$M); } sub condition { @@ -486,7 +610,7 @@ sub condition { } sub order {shift->order_LR(@_)} -sub order_LR { +sub order_LR { # order of LR decomposition matrix (number of non-zero equations) my $self = shift; return $self->wwMatrixLR->order_LR; } diff --git a/lib/Value/Point.pm b/lib/Value/Point.pm index 6ab10d25bf..9d508ef265 100644 --- a/lib/Value/Point.pm +++ b/lib/Value/Point.pm @@ -81,8 +81,7 @@ sub sub { sub mult { my ($l,$r) = @_; my $self = $l; - Value::Error("Points can only be multiplied by Numbers") - unless (Value::matchNumber($r) || Value::isComplex($r)); + Value::Error("Points can only be multiplied by Numbers") unless Value::isNumber($r); my @coords = (); foreach my $x ($l->value) {push(@coords,$x*$r)} return $self->make(@coords); @@ -91,8 +90,7 @@ sub mult { sub div { my ($l,$r,$flag) = @_; my $self = $l; Value::Error("Can't divide by a Point") if $flag; - Value::Error("Points can only be divided by Numbers") - unless (Value::matchNumber($r) || Value::isComplex($r)); + Value::Error("Points can only be divided by Numbers") unless Value::isNumber($r); Value::Error("Division by zero") if $r == 0; my @coords = (); foreach my $x ($l->value) {push(@coords,$x/$r)} diff --git a/lib/Value/Vector.pm b/lib/Value/Vector.pm index 16116a2dab..52ac31f79f 100644 --- a/lib/Value/Vector.pm +++ b/lib/Value/Vector.pm @@ -92,8 +92,7 @@ sub sub { sub mult { my ($l,$r,$flag) = @_; my $self = $l; - Value::Error("Vectors can only be multiplied by Numbers") - unless (Value::matchNumber($r) || Value::isComplex($r)); + Value::Error("Vectors can only be multiplied by Numbers") unless Value::isNumber($r); my @coords = (); foreach my $x ($l->value) {push(@coords,$x*$r)} return $self->make(@coords); @@ -102,8 +101,7 @@ sub mult { sub div { my ($l,$r,$flag) = @_; my $self = $l; Value::Error("Can't divide by a Vector") if $flag; - Value::Error("Vectors can only be divided by Numbers") - unless (Value::matchNumber($r) || Value::isComplex($r)); + Value::Error("Vectors can only be divided by Numbers") unless Value::isNumber($r); Value::Error("Division by zero") if $r == 0; my @coords = (); foreach my $x ($l->value) {push(@coords,$x/$r)} diff --git a/lib/WeBWorK/PG/Translator.pm b/lib/WeBWorK/PG/Translator.pm index c3028b3b9f..1779bdc20c 100644 --- a/lib/WeBWorK/PG/Translator.pm +++ b/lib/WeBWorK/PG/Translator.pm @@ -1858,8 +1858,8 @@ sub default_preprocess_code { $evalString =~ s/\n\h*END_PGML_HINT[\h;]*\n/\nEND_PGML_HINT\n/g; $evalString =~ s/\n\h*END_SOLUTION[\h;]*\n/\nEND_SOLUTION\n/g; $evalString =~ s/\n\h*END_HINT[\h;]*\n/\nEND_HINT\n/g; - $evalString =~ s/\n\h*BEGIN_TEXT[\h;]*\n/\nTEXT\(EV3P\(<<'END_TEXT'\)\);\n/g; - $evalString =~ s/\n\h*BEGIN_PGML[\h;]*\n/\nTEXT\(PGML::Format2\(<<'END_PGML'\)\);\n/g; + $evalString =~ s/\n\h*BEGIN_TEXT[\h;]*\n/\nSTATEMENT\(EV3P\(<<'END_TEXT'\)\);\n/g; + $evalString =~ s/\n\h*BEGIN_PGML[\h;]*\n/\nSTATEMENT\(PGML::Format2\(<<'END_PGML'\)\);\n/g; $evalString =~ s/\n\h*BEGIN_PGML_SOLUTION[\h;]*\n/\nSOLUTION\(PGML::Format2\(<<'END_PGML_SOLUTION'\)\);\n/g; $evalString =~ s/\n\h*BEGIN_PGML_HINT[\h;]*\n/\nHINT\(PGML::Format2\(<<'END_PGML_HINT'\)\);\n/g; $evalString =~ s/\n\h*BEGIN_SOLUTION[\h;]*\n/\nSOLUTION\(EV3P\(<<'END_SOLUTION'\)\);\n/g; diff --git a/macros/AppletObjects.pl b/macros/AppletObjects.pl index 055df41fe4..cbb4d910e9 100644 --- a/macros/AppletObjects.pl +++ b/macros/AppletObjects.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/AppletObjects.pl,v 1.24 2010/01/03 17:13:46 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under @@ -334,7 +334,7 @@ sub insertAll { ## inserts both header text and object text # Return HTML or TeX strings to be included in the body of the page ########################## - return main::MODES(TeX=>' {\bf applet } ', HTML=>$self->insertObject.$main::BR.$state_storage_html_code.$answerBox_code); + return main::MODES(TeX=>' {\bf applet } ', HTML=>$self->insertObject.$main::BR.$state_storage_html_code.$answerBox_code, PTX=>' applet '); } =head3 Example problem diff --git a/macros/CanvasObject.pl b/macros/CanvasObject.pl index fe77c19efd..4190ac2fa6 100644 --- a/macros/CanvasObject.pl +++ b/macros/CanvasObject.pl @@ -172,7 +172,7 @@ sub insertCanvas { my $myWidth = shift() || 200; my $myHeight = shift() ||200; - $canvasObject = MODES(TeX=>"canvasObject",HTML=><"canvasObject", PTX=>" canvas object ", HTML=>< var canvasWidth = $myWidth; var canvasHeight = $myHeight; END_CANVAS @@ -181,7 +181,7 @@ sub insertCanvas { } sub insertYvaluesInputBox { - $yValuesInput = MODES(TeX=>"yVAluesInput",HTML=><"yValuesInput", PTX=>" y-values input ", HTML=>< Y-values: @@ -193,7 +193,7 @@ sub insertYvaluesInputBox { } sub insertGridButtons { - $gridButtons = MODES(TeX=>"gridButtons",HTML=><"gridButtons", PTX=>" grid buttons ", HTML=><Toggle Grid @@ -406,7 +406,7 @@ sub insertGridButtons { # } sub insertPointsArea { - $pointsArea = MODES(TeX=>"pointsArea",HTML=><"pointsArea", PTX=>" points area ", HTML=><Get Points
EOF diff --git a/macros/LinearProgramming.pl b/macros/LinearProgramming.pl index 4456f6266a..d2e2fa2c38 100644 --- a/macros/LinearProgramming.pl +++ b/macros/LinearProgramming.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/LiveGraphics3D.pl b/macros/LiveGraphics3D.pl index e829b0ae85..f0cc1e4a13 100644 --- a/macros/LiveGraphics3D.pl +++ b/macros/LiveGraphics3D.pl @@ -87,7 +87,7 @@ sub LiveGraphics3D { size => [250,250], jar => "live.jar", # "${htmlURL}live.jar", codebase => findAppletCodebase("live.jar"), - # codebase => "http://hosted2.webwork.rochester.edu/webwork2_files/applets/", # used for testing + # codebase => "https://demo.webwork.rochester.edu/webwork2_files/applets/", # used for testing background => "#FFFFFF", scale => 1., tex_size => 500, diff --git a/macros/MathObjects.pl b/macros/MathObjects.pl index ea24bfc1b1..b1a17b7fe4 100644 --- a/macros/MathObjects.pl +++ b/macros/MathObjects.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/MatrixCheckers.pl b/macros/MatrixCheckers.pl index 6f4c963173..3b57748745 100644 --- a/macros/MatrixCheckers.pl +++ b/macros/MatrixCheckers.pl @@ -87,23 +87,23 @@ =head1 DESCRIPTION are produced by C<\(\Bigg\lbrace\)> and C<\(\Bigg\rbrace\)>, are a matter of personal preference (since a basis is an ordered set, I like to include braces). -=over 12 -Context()->texStrings; -BEGIN_TEXT -Find an orthonormal basis for... -$BR -$BR -$BCENTER -\(\Bigg\lbrace\) -\{ $multians->ans_array(15) \}, -\{ $multians->ans_array(15) \} -\(\Bigg\rbrace.\) -$ECENTER -END_TEXT -Context()->normalStrings; -=back + Context()->texStrings; + BEGIN_TEXT + Find an orthonormal basis for... + $BR + $BR + $BCENTER + \(\Bigg\lbrace\) + \{ $multians->ans_array(15) \}, + \{ $multians->ans_array(15) \} + \(\Bigg\rbrace.\) + $ECENTER + END_TEXT + Context()->normalStrings; + + The answer evaluation section of the PG file is totally standard. @@ -144,7 +144,7 @@ sub concatenate_columns_into_matrix { for my $column (@c) { push(@temp,Matrix($column)->transpose->row(1)); } - return Matrix(@temp)->transpose; + return Matrix(\@temp)->transpose; } @@ -314,8 +314,8 @@ sub basis_checker_rows { return 0 if scalar(@s) < scalar(@c); # count the number of vector inputs # These two lines are what is different from basis_checker_columns - my $C = Matrix(@c)->transpose; # put the rows of @c into columns of $C. - my $S = Matrix(@s)->transpose; # put the rows of @s into columns of $S. + my $C = Matrix(\@c)->transpose; # put the rows of @c into columns of $C. + my $S = Matrix(\@s)->transpose; # put the rows of @s into columns of $S. # Put $C and $S into the local context so that # all of the computations that follow will also be in @@ -362,8 +362,8 @@ sub orthonormal_basis_checker_rows { return 0 if scalar(@s) < scalar(@c); # count the number of vector inputs # These two lines are what is different from basis_checker_columns - my $C = Matrix(@c)->transpose; # put the rows of @c into columns of $C. - my $S = Matrix(@s)->transpose; # put the rows of @s into columns of $S. + my $C = Matrix(\@c)->transpose; # put the rows of @c into columns of $C. + my $S = Matrix(\@s)->transpose; # put the rows of @s into columns of $S. # Put $C and $S into the local context so that # all of the computations that follow will also be in diff --git a/macros/MatrixReduce.pl b/macros/MatrixReduce.pl index 010d732269..57e64e4f24 100644 --- a/macros/MatrixReduce.pl +++ b/macros/MatrixReduce.pl @@ -14,27 +14,49 @@ =head1 SYNOPSIS =over 12 -=item Get the reduced row echelon form: C<$Areduced = rref($A);> Should be used in the fraction context with all entries of $A made into fractions. +=item Get the reduced row echelon form: C<$Areduced = rref($A);> + +Should be used in the fraction context with all entries of $A made into fractions. -=item Make matrix entries do fraction arithmetic (rather than decimal arithmetic): After selecting the Fraction context using Context('Fraction')->parens->set("[" => {formMatrix => 1}), C<$A = apply_fraction_to_matrix_entries($A);> applies Fraction() to all of the entries of $A, which makes subsequent matrix algebra computations with $A use fraction arithmetic. +=item Make matrix entries do fraction arithmetic (rather than decimal arithmetic): + +After selecting the Fraction context using Context('Fraction')->parens->set("[" => {formMatrix => 1}), C<$A = apply_fraction_to_matrix_entries($A);> applies Fraction() to all of the entries of $A, which makes subsequent matrix algebra computations with $A use fraction arithmetic. =item Get the reduced column echelon form: C<$Areduced = rcef($A);> -=item Change the value of a matrix entry: C changes the [2,3] entry to the value 50. +=item Change the value of a matrix entry: C + +changes the [2,3] entry to the value 50. + +=item Construct an n x n identity matrix: C<$E = identity_matrix(5);> + +(This is an alias for Value::Matrix->I(5);) + +=item Construct an n x n elementary matrix that will permute rows i and j: + +C<$E = elem_matrix_row_switch(5,2,4);> creates a 5 x 5 identity matrix and swaps rows 2 and 4. + +=item Construct an n x n elementary matrix that will multiply row i by s: C<$E = elem_matrix_row_mult(5,2,4);> -=item Construct an n x n identity matrix: C<$E = identity_matrix(5);> +creates a 5 x 5 identity matrix and swaps puts 4 in the second spot on the diagonal. -=item Construct an n x n elementary matrix that will permute rows i and j: C<$E = elem_matrix_row_switch(5,2,4);> creates a 5 x 5 identity matrix and swaps rows 2 and 4. -=item Construct an n x n elementary matrix that will multiply row i by s: C<$E = elem_matrix_row_mult(5,2,4);> creates a 5 x 5 identity matrix and swaps puts 4 in the second spot on the diagonal. +=item Construct an n x n elementary matrix that will multiply row i by s: C<$E = elem_matrix_row_mult(5,2,4);> creates a 5 x 5 identity matrix and puts 4 in the second spot on the diagonal. -=item Construct an n x n elementary matrix that will multiply row i by s: C<$E3 = elem_matrix_row_add(5,3,1,35);> creates a 5 x 5 identity matrix and swaps puts 35 in the (3,1) position. +=item Construct an n x n elementary matrix that will add s times row j to row i: C<$E3 = elem_matrix_row_add(5,3,1,35);> creates a 5 x 5 identity matrix and puts 35 in the (3,1) position. -=item Perform the row switch transform that swaps (row i) with (row j): C<$Areduced = row_switch($A,2,4);> swaps rows 2 and 4 in matrix $A. -=item Perform the row multiplication transform s * (row i) placed into (row i): C<$Areduced = row_mult(A,2,10);> multiplies every entry in row 2 of $A by 10. +=item Perform the row switch transform that swaps (row i) with (row j): C<$Areduced = row_switch($A,2,4);> -=item Perform the row addition transform (row i) + s * (row j) placed into (row i): C<$Areduced = row_add($A,2,1,10);> adds 10 times row 1 to row 2 and places the result in row 2. (Same as constructing $E to be the identity with 10 placed in entry (2,1), then multiplying $E * $A.) +swaps rows 2 and 4 in matrix $A. + +=item Perform the row multiplication transform s * (row i) placed into (row i): C<$Areduced = row_mult(A,2,10);> + +multiplies every entry in row 2 of $A by 10. + +=item Perform the row addition transform (row i) + s * (row j) placed into (row i): C<$Areduced = row_add($A,2,1,10);> + +adds 10 times row 1 to row 2 and places the result in row 2. (Same as constructing $E to be the identity with 10 placed in entry (2,1), then multiplying $E * $A.) =back @@ -42,61 +64,59 @@ =head1 DESCRIPTION Usage: -=over 12 - -DOCUMENT(); -loadMacros( -"PGstandard.pl", -"MathObjects.pl", -"MatrixReduce.pl", # automatically loads contextFraction.pl and MathObjects.pl -"PGcourse.pl", -); -$showPartialCorrectAnswers = 0; -TEXT(beginproblem()); + DOCUMENT(); + loadMacros( + "PGstandard.pl", + "MathObjects.pl", + "MatrixReduce.pl", # automatically loads contextFraction.pl and MathObjects.pl + "PGcourse.pl", + ); + $showPartialCorrectAnswers = 0; + TEXT(beginproblem()); -# Context('Matrix'); # for decimal arithmetic -Context('Fraction'); # for fraction arithmetic + # Context('Matrix'); # for decimal arithmetic + Context('Fraction'); # for fraction arithmetic -$A = Matrix([ -[random(-5,5,1),random(-5,5,1),random(-5,5,1),3], -[random(-5,5,1),random(-5,5,1),random(-5,5,1),0.75], -[random(-5,5,1),random(-5,5,1),random(-5,5,1),9/4], -]); + $A = Matrix([ + [random(-5,5,1),random(-5,5,1),random(-5,5,1),3], + [random(-5,5,1),random(-5,5,1),random(-5,5,1),0.75], + [random(-5,5,1),random(-5,5,1),random(-5,5,1),9/4], + ]); -$A = apply_fraction_to_matrix_entries($A); # try commenting this line out for different results + $A = apply_fraction_to_matrix_entries($A); # try commenting this line out for different results -$Arref = rref($A); + $Arref = rref($A); -$Aswitch = row_switch($A, 2, 3); + $Aswitch = row_switch($A, 2, 3); -$Amult = row_mult($A, 2, 4); + $Amult = row_mult($A, 2, 4); -$Aadd = row_add($A, 2, 1, 10); + $Aadd = row_add($A, 2, 1, 10); -$E = elem_matrix_row_add(3,2,1,10); -$EA = $E * $A; + $E = elem_matrix_row_add(3,2,1,10); + $EA = $E * $A; -$E1 = elem_matrix_row_switch(5,2,4); -$E2 = elem_matrix_row_mult(5,4,Fraction(1/10)); -$E3 = elem_matrix_row_add(5,3,1,35); -$E4 = identity_matrix(4); -change_matrix_entry($E4,[3,2],10); + $E1 = elem_matrix_row_switch(5,2,4); + $E2 = elem_matrix_row_mult(5,4,Fraction(1/10)); + $E3 = elem_matrix_row_add(5,3,1,35); + $E4 = identity_matrix(4); + change_matrix_entry($E4,[3,2],10); -Context()->texStrings; -BEGIN_TEXT -The original matrix and its row reduced echelon form: -\[ $A \sim $Arref. \] -$BR -The original matrix with rows switched, multiplied, or added together: -\[ $Aswitch, $Amult, $Aadd. \] -$BR -Some elementary matrices. -\[$E1, $E2, $E3, $E4\] -END_TEXT -Context()->normalStrings; + Context()->texStrings; + BEGIN_TEXT + The original matrix and its row reduced echelon form: + \[ $A \sim $Arref. \] + $BR + The original matrix with rows switched, multiplied, or added together: + \[ $Aswitch, $Amult, $Aadd. \] + $BR + Some elementary matrices. + \[$E1, $E2, $E3, $E4\] + END_TEXT + Context()->normalStrings; -COMMENT('MathObject version.'); -ENDDOCUMENT(); + COMMENT('MathObject version.'); + ENDDOCUMENT(); =back @@ -134,14 +154,14 @@ sub rref { my $M = shift; my @m = $M->value; my @m_reduced = rref_perl_array(@m); - return Matrix(@m_reduced); + return Matrix(\@m_reduced); } sub rcef { my $M = shift; my @m = $M->transpose->value; my @m_reduced = rref_perl_array(@m); - return Matrix(@m_reduced)->transpose; + return Matrix(\@m_reduced)->transpose; } sub rref_perl_array { @@ -204,7 +224,7 @@ sub elem_matrix_row_switch { # $n = number of rows (and columns) in matrix # $i and $j are indices of rows to be switched (index starting at 1, not 0) - ($n,$i,$j) = @_; + my ($n,$i,$j) = @_; if ($i < 1 or $j < 1 or $i > $n or $j > $n) { warn "Index out of bounds in Elem_row_switch(). Returning identity matrix."; return Value::Matrix->I($n); @@ -212,7 +232,7 @@ sub elem_matrix_row_switch { my $M = Value::Matrix->I($n); # construct identity matrix my @m = $M->value; @m[$i - 1, $j - 1] = @m[$j - 1, $i - 1]; # switch rows - return Matrix(@m); + return Matrix(\@m); } @@ -222,7 +242,7 @@ sub elem_matrix_row_mult { # $n = number of rows (and columns) in matrix # $i and $j are indices of rows to be switched (index starting at 1, not 0) - ($n,$i,$s) = @_; + my ($n,$i,$s) = @_; if ($i < 1 or $i > $n) { warn "Index out of bounds in elem_row_mult(). Returning identity matrix."; return Value::Matrix->I($n); @@ -234,7 +254,7 @@ sub elem_matrix_row_mult { my $M = Value::Matrix->I($n); # construct identity matrix my @m = $M->value; foreach my $rowval ( @{$m[$i - 1]} ) { $rowval *= $s; } - return Matrix(@m); + return Matrix(\@m); } @@ -244,7 +264,7 @@ sub elem_matrix_row_add { # $n = number of rows (and columns) in matrix # $i and $j are indices of rows to be switched (index starting at 1, not 0) - ($n,$i,$j,$s) = @_; + my ($n,$i,$j,$s) = @_; if ($i < 1 or $j < 1 or $i > $n or $j > $n) { warn "Index out of bounds in elem_matrix_row_add(). Returning identity matrix."; return Value::Matrix->I($n); @@ -256,7 +276,7 @@ sub elem_matrix_row_add { my $M = Value::Matrix->I($n); # construct identity matrix my @m = $M->value; $m[$i - 1][$j - 1] = $s; - return Matrix(@m); + return Matrix(\@m); } @@ -290,7 +310,7 @@ sub row_add { foreach my $k (0..$c-1) { $m[$i - 1][$k] += $s * $m[$j - 1][$k]; } - return Matrix(@m); + return Matrix(\@m); } @@ -308,7 +328,7 @@ sub row_switch { } my @m = $M->value; @m[$i1 - 1,$i2 - 1] = @m[$i2 - 1,$i1 - 1]; - return Matrix(@m); + return Matrix(\@m); } @@ -327,7 +347,7 @@ sub row_mult { if ($s == 0 and $permissionLevel >= 10) { warn "Scaling a row by zero is not a valid row operation. (This warning is only shown to professors.)"; } my @m = $M->value; foreach my $rowval ( @{$m[$i - 1]} ) { $rowval *= $s; } # row multiplication - return Matrix(@m); + return Matrix(\@m); } @@ -341,7 +361,7 @@ sub apply_fraction_to_matrix_entries { foreach my $i (0..$r-1) { foreach my $rowval ( @{$m[$i]} ) { $rowval = Fraction("$rowval"); } } - return Matrix(@m); + return Matrix(\@m); } diff --git a/macros/MatrixUnits.pl b/macros/MatrixUnits.pl index 810d06f5ee..36959d0402 100644 --- a/macros/MatrixUnits.pl +++ b/macros/MatrixUnits.pl @@ -38,7 +38,7 @@ =head1 DESCRIPTION Note that the indexing on MathObject matrices starts at 1, while the indexing on perl arrays starts at 0, so that C<$A-element(1,1);> corresponds to C<$a[0][0];>. The perl arrays can be made into MathObject matrices by -C<$A = Matrix(@a);>, and this is, in fact, what the C and C +C<$A = Matrix(\@a);>, and this is, in fact, what the C and C subroutines do for you. The perl versions C<@a = GLnZ_perl()> and C<@a = SLnZ_perl()> are useful if you want to have quick access to the matrix values (as perl reals stored in C<@a>) without having to pull them out of a @@ -65,7 +65,7 @@ =head1 AUTHORS sub GL2Z { my @a = GL2Z_perl(); - return Matrix(@a); + return Matrix(\@a); } sub GL2Z_perl { @@ -88,7 +88,7 @@ sub GL2Z_perl { sub SL2Z { my @a = SL2Z_perl(); - return Matrix(@a); + return Matrix(\@a); } sub SL2Z_perl { @@ -116,7 +116,7 @@ sub SL2Z_perl { sub GL3Z { my @a = GL3Z_perl(); - return Matrix(@a); + return Matrix(\@a); } sub GL3Z_perl { @@ -151,7 +151,7 @@ sub GL3Z_perl { sub SL3Z { my @a = SL3Z_perl(); - return Matrix(@a); + return Matrix(\@a); } sub SL3Z_perl { @@ -190,7 +190,7 @@ sub SL3Z_perl { sub GL4Z { my @a = GL4Z_perl(); - return Matrix(@a); + return Matrix(\@a); } @@ -243,7 +243,7 @@ sub GL4Z_perl { sub SL4Z { my @a = SL4Z_perl(); - return Matrix(@a); + return Matrix(\@a); } sub SL4Z_perl { diff --git a/macros/PG.pl b/macros/PG.pl index b36e3f617e..4e59dc5fb2 100644 --- a/macros/PG.pl +++ b/macros/PG.pl @@ -763,7 +763,7 @@ sub includePGproblem { ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/PG.pl,v 1.46 2010/05/27 02:22:51 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/PGML.pl b/macros/PGML.pl index d773fcdbd1..e7971e1086 100644 --- a/macros/PGML.pl +++ b/macros/PGML.pl @@ -44,8 +44,8 @@ package PGML::Parse; my $emphasis = '\*+|_+'; my $chars = '\\\\.|[{}[\]\'"]'; my $ansrule = '\[(?:_+|[ox^])\]\*?'; -my $open = '\[(?:[!<%@$]|::?|``?|\|+ ?)'; -my $close = '(?:[!>%@$]|::?|``?| ?\|+)\]'; +my $open = '\[(?:[!<%@$]|::?:?|``?`?|\|+ ?)'; +my $close = '(?:[!>%@$]|::?:?|``?`?| ?\|+)\]'; my $noop = '\[\]'; my $splitPattern = @@ -276,7 +276,7 @@ sub ForceBreak { sub Par { my $self = shift; my $token = shift; - $self->End; + $self->End(null, shift); $self->Item("par",$token,{noIndent => 1}); $self->{atLineStart} = $self->{ignoreNL} = 1; $self->{indent} = $self->{actualIndent} = 0; @@ -357,7 +357,7 @@ sub Rule { sub Bullet { my $self = shift; my $token = shift; my $bullet = shift; return $self->Text($token) unless $self->{atLineStart}; - $bullet = {'*'=>'bullet', '+'=>'square', 'o'=>'circle', '-'=>'bullet'}->{substr($token,0,1)} if $bullet eq 'bullet'; + $bullet = {'*'=>'disc', '+'=>'square', 'o'=>'circle', '-'=>'bullet'}->{substr($token,0,1)} if $bullet eq 'bullet'; my $block = $self->{block}; if ($block->{type} ne 'root' && !$block->{align}) { while ($block->{type} ne 'root' && !$block->{prev}{align}) {$block = $block->{prev}} @@ -454,11 +454,16 @@ sub NOOP { parsed=>1, allowStar=>1, allowDblStar=>1, allowTriStar=>1, options=>["context","reduced"]}, "[::" => {type=>'math', parseComments=>1, parseSubstitutions=>1, terminator=>qr/::\]/, terminateMethod=>'terminateGetString', + parsed=>1, allowStar=>1, allowDblStar=>1, allowTriStar=>1, displaystyle=>1, options=>["context","reduced"]}, + "[:::" => {type=>'math', parseComments=>1, parseSubstitutions=>1, + terminator=>qr/:::\]/, terminateMethod=>'terminateGetString', parsed=>1, allowStar=>1, allowDblStar=>1, allowTriStar=>1, display=>1, options=>["context","reduced"]}, "[`" => {type=>'math', parseComments=>1, parseSubstitutions=>1, terminator=>qr/\`\]/, terminateMethod=>'terminateGetString',}, "[``" => {type=>'math', parseComments=>1, parseSubstitutions=>1, - terminator=>qr/\`\`\]/, terminateMethod=>'terminateGetString', display=>1}, + terminator=>qr/\`\`\]/, terminateMethod=>'terminateGetString', displaystyle=>1}, + "[```" => {type=>'math', parseComments=>1, parseSubstitutions=>1, + terminator=>qr/\`\`\`\]/, terminateMethod=>'terminateGetString', display=>1}, "[!" => {type=>'image', parseComments=>1, parseSubstitutions=>1, terminator=>qr/!\]/, terminateMethod=>'terminateGetString', cancelNL=>1, options=>["title"]}, @@ -532,7 +537,7 @@ sub terminatePre { my $self = shift; my $token = shift; $self->{block}{terminator} = ''; # we add the ending token to the text below if ($token =~ m/\n\n/) { - $self->Par($token); + $self->Par($token, $self->{block}); $self->{block} = $self->{block}{prev}; } else { $self->Text($token); @@ -966,8 +971,9 @@ sub Math { $obj = $obj->reduce if $item->{reduced}; $math = $obj->TeX; } - $math = "\\displaystyle{$math}" if $item->{display}; - return $math; + $math = "\\displaystyle{$math}" if $item->{displaystyle}; + my $mathmode = ($item->{display}) ? 'display' : 'inline' ; + return ($math,$mathmode); } sub Answer { @@ -1094,6 +1100,7 @@ sub Align { Alpha => 'ol type="A"', roman => 'ol type="i"', Roman => 'ol type="I"', + disc => 'ul type="disc"', circle => 'ul type="circle"', square => 'ul type="square"', ); @@ -1187,7 +1194,7 @@ sub Verbatim { sub Math { my $self = shift; - return main::math_ev3($self->SUPER::Math(@_)); + return main::general_math_ev3($self->SUPER::Math(@_)); } ###################################################################### @@ -1327,11 +1334,139 @@ sub Verbatim { return $text; } + sub Math { my $self = shift; - return main::math_ev3($self->SUPER::Math(@_)); + return main::general_math_ev3($self->SUPER::Math(@_)); +} + +###################################################################### +###################################################################### + +package PGML::Format::ptx; +our @ISA = ('PGML::Format'); + +sub Escape { + my $self = shift; + my $string = shift; return "" unless defined $string; + $string = main::PTX_special_character_cleanup($string); + return $string; } +# No indentation for PTX +sub Indent { + my $self = shift; my $item = shift; + return $self->string($item); +} + +# No align for PTX +sub Align { + my $self = shift; my $item = shift; + return $self->string($item); +} + +my %bullet = ( + bullet => 'ul', + numeric => 'ol label="1."', + alpha => 'ol label="a."', + Alpha => 'ol label="A."', + roman => 'ol label="i."', + Roman => 'ol label="I."', + disc => 'ul label="disc"', + circle => 'ul label="circle"', + square => 'ul label="square"', +); +sub List { + my $self = shift; my $item = shift; + my $list = $bullet{$item->{bullet}}; + return + $self->nl . + '<'.$list.'>'."\n" . + $self->string($item) . + $self->nl . + "\n"; +} + +sub Bullet { + my $self = shift; my $item = shift; + return $self->nl.'
  • '.$self->string($item).'
  • '; +} + +sub Code { + my $self = shift; my $item = shift; + my $class = ($item->{class} ? ' class="'.$item->{class}.'"' : ""); + return $self->nl . + "\n" . + join("<\/cline>\n", split(/\n/,$self->string($item))) . + "<\/cline>\n<\/cd>\n"; +} + +sub Pre { + my $self = shift; my $item = shift; + return + $self->nl . + '
    ' .
    +    $self->string($item) .
    +    "
    \n"; +} + +# PreTeXt can't use headings. +sub Heading { + my $self = shift; my $item = shift; + my $n = $item->{n}; + my $text = $self->string($item); + $text =~ s/^ +| +$//gm; $text =~ s! +(
    )!$1!g; + return $text."\n"; +} + +sub Par { + my $self = shift; my $item = shift; + return $self->nl."\n"; +} + +sub Break {"\n\n"} + +sub Bold { + my $self = shift; my $item = shift; + return ''.$self->string($item).''; +} + +sub Italic { + my $self = shift; my $item = shift; + return ''.$self->string($item).''; +} + +our %openQuote = ('"' => "", "'" => ""); +our %closeQuote = ('"' => "", "'" => ""); +sub Quote { + my $self = shift; my $item = shift; my $string = shift; + return $openQuote{$item->{token}} if $string eq "" || $string =~ m/(^|[ ({\[\s])$/; + return $closeQuote{$item->{token}}; +} + +# No rule for PTX +sub Rule { + my $self = shift; my $item = shift; + return $self->nl; +} + +sub Verbatim { + my $self = shift; my $item = shift; + #Don't escape most content. Just < and & + #my $text = $self->Escape($item->{text}); + my $text = $item->{text}; + $text =~ s/$text"; + return $text; +} + +sub Math { + my $self = shift; + return main::general_math_ev3($self->SUPER::Math(@_)); +} + + ###################################################################### ###################################################################### @@ -1343,6 +1478,9 @@ sub Format { my $format; if ($main::displayMode eq 'TeX') { $format = "{\\pgmlSetup\n".PGML::Format::tex->new($parser)->format."\\par}%\n"; + } elsif ($main::displayMode eq 'PTX') { + $format = PGML::Format::ptx->new($parser)->format."\n"; + $format = main::PTX_cleanup($format); } else { $format = '
    '."\n".PGML::Format::html->new($parser)->format.'
    '."\n"; } @@ -1401,6 +1539,7 @@ sub LaTeX { \def\pgmlIndent{\par\advance\leftskip by 2em \advance\pgmlPercent by .02em \pgmlCount=0}% \def\pgmlbulletItem{\par\indent\llap{$\bullet$ }\ignorespaces}% +\def\pgmldiscItem{\par\indent\llap{$\bullet$ }\ignorespaces}% \def\pgmlcircleItem{\par\indent\llap{$\circ$ }\ignorespaces}% \def\pgmlsquareItem{\par\indent\llap{\vrule height 1ex width .75ex depth -.25ex\ }\ignorespaces}% \def\pgmlnumericItem{\par\indent\advance\pgmlCount by 1 \llap{\the\pgmlCount. }\ignorespaces}% diff --git a/macros/PGanswermacros.pl b/macros/PGanswermacros.pl index 09443637b1..cf3e3ef005 100644 --- a/macros/PGanswermacros.pl +++ b/macros/PGanswermacros.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/PGanswermacros.pl,v 1.72 2010/02/01 01:33:05 apizer Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/PGbasicmacros.pl b/macros/PGbasicmacros.pl index 7f2ce4847b..38f4acd74c 100644 --- a/macros/PGbasicmacros.pl +++ b/macros/PGbasicmacros.pl @@ -77,6 +77,8 @@ BEGIN $EUL, $BCENTER, $ECENTER, + $BLTR, + $ELTR, $HR, $LBRACE, $RBRACE, @@ -144,6 +146,8 @@ sub _PGbasicmacros_init { $main::EUL = EUL(); $main::BCENTER = BCENTER(); $main::ECENTER = ECENTER(); + $main::BLTR = BLTR(); + $main::ELTR = ELTR(); $main::HR = HR(); $main::LBRACE = LBRACE(); $main::RBRACE = RBRACE(); @@ -197,6 +201,8 @@ sub _PGbasicmacros_init { $EUL = EUL(); $BCENTER = BCENTER(); $ECENTER = ECENTER(); + $BLTR = BLTR(); + $ELTR = ELTR(); $HR = HR(); $LBRACE = LBRACE(); $RBRACE = RBRACE(); @@ -396,9 +402,10 @@ sub NAMED_ANS_RULE { # Note: codeshard is used in the css to identify input elements # that come from pg - HTML => qq!\n!. + HTML => qq!\n!. $add_html. # added for dragmath qq!\n!, + PTX => '', ); } @@ -439,7 +446,8 @@ sub NAMED_HIDDEN_ANS_RULE { # this is used to hold information being passed into TeX => "\\mbox{\\parbox[t]{${tcol}ex}{\\hrulefill}}", Latex2HTML => qq!\\begin{rawhtml}\\end{rawhtml}!, HTML => qq!!. - qq!! + qq!!, + PTX => '', ); } sub NAMED_ANS_RULE_OPTION { # deprecated @@ -457,11 +465,17 @@ sub NAMED_ANS_RULE_EXTENSION { } else { $label = generate_aria_label($name); } - # this is the name of the parent answer group + # $answer_group_name is the name of the parent answer group + # the group name is usually the same as the answer blank name + # when there is only one answer blank. + + + my $answer_group_name = $options{answer_group_name}//''; unless ($answer_group_name) { WARN_MESSAGE("Error in NAMED_ANSWER_RULE_EXTENSION: every call to this subroutine needs - to have \$options{answer_group_name} defined. Answer blank name: $name"); + to have \$options{answer_group_name} defined. For a single answer blank this is + usually the same as the answer blank name. Answer blank name: $name"); } # warn "from named answer rule extension in PGbasic answer_group_name: |$answer_group_name|"; my $answer_value = ''; @@ -482,8 +496,9 @@ sub NAMED_ANS_RULE_EXTENSION { MODES( TeX => "\\mbox{\\parbox[t]{${tcol}ex}{\\hrulefill}}", Latex2HTML => qq!\\begin{rawhtml}\n\n\\end{rawhtml}\n!, - HTML => qq!!. - qq!! + HTML => qq!!. + qq!!, + PTX => '', ); } @@ -524,7 +539,8 @@ sub ANS_RULE { #deprecated HTML => qq! - ! + !, + PTX => '', ); $out; } @@ -559,7 +575,8 @@ sub NAMED_ANS_RADIO { MODES( TeX => qq!\\item{$tag}\n!, Latex2HTML => qq!\\begin{rawhtml}\n\\end{rawhtml}$tag!, - HTML => qq!! + HTML => qq!!, + PTX => '
  • '."$tag".'
  • '."\n", ); } @@ -598,7 +615,8 @@ sub NAMED_ANS_RADIO_EXTENSION { MODES( TeX => qq!\\item{$tag}\n!, Latex2HTML => qq!\\begin{rawhtml}\n\\end{rawhtml}$tag!, - HTML => qq!! + HTML => qq!!, + PTX => '
  • '."$tag".'
  • '."\n", ); } @@ -759,7 +777,8 @@ sub NAMED_ANS_CHECKBOX { MODES( TeX => qq!\\item{$tag}\n!, Latex2HTML => qq!\\begin{rawhtml}\n\\end{rawhtml}$tag!, - HTML => qq!! + HTML => qq!!, + PTX => '
  • '."$tag".'
  • '."\n", ); } @@ -796,7 +815,8 @@ sub NAMED_ANS_CHECKBOX_OPTION { MODES( TeX => qq!\\item{$tag}\n!, Latex2HTML => qq!\\begin{rawhtml}\n\\end{rawhtml}$tag!, - HTML => qq!! + HTML => qq!!, + PTX => '
  • '."$tag".'
  • '."\n", ); } @@ -879,6 +899,9 @@ sub ans_radio_buttons { if ($displayMode eq 'TeX') { $radio_buttons[0] = "\n\\begin{itemize}\n" . $radio_buttons[0]; $radio_buttons[$#radio_buttons] .= "\n\\end{itemize}\n"; + } elsif ($displayMode eq 'PTX') { + $radio_buttons[0] = '' . "\n" . $radio_buttons[0]; + $radio_buttons[$#radio_buttons] .= ''; } (wantarray) ? @radio_buttons: join(" ", @radio_buttons); @@ -892,6 +915,9 @@ sub ans_checkbox { if ($displayMode eq 'TeX') { $checkboxes[0] = "\n\\begin{itemize}\n" . $checkboxes[0]; $checkboxes[$#checkboxes] .= "\n\\end{itemize}\n"; + } elsif ($displayMode eq 'PTX') { + $checkboxes[0] = '' . "\n" . $checkboxes[0]; + $checkboxes[$#checkboxes] .= '' } (wantarray) ? @checkboxes: join(" ", @checkboxes); @@ -912,7 +938,8 @@ sub tex_ans_rule { 'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 'HTML_dpng' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', - 'HTML' => $answer_rule + 'HTML' => $answer_rule, + 'PTX' => 'Answer boxes cannot be placed inside typeset equations', ); $out; @@ -928,7 +955,8 @@ sub tex_ans_rule_extension { 'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}', 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 'HTML_dpng' => '\fbox{Answer boxes cannot be placed inside typeset equations}', - 'HTML' => $answer_rule + 'HTML' => $answer_rule, + 'PTX' => 'Answer boxes cannot be placed inside typeset equations', ); $out; @@ -944,7 +972,8 @@ sub NAMED_TEX_ANS_RULE { 'Latex2HTML' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 'HTML_dpng' => '\\fbox{Answer boxes cannot be placed inside typeset equations}', - 'HTML' => $answer_rule + 'HTML' => $answer_rule, + 'PTX' => 'Answer boxes cannot be placed inside typeset equations', ); $out; @@ -959,7 +988,8 @@ sub NAMED_TEX_ANS_RULE_EXTENSION { 'Latex2HTML' => '\fbox{Answer boxes cannot be placed inside typeset equations}', 'HTML_tth' => '\\begin{rawhtml} '. $answer_rule.'\\end{rawhtml}', 'HTML_dpng' => '\fbox{Answer boxes cannot be placed inside typeset equations}', - 'HTML' => $answer_rule + 'HTML' => $answer_rule, + 'PTX' => 'Answer boxes cannot be placed inside typeset equations', ); $out; @@ -1017,6 +1047,13 @@ sub NAMED_POP_UP_LIST { $out .= " \\begin{rawhtml}\\end{rawhtml}\n"; } elsif ( $displayMode eq "TeX") { $out .= "\\fbox{?}"; + } elsif ( $displayMode eq "PTX") { + $out = '' . "\n"; + my $i; + foreach ($i=0; $i< @list; $i=$i+2) { + $out .= '
  • '.$list[$i+1].'
  • '."\n"; + }; + $out .= '
    '; } $name = RECORD_ANS_NAME($name,$answer_value); # record answer name $out; @@ -1043,6 +1080,9 @@ =head5 answer_matrix The options are passed on to display_matrix. + Note (7/21/2107) The above usage does not work. Omitting the \[ \] works, but also must + load PGmatrixmacros.pl to get display_matrix used below + =cut @@ -1123,7 +1163,8 @@ sub NAMED_ANS_ARRAY_EXTENSION{ MODES( TeX => "\\mbox{\\parbox[t]{10pt}{\\hrulefill}}\\hrulefill\\quad ", Latex2HTML => qq!\\begin{rawhtml}\n\n\\end{rawhtml}\n!, - HTML => qq!\n! + HTML => qq!\n!, + PTX => qq!!, ); } @@ -1180,10 +1221,9 @@ sub ans_array_extension{ for( my $i = 0; $i < $n; $i+=1) { $name = NEW_ANS_ARRAY_NAME_EXTENSION($num,$j,$i); - $array[$j][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col, ans_label=>$ans_label); + $array[$j][$i] = NAMED_ANS_ARRAY_EXTENSION($name,$col, answer_group_name=>$ans_label,@options); } - } my $ra_local_display_matrix=PG_restricted_eval(q!\&main::display_matrix!); &$ra_local_display_matrix( \@array, @options ); @@ -1193,7 +1233,7 @@ sub ans_array_extension{ # end answer blank macros -=head2 Hints and solutions macros +=head2 Hints, solutions, and statement macros solution('text','text2',...); SOLUTION('text','text2',...); # equivalent to TEXT(solution(...)); @@ -1201,6 +1241,12 @@ =head2 Hints and solutions macros hint('text', 'text2', ...); HINT('text', 'text2',...); # equivalent to TEXT("$BR$HINT" . hint(@_) . "$BR") if hint(@_); + statement('text'); + STATEMENT('text'); # equivalent to TEXT(statement(...)); + +statement takes a string, probably from EV3P, and possibly wraps opening and closing +content, paralleling one feature of solution and hint. + Solution prints its concatenated input when the check box named 'ShowSol' is set and the time is after the answer date. The check box 'ShowSol' is visible only after the answer date or when the problem is viewed by a professor. @@ -1260,12 +1306,14 @@ sub SOLUTION { base64 =>1 ) ) if solution(@_); } elsif ($displayMode=~/TeX/) { TEXT( - "\n%%% BEGIN SOLUTION\n", #Marker used in MathBook XML extraction; contact alex.jordan@pcc.edu before modifying + "\n%%% BEGIN SOLUTION\n", #Marker used in PreTeXt LaTeX extraction; contact alex.jordan@pcc.edu before modifying $PAR,SOLUTION_HEADING(), solution(@_).$PAR, - "\n%%% END SOLUTION\n" #Marker used in MathBook XML extraction; contact alex.jordan@pcc.edu before modifying + "\n%%% END SOLUTION\n" #Marker used in PreTeXt LaTeX extraction; contact alex.jordan@pcc.edu before modifying ) if solution(@_) ; } elsif ($displayMode=~/HTML/) { TEXT( $PAR.SOLUTION_HEADING().$BR.solution(@_).$PAR) if solution(@_) ; + } elsif ($displayMode=~/PTX/) { + TEXT( '',"\n",solution(@_),"\n",'',"\n\n") if solution(@_) ; } else { TEXT( $PAR.solution(@_).$PAR) if solution(@_) ; } @@ -1303,7 +1351,9 @@ sub hint { ## the second test above prevents a hint being shown if a doctored form is submitted $out = join(' ',@in); } - } + } elsif ($displayMode=~/PTX/) { + $out = join(' ',@in); + } $out ; } @@ -1315,17 +1365,31 @@ sub HINT { base64 => 1) ) if hint(@_); } elsif ($displayMode=~/TeX/) { TEXT( - "\n%%% BEGIN HINT\n", #Marker used in MathBook XML extraction; contact alex.jordan@pcc.edu before modifying + "\n%%% BEGIN HINT\n", #Marker used in PreTeXt LaTeX extraction; contact alex.jordan@pcc.edu before modifying $PAR,HINT_HEADING(), hint(@_).$PAR, - "\n%%% END HINT\n" #Marker used in MathBook XML extraction; contact alex.jordan@pcc.edu before modifying + "\n%%% END HINT\n" #Marker used in PreTeXt LaTeX extraction; contact alex.jordan@pcc.edu before modifying ) if hint(@_) ; + } elsif ($displayMode=~/PTX/) { + TEXT( '',"\n",hint(@_),"\n",'',"\n\n") if hint(@_); } else { TEXT($PAR, HINT_HEADING(), $BR. hint(@_) . $PAR) if hint(@_); } } -# End hints and solutions macros +sub statement { + my @in = @_; + my $out = join(' ',@in); + $out; +} + +sub STATEMENT { +if ($displayMode eq 'PTX') { TEXT('',"\n", statement(@_), "\n", '',"\n\n"); } + else { TEXT( statement(@_) ) }; +} + + +# End hints and solutions and statement macros ################################# =head2 Comments to instructors @@ -1441,6 +1505,7 @@ sub M3 { our %DISPLAY_MODE_FAILOVER = ( TeX => [], HTML => [], + PTX => [ "HTML" ], HTML_tth => [ "HTML", ], HTML_dpng => [ "HTML_tth", "HTML", ], HTML_jsMath => [ "HTML_dpng", "HTML_tth", "HTML", ], @@ -1512,6 +1577,8 @@ =head2 Display constants $EUL EUL() end underlined type $BCENTER BCENTER() begin centered environment $ECENTER ECENTER() end centered environment + $BLTR BLTR() begin left to right environment + $ELTR ELTR() end left to right environment $HR HR() horizontal rule $LBRACE LBRACE() left brace $LB LB () left brace @@ -1540,22 +1607,22 @@ sub ALPHABET { # Some constants which are different in tex and in HTML # The order of arguments is TeX, Latex2HTML, HTML # Adopted Davide Cervone's improvements to PAR, LTS, GTS, LTE, GTE, LBRACE, RBRACE, LB, RB. 7-14-03 AKP -sub PAR { MODES( TeX => '\\par ', Latex2HTML => '\\begin{rawhtml}

    \\end{rawhtml}', HTML => '

    '); }; +sub PAR { MODES( TeX => '\\par ', Latex2HTML => '\\begin{rawhtml}

    \\end{rawhtml}', HTML => '

    ', PTX => "\n\n"); }; #sub BR { MODES( TeX => '\\par\\noindent ', Latex2HTML => '\\begin{rawhtml}
    \\end{rawhtml}', HTML => '
    '); }; # Alternate definition of BR which is slightly more flexible and gives more white space in printed output # which looks better but kills more trees. -sub BR { MODES( TeX => '\\leavevmode\\\\\\relax ', Latex2HTML => '\\begin{rawhtml}
    \\end{rawhtml}', HTML => '
    '); }; -sub BRBR { MODES( TeX => '\\leavevmode\\\\\\relax \\leavevmode\\\\\\relax ', Latex2HTML => '\\begin{rawhtml}

    \\end{rawhtml}', HTML => '

    '); }; -sub LQ { MODES( TeX => "\\lq\\lq{}", Latex2HTML => '"', HTML => '"' ); }; -sub RQ { MODES( TeX => "\\rq\\rq{}", Latex2HTML => '"', HTML => '"' ); }; -sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML => ''); }; # begin math mode -sub EM { MODES(TeX => '\\)', Latex2HTML => '\\)', HTML => ''); }; # end math mode -sub BDM { MODES(TeX => '\\[', Latex2HTML => '\\[', HTML => '

    '); }; #begin displayMath mode -sub EDM { MODES(TeX => '\\]', Latex2HTML => '\\]', HTML => '

    '); }; #end displayMath mode -sub LTS { MODES(TeX => '<', Latex2HTML => '\\lt ', HTML => '<', HTML_tth => '<' ); }; -sub GTS { MODES(TeX => '>', Latex2HTML => '\\gt ', HTML => '>', HTML_tth => '>' ); }; -sub LTE { MODES(TeX => '\\le ', Latex2HTML => '\\le ', HTML => '<', HTML_tth => '\\le ' ); }; -sub GTE { MODES(TeX => '\\ge ', Latex2HTML => '\\ge ', HTML => '>', HTML_tth => '\\ge ' ); }; +sub BR { MODES( TeX => '\\leavevmode\\\\\\relax ', Latex2HTML => '\\begin{rawhtml}
    \\end{rawhtml}', HTML => '
    ', PTX => "\n\n"); }; +sub BRBR { MODES( TeX => '\\leavevmode\\\\\\relax \\leavevmode\\\\\\relax ', Latex2HTML => '\\begin{rawhtml}

    \\end{rawhtml}', HTML => '

    ', PTX => "\n"); }; +sub LQ { MODES( TeX => "\\lq\\lq{}", Latex2HTML => '"', HTML => '"', PTX => '' ); }; +sub RQ { MODES( TeX => "\\rq\\rq{}", Latex2HTML => '"', HTML => '"', PTX => '' ); }; +sub BM { MODES(TeX => '\\(', Latex2HTML => '\\(', HTML => '', PTX => ''); }; # begin math mode +sub EM { MODES(TeX => '\\)', Latex2HTML => '\\)', HTML => '', PTX => ''); }; # end math mode +sub BDM { MODES(TeX => '\\[', Latex2HTML => '\\[', HTML => '

    ', PTX => ''); }; #begin displayMath mode +sub EDM { MODES(TeX => '\\]', Latex2HTML => '\\]', HTML => '

    ', PTX => ''); }; #end displayMath mode +sub LTS { MODES(TeX => '<', Latex2HTML => '\\lt ', HTML => '<', HTML_tth => '<', PTX => '\lt' ); }; #only for use in math mode +sub GTS { MODES(TeX => '>', Latex2HTML => '\\gt ', HTML => '>', HTML_tth => '>', PTX => '\gt' ); }; #only for use in math mode +sub LTE { MODES(TeX => '\\le ', Latex2HTML => '\\le ', HTML => '<', HTML_tth => '\\le ', PTX => '\leq' ); }; #only for use in math mode +sub GTE { MODES(TeX => '\\ge ', Latex2HTML => '\\ge ', HTML => '>', HTML_tth => '\\ge ', PTX => '\geq' ); }; #only for use in math mode sub BEGIN_ONE_COLUMN { MODES(TeX => "\\ifdefined\\nocolumns\\else \\end{multicols}\\fi\n", Latex2HTML => " ", HTML => " "); }; sub END_ONE_COLUMN { MODES(TeX => " \\ifdefined\\nocolumns\\else \\begin{multicols}{2}\n\\columnwidth=\\linewidth \\fi\n", @@ -1564,32 +1631,35 @@ sub ALPHABET { }; sub SOLUTION_HEADING { MODES( TeX => '\\par {\\bf '.maketext('Solution:').' }', Latex2HTML => '\\par {\\bf '.maketext('Solution:').' }', - HTML => ''.maketext('Solution:').' '); + HTML => ''.maketext('Solution:').' ', + PTX => ''); }; -sub HINT_HEADING { MODES( TeX => "\\par {\\bf ".maketext('Hint:')." }", Latex2HTML => "\\par {\\bf ".maketext('Hint:')." }", HTML => "".maketext('Hint:')." "); }; -sub US { MODES(TeX => '\\_', Latex2HTML => '\\_', HTML => '_');}; # underscore, e.g. file${US}name -sub SPACE { MODES(TeX => '\\ ', Latex2HTML => '\\ ', HTML => ' ');}; # force a space in latex, doesn't force extra space in html -sub NBSP { MODES(TeX => '~', Latex2HTML => '~', HTML => ' ');}; -sub NDASH { MODES(TeX => '--', Latex2HTML => '--', HTML => '–');}; -sub MDASH { MODES(TeX => '---', Latex2HTML => '---', HTML => '—');}; -sub BBOLD { MODES(TeX => '{\\bf ', Latex2HTML => '{\\bf ', HTML => ''); }; -sub EBOLD { MODES( TeX => '}', Latex2HTML => '}',HTML => ''); }; -sub BLABEL { MODES(TeX => '', Latex2HTML => '', HTML => ''); }; -sub BITALIC { MODES(TeX => '{\\it ', Latex2HTML => '{\\it ', HTML => ''); }; -sub EITALIC { MODES(TeX => '} ', Latex2HTML => '} ', HTML => ''); }; -sub BUL { MODES(TeX => '\\underline{', Latex2HTML => '\\underline{', HTML => ''); }; -sub EUL { MODES(TeX => '}', Latex2HTML => '}', HTML => ''); }; -sub BCENTER { MODES(TeX => '\\begin{center} ', Latex2HTML => ' \\begin{rawhtml}
    \\end{rawhtml} ', HTML => '
    '); }; -sub ECENTER { MODES(TeX => '\\end{center} ', Latex2HTML => ' \\begin{rawhtml}
    \\end{rawhtml} ', HTML => '
    '); }; -sub HR { MODES(TeX => '\\par\\hrulefill\\par ', Latex2HTML => '\\begin{rawhtml}
    \\end{rawhtml}', HTML => '
    '); }; -sub LBRACE { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '{' , HTML_tth=> '\\lbrace' ); }; -sub RBRACE { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '}' , HTML_tth=> '\\rbrace',); }; -sub LB { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '{' , HTML_tth=> '\\lbrace' ); }; -sub RB { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '}' , HTML_tth=> '\\rbrace',); }; -sub DOLLAR { MODES( TeX => '\\$', Latex2HTML => '$', HTML => '$' ); }; -sub PERCENT { MODES( TeX => '\\%', Latex2HTML => '\\%', HTML => '%' ); }; -sub CARET { MODES( TeX => '\\verb+^+', Latex2HTML => '\\verb+^+', HTML => '^' ); }; +sub HINT_HEADING { MODES( TeX => "\\par {\\bf ".maketext('Hint:')." }", Latex2HTML => "\\par {\\bf ".maketext('Hint:')." }", HTML => "".maketext('Hint:')." ", PTX => ''); }; +sub US { MODES(TeX => '\\_', Latex2HTML => '\\_', HTML => '_', PTX => '');}; # underscore, e.g. file${US}name +sub SPACE { MODES(TeX => '\\ ', Latex2HTML => '\\ ', HTML => ' ', PTX => ' ');}; # force a space in latex, doesn't force extra space in html +sub NBSP { MODES(TeX => '~', Latex2HTML => '~', HTML => ' ', PTX => '');}; +sub NDASH { MODES(TeX => '--', Latex2HTML => '--', HTML => '–', PTX => '');}; +sub MDASH { MODES(TeX => '---', Latex2HTML => '---', HTML => '—', PTX => '');}; +sub BBOLD { MODES(TeX => '{\\bf ', Latex2HTML => '{\\bf ', HTML => '', PTX => ''); }; +sub EBOLD { MODES( TeX => '}', Latex2HTML => '}',HTML => '', PTX => ''); }; +sub BLABEL { MODES(TeX => '', Latex2HTML => '', HTML => '', PTX => ''); }; +sub BITALIC { MODES(TeX => '{\\it ', Latex2HTML => '{\\it ', HTML => '', PTX => ''); }; +sub EITALIC { MODES(TeX => '} ', Latex2HTML => '} ', HTML => '', PTX => ''); }; +sub BUL { MODES(TeX => '\\underline{', Latex2HTML => '\\underline{', HTML => '', PTX => ''); }; +sub EUL { MODES(TeX => '}', Latex2HTML => '}', HTML => '', PTX => ''); }; +sub BCENTER { MODES(TeX => '\\begin{center} ', Latex2HTML => ' \\begin{rawhtml}
    \\end{rawhtml} ', HTML => '
    ', PTX => ''); }; +sub ECENTER { MODES(TeX => '\\end{center} ', Latex2HTML => ' \\begin{rawhtml}
    \\end{rawhtml} ', HTML => '
    ', PTX => ''); }; +sub BLTR { MODES(TeX => ' ', Latex2HTML => ' \\begin{rawhtml}
    \\end{rawhtml} ', HTML => '', PTX => ''); }; +sub ELTR { MODES(TeX => ' ', Latex2HTML => ' \\begin{rawhtml}
    \\end{rawhtml} ', HTML => '', PTX => ''); }; +sub HR { MODES(TeX => '\\par\\hrulefill\\par ', Latex2HTML => '\\begin{rawhtml}
    \\end{rawhtml}', HTML => '
    ', PTX => ''); }; +sub LBRACE { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '{' , HTML_tth=> '\\lbrace', PTX => '' ); }; #not for use in math mode +sub RBRACE { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '}' , HTML_tth=> '\\rbrace', PTX => '' ); }; #not for use in math mode +sub LB { MODES( TeX => '\{', Latex2HTML => '\\lbrace', HTML => '{' , HTML_tth=> '\\lbrace', PTX => '' ); }; #not for use in math mode +sub RB { MODES( TeX => '\}', Latex2HTML => '\\rbrace', HTML => '}' , HTML_tth=> '\\rbrace', PTX => '' ); }; #not for use in math mode +sub DOLLAR { MODES( TeX => '\\$', Latex2HTML => '$', HTML => '$', PTX => '' ); }; +sub PERCENT { MODES( TeX => '\\%', Latex2HTML => '\\%', HTML => '%', PTX => '' ); }; +sub CARET { MODES( TeX => '\\verb+^+', Latex2HTML => '\\verb+^+', HTML => '^', PTX => '' ); }; sub PI {4*atan2(1,1);}; sub E {exp(1);}; @@ -1930,6 +2000,12 @@ sub general_math_ev3 { $in = HTML::Entities::encode_entities($in); $out = "`$in`" if $mode eq "inline"; $out = '
    `'.$in.'`
    ' if $mode eq "display"; + } elsif ($displayMode eq "PTX") { + #protect XML control characters + $in =~ s/\&(?!([\w#]+;))/\\amp /g; + $in =~ s/'."$in".'' if $mode eq "inline"; + $out = ''."$in".'' if $mode eq "display"; } elsif ($displayMode eq "HTML_LaTeXMathML") { $in = HTML::Entities::encode_entities($in); $in = '{'.$in.'}'; @@ -2073,7 +2149,8 @@ sub EV3P { $string = ev_substring($string,"\\(","\\)",\&math_ev3); $string = ev_substring($string,"\\[","\\]",\&display_math_ev3); } - + + if ($displayMode eq 'PTX') {$string = PTX_cleanup($string)}; return $string; } @@ -2111,6 +2188,85 @@ sub EV3P_parser { } +sub PTX_cleanup { + my $string = shift; + # Wrap

    tags where necessary, and other cleanup + # Nothing else should be creating p tags, so assume all p tags created here + # The only supported top-level elements within a statement, hint, or solution in a problem + # are p, blockquote, pre, sidebyside + if ($displayMode eq 'PTX') { + #encase entire string in

    + $string = "

    ".$string."

    "; + + #a may have been created within a of a as a container of an + #so here we clean that up + $string =~ s/(?s)(((?!<\/cell>).)*?)]*>(.*?)<\/sidebyside>(.*?<\/cell>)/$1$3$4/g; + + #inside a sidebyside, the only permissible children are p, image, video, and tabular + #insert opening and closing p, to be removed later if they enclose an image, video or tabular + $string =~ s/(]*(?)/$1\n

    /g; + $string =~ s/(<\/sidebyside>)/<\/p>\n$1/g; + #ditto for li + $string =~ s/(]*(?)/$1\n

    /g; + $string =~ s/(<\/li>)/<\/p>\n$1/g; + + #close p right before any sidebyside, blockquote, or pre, image, video, or tabular + #and open p immediately following. Later any potential side effects are cleaned up. + $string =~ s/(<(sidebyside|blockquote|pre|image|video|tabular)[^>]*(?)/<\/p>\n$1/g; + $string =~ s/(<\/(sidebyside|blockquote|pre|image|video|tabular)>)/$1\n

    /g; + $string =~ s/(<(sidebyside|blockquote|pre|image|video|tabular)[^>]*(?<=\/)>)/<\/p>\n$1\n

    /g; + + #within a , we may have an issue if there was an image that had '<\p>' and '

    ' wrapped around + #it from the above block. If the '

    ' has a preceding '

    ' within the cell, no problem. Otherwise, + #the '

    ' must go. Likewise at the other end. + $string =~ s/(?s)(.*?)<\/p>\n(]*(?<=\/)>)\n

    (.*?<\/cell>)/$1$2$3/g; + + #remove blank lines; assume the intent was to end a p and start a new one + #but don't put closing and opening p replacing the blank lines if they precede or follow a + #or an

  • + $string =~ s/(\r\n?|\n)(\r\n?|\n)+(?!(|<\/tabular>|
  • |\r\n?|\n))/<\/p>\n

    /g; + $string =~ s/(\r\n?|\n)(\r\n?|\n)+(?=(|<\/tabular>|

  • |\r\n?|\n))/\n/g; + + #remove whitespace following

    + $string =~ s/(?s)(

    )\s*/$1/g; + + #remove whitespace preceding

    + $string =~ s/(?s)\s*(<\/p>)/$1/g; + + #remove empty p + $string =~ s/(\r\n?|\n)?

    <\/p>//g; + + #a tabular cell may have

    and

    but no corresponding width specification in a col. + #if so, remove all

    and

    from all cells. + my $previous; + do { + $previous = $string; + $string =~ s/(?s)((?:\s|/g; + $string =~ s/#//g; + $string =~ s/\$//g; + $string =~ s/\%//g; + $string =~ s/\\//g; + $string =~ s/_//g; + $string =~ s/{//g; + $string =~ s/}//g; + $string =~ s/~//g; + $string; +} + + =head2 Formatting macros beginproblem() # generates text listing number and the point value of @@ -2218,7 +2374,7 @@ sub beginproblem { (defined($effectivePermissionLevel) && defined($PRINT_FILE_NAMES_PERMISSION_LEVEL) && $effectivePermissionLevel >= $PRINT_FILE_NAMES_PERMISSION_LEVEL) || ( defined($inlist{ $studentLogin }) and ( $inlist{ $studentLogin }>0 ) )?1:0 ; $out .= MODES( TeX => - "\n%%% BEGIN PROBLEM PREAMBLE\n", #Marker used in MathBook XML extraction; contact alex.jordan@pcc.edu before modifying + "\n%%% BEGIN PROBLEM PREAMBLE\n", #Marker used in PreTeXt LaTeX extraction; contact alex.jordan@pcc.edu before modifying HTML => '

    '); if ( $print_path_name_flag ) { $out .= &M3("{\\bf ${probNum}. {\\footnotesize ($problemValue $points) \\path|$fileName|}}\\newline ", @@ -2233,8 +2389,9 @@ sub beginproblem { } $out .= MODES(%{main::PG_restricted_eval(q!$main::problemPreamble!)}); $out .= MODES( TeX => - "\n%%% END PROBLEM PREAMBLE\n", #Marker used in MathBook XML extraction; contact alex.jordan@pcc.edu before modifying + "\n%%% END PROBLEM PREAMBLE\n", #Marker used in PreTeXt LaTeX extraction; contact alex.jordan@pcc.edu before modifying HTML => ""); + if ($displayMode eq 'PTX') {$out = ''}; $out; } @@ -2296,12 +2453,12 @@ sub OL { my $i = 0; my @alpha = ('A'..'Z', 'AA'..'ZZ'); my $letter; - my $out= &M3( - "\\begin{enumerate}\n", - " \\begin{rawhtml}

      \\end{rawhtml} ", + my $out = MODES(TeX=> "\\begin{enumerate}\n", + Latex2HTML=> " \\begin{rawhtml}
        \\end{rawhtml} ", # kludge to fix IE/CSS problem #"
          \n" - "
          \n" + HTML=> "
          \n", + PTX=> '
            '."\n", ) ; my $elem; foreach $elem (@array) { @@ -2312,15 +2469,17 @@ sub OL { #HTML=> "
          1. $elem\n", HTML=> "
            $letter. $elem\n", #HTML_dpng=> "
          2. $elem

            \n" - HTML_dpng=> "
            $letter. $elem \n" + HTML_dpng=> "
            $letter. $elem \n", + PTX=> "
          3. $elem

          4. \n", ); $i++; } - $out .= &M3( - "\\end{enumerate}\n", - " \\begin{rawhtml}
          \n \\end{rawhtml} ", + $out .= MODES( + TeX=> "\\end{enumerate}\n", + Latex2HTML=> " \\begin{rawhtml}
        \n \\end{rawhtml} ", #"
      \n" - "\n" + HTML=> "\n", + PTX=> '
    '."\n", ) ; } @@ -2328,10 +2487,13 @@ sub htmlLink { my $url = shift; my $text = shift; my $options = shift; + my $sanitized_url = $url; + $sanitized_url =~ s/&/&/g; $options = "" unless defined($options); return "$BBOLD [ the link to '$text' is broken ] $EBOLD" unless defined($url) and $url; MODES( TeX => "{\\bf \\underline{$text}}", - HTML => "$text" + HTML => "$text", + PTX => "$text", ); } @@ -2373,7 +2535,8 @@ sub knowlLink { # an new syntax for knowlLink that facilitates a local HERE docu } #my $option_string = qq!url = "$options{url}" value = "$options{value}" !; MODES( TeX => "{\\bf \\underline{$display_text}}", - HTML => "$display_text" + HTML => "$display_text", + PTX => ''.$display_text.'', ); @@ -2389,6 +2552,7 @@ sub iframe { HTML => qq!\n \n!, + PTX => '', ); } @@ -2492,6 +2656,7 @@ sub appletLink { Latex2HTML => "\\begin{rawhtml} $options \\end{rawhtml}", HTML => " \n $options \n ", #HTML => qq! $options ! + PTX => 'PreTeXt does not support appletLink', ); } @@ -2501,7 +2666,8 @@ sub oldAppletLink { $options = "" unless defined($options); MODES( TeX => "{\\bf \\underline{APPLET} }", Latex2HTML => "\\begin{rawhtml} $options \\end{rawhtml}", - HTML => " $options " + HTML => " $options ", + PTX => 'PreTeXt does not support appletLink', ); } sub spf { @@ -2584,6 +2750,9 @@ sub begintable { if ($displayMode eq 'TeX') { $out .= "\n\\par\\smallskip\\begin{center}\\begin{tabular}{" . "|c" x $number . "|} \\hline\n"; } + elsif ($displayMode eq 'PTX') { + $out .= "\n".''."\n"; + } elsif ($displayMode eq 'Latex2HTML') { $out .= "\n\\begin{rawhtml} \n\\end{rawhtml}"; } @@ -2608,6 +2777,9 @@ sub endtable { if ($displayMode eq 'TeX') { $out .= "\n\\end {tabular}\\end{center}\\par\\smallskip\n"; } + elsif ($displayMode eq 'PTX') { + $out .= "\n".''."\n"; + } elsif ($displayMode eq 'Latex2HTML') { $out .= "\n\\begin{rawhtml}
    \n\\end{rawhtml}"; } @@ -2639,6 +2811,13 @@ sub row { $out .= "\\\\ \\hline \n"; # carriage returns must be added manually for tex } + elsif ($displayMode eq 'PTX') { + $out .= ''."\n"; + while (@elements) { + $out .= ''.shift(@elements).''."\n"; + } + $out .= ''."\n"; + } elsif ($displayMode eq 'Latex2HTML') { $out .= "\n\\begin{rawhtml}\n\n\\end{rawhtml}\n"; while (@elements) { @@ -2689,7 +2868,7 @@ sub image { my %in_options = @opt; my %known_options = ( width => 100, - height => 100, + height => '', tex_size => 800, extra_html_tags => '', ); @@ -2709,6 +2888,11 @@ sub image { my $width_ratio = $tex_size*(.001); my @image_list = (); + # if height was explicitly given, create string for height attribute to be used in HTML, LaTeX2HTML + # otherwise omit a height attribute and allow the browser to use aspect ratio preservation + my $height_attrib = ''; + $height_attrib = qq{height = "$height"} if ($height); + if (ref($image_ref) =~ /ARRAY/ ) { @image_list = @{$image_ref}; } else { @@ -2741,7 +2925,7 @@ sub image { } } elsif ($displayMode eq 'Latex2HTML') { my $wid = ($envir->{onTheFlyImageSize} || 0)+ 30; - $out = qq!\\begin{rawhtml}\n\n + $out = qq!\\begin{rawhtml}\n\n \\end{rawhtml}\n ! } elsif ($displayMode eq 'HTML_MathJax' || $displayMode eq 'HTML_dpng' @@ -2754,10 +2938,13 @@ sub image { my $wid = ($envir->{onTheFlyImageSize} || 0) +30; $out = qq! - + ! - } else { + } elsif ($displayMode eq 'PTX') { + my $ptxwidth = 100*$width/600; + $out = qq!\n\n<\/sidebyside>! + } else { $out = "Error: PGbasicmacros: image: Unknown displayMode: $displayMode.\n"; } push(@output_list, $out); @@ -2775,7 +2962,8 @@ sub embedSVG { } return MODES( HTML => q! !, - TeX => "Can't process svg in tex mode yet \\includegraphics[width=6in]{" . alias( $file_name ) . "}" + TeX => "Can't process svg in tex mode yet \\includegraphics[width=6in]{" . alias( $file_name ) . "}", + PTX => '', ); } @@ -2788,7 +2976,8 @@ sub embedPDF { q! type="application/pdf" width="100%" height="100%">!, - TeX => "\\includegraphics[width=6in]{" . alias( $file_name ) . "}" + TeX => "\\includegraphics[width=6in]{" . alias( $file_name ) . "}", + PTX => '', ) ; } @@ -2859,6 +3048,9 @@ sub video { ${htmlmessage}\n \n ! + } elsif ($displayMode eq 'PTX') { + my $ptxwidth = 100*$width/600; + $out = qq!! } else { $out = "Error: PGbasicmacros: video: Unknown displayMode: $displayMode.\n"; } diff --git a/macros/PGchoicemacros.pl b/macros/PGchoicemacros.pl index 80e911e846..997d9837f6 100644 --- a/macros/PGchoicemacros.pl +++ b/macros/PGchoicemacros.pl @@ -296,6 +296,10 @@ sub std_print_q { $i++; } $out .= "\\end{enumerate}\n"; + } elsif ($main::displayMode eq 'PTX') { + $out = "
      \n
    1. "; + $out .= join("
    2. \n
    3. ",@questions); + $out .= "
    4. \n
    "; } else { $out = "Error: PGchoicemacros: std_print_q: Unknown displayMode: $main::displayMode.\n"; } @@ -356,6 +360,10 @@ sub pop_up_list_print_q { $i++; } $out .= "\\end{enumerate}\n"; + } elsif ($main::displayMode eq 'PTX') { + $out = "
      \n
    1. "; + $out .= join("
    2. \n
    3. ",@questions); + $out .= "
    4. \n
    "; } else { $out = "Error: PGchoicemacros: pop_up_list_print_q: Unknown displayMode: $main::displayMode.\n"; } @@ -417,8 +425,12 @@ sub quest_first_pop_up_list_print_q { $i++; } $out .= "\\end{enumerate}\n"; + } elsif ($main::displayMode eq 'PTX') { + $out = "
      \n
    1. "; + $out .= join("
    2. \n
    3. ",@questions); + $out .= "
    4. \n
    "; } else { - $out = "Error: PGchoicemacros: pop_up_list_print_q: Unknown displayMode: $main::displayMode.\n"; + $out = "Error: PGchoicemacros: quest_first_pop_up_list_print_q : Unknown displayMode: $main::displayMode.\n"; } $out; @@ -477,8 +489,12 @@ sub ans_in_middle_pop_up_list_print_q { $i++; } $out .= "\\end{enumerate}\n"; + } elsif ($main::displayMode eq 'PTX') { + $out = "
      \n
    1. "; + $out .= join("
    2. \n
    3. ",@questions); + $out .= "
    4. \n
    "; } else { - $out = "Error: PGchoicemacros: pop_up_list_print_q: Unknown displayMode: $main::displayMode.\n"; + $out = "Error: PGchoicemacros: ans_in_middle_pop_up_list_print_q: Unknown displayMode: $main::displayMode.\n"; } $out; @@ -537,29 +553,32 @@ sub std_print_a { my $i = 0; my @alpha = ('A'..'Z', 'AA'..'ZZ'); my $letter; - my $out= &main::M3( - "\\begin{enumerate}\n", - " \\begin{rawhtml}
      \\end{rawhtml} ", + my $out= &main::MODES( + TeX=> "\\begin{enumerate}\n", + Latex2HTML=> " \\begin{rawhtml}
        \\end{rawhtml} ", # kludge to fix IE/CSS problem #"
          \n" - "
          \n" + HTML=> "
          \n", + PTX=> '
            '."\n", ) ; my $elem; foreach $elem (@array) { $letter = shift @alpha; - $out .= &main::M3( - "\\item[$main::ALPHABET[$i].] $elem\n", - " \\begin{rawhtml}
          1. \\end{rawhtml} $elem ", + $out .= &main::MODES( + TeX=> "\\item[$main::ALPHABET[$i].] $elem\n", + Latex2HTML=> " \\begin{rawhtml}
          2. \\end{rawhtml} $elem ", #"
          3. $elem
          4. \n" - "
            $letter. $elem\n" + HTML=>"
            $letter. $elem\n", + PTX=>"
          5. $elem
          6. \n", ) ; $i++; } - $out .= &main::M3( - "\\end{enumerate}\n", - " \\begin{rawhtml}
          \n \\end{rawhtml} ", + $out .= &main::MODES( + TeX=> "\\end{enumerate}\n", + Latex2HTML=>" \\begin{rawhtml}
        \n \\end{rawhtml} ", #"
      \n" - "\n" + HTML=> "\n", + PTX=> "
    ", ) ; $out; @@ -617,6 +636,10 @@ sub radio_print_a { #$out = "\n\\par\\begin{itemize}\n"; $out .= join '', @radio_buttons; #$out .= "\\end{itemize}\n"; + } elsif ($main::displayMode eq 'PTX') { + $out = ''."\n".'
  • '; + $out .= join("
  • \n
  • ", @answers); + $out .= "
  • \n
    \n"; } else { $out = "Error: PGchoicemacros: radio_print_a: Unknown displayMode: $main::displayMode.\n"; } @@ -675,6 +698,10 @@ sub checkbox_print_a { #$out = "\n\\par\\begin{itemize}\n"; $out .= join '', @radio_buttons ; #$out .= "\\end{itemize}\n"; + } elsif ($main::displayMode eq 'PTX') { + $out = ''."\n".'
  • '; + $out .= join("
  • \n
  • ", @answers); + $out .= "
  • \n
    \n"; } else { $out = "Error: PGchoicemacros: checkbox_print_a: Unknown displayMode: $main::displayMode.\n"; } diff --git a/macros/PGessaymacros.pl b/macros/PGessaymacros.pl index 2483172603..200914ac99 100644 --- a/macros/PGessaymacros.pl +++ b/macros/PGessaymacros.pl @@ -112,11 +112,12 @@ sub essay_cmp { my $out = MODES( TeX => qq!\\vskip $height in \\hrulefill\\quad !, Latex2HTML => qq!\\begin{rawhtml}\\end{rawhtml}!, - HTML => qq! + HTML => qq! - ! + !, + PTX => '', ); $out; @@ -127,14 +128,15 @@ sub essay_cmp { my $out = MODES( TeX => '', Latex2HTML => '', - HTML => qq! + HTML => qq!

    This is an essay answer text box. You can type your answer in here and, after you hit submit, it will be saved so that your instructor can grade it at a later date. If your instructor makes any comments on your answer those comments will appear on this page after the question has been graded. You can use LaTeX to make your math equations look pretty. LaTeX expressions should be enclosed using the parenthesis notation and not dollar signs.

    - ! + !, + PTX => '', ); $out; diff --git a/macros/PGfunctionevaluators.pl b/macros/PGfunctionevaluators.pl index b1141d21c0..3e45b36f79 100644 --- a/macros/PGfunctionevaluators.pl +++ b/macros/PGfunctionevaluators.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/PGmatrixmacros.pl b/macros/PGmatrixmacros.pl index 070646c63a..7982aa7eaa 100644 --- a/macros/PGmatrixmacros.pl +++ b/macros/PGmatrixmacros.pl @@ -12,11 +12,20 @@ =head1 SYNPOSIS =head1 DESCRIPTION -Almost all of the macros in the file are very rough at best. The most useful is display_matrix. -Many of the other macros work with vectors and matrices stored as anonymous arrays. +These macros are fairly old. The most useful is display_matrix and +its variants. -Frequently it may be -more useful to use the Matrix objects defined RealMatrix.pm and Matrix.pm and the constructs listed there. +Frequently it will be +most useful to use the MathObjects Matrix (defined in Value::Matrix.pm) +and Vector types which +have more capabilities and more error checking than the subroutines in +this file. These macros have no object orientation and +work with vectors and matrices +stored as perl anonymous arrays. + +There are also Matrix objects defined in +RealMatrix.pm and Matrix.pm but in almost all cases the +MathObjects Matrix types are preferable. =cut @@ -28,132 +37,57 @@ BEGIN sub _PGmatrixmacros_init { } -# this subroutine zero_check is not very well designed below -- if it is used much it should receive -# more work -- particularly for checking relative tolerance. More work needs to be done if this is -# actually used. - -sub zero_check{ - my $array = shift; - my %options = @_; - my $num = @$array; - my $i; - my $max = 0; my $mm; - for ($i=0; $i< $num; $i++) { - $mm = $array->[$i] ; - $max = abs($mm) if abs($mm) > $max; - } - my $tol = $options{tol}; - $tol = 0.01*$options{reltol}*$options{avg} if defined($options{reltol}) and defined $options{avg}; - $tol = .000001 unless defined($tol); - ($max <$tol) ? 1: 0; # 1 if the array is close to zero; -} -sub vec_dot{ - my $vec1 = shift; - my $vec2 = shift; - warn "vectors must have the same length" unless @$vec1 == @$vec2; # the vectors must have the same length. - my @vec1=@$vec1; - my @vec2=@$vec2; - my $sum = 0; - - while(@vec1) { - $sum += shift(@vec1)*shift(@vec2); - } - $sum; -} -sub proj_vec { - my $vec = shift; - warn "First input must be a column matrix" unless ref($vec) eq 'Matrix' and ${$vec->dim()}[1] == 1; - my $matrix = shift; # the matrix represents a set of vectors spanning the linear space - # onto which we want to project the vector. - warn "Second input must be a matrix" unless ref($matrix) eq 'Matrix' and ${$matrix->dim()}[1] == ${$vec->dim()}[0]; - $matrix * transpose($matrix) * $vec; -} - -sub vec_cmp{ #check to see that the submitted vector is a non-zero multiple of the correct vector - my $correct_vector = shift; - my %options = @_; - my $ans_eval = sub { - my $in = shift @_; - - my $ans_hash = new AnswerHash; - my @in = split("\0",$in); - my @correct_vector=@$correct_vector; - $ans_hash->{student_ans} = "( " . join(", ", @in ) . " )"; - $ans_hash->{correct_ans} = "( " . join(", ", @correct_vector ) . " )"; - - return($ans_hash) unless @$correct_vector == @in; # make sure the vectors are the same dimension - - my $correct_length = vec_dot($correct_vector,$correct_vector); - my $in_length = vec_dot(\@in,\@in); - return($ans_hash) if $in_length == 0; - - if (defined($correct_length) and $correct_length != 0) { - my $constant = vec_dot($correct_vector,\@in)/$correct_length; - my @difference = (); - for(my $i=0; $i < @correct_vector; $i++ ) { - $difference[$i]=$constant*$correct_vector[$i] - $in[$i]; - } - $ans_hash->{score} = zero_check(\@difference); - - } else { - $ans_hash->{score} = 1 if vec_dot(\@in,\@in) == 0; - } - $ans_hash; - - }; - - $ans_eval; -} ############ =head4 display_matrix - Usage \{ display_matrix( [ [1, '\(\sin x\)'], [ans_rule(5), 6] ]) \} - \{ display_matrix($A, align=>'crvl') \} - \[ \{ display_matrix_mm($A) \} \] - \[ \{ display_matrix_mm([ [1, 3], [4, 6] ]) \} \] - - display_matrix produces a matrix for display purposes. It checks whether - it is producing LaTeX output, or if it is displaying on a web page in one - of the various modes. The input can either be of type Matrix, Value::Matrix (mathobject) - or a reference to an array. - - Entries can be numbers, Fraction objects, bits of math mode, or answer - boxes. An entire row can be replaced by the string 'hline' to produce - a horizontal line in the matrix. - - display_matrix_mm functions similarly, except that it should be inside - math mode. display_matrix_mm cannot contain answer boxes in its entries. - Entries to display_matrix_mm should assume that they are already in - math mode. - - Both functions take an optional alignment string, similar to ones in - LaTeX tabulars and arrays. Here c for centered columns, l for left - flushed columns, and r for right flushed columns. - - The alignment string can also specify vertical rules to be placed in the - matrix. Here s or | denote a solid line, d is a dashed line, and v - requests the default vertical line. This can be set on a system-wide - or course-wide basis via the variable $defaultDisplayMatrixStyle, and - it can default to solid, dashed, or no vertical line (n for none). - - The matrix has left and right delimiters also specified by - $defaultDisplayMatrixStyle. They can be parentheses, square brackets, - braces, vertical bars, or none. The default can be overridden in - an individual problem with optional arguments such as left=>"|", or - right=>"]". - - You can specify an optional argument of 'top_labels'=> ['a', 'b', 'c']. - These are placed above the columns of the matrix (as is typical for - linear programming tableau, for example). The entries will be typeset - in math mode. - - Top labels require a bit of care. For image modes, they look better - with display_matrix_mm where it is all one big image, but they work with - display_matrix. With tth, you pretty much have to use display_matrix - since tth can't handle the TeX tricks used to get the column headers - up there if it gets the whole matrix at once. + Usage + \{ display_matrix( [ [1, '\(\sin x\)'], [ans_rule(5), 6] ]) \} + \{ display_matrix($A, align=>'crvl') \} + \[ \{ display_matrix_mm($A) \} \] + \[ \{ display_matrix_mm([ [1, 3], [4, 6] ]) \} \] + +display_matrix produces a matrix for display purposes. It checks whether +it is producing LaTeX output, or if it is displaying on a web page in one +of the various modes. The input can either be of type Matrix, Value::Matrix (mathobject) +or a reference to an array. + +Entries can be numbers, Fraction objects, bits of math mode, or answer +boxes. An entire row can be replaced by the string 'hline' to produce +a horizontal line in the matrix. + +display_matrix_mm functions similarly, except that it should be inside +math mode. display_matrix_mm cannot contain answer boxes in its entries. +Entries to display_matrix_mm should assume that they are already in +math mode. + +Both functions take an optional alignment string, similar to ones in +LaTeX tabulars and arrays. Here c for centered columns, l for left +flushed columns, and r for right flushed columns. + +The alignment string can also specify vertical rules to be placed in the +matrix. Here s or | denote a solid line, d is a dashed line, and v +requests the default vertical line. This can be set on a system-wide +or course-wide basis via the variable $defaultDisplayMatrixStyle, and +it can default to solid, dashed, or no vertical line (n for none). + +The matrix has left and right delimiters also specified by +$defaultDisplayMatrixStyle. They can be parentheses, square brackets, +braces, vertical bars, or none. The default can be overridden in +an individual problem with optional arguments such as left=>"|", or +right=>"]". + +You can specify an optional argument of 'top_labels'=> ['a', 'b', 'c']. +These are placed above the columns of the matrix (as is typical for +linear programming tableau, for example). The entries will be typeset +in math mode. + +Top labels require a bit of care. For image modes, they look better +with display_matrix_mm where it is all one big image, but they work with +display_matrix. With tth, you pretty much have to use display_matrix +since tth can't handle the TeX tricks used to get the column headers +up there if it gets the whole matrix at once. =cut @@ -291,6 +225,9 @@ sub dm_begin_matrix { or $main::displayMode eq 'HTML_img') { $out .= qq!\n!; } + elsif ( $main::displayMode eq 'PTX' ) { + $out .= qq!\n\n!; + } else { $out = "Error: dm_begin_matrix: Unknown displayMode: $main::displayMode.\n"; } @@ -339,6 +276,8 @@ sub dm_special_tops { $out .= "$brh$erh"; } $out .= ""; + } + elsif ( $main::displayMode eq 'PTX' ) { } else { $out = "Error: dm_begin_matrix: Unknown displayMode: $main::displayMode.\n"; } @@ -348,7 +287,7 @@ sub dm_special_tops { sub dm_mat_left { my $numrows = shift; my %opts = @_; - if ($main::displayMode eq 'TeX' or $opts{'force_tex'}) { + if ($main::displayMode eq 'TeX' or $opts{'force_tex'} or $main::displayMode eq 'PTX') { return ""; # left delim is built into begin matrix } my $out=''; @@ -392,7 +331,7 @@ sub dm_mat_right { } - if ($main::displayMode eq 'TeX' or $opts{'force_tex'}) { + if ($main::displayMode eq 'TeX' or $opts{'force_tex'} or $main::displayMode eq 'PTX') { return ""; } @@ -442,6 +381,9 @@ sub dm_end_matrix { or $main::displayMode eq 'HTML_img') { $out .= "
    $erh". ' \('.$j.'\)'."$brh
    \n"; } + elsif ( $main::displayMode eq 'PTX') { + $out .= qq!\n\n!; + } else { $out = "Error: PGmatrixmacros: dm_end_matrix: Unknown displayMode: $main::displayMode.\n"; } @@ -596,6 +538,16 @@ sub dm_mat_row { } if(not $opts{'isfirst'}) {$out .="$brh$erh\n";} } + elsif ($main::displayMode eq 'PTX') { + $out .= "\n"; + while (@elements) { + $colcount++; + $out .= ''; + $out .= shift(@elements); + $out .= "\n"; + } + $out .= "\n"; + } else { $out = "Error: dm_mat_row: Unknown displayMode: $main::displayMode.\n"; } @@ -692,6 +644,7 @@ sub mbox { =head4 ra_flatten_matrix Usage: ra_flatten_matrix($A) + returns: [a11, a12,a21,a22] where $A is a matrix object The output is a reference to an array. The matrix is placed in the array by iterating @@ -715,9 +668,17 @@ sub ra_flatten_matrix{ \@array; } -# This subroutine is probably obsolete and not generally useful. It was patterned after the APL -# constructs for multiplying matrices. It might come in handy for non-standard multiplication of -# of matrices (e.g. mod 2) for indice matrices. + +=head4 apl_matrix_mult() + + # This subroutine is probably obsolete and not generally useful. + # It was patterned after the APL + # constructs for multiplying matrices. It might come in handy + # for non-standard multiplication of + # of matrices (e.g. mod 2) for indice matrices. + +=cut + sub apl_matrix_mult{ my $ra_a= shift; my $ra_b= shift; @@ -763,9 +724,11 @@ sub make_matrix{ =head4 create2d_matrix -This can be a useful method for quickly entering small matrices by hand. --MEG +This can be a useful method for quickly entering small matrices by hand. + --MEG - create2d_matrix("1 2 4, 5 6 8"); + create2d_matrix("1 2 4, 5 6 8"); or + create2d_matrix("1 2 4; 5 6 8"); produces the anonymous array [[1,2,4],[5,6,8] ] @@ -775,11 +738,42 @@ =head4 create2d_matrix sub create2d_matrix { my $string = shift; - my @rows = split("\\s*,\\s*",$string); + my @rows = split("\\s*[,;]\\s*",$string); @rows = map {[split("\\s", $_ )]} @rows; [@rows]; } + +=head2 convert_to_array_ref { + + $output_matrix = convert_to_array_ref($input_matrix) + +Converts a MathObject matrix (ref($input_matrix) eq 'Value::Matrix') +or a MatrixReal1 matrix (ref($input_matrix) eq 'Matrix') to +a reference to an array (e.g [[4,6],[3,2]]). +This adaptor allows all of the LinearProgramming.pl subroutines to be used with +MathObject arrays. + +$mathobject_matrix->value outputs an array (usually an array of array references) so placing it inside +square bracket produces and array reference (of array references) which is what lp_display_mm() is +seeking. + +=cut + +sub convert_to_array_ref { + my $input = shift; + if (Value::isParser($input) and Value::classMatch($input,"Matrix")) { + $input = [$input->value]; + } elsif (ref($input) eq 'Matrix' ) { + $input = $input->array_ref; + } elsif (ref($input) =~/ARRAY/) { + # no change to input value + } else { + WARN_MESSAGE("This does not appear to be a matrix "); + } + $input; +} + =head4 check_matrix_from_ans_box_cmp An answer checker factory built on create2d_matrix. This still needs @@ -796,7 +790,6 @@ sub check_matrix_from_ans_box_cmp{ my $string_matrix_cmp = sub { $string = shift @_; my $studentMatrix; - # eval { $studentMatrix = Matrix(create2d_matrix($string)); die "I give up";}; #caught by op_mask $studentMatrix = Matrix(create2d_matrix($string)); die "I give up"; # main::DEBUG_MESSAGE(ref($studentMatrix). "$studentMatrix with error "); # errors are returned as warnings. Can't seem to trap them. @@ -815,67 +808,100 @@ sub check_matrix_from_ans_box_cmp{ } -=head2 convert_to_array_ref { - $output_matrix = convert_to_array_ref($input_matrix) +=head4 zero_check (deprecated -- use MathObjects matrices and vectors) -Converts a MathObject matrix (ref($input_matrix eq 'Value::Matrix') -or a MatrixReal1 matrix (ref($input_matrix eq 'Matrix')to -a reference to an array (e.g [[4,6],[3,2]]). -This adaptor allows all of the Linear Programming subroutines to be used with -MathObject arrays. + # this subroutine zero_check is not very well designed below -- if it is used much it should receive + # more work -- particularly for checking relative tolerance. More work needs to be done if this is + # actually used. -$mathobject_matrix->value outputs an array (usually an array of array references) so placing it inside -square bracket produces and array reference (of array references) which is what lp_display_mm() is -seeking. +=cut + +sub zero_check{ + my $array = shift; + my %options = @_; + my $num = @$array; + my $i; + my $max = 0; my $mm; + for ($i=0; $i< $num; $i++) { + $mm = $array->[$i] ; + $max = abs($mm) if abs($mm) > $max; + } + my $tol = $options{tol}; + $tol = 0.01*$options{reltol}*$options{avg} if defined($options{reltol}) and defined $options{avg}; + $tol = .000001 unless defined($tol); + ($max <$tol) ? 1: 0; # 1 if the array is close to zero; +} + +=head4 vec_dot() (deprecated -- use MathObjects vectors and matrices) + +sub vec_dot{ + my $vec1 = shift; + my $vec2 = shift; + warn "vectors must have the same length" unless @$vec1 == @$vec2; # the vectors must have the same length. + my @vec1=@$vec1; + my @vec2=@$vec2; + my $sum = 0; + + while(@vec1) { + $sum += shift(@vec1)*shift(@vec2); + } + $sum; +} + +=head4 proj_vect (deprecated -- use MathObjects vectors and matrices) =cut -sub convert_to_array_ref { - my $input = shift; - if (ref($input) eq 'Value::Matrix' ) { - $input = [$input->value]; - } elsif (ref($input) eq 'Matrix' ) { - $input = $input->array_ref; - } elsif (ref($input) =~/ARRAY/) { - # no change to input value - } else { - WARN_MESSAGE("This does not appear to be a matrix "); - } - $input; +sub proj_vec { + my $vec = shift; + warn "First input must be a column matrix" unless ref($vec) eq 'Matrix' and ${$vec->dim()}[1] == 1; + my $matrix = shift; # the matrix represents a set of vectors spanning the linear space + # onto which we want to project the vector. + warn "Second input must be a matrix" unless ref($matrix) eq 'Matrix' and ${$matrix->dim()}[1] == ${$vec->dim()}[0]; + $matrix * transpose($matrix) * $vec; +} + +=head4 vec_cmp (deprecated -- use MathObjects vectors and matrices) + +=cut + + +sub vec_cmp{ #check to see that the submitted vector is a non-zero multiple of the correct vector + my $correct_vector = shift; + my %options = @_; + my $ans_eval = sub { + my $in = shift @_; + + my $ans_hash = new AnswerHash; + my @in = split("\0",$in); + my @correct_vector=@$correct_vector; + $ans_hash->{student_ans} = "( " . join(", ", @in ) . " )"; + $ans_hash->{correct_ans} = "( " . join(", ", @correct_vector ) . " )"; + + return($ans_hash) unless @$correct_vector == @in; # make sure the vectors are the same dimension + + my $correct_length = vec_dot($correct_vector,$correct_vector); + my $in_length = vec_dot(\@in,\@in); + return($ans_hash) if $in_length == 0; + + if (defined($correct_length) and $correct_length != 0) { + my $constant = vec_dot($correct_vector,\@in)/$correct_length; + my @difference = (); + for(my $i=0; $i < @correct_vector; $i++ ) { + $difference[$i]=$constant*$correct_vector[$i] - $in[$i]; + } + $ans_hash->{score} = zero_check(\@difference); + + } else { + $ans_hash->{score} = 1 if vec_dot(\@in,\@in) == 0; + } + $ans_hash; + + }; + + $ans_eval; } -# sub format_answer{ -# my $ra_eigenvalues = shift; -# my $ra_eigenvectors = shift; -# my $functionName = shift; -# my @eigenvalues=@$ra_eigenvalues; -# my $size= @eigenvalues; -# my $ra_eigen = make_matrix( sub {my ($i,$j) = @_; ($i==$j) ? "e^{$eigenvalues[$j] t}": 0 }, $size,$size); -# my $out = qq! -# $functionName(t) =! . -# displayMatrix(apl_matrix_mult($ra_eigenvectors,$ra_eigen, -# 'times'=>sub{($_[0] and $_[1]) ? "$_[0]$_[1]" : ''}, -# 'plus'=>sub{ my $out = join("",@_); ($out) ?$out : '0' } -# ) ) ; -# $out; -# } -# sub format_vector_answer{ -# my $ra_eigenvalues = shift; -# my $ra_eigenvectors = shift; -# my $functionName = shift; -# my @eigenvalues=@$ra_eigenvalues; -# my $size= @eigenvalues; -# my $ra_eigen = make_matrix( sub {my ($i,$j) = @_; ($i==$j) ? "e^{$eigenvalues[$j] t}": 0 }, $size,$size); -# my $out = qq! -# $functionName(t) =! . -# displayMatrix($ra_eigenvectors)."e^{$eigenvalues[0] t}" ; -# $out; -# } -# sub format_question{ -# my $ra_matrix = shift; -# my $out = qq! y'(t) = ! . displayMatrix($B). q! y(t)! -# -# } 1; diff --git a/macros/PGmiscevaluators.pl b/macros/PGmiscevaluators.pl index 3267dee939..ef9c8e0a9d 100644 --- a/macros/PGmiscevaluators.pl +++ b/macros/PGmiscevaluators.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/PGmorematrixmacros.pl b/macros/PGmorematrixmacros.pl index b2af761786..c6222b40ec 100644 --- a/macros/PGmorematrixmacros.pl +++ b/macros/PGmorematrixmacros.pl @@ -5,9 +5,24 @@ BEGIN # set the prefix used for arrays. our $ArRaY = $main::PG->{ARRAY_PREFIX}; +=head2 NAME + + macros/PGmorematrixmacros.pl + +=cut + + sub _PGmorematrixmacros_init{} -sub random_inv_matrix { ## Builds and returns a random invertible \$row by \$col matrix. +=head4 random_inv_matrix + +## Builds and returns a random invertible \$row by \$col matrix. + +=cut + + +sub random_inv_matrix { +## Builds and returns a random invertible \$row by \$col matrix. warn "Usage: \$new_matrix = random_inv_matrix(\$rows,\$cols)" if (@_ != 2); @@ -54,16 +69,29 @@ sub random_diag_matrix{ ## Builds and returns a random diagonal \$n by \$n matri return $D; } +=head4 swap_rows ($matrix, $row1, $row2) + + (deprecated use MathObject Matrix instead) + +$matrix is assumed to be a RealMatrix1 object. +It is better to use MathObject Matrices and row swap mechanisms +from MatrixReduce.pl instead. + +=cut + + sub swap_rows{ warn "Usage: \$new_matrix = swap_rows(\$matrix,\$row1,\$row2);" if (@_ != 3); my $matrix = $_[0]; my ($i,$j) = ($_[1],$_[2]); + warn "Error: Rows to be swapped must exist!" if ($i>@$matrix or $j >@$matrix); warn "Warning: Swapping the same row is pointless" - if ($i==$j); + if ($i==$j); + my $cols = @{$matrix->[0]}; my $B = new Matrix(@$matrix,$cols); foreach my $k (1..$cols){ @@ -73,6 +101,16 @@ sub swap_rows{ return $B; } +=head4 row_mult ($matrix, $scaler, $row) + + (deprecated use MathObject Matrix instead) + +$matrix is assumed to be a RealMatrix1 object. +It is better to use MathObject Matrices and row swap mechanisms +from MatrixReduce.pl instead. + +=cut + sub row_mult{ warn "Usage: \$new_matrix = row_mult(\$matrix,\$scalar,\$row);" @@ -88,6 +126,18 @@ sub row_mult{ return $B; } +=head4 linear_combo($matrix, $scalar, $row1, $row2) + + (deprecated use MathObject Matrix instead) + +Adds a multiple of row1 to row2. + +$matrix is assumed to be a RealMatrix1 object. +It is better to use MathObject Matrices and subroutines +from MatrixReduce.pl instead. + +=cut + sub linear_combo{ warn "Usage: \$new_matrix = linear_combo(\$matrix,\$scalar,\$row1,\$row2);" @@ -106,6 +156,15 @@ sub linear_combo{ return $B; } + +=head2 + +These should be compared to similar subroutines made later in +MatrixCheckers.pl + + +=cut + =head3 basis_cmp() Compares a list of vectors by finding the change of coordinate matrix @@ -378,6 +437,8 @@ sub compare_basis { =head2 vec_list_string +(this is mostly obsolete. One should use MathObject Vectors instead. ) + This is a check_syntax type method (in fact I borrowed some of that method's code) for vector input. The student needs to enter vectors like: [1,0,0],[1,2,3],[0,9/sqrt(10),1/sqrt(10)] Each entry can contain functions and operations and the usual math constants (pi and e). @@ -503,8 +564,14 @@ sub vec_list_string{ $rh_ans; } + + + =head5 ans_array_filter + (this filter is not necessary when using MathObjects. It may someday be useful + again if the AnswerEvaluator pipeline is used to its fullest extent. ) + This filter was created to get, format, and evaluate each entry of the ans_array and ans_array_extension answer entry methods. Running this filter is necessary to get all the entries out of the answer hash. Each entry is evaluated and the resulting number is put in the display for student answer @@ -616,6 +683,20 @@ sub ans_array_filter{ } +=head3 + +The following subroutines, meant to be used with MatrixReal1 type matrices, are +deprecated. In general you should use the MathObject Matrix type and the +checking methods in MatrixCheckers.pl + + are_orthogonal_vecs($vec_ref, %opts) + is_diagonal($matrix, %opts) + are_unit_vecs($vec_ref, %opts) + display_correct_vecs($vec_ref, %opts) + vec_solution_cmp($vec,%opts) + filter: compare_vec_solution($rh_ans,%opts); + +=cut sub are_orthogonal_vecs{ my ($vec_ref , %opts) = @_; diff --git a/macros/PGnumericevaluators.pl b/macros/PGnumericevaluators.pl index e9937f2c0d..684806eab7 100644 --- a/macros/PGnumericevaluators.pl +++ b/macros/PGnumericevaluators.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/PGstandard.pl b/macros/PGstandard.pl index 1805136772..a6598259bb 100644 --- a/macros/PGstandard.pl +++ b/macros/PGstandard.pl @@ -21,6 +21,8 @@ =head1 DESCRIPTION =item * PGauxiliaryFunctions.pl +=item * customizeLaTeX.pl + =back =cut @@ -30,6 +32,7 @@ =head1 DESCRIPTION "PGbasicmacros.pl", "PGanswermacros.pl", "PGauxiliaryFunctions.pl", + "customizeLaTeX.pl", ); 1; diff --git a/macros/PGstringevaluators.pl b/macros/PGstringevaluators.pl index 9c474fef15..af4ebb1b3f 100644 --- a/macros/PGstringevaluators.pl +++ b/macros/PGstringevaluators.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/PGtextevaluators.pl b/macros/PGtextevaluators.pl index 9fbe48177b..c3ac67728c 100644 --- a/macros/PGtextevaluators.pl +++ b/macros/PGtextevaluators.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/Parser.pl b/macros/Parser.pl index 6deddb8441..6c94f98e54 100644 --- a/macros/Parser.pl +++ b/macros/Parser.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under @@ -132,12 +132,10 @@ =head2 Context # ^uses Parser::Context::current # ^uses %context sub Context {Parser::Context->current(\%context,@_)} -unless (%context && $context{current}) { - # ^variable our %context - %context = (); # Locally defined contexts, including 'current' context - # ^uses Context - Context(); # Initialize context (for persistent mod_perl) -} +# # ^variable our %context +%context = () unless %context; # Locally defined contexts, including 'current' context +# ^uses Context +Context("Numeric"); # Set initial context ########################################################################### # diff --git a/macros/alignedChoice.pl b/macros/alignedChoice.pl index fe0af9ed3b..b885e5150f 100644 --- a/macros/alignedChoice.pl +++ b/macros/alignedChoice.pl @@ -62,6 +62,17 @@ sub aligned_print_q { $out .= "\\ ". ans_rule($length) . $rest . "\\\\ \\noalign{\\kern $tsep}\n"; } $out .= "\\end{tabular}\n"; + } elsif ($main::displayMode eq "PTX") { + $out = "\n\n"; + foreach $quest (@questions) { + if (ref($quest) eq 'ARRAY') {($quest,$rest) = @{$quest}} else {$rest = ''} + $out .= "\n"; + $out .= "" . $i++ . ".\n" if ($numbered); + $out .= "$quest\n"; + $out .= "=\n" if ($equals); + $out .= "" . ans_rule($length) . $rest . "\n\n"; + } + $out .= "\n\n"; } else { $out = "Error: std_aligned_print_q: Unknown displayMode: ". $main::displayMode; diff --git a/macros/answerComposition.pl b/macros/answerComposition.pl index b4a3133307..84930396da 100644 --- a/macros/answerComposition.pl +++ b/macros/answerComposition.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/answerComposition.pl,v 1.8 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/answerCustom.pl b/macros/answerCustom.pl index 2fcc31ad50..2b166f9ef9 100644 --- a/macros/answerCustom.pl +++ b/macros/answerCustom.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/answerHints.pl b/macros/answerHints.pl index 2b0f2c3cd3..bb1a6400b9 100644 --- a/macros/answerHints.pl +++ b/macros/answerHints.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/answerVariableList.pl b/macros/answerVariableList.pl index 3d4e364705..8b0c7a1a5e 100644 --- a/macros/answerVariableList.pl +++ b/macros/answerVariableList.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/compoundProblem.pl b/macros/compoundProblem.pl index 26bd3ef1e4..51bf1f818c 100644 --- a/macros/compoundProblem.pl +++ b/macros/compoundProblem.pl @@ -520,7 +520,7 @@ sub nextForced { sub part { my $self = shift; my $status = $self->{status}; my $part = shift; - return $status->{part} unless defined $part && $main::displayMode ne 'TeX'; + return $status->{part} unless defined $part && $main::displayMode ne 'TeX' && $main::displayMode ne 'PTX'; $part = 1 if $part < 1; $part = $self->{parts} if $part > $self->{parts}; if ($part > $status->{part} && !$main::inputs_ref->{_noadvance}) { unless ((lc($self->{nextVisible}) eq 'ifcorrect' && $status->{raw} < 1) || diff --git a/macros/compoundProblem2.pl b/macros/compoundProblem2.pl index 810bd917c9..8718b07e92 100644 --- a/macros/compoundProblem2.pl +++ b/macros/compoundProblem2.pl @@ -101,10 +101,11 @@ sub DISPLAY_SECTION {

    Part: $name:

    - !, TeX=>"\\par{\\bf Part: $name }\\par")); + !, TeX=>"\\par{\\bf Part: $name }\\par", + PTX=>"\n")); my $rendered_text_string = EV3($text_string); TEXT( $rendered_text_string ) if $options{canshow}==1; - TEXT( MODES(HTML=>"

    ", TeX=>'\\par' ) ); + TEXT( MODES(HTML=>"

    ", TeX=>'\\par', PTX=>"\n" ) ); } @@ -112,16 +113,16 @@ sub DISPLAY_SECTION { # FIXME we will make a $cp object that keeps track of the part -sub BEGIN_SECTIONS {TEXT(MODES(HTML=>q!
      !,TeX=>'')); warn "start sections\n\n"; } +sub BEGIN_SECTIONS {TEXT(MODES(HTML=>q!
        !,TeX=>'',PTX=>'')); warn "start sections\n\n"; } sub END_SECTIONS { my $part = shift; - TEXT(MODES( HTML=>q!
      '')); + TEXT(MODES( HTML=>q!
    '',PTX=>'')); TEXT(MODES(HTML =>$PAR .qq! - ! , TeX=>'')); + ! , TeX=>'',PTX=>'')); } -1; \ No newline at end of file +1; diff --git a/macros/compoundProblem5.pl b/macros/compoundProblem5.pl index 348383f940..7393773e52 100644 --- a/macros/compoundProblem5.pl +++ b/macros/compoundProblem5.pl @@ -441,7 +441,7 @@ sub process_section { # Get the script to open or prevent the section from opening # my $action = $canshow ? "canshow()" : "cannotshow()"; - my $scriptpreamble = main::MODES(TeX=>'', HTML=>qq!!); + my $scriptpreamble = main::MODES(TeX=>'', PTX=>'', HTML=>qq!!); my $renderedtext = $canshow ? $section->{renderedtext} : '' ; $renderedtext = $scriptpreamble . "\n" . $renderedtext; $renderedtext .= $section->{solution} if main::not_null($section->{solution}); @@ -453,7 +453,8 @@ sub process_section { HTML=> qq!
  • Section: $name:

    $renderedtext

  • - !, TeX=>"\\par{\\bf Section: $name}\\par $renderedtext\\par" + !, TeX=>"\\par{\\bf Section: $name}\\par $renderedtext\\par", + PTX=>"\n$renderedtext\n", ); ($iscorrect,$canshow); } @@ -675,7 +676,7 @@ sub openSections { my $self = shift; my $script = ''; $self->HIDE_OTHER_RESULTS(@_); foreach my $s (@_) {$script .= qq!\$("#section$s").openSection()\n!;} - main::TEXT(main::MODES(TeX=>'', HTML=>qq!!)); + main::TEXT(main::MODES(TeX=>'', PTX=>'', HTML=>qq!!)); } diff --git a/macros/contextABCD.pl b/macros/contextABCD.pl index ef5a0008ff..774daa5be8 100644 --- a/macros/contextABCD.pl +++ b/macros/contextABCD.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextArbitraryString.pl b/macros/contextArbitraryString.pl index e6aacad5ff..ddfb34bf4b 100644 --- a/macros/contextArbitraryString.pl +++ b/macros/contextArbitraryString.pl @@ -112,7 +112,7 @@ sub quoteHTML { my $self = shift; my $s = $self->SUPER::quoteHTML(shift); $s = "
    $s
    " - unless $main::displayMode eq "TeX"; + unless ($main::displayMode eq "TeX" or $main::displayMode eq "PTX"); return $s; } diff --git a/macros/contextCurrency.pl b/macros/contextCurrency.pl index 3690c362ba..0b8111efb8 100644 --- a/macros/contextCurrency.pl +++ b/macros/contextCurrency.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/contextCurrency.pl,v 1.17 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under @@ -247,7 +247,7 @@ sub new { $context->operators->remove($symbol) if $context->operators->get($symbol); $context->operators->add( $symbol => {precedence => 10, associativity => $associativity, type => "unary", - string => ($main::displayMode eq 'TeX' ? Currency::quoteTeX($symbol) : $symbol), + string => (($main::displayMode eq 'TeX' or $main::displayMode eq 'PTX') ? Currency::quoteTeX($symbol) : $symbol), TeX => Currency::quoteTeX($symbol), class => 'Currency::UOP::currency'}, ); $context->{parser}{Number} = "Currency::Number"; diff --git a/macros/contextInequalities.pl b/macros/contextInequalities.pl index 983aed14fd..3d15d9d25f 100644 --- a/macros/contextInequalities.pl +++ b/macros/contextInequalities.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/contextInequalities.pl,v 1.23 2010/03/22 11:01:55 dpvc Exp $ # # This program is free software; you can redistribute it and/or modify it under @@ -696,6 +696,7 @@ sub display { push(@points,$X.$ne.$x->{data}[0]->$method($equation)); $interval = $interval->with(isCopy=>1, data=>[$interval->value]) unless $interval->{isCopy}; $interval->{data}[1] = $x->{data}[1]; + $interval->{close} = $x->{close}; $interval->{rightInfinite} = 1 if $x->{rightInfinite}; next; } diff --git a/macros/contextIntegerFunctions.pl b/macros/contextIntegerFunctions.pl index 83bcf5a40c..49c6ae9808 100644 --- a/macros/contextIntegerFunctions.pl +++ b/macros/contextIntegerFunctions.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextLimitedComplex.pl b/macros/contextLimitedComplex.pl index 80237dae07..f58cb1bf47 100644 --- a/macros/contextLimitedComplex.pl +++ b/macros/contextLimitedComplex.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextLimitedNumeric.pl b/macros/contextLimitedNumeric.pl index 725fc78259..adce6bdef0 100644 --- a/macros/contextLimitedNumeric.pl +++ b/macros/contextLimitedNumeric.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under @@ -28,9 +28,9 @@ =head1 DESCRIPTION one of the following commands: Context("LimitedNumeric-List"); - Context("LimiteNumeric"); + Context("LimitedNumeric"); -(Now uses Parcer::Legacy::LimitedNumeric to implement +(Now uses Parser::Legacy::LimitedNumeric to implement these contexts.) =cut diff --git a/macros/contextLimitedPoint.pl b/macros/contextLimitedPoint.pl index 080fc2a7c8..72d93d8083 100644 --- a/macros/contextLimitedPoint.pl +++ b/macros/contextLimitedPoint.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/contextLimitedPoint.pl,v 1.14 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextLimitedPolynomial.pl b/macros/contextLimitedPolynomial.pl index 22ab7e717a..7b63c0d05a 100644 --- a/macros/contextLimitedPolynomial.pl +++ b/macros/contextLimitedPolynomial.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under @@ -129,7 +129,7 @@ sub _check { return; } return if $self->checkPolynomial; - $self->Error("Your answer doesn't look like a polynomial"); + $self->Error("Your answer doesn't look like a simplified polynomial"); } # @@ -151,7 +151,7 @@ sub checkExponents { if ($l->{exponents}) { my $single = $self->context->flag('singlePowers'); foreach my $i (0..scalar(@{$exponents})-1) { - $self->Error("A variable can appear only once in each term of a polynomial") + $self->Error("A variable can appear only once in each term of a simplified polynomial") if $exponents->[$i] && $l->{exponents}[$i] && $single; $exponents->[$i] += $l->{exponents}[$i]; } @@ -244,7 +244,7 @@ sub checkPolynomial { sub checkStrict { my $self = shift; - $self->Error("You can only use '%s' between coefficents and variables in a polynomial",$self->{bop}); + $self->Error("You can only use '%s' between coefficents and variables in a simplified polynomial",$self->{bop}); } ############################################## @@ -257,7 +257,7 @@ sub checkPolynomial { my ($l,$r) = ($self->{lop},$self->{rop}); $self->Error("In a polynomial, you can only divide by numbers") unless LimitedPolynomial::isConstant($r); - $self->Error("You can only divide a single term by a number") + $self->Error("You can only divide a single term by a number in a simplified polynomial") if $l->{isPoly} && $l->{isPoly} != 2; $self->{isPoly} = $l->{isPoly}; @@ -279,7 +279,7 @@ package LimitedPolynomial::BOP::power; sub checkPolynomial { my $self = shift; my ($l,$r) = ($self->{lop},$self->{rop}); - $self->Error("You can only raise a variable to a power in a polynomial") + $self->Error("You can only raise a variable to a power in a simplifiedpolynomial") unless $l->class eq 'Variable'; $self->Error("Exponents must be constant in a polynomial") unless LimitedPolynomial::isConstant($r); @@ -297,7 +297,7 @@ sub checkPolynomial { sub checkStrict { my $self = shift; - $self->Error("You can only use powers of a variable in a polynomial"); + $self->Error("You can only use powers of a variable in a simplified polynomial"); } ############################################## diff --git a/macros/contextLimitedPowers.pl b/macros/contextLimitedPowers.pl index 02e3076886..adb18de1b2 100644 --- a/macros/contextLimitedPowers.pl +++ b/macros/contextLimitedPowers.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextLimitedVector.pl b/macros/contextLimitedVector.pl index e8804e7a6f..abe12ed044 100644 --- a/macros/contextLimitedVector.pl +++ b/macros/contextLimitedVector.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextPeriodic.pl b/macros/contextPeriodic.pl index 8c791b9164..b8186c881b 100644 --- a/macros/contextPeriodic.pl +++ b/macros/contextPeriodic.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextPermutation.pl b/macros/contextPermutation.pl index b118010044..eda3d87fb2 100644 --- a/macros/contextPermutation.pl +++ b/macros/contextPermutation.pl @@ -28,7 +28,7 @@ =head1 DESCRIPTION parentheses. Cycles are multiplied by juxtaposition. A permutation can be multiplied on the left by a number in order to obtain the result of that number under the action of the permutation. -Exponentiation is alos allowed (as described below). +Exponentiation is also allowed (as described below). There are three contexts included here: C, which allows permutations in any form, C, which diff --git a/macros/contextPiecewiseFunction.pl b/macros/contextPiecewiseFunction.pl index d18e227156..c3232614ce 100644 --- a/macros/contextPiecewiseFunction.pl +++ b/macros/contextPiecewiseFunction.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextReaction.pl b/macros/contextReaction.pl index d37faa81ad..63de15a5fd 100644 --- a/macros/contextReaction.pl +++ b/macros/contextReaction.pl @@ -23,6 +23,19 @@ =head1 DESCRIPTION $R = Formula("4P + 5O_2 --> 2P_2O_5"); +Ions can be specified using ^ to produce superscripts, as in Na^+1 or +Na^{+1}. Note that the charge must be listed with prefix notation +(+1), not postfix notation (1+), and that a number is required (so you +can't use just Na^+). + +States can be appended to compounds, as in AgCl(s). So you can +make reactions like the following: + + Ag^{+1}(aq) + Cl^{-1}(aq) --> AgCl(s) + +Note that a state can be given by itself, e.g., (l), so you can ask +for a student to supply just a state. + Reactions know how to create their own TeX versions (via $R->TeX), and know how to check student answers (via $R->cmp), just like any other MathObject. @@ -53,16 +66,27 @@ =head1 DESCRIPTION different and unequal in this context. All the elements of the periodic table are available within the -Reaction Context. If you need additional terms, like "Heat" for -example, you can add them as variables: +Reaction Context, as are the states (aq), (s), (l), (g), and (ppt). +If you need additional terms, like "Heat" for example, you can add +them as variables: Context()->variables->add(Heat => $context::Reaction::CONSTANT); Then you can make formulas that include Heat as a term. These -"constants" are not allowed to have coefficients or subscripts, and -can not be combined with compounds except by addition. If you want a -term that can be combined in those ways, use -$context::Reaction::ELEMENT instead. +"constants" are not allowed to have coefficients or sub- or +superscripts, and can not be combined with compounds except by +addition. If you want a term that can be combined in those ways, use +$context::Reaction::ELEMENT instead, as in + + Context()->variables->add(e => $context::Reaction::ELEMENT); + +to allow "e" for electrons, for example. + +If you need to add more states, use $context::Reaction::STATE, as in + + Context()->variables->add('(x)' => $context::Reaction::STATE); + +to allow a state of (x) for a compound. =cut @@ -82,15 +106,18 @@ package context::Reaction; # our $ELEMENT = {isValue => 1, type => Value::Type("Element",1)}; our $MOLECULE = {isValue => 1, type => Value::Type("Molecule",1)}; +our $ION = {isValue => 1, type => Value::Type("Ion",1)}; our $COMPOUND = {isValue => 1, type => Value::Type("Compound",1)}; our $REACTION = {isValue => 1, type => Value::Type("Reaction",1)}; our $CONSTANT = {isValue => 1, type => Value::Type("Constant",1)}; +our $STATE = {isValue => 1, type => Value::Type("State",1)}; # # Set up the context and Reaction() constructor # sub Init { my $context = $main::context{Reaction} = Parser::Context->getCopy("Numeric"); + $context->{name} = "Reaction"; $context->functions->clear(); $context->strings->clear(); $context->constants->clear(); @@ -108,7 +135,7 @@ sub Init { '-->' => {precedence => 1, associativity => 'left', type => 'bin', string => ' --> ', class => 'context::Reaction::BOP::arrow', TeX => " \\longrightarrow "}, - '+' => {precedence => 2, associativity => 'left', type => 'bin', string => ' + ', + '+' => {precedence => 2, associativity => 'left', type => 'both', string => ' + ', class => 'context::Reaction::BOP::add', isComma => 1}, ' ' => {precedence => 3, associativity => 'left', type => 'bin', string => ' ', @@ -117,11 +144,17 @@ sub Init { '_' => {precedence => 4, associativity => 'left', type => 'bin', string => '_', class => 'context::Reaction::BOP::underscore'}, - '-' => {precedence => 5, associativity => 'left', type => 'both', string => ' - ', + '^' => {precedence => 4, associativity => 'left', type => 'bin', string => '^', + class => 'context::Reaction::BOP::superscript'}, + + '-' => {precedence => 5, associativity => 'left', type => 'both', string => '-', class => 'Parser::BOP::undefined'}, 'u-'=> {precedence => 6, associativity => 'left', type => 'unary', string => '-', - class => 'Parser::UOP::undefined', hidden => 1}, + class => 'context::Reaction::UOP::minus', hidden => 1}, + 'u+'=> {precedence => 6, associativity => 'left', type => 'unary', string => '+', + class => 'context::Reaction::UOP::plus', hidden => 1}, ); + $context->variables->{namePattern} = qr/\(?[a-zA-Z][a-zA-Z0-9]*\)?/; $context->variables->are( map {$_ => $ELEMENT} ( "H", "He", @@ -130,18 +163,26 @@ sub Init { "K", "Ca", "Sc","Ti","V", "Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Ge","As","Se","Br","Kr", "Rb","Sr", "Y", "Zr","Nb","Mo","Tc","Ru","Rh","Pd","Ag","Cd","In","Sn","Sb","Te","I", "Xe", "Cs","Ba", "Lu","Hf","Ta","W", "Re","Os","Ir","Pt","Au","Hg","Ti","Pb","Bi","Po","At","Rn", - "Fr","Ra", "Lr","Rf","Db","Sg","Bh","Hs","Mt","Ds","Rg","Cn","Uut","Uuq","Uup","Uuh","Uus","Uuo", + "Fr","Ra", "Lr","Rf","Db","Sg","Bh","Hs","Mt","Ds","Rg","Cn","Nh","Fl","Mc","Lv","Ts","Og", "La","Ce","Pr","Nd","Pm","Sm","Eu","Gd","Tb","Dy","Ho","Er","Tm","Yb", "Ac","Th","Pa","U", "Np","Pu","Am","Cm","Bk","Cf","Es","Fm","Md","No", ) ); + $context->variables->add( + map {$_ => $STATE} ( + "(aq)", "(s)", "(l)", "(g)", "(ppt)", + ) + ); + $context->reductions->clear(); + $context->flags->set(reduceConstants => 0); $context->{parser}{Number} = "context::Reaction::Number"; $context->{parser}{Variable} = "context::Reaction::Variable"; $context->{parser}{Formula} = "context::Reaction"; $context->{value}{Reaction} = "context::Reaction"; $context->{value}{Element} = "context::Reaction::Variable"; $context->{value}{Constant} = "context::Reaction::Variable"; + $context->{value}{State} = "context::Reaction::Variable"; Parser::Number::NoDecimals($context); main::PG_restricted_eval('sub Reaction {Value->Package("Formula")->new(@_)};'); @@ -254,7 +295,7 @@ sub TeX { # sub TYPE { my $self = shift; - return ($self->type eq 'Constant'? "'$self->{name}'" : 'an element'); + return ($self->type eq 'Constant' || $self->type eq 'State' ? 'a state' : 'an element'); } ###################################################################### @@ -357,11 +398,14 @@ sub _check { $self->Error("Can't combine %s and %s",$self->{lop}->TYPE,$self->{rop}->TYPE) unless ($self->{lop}->class eq 'Number' || $self->{lop}->isChemical) && $self->{rop}->isChemical; + $self->Error("Compound already has a state") + if $self->{lop}{hasState} && $self->{rop}->type eq 'State'; $self->Error("Can't combine %s with %s",$self->{lop}{name},$self->{rop}->TYPE) if $self->{lop}->type eq 'Constant'; $self->Error("Can't combine %s with %s",$self->{lop}->TYPE,$self->{rop}{name}) if $self->{rop}->type eq 'Constant'; $self->{type} = $COMPOUND->{type}; + $self->{hasState} = 1 if $self->{rop}->type eq 'State'; } # @@ -419,6 +463,93 @@ sub string { sub TYPE {'a molecule'} +###################################################################### +# +# Implements the superscript for creating ions +# +package context::Reaction::BOP::superscript; +our @ISA = ('context::Reaction::BOP'); + +# +# Check that the operands are OK +# +sub _check { + my $self = shift; + $self->Error("The left-hand side of '^' must be an element or molecule, not %s",$self->{lop}->TYPE) + unless $self->{lop}->type eq 'Element' || $self->{lop}->type eq 'Molecule'; + $self->Error("The right-hand side of '^' must be a signed number, not %s",$self->{rop}->TYPE) + unless $self->{rop}->class eq 'UOP'; + $self->{type} = $ION->{type}; +} + +# +# Create proper TeX output +# +sub TeX { + my $self = shift; + my $left = $self->{lop}->TeX; + return $left."^{".$self->{rop}->TeX."}"; +} + +# +# Create proper text output +# +sub string { + my $self = shift; + my $left = $self->{lop}->string; + return $left."^".$self->{rop}->string; +} + +sub TYPE {'an ion'} + +###################################################################### +# +# General unary operator (minus and plus are subclasses of this). +# +package context::Reaction::UOP; +our @ISA = ('Parser::UOP'); + +sub _check { + my $self = shift; + return if ($self->checkNumber); + $self->{type} = $Value::Type{number}; +} + +# +# Unary operators produce numbers +# +sub isChemical {0} + +sub eval {context::Reaction::eval(@_)} + +# +# Two nodes are equivalent if their operands are equivalent +# and they have the same operator +# +sub equivalent { + my $self = shift; my $other = shift; + return 0 unless $other->class eq 'UOP'; + return 0 unless $self->{uop} eq $other->{uop}; + return $self->{op}->equivalent($other->{op}); +} + +sub TYPE {'a signed number'}; + +###################################################################### +# +# Negative numbers (for ion exponents) +# +package context::Reaction::UOP::minus; +our @ISA = ('context::Reaction::UOP'); + +###################################################################### +# +# Positive numbers (for ion exponents) +# +package context::Reaction::UOP::plus; +our @ISA = ('context::Reaction::UOP'); + + ###################################################################### # # Implements sums of compounds as a list diff --git a/macros/contextScientificNotation.pl b/macros/contextScientificNotation.pl index ab3299e32a..c098724e6c 100644 --- a/macros/contextScientificNotation.pl +++ b/macros/contextScientificNotation.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextString.pl b/macros/contextString.pl index 7187dede0c..6a323c3b5b 100644 --- a/macros/contextString.pl +++ b/macros/contextString.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/contextTF.pl b/macros/contextTF.pl index c7aebfc625..22fe43c053 100644 --- a/macros/contextTF.pl +++ b/macros/contextTF.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/customizeLaTeX.pl b/macros/customizeLaTeX.pl new file mode 100644 index 0000000000..7b22290d98 --- /dev/null +++ b/macros/customizeLaTeX.pl @@ -0,0 +1,130 @@ +=head1 NAME + +customizeLaTeX.pl - Defines default LaTeX constructs for certain mathematical + ideas. + +=head1 DESCRIPTION + +The functions are loaded by default. Any/all can be overridden +in your course's PGcourse.pl +=cut + +sub _customizeLaTeX_init { + +} #prevents this file from being loaded twice. + +##### Set theory macros +sub set_minus{ + #return "\\setminus"; + return '-'; +}; + +##### Logic macros +sub negate { + return "\\mathbin{\\sim}"; + #return "\\lnot"; +}; + +sub implies { + return "\\implies"; + #return "\\Rightarrow"; +} + +##### Linear algebra macros + +sub vectorstyle { + my $v = shift; + return "\\vec{$v}" + #return "$v"; +} + +sub polynomials_of_degree_up_to_degree_over_ring_in_variable { + my ($n, $R, $variable) = @_; + return $R."[".$variable."]_{\\mathrm{Grad} \\leq ".$n."}"; +} + +sub matrix_of_homomorphism_with_respect_to_bases { + my ($homomorphism, $basis_source, $basis_target) = @_; + return "{}^{$basis_target}{".$homomorphism."}^{$basis_source}"; +} + +sub coordinates_of_vector_with_respect_to_basis { + my ($vector, $basis) = @_; + return "{}^{$basis}{(".$vector.")}"; +} + +sub span { + my ($set) = @_; + return "\\langle $set \\rangle"; +} + +sub matrices_over_ring { + my ($rows, $columns, $ring) = @_; + return "{$ring}^{$rows \\times $columns}"; + # return "M_{$rows \\times $columns}($ring)"; + # return "M_{$rows, $columns}($ring)"; +} + +##### Algebra macros + +sub cyclic { + + my $n = shift; + + # leave one of the following return commands uncommented, depending on what notation you want to use for finite cyclic groups (e.g., Z/nZ) + + # display order n cyclic group as Z_n + return "\\mathbb{Z}_{$n}"; + + # display order n cyclic group as C_n + # return "C_{$n}"; + + # display order n cyclic group as Z/nZ + # return "\\mathbb{Z}/{$n}\\mathbb{Z}"; + +}; + +# Macro to display the ring Z/nZ +sub ZmodnZ { + my $n = shift; + return "\\mathbb{Z} / $n \\mathbb{Z}"; +} + +sub dihedral { + + my $n = shift; + + # if you want to display dihedral groups as D_n (for instance, D_4 is the dihedral group of order 8), then leave this subroutine unmodified + + + # if you want to display dihedral groups as D_{2n} (for instance, D_8 is the dihedral group of order 8), then uncomment this set of if/else statements. The regular expression conditionals are to make sure it handles different types of arguments correctly. + # if( "$n" =~ m/^\s*(\d+)\s*$/ ) + # { + # $n = 2 * $1; + # } + # elsif( "$n" =~ m/^\s*(\w+)\s*$/ ) + # { + # $n = "2$1"; + # } + # else + # { + # $n = "2($n)"; + # } + + return "D_{$n}"; + +}; + +sub quaternions { + + # if you want to display the Quaternion group as Q_8, then leave this subroutine unmodified + + return "Q_8" + + # Alternatives + + # return "H_8" + # return "Q" +}; + +1; diff --git a/macros/extraAnswerEvaluators.pl b/macros/extraAnswerEvaluators.pl index 5464c61ac7..fb3af4b9e5 100644 --- a/macros/extraAnswerEvaluators.pl +++ b/macros/extraAnswerEvaluators.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/niceTables.pl b/macros/niceTables.pl index 79e9884f5b..571d809222 100644 --- a/macros/niceTables.pl +++ b/macros/niceTables.pl @@ -15,7 +15,7 @@ ## ## NOTE: In order to reduce separate setting of on-screen and hard copy settings as much as possible, Perl 5.10+ ## tools are used. These macros may behave unexpectedly or not work at all with older versions of Perl. -## These macros use LaTeX packages inthe hard copy that wer not formerly part of a WeBWorK hard copy preamble. +## These macros use LaTeX packages in the hard copy that wer not formerly part of a WeBWorK hard copy preamble. ## Your LaTeX distribution needs to have the packages: booktabs, tabularx, colortbl, caption, xcolor ## And if you have a WeBWorK version earlier than 2.10, you need to add calls to these packages to hardcopyPreamble.tex ## in webwork2/conf/snippets/ @@ -36,6 +36,11 @@ =head2 pccTables.pl # Generally, you give settings for the hard copy tex version first. Many common such settings are automatically # translated into CSS styling for the on-screen. You can then override or augment the CSS for the on-screen version. # + # With PTX output, not all features below are supported. Perhaps they can be added upon request. + # Contact Alex Jordan with questions. + # This version supports the center, caption, midrules, encase, and noencase options in PTX. It also honors the + # horizontal alignment portions of the align option (but not vertical rules or anything found in @{}). + # # Options for the WHOLE TABLE # # Applies to on-screen *and* hard copy: @@ -324,7 +329,9 @@ sub DataTable { # alignment: p{width}, r, c, l, or X my @alignmentcolumns; for my $i (0..$#columnalignments) {$alignmentcolumns[$columnalignments[$i]] = $i}; - # @alignmentcolumns is an array with one element per column, where the elements are each one of p{width}, r, c, l, or X + # @alignmentcolumns is an array whose ith element is undefined unless the ith element of @htmlalignment was one + # of p{width}, r, c, l, or X. Otherwise it is the index of the entry in @columnalignments that corresponds to + # that alignment # append css to author's columnscss->[$i] that corresponds to the alignemnts in @alignmentcolumns for my $i (0..$#columnalignments) { @@ -441,7 +448,9 @@ sub DataTable { my @alignmentcolumns; for my $k (0..$#columnalignments) {$alignmentcolumns[$columnalignments[$k]] = $k}; - # @alignmentcolumns is an array with one element per column, where the elements are each one of p{width}, r, c, l, or X + # @alignmentcolumns is an array whose ith element is undefined unless the ith element of @htmlalignment was one + # of p{width}, r, c, l, or X. Otherwise it is the index of the entry in @columnalignments that corresponds to + # that alignment # Again, this should only have one entry. for my $k (0..$#columnalignments) { @@ -494,15 +503,23 @@ sub DataTable { if ($midrules == 1) {$midrulescss = 'border-top:solid 1px; '}; my $table = ''; - # build html string for the table + my $ptxtable = ''; + # build html and ptx strings for the table (which have structural similarities that distinguish them from tex) if ($options{LaYoUt} != 1) { $table = ''; - if ($caption ne '') {$table .= '';} + $ptxtable = "\n\n"; $table .= ''; for my $i (0..$#{$columnscss}) {$columnscss->[$i] = '' unless (defined($columnscss->[$i])); - $table .= '';}; + $table .= ''; + }; $table .= ''; + if ($caption ne '') { + $table .= ''; + # Needs to be a better way to incorporate the caption into PTX output + # This way makes "captions" that extend past the table + #$ptxtable .= "\n".''.$caption.''."\n\n"; + } my $bodystarted = 0; for my $i (0..$#{$dataref}) {my $midrulecss = ($midrule[$i] == 1) ? 'border-bottom:solid 1px; ' : ''; @@ -510,6 +527,7 @@ sub DataTable { if ($headerrow[$i] == 1) {$table .= ''; } elsif (!$bodystarted) {$table .= ''; $bodystarted = 1}; $table .= ''; + $ptxtable .= "\n"; for my $j (0..$numcols[$i]) {my $colspan = (${$dataref->[$i][$j]}{colspan} eq '') ? '' : 'colspan = "'.${$dataref->[$i][$j]}{colspan}.'" '; if (uc(${$dataref->[$i][$j]}{header}) eq 'TH') @@ -523,12 +541,15 @@ sub DataTable { elsif (uc($headerrow[$i]) == 1) {$table .= '';} else {$table .= '';} + $ptxtable .= '' . ${$dataref->[$i][$j]}{data} . '' . "\n"; } $table .= ""; + $ptxtable .= "\n"; if ($headerrow[$i] == 1) {$table .= '';} elsif ($bodystarted and ($i == $#{$dataref})) {$table .= '';}; }; $table .= "
    '.$caption.''.$caption.'
    '.${$dataref->[$i][$j]}{data}.''.${$dataref->[$i][$j]}{data}.'
    "; + $ptxtable .= "\n"; }# now if it is a Layout Table... else { $table = '
    '; @@ -618,6 +639,7 @@ sub DataTable { MODES( TeX => $textable, HTML => $table, + PTX => $ptxtable, ); } @@ -634,7 +656,9 @@ sub DataTable { sub LayoutTable { my $dataref = shift; - DataTable($dataref,LaYoUt=>1,@_); + if ($main::displayMode eq 'PTX') + {DataTable($dataref,@_);} + else {DataTable($dataref,LaYoUt=>1,@_);}; } @@ -665,6 +689,4 @@ sub TeX_Alignment_to_CSS { return $css; } - - 1; diff --git a/macros/parserAssignment.pl b/macros/parserAssignment.pl index f4b86c0d81..539a75c9d6 100644 --- a/macros/parserAssignment.pl +++ b/macros/parserAssignment.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserAutoStrings.pl b/macros/parserAutoStrings.pl index 6347730b11..e260701d73 100644 --- a/macros/parserAutoStrings.pl +++ b/macros/parserAutoStrings.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserCustomization.pl b/macros/parserCustomization.pl index 1036fc95e5..e57172b3f3 100644 --- a/macros/parserCustomization.pl +++ b/macros/parserCustomization.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserDifferenceQuotient.pl b/macros/parserDifferenceQuotient.pl index b9440518e2..048c5f1371 100644 --- a/macros/parserDifferenceQuotient.pl +++ b/macros/parserDifferenceQuotient.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserFormulaUpToConstant.pl b/macros/parserFormulaUpToConstant.pl index 7ab078e188..d034067e76 100644 --- a/macros/parserFormulaUpToConstant.pl +++ b/macros/parserFormulaUpToConstant.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/parserFormulaUpToConstant.pl,v 1.23 2010/02/08 13:56:09 dpvc Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserFormulaWithUnits.pl b/macros/parserFormulaWithUnits.pl index 9a625ece97..556648c743 100644 --- a/macros/parserFormulaWithUnits.pl +++ b/macros/parserFormulaWithUnits.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserFunction.pl b/macros/parserFunction.pl index f445c33278..b407c11d4d 100644 --- a/macros/parserFunction.pl +++ b/macros/parserFunction.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserImplicitEquation.pl b/macros/parserImplicitEquation.pl index 7af0f47b25..269d8edad1 100644 --- a/macros/parserImplicitEquation.pl +++ b/macros/parserImplicitEquation.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/parserImplicitEquation.pl,v 1.14 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserImplicitPlane.pl b/macros/parserImplicitPlane.pl index f81b7725d7..4765395cd3 100644 --- a/macros/parserImplicitPlane.pl +++ b/macros/parserImplicitPlane.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserLinearInequality.pl b/macros/parserLinearInequality.pl index fcb45a36fc..943edcf6dd 100644 --- a/macros/parserLinearInequality.pl +++ b/macros/parserLinearInequality.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserMultiAnswer.pl b/macros/parserMultiAnswer.pl index 898158d7f1..f05b22023f 100644 --- a/macros/parserMultiAnswer.pl +++ b/macros/parserMultiAnswer.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/parserMultiAnswer.pl,v 1.11 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserMultiPart.pl b/macros/parserMultiPart.pl index b25d803fb9..3343816afd 100644 --- a/macros/parserMultiPart.pl +++ b/macros/parserMultiPart.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserNumberWithUnits.pl b/macros/parserNumberWithUnits.pl index dd958d555a..3d58bae5b1 100644 --- a/macros/parserNumberWithUnits.pl +++ b/macros/parserNumberWithUnits.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserParametricLine.pl b/macros/parserParametricLine.pl index 7c19c613fc..9cda736bc5 100644 --- a/macros/parserParametricLine.pl +++ b/macros/parserParametricLine.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/parserParametricLine.pl,v 1.17 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserPopUp.pl b/macros/parserPopUp.pl index c2e7621904..299598b248 100644 --- a/macros/parserPopUp.pl +++ b/macros/parserPopUp.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/parserPopUp.pl,v 1.10 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under @@ -172,6 +172,14 @@ sub MENU { $menu .= qq!$option\n!; }; $menu .= ""; + } elsif ($main::displayMode eq 'PTX') { + $menu = '' . "\n"; + foreach my $item (@list) { + $menu .= '
  • '; + my $cleaned_item = main::PTX_special_character_cleanup($item); + $menu .= $cleaned_item . '
  • '. "\n"; + } + $menu .= '
    '; } elsif ($main::displayMode eq "TeX") { # if the total number of characters is not more than # 30 and not containing / or ] then we print out diff --git a/macros/parserRadioButtons.pl b/macros/parserRadioButtons.pl index dc13f18103..e82cab7a10 100644 --- a/macros/parserRadioButtons.pl +++ b/macros/parserRadioButtons.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under @@ -318,7 +318,7 @@ sub getCorrectChoice { my $self = shift; my $value = shift; if ($value =~ m/^\d+$/ && !$self->{noindex}) { $value = ($self->flattenChoices)[$value]; - Value::Error("The correct anser index is outside the range of choices provided") + Value::Error("The correct answer index is outside the range of choices provided") if !defined($value); } my @choices = @{$self->{orderedChoices}}; @@ -556,8 +556,12 @@ sub BUTTONS { $radio[0] = "\n\\begin{itemize}\n" . $radio[0]; $radio[$#radio_buttons] .= "\n\\end{itemize}\n"; } + if ($main::displayMode eq 'PTX') { + $radio[0] = '' . "\n" . $radio[0]; + $radio[$#radio_buttons] .= ''; + }; @radio = $self->makeUncheckable(@radio) if $self->{uncheckable}; - (wantarray) ? @radio : join($self->{separator}, @radio); + (wantarray) ? @radio : join(($main::displayMode eq 'PTX')?'':$self->{separator}, @radio); } sub protect { diff --git a/macros/parserSolutionFor.pl b/macros/parserSolutionFor.pl index c0c1692c6f..894058f09e 100644 --- a/macros/parserSolutionFor.pl +++ b/macros/parserSolutionFor.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/parserVectorUtils.pl b/macros/parserVectorUtils.pl index 83820c530a..f43061561f 100644 --- a/macros/parserVectorUtils.pl +++ b/macros/parserVectorUtils.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/problemPreserveAnswers.pl b/macros/problemPreserveAnswers.pl index 63222949ae..ad72195a2d 100644 --- a/macros/problemPreserveAnswers.pl +++ b/macros/problemPreserveAnswers.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader$ # # This program is free software; you can redistribute it and/or modify it under diff --git a/macros/problemRandomize.pl b/macros/problemRandomize.pl index 26e3a98546..092e391949 100644 --- a/macros/problemRandomize.pl +++ b/macros/problemRandomize.pl @@ -1,6 +1,6 @@ ################################################################################ # WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# Copyright © 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ # $CVSHeader: pg/macros/problemRandomize.pl,v 1.12 2009/06/25 23:28:44 gage Exp $ # # This program is free software; you can redistribute it and/or modify it under @@ -182,9 +182,9 @@ sub new { style => "Button", styleName => ($main::inputs_ref->{effectiveUser} ne $main::inputs_ref->{user} ? "checkAnswers" : "submitAnswers"), label => undef, - buttonLabel => "Get a new version of this problem", - checkboxLabel => "Get a new version of this problem", - inputLabel => "Set random seed to:", + buttonLabel => $main::PG->maketext("Get a new version of this problem"), + checkboxLabel => $main::PG->maketext("Get a new version of this problem"), + inputLabel => $main::PG->maketext("Set random seed to:"), grader => $main::PG->{flags}->{PROBLEM_GRADER_TO_USE} || \&main::avg_problem_grader, #$main::PG_FLAGS{PROBLEM_GRADER_TO_USE} random => $main::PG_random_generator, status => {}, @@ -379,8 +379,8 @@ sub grader { # Add the problemRandomize message and data # if ($isWhen && !$okDate) { - $result->{msg} .= "
    Note: " if $result->{msg}; - $result->{msg} .= "You can get a new version of this problem after the due date."; + $result->{msg} .= "
    ".$main::PG->maketext("Note:")." " if $result->{msg}; + $result->{msg} .= $main::PG->maketext("You can get a new version of this problem after the due date."); } if (!$result->{msg}) { # hack to remove unwanted "Note: " from the problem @@ -404,9 +404,9 @@ sub grader { if ($self->{isReset} && $isSubmit) { $result->{msg} .= ""; $state->{state_summary_msg} = - "Note: This is a new (re-randomized) version of the problem.".$main::BR. - "If you come back to it later, it may revert to its original version.".$main::BR. - "Hardcopy will always print the original version of the problem."; + "".$main::PG->maketext("Note:").""." ".$main::PG->maketext("This is a new (re-randomized) version of the problem.").$main::BR. + $main::PG->maketext("If you come back to it later, it may revert to its original version.").$main::BR. + $main::PG->maketext("Hardcopy will always print the original version of the problem."); } # diff --git a/macros/sage.pl b/macros/sage.pl index dd56acc047..03d5842e9a 100644 --- a/macros/sage.pl +++ b/macros/sage.pl @@ -5,33 +5,155 @@ sub _sage_init { PG_restricted_eval('sub Sage {new sage(@_) }'); -} +} +# Sage() is defined as an alias for creating a new sage object. + + + package sage; -## Options: -## sage( SageCode (not yet), ButtonText, CellServerAddress) +=head3 Sage cell + + usage: Sage( SageCode => 'print 1+2; record_answer(3)', + ButtonText => 'Start/Restart the Interactive Cell', + ShowAnswerBlank => # "hidden" (default) or "visible" or 'none' + CellServerAddress => 'https://sagecell.sagemath.org' + ); + NAMED_ANS(sageAnswer => Compute('3')->cmp); + +The arguments are all optional but usually you will want to supply your own SageCode. + +This method of calling sage was designed specially for presenting sage "interacts", applet +like creations in SageMath, although it may be useful for other purposes also. If the answer blank +is hidden then the interact fills in the answer as a result of manipulations on the applet +performed by the student and the student cannot override the answer. + +To return answers from the sage interact: + +The function record_answer(answer_list) called from within the SageCode +creates a NAMED_ANS_RULE or NAMED_HIDDEN_ANS_RULE with +the values of the answer_list inserted. If ShowAnswerBlank is "hidden" then the HIDDEN answer rule is +used; if ShowAnswerBlank is 'none' then no answer blank is inserted. + +For the current implementation the Sage interact can create only one answer blank. + +When the sage interact creates an answer blank it must be checked by WeBWorK using the construction + +C $correctAnswer-Ecmp)> + +where 'sageAnswer' is the SageAnswerName and $correctAnswer is a MathObject. + +By default the sage created answer blanks are hidden, but it is visible if ShowAnswerBlank is set to +'visible'. When visible the answer blanks occur within the borders which define the output +of the sage applet. The answer blanks are 15 spaces long. + + + +=cut sub new { my $self = shift; my $class = ref($self) || $self; my %options = ( - SageCode => 'print 1+2', + SageCode => 'print 1+2;record_answer(3)', ButtonText => 'Start/Restart the Interactive Cell', CellServer => 'https://sagecell.sagemath.org', - SageAnswerName => 'sageAnswer', # not used yet - SageAnswerValue => 'ansList', # not used yet + SageAnswerName => 'sageAnswer', + SageAnswerValue => 'ansList', # used in early versions, may no longer be needed AutoEvaluateCell => 'true', - ShowAnswerBlank => 'hidden', - accepted_tos =>'false', # force author to accept terms of service explicitly + ShowAnswerBlank => 'hidden', #'hidden','visible','none' + AnswerReturn => 1, # (legacy, use ShowAnswerBlank=>'none') + # 0 means no answer blank is registered +# accepted_tos =>'false', # force author to accept terms of service explicitly + # removed because sagecell.sagemath.org no longer requires + # acknowledgement of terms of service. @_ ); - $self = bless { - %options - }, $class; + +# handle legacy case where AnswerReturn was used + unless ($options{ShowAnswerBlank} =~ \S){ + if ($options{AnswerReturn} == 0) { + $options{ShowAnswerBlank} = 'none'; + } else { + $options{ShowAnswerBlank} = 'hidden'; + } + } + + + # lets create a new hash for "self" + $self = bless {%options}, $class; + + + # main::RECORD_ANS_NAME($self->{SageAnswerName}, 345); -- old version of code + + + # Create python/sage function "record_answer()" + # to print a WeBWorK answer blank from within + # the sage interact. The function is different depending on whether the answer blank exists + # and whether it is visible. + + # (1) $recordAnswerBlank will hold the code defining 'record_answer' which, when called from + # within Sage prints a WeBWorK (HIDDEN)_NAMED_ANS_RULE and inserts the answers values. + # This is the mechanism for returning an answer created by the sage interact. + # By default this answer blank is hidden, but it is visible if ShowAnswerBlank is set to + # 'visible'. When visible the answer blanks occur within the borders which define the output + # of the sage applet. The answer blanks are 15 spaces long. + # FIXME: For the current implementation the Sage interact can create only one answer blank. + # provisions for giving each answer blank a different label would need to be created. + + my $recordAnswerBlank=''; + if ($self->{ShowAnswerBlank} eq 'visible') { + $recordAnswerBlank = "Answer: ".main::NAMED_ANS_RULE($self->{SageAnswerName}, 15); + } elsif ($self->{ShowAnswerBlank} eq 'hidden') { + $recordAnswerBlank = main::NAMED_HIDDEN_ANS_RULE($self->{SageAnswerName},15); + } elsif ($self->{ShowAnswerBlank} eq 'none') { + $recordAnswerBlank = 'none'; # don't register an answer blank + } else { + main::WARN_MESSAGE("Option $option{ShowAnswerBlank} is not valid for displaying sage answer rule. "); + } + # you could add an option to print an ANSWER BOX instead of an ANSWER RULE + + + #FIXME -- for some reason the answer blank, printed with pretty_print + # floats to the top of the printed Sage block above print statements. + # ???? This can be intermittent, which is even more surprising. + + # (2) now determine whether the record_answer() prints an answer blank + # or is a noop - a dummy operation. + + if ($recordAnswerBlank eq 'none') { + $sage::recordAnswerString = < + # ' + # %(ansVals,) ) ) + # the next line replaces the first VALUE="(1, 1)" with %s, so that we have: + # def record_answer(ansVals): pretty_print( HtmlFragment( + # ' + # ' + # %(ansVals,) ) ) + # When evaluated in sage (python) %s is replaced by the value of ansVals + + $sage::recordAnswerString =~ s/value="[^"]*"/value="%s"/i; + # this line removes any returns from the output -- this might not be necessary + $sage::recordAnswerString =~ s/\n/ /g; - main::RECORD_ANS_NAME($self->{SageAnswerName}, 345); $self->sageCode(); $self->sagePrint(); @@ -39,18 +161,22 @@ sub new { } +# Notice that python is white space sensitive so the code +# needs to be left justified when inserted to +# avoid indentation errors. +# + sub sageCode{ my $self = shift; main::TEXT(main::MODES(TeX=>"", HTML=><<"SAGE_CODE")); -
    -
    @@ -77,7 +203,42 @@ sub sagePrint{ } -#### Experimental stuff. +=head3 sageCalculatorPad code. + + +This is a simple interface for embedding a sage calculation cell in any problem. +Details for this can be found at +L +and for more detail: +L. + +The latter reference provides information for embedding a customized sageCell with more +options than are provided by sageCalculatorPad() + +=cut + +=head3 Sample sageCalculatorPad + + sageCalculatorHeader(); # set up javaScript needed for the sageCalculatorPad + + + Context()->texStrings; + TEXT( + sageCalculatorPad( "Use this calculator pad to make calculations", + q! + data = [1, 3, 4, 1, 7, 4, 2, 3, 2, 4, 2, 5, 4, 1, 3, 3, 2] + n = len(data); print "Number of data values =",n + s = sum(data); print " Sum of data = ",s + s2 = sum((x^2 for x in data)); print " Sum of squares = ",s2 + s3 = sum((x^3 for x in data)); print " Sum of cubes = ",s3 + s4 = sum((x^4 for x in data)); print " Sum of forths = ",s4;print + mu = mean(data); print " The mean =",mu + var = variance(data); print " The sample variance = ",var + ! + ) + ); + +=cut package main; sub sageCalculatorHeader { @@ -86,13 +247,10 @@ sub sageCalculatorHeader { @@ -101,14 +259,14 @@ sub sageCalculatorHeader { } sub sageCalculatorPad { - my $top_test = shift; + my $top_text = shift; my $initial_contents = shift; -main::TEXT(main::MODES(TeX=>"", HTML=><<"EOF")); +main::TEXT(main::MODES(TeX=>"SageCell: $top_text", HTML=><<"EOF"));

    $top_text

    -

    +
    ")); + push(@{$self->{output}},main::MODES(TeX=>'', HTML=>"",PTX=>'')); $self->hide_other_results(@_); } @@ -571,7 +572,7 @@ sub add_container { $isopen = $self->is_open; $scaffold->is_open($self) if $isopen; - splice(@$PG_OUTPUT,0,scalar(@$PG_OUTPUT)) if !($canopen || $iscorrect) || (!$isopen && $Scaffold::isHardcopy); + splice(@$PG_OUTPUT,0,scalar(@$PG_OUTPUT)) if !($canopen || $iscorrect || $Scaffold::isPTX) || (!$isopen && $Scaffold::isHardcopy); unshift(@$PG_OUTPUT,@{main::MODES( HTML => [ '
    ', @@ -580,10 +581,12 @@ sub add_container { '', ], TeX => ["\\par{\\bf $self->{name}}\\par "], + PTX => ["\n"], )}); push(@$PG_OUTPUT,main::MODES( HTML => '

    ', - TeX => "\\par " + TeX => "\\par ", + PTX => "<\/stage>\n", )); } @@ -745,7 +748,7 @@ package main; # # Set up some styles and the jQuery calls for opening and closing the scaffolds. # -TEXT(<<'END_HEADER_TEXT') if !$Scaffold::isHardcopy; # should be HEADER_TEXT, but that gets lost in library browser +TEXT(<<'END_HEADER_TEXT') if !($Scaffold::isHardcopy or $Scaffold::isPTX); # should be HEADER_TEXT, but that gets lost in library browser