diff --git a/OpenProblemLibrary/macros/Alfred/Alfredmacros.pl b/OpenProblemLibrary/macros/Alfred/Alfredmacros.pl deleted file mode 100755 index de0871b50a..0000000000 --- a/OpenProblemLibrary/macros/Alfred/Alfredmacros.pl +++ /dev/null @@ -1,369 +0,0 @@ -# A group of macros used in the Alfred problem library. - -sub _Alfredmacros_init {} #don't reload these macros. - - -# Given a point (x,y) this macro computes the angle with respect to x-axis. The angle will be between 0 and 2pi. -sub inversetrig -{my $refangle = arctan(abs($_[1]/$_[0])); - if($_[0] == 0) - {0} - elsif ($_[0]>0) - {if($_[1] == 0) - {0} - elsif($_[1]>0) - {$refangle;} - else - {2*pi-$refangle;} - } - else - {if($_[1] == 0) - {pi} - elsif($_[1]>0) - {pi-$refangle;} - else - {pi+$refangle} - } -} - -## Compute the max and min of an array of numbers - - - -#This macro prevents students from double clicking in an answer box. This macro #is necessary for multiple integral problems where the answer box is typeset -#into the integration symbols. -sub doubleclickprevent -{TEXT(MODES( - TeX => "", - HTML => "" - )); -} - -#The problems that have the answer box in the limits must be displayed in JS -#math mode. This macro warns the user to use JS math mode if they are not. -sub jsMathwarn -{TEXT(MODES( - TeX => '', - HTML_jsMath => '', - HTML => $HR."Warning: to use this problem, you need to". - "select jsMath mode in the Display Options panel at the left".$HR, -)); -} - -sub jsmathmode -{ -TEXT(MODES( - TeX => '', - HTML_jsMath => '', - HTML => $HR."Warning: to use this problem, you need to ". - "select jsMath mode in the Display Options panel at the left".$HR, -)); -TEXT(MODES( - TeX => "", - HTML => "" - )); - -} - -sub mathjaxmode -{TEXT(MODES( - TeX => '', - HTML_MathJax => '', - HTML => $HR."Warning: to use this problem, you need to ". - "select MathJax mode in the Display Options panel at the left".$HR, -)); -} - -#This subroutine includes the Strang's textbook into a problem, you have to -#feed it the chapter and section. It is assumed that the book is in the course -#directory and is labeled strangtextbook. The parameters are -#strang(chapter,section,(optional) section title. -#example \{&strang(16,5,"surface integrals")\}. -$wwstrang = "http://webwork.alfred.edu/webwork2_course_files/strangcalculus"; -sub strang -{ -htmlLink(qq!$wwstrang/Strang-$_[0]-$_[1].pdf!,"$_[0].$_[1] $_[2] from Gilbert Strang's Calculus",q/TARGET="new_window"/) -} - -#Inserts a link to a trig table in the problem. -#example \{&trig_table()\} -sub trig_table -{ -htmlLink(qq!$wwstrang/trig_identities.pdf!,"Trig Identities",q/TARGET="new_window"/) -} - -sub strang_index -{ -htmlLink(qq!$wwstrang/Index.pdf!,"Index",q/TARGET="new_window"/) -} - -sub strang_TOC -{ -htmlLink(qq!$wwstrang/TOC.pdf!,"Table of Contents",q/TARGET="new_window"/) -} - -sub product_Rule_cmp { -my ( $correct, $student, $self ) = @_; - my ( $f1stu, $f2stu,$f3stu,$f4stu ) = @{$student}; - my ( $f1, $f2, $f3, $f4 ) = @{$correct}; - my @fgrade = (0,0,0,0); - my @fstu = ($f1stu,$f2stu,$f3stu,$f4stu); - my @fcorrect = ($f1,$f2,$f3,$f4); - #we will associate each student answer with a prime number, noting which student answer is in which blank. This allows us to make use of the fundamental theorem of arithmetic. - my @prime = (2,3,5,7); - my @answerblank = (0,0,0,0); - - for($i=0;$i<4;$i++){ - for($j=0;$j<4;$j++){ - if(($fcorrect[$i]==$fstu[$j])&&($answerblank[$j]==0)){ - {$answerblank[$j] = $prime[$i]; - $j = 4 # you have to terminate the inner loop in this case for the special case of f=e^x where f and f' are the same. - } - } - } - }; - for($i=0;$i<4;$i++){ - if(!$answerblank[$i]){ - $self->setMessage($i+1,"All of your answers should be $f, $g, or a derivative of one of these functions"); - } - } -#now we rely on the fact that products of primes are unique. First we check to see if all of the blanks are correct - if ((($answerblank[0]*$answerblank[1] == 6)&&($answerblank[2]*$answerblank[3] == 35))||(($answerblank[0]*$answerblank[1] == 35)&&($answerblank[2]*$answerblank[3] == 6))){ - @fgrade = (1,1,1,1); - } -#now check to see if the first pair of blanks is correct, knowing one pair is not - elsif ($answerblank[0]*$answerblank[1] == 6){ - if (($answerblank[2] == 5)||($answerblank[2] == 7)){ - @fgrade = (1,1,1,0); - } - elsif (($answerblank[3] == 5)||($answerblank[3] == 7)){ - @fgrade = (1,1,0,1); - } - else {@fgrade = (1,1,0,0);} - } - elsif ($answerblank[0]*$answerblank[1] == 35){ - if (($answerblank[2] == 2)||($answerblank[2] == 3)){ - @fgrade = (1,1,1,0); - } - elsif (($answerblank[3] == 2)||($answerblank[3] == 3)){ - @fgrade = (1,1,0,1); - } - else {@fgrade = (1,1,0,0);} - } -#if both sets are not correct, and the first set is not correct, check to see if the last pair are - elsif ($answerblank[2]*$answerblank[3] == 6){ - if (($answerblank[0] == 5)||($answerblank[0] == 7)){ - @fgrade = (1,0,1,1); - } - elsif (($answerblank[1] == 5)||($answerblank[1] == 7)){ - @fgrade = (0,1,1,1); - } - else {@fgrade = (0,0,1,1);} - } - elsif ($answerblank[2]*$answerblank[3] == 35){ - if (($answerblank[0] == 2)||($answerblank[0] == 3)){ - @fgrade = (1,0,1,1); - } - elsif (($answerblank[1] == 2)||($answerblank[1] == 3)){ - @fgrade = (0,1,1,1); - } - else {@fgrade = (0,0,1,1);} - } -#at this point they don't have a matched set of blanks correct. look for a single function in each pair that is right. You have to make sure you only get one for each pair of answer blanks. - else{ - if (($answerblank[0])&&($answerblank[2])&&($answerblank[0]*$answerblank[2] !=6)&&($answerblank[0]*$answerblank[2] !=35)&&($answerblank[0]!=$answerblank[2])){ - @fgrade = (1,0,1,0); - } - elsif (($answerblank[0])&&($answerblank[3])&&($answerblank[0]*$answerblank[3] !=6)&&($answerblank[0]*$answerblank[3] !=35)&&($answerblank[0]!=$answerblank[3])){ - @fgrade = (1,0,0,1); - } - elsif (($answerblank[1])&&($answerblank[2])&&($answerblank[1]*$answerblank[2] !=6)&&($answerblank[1]*$answerblank[2] !=35)&&($answerblank[1]!=$answerblank[2])){ - @fgrade = (0,1,1,0); - } - elsif (($answerblank[1])&&($answerblank[3])&&($answerblank[1]*$answerblank[3] !=6)&&($answerblank[1]*$answerblank[3] !=35)&&($answerblank[1]!=$answerblank[3])){ - @fgrade = (0,1,0,1); - } - elsif ($answerblank[0]){@fgrade = (1,0,0,0)} - elsif ($answerblank[1]){@fgrade = (0,1,0,0)} - elsif ($answerblank[2]){@fgrade = (0,0,1,0)} - elsif ($answerblank[3]){@fgrade = (0,0,0,1)} - }; - return [@fgrade]; -} - - - -sub check_boundary_conditions { -my ( $correct, $student, $self ) = @_; - return product_Rule_cmp(@_) ; - } - -sub Snxy(){ - my %args = @_; - my @x = @{$args{inputs}}; - my @y = @{$args{outputs}}; - my $m = $args{m}; - my $n = $args{n}; - my $i = 0; - my $sum = 0; - if ($#x == $#y){ - for ($i=0;$i <= $#x;$i++){ - $sum = $sum + ($x[$i])**($n)*($y[$i])**($m); - } - } - else {$sum = 0}; - return $sum; -} - -### To use the macros your problem must include unionTables.pl, and of course Alfredmacros.pl -### Table integral returns a string that can be included in a Table to output an integral whose upper and lower limits -### of integration can be answer blanks. There are several optional parameters: -### width - change the width of the answer blanks. defaults to 3. -### lowerwidth - change the width of the lower answer blank. defaults to width -### upperwidth - change the width of the upper answer blank. defaults to width. -### upper - the uppper limit of integration, does not have to be an answer blank, defaults to answer blank with width "width" -### lower - the lower limit of integration, does not have to be an answer blank, defaults to answer blank with width "width" -### limits - boolean, if 1 puts the limits of integration above and below the integral symbol, if 0 puts them after the integral symbol. -### default is 1. -### Your code must include unionTables.pl, and of course Alfredmacros.pl -### An example: -### \{BeginTable(center=>0). -### Row([tableintegral(), -### ],separation=>2). -### EndTable(); -### \} -### which will print an integral with answer blank on the upper and lower limits with the default length of 3 -### -### This example prints out a double integral, the first integral with answer blanks with width 10, the second integral -### has 0 for the lower limit of integration and an answer blank with width 5 for the upper limit of integration. -### The default limits of integratin are answer blanks with width 3, in this case the default width was overridden to 5 -### and the default lower limit was changed to a zero. -### \{BeginTable(center=>0). -### Row([tableintegral(width=>10,limits=>'\(0\)'),tableintegral(width=>5,lower=>'\(0\)',limits=>0), -### ],separation=>2). -### EndTable(); -### \} -### An example where the width of the upper and lower answer blanks have different widths. -### \{BeginTable(center=>0). -### Row([tableintegral(lowerwidth=>10,upperwidth=>1) -### ],separation=>2). -### EndTable(); -### \} - -sub tableintegral{ - my %arg = @_; - my $width = delete $arg{width} // 3; - my $lowerwidth = delete $arg{lowerwidth} // $width; - my $upperwidth = delete $arg{upperwidth} // $width; - my $lower = delete $arg{lower} // ans_rule($lowerwidth); - my $upper = delete $arg{upper} // ans_rule($upperwidth); - my $limits = delete $arg{limits} // 1; - if ($limits == 1){ - return $upper.$BR.'\(\displaystyle\int\)'.$BR.$lower - } - else { - return '\(\displaystyle\int\)',$upper.$BR.$BR.$lower - } -}; - -# a sum with answer blanks for the summation variable, lower limit, and upper limit -#\{ BeginTable(center=>0). -# Row([tablesum(width=>10), -# ],separation=>2). -# EndTable(); -#\} -# a sum with answer blanks for the upper and lower limits, and the summation variable is i -#\{ BeginTable(center=>0). -# Row([tablesum(width=>10,sumvariable=>'i'), -# ],separation=>2). -# EndTable(); -#\} -# a sum with answer blanks for the upper and lower limits, and summation variable is not used. -#\{ BeginTable(center=>0). -# Row([tablesum(width=>10,usesumvariable=>0), -# ],separation=>2). -# EndTable(); -#\} -# sum from n = 1 to infinity -#\{ BeginTable(center=>0). -# Row([tablesum(sumvariable=>'\(n\)',lower=>'\(1\)', upper=>'\(\hskip 3pt\infty\)') ],separation=>2). -# EndTable(); -#\} - -sub tablesum{ - my %arg = @_; - my $width = delete $arg{width} // 3; - my $lowerwidth = delete $arg{lowerwidth} // $width; - my $upperwidth = delete $arg{upperwidth} // $width; - my $lower = delete $arg{lower} // ans_rule($lowerwidth); - my $upper = delete $arg{upper} // ans_rule($upperwidth); - my $limits = delete $arg{limits} // 1; - my $sumvariable = delete $arg{sumvariable} // ans_rule($width); - my $usesumvariable = delete $arg{usesumvariable} // 1; - if ($usesumvariable == 0){ - if ($limits == 1){ - return $upper.$BR.'\(\displaystyle\sum\)'.$BR.$lower - } - else { - return '\(\displaystyle\int\)',$upper.$BR.$BR.$lower - } - } - else { - if ($limits == 1){ - return $upper.$BR.'\(\displaystyle\sum\)'.$BR.$sumvariable.'\( = \)'.$lower - } - else { - return '\(\displaystyle\int\)',$upper.$BR.$BR.$sumvariable.'\( = \)'.$lower - } - } -}; - - -### Create a vertical bar with an upper and lower limit. -sub tableevaluate{ - my %arg = @_; - my $width = delete $arg{width} // 3; - my $lowerwidth = delete $arg{lowerwidth} // $width; - my $upperwidth = delete $arg{upperwidth} // $width; - my $lower = delete $arg{lower} // ans_rule($lowerwidth); - my $upper = delete $arg{upper} // ans_rule($upperwidth); - return - '\(\Bigg\vert\)',$upper.$BR.$BR.$lower -}; - -### Create a subscripted character -sub tablesubscript{ - my %arg = @_; - my $width = delete $arg{width} // 3; - my $lower = delete $arg{lower} // ans_rule($width); - my $variable = delete $arg{variable} // 'c'; - return - $variable,$BR.$BR.$lower -}; - -### Create a superscripted character -sub tablesuperscript{ - my %arg = @_; - my $width = delete $arg{width} // 3; - my $upper = delete $arg{upper} // ans_rule($width); - my $variable = delete $arg{variable} // 'c'; - return - $variable,$upper.$BR.$BR.$BR -}; - - - -### A fraction -sub tablefrac{ - my %arg = @_; - my $width = delete $arg{width} // 3; - my $lower = delete $arg{lower} // ans_rule($width); - my $upper = delete $arg{upper} // ans_rule($width); - my $barwidth = delete $arg{barwidth} // 10+$width; - my $divisionbar = ""; - for ($count = 1;$count <= $barwidth; $count++){ - $divisionbar = $divisionbar."-"; - } - return $upper.$BR.$divisionbar.$BR.$lower -}; - diff --git a/OpenProblemLibrary/macros/BrockPhysics/BrockPhysicsMacros.pl b/OpenProblemLibrary/macros/BrockPhysics/BrockPhysicsMacros.pl deleted file mode 100644 index 67405cbe6f..0000000000 --- a/OpenProblemLibrary/macros/BrockPhysics/BrockPhysicsMacros.pl +++ /dev/null @@ -1 +0,0 @@ -# this file defines all Brock-Physics-specific macros. \ No newline at end of file diff --git a/OpenProblemLibrary/macros/BrockPhysics/fixedPrecision.pl b/OpenProblemLibrary/macros/BrockPhysics/fixedPrecision.pl deleted file mode 100644 index f14ed0599a..0000000000 --- a/OpenProblemLibrary/macros/BrockPhysics/fixedPrecision.pl +++ /dev/null @@ -1,53 +0,0 @@ -sub _fixedPrecision_init {} - -package FixedPrecision; -our @ISA = ("Value::Real"); - -sub new { - my $self = shift; my $class = ref($self) || $self; - my $context = (Value::isContext($_[0]) ? shift : $self->context); - my $x = shift; my $n = shift; - Value::Error("Too many arguments") if scalar(@_) > 0; - if (defined($n)) { - $x = main::prfmt($x,"%.${n}f"); - } else { - $x =~ s/\s+//g; - my ($int,$dec) = split(/\./,$x); - $n = length($dec); - } - $self = bless $self->SUPER::new($context,$x), $class; - $self->{decimals} = $n; $self->{isValue} = 1; - return $self; -} - -sub string { - my $self = shift; - main::prfmt($self->value,"%.".$self->{decimals}."f"); -} - -sub compare { - my ($self,$l,$r) = Value::checkOpOrder(@_); - $l cmp $r; -} - -package FixedPrecisionNumber; -our @ISA = ("Parser::Number"); - -sub new { - my $self = shift; my $class = ref($self) || $self; - my $equation = shift; my $context = $equation->{context}; - $self = bless $self->SUPER::new($equation,@_), $class; - $self->{value} = FixedPrecision->new($self->{value_string}); - return $self; -} - -sub string {(shift)->{value}->string(@_)} -sub TeX {(shift)->{value}->TeX(@_)} - -package main; - -Context()->{parser}{Number} = "FixedPrecisionNumber"; - -sub FixedPrecision {FixedPrecision->new(@_)}; - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/CofIdaho_macros.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/CofIdaho_macros.pl deleted file mode 100755 index 262ef22dc4..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/CofIdaho_macros.pl +++ /dev/null @@ -1,758 +0,0 @@ -=pod - -=head1 NAME - - extra macros for Intermediate Algebra problems at The College of Idaho - -=head1 Synposis - macros by R Cruz -- The College of Idaho -=cut - -=head3 Format the instuctor answer - -=pod - -1) SimplifyExponents: Formats an expression without negative exponents - To use: SimplifyExponents(num, den,~~@var,@exp); - where num = numerator's constant - den = denominator's constant - ~~@var = pointer to an array with the list of variables - @exp = array with the exponents for the variables - -2) num_and_unit_checker: Checks for units like "apples" or "oranges" - To use: ANS (num_and_units_checker($correct_answer, "units"); - -3) strict_percent_cmp: Checks percentages have been rounded as required. - To use: ANS(strict_percent_cmp($answer,"%",roundto)); - where $answer is in percent form: xx.x - roundto = "tenths" or "hundredths" - -4) Picky_equation_cmp: Checks both sides of an equation model the word problem. - To use: Picky_equation_cmp("2x+1=5"); - Note: This one allows only the variables that have been used in the CofIdaho - problems. It should be changed so that it is more flexible, or better, - convert it to a MathObject. - -5) SlopeIntercept_equation_cmp: Checks both sides of an equation for the - slope-intercept form of a line. - To use: ANS(SlopeIntercept_equation_cmp($answer)); - where $answer = "y = $m x + $b"; (Use only y and x for variables.) - -6) functionNotation_cmp: Checks for "f(x)= ***" notation - To use: - ANS(functionNotation_cmp("f(x)=2x",["x","f"])); - -7) Checks factors of polynomials. - To use: The answer must be submitted in this form: - ANS(FactorEvaluator($answer,[variable array])); - If the polynomial is prime: - ANS(FactorEvaluator("Does not factor",polynomial,[variable array])); - - Note: Answers must be in the form: monomial(poly)...(poly) with - or without parentheses about the monomial. The polynomial factors must - not contain any other grouping symbols and, in any case, only parenthesis - may be used. This needs to be in the instructions for any set that uses - this macro (in the screenHeader.pg file). - - Note: "StrictFactoringEvaluator" requires leading negatives to be factored out. - This not may not work with all situations. BE SURE TO CHANGE THIS FILE IF THE - FactoringEvaluator IS CHANGED!! - -8) RationalExpEvaluator: Checks for simplified rational expressions. - To use: ANS(RationalExpEvaluation($answer,[variable array])); - or something like: ANS(RationalExpEvaluator($answer,["x","y"])); - - Note: Answers must be of the form: (poly)/(poly) - -9) ReduceFraction: Returns a string that represents a reduced fraction. - To use: $a = SimplifyFraction(numerator expression,denominator espression); -=cut - - - -################################################################### -# 1) Formats an expression without negative exponents -# Thanks to John Jones at ASU for this macro. - -sub SimplifyExponents { -loadMacros("contextLimitedPowers.pl"); - my $num = shift; - my $den = shift; - my $varref = shift; - my @expos = @_; - my @vars = @$varref; - - Context()->operators->set(@LimitedPowers::OnlyPositiveIntegers); - - my $answer_n = Formula("$num"); - my $answer_d = Formula("$den"); - - for $j (0..(scalar(@vars)-1)) { - $answer_n *= Formula("$vars[$j]^$expos[$j] ") if ($expos[$j] > 0); - $answer_d *= Formula("$vars[$j]^(-1*$expos[$j]) ") if ($expos[$j] < 0); - } - - $answer = $answer_n/$answer_d; - - return($answer->reduce()); -} - - -sub Simplified { - my ($correct, $student, $ah)=@_; - return unless $ah->{score} == 1; #This does not work??? - my $VariablesPlus1 = 2; #Put in the number of variables plus 1 - my $check = $ah->{student_ans}; - if(!( $ah->{isPreview}) && $correct == $student - && $check =~ /(([abxy].*){$VariablesPlus1,})|([\Q^].*[\Q^])/) - { - $ah->{ans_message} = "Simplify your answer"; - } - - return ($correct == $student && - ($ah->{student_ans} !~ /(([abxy].*){$VariablesPlus1,})|([\Q^].*[\Q^])/)); - } - - -################################################################### -# 2) Checks for units - -sub num_and_unit_checker{ - my $correct_ans = shift; #the answer - my $correct_units = shift; #the unit type - my $old_evaluator = num_cmp($correct_ans); - - my $new_evaluator = sub - { - my $student_ans = shift; - my $formatted_ans = $student_ans; - $formatted_ans =~ s{$correct_units}{}; - my $ans_hash = $old_evaluator->evaluate($formatted_ans); - if ( $student_ans !~ /$correct_units/) { - $ans_hash->{score} = 0; -$ans_hash->setKeys( 'ans_message' =>"Enter your answer with the correct units: $correct_units"); - } - $ans_hash->{original_student_ans}="$student_ans"; - $ans_hash->{preview_latex_string}="$student_ans"; - $ans_hash->{student_ans} = "$student_ans"; - $ans_hash->{correct_ans} = "$correct_ans"." $correct_units"; - - return $ans_hash; - }; - - return $new_evaluator; -} - - -################################################################### -# 3) Checks for rounded percents - -sub strict_percent_cmp{ - my $correct_ans = shift; #the answer - my $correct_units = shift; #the unit type which should be "%" - my $error = shift; #what to round to - my $errorAmount = .1; - if ($error eq "hundredth") {$errorAmount=.01;} - my $old_evaluator = num_cmp($correct_ans,tol=>0); - - my $new_evaluator = sub - { - my $student_ans = shift; - my $formatted_ans = $student_ans; - $formatted_ans =~ s{$correct_units}{}; - my $ans_hash = $old_evaluator->evaluate($formatted_ans); - if ( $student_ans !~ /$correct_units/) { - $ans_hash->{score} = 0; -$ans_hash->setKeys( 'ans_message' =>"Enter your answer with the correct units: $correct_units"); - } - else { - if ($ans_hash->{score}==0 && - abs($formatted_ans-$correct_ans)<$errorAmount) { -$ans_hash->setKeys( 'ans_message' =>"Round your answer to the nearest $error."); - } - } - $ans_hash->{original_student_ans}="$student_ans"; - $ans_hash->{preview_latex_string}="$student_ans"; - $ans_hash->{student_ans} = "$student_ans"; - $ans_hash->{correct_ans} = "$correct_ans"." $correct_units"; - - return $ans_hash; - }; - - return $new_evaluator; -} - - -################################################################### -# 4) Checks both sides of an equation model the word problem. - -sub Picky_equation_cmp { - -loadMacros( "unionUtils.pl", - "answerUtils.pl", - "listAnswer.pl", -"extraAnswerEvaluators.pl" -); - - my $ans = shift; - -my $new_evaluator = sub { - my $student = shift; - - $ans =~ tr/[=]/,/; - $student =~ tr/[=]/,/; - - my $old_evaluator = fun_list_cmp($ans,vars=>['a','b','c','d','h','l','n','v','w','x','y','s','t','A','L','R','C','P']); - my $ans_hash_old = $old_evaluator->evaluate($student); - - $ans =~ tr/,/=/; - $student =~ tr/,/=/; - $ans_hash_old->{correct_ans}="$ans"; - $ans_hash_old->{original_student_ans}="$student"; - $ans_hash_old->{student_ans}="$student"; - $ans_hash_old->{preview_latex_string}="$student"; - if ($ans_hash_old->{score}!=1) { - $ans_hash_old->{score}=0; -# $ans_hash_old->setKeys( 'ans_message' =>"Your equation must model the problem."); - $ans_hash_old->setKeys( 'ans_message'=>"At least one side is incorrect."); - } - if ($student !~ /[=]/) { - if ($ans =~/[RCP]/) { - @side = split(/[=]/,$ans); - $ans_hash_old->setKeys( 'ans_message'=>"Enter your answer in the form: $side[0] = expression.");} - else { $ans_hash_old->setKeys( 'ans_message' =>"You must enter an equation.");} - }; - -return $ans_hash_old; -}; -return $new_evaluator; -} - -################################################################### -# 5) Checks both sides of an equation for the slope-intercept form of a line. - -sub SlopeIntercept_equation_cmp { - -loadMacros( "unionUtils.pl", - "answerUtils.pl", - "contextLimitedPolynomial.pl" -); - - my $ans = shift; - my $formatAns = $ans; - $formatAns =~ tr/ //; - my @correctSides = split(/[=]/, $formatAns); - -my $new_evaluator = sub { - my $left_evaluator = fun_cmp($correctSides[0],vars=>['x','y']); - my $right_evaluator = fun_cmp($correctSides[1],vars=>['x','y']); - - my $student = shift; - my $formatStudent= $student; - $formatStudent =~ tr/ //; - if ($student !~ /[=]/) { - my $ans_hash = $right_evaluator->evaluate($formatStudent); - $ans_hash->{correct_ans}="$ans"; - $ans_hash->{original_student_ans}="$student"; - $ans_hash->{student_ans}="$student"; - $ans_hash->{preview_latex_string}="$student"; - $ans_hash->setKeys( 'ans_message' =>"You must enter an equation."); - $ans_hash->{score}=0; - return $ans_hash; - } - else { - my @studentSides = split(/[=]/, $formatStudent); - - my $left_ans_hash = $left_evaluator->evaluate($studentSides[0]); - my $right_ans_hash = $right_evaluator->evaluate($studentSides[1]); - - $left_ans_hash->{correct_ans}="$ans"; - $left_ans_hash->{original_student_ans}="$student"; - $left_ans_hash->{student_ans}="$student"; - $left_ans_hash->{preview_latex_string}="$student"; - - if ($right_ans_hash->{score}==0) {$left_ans_hash->{score}=0;} - - my $perlNumber = "[0-9]+[\.\/]?[0-9]*|\.[0-9]+"; - my $BadForm = ($studentSides[0]=~/\w.*\w/ - || $studentSides[1] =~ /[a-zA-Z].*[a-zA-Z]/ - || $studentSides[1] =~ /($perlNumber)[+-]+($perlNumber[a-zA-Z][+-])?($perlNumber)/); - if ($BadForm) { - $left_ans_hash->setKeys( - 'ans_message' =>"Enter your answer in the form: $BR y = mx+b, y = b or x = c"); - $left_ans_hash->{score}=0; - } - - return $left_ans_hash; - } -}; -return $new_evaluator; -} - - -################################################################### -# 6) Checks for function notation in an equality. - -sub functionNotation_cmp { - -loadMacros( "unionUtils.pl", - "answerUtils.pl" -); - - my $ans = shift; - my @variables = @_; - - my $formatAns = $ans; - $formatAns =~ tr/ //; - my @correctSides = split(/[=]/, $formatAns); - -my $new_evaluator = sub { -# my $notation_evaluator = fun_cmp($correctSides[0],vars=>@variables); - my $notation_evaluator = ordered_cs_str_cmp($correctSides[0]); - my $notation2_evaluator = unordered_str_cmp($correctSides[0]); - my $formula_evaluator = fun_cmp($correctSides[1],vars=>@variables,tol=>.001); - - my $student = shift; - my $formatStudent= $student; - $formatStudent =~ tr/ //; - - if ($student !~ /[=]/ || $student !~ /(\w\s*\(\s*\w\s*\))/) { - my $ans_hash = $formula_evaluator->evaluate($formatStudent); - $ans_hash->{correct_ans}="$ans"; - $ans_hash->{original_student_ans}="$student"; - $ans_hash->{student_ans}="$student"; - $ans_hash->{preview_latex_string}="$student"; - $ans_hash->setKeys( 'ans_message' =>"Your answer must be in function notation."); - return $ans_hash; - } - else { - my @studentSides = split(/[=]/, $formatStudent); - - my $notation_ans_hash = $notation_evaluator->evaluate($studentSides[0]); - my $notation2_ans_hash = $notation2_evaluator->evaluate($studentSides[0]); - my $formula_ans_hash = $formula_evaluator->evaluate($studentSides[1]); - - $formula_ans_hash->{correct_ans}="$ans"; - $formula_ans_hash->{original_student_ans}="$student"; - $formula_ans_hash->{student_ans}="$student"; - $formula_ans_hash->{preview_latex_string}="$student"; - - if ($notation_ans_hash->{score}!=1) { - $formula_ans_hash->{ans_message} = $notation_ans_hash->{ans_message}; - if ($notation2_ans_hash->{score}==1) - {$formula_ans_hash->setKeys( 'ans_message' =>"Your answer must be in correct function notation.");} - $formula_ans_hash->{score} = 0; - } - - return $formula_ans_hash; - } -}; -return $new_evaluator; -} - -################################################################### -# 7) Checks factors of polynomials. -# Note: Student's answers must be of the form: monomial(poly)...(poly) with -# or without parentheses about the monomial. The polynomial factors must -# not contain any other grouping symbols and, in any case, only parenthesis -# may be used. This needs to be in the instructions for any set that uses -# this macro (in the screenHeader.pg file). -# Note: "StrictFactoringEvaluator" requires leading negatives to be factored out. -# This not may not work with all situations. BE SURE TO CHANGE THIS FILE IF THE -# FactoringEvaluator IS CHANGED!! -#---------For diagnosing problems-------------------------------------- -# $ans_hash->setKeys( 'ans_message' =>"FYI: Not checking correctly. -# AnswerHashScore: $ans_hash->{score} : -# Student: $#student_factors, $student_ans, -# Formatted: $format_student_ans, -# Student Factors: $student_factors[0], $student_factors[1], -# : Correct: $#factors, $factors[0], $factors[1], -# Number matched: $CorrectFactors"); -#---------------------------------------------------------------------- - - -sub FactoringEvaluator { - - Context()->strings->add("Does not factor"=>()); - - my $ans = shift; - my $ans_text = $ans; #For string answers like "Does not factor" - if ($ans=~/factor/) - { - $ans = shift; - } - my @vars = @_; - - my $format_ans = $ans; - $format_ans =~ s/\*/ /g; #Remove any astrix - $format_ans =~ s/[\(]/,\(/g; #Put in the delimiter , - if ($format_ans=~/^,/) {$format_ans=~ s/,//;} #Remove any leading comma - my @factors = split(/[,]/, $format_ans); #Split off the terms - for $k (0..$#factors) {$factors[$k] = Formula($factors[$k])->reduce;} - $ans = Formula($ans)->reduce; - - my $old_evaluator = fun_cmp($ans,var=>@vars); - - my $new_evaluator = sub { - local($factor_eval,$negfactor_eval,$factor_hash,$negfactor_hash); - - my $student_ans = shift; - -#---------For string answers---------------------------------------------- - if ($student_ans =~ /factor/) - { - my $old_eval_string = str_cmp($ans_text); - my $ans_hash = $old_eval_string->evaluate($student_ans); - $ans_hash->{correct_ans} = $ans_text; #For better display (no caps) - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - return $ans_hash; - } -#--------------------------------------------------------------------------- - - my $ans_hash = $old_evaluator->evaluate($student_ans); - -#----------The only parentheses are allowed for grouping symbols------------ -#----------and only the minimum set needed to enter the answer correctly.--- - if ($student_ans =~ /[\{\}\[\]]/) - { - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{score}=0; - $ans_hash->setKeys( 'ans_message' =>"You may only use parentheses in your answer."); - return $ans_hash; - } - if ($student_ans =~ /[\(][^\)]*[\(]/) - { - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{score}=0; - $ans_hash->setKeys( 'ans_message' =>"Your answer has extraneous grouping symbols."); - return $ans_hash; - } - -#-----------Check the factors--------------------------------------------- - - if ($ans_hash->{score}==1) { - my $format_student_ans = $student_ans; - $format_student_ans =~ s/[\*]/ /g; #Remove any astrix - $format_student_ans =~ s/[\(]/,\(/g; #Put in the delimiter , - if ($format_student_ans=~/^,/) {$format_student_ans=~ s/,//;} #Remove leading commas - my @student_factors = split(/[,]/, $format_student_ans); #Split off the terms - for $k (0..$#student_factors) {$student_factors[$k] = Formula($student_factors[$k]);} - - #########Could this be done with a list?--would have to change the list's error msg. - - my $CorrectFactors = 0; - - foreach $i (0..$#factors) - { - $factor_eval = fun_cmp($factors[$i],var=>@vars); - $negfactor_eval = fun_cmp(-1*($factors[$i]),var=>@vars); - foreach $j (0..$#student_factors) - { - $factor_hash = $factor_eval->evaluate($student_factors[$j]); - $negfactor_hash = $negfactor_eval->evaluate($student_factors[$j]); - if ($factor_hash->{score}==1 || $negfactor_hash->{score}==1) - { - $CorrectFactors=$CorrectFactors+1; - -#---------This segment is for binomial factors raised to powers---------------- -# Example: (3x+1)^2 is correct, and (9x^2+6x+1) should not be accepted -# SHOULD MAKE THIS MORE GENERAL: TAKE OTHER EXPONENTS BESIDES INTEGERS, -# THE PATTERN SHOULD BE BETTER DEFINED. -#------------------------------------------------------------------------------ - $factor_string = $factors[$i]->string; - $st_factor_string = $student_factors[$j]->string; - if ($factor_string =~ /\(.*[+-]{1}.*\)\^\d+$/ && $st_factor_string !~ /\(.*[+-]{1}.*\)\^\d+$/) - { - $ans_hash->{score}=0; - $ans_hash->setKeys( 'ans_message' =>"Factor the expression completely."); - } -#------------------------------------------------------------------------------- - } - } - } - - if ($CorrectFactors!=$#factors+1) - { - $ans_hash->{score}=0; - $ans_hash->setKeys( 'ans_message' =>"Check that your answer is factored - completely and is entered in the - required format."); - } - } - - if ($ans_text=~/factor/) {$ans_hash->{correct_ans} = $ans_text;} #In case the answer is a string - - return $ans_hash; - }; #END of SUB - - Context()->strings->remove("Does not factor"=>()); - return $new_evaluator; -} - - -sub StrictFactoringEvaluator { - - my $ans = shift; - my @vars = @_; - - my $format_ans = $ans; - $format_ans =~ s/[\*]//g; #Remove any astrix - $format_ans =~ s/[\(]/,\(/g; #Put in the delimiter , - if ($format_ans=~/^,/) {$format_ans=~ s/,//;} #Remove any leading commas - my @factors = split(/[,]/, $format_ans); #Split off the terms - for $k (0..$#factors) {$factors[$k] = Formula($factors[$k]);} - $ans = Formula($ans)->reduce; - - my $old_evaluator = fun_cmp($ans,var=>@vars); - - my $new_evaluator = sub { - local($factor_eval,$factor_hash); - - my $student_ans = shift; - -#----This could be changed so that it would check prime polyomials here instead -#----of in the problem template. - if ($student_ans =~ /factor/) - { - my $string_ans = $ans->string; - my $old_eval_string = std_cs_str_cmp($string_ans); - my $ans_hash = $old_eval_string->evaluate($student_ans); - return $ans_hash; - } -#------------------------------------------------------ - - my $ans_hash = $old_evaluator->evaluate($student_ans); - - if ($ans_hash->{score} == 1) { #Check factors - my $format_student_ans = $student_ans; - $format_student_ans =~ s/[\*]//g; #Remove any astrix - $format_student_ans =~ s/[\(]/,\(/g; #Put in the delimiter , - if ($format_student_ans=~/^,/) {$format_student_ans=~ s/,//;} #Remove any leading commas - my @student_factors = split(/[,]/, $format_student_ans); #Split off the terms - for $k (0..$#student_factors) {$student_factors[$k] = Formula($student_factors[$k]);} - - #########Could this be done with a list?--would have to change the list's error msg. - - my $CorrectFactors = 0; - - foreach $i (0..$#factors) - { - $factor_eval = fun_cmp($factors[$i],var=>@vars); - foreach $j (0..$#student_factors) - { - $factor_hash = $factor_eval->evaluate($student_factors[$j]); - if ($factor_hash->{score}==1) - { - $CorrectFactors=$CorrectFactors+1; - } - } - } - - if ($CorrectFactors!=$#factors+1) - { - $ans_hash->{score}=0; - $ans_hash->setKeys( 'ans_message' =>"Check that your answer is factored completely and is entered in the required format."); - } - } - return $ans_hash; - }; - return $new_evaluator; -} - -################################################################### -# 8) Checks for simplified rational expressions. -# Note: Student's answers must be of the form: (poly)/(poly) -# -#---------For diagnosing problems-------------------------------------- -# $ans_hash->setKeys( 'ans_message' =>"FYI: Not checking correctly. -# Student: $student_num, $student_den,$student_ans; -# Answer: $num,$den,$ans"); -#---------------------------------------------------------------------- - -sub RationalExpEvaluator { - - Context()->strings->add( "Does not simplify" => () ); - - my $ans = shift; - my $ans_text = $ans; #Mainly for string answers like "Does not simplify" - if ( $ans =~ /not/ ) { - $ans = shift; - } - my @vars = @_; - - #---------Split off the numerator/denominator - my @factors = split( q[/], $ans ); - my $num = Formula( $factors[0] ); - my $den = ($#factors > 0) ? Formula( $factors[1] ) : Formula("1"); - - my $old_evaluator = fun_cmp( $ans, var => @vars ); - - my $new_evaluator = sub { - my $student_ans = shift; - my $student_ans_text = $student_ans; - - #---------For string answers---------------------------------------------- - if ( $student_ans_text =~ /not/ ) { - my $old_eval_string = str_cmp($ans_text); - my $ans_hash = $old_eval_string->evaluate($student_ans_text); - $ans_hash->{correct_ans} = $ans_text; - $ans_hash->{student_ans} = $student_ans_text; - $ans_hash->{original_student_ans} = $student_ans_text; - return $ans_hash; - } - #------------------------------------------------------------------------- - - my $ans_hash = $old_evaluator->evaluate($student_ans); - - if ( $ans_hash->{score} == 1 ) { - #--------Check for a scalar answer in case and the student entered a decimal - if ( $student_ans !~ /[a-zA-Z]/ && $ans !~ /[a-zA-Z]/ ) { - $ans_hash->{correct_ans} = $ans_text; - $ans_hash->{student_ans} = $student_ans_text; - $ans_hash->{original_student_ans} = $ans_hash->{student_ans}; - return $ans_hash; - } - #-------------------------------------------------------------------- - - #--------Split off the numerator/denominator------------------------- - my $student_ans_mo = Formula( $student_ans_text ); - my ( $student_num, $student_den ); - my @student_factors; - - # use MO parse tree rather than regex split because of surrounding parens from mathquill - if ( $student_ans_mo->{tree}->class eq 'BOP' && $student_ans_mo->{tree}{bop} =~ m!/! ) { - $student_num = $student_ans_mo->{tree}{lop}; - $student_den = $student_ans_mo->{tree}{rop}; - @student_factors = ( $student_num->string, $student_den->string ); - } else { - $student_num = $student_ans_mo; - $student_den = Formula('1'); - @student_factors = ( $student_num->string ); - } - #-------------------------------------------------------------------- - - if ( $#factors != $#student_factors ) { - $ans_hash->{score} = 0; - } else { - my $num_eval = fun_cmp( $num, var => @vars ); - my $num_hash = $num_eval->evaluate($student_num); - $ans_hash->{score} = $num_hash->{score}; - - if ( $#factors > 0 ) { - my $den_eval = fun_cmp( $den, var => @vars ); - my $den_hash = $den_eval->evaluate($student_den); - $ans_hash->{score} = $den_hash->{score}; - } - } - - $ans_hash->{ans_message} = "Simplify your answer." if ( $ans_hash->{score} == 0 ); - } - - return $ans_hash; - }; - - Context()->strings->remove( "Does not simplify" => () ); - return $new_evaluator; -} - -################################################################### -# 9) Writes a fraction in reduced form. -# - -sub ReduceFraction { - my $num = shift; - my $den = shift; - my $n = 1; - my $d = 1; - - ($n,$d) = reduce($num,$den); - my $result = "$n/$d"; - if ($d==1) {$result = "$n";} - - return($result); -} - - - -################################################################### -# 10) Checks a list of equations. -# Designed for checking a list of asymptotes - -sub equation_cmp_list { - -loadMacros( - "extraAnswerEvaluators.pl" -); - - my $ans = shift; - my @vars = @_; - - my $format_ans = $ans; - $format_ans =~ tr/ //; #Remove any whitespace - my @ans_list = split(/[,]/, $format_ans); #Split off the terms - -# -# my $old_evaluator2 = str_cmp($ans_list[1]); -# Would like to change this to an equation evaluator later -# my $old_evaluator1 = equation_cmp($ans[0],vars=>@vars); -# my $old_evaluator2 = equation_cmp($ans[1],vars=>@vars); - - my $new_evaluator = sub { - - local($eq_eval,$ans_hash); - - my $student_ans = shift; - chomp $student_ans; - my $format_st = $student_ans; - $format_st =~ tr/ //; #Remove any whitespace - - my @student_list = split(/,/,$format_st); - - my $Correct = -1; - foreach $i (0..$#ans_list) #Count the number of matches between the lists - { - my $eq_eval = str_cmp($ans_list[$i]); - foreach $j (0..$#student_list) - { - $ans_hash = $eq_eval->evaluate($student_list[$j]); - if ($ans_hash->{score}==1) {$Correct=$Correct+1;} - } - } - my $Duplicates = -1; - foreach $i (0..$#student_list) #Check for duplicates in the student's list - { - my $eq_eval = str_cmp($student_list[$i]); - foreach $j (0..$#student_list) - { - $ans_hash = $eq_eval->evaluate($student_list[$j]); - if ($ans_hash->{score}==1) {$Duplicates=$Duplicates+1;} - } - } - if ($Correct!=$#ans_list || $Duplicates!=$#student_list) - { - $ans_hash->{score}=0; - if ($Duplicates!=$#student_list) - { - $ans_hash->setKeys( 'ans_message' =>"You have listed an asymptote more than once."); - } - } - else - { - $ans_hash->{score}=1; - } - $ans_hash->{correct_ans} = $ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $ans_hash->{student_ans}; -# $ans_hash->{type} = '', - $ans_hash->{preview_text_string} = $student_ans; - $ans_hash->{preview_latex_string} = $student_ans; - return $ans_hash; - }; - return $new_evaluator; -} - - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/Differentiation.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/Differentiation.pl deleted file mode 100755 index b614caeeb9..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/Differentiation.pl +++ /dev/null @@ -1,20 +0,0 @@ -# -# Example of how to add new functionality to the Parser. -# -# Here we load new methods for the Parser object classes. Note, however, -# that these are PERSISTANT when used with webwork2 (mod_perl), and so we -# need to take care not to load them more than once. We look for the -# variable $Parser::Differentiation::loaded, which is defined in the -# differentiation package, in order to tell. -# -# DifferentiationDefs.pl is really just a copy of the -# Parser::Differentiation.pm file, and you really could just preload the -# latter instead by uncommenting the 'use Parser::Differentiation' line at -# the bottom of Parser.pm. (This file is really just a sample). The way -# it's done here will load it the first time it gets used, then will keep -# it around, so not much overhead even this way. -# - -loadMacros("DifferentiationDefs.pl") unless $Parser::Differentiation::loaded; - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/DifferentiationDefs.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/DifferentiationDefs.pl deleted file mode 100755 index 32c37b91aa..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/DifferentiationDefs.pl +++ /dev/null @@ -1,635 +0,0 @@ -# -# Extend differentiation to multiple variables -# Check differentiation for complex functions -# Do derivatives for norm and unit. -# -# Make shortcuts for getting numbers 1, 2, and sqrt, etc. -# - -################################################## -# -# Differentiate the formula in terms of the given variable -# -sub Parser::D { - my $self = shift; my $x = shift; - if (!defined($x)) { - my @vars = keys(%{$self->{variables}}); - my $n = scalar(@vars); - if ($n == 0) { - return $self->new('0') if $self->{isNumber}; - $x = 'x'; - } else { - $self->Error("You must specify a variable to differentiate by") unless $n ==1; - $x = $vars[0]; - } - } else { - return $self->new('0') unless defined $self->{variables}{$x}; - } - return $self->new($self->{tree}->D($x)); -} - -sub Item::D { - my $self = shift; - my $type = ref($self); $type =~ s/.*:://; - $self->Error("Differentiation for '$type' is not implemented",$self->{ref}); -} - - -######################################################################### - -sub Parser::BOP::comma::D {Item::D(shift)} -sub Parser::BOP::union::D {Item::D(shift)} - -sub Parser::BOP::add::D { - my $self = shift; my $x = shift; - $self = Parser::BOP->new( - $self->{equation},$self->{bop}, - $self->{lop}->D($x),$self->{rop}->D($x) - ); - return $self->reduce; -} - - -sub Parser::BOP::subtract::D { - my $self = shift; my $x = shift; - $self = Parser::BOP->new( - $self->{equation},$self->{bop}, - $self->{lop}->D($x),$self->{rop}->D($x) - ); - return $self->reduce; -} - -sub Parser::BOP::multiply::D { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - $self = - Parser::BOP->new($equation,'+', - Parser::BOP->new($equation,$self->{bop}, - $self->{lop}->D($x),$self->{rop}->copy($equation)), - Parser::BOP->new($equation,$self->{bop}, - $self->{lop}->copy($equation),$self->{rop}->D($x)) - ); - return $self->reduce; -} - -sub Parser::BOP::divide::D { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - $self = - Parser::BOP->new($equation,$self->{bop}, - Parser::BOP->new($equation,'-', - Parser::BOP->new($equation,'*', - $self->{lop}->D($x),$self->{rop}->copy($equation)), - Parser::BOP->new($equation,'*', - $self->{lop}->copy($equation),$self->{rop}->D($x)) - ), - Parser::BOP->new($equation,'^', - $self->{rop},Parser::Number->new($equation,2) - ) - ); - return $self->reduce; -} - -sub Parser::BOP::power::D { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - my $vars = $self->{rop}->getVariables; - if (defined($vars->{$x})) { - $vars = $self->{lop}->getVariables; - if (defined($vars->{$x})) { - $self = - Parser::Function->new($equation,'exp', - [Parser::BOP->new($equation,'*',$self->{rop}->copy($equation), - Parser::Function->new($equation,'log',[$self->{lop}->copy($equation)],0))]); - return $self->D($x); - } - $self = Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'log',[$self->{lop}->copy($equation)],0), - Parser::BOP->new($equation,'*', - $self->copy($equation),$self->{rop}->D($x)) - ); - } else { - $self = - Parser::BOP->new($equation,'*', - Parser::BOP->new($equation,'*', - $self->{rop}->copy($equation), - Parser::BOP->new($equation,$self->{bop}, - $self->{lop}->copy($equation), - Parser::BOP->new($equation,'-', - $self->{rop}->copy($equation), - Parser::Number->new($equation,1) - ) - ) - ), - $self->{lop}->D($x) - ); - } - return $self->reduce; -} - -sub Parser::BOP::cross::D {Item::D(shift)} -sub Parser::BOP::dot::D {Item::D(shift)} -sub Parser::BOP::underscore::D {Item::D(shift)} - -######################################################################### - -sub Parser::UOP::plus::D { - my $self = shift; my $x = shift; - return $self->{op}->D($x) -} - -sub Parser::UOP::minus::D { - my $self = shift; my $x = shift; - $self = Parser::UOP->new($self->{equation},'u-',$self->{op}->D($x)); - return $self->reduce; -} - -sub Parser::UOP::factorial::D {Item::D(shift)} - -######################################################################### - -sub Parser::Function::D { - my $self = shift; - $self->Error("Differentiation of '$self->{name}' not implemented",$self->{ref}); -} - -sub Parser::Function::D_chain { - my $self = shift; my $x = $self->{params}[0]; - my $name = "D_" . $self->{name}; - $self = Parser::BOP->new($self->{equation},'*',$self->$name($x->copy),$x->D(shift)); - return $self->reduce; -} - -############################# - -sub Parser::Function::trig::D {Parser::Function::D_chain(@_)} - -sub Parser::Function::trig::D_sin { - my $self = shift; my $x = shift; - return Parser::Function->new($self->{equation},'cos',[$x]); -} - -sub Parser::Function::trig::D_cos { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::Function->new($equation,'sin',[$x]) - ); -} - -sub Parser::Function::trig::D_tan { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'^', - Parser::Function->new($equation,'sec',[$x]), - Parser::Number->new($equation,2) - ); -} - -sub Parser::Function::trig::D_cot { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'^', - Parser::Function->new($equation,'csc',[$x]), - Parser::Number->new($equation,2) - ) - ); -} - -sub Parser::Function::trig::D_sec { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'sec',[$x]), - Parser::Function->new($equation,'tan',[$x]) - ); -} - -sub Parser::Function::trig::D_csc { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'csc',[$x]), - Parser::Function->new($equation,'cot',[$x]) - ) - ); -} - -sub Parser::Function::trig::D_asin { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'-', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x,Parser::Number->new($equation,2) - ) - )] - ) - ); -} - -sub Parser::Function::trig::D_acos { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'-', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x,Parser::Number->new($equation,2) - ) - )] - ) - ) - ); -} - -sub Parser::Function::trig::D_atan { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'+', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ) - ) - ); -} - -sub Parser::Function::trig::D_acot { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'+', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ) - ) - ) - ); -} - -sub Parser::Function::trig::D_asec { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'abs',[$x]), - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'-', - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ), - Parser::Number->new($equation,1) - )] - ) - ) - ); -} - -sub Parser::Function::trig::D_acsc { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'abs',[$x]), - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'-', - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ), - Parser::Number->new($equation,1) - )] - ) - ) - ) - ); -} - - -############################# - -sub Parser::Function::hyperbolic::D {Parser::Function::D_chain(@_)} - -sub Parser::Function::hyperbolic::D_sinh { - my $self = shift; my $x = shift; - return Parser::Function->new($self->{equation},'cosh',[$x]); -} - -sub Parser::Function::hyperbolic::D_cosh { - my $self = shift; my $x = shift; - return Parser::Function->new($self->{equation},'sinh',[$x]); -} - -sub Parser::Function::hyperbolic::D_tanh { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'^', - Parser::Function->new($equation,'sech',[$x]), - Parser::Number->new($equation,2) - ); -} - -sub Parser::Function::hyperbolic::D_coth { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'^', - Parser::Function->new($equation,'csch',[$x]), - Parser::Number->new($equation,2) - ) - ); -} - -sub Parser::Function::hyperbolic::D_sech { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'sech',[$x]), - Parser::Function->new($equation,'tanh',[$x]) - ) - ); -} - -sub Parser::Function::hyperbolic::D_csch { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'csch',[$x]), - Parser::Function->new($equation,'coth',[$x]) - ) - ); -} - -sub Parser::Function::hyperbolic::D_asinh { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'+', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ) - )] - ) - ); -} - -sub Parser::Function::hyperbolic::D_acosh { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'-', - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ), - Parser::Number->new($equation,1) - )] - ) - ); -} - -sub Parser::Function::hyperbolic::D_atanh { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'-', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ) - ) - ); -} - -sub Parser::Function::hyperbolic::D_acoth { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'-', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ) - ) - ); -} - -sub Parser::Function::hyperbolic::D_asech { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'*', - $x, - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'-', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ) - )] - ) - ) - ) - ); -} - -sub Parser::Function::hyperbolic::D_acsch { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::UOP->new($equation,'u-', - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'*', - Parser::Function->new($equation,'abs',[$x]), - Parser::Function->new($equation,'sqrt',[ - Parser::BOP->new($equation,'+', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'^', - $x, Parser::Number->new($equation,2) - ) - )] - ) - ) - ) - ); -} - - -############################# - -sub Parser::Function::numeric::D {Parser::Function::D_chain(@_)} - -sub Parser::Function::numeric::D_log { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return Parser::BOP->new($equation,'/',Parser::Number->new($equation,1),$x); -} - -sub Parser::Function::numeric::D_log10 { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'*', - Parser::Number->new($equation,CORE::log(10)), $x - ) - ); -} - -sub Parser::Function::numeric::D_exp { - my $self = shift; my $x = shift; - return $self->copy(); -} - -sub Parser::Function::numeric::D_sqrt { - my $self = shift; my $x = shift; - my $equation = $self->{equation}; - return - Parser::BOP->new($equation,'/', - Parser::Number->new($equation,1), - Parser::BOP->new($equation,'*', - Parser::Number->new($equation,2), - $self->copy - ) - ); -} - -sub Parser::Function::numeric::D_abs {Parser::Function::D(@_)} -sub Parser::Function::numeric::D_int {Parser::Function::D(@_)} -sub Parser::Function::numeric::D_sgn {Parser::Function::D(@_)} - -######################################################################### - -sub Parser::List::D { - my $self = shift; my $x = shift; - $self = $self->copy($self->{equation}); - foreach my $f (@{$self->{coords}}) {$f = $f->D($x)} - return $self->reduce; -} - - -sub Parser::List::Interval::D { - my $self = shift; - $self->Error("Can't differentiate intervals",$self->{ref}); -} - -sub Parser::List::AbsoluteValue::D { - my $self = shift; - $self->Error("Can't differentiate absolute values",$self->{ref}); -} - - -######################################################################### - -sub Parser::Number::D {Parser::Number->new(shift->{equation},0)} - -######################################################################### - -sub Parser::Complex::D {Parser::Number->new(shift->{equation},0)} - -######################################################################### - -sub Parser::Constant::D {Parser::Number->new(shift->{equation},0)} - -######################################################################### - -sub Parser::Value::D { - my $self = shift; my $x = shift; my $equation = $self->{equation}; - return Parser::Value->new($equation,$self->{value}->D($x,$equation)); -} - -sub Value::D { - my $self = shift; my $x = shift; my $equation = shift; - return 0 if $self->isComplex; - my @coords = @{$self->{data}}; - foreach my $n (@coords) - {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x,$equation)->data}} - return $self->new([@coords]); -} - -sub Value::List::D { - my $self = shift; my $x = shift; my $equation = shift; - my @coords = @{$self->{data}}; - foreach my $n (@coords) - {if (ref($n) eq "") {$n = 0} else {$n = $n->D($x)}} - return $self->new([@coords]); -} - -sub Value::Interval::D { - shift; shift; my $self = shift; - $self->Error("Can't differentiate intervals",$self->{ref}); -} - -sub Value::Union::D { - shift; shift; my $self = shift; - $self->Error("Can't differentiate unions",$self->{ref}); -} - -######################################################################### - -sub Parser::Variable::D { - my $self = shift; my $x = shift; - my $d = ($self->{name} eq $x)? 1: 0; - return Parser::Number->new($self->{equation},$d); -} - -######################################################################### - -sub Parser::String::D {Parser::Number->new(shift->{equation},0)} - -######################################################################### - -package Parser::Differentiation; -our $loaded = 1; - -######################################################################### - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/PGstandard.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/PGstandard.pl deleted file mode 100755 index d86d983e8e..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/PGstandard.pl +++ /dev/null @@ -1,10 +0,0 @@ -loadMacros( - "PG.pl", - "PGbasicmacros.pl", - "PGchoicemacros.pl", - "PGanswermacros.pl", - "PGauxiliaryFunctions.pl", - "PGgraphmacros.pl", -); - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/PGunion.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/PGunion.pl deleted file mode 100755 index 8b29a6fa78..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/PGunion.pl +++ /dev/null @@ -1,14 +0,0 @@ -# -# Load most of the interesting code developed at Union. -# - -loadMacros( - "unionMacros.pl", - "unionUtils.pl", - "unionProblem.pl", - "unionImage.pl", - "unionLists.pl", - "unionTables.pl", -); - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/altPlotMacros.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/altPlotMacros.pl deleted file mode 100755 index ae47655386..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/altPlotMacros.pl +++ /dev/null @@ -1,108 +0,0 @@ -sub _altPlotMacros_int {}; # don't reload this file - -# -# An alternative to plot_functions that allows any perl -# expression as the function (it doesn't get parsed by -# the PG function parser, so you can do fancier -# expressions; in particular, things like f(x,y)) -# -# This is just a hack, as it doesn't really parse the -# expression, so might not translate everything properly. -# It will be obsolete when I finish my new expression -# parser, but I needed it to handle traces of multivariable -# functions easier. --- DPVC -# - -sub alt_plot_functions { - my $graph = shift; - my @function_list = @_; - my $error = ""; - $error .= "The first argument to plot_functions must be a graph object" unless ref($graph) =~/WWPlot/; - my $fn; - my @functions=(); - foreach $fn (@function_list) { - - # model: "2.5-x^2 for x in <-1,0> using color:red and weight:2" - if ($fn =~ /^(.+)for\s*(\w+)\s*in\s*([\(\[\<])\s*([\d\.\-]+)\s*,\s*([\d\.\-]+)\s*([\)\]\>])\s*using\s*(.*)$/ ) { - my ($rule,$var, $left_br, $left_end, $right_end, $right_br, $options) = - ($1, $2, $3, $4, $5, $6, $7); - my %options = split( /\s*and\s*|\s*:\s*|\s*,\s*|\s*=\s*|\s+/,$options); - my ($color, $weight); - if ( defined($options{'color'}) ){ - $color = $options{'color'}; #set pen color - } else { - $color = 'default_color'; - } - if ( defined($options{'weight'}) ) { - $weight = $options{'weight'}; # set pen weight (width in pixels) - } else { - $weight = 2; - } - - my $subRef = alt_string_to_sub($rule,$var); - my $funRef = new Fun($subRef,$graph); - $funRef->color($color); - $funRef->weight($weight); - $funRef->domain($left_end , $right_end); - push(@functions,$funRef); - # place open (1,3) or closed (1,3) circle at the endpoints or do nothing <1,3> - if ($left_br eq '[' ) { - $graph->stamps(closed_circle($left_end,&$subRef($left_end),$color) ); - } elsif ($left_br eq '(' ) { - $graph->stamps(open_circle($left_end, &$subRef($left_end), $color) ); - } - if ($right_br eq ']' ) { - $graph->stamps(closed_circle($right_end,&$subRef($right_end),$color) ); - } elsif ($right_br eq ')' ) { - $graph->stamps(open_circle($right_end, &$subRef($right_end), $color) ); - } - - } else { - $error .= "Error in parsing: $fn"; - } - - } - die ("Error in plot_functions:\n\t $error") if $error; - @functions; # return function references unless there is an error. -} - -# -# A cheap way to convert a string to a perl function -# that returns the value of the expression given in the -# string. Since no special parsing is done, you need -# to make sure your function is essentially in perl -# form. -# -sub alt_string_to_sub { - my $expr = my_math_constants(shift); - my $x = shift; - # - # Give the variable a $ and rename it with an leading underscore - # - $expr =~ s/(\b|\d)$x\b/$1(\$_x)/g; - # - # Fix up the expression - # - $expr =~ s!\\frac\{([^\}]*)\}\s*\{([^\}]*)\}!($1)/($2)!g; # fractions - $expr =~ s/\{([^\}]*)\}/($1)/g; # TeX parameters - $expr =~ s/\\//g; # TeX slashes - $expr =~ s/\^/**/g; # change ^ to ** - # - # Do implied multiplication - # - $expr =~ s/\)\s*\(/\)*\(/g; - $expr =~ s/\)\s*([a-zA-Z0-9.])/\)*$1/g; - $expr =~ s/([\d.])(\s\d)/$1*$2/g; - $expr =~ s/([\d.])\s*([a-zA-Z\(])/$1*$2/g; - # - # Fix +-, -+, ++ and -- - # - $expr =~ s/\+\s*([\+-])/$1/g; - $expr =~ s/-\s*\+/-/g; $expr =~ s/-\s*-/+/g; - # - # Make the function - # - PG_restricted_eval "sub {my \$_x = shift; return $expr}"; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/answerUtils.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/answerUtils.pl deleted file mode 100755 index b49ad4cc86..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/answerUtils.pl +++ /dev/null @@ -1,100 +0,0 @@ -########################################################################## -# -# Utility routines for answer checkers -# -######################################################################### - -sub _answerUtils_init {}; # don't load again - -loadMacros("unionUtils.pl"); - -# -# Evaluate an answer checker with a given student answer. -# (works with old- and new-style answer checkers) -# -sub evaluateAnswer { - my ($ans_evaluator,$student_answer,$skipblanks) = @_; - return if (!$ans_evaluator); - return if ($skipblanks && trimString($student_answer) eq ''); - clearEvaluator($ans_evaluator); - if (ref($ans_evaluator) eq 'AnswerEvaluator') { - return($ans_evaluator->evaluate($student_answer)); - } elsif (ref($ans_evaluator) eq 'CODE' ) { - return(&$ans_evaluator($student_answer)); - } else { - warn "There is a problem using the answer evaluator"; - } -} - -# -# Call an answer evaluator (works for new- and old-style checkers) -# -sub isCorrectAnswer { - my $cmp = shift; my $correct = $cmp->{rh_ans}->{correct_ans}; - my $hash = evaluateAnswer($cmp,@_,1); - $cmp->{rh_ans}->{correct_ans} = $correct; - return(0) unless defined($hash); - return($hash->{score} == 1); -} - -# -# Clear the error condition for an answer evaluator -# -sub clearEvaluator { - my $hash = hashFor(shift); - return(0) unless defined($hash); - $hash->setKeys( - ans_message => '', - preview_text_string => '', - preview_latex_string => '', - original_student_ans => '', - student_ans => '', - ); - $hash->score(0); -} - -# -# Get the answer hash for a given evaluator -# -sub hashFor { - my ($ans_evaluator) = @_; - if (ref($ans_evaluator) eq 'AnswerEvaluator') {return $ans_evaluator->rh_ans} - elsif (ref($ans_evaluator) eq 'CODE' ) {return $ans_evaluator} - else {warn "There is a problem using the answer evaluator"} -} - -# -# Make error messages returned by answer checkers prettier -# -sub IndentError { - my $error = trimString(shift); - my $n = shift; $n = 4 unless defined($n); - my $indent = ''; $indent .= ' ' while ($n--); - $error =~ s/ (There is a syntax error)/\n$1/g; - $error =~ s/ Your/\nYour/g; - $error =~ s/\n */\n$indent/g; - return $indent.$error -} - -# -# Return the string or a blank string (if it was not defined) -# -sub StringOrBlank { - my $s = shift; my $default = shift; - $default = '' unless defined($default); - $s = $default unless defined($s); - return $s; -} - -# -# Check for preview mode -# -sub isPreviewMode { - # for WW1 - return ($inputs_ref->{action} =~ m/^Preview/) - if (defined($inputs_ref) && defined($inputs_ref->{action})); - # for WW2 - return defined($inputs_ref) && defined($inputs_ref->{previewAnswers}); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/choiceUtils.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/choiceUtils.pl deleted file mode 100755 index 7b76e398a3..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/choiceUtils.pl +++ /dev/null @@ -1,76 +0,0 @@ -sub _choiceUtils_init {}; # don't reload this file - -# -# A replacement for std_print_q that uses tables to align the questions, so -# that if a question wraps, it is properly indented. -# - -sub alt_print_q { - my $self = shift; - my @questions = @_; - my $length = $self->{ans_rule_len}; - my $sep = $self->{separation}; $sep = 0 unless defined($sep); - my $valign = $self->{valign}; $valign = "TOP" unless defined($valign); - my $i=1; my $quest; - - my $out = ""; - if ($main::displayMode =~ m/^HTML/) { - $out = "\n

\n\n"; - foreach $quest (@questions) { - $out .= '\n"; - } - $out .= "
'. ans_rule($length). - " ".$i++.". $quest
\n"; - } elsif ($main::displayMode eq 'Latex2HTML') { - $out = "\\par\n\\begin{rawhtml}" . - "\\end{rawhtml}\n"; - foreach $quest (@questions) { - $out .= '\begin{rawhtml}'. - ''. - '\\end{rawhtml}'."\n"; - } - $out .= "\\begin{rawhtml}
\end{rawhtml}'.ans_rule($length).'\begin{rawhtml} '.$i++.'. \\end{rawhtml} '.$quest. - '\begin{rawhtml}
\n\\end{rawhtml}"; - } elsif ($main::displayMode eq 'TeX') { - $out = "\n\\par\\begin{enumerate}\n\\advance\\leftskip by 2em"; - foreach $quest (@questions) - {$out .= "\\item[".ans_rule($length).' '.$i++.".] $quest\n"} - $out .= "\\end{enumerate}\n"; - } else { - $out = "Error: alt_print_q: Unknown displayMode: $main::displayMode."; - } - $out; -} - -sub alt_print_a { - my $self = shift; - my (@array) = @_; - my $sep = $self->{separation} || 0; - my $valign = $self->{valign} || "TOP"; - my $i = 0; - - my $out= MODES( - TeX => "\\begin{enumerate}\n", - Latex2HTML => qq{\\begin{rawhtml}\\end{rawhtml}\n}, - HTML => qq{
}, - ); - my $elem; - foreach $elem (@array) { - my $c = $main::ALPHABET[$i]; - $out .= MODES( - TeX => "\\item[$c.] $elem\n", - Latex2HTML => qq{\\begin{rawhtml}} - . qq{\\end{rawhtml}\n}, - HTML => qq{\n}, - ); - $i++; - } - $out .= MODES( - TeX => "\\end{enumerate}\n", - Latex2HTML => "\\begin{rawhtml}
$c. \\end{rawhtml}$elem\\begin{rawhtml}
$c. $elem
\\end{rawhtml}\n", - HTML => "\n", - ); - $out; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/compositionAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/compositionAnswer.pl deleted file mode 100755 index 1fad973813..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/compositionAnswer.pl +++ /dev/null @@ -1,154 +0,0 @@ -loadMacros('answerUtils.pl'); - -sub _compositionAnswer_init {}; # don't reload this file - -###################################################################### -# -# An answer checker that determines if two functions compose -# to form a given function. (For use in problems where you ask -# a student to break a given function into a composition of two -# simpler functions, neither of which is allowed to be the identity -# function.) -# - -###################################################################### -# -# An answer checked to see if f composed with g matches a given function. -# -# Usage: COMPOSITION_ANS(f,g,options) -# -# where f and g are one possible decomposition of the target function -# (these are used to display the "correct" answer, and the composition -# is computed from them) and options are any of the options allowed -# by composition_ans_list below. -# -# This function actually supplies TWO answer checkers, for the two -# previous answer blanks. So be sure to call it immediately after -# the answer blanks have been supplied. (It may be best to use the -# NAMED_COMPOSITION_ANS checker below, which specifies the answer -# blanks explicitly.) -# -# Example: -# -# BEGIN_TEXT -# \(f\circ g = (1+x)^2\) when -# \(f(x)\) = \{ans_rule(20)\} and \(g(x)\) = \{ans_rule(20)\} -# END_TEXT -# COMPOSITION_ANS("x^2","1+x"); -# -# -sub COMPOSITION_ANS { - my $f = shift; my $g = shift; - my $fID = ANS_NUM_TO_NAME($main::ans_rule_count-1); - my $gID = ANS_NUM_TO_NAME($main::ans_rule_count); - my %ans = composition_ans_list($fID=>$f,$gID=>$g,@_); - ANS(values(%ans)); -} - - -###################################################################### -# -# An answer checked to see if f composed with g matches a given function. -# -# Usage: NAMED_COMPOSITION_ANS(fID=>f,gID->g,options) -# -# where fID and gID are the names of the answer rules for the functions -# f and g, and f and g are the answers for the functions. Options are -# any of the options allowed by composition_ans_list below. -# -# This routine allows you to put the f and g answer blanks at any -# location in the problem, and in any order. -# -# Example: -# -# BEGIN_TEXT -# \(g\circ f = (1+x)^2\) when -# \(f(x)\) = \{NAMED_ANS('f',20)\} and \(g(x)\) = \{NAMED_ANS('g',20)\} -# END_TEXT -# NAMED_COMPOSITION_ANS(f => "x^2", g => "1+x"); - -sub NAMED_COMPOSITION_ANS {NAMED_ANS(composition_ans_list(@_))} - - -###################################################################### -# -# This is an internal routine that returns the named answer checkers -# used by COMPOSITION_ANS and NAMED_COMPOSITION_ANS above. -# -# Usage: composition_ans_list(fID=>f,gID=>g,options) -# -# where fID and gID are the names of the answer rules for the functions -# and f and g are the answers for these functions. Options are from -# among: -# -# var => 'x' the name of the variable to use (both -# functions use the same variable -- this -# should probably be improved). -# -# or any parameters that can be passed to fun_cmp. -# -sub composition_ans_list { - my ($fID,$f,$gID,$g,%params) = @_; - my ($i,$ident,$comp,$eval,$field,$fog,$student_ans); - my @IDs = ($fID,$gID); - my @cmp = (composition_cmp($f),composition_cmp($g)); - my @ans = ($fID => $cmp[0], $gID => $cmp[1]); - my $error = 0; - my $var = "x"; $var = $params{'var'} if (defined($params{'var'})); - - # - # Check that the answers exist (otherwise it's our first time through) - # - foreach $i (@IDs) {return(@ans) if (!defined($inputs_ref->{$i}))} - - $ident = fun_cmp($var,%params); - foreach $i (0,1) { - $eval = evaluateAnswer($ident,$inputs_ref->{$IDs[$i]}); - if ($eval->{ans_message} ne "") {$error = 1} - elsif ($eval->{score} == 1) { - $eval->{ans_message} = "The identity function is not allowed"; - $error = 1; $eval->{score} = 0; - } - foreach $field ('ans_message','error_message','preview_latex_string', - 'preview_text_string','student_ans') { - $cmp[$i]->rh_ans->{$field} = $eval->{$field}; $eval->{$field} = ""; - } - } - - if (!$error) { - $fog = $f; $fog =~ s/$var/($g)/g; - $student_ans = $inputs_ref->{$fID}; - $student_ans =~ s/$var/($inputs_ref->{$gID})/g; - if (isCorrectAnswer(fun_cmp($fog,%params),$student_ans)) { - $cmp[0]->rh_ans->{score} = $cmp[1]->rh_ans->{score} = 1; - } - } - return (@ans); -} - - -###################################################################### -# -# Evaluator that always returns correct or always returns incorrect, -# depending on the parameter passed to it. Used by COMPOSITION_ANS -# to produce "dummy" answer checkers for the two parts of the -# composition. -# -sub composition_cmp { - my $score = shift; - my %params = @_; - $params{debug} = 0 unless defined($params{debug}); - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash(type => "composition", correct_ans => $score); - $answerEvaluator->install_evaluator(\&composition_cmp_check,%params); - return $answerEvaluator; -} - -sub composition_cmp_check { - my $ans = shift; - my %params = @_; - return($ans); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/compoundProblem.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/compoundProblem.pl deleted file mode 100755 index 3538bfef48..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/compoundProblem.pl +++ /dev/null @@ -1,617 +0,0 @@ -sub _compoundProblem_init {}; # don't reload this file - -##################################################################### -# -# This package implements a method of handling multi-part problems -# that show only a single part at any one time. The students can -# work on one part at a time, and then when they get it right (or -# under other circumstances deterimed by the professor), they can -# move on to the next part. Students can not return to earlier parts -# once they have been completed. The score for problem as a whole is -# made up from the scores on the individual parts, and the relative -# weighting of the various parts can be specified by the problem -# author. -# -# To use the compoundProblem library, use -# -# loadMacros("compoundProblem.pl"); -# -# at the top of your file, and then create a compoundProblem object -# via the command -# -# $cp = new compoundProblem(options) -# -# where '$cp' is the name of a variable that you will use to -# refer to the compound problem, and 'options' can include: -# -# parts => n The number of parts in the problem. -# Default: 1 -# -# weights => [n1,...,nm] The relative weights to give to each -# part in the problem. For example, -# weights => [2,1,1] -# would cause the first part to be worth 50% -# of the points (twice the amount for each of -# the other two), while the second and third -# part would be worth 25% each. If weights -# are not supplied, the parts are weighted -# by the number of answer blanks in each part -# (and you must provide the total number of -# blanks in all the parts by supplying the -# totalAnswers option). -# -# totalAnswers => n The total number of answer blanks in all -# the parts put together (this is used when -# computing the per-part scores, if part -# weights are not provided). -# -# saveAllAnswers => 0 or 1 Usually, the contents of named answer blanks -# from previous parts are made available to -# later parts using variables with the -# same name as the answer blank. Setting -# saveAllAnswers to 1 will cause ALL answer -# blanks to be available (via variables -# like $AnSwEr1, and so on). -# Default: 0 -# -# parserValues => 0 or 1 Determines whether the answers from previous -# parts are returned as MathObjects (like -# those returned from Real(), Vector(), etc) -# or as strings (the unparsed contents of the -# student answer). If you intend to use the -# previous answers as numbers, for example, -# you would want to set this to 1 so that you -# would get the final result of any formula -# the student typed, rather than the formula -# itself as a character string. -# Default: 0 -# -# nextVisible => type Tells when the "go on to the next part" option -# is available to the student. The possible -# types include: -# -# 'ifCorrect' next is available only when -# all the answers are correct. -# -# 'Always' next is always available -# (but remember that students -# can't go back once they go -# on.) -# -# 'Never' next is never allowed (the -# problem will control going -# on to the next part itself). -# -# Default: 'ifCorrect' -# -# nextStyle => type Determines the style of "next" indicator to display -# (when it is available). The type can be one of: -# -# 'CheckBox' a checkbox that allows the students -# to go on to the next part when they -# submit their answers. -# -# 'Button' a button that submits their answers -# and goes on to the next part. -# -# 'Forced' forces the student to go on to the -# next part the next time they submit -# answers. -# -# 'HTML' allows you to provide an arbitrary -# HTML string of your own. -# -# Default: 'Checkbox' -# -# nextLabel => string Specifies the string to use as the label for the checkbox, -# the name of the button, the text of the message indicating -# that the next submit will move to the next part, or the -# HTML string, depending on the setting of nextStyle above. -# -# nextNoChange => 0 or 1 Since the students must submit their answers again to go on -# to the next part, it is possible for them to change their -# answers before they submit, and if nextVisible is 'ifCorrect' -# they might go on to the next without having correct answers -# stored. This option lets you control whether the answers -# are checked against the previous ones before going on to the -# next part. If the answers don't match, a warning is issued -# and they are not allowed to move on. -# Default: 1 -# -# allowReset => 0 or 1 Determines whether a "Go back to the first part" checkbox -# is provided on parts 2 and later. This is intended for -# the professor during testing of the problem (otherwise -# it would be impossible to go back to earlier parts). -# Default: 0 -# -# resetLabel => string The string used to label the reset checkbox. -# -# Once you have created a compoundProblem object, you can use $cp->part to -# determine the part that the student is working on, and use 'if' statements -# to display the proper information for the given part. The compoundProblem -# object takes care of maintaining the data as the parts change. (See the -# compoundProblem.pg file for an example of a compound problem.) -# -# In order to handle the scoring of the problem as a whole when only part is -# showing, the compoundProblem object uses its own problem grader to manage -# the scores, and calls your own grader from there. The default is to use -# the one that was installed before the compoundProblem object was created, -# or avg_problem_grader if none was installed. You can specify a different -# one using the $cp->useGrader() method (see below). It is important that -# you NOT call install_problem_grader() yourself once you have created the -# compoundProblem object, as that would disable the special grader, causing -# the compound problem to fail to work properly. -# -# You may call the following methods once you have a compoundProblem: -# -# $cp->part Returns the part the student is working on. -# $cp->part(n) Sets the part to be part n, as long as the -# student has finished the preceeding parts. -# If not, the part is set to the highest -# one the student hasn't completed, and he -# can work up to the given part. (The -# nextVisible option is set to 'ifCorrect' if -# it was 'Never' so that students can go on -# once they finish the earlier parts.) -# -# $cp->useGrader(code_ref) Supplies your own grader to use in -# place of the default one. For example: -# $cp->useGrader(~~&std_problem_grader); -# -# $cp->score Returns the (weighted) score for this part. -# Note that this is the score shown at the bottom -# of the page on which the student pressed submit -# (not the score for the answers the student is -# submitting -- that is not available until -# after the body of the problem has been created). -# -# $cp->scoreRaw Returns the unweighted score for this part. -# -# $cp->scoreOverall Returns the overall score for the problem -# so far. -# -# $cp->addAnswers(list) Make additional answer blanks be available -# from one part to another. E.g., -# $cp->addAnswers('AnSwEr1'); -# would make the first unnamed blank be available -# in later parts as well. (This command should -# be issued only when the part containing the -# given answer blank is displayed.) -# -# $cp->nextCheckbox(label) Returns the HTML string for the "go on to next -# part" checkbox so you can use it in the body of -# the problem if you wish. This should not be -# inserted when the $displayMode is 'TeX'. If the -# label is not given or is blank, the default label -# is used. -# -# $cp->nextButton(label) Returns the HTML string for the "go on to next -# part" button so you can use it in the body of -# the problem if you wish. This should not be -# inserted when the $displayMode is 'TeX'. If the -# label is not given or is blank, the default label -# is used. -# -# $cp->nextForces(label) Returns the HTML string for the forced "go on to -# next part" so you can use it in the body of -# the problem if you wish. This should not be -# inserted when the $displayMode is 'TeX'. If the -# label is not given or is blank, the default label -# is used. -# -# $cp->reset Go back to part 1, clearing the answers -# and score. (Best used when debugging problems.) -# -# $cp->resetCheckbox(label) Returns the HTML string for the reset checkbox -# so that you can provide one within the body -# of the problem if you wish. This should not be -# inserted when the $displayMode is 'TeX'. If the -# label is not given or is blank, the default label -# will be used. -# -###################################################################### - -package compoundProblem; -# -# The state data that is stored between invocations of -# the problem. -# -our %defaultStatus = ( - part => 1, # the current part - answers => "", # answer labels from previous parts - new_answers => "", # answer labels for THIS part - ans_rule_count => 0, # the ans_rule count from previous parts - new_ans_rule_count => 0, # the ans_rule count from THIS part - score => 0, # the (weighted) score on this part - total => 0, # the total on previous parts - raw => 0, # raw score on this part -); -# -# Create a new instance of the compound Problem and initialize -# it. This includes reading the status from the previous -# parts, defining the variables from the answers to previous parts, -# and setting up the grader so that the current data can be saved. -# - -sub new { - my $self = shift; my $class = ref($self) || $self; - my $cp = bless { - parts => 1, - totalAnswers => undef, - weights => undef, # array of weights per part - saveAllAnswers => 0, # usually only save named answers - parserValues => 0, # make Parser objects from the answers? - nextVisible => "ifCorrect", # or "Always" or "Never" - nextStyle => "Checkbox", # or "Button", "Forced", or "HTML" - nextLabel => undef, # Checkbox text or button name or HTML - nextNoChange => 1, # true if answer can't change for new part - allowReset => 0, # true to show "back to part 1" button - resetLabel => undef, # label for reset button - grader => $main::PG_FLAGS{PROBLEM_GRADER_TO_USE} || \&main::avg_problem_grader, - @_, - status => $defaultStatus, - }, $class; - die "You must provide either the totalAnswers or weights" - unless $cp->{totalAnswers} || $cp->{weights}; - $cp->getTotalWeight if $cp->{weights}; - main::loadMacros("Parser.pl") if $cp->{parserValues}; - $cp->reset if $cp->{allowReset} && $main::inputs_ref->{_reset}; - $cp->getStatus; - $cp->initPart; - return $cp; -} - -# -# Compute the total of the weights so that the parts can -# be properly scaled. -# -sub getTotalWeight { - my $self = shift; - $self->{totalWeight} = 0; $self->{totalAnswers} = 1; - foreach my $w (@{$self->{weights}}) {$self->{totalWeight} += $w} - $self->{totalWeight} = 1 if $self->{totalWeight} == 0; -} - -# -# Look up the status from the previous invocation -# and see if we need to go on to the next part. -# -sub getStatus { - my $self = shift; - main::RECORD_FORM_LABEL("_next"); - main::RECORD_FORM_LABEL("_status"); - $self->{status} = $self->decode; - $self->{isNew} = $main::inputs_ref->{_next} || ($main::inputs_ref->{submitAnswers} && - $main::inputs_ref->{submitAnswers} eq ($self->{nextLabel} || "Go on to Next Part")); - if ($self->{isNew}) { - $self->checkAnswers; - $self->incrementPart unless $self->{nextNoChange} && $self->{answersChanged}; - } -} - -# -# Initialize the current part by setting the ans_rule -# count (so that later parts will get unique answer names), -# installing the grader (to save the data), and setting -# the variables for previous answers. -# -sub initPart { - my $self = shift; - $main::ans_rule_count = $self->{status}{ans_rule_count}; - main::install_problem_grader(\&compoundProblem::grader); - $main::PG_FLAGS{compoundProblem} = $self; - $self->initAnswers($self->{status}{answers}); -} - -# -# Look through the list of answer labels and set -# the variables for them to be the associated student -# answer. Make it a Parser value if requested. -# Record the value so that is will be available -# again on the next invocation. -# -sub initAnswers { - my $self = shift; my $answers = shift; - foreach my $id (split(/;/,$answers)) { - my $value = $main::inputs_ref->{$id}; $value = '' unless defined($value); - if ($self->{parserValues}) { - my $parser = Parser::Formula($value); - $parser = Parser::Evaluate($parser) if $parser && $parser->isConstant; - $value = $parser if $parser; - } - ${"main::$id"} = $value unless $id =~ m/$main::ANSWER_PREFIX/o; - $value = quoteHTML($value); - main::TEXT(qq!!); - main::RECORD_FORM_LABEL($id); - } -} - -# -# Look to see is any answers have changed on this -# invocation of the problem. -# -sub checkAnswers { - my $self = shift; - foreach my $id (keys(%{$main::inputs_ref})) { - if ($id =~ m/^previous_(.*)$/) { - if ($main::inputs_ref->{$id} ne $main::inputs_ref->{$1}) { - $self->{answersChanged} = 1; - $self->{isNew} = 0 if $self->{nextNoChange}; - return; - } - } - } -} - -# -# Go on to the next part, updating the status -# to include the data from the old part so that -# it will be properly preserved when the next -# part is showing. -# -sub incrementPart { - my $self = shift; - my $status = $self->{status}; - if ($status->{part} < $self->{parts}) { - $status->{part}++; - $status->{answers} .= ';' if $status->{answers}; - $status->{answers} .= $status->{new_answers}; - $status->{ans_rule_count} = $status->{new_ans_rule_count}; - $status->{total} += $status->{score}; - $status->{score} = $status->{raw} = 0; - $status->{new_answers} = ''; - } -} - -###################################################################### -# -# Encode all the status information so that it can be -# maintained as the student submits answers. Since this -# state information includes things like the score from -# the previous parts, it is "encrypted" using a dumb -# hex encoding (making it harder for a student to recognize -# it as valuable data if they view the page source). -# -sub encode { - my $self = shift; my $status = shift || $self->{status}; - my @data = (); my $data = ""; - foreach my $id (main::lex_sort(keys(%defaultStatus))) {push(@data,$status->{$id})} - foreach my $c (split(//,join('|',@data))) {$data .= toHex($c)} - return $data; -} - -# -# Decode the data and break it into the status hash. -# -sub decode { - my $self = shift; my $status = shift || $main::inputs_ref->{_status}; - return {%defaultStatus} unless $status; - my @data = (); foreach my $hex (split(/(..)/,$status)) {push(@data,fromHex($hex)) if $hex ne ''} - @data = split('\\|',join('',@data)); $status = {%defaultStatus}; - foreach my $id (main::lex_sort(keys(%defaultStatus))) {$status->{$id} = shift(@data)} - return $status; -} - -# -# Hex encoding is shifted by 10 to obfuscate it further. -# (shouldn't be a problem since the status will be made of -# printable characters, so they are all above ASCII 32) -# -sub toHex {main::spf(ord(shift)-10,"%X")} -sub fromHex {main::spf(hex(shift)+10,"%c")} - -# -# Make sure the data can be properly preserved within -# an HTML tag. -# -sub quoteHTML { - my $string = shift; - $string =~ s/&/\&/g; $string =~ s/"/\"/g; - $string =~ s/>/\>/g; $string =~ s/{grader} = shift; -} - -# -# Make additional answer blanks from the current part -# be preserved for use in future parts. -# -sub addAnswers { - my $self = shift; - $self->{extraAnswers} = [] unless $self->{extraAnswers}; - push(@{$self->{extraAnswers}},@_); -} - -# -# Go back to part 1 and clear the answers and scores. -# -sub reset { - my $self = shift; - if ($main::inputs_ref->{_status}) { - my $status = $self->decode($main::inputs_ref->{_status}); - foreach my $id (split(/;/,$status->{answers})) {delete $main::inputs_ref->{$id}} - foreach my $id (1..$status->{ans_rule_count}) - {delete $main::inputs_ref->{"${main::QUIZ_PREFIX}${main::ANSWER_PREFIX}$id"}} - } - $main::inputs_ref->{_status} = $self->encode(\%defaultStatus); - $main::inputs_ref->{_next} = 0; -} - -# -# Return the HTML for the "Go back to part 1" checkbox. -# -sub resetCheckbox { - my $self = shift; - my $label = shift || " Go back to Part 1 (when you submit your answers)."; - my $par = shift; $par = ($par ? $main::PAR : ''); - qq'$par$label'; -} - -# -# Return the HTML for the "next part" checkbox. -# -sub nextCheckbox { - my $self = shift; - my $label = shift || " Go on to next part (when you submit your answers)."; - my $par = shift; $par = ($par ? $main::PAR : ''); - $self->{nextInserted} = 1; - qq!$par$label!; -} - -# -# Return the HTML for the "next part" button. -# -sub nextButton { - my $self = shift; - my $label = quoteHTML(shift || "Go on to Next Part"); - my $par = shift; $par = ($par ? $main::PAR : ''); - $par . qq!!; -} - -# -# Return the HTML for when going to the next part is forced. -# -sub nextForced { - my $self = shift; - my $label = shift || "Submit your answers again to go on to the next part."; - $label = $main::PAR . $label if shift; - $self->{nextInserted} = 1; - qq!$label!; -} - -# -# Return the raw HTML provided -# -sub nextHTML {shift; shift} - -###################################################################### -# -# Return the current part, or try to set the part to the given -# part (returns the part actually set, which may be earlier if -# the student didn't complete an earlier part). -# -sub part { - my $self = shift; my $status = $self->{status}; - my $part = shift; - return $status->{part} unless defined $part && $main::displayMode ne 'TeX'; - $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) || - lc($self->{nextVisible}) eq 'never') { - $self->initAnswers($status->{new_answers}); - $self->incrementPart; $self->{isNew} = 1; - } - } - if ($part != $status->{part}) { - main::TEXT(''); - $self->{nextVisible} = 'IfCorrect' if lc($self->{nextVisible}) eq 'never'; - } - return $status->{part}; -} - -# -# Return the various scores -# -sub score {shift->{status}{score}} -sub scoreRaw {shift->{status}{raw}} -sub scoreOverall { - my $self = shift; - return $self->{status}{score} + $self->{status}{total}; -} - -###################################################################### -# -# The custom grader that does the work of computing the scores -# and saving the data. -# -sub grader { - my $self = $main::PG_FLAGS{compoundProblem}; - - # - # Get the answer names and the weight for the current part. - # - my @answers = keys(%{$_[0]}); - my $weight = scalar(@answers)/$self->{totalAnswers}; - $weight = $self->{weights}[$self->{status}{part}-1]/$self->{totalWeight} - if $self->{weights} && defined($self->{weights}[$self->{status}{part}-1]); - @answers = grep(!/$main::ANSWER_PREFIX/o,@answers) unless $self->{saveAllAnswers}; - push(@answers,@{$self->{extraAnswers}}) if $self->{extraAnswers}; - my $space = ''; - - # - # Call the original grader, but put back the old recorded_score - # (the grader will have updated it based on the score for the PART, - # not the problem as a whole). - # - my $oldScore = ($_[1])->{recorded_score}; - my ($result,$state) = &{$self->{grader}}(@_); - $state->{recorded_score} = $oldScore; - - # - # Update that state information and encode it. - # - my $status = $self->{status}; - $status->{raw} = $result->{score}; - $status->{score} = $result->{score}*$weight; - $status->{new_ans_rule_count} = $main::ans_rule_count; - $status->{new_answers} = join(';',@answers); - my $data = quoteHTML($self->encode); - - # - # Update the recorded score - # - my $newScore = $status->{total} + $status->{score}; - $state->{recorded_score} = $newScore if $newScore > $oldScore; - $state->{recorded_score} = 0 if $self->{allowReset} && $main::inputs_ref->{_reset}; - - # - # Add the compoundProblem message and data - # - $result->{type} = "compoundProblem ($result->{type})"; - $result->{msg} .= '

Note: ' if $result->{msg}; - $result->{msg} .= 'This problem has more than one part.' - . '
'.$space.'Your score for this attempt is for this part only;' - . '
'.$space.'your overall score is for all the parts combined.' - . qq!!; - - # - # Warn if the answers changed when they shouldn't have - # - $result->{msg} .= '

You may not change your answers when going on to the next part!' - if $self->{nextNoChange} && $self->{answersChanged}; - - # - # Include the "next part" checkbox, button, or whatever. - # - my $par = 1; - if ($self->{parts} > $status->{part} && !$main::inputs_ref->{previewAnswers}) { - if (lc($self->{nextVisible}) eq 'always' || - (lc($self->{nextVisible}) eq 'ifcorrect' && $result->{score} >= 1)) { - my $method = "next".$self->{nextStyle}; $par = 0; - $result->{msg} .= $self->$method($self->{nextLabel},1).'
'; - } - } - - # - # Add the reset checkbox, if needed - # - $result->{msg} .= $self->resetCheckbox($self->{resetLabel},$par) - if $self->{allowReset} && $status->{part} > 1; - - # - # Make sure we don't go on unless the next button really is checked - # - $result->{msg} .= '' - unless $self->{nextInserted}; - - return ($result,$state); -} diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/contextFunctionAssign.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/contextFunctionAssign.pl deleted file mode 100755 index 6a7dfcc54e..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/contextFunctionAssign.pl +++ /dev/null @@ -1,20 +0,0 @@ -###################################################################### -#Description: macro to load parserAssignment and change the error -# message to be more specific for a function. Requires -# answers be submitted in the form: -# y=formula or f(x)=formula -###################################################################### -loadMacros("parserAssignment.pl"); - - sub parser::Assignment::Formula::cmp_equal { - my $self = shift; my $ans = shift; - Value::cmp_equal($self,$ans); - if ($ans->{ans_message} =~ m/Your answer isn't.*it looks like/s) { - $ans->{ans_message} = - "Warning: Your answer should be of the form: '".$self->{tree}{lop}->string."= formula'"; - } - } - -###################################################################### - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/contextInequalitiesAllowStrings.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/contextInequalitiesAllowStrings.pl deleted file mode 100755 index 40fc80351d..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/contextInequalitiesAllowStrings.pl +++ /dev/null @@ -1,55 +0,0 @@ -### - -=head1 NAME - - extra macros for Intermediate Algebra problems at CofI - -=head1 Synposis - macros by R Cruz -- The College of Idaho -=cut - -=head3 Allows string answers for the set of real numbers and the empty set - -=pod - Adds the string "All real numbers" to the Inequalities context - NOT WORKING: Adds the string "No solution" to the Inequalities context - -=cut - -loadMacros("contextInequalities.pl"); - -sub _contextInequalitiesAllowStrings_init { - my $context = $main::context{"Inequalities-AllowStrings"} = - Parser::Context->getCopy("Inequalities"); - - $context->constants->{namePattern} = qr/.*/; - -#----Add variations of "All real numbers" - - $context->constants->redefine("All real numbers",from=>"Interval",using=>"R"); - $context->constants->set("All real numbers"=>{TeX=>"\\mbox{All real numbers}"}); - - $context->constants->redefine("all real numbers",from=>"Interval",using=>"R"); - $context->constants->set("all real numbers"=>{TeX=>"\\mbox{all real numbers}"}); - - $context->constants->redefine("All Real numbers",from=>"Interval",using=>"R"); - $context->constants->set("All Real numbers"=>{TeX=>"\\mbox{All Real numbers}"}); - - $context->constants->redefine("ALL REAL NUMBERS",from=>"Interval",using=>"R"); - $context->constants->set("ALL REAL NUMBERS"=>{TeX=>"\\mbox{ALL REAL NUMBERS}"}); - -#-----Add variations of "No solution" = NONE -#************This part does not work. Can't get it to "take" the empty set -# Tried Set(), NONE and DNE -# -# $context->constants->redefine("No solution"",from=>"Interval",using=>"{}"); -# $context->constants->set("No solution"=>{TeX=>"\\mbox{No solution}"}); -# -# $context->constants->redefine("no solution"",from=>"Interval",using=>"{}"); -# $context->constants->set("no solution"=>{TeX=>"\\mbox{no solution}"}); -# -# $context->constants->redefine("NO SOLUTION"",from=>"Interval",using=>"{}"); -# $context->constants->set("NO SOLUTION"=>{TeX=>"\\mbox{NO SOLUTION}"}); - - } -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/courseHeaders.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/courseHeaders.pl deleted file mode 100755 index 164f683961..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/courseHeaders.pl +++ /dev/null @@ -1,193 +0,0 @@ -#!/usr/local/bin/perl - -sub _courseHeaders_init { -# -# This is the hint to display in screen headers. You can change this -# in the specific header .pg file for a problem set, provided you do so -# after the call to loadMacros that loads this file. -# - -$HINT = - "Give 4 or 5 significant digits when entering decimal numbers. ". - "For most problems, you can enter elementary expressions such as ". - "${BTT}2^3${ETT} instead of ${BTT}8${ETT}, ${BTT}sin(3pi/2)${ETT} ". - "instead of ${BTT}-1${ETT}, ${BTT}e^(ln(2))${ETT} instead of ${BTT}2${ETT}, ". - "${BTT}(2+tan(3))*(4-sin(5))^6-7/8${ETT} instead of ${BTT}27620.3413${ETT}, ". - "etc. Consult WeBWorK's ". - htmlLink('http://webwork.math.rochester.edu/webwork_system_html/docs/docs/pglanguage/availablefunctions.html','list of functions'). - " or review the problems in the orientation homework set for more ". - "information on the functions and values that WeBWorK understands."; - -}; - -###################################################################### -# -# This file implements a standard format for the screen and paper -# headers. The same file works for both, so there is no need to -# make duplicate files. See courseHeader() below for details. -# - -loadMacros( - "PG.pl", - "PGbasicmacros.pl", - "PGunion.pl" -); - -# -# Get the course name from the course ID -# -$course = $courseName; -$course =~ s/\d\d(FA|WI|SP|SU)-//; # Union-sepcific -$course =~ s/-.*//; # Union-specific -$course = protect_underbar($course); # just in case - -# -# Set some variables to use in headers -# -$dateTime = $formatedDueDate; -$sectionNumber = protect_underbar($sectionNumber); -$setNumber = protect_underbar($setNumber); - -# -# Evaluate the string as in PG, then trim white space from the ends -# -sub EV_trim { - my $string = EV2(@_); - $string =~ s/(^\s+|\s+$)//g; - return $string; -} - -# -# Get a number sign in all modes -# -$NUMBER = MODES(TeX => '\#', HTML => '#'); - -# -# couseHeader creates the header based on values passed to it. -# It works for both screen and paper headers, so you only need -# one header file. The options you can pass it are: -# -# topic => '...' a short string to be used in -# "this is an assignment on ..." -# -# preposition => 'on' -# what word to use before the topic -# -# bookinfo => '...' the section of the book to refer to for more -# information. -# -# bookprobs => ['...','...',] -# a list of strings giving problems from -# various sections of the book. As many -# strings can be supplied as you want. -# They will be placed on separate lines in -# the screen header, but on one line in the PDF -# file. If left empty, the word "none" will be -# supplied automatically. If set to undef, -# no book problems will be given. -# -# moreprobs => ['...','...',] -# a list of strings giving problems from -# various sections of the book. (See bookprobs -# for details of how this works.) -# -# text => '...' This is additional text to be inserted -# after the book assignment. It can explain -# more about the problems, give hints, etc. -# -# showhint => 0 or 1 Indicates whether the message about typing -# webwork answers should be shown in the screen -# header. It defaults to 1 (yes). -# You can change the variable $HINT to be -# a different hint, if you want. Be sure -# to do this AFTER the loadMacros call. -# -# -sub courseHeader { - my %ops = ( - topic => "", - preposition => "on", - bookinfo => "", - bookprobs => [], - moreprobs => undef, - text => "", - showhint => 1, - - topic_text => 'This is a collection of WeBWorK problems ', - bookinfo_text => 'Relevant information can be found in %s of your text.', - bookprobs_text => 'Book problems to do as part of this assignment:', - moreprobs_text => 'Additional problems to do with this assignment:', - @_ - ); - - if ($displayMode =~ m/^HTML/ || $displayMode eq "Latex2HTML") { -# TEXT($BBLOCKQUOTE); - TEXT(EV_trim($ops{topic_text}.$ops{preposition}.' '.$ops{topic}).".\n") - if (defined($ops{topic}) && $ops{topic} ne ""); - TEXT(sprintf($ops{bookinfo_text},$ops{bookinfo})) - if (defined($ops{bookinfo}) && $ops{bookinfo} ne ""); - TEXT($BR,$PAR,"\n\n"); - if (ref($ops{bookprobs}) eq "ARRAY") { - TEXT($ops{bookprobs_text}."\n"); - if (scalar(@{$ops{bookprobs}}) == 0) {TEXT("none.\n\n")} else { - TEXT( - $BBLOCKQUOTE, - EV_trim(join($BR."\n",@{$ops{bookprobs}})), - $EBLOCKQUOTE,"\n" - ); - } - TEXT($PAR,"\n\n"); - } - if (ref($ops{moreprobs}) eq "ARRAY") { - TEXT($ops{moreprobs_text}."\n"); - if (scalar(@{$ops{moreprobs}}) == 0) {TEXT("none.\n\n")} else { - TEXT( - $BBLOCKQUOTE, - EV_trim(join($BR."\n",@{$ops{moreprobs}})), - $EBLOCKQUOTE,"\n" - ); - } - } - TEXT($HR,$PAR,EV_trim($ops{text})) if ($ops{text} ne ""); - TEXT($HR,$PAR."\n\n",$BSMALL.$HINT.$ESMALL."\n\n",$PAR) - if ($ops{showhint}); -# TEXT($EBLOCKQUOTE); - } elsif ($displayMode eq "TeX") { - TEXT( - $BEGIN_ONE_COLUMN, - '{\parindent=0pt \parskip=4pt',"\n", - '{\large\bf '.$studentName.'\hfill '.$course.' '.$sectionNumber.'}', - '\break\null\hfill '. - "WeBWorK assignment $setNumber due $dateTime".'\par'."\n", - ); - TEXT(EV_trim($ops{topic_text}.$ops{preposition}.' '.$ops{topic}).".\n") - if (defined($ops{topic}) && $ops{topic} ne ""); - TEXT(sprintf($ops{bookinfo_text},$ops{bookinfo})) - if (defined($ops{bookinfo}) && $ops{bookinfo} ne ""); - TEXT('\par',"\n\n"); - if (ref($ops{bookprobs}) eq "ARRAY") { - TEXT($ops{bookprobs_text}.' '); - if (scalar(@{$ops{bookprobs}}) == 0) {TEXT("none.\\par\n\n")} else { - TEXT( - EV_trim(join("; ",@{$ops{bookprobs}})). - '.\par'."\n\n" - ); - } - } - if (ref($ops{moreprobs}) eq "ARRAY") { - TEXT($ops{moreprobs_text}.' '); - if (scalar(@{$ops{moreprobs}}) == 0) {TEXT("none.\\par\n\n")} else { - TEXT( - EV_trim(join("; ",@{$ops{moreprobs}})). - '.\par'."\n\n" - ); - } - } - TEXT(EV_trim($ops{text}).'\par'."\n\n") if ($ops{text} ne ""); - TEXT('\par}',$END_ONE_COLUMN); - } else { - warn "courseHeader: Unknown display mode: $displayMode" - } -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/diffquotientAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/diffquotientAnswer.pl deleted file mode 100755 index 21d246d8dd..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/diffquotientAnswer.pl +++ /dev/null @@ -1,119 +0,0 @@ -loadMacros('answerUtils.pl'); - -sub _diffquotientAnswer_init {}; # don't reload this file - -######################################################################### -## -## An answer checker for handling difference quotients. It requires -## that the student simplify the equation (at least far enough to -## cancel the dx in the bottom). -## - - -######################################################################### -# -# Usage: diff_quotient_cmp(ans,options) -# -# where ans is the correct difference quotient, and options are -# from among: -# -# var => 'x' specifies the variable for the difference quotient -# -# limits => [a,b] gives the lower and upper limits of the domain -# to use for checking the student's response. -# -# or any of the parameters that can be passed to fun_cmp(). -# -# This checker checks that the student answer is the correct difference -# and that it doesn't contain dx in the denominator (i.e., they will -# have had to simplify it at least far enough to cancel the dx). -# -# The checker accepts 'dx' or 'H' as the difference in the x variable. -# (This is a bit of a hack, and there is a chance that the student could -# receive odd error messages because it it.) -# -# Example: -# -# BEGIN_TEXT -# Find the simplified difference quotient for \(f(x)=x^2\): -# \{ans_rule(30)\}. -# END_TEXT -# -# ANS(diff_quotient_cmp("2x+dx")); -# -# - -sub diff_quotient_cmp { - my $answer = shift || ''; - my %params = ( - debug => 0, - var => 'x', - limits => [$functLLimitDefault,$functULimitDefault], - @_ - ); - # - # Get the variable and limits - # - my $x = $params{var}; delete $params{var}; - my ($ll,$ul) = @{$params{limits}}; - $params{vars} = [$x,'H']; - $params{limits} = [[$ll,$ul],[$functLLimitDefault,$functULimitDefault]]; - # - # Convert dx to H - # - my $answer_h = $answer; $answer_h =~ s/d$x/H/g; - # - # The answer checker that tests for division by zero - # - my $zero = fun_cmp($answer_h,%params); - $zero->{rh_ans}{evaluation_points} = [[($ul+$ll)/2,0]]; - - my $ans = new AnswerEvaluator; - $ans->{debug} = $params{debug}; - $ans->ans_hash( - type => "difference quotient", - correct_ans => $answer, - cmp => fun_cmp($answer_h,%params), - zero_cmp => $zero, - var => $x, - ); - $ans->install_evaluator(\&diff_quotient_cmp_check,%params); - return $ans; -} - -# -# The guts of the checker -# -sub diff_quotient_cmp_check { - my $ans = shift; - my $x = $ans->{var}; - my $answer = $ans->{student_ans}; $answer =~ s/d$x/H/g; - - # - # Check the answer, and copy the score and messages - # - my $hash = evaluateAnswer($ans->{cmp},$answer); - foreach my $id ('score','student_ans','ans_message','error_message', - 'error_flags','preview_text_string','preview_latex_string') { - $ans->{$id} = $hash->{$id}; - $ans->{$id} =~ s/H/d$x/g if defined($ans->{$id}); - } - - # - # If the function matches, check that it is simplified - # (no division by zero when dx = 0). Otherwise, give an - # appropriate error message. - # - if ($hash->{score} == 1) { - $hash = evaluateAnswer($ans->{zero_cmp},$answer); - if ($hash->{ans_message} =~ m/division by zero/) { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = - "It looks like you didn't finish simplifying your answer\n"; - } - } - - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/ev3p.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/ev3p.pl deleted file mode 100755 index 0311a71969..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/ev3p.pl +++ /dev/null @@ -1,103 +0,0 @@ -sub _EV3P_init {} - -###################################################################### -# -# New version of EV3 that allows `...` and ``...`` to insert TeX produced -# by the new Parser (in math and display modes). -# -# Format: EV3P(string,...); -# EV3P({options},string,...); -# -# where options can include: -# -# processCommands => 0 or 1 Indicates if the student's answer will -# be allowed to process \{...\}. -# Default: 1 -# -# processVariables => 0 1 Indicates whether variable substitution -# should be performed on the student's -# answer. -# Default: 1 -# -# processMath => 0 or 1 Indicates whether \(...\), \[...\], -# `...` and ``...`` will be processed -# in the student's answer. -# Default: 1 -# -# processParser => 0 or 1 Indicates if `...` and ``...`` should -# be processed when math is being -# processed. -# Default: 1 -# -# fixDollars => 0 or 1 Specifies whether dollar signs not followed -# by a letter should be replaced by ${DOLLAR} -# prior to variable substitution (to prevent -# accidental substitution of strange Perl -# values). -# Default: 1 -# -sub EV3P { - my $option_ref = {}; $option_ref = shift if ref($_[0]) eq 'HASH'; - my %options = ( - processCommands => 1, - processVariables => 1, - processParser => 1, - processMath => 1, - fixDollars => 1, - %{$option_ref}, - ); - my $string = join(" ",@_); - $string = ev_substring($string,"\\\\{","\\\\}",\&safe_ev) if $options{processCommands}; - if ($options{processVariables}) { - my $eval_string = $string; - $eval_string =~ s/\$(?![a-z])/\${DOLLAR}/gi if $options{fixDollars}; - my ($evaluated_string,$PG_eval_errors,$PG_full_errors) = - PG_restricted_eval("</>/g; - $evaluated_string = $BBOLD."(Error: $error in '$string')".$EBOLD; - } - $string = $evaluated_string; - } - if ($options{processMath}) { - $string = EV3P_parser($string) if $options{processParser}; - $string = ev_substring($string,"\\(","\\)",\&math_ev3); - $string = ev_substring($string,"\\[","\\]",\&display_math_ev3); - } - return $string; -} - -# -# Look through a string for ``...`` or `...` and use -# the parser to produce TeX code for the specified mathematics. -# ``...`` does display math, `...` does in-line math. They -# can also be used within math mode already, in which case they -# use whatever mode is already in effect. -# -sub EV3P_parser { - my $string = shift; - return $string unless $string =~ m/`/; - my $start = ''; my %end = ('\('=>'\)','\['=>'\]'); - my @parts = split(/(``.*?``\*?|`.+?`\*?|(?:\\[()\[\]]))/s,$string); - foreach my $part (@parts) { - if ($part =~ m/^(``?)(.*)\1(\*?)$/s) { - my ($delim,$math,$star) = ($1,$2,$3); - my $f = Parser::Formula($math); - if (defined($f)) { - $f = $f->reduce if $star; - $part = $f->TeX; - $part = ($delim eq '`' ? '\('.$part.'\)': '\['.$part.'\]') if (!$start); - } else { - ## FIXME: use context->{error}{ref} to highlight error in $math. - $part = $BBOLD."(Error: $$Value::context->{error}{message} '$math')".$EBOLD; - $part = $end{$start}." ".$part." ".$start if $start; - } - } - elsif ($start) {$start = '' if $part eq $end{$start}} - elsif ($end{$part}) {$start = $part} - } - return join('',@parts); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/imageChoice.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/imageChoice.pl deleted file mode 100755 index 178f473e16..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/imageChoice.pl +++ /dev/null @@ -1,91 +0,0 @@ -loadMacros( - 'PGchoicemacros.pl', - 'unionUtils.pl', - 'choiceUtils.pl', -); - -sub _imageChoice_init {}; # don't reload this file - -###################################################################### -# -# Create a match list where the answers are images -# -# Usage: $ml = new_image_match_list(options); -# -# where options are those that can be supplied to image_print_a below. -# The answers should be an image name or reference to to a plot object -# (or a reference to a pair of either of these), and they are passed -# to the Image function for processing. See unionUtils.pl for more -# information on how these are handled. -# -sub new_image_match_list { - my $ml = new Match(random(1,2000,1), \&alt_print_q, \&img_print_a); - $ml->{ImageOptions} = [@_]; - $ml; -} - -###################################################################### -# -# A print routine for image matching. This is designed to display -# four graphs per row. More can be included by setting the ImageOptions -# variable in the match list. For example: -# -# $ml->{ImageOptions} = [size => [100,100]] -# -# You can include the following options: -# -# size => [w,h] the width and height of the images -# width => n the width of the images (obsolete) -# height => n the height of the images (obsolete) -# tex_size => n the size for the TeX version of the image -# separation => n the spacing between the images in a row -# vseparation => n the spacing between the images and captions -# link => 0 or 1 1 to make a link to the original image -# columns => n the number of images in each row (defaults to 4) -# border => n the width of the image border -# - -sub img_print_a { - my $self = shift; - $self->{ImageOptions} = [] unless defined($self->{ImageOptions}); - my %options = ( - width => 150, height => 150, tex_size => 200, columns => 4, - separation => 15, vseparation => 5, link => 0, @{$self->{ImageOptions}}); - my ($w,$h,$m) = ($options{width},$options{height},$options{columns}); - ($w,$h) = @{$options{size}} if defined($options{size}); - my ($sep,$vsep) = ($options{separation},$options{vseparation}); - my ($tsize,$link) = ($options{tex_size},$options{link}); - my $border = $options{border}; $border = ($link?2:1) unless defined($border); - my $HTML; my @images = (); my @labels = (); my $i = 0; - my $out; - - $out = BeginTable(tex_spacing => "5pt", tex_border => "5pt"); - while ($image = shift) { - push(@images,Image( - $image, size => [$w,$h], tex_size => $tsize, - link => $link, border => $border - )); - push(@labels,MODES( - TeX => "\\hfil \\textbf{$main::ALPHABET[$i]}", - Latex2HTML=>$bHTML."

$main::ALPHABET[$i]
".$eHTML, - HTML => "
$main::ALPHABET[$i]
" - )); - if ((++$i % $m) == 0) { - $out .= TableSpace(2*$vsep,6) if ($i > $m); - $out .= Row([@images], separation => $sep). - TableSpace($vsep,0). - Row([@labels], separation => $sep); - @images = (); @labels = (); - } - } - if (scalar(@images) > 0) { - $out .= TableSpace(2*$vsep) if ($i > $m && $displayMode ne "TeX"); - $out .= Row([@images], separation => $sep). - TableSpace($vsep,0). - Row([@labels], separation => $sep); - } - $out .= EndTable(tex_border => "5pt"); - $out; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/infiniteAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/infiniteAnswer.pl deleted file mode 100755 index 2625b8557e..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/infiniteAnswer.pl +++ /dev/null @@ -1,131 +0,0 @@ -loadMacros('unionUtils.pl','answerUtils.pl'); - -sub _infiniteAnswer_init { - - #$INFINITY_WORD = "INF"; - $INFINITY_WORD = "infinity" unless defined($INFINITY_WORD); - - # - # A message that can be inclued (within BEGIN_TEXT and END_TEXT) - # to tell the student how to enter infinity and minus infinity - # - $INFINITY_MESSAGE = - $BITALIC.$BSMALL. - "Use ${LQ}$INFINITY_WORD${RQ} for $LQ\\(\\infty\\)$RQ ". - "and ${LQ}-$INFINITY_WORD${RQ} for $LQ\\(-\\infty\\)$RQ." . - $ESMALL.$EITALIC; - - $DNE_MESSAGE = - $BITALIC.$BSMALL. - "Use ${LQ}DNE${RQ} for ${LQ}Does not exist${RQ}.". - $ESMALL.$EITALIC; - - $INFINITY_MESSAGE = "" if $displayMode eq 'TeX'; - $DNE_MESSAGE = "" if $displayMode eq 'TeX'; - -}; - -########################################################################## -# -# Check for a number or "-INF", "INF", "DNE" or several synonyms. -# -# WeBWorK's std_num_str_cmp doesn't allow for strings like "-INF", so it -# uses the unsatisfying "MINF" instead. This answer checker provides -# for both MINF and -INF to mean negative infinity, and INF or NaN for -# positive infinity, and similarly INFINITY or -INFINITY. It also -# allows for DNE as an answer. The strings can be entered in upper- -# or lower-case and still be recognized. -# -# Usage: infinite_num_cmp(number, options) -# -# where number is a number or one of the words for infinity, and options -# are chosen from -# -# allowDNE => 0 or 1 whether to accept DNE as a valid entry -# (default is 0) -# -# DNEisINF => 0 or 1 whether to treat DNE as INF or as a -# separate word (default is 0) -# -# -sub infinite_num_cmp { - my $infPattern = '^(INF|INFINITY|\+INF|\+INFINITY|NaN)$'; - my $neginfPattern = '^(-INF|-INFINITY|MINF)$'; - my $num = shift; - my %params = (allowDNE => 0, DNEisINF => 0, - infPattern => $infPattern, - neginfPattern => $neginfPattern, @_); - my %numops = @_; - delete($numops{allowDNE}) if (defined($numops{allowDNE})); - delete($numops{DNEisINF}) if (defined($numops{DNEisINF})); - $params{debug} = 0 unless defined($params{debug}); - $num = "-$INFINITY_WORD" if ($num =~ m/$neginfPattern/i); - $num = $INFINITY_WORD if ($num =~ m/$infPattern/i); - if ($params{allowDNE}) { - $params{strings} = ["INF","-INF","+INF","+INFINITY","INFINITY","-INFINITY","DNE"]; - $params{num_cmp} = num_cmp($num, - strings => ["INF","-INF","+INF","MINF","NaN","INFINITY","-INFINITY","+INFINITY","DNE"], - %numops); - } else { - $params{strings} = ["INF","-INF","+INF","INFINITY","-INFINITY","+INFINITY"]; - $params{num_cmp} = num_cmp($num, - strings => ["INF","-INF","+INF","MINF","NaN","INFINITY","-INFINITY","+INFINITY"], - %numops); - } - $params{num_cmp}->install_post_filter('reset'); # to prevent NUM_CMP from putting - # error messages in student_ans - $params{isString} = 0; - foreach my $string (@{$params{strings}}) { - if (uc($string) eq uc($num)) {$params{isString} = 1; last} - } - - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash(correct_ans => $num, type => "infinite number"); - $answerEvaluator->install_evaluator(\&infinite_num_check,%params); - return $answerEvaluator; -} - -# -# The guts of the checker -# -sub infinite_num_check { - my $ans = shift; - my %params = @_; - $ans->{student_ans} = trimString($ans->{student_ans}); - my $answer = $ans->{student_ans}; - my $realAnswer = $ans->{correct_ans}; - my $isString = 0; - $realAnswer = "DNE" - if (uc($answer) eq "DNE" && $params{DNEisINF} && - $realAnswer =~ m/$params{infPattern}/i); - $answer = "-$INFINITY_WORD" if ($answer =~ m/$params{neginfPattern}/i); - $answer = $INFINITY_WORD if ($answer =~ m/$params{infPattern}/i); - if ($params{isString} && uc($answer) eq uc($realAnswer)) { - $ans->score(1); $isString = 1 - } else { - foreach my $string (@{$params{strings}}) { - if (uc($answer) eq uc($string)) {$ans->score(0); $isString = 1} - } - } - if ($isString) { - $ans->{student_ans} = $answer; - $ans->{preview_text_string} = $answer; - $ans->{preview_latex_string} = $answer; - return $ans; - } - # - # transfer the NUM_CMP error message to the answer message - # so we can report it correctly in other checkers - # - my $hash = evaluateAnswer($params{num_cmp},$ans->{student_ans}); - $hash->{ans_message} = trimString($hash->{error_message}) - if ($hash->{ans_message} eq ''); - $hash->{ans_message} = - "Your answer does not seem to be a number or infinity" - if ($hash->{ans_message} =~ m/recognized answer/); - $hash->clear_error('EVAL'); # in case one was left over from std_num_cmp - return $hash; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/integerAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/integerAnswer.pl deleted file mode 100755 index 8148f01cc5..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/integerAnswer.pl +++ /dev/null @@ -1,52 +0,0 @@ -sub _integerAnswer_init { - $INTEGER_TOLERANCE = 1E-14; -}; - -###################################################################### -# -# Integer answer checker. -# -# integer_cmp(ans,options) -# -# where options are those allowed by num_cmp(). -# -# This provided an answer checker that compares against an integer. -# it is really just a shell around num_cmp() that sets the tolerance to -# an absolute tolerance of .5 (so that only the correct integer -# would be correct). It also adds a post-filter to check that -# the student answer actually IS an integer. -# -# Note, the checker is only good to about 12 digits. -# - -sub integer_cmp { - my $cmp = num_cmp(@_,tol => .5); - $cmp->install_post_filter(\&check_integer_answer); - my $x = $cmp->{rh_ans}{correct_ans}; - warn "Professor's answer is too large for integer comparison" - if (abs($x) > (1/$INTEGER_TOLERANCE)/100); - if (abs($x - int($x+$x*$INTEGER_TOLERANCE)) > $x*$INTEGER_TOLERANCE) { - warn "Professor's answer is not an integer"; - } else { - $cmp->{rh_ans}{original_correct_ans} = - $cmp->{rh_ans}{correct_ans} = sprintf("%.0f",$x); - } - return $cmp; -} - -sub check_integer_answer { - my $ans = shift; - if ($ans->{ans_message} eq "" && $ans->{error_message} eq "") { - my $x = $ans->{student_ans}; - if (abs($x - int($x+$x*$INTEGER_TOLERANCE)) > $x*$INTEGER_TOLERANCE) { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = - "Your answer is not an integer"; - } else { - $ans->{student_ans} = sprintf("%.0f",$x); - } - } - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/intervalAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/intervalAnswer.pl deleted file mode 100755 index 352b02a1a9..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/intervalAnswer.pl +++ /dev/null @@ -1,258 +0,0 @@ -loadMacros('infiniteAnswer.pl'); - -sub _intervalAnswer_init {}; # don't reload this file - -###################################################################### -## -## Interval answer checkers. -## -## num_interval_cmp() intervals with numeric or infinite endpoints -## fun_interval_cmp() intervals with functions as endpoints -## -## interval_cmp() provide your own endpoint checkers -## - -###################################################################### -# -# num_interval_cmp(answer,options) -# -# where options are from among: -# -# cmp_params => [list] parameters to be passed to infinite_num_cmp -# for each coordinate -# -# or any of the options to interval_cmp (see below). -# -# This provided an answer checker that compares against an interval -# with numeric or infinite endpoints. -# -# e.g. num_point_cmp("(1,10)"); -# num_point_cmp("(-INF,10)"); -# - -sub num_interval_cmp { - my $ans = shift; - std_interval_cmp($ans, cmp => \&infinite_num_cmp, @_); -} - - -###################################################################### -# -# num_interval_cmp(answer,options) -# -# where options are from among: -# -# cmp_params => [list] parameters to be passed to infinite_num_cmp -# for each coordinate -# -# vars => [list] the variables for the formulas -# -# or any of the options to interval_cmp (see below). -# -# This provided an answer checker that compares against an interval -# with numeric or infinite endpoints. -# -# e.g. fun_point_cmp("(x,2x)"); -# fun_point_cmp("(-t,t+2)", vars => 't'); -# - -sub fun_interval_cmp { - my $ans = shift; - my %params = (cmp_params => [], @_); - if (defined($params{vars})) { - $params{cmp_params} = [vars => $params{vars}, @{$params{cmp_params}}]; - delete $params{vars}; - } - std_interval_cmp($ans, cmp => \&fun_cmp, %params); -} - - -###################################################################### -# -# std_interval_cmp(ans,options) -# -# where ans is the correct answer as a string (e.g. "(1,INF)"), and -# options are from among the following: -# -# cmp => \&cmp a reference to the answer checker to use for each -# endpoint (e.g., \&std_num_cmp, or ~~&std_num_cmp -# in a .pg file) -# -# cmp_params => [...] is a reference to a list of parameters for the -# answer checker (e.g., [vars => 'x']) -# -# or any of the options allowed by interval_cmp (see below). -# -# This returns an answer checker that compares the interval using the -# given answer checker on each endpoint. -# -# e.g. std_interval_cmp("(1,INF)",cmp => ~~&infinite_num_cmp); -# - -sub std_interval_cmp { - my $ans = shift; - my %params = ( - cmp => \&infinite_num_cmp, - cmp_params => [], - debug => 0, - @_ - ); - my ($cmp,$cmp_params) = ($params{cmp},$params{cmp_params}); - delete $params{cmp}; delete $params{cmp_params}; - die "The correct answer doesn't look like an interval" - if ($ans !~ m/^(\[|\()([^,]*),([^,]*)(\]|\))$/); - my ($left,$a,$b,$right) = ($1,$2,$3,$4); - interval_cmp(&{$cmp}($a,@{$cmp_params}),&{$cmp}($b,@{$cmp_params}), - ends => "$left$right", %params); -} - - -########################################################################## -# -# Check if an answer is an interval -# -# Format: -# -# interval_cmp(left_evaluator,right_evaluator, options) -# -# where "left_evaluator" is an answer checker for the left endpoint -# and "right_evaluator" is an answer checker for the right endpoint, -# -# Options can be taken from: -# -# ends => string indicates the type of interval, where -# "string" is one of '()', '(]', '[)', or '[]'. -# -# showHints => 0 or 1 indicates whether to show hints about -# the correctness of the endpoints -# (default is 1). -# -# showOrderHints => 0 or 1 -# indicates whether to show hints about -# the order of the endpoints -# (default is $showHints). -# -# Example: -# -# interval_cmp(std_num_cmp(0),std_num_cmp(1), ends=>'[)'); -# -sub interval_cmp { - my $lendpnt = shift; - my $rendpnt = shift; - my %params = @_; - set_default_options(\%params, - ends => '()', - showHints => 1, - showOrderHints => undef, - debug => 0 - ); - $params{lendpnt} = $lendpnt; - $params{rendpnt} = $rendpnt; - - my $ends = $params{ends}; - my ($ltype,$rtype) = (substr($ends,0,1),substr($ends,-1)); - my ($lhash,$rhash) = (hashFor($lendpnt),hashFor($rendpnt)); - # - # Needed to get preview to work properly - # - my $type = "interval"; - $type .= " (number)" - if ($lhash->{type} =~ m/number/ || $rhash->{type} =~ m/number/); - - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => $ltype . $lhash->{correct_ans} . "," . - $rhash->{correct_ans} . $rtype, - type => $type - ); - $answerEvaluator->install_evaluator(\&interval_check,%params); - return $answerEvaluator; -} - -# -# The guts of the checker -# -sub interval_check { - my $ans = shift; - my %params = @_; - my ($lendpnt,$rendpnt) = ($params{lendpnt},$params{rendpnt}); - my $ends = $params{ends}; - - my $showHints = defined($params{showHints})? - $params{showHints} : $showPartialCorrectAnswers; - my $showOrderHints = defined($params{showOrderHints})? - $params{showOrderHints} : $showPartialCorrectAnswers; - $showHints = $showOrderHints = 0 if (isPreviewMode()); - - $ans->{student_ans} = trimString($ans->{student_ans}); - my $answer = $ans->{student_ans}; - my ($sl,$sa,$sb,$sr) = ($answer =~ m/^(\[|\()([^,]*),([^,]*)(\]|\))$/); - my @errors = (); my $score = 1; - - if (!defined($sl)) { - push(@errors,"Your answer doesn't look like an interval."); - } else { - my ($lhash,$rhash) = - (evaluateAnswer($lendpnt,$sa),evaluateAnswer($rendpnt,$sb)); - # - # (Second check is a hack since std_num_cmp with strings - # doesn't always set the error message when an error - # occurs. Grrr!) - # - if ($lhash->{ans_message} ne "" || - $lhash->{student_ans} =~ m/ Your answer/) { - push(@errors,"Error evaluating left endpoint: "); - push(@errors,IndentError($lhash->{student_ans})); - push(@errors,IndentError($lhash->{ans_message})); - } else { - $sa = $lhash->{student_ans}; - if ($lhash->{score} != 1) { - push(@errors,"The left endpoint is incorrect") if ($showHints); - $score = 0; - } - } - if ($rhash->{ans_message} ne "" || - $rhash->{student_ans} =~ m/ Your answer/) { - push(@errors,"Error evaluating right endpoint:"); - push(@errors,IndentError($rhash->{student_ans})); - push(@errors,IndentError($rhash->{ans_message})); - } else { - $sb = $rhash->{student_ans}; - if ($rhash->{score} != 1) { - push(@errors,"The right endpoint is incorrect") if ($showHints); - $score = 0; - } - } - $ans->setKeys(student_ans => $sl.$sa.",".$sb.$sr); - my ($la,$ra) = ($lhash->{student_ans},$rhash->{student_ans}); - push(@errors,"Note: The left endpoint is greater than ". - "the right endpoint (empty interval)") - if ($showOrderHints && isNumber($la) && isNumber($ra) && $la > $ra); - if ($sl.$sr ne $ends) { - push(@errors,"The type of interval is incorrect") if ($showHints); - $score = 0; - } - $ans->setKeys( - preview_text_string => - $sl . StringOrBlank($lhash->{preview_text_string},$sa) . "," . - StringOrBlank($rhash->{preview_text_string},$sb) . $sr, - preview_latex_string => - $sl . StringOrBlank($lhash->{preview_latex_string},$sa) . "," . - StringOrBlank($rhash->{preview_latex_string},$sb) . $sr - ); - clearEvaluator($lendpnt); - clearEvaluator($rendpnt); - } - - if (scalar(@errors) == 0) { - $ans->score($score); - $ans->{ans_message} = $ans->{error_message} = ''; - } else { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = join("\n",@errors); - } - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/lineAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/lineAnswer.pl deleted file mode 100755 index a17348e4d0..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/lineAnswer.pl +++ /dev/null @@ -1,73 +0,0 @@ -loadMacros('vectorAnswer.pl'); - -sub _lineAnswer_init {}; # don't reload this file - -###################################################################### -# -# Compare a parametric formula to a given line -# (allows any parallel vector, and any point on the line). -# -# Usage: parametric_line_cmp(ans,options) -# -# where ans is the answer in vector parametric form (e.g., "<1+3t,2-t>") -# or as a Vector object, and options are those that are allowed by -# point_cmp. The variable is assumed to be 't', but that can be changed -# using the cmp_params option. -# - -sub parametric_line_cmp { - my $answer = shift; - my $ans = fun_point_cmp($answer, - post_process => \&checkParametricLine, - showHints => 0, showLengthHints => 1, - cmp_params => [vars => 't'], - @_ - ); - foreach my $cmp (@{$ans->{rh_ans}->{components}}) { - # - # We evaluate the functions at t=0 and t=1 to get - # p = L(0) and v = L(1)-L(0). - # - $cmp->{rh_ans}->{evaluation_points} = [[0],[1]]; - } - return $ans; -} - -# -# The guts of the checker (called as a post-processor by -# the point checker). -# -sub checkParametricLine { - my $ans = shift; - my (@p,@cp,@v,@cv); - # - # Get the P and V for each line - # (we arranged above to evaluate at t=0 and t=1). - # - foreach my $cmp (@{$ans->{components}}) { - push(@cp,$cmp->{rh_ans}->{ra_instructor_values}->[0]); - push(@cv,$cmp->{rh_ans}->{ra_instructor_values}->[1]); - push(@p,$cmp->{rh_ans}->{ra_student_values}->[0]); - push(@v,$cmp->{rh_ans}->{ra_student_values}->[1]); - } - # - # Get the vector (v = L(1)-L(0)). - # - $v = vDiff(Point(@v),Point(@p)); $cv = vDiff(Point(@cv),Point(@cp)); - # - # The vectors must be parallel to be equal. - # - return 0 if (!areParallel($v,$cv)); - # - # If the points are equal, the lines are. - # - my $w = vDiff(Point(@cp),Point(@p)); - return 1 if (isCorrectAnswer(std_num_cmp(0),Norm($w))); - # - # Otherwise, if the vector between the two points is - # parallel to the direction vectors, the lines are equal. - # - return (areParallel($v,$w)); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/listAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/listAnswer.pl deleted file mode 100755 index cbaf9079b1..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/listAnswer.pl +++ /dev/null @@ -1,580 +0,0 @@ -sub _listAnswer_init {}; # don't reload this file - -###################################################################### -# -# This file implements a general list answer checker, which allows -# you to specify how the answer is to be split up, whether -# the entries must appear in the same order as the correct answer, -# whether informational messages are to be produced for incorrect -# entries, and what answer checker to call on each entry. -# -# The list_cmp() and std_list_cmp() functions are very general, and -# could be used to implement a variety of special cases of lists. -# A number of these are given below, including: -# -# num_list_cmp() a list of numeric values -# infinite_num_list_cmp() a list of numeric values or infinities -# fun_list_cmp() a list of formulas -# num_interval_list_cmp() a list of intervals with numeric endpoints -# fun_interval_list_cmp() a list of intervals with endpoint equations -# num_union_list_cmp() a list of unions of numeric intervals -# fun_union_list_cmp() a list of unions of variable intervals -# num_point_list_cmp() a list of numeric points -# fun_point_list_cmp() a list of formulaic points -# num_vector_list_cmp() a list of numeric vectors -# fun_vector_list_cmp() a list of formulaic vectors -# -# Some of these require that you load the .pl file for the -# associated answer checker as well. For example, to use -# num_point_list_cmp(), you must include -# -# loadMacros("pointAnswer.pl"); -# -# in your .pg file. -# -# Partial credit is given for getting some entries correct, unless -# $showParialCorrectAnswers is set to 0. A system level change to -# displayMacros.pl is required to make the system indicate this in -# a meaningful way. You can prevent partial credit by using the -# partialCredit => 0 option to any of the *_cmp() routines. -# - -###################################################################### - - -loadMacros( - "unionUtils.pl", - "answerUtils.pl", -); - -###################################################################### -# -# Usage: num_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "10, -3, 5") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_list_cmp {std_list_cmp(@_)} - - -###################################################################### -# -# Usage: infinite_num_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "10, -INF, 5") -# and options are any of those that can be passed to std_list_cmp. -# - -sub infinite_num_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&infinite_num_cmp, - type => "infinite number", @_); -} - - -###################################################################### -# -# Usage: fun_list_cmp(ans,options) -# -# where ans is the string indicating the correct list and options are -# any of those that can be passed to std_list_cmp. -# -# Options can also include: -# -# vars => [list] the variables for the function checker -# -# or any of the options allowed by list_cmp (see below). -# - -sub fun_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_cmp, - entry_type => 'equation', - fun_var_params(@_)); -} - -# -# Put the function's 'vars' option into the 'cmp_params' option. -# -sub fun_var_params { - my %params = (cmp_defaults => [], @_); - if (defined($params{vars})) { - $params{cmp_defaults} = [vars => $params{vars}, @{$params{cmp_defaults}}]; - delete $params{vars} - } - return %params; -} - - -###################################################################### -# -# Usage: num_interval_list_cmp(ans,options) -# fun_interval_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "[1,3), (-inf,-5)") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_interval_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_interval_cmp, - type => "number interval", - entry_type => 'interval', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_interval_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_interval_cmp, - type => "function interval", - entry_type => 'interval', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0], - @_); -} - - -###################################################################### -# -# Usage: num_union_list_cmp(ans,options) -# fun_union_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., -# "[1,3) U (-inf,-5), [10,11]") and options are any of those that can -# be passed to std_list_cmp. -# - -sub num_union_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_union_cmp, - type => "number union", - entry_type => 'interval or union', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0, showLengthHints => 0], - @_); -} - -sub fun_union_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_union_cmp, - type => "fun union", - entry_type => 'interval or union', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - fun_var_params( - cmp_defaults => [showHints => 0, showLengthHints => 0], - @_ - )); -} - - -###################################################################### -# -# Usage: num_point_list_cmp(ans,options) -# fun_point_list_cmp(ans,options) -# num_vector_list_cmp(ans,options) -# fun_vector_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "(1,2,3), (3,-2,4)") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_point_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_point_cmp, - type => "number point", - entry_type => 'point', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => undef, - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_point_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_point_cmp, - type => "function point", - entry_type => 'point', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => undef, - fun_var_params(cmp_defaults => [showHints => 0],@_)); -} - -sub num_vector_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_vector_cmp, - type => "number vector", - entry_type => 'vector', - separator => ' , ', - split => \&paren_split, - split_defaults => [ - separator => ',', - open => '[(<', - close => '])>', - ], - cmp_ans => undef, - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_vector_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_vector_cmp, - type => "function vector", - entry_type => 'vector', - separator => ' , ', - split => \&paren_split, - split_defaults => [ - separator => ',', - open => '[(<', - close => '])>', - ], - cmp_ans => undef, - fun_var_params(cmp_defaults => [showHints => 0],@_)); -} - - -###################################################################### -# -# std_list_cmp(ans,options) -# -# where ans is the correct answer as a string (e.g. "10, -5"), and -# options are from among the following: -# -# cmp => \&cmp a reference to the answer checker to use for each -# list element (eg, \&std_num_cmp or \&fun_cmp) -# -# cmp_params => [...] a reference to a list of parameters for the -# answer checker (e.g., cmp_params => [vars => 'x']) -# -# or any of the options allowed by list_cmp (see below). -# -# This returns an answer checker that compares the list using the -# given answer checker on each element in the list. -# -# e.g. std_list_cmp("10, -5",cmp => ~~&std_num_cmp); -# - -sub std_list_cmp { - my $ans = shift; - my %params = ( - cmp => \&std_num_cmp, - cmp_ans => "0", - cmp_params => [], - cmp_defaults => [], - split => \&std_split, - split_params => [], - split_defaults => [separator => ','], - debug => 0, - @_ - ); - my $cmp = $params{cmp}; - my @cmp_params = (@{$params{cmp_defaults}},@{$params{cmp_params}}); - my $split = $params{split}; - my @split_params = (@{$params{split_defaults}},@{$params{split_params}}); - my $ans_ref = &{$split}($ans,@split_params); - warn "Error in professor's answer: $ans_ref" if (ref($ans_ref) ne "ARRAY"); - my @list = (); - foreach my $element (@{$ans_ref}) { - $element = trimString($element); - push(@list,&{$cmp}($element,@cmp_params)); - } - $params{cmp_ans} = hashFor($list[0])->{correct_ans} - if (!defined($params{cmp_ans}) && scalar(@list)); - list_cmp([@list],%params); -} - - -###################################################################### -# -# Usage: list_cmp([list],options) -# -# where list is the array of checkers for the elements in the list -# and options are among: -# -# showHints => 0 or 1 indicates whether to show messages about -# which elements are correct. -# -# showLengthHints => 0 or 1 -# indicates whether to show messages about -# having too many entries in the list -# -# ordered => 0 or 1 specifies if the order of the student's -# answers must match thos of the professor. -# If 0, they can be in any order, if 1, they -# must be in the same order. Defaut: 0. -# -# partialCredit => 0 or 1 indicates if scores other than 0 and 1 are -# to be used (to provide for partial credit -# when some of the answers are correct). -# -# cmp => \&cmp reference to a comparison -# for doing syntax checking on the -# elements given by the student. -# -# cmp_params => [...] parameters to pass to the answer checker -# for syntax checking. -# -# cmp_defaults => [...] default parameters for the syntax answer check -# -# cmp_ans => "..." a string to use for the answer when checking -# for syntax errors in the entries that aren't -# correct. -# -# split => \&split a reference to a routine to split the -# answer string into elements (by default, it -# uses the standard perl split() funciton -# splitting at commas) -# -# split_params => [...] a reference to a list of parameters for the -# split routine (e.g., -# split_params => [separator => ',']) -# -# split_defaults => [...] a reference to a list of defaults for the -# split_params list above. -# -# separator => ', ' the formatted specifier for use when -# creating the string to display (it can -# contain spaces, whereas the separator -# in split_defaults or split_params -# should not). Note that split_defaults -# usually includes the separator used -# for the actual splitting. -# - -sub list_cmp { - my $elements = shift; - $elements = [$elements] unless ref($elements) eq 'ARRAY'; - my %params = @_; - set_default_options(\%params, - cmp => \&std_num_cmp, - cmp_params => [], - cmp_defaults => [], - cmp_ans => "0", - split => \&std_split, - split_params => [], - split_defaults => [separator => ','], - separator => ', ', - debug => 0, - type => "number", - entry_type => "number", - list_type => "list", - showHints => undef, - showLengthHints => undef, - ordered => undef, - ); - my $type = "list ($params{type})"; - my @list = (); - foreach my $cmp (@{$elements}) {push(@list,hashFor($cmp)->{correct_ans})} - - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => join($params{separator},@list), - elements => $elements, - type => $type, - ); - $answerEvaluator->install_evaluator(\&list_check,%params); - return $answerEvaluator; -} - -# -# The guts of the list checker. -# -sub list_check { - my $ans = shift; - my %params = @_; - my $value = $params{entry_type}; my $ltype = $params{list_type}; - my $cmp = $params{cmp}; my $cmp_ans = $params{cmp_ans}; - my @cmp_params = (@{$params{cmp_defaults}},@{$params{cmp_params}}); - my $split = $params{split}; - my @split_params = (@{$params{split_defaults}},@{$params{split_params}}); - my @cmp = @{$ans->{elements}}; - my (@errors,@list,@text,@latex); - my $ordered = $params{ordered}; - my $showHints = defined($params{showHints})? - $params{showHints} : $showPartialCorrectAnswers; - my $showLengthHints = defined($params{showLengthHints})? - $params{showLengthHints} : $showPartialCorrectAnswers; - my $partialCredit = defined($params{partialCredit})? - $params{partialCredit} : $showPartialCorrectAnswers; - $showHints = $showLengthHints = 0 if (isPreviewMode()); - - my $list_ref = &{$split}($ans->{student_ans},@split_params); - if (ref($list_ref) ne "ARRAY") { - $ans->score(0); $ans->{error} = 1; - $ans->{error_message} = $ans->{ans_message} = $list_ref; - return $ans; - } - - my $maxscore = scalar(@cmp); - my $m = scalar(@{$list_ref}); - $maxscore = $m if ($m > $maxscore); - my $i = 0; my $score = 0; my $hash; - my $indent = ($m > 1)? 4: 0; - - ELEMENT: foreach my $element (@{$list_ref}) { - $element = trimString($element); $i++; - if ($element eq '') { - push(@errors,"Your ".NameForNumber($i)." $value is blank"); - push(@list,''); push(@text,''); push(@latex,''); - next ELEMENT; - } - if ($ordered) { - $hash = evaluateAnswer(shift(@cmp),$element); - if ($hash && $hash->{score} == 1) { - push(@list,$hash->{student_ans}); - push(@text,StringOrBlank($hash->{preview_text_string})); - push(@latex,StringOrBlank($hash->{preview_latex_string})); - $score++; next ELEMENT; - } - } else { - foreach my $k (0..$#cmp) { - $hash = evaluateAnswer($cmp[$k],$element); - if ($hash && $hash->{score} == 1) { - splice(@cmp,$k,1); - push(@list,$hash->{student_ans}); - push(@text,StringOrBlank($hash->{preview_text_string})); - push(@latex,StringOrBlank($hash->{preview_latex_string})); - $score++; next ELEMENT; - } - } - } - $hash = evaluateAnswer(&{$cmp}($cmp_ans,@cmp_params),$element) - if (!$ordered || !$hash); - if ($hash->{ans_message} ne '') { - push(@errors,"Error evaluating the ".NameForNumber($i)." $value:") - if ($m > 1); - push(@errors,IndentError($hash->{student_ans},$indent)) - if ($m > 1 || $hash->{student_ans} =~ m/error/i); - push(@errors,IndentError($hash->{ans_message},$indent)); - push(@list,$element); - } else { - push(@list,$hash->{student_ans}); - push(@errors,"Your ".NameForNumber($i)." $value is incorrect") - if $showHints && $m > 1; - } - push(@text,StringOrBlank($hash->{preview_text_string})); - push(@latex,StringOrBlank($hash->{preview_latex_string})); - } - - if ($showLengthHints) { - $value =~ s/ or /s or /; # fix "interval or union" - push(@errors,"There should be more ${value}s in your $ltype") - if ($score == $m && scalar(@cmp) > 0); - push(@errors,"There should be fewer ${value}s in your $ltype") - if ($score < $maxscore && $score == scalar(@{$ans->{elements}})); - } - - $ans->{student_ans} = join($params{separator},@list); - $ans->{preview_text_string} = join($params{separator},@text); - $ans->{preview_latex_string} = join($params{separator},@latex); - - $score = 0 if ($score != $maxscore && !$partialCredit); - $ans->score($score/$maxscore); - $ans->{error_message} = $ans->{ans_message} = join("\n",@errors); - - return $ans; -} - - -###################################################################### -# -# Do a split at a specific character -# (routine should return a reference to the split list of -# answers, or a string that is an error message indicating -# a syntax error in the string). -# - -sub std_split { - my $s = shift; - my %params = (separator => ',', @_); - return [split($params{separator},$s)]; -} - - -###################################################################### -# -# Split at commas between intervals or points. -# (Try to match parentheses, and only split when not within an -# open set of parentheses). -# -# Paremeters can include: -# -# separator => ',' the character to split at (you can use -# a regexp like '[,;]' to allow several -# characters). Default: ',' -# -# open => '...' the characters that count as open -# parentheses. Default: '[(' -# -# close => '...' the characters that count as close -# parentheses. Default: '])' -# -# spaceSeparates => 0 or 1 -# indicates whether a space can be used to -# separate entries in the list (when the regular -# separator is not already there). This will -# only occur at spaces between unnested close -# and open parens (with nothing but spaces in -# between). Default: 1 -# - -sub paren_split { - my $s = shift; - my %params = @_; - set_default_options(\%params, - separator => ',', - open => '[(', - close => '])', - spaceSeparates => 1, - ); - my $separator = $params{separator}; - my ($open,$close) = ($params{open},$params{close}); - $open =~ s/\]/\\\]/g; $close =~ s/\]/\\\]/g; - my $spaceSeparates = $params{spaceSeparates}; - my $parens = 0; my $c; - my @list; my $element; my $space = 0; - - foreach $c (split('',$s)) { - if ($c =~ m/[$open]/) { - if ($spaceSeparates && $space == 2) {push(@list,$element); $element = ''} - $parens++; $space = 0; - } - elsif ($c =~ m/[$close]/) { - $parens-- if $parens; - $space = ($parens == 0); - } - elsif ($c =~ m/$separator/) { - $space = 0; - if ($parens == 0) {push(@list,$element); $element = ''; next} - } - elsif ($c eq ' ' && $space) {$space = 2} - elsif ($c ne ' ') {$space = 0} - $element .= $c; - } - push (@list,$element); - return [@list]; -} - -###################################################################### - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/parallelAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/parallelAnswer.pl deleted file mode 100755 index 704864c253..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/parallelAnswer.pl +++ /dev/null @@ -1,47 +0,0 @@ -loadMacros("vectorAnswer.pl"); - -sub _parallelAnswer_init {}; # don't reload this file - -###################################################################### -# -# Check if the student's vector is parallel to the answer vector. -# -# Usage: parallel_vector_cmp(ans,options) -# -# Where ans is the vector to compare against (e.g., "<1,2,3>" or a -# Vector object), and options are any of the options allowed -# by point_cmp. The vector must be a constant, not a formula. -# One additional option is allowed: -# -# same_direction => 0 or 1 indicates if the two vectors must -# also point in the same (not -# opposite) direction. Default: 0. -# - -sub parallel_vector_cmp { - my $answer = shift; - my %params = (same_direction => 0, @_); - my $sameDirection = $params{same_direction}; - delete $params{same_direction}; - my $cmp = num_point_cmp($answer, - post_process => \&checkParallel, - showHints => 0, - %params - ); - $cmp->{rh_ans}{same_direction} = $sameDirection; - return $cmp; -} - -# -# The parallel check, as a post-processor to point_cmp -# -sub checkParallel { - my $ans = shift; - areParallel( - Point(@{$ans->{correct_point}}), - Point(@{$ans->{answer_point}}), - $ans->{same_direction} - ); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/parserImplicitEquation.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/parserImplicitEquation.pl deleted file mode 100755 index 69a59da3ff..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/parserImplicitEquation.pl +++ /dev/null @@ -1,340 +0,0 @@ -loadMacros("MathObjects.pl"); - -sub _parserImplicitEquation_init {ImplicitEquation::Init()}; # don't reload this file - -=head1 DESCRIPTION - - ###################################################################### - # - # This is a MathObject class that implements an answer checker for - # implicitly defined equations. The checker looks for the zeros of - # the equation and tests that the student and professor equations - # both have the same solutions. - # - # This type of check is very subtle, and there are important issues - # that you may have to take into account. The solutions to the - # equations are found numerically, and so they will not be exact; - # that means that there are tolerances that may need to be adjusted - # for your particular equation. Also, it is always possible for the - # student to represent the function in a form that will exceed those - # tolerances, and so be marked as incorrect. The answer checker - # attempts to set the parameters based on the values of the functions - # involved, but this may not work perfectly. - # - # The method used to locate the solutions of A=B is by finding zeros - # of A-B, and it requires this function to take on both positive and - # negative values (that is, it can only find transverse intersections - # of the surface A-B=0 and the plane at height 0). For example, even - # though the solutions of (x^2+y^1-1)^2=0 form a circle, the - # algorithm will fail to find any solutions for this equation. - # - # In order to locate the zeros, you may need to change the limits so - # that they include regions where the function is both positive and - # negative (see below). The algorithm will avoid discontinuities, so - # you can specify things like x-y=1/(x+y) rather than x^2-y^2=1. - # - # Because the solutions are found using a random search, it is - # possible the randomly chosen starting points don't locate enough - # zeros for the function, in which case the check will fail. This - # can happen for both the professor's function and the student's, - # since zeros are found for both. This means that a correct answer - # can sometimes be marked incorrect depending on the random points - # chosen initially. These points also affect the values selected for - # the tolerances used to determine when a function's value is zero, - # and so can affect whether the student's function is marked as - # correct or not. - # - # If an equation has several components or branches, it is possible - # that the random location of solutions will not find zeros on some - # of the branches, and so might incorrectly mark as correct an - # equation that only is zero on one of the components. For example, - # x^2-y^2=0 has solutions along the lines y=x and y=-x, so it is - # possible that x-y=0 or x+y=0 will be marked as correct if the - # random points are unluckily chosen. One way to reduce this problem - # is to increase the number of solutions that are required (by - # setting the ImplicitPoints flag in the Context). Another is to - # specify the solutions yourself, so that you are sure there are - # points on each component. - # - # These problems should be rare, and the values for the various - # parameters have been set in an attempt to minimize the possibility - # of these errors, but they can occur, and you should be aware of - # them, and their possible solutions. - # - # - # Usage examples: - # - # Context("ImplicitEquation"); - # $f = ImplicitEquation("x^2 = cos(y)"); - # $f = ImplicitEquation("x^2 - 2y^2 = 5",limits=>[[-3,3],[-2,2]]); - # $f = ImplicitEquation("x=1/y",tolerance=>.0001); - # - # Then use - # - # ANS($f->cmp); - # - # to get the answer checker for $f. - # - # There are a number of Context flags that control the answer checker. - # These include: - # - # ImplicitPoints => 7 (the number of solutions to test) - # ImplicitTolerance => 1E-6 (relative tolerance value for when - # the tested function is zero) - # ImplicitAbsoluteMinTolerance => 1E-3 (the minimum tolerance allowed) - # ImplicitAbsoluteMaxTolerance => 1E-3 (the maximum tolerance allowed) - # ImplicitPointTolerance => 1E-9 (relative tolerance for how close - # the solution point must be to an - # actual solution) - # BisectionTolerance => .01 (extra factor used for the tolerance - # when finding the solutions) - # BisectionCutoff => 40 (maximum number of bisections to - # perform when looking for a solution) - # - # You may set any of these using Context()->flags->set(...). - # - # In addition to the Context flags, you can set some values within - # the ImplicitEquation itself: - # - # tolerance (the absolute tolerance for zeros of the function) - # bisect_tolerance (the tolerance used when searching for zeros) - # point_tolerance (the absolute tolerance for how close to an - # actual solution the located solution must be) - # limits (the domain to use for the function; see the - # documentation for the Formula object) - # solutions (a reference to an array of references to arrays - # that contain the coordinates of the points - # that are the solutions of the equation) - # - # These can be set in the in the ImplicitEquation() call that creates - # the object, as in the examples below: - # - # For example: - # - # $f = ImplicitEquation("x^2-y^2=0", - # solutions => [[0,0],[1,1],[-1,1],[-1,-1],[1,-1]], - # tolerance => .001 - # ); - # - # - # $f = ImplicitEquation("xy=5",limits=>[-3,3]); - # - # The limits value can be set globally within the Context, if you wish, - # and the others can be controlled by the Context flags discussed - # above. - # - ###################################################################### - -=cut - -# -# Create the ImplicitEquation package -# -package ImplicitEquation; -our @ISA = qw(Value::Formula); - -sub Init { - my $context = $main::context{ImplicitEquation} = Parser::Context->getCopy("Numeric"); - $context->{name} = "ImplicitEquation"; - $context->variables->are(x=>'Real',y=>'Real'); - $context{precedence}{ImplicitEquation} = $context->{precedence}{special}; - Parser::BOP::equality->Allow($context); - $context->flags->set( - ImplicitPoints => 10, - ImplicitTolerance => 1E-6, - ImplicitAbsoluteMinTolerance => 1E-3, - ImplicitAbsoluteMaxTolerance => 1, - ImplicitPointTolerance => 1E-9, - BisectionTolerance => .01, - BisectionCutoff => 40, - ); - - main::Context("ImplicitEquation"); ### FIXEME: probably should require author to set this explicitly - - main::PG_restricted_eval('sub ImplicitEquation {ImplicitEquation->new(@_)}'); -} - -sub new { - my $self = shift; my $class = ref($self) || $self; - my $context = (Value::isContext($_[0]) ? shift : $self->context); - my $f = shift; return $f if ref($f) eq $class; - $f = main::Formula($f); - Value::Error("Your formula doesn't look like an implicit equation") - unless $f->type eq 'Equality'; - my $F = ($context->Package("Formula")->new($context,$f->{tree}{lop}) - - $context->Package("Formula")->new($context,$f->{tree}{rop}))->reduce; - $F = $context->Package("Formula")->new($F) unless Value::isFormula($F); - Value::Error("Your equation must be real-valued") unless $F->isRealNumber; - Value::Error("Your equation should not be constant") if $F->isConstant; - Value::Error("Your equation can not contain adaptive parameters") - if ($F->usesOneOf($context->variables->parameters)); - $F = bless $F, $class; - my %options = (@_); # user can supply limits, tolerance, etc. - foreach my $id (keys %options) {$F->{$id} = $options{$id}} - $F->{F} = $f; $F->{isValue} = $F->{isFormula} = 1; - $F->createPoints unless $F->{solutions}; - return $F; -} - -# -# Override the comparison method. -# -# Turn the right-hand equation into an ImplicitEquation. This creates -# the test points (i.e., finds the solution points). Then check -# the professor's function on the student's test points and the -# student's function on the professor's test points. -# - -sub compare { - my ($l,$r) = @_; my $self = $l; my $tolerance; - my @params; @params = (limits=>$l->{limits}) if $l->{limits}; - $r = ImplicitEquation->new($r,@params); - Value::Error("Functions from different contexts can't be compared") - unless $l->{context} == $r->{context}; - - # - # They are not equal if couldn't get solutions for one of them - # - return 1 unless $l->{solutions} && $r->{solutions}; - - # - # Test the right-hand function on the solutions of the left-hand one - # and vice-versa - # - my $rzeros = $r->createPointValues($l->{solutions}); - my $lzeros = $l->createPointValues($r->{solutions}); - return 1 unless $lzeros && $rzeros; - - # - # Check that the values are, in fact, zeros - # - $tolerance = $r->getFlag('tolerance',1E-3); - foreach my $v (@{$rzeros}) {return 1 unless abs($v) < $tolerance} - $tolerance = $l->getFlag('tolerance',1E-3); - foreach my $v (@{$lzeros}) {return 1 unless abs($v) < $tolerance} - - return 0; # equal -} - -# -# Use the original equation for these (but not for perl(), since we -# need that to use perlFunction). -# -sub string {shift->{F}->string} -sub TeX {shift->{F}->TeX} - -sub cmp_class {'an Implicit Equation'} -sub showClass {shift->cmp_class} - -# -# Locate points that satisfy the equation -# -sub createPoints { - my $self = shift; - my $num_points = int($self->getFlag('ImplicitPoints',10)); - $num_points = 1 if $num_points < 1; - - # - # Get some positive and negative test points (try up to 5 times) - # - my ($OK,$p,$n,$z,$f,@zero); my $k = 5; - while (!$OK && --$k) {($OK,$p,$n,$z,$f) = $self->getPositiveNegativeZero($num_points)} - Value::Error("Can't find any solutions to your equation") unless $OK; - my ($P,@intervals) = $self->getIntervals($p,$n); - - # - # Get relative tolerance values and make them absolute - # - my $minTolerance = $self->getFlag('ImplicitAbsoluteMinTolerance'); - my $maxTolerance = $self->getFlag('ImplicitAbsoluteMaxTolerance'); - my $tolerance = $f * $self->getFlag('ImplicitTolerance'); - $tolerance = $minTolerance if $tolerance < $minTolerance; - $tolerance = $maxTolerance if $tolerance > $maxTolerance; - $self->{tolerance} = $tolerance unless $self->{tolerance}; - $self->{bisect_tolerance} = $self->{tolerance} * $self->getFlag('BisectionTolerance') - unless $self->{bisect_tolerance}; - $self->{point_tolerance} = $P * $self->getFlag('ImplicitPointTolerance') - unless $self->{point_tolerance}; - - # - # Locate solutions to be used for comparison test - # - @zero = @{$z}; @zero = $zero[0..$num_points-1] if (scalar(@zero) > $num_points); - for ($i = 0; scalar(@zero) < $num_points && $i < scalar(@intervals); $i++) { - my $Q = $self->Bisect($intervals[$i][0],$intervals[$i][1]); - push(@zero,$Q) if $Q; - } - Value::Error("Can't find enough solutions for an effective test") - unless scalar(@zero) == $num_points; - - # - # Save the solutions to the equation - # - $self->{solutions} = [@zero]; -} - -# -# Get random points and sort them by sign of the function. -# Also, get the average function value, and indicate if -# we actually did find both positive and negative values. -# -sub getPositiveNegativeZero { - my $self = shift; my $n = shift; - my ($p,$v) = $self->SUPER::createRandomPoints(3*$n); - my (@pos,@neg,@zero); - my $f = 0; my $k = 0; - foreach my $i (0..scalar(@{$v})-1) { - if ($v->[$i] == 0) {push(@zero,$p->[$i])} else { - $f += abs($v->[$i]); $k++; - if ($v->[$i] > 0) {push(@pos,$p->[$i])} else {push(@neg,$p->[$i])} - } - } - $f /= $k if $k; - return (scalar(@pos) && scalar(@neg),[@pos],[@neg],[@zero],$f); -} - -# -# Make a list of positive and negative points sorted by -# the distance between them. Also return the average distance -# between points. -# -sub getIntervals { - my $self = shift; my $pos = shift; my $neg = shift; - my @intervals = (); my $D = 0; - my $point = $self->Package("Point"); - my $context = $self->context; - foreach my $p (@{$pos}) { - foreach my $n (@{$neg}) { - my $d = abs($point->make($context,@{$p}) - $point->make($context,@{$n})); - push(@intervals,[$p,$n,$d]); $D += $d; - } - } - @intervals = main::PGsort(sub {$_[0]->[2] < $_[1]->[2]},@intervals); - return($D/scalar(@intervals),@intervals); -} - -# -# Use bisection algorithm to find a point where the function is zero -# (i.e., where the original equation is an equality) -# If we can't find a point (e.g., we are at a discontinuity), -# return an undefined value. -# -sub Bisect { - my $self = shift; - my $tolerance = $self->getFlag('bisect_tolerance',1E-5); - my $ptolerance = $self->getFlag('point_tolerance',1E-9); - my $m = $self->getFlag('BisectionCutoff',30); my ($P,$f); - my $point = $self->Package("Point"); my $context = $self->context; - my $P0 = $point->make($context,@{$_[0]}); my $P1 = $point->make($context,@{$_[1]}); - my ($f0,$f1) = @{$self->createPointValues([$P0->data,$P1->data],1)}; - for (my $i = 0; $i < $m; $i++) { - $P = ($P0+$P1)/2; $f = $self->createPointValues([$P->data]); - return unless ref($f); - $f = $f->[0]; - return [$P->value] if abs($f) < $tolerance && abs($P1-$P0) < $ptolerance; - if ($f > 0) {$P0 = $P; $f0 = $f} else {$P1 = $P; $f1 = $f} - } - return; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/parserTables.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/parserTables.pl deleted file mode 100755 index bd382392f6..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/parserTables.pl +++ /dev/null @@ -1,87 +0,0 @@ -loadMacros("parserUtils.pl"); - -############################################# -# -# For Parser example tables: -# - -$BTT = MODES(TeX=>'{\tt ', Latex2HTML => $bHTML.''.$eHTML, HTML => ''); -$ETT = MODES(TeX=>'}', Latex2HTML => $bHTML.''.$eHTML, HTML => ''); - -$BC = MODES( - TeX=>'{\small\it ', - Latex2HTML => $bHTML.''.$eHTML, - HTML => '' -); -$EC = MODES( - TeX=>'}', - Latex2HTML => $bHTML.''.$eHTML, - HTML => '' -); - -$LT = MODES(TeX => "<", Latex2HTML => "<", HTML => '<'); -$GT = MODES(TeX => ">", Latex2HTML => ">", HTML => '>'); - -$TEX = MODES(TeX => '{\TeX}', HTML => 'TeX', HTML_dpng => '\(\bf\TeX\)'); - -@rowOptions = ( - indent => 0, - separation => 0, - align => 'LEFT" NOWRAP="1', # alignment hack to get NOWRAP -); - -sub ParserRow { - my $f = shift; my $t = ''; - Context()->clearError; - my ($s,$err) = PG_restricted_eval($f); - if (defined $s) { - my $ss = $s; - if (ref($s) && \&{$s->string}) { - $t = '\('.$s->TeX.'\)'; - $s = $s->string; - } elsif ($s !~ m/^[a-z]+$/i) { - $t = '\('.Formula($s)->TeX.'\)'; - $s = Formula($s)->string; - } - $s =~ s//$GT/g; - if (ref($ss) && \&{$ss->class}) { - if ($ss->class eq 'Formula') { - $s .= ' '.$BC.'(Formula returning '.$ss->showType.')'.$EC; - } else { - $s .= ' '.$BC.'('.$ss->class.' object)'.$EC; - } - } - } else { - $s = $BC. (Context()->{error}{message} || $err) . $EC; - $t = ''; - } - $f =~ s//$GT/g; - if ($displayMode eq 'TeX') { - $f =~ s/\^/\\char`\\^/g; $s =~ s/\^/\\char`\\^/g; - $f =~ s/#/\\#/g; $s =~ s/#/\\#/g; - } - my $row = Row([$BTT.$f.$ETT,$BTT.$s.$ETT,$t],@rowOptions); - $row =~ s/\$/\${DOLLAR}/g; - return $row; -} - -sub ParserTable { - my $table = - BeginTable(border=>1, padding=>20). - Row([$BBOLD."Perl Code".$EBOLD, - $BBOLD."Result".$EBOLD, - $BBOLD.$TEX.' version'.$EBOLD],@rowOptions); - foreach my $f (@_) {$table .= ParserRow($f)} - $table .= EndTable(); - return $table; -} - -sub Title { - my $title = shift; - - MODES( - TeX => "\\par\\centerline{\\bf $title}\\par\\nobreak\n", - Latex2HTML => $bHTML.'

'.$title.'

'.$eHTML, - HTML => '

'.$title.'

' - ); -} diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/parserUtils.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/parserUtils.pl deleted file mode 100755 index 4db7dd5c4d..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/parserUtils.pl +++ /dev/null @@ -1,61 +0,0 @@ -loadMacros( - "unionImage.pl", - "unionTables.pl", -); - -$bHTML = '\begin{rawhtml}'; -$eHTML = '\end{rawhtml}'; - -# HTML(htmlcode) -# HTML(htmlcode,texcode) -# -# Insert $html in HTML mode or \begin{rawhtml}$html\end{rawhtml} in -# Latex2HTML mode. In TeX mode, insert nothing for the first form, and -# $tex for the second form. -# -sub HTML { - my ($html,$tex) = @_; - return('') unless (defined($html) && $html ne ''); - $tex = '' unless (defined($tex)); - MODES(TeX => $tex, Latex2HTML => $bHTML.$html.$eHTML, HTML => $html); -} - -# -# Begin and end mode -# -$BTT = HTML('','\texttt{'); -$ETT = HTML('','}'); - -# -# Begin and end mode -# -$BSMALL = HTML('','{\small '); -$ESMALL = HTML('','}'); - -# -# Block quotes -# -$BBLOCKQUOTE = HTML('
','\hskip3em '); -$EBLOCKQUOTE = HTML('
'); - -# -# Smart-quotes in TeX mode, regular quotes in HTML mode -# -$LQ = MODES(TeX => '``', Latex2HTML => '"', HTML => '"'); -$RQ = MODES(TeX => "''", Latex2HTML => '"', HTML => '"'); - -# -# make sure all characters are displayed -# -sub protectHTML { - my $string = shift; - $string =~ s/&/\&/g; - $string =~ s//\>/g; - $string; -} - -sub _parserUtils_init {} - -1; - diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/piecewiseFunctions.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/piecewiseFunctions.pl deleted file mode 100755 index 3a903640d0..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/piecewiseFunctions.pl +++ /dev/null @@ -1,97 +0,0 @@ -#! /usr/local/bin/perl - -sub _piecewiseFunctions_init {}; # don't reload this file - -# -# Implements a method of producing piecewise-defined functions that -# works in all modes (TeX, Latex2HTML and HTML). -# - -################################################## -# -# piecewiseFunction(name, [part1, part1-x, ...], options) -# -# Here, "name" is a string like "f(x)" that is the name of the -# function, -# -# "part1" is the function's definition for piece one, e.g. "x+4". -# (It is put in math mode automatically.) -# -# "part1-x" is the function's domain description, e.g., "if \(x $LTS 3\)". -# (It is NOT in math mode automatically.) -# -# You can supply as many partN,partN-x pairs as needed. -# -# The options are: -# -# bracesize => n specifies height of braces for HTML modes -# spacing => n gives row spacing for HTML -# -sub piecewiseFunction { - my $f = shift; - my $arrayref = shift; my @array = @{$arrayref}; - my %options = @_; - set_default_options(\%options, bracesize => 0, spacing => 5); - my ($size,$sep) = ($options{bracesize},$options{spacing}); - my ($output,$fx,$ifx) = ('','',''); - - if ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth') { - # - # A hack to handle piecewise functions in HTML - # - $output = - '
-
- ' . - "\n\n". - ' - \n
' . "\\($f\\) = " . - '$braceHTML[$size]'."\n"; - while (@array) { - $fx = shift(@array); $ifx = shift(@array); - $output .= ''."\n".'\n"; - $output .= '' if (@array); - } - $output .= "
' . DMATH($fx) . - ' ' . $ifx . "
\n"; - } elsif ($displayMode =~ m/^HTML/ || - $displayMode eq "Latex2HTML" || $displayMode eq "TeX") { - $output = '\[' . $f . '= \begin{cases}'; - while (@array) { - $fx = shift(@array); $ifx = shift(@array); - $ifx =~ s/\\\(/\}/g; $ifx =~ s/\\\)/\\text\{/g; - $output .= $fx . '&\text{' . $ifx . '}\\\\' . "\n"; - } - $output =~ s/\\text\{\}//g; $output =~ s/\\\\$//; - $output .= '\end{cases}\]'; - } else { - warn "piecewiseFunction: Unknown display mode: $displayMode" - } - return($output); -} - -# -# Characters that make up the brace in the symbol font -# -($brt,$brm,$brb,$brc) = ('','','',''); - -# -# Braces of different sizes (an alternative would be to -# add $brc characters to extend the vertical parts). -# If you want additional sizes, you can push more onto the end -# of the array from within the .pg file, or add them here. -# -# -@braceHTML = ( - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
}, - qq{$brt
$brm
$brb
} -); - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/planeAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/planeAnswer.pl deleted file mode 100755 index 2efeccd883..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/planeAnswer.pl +++ /dev/null @@ -1,147 +0,0 @@ -loadMacros( - 'unionAnswer.pl', - 'unionMacros.pl', - 'unionVectors.pl', - 'vectorUtils.pl', -); - -sub _planeAnswer_init {}; # don't reload this file - -# -# Check that a formula represents a given plane implicitly -# -# Usage: implicit_plane_cmp(N,P); -# -# where N is a normal vector to the plane and P is a point on the -# plane. Ex: plane_cmp(Vector(1,2,3),Point(0,0,0)); or -# plane_cmp([1,2,3],[0,0,0]); -# -# The student enter's an answer in the form "a x + b y + c z = d", -# or anything equivalent to that. This answer checking will properly -# recognize the plane even if it is given as a different multiple of -# the equation, or if it is reorganized, or has computations within it. -# E.g., the student could enter "0 = 20 -(x + 3y + z)". -# -# The answer checker will produce the string used for displaying the -# correct answer for you automatically, properly handling zero coefficients. -# - -sub implicit_plane_cmp { - my $N = Vector(shift); my $P = Point(shift); - my %params = @_; - set_default_options(\%params, - showHints => 1, - debug => 0, - ); - my $answer = Plane($N,$P); - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => $answer, - type => "implicit plane", - N => $N, P => $P, - ); - $answerEvaluator->install_evaluator(\&implicit_plane_check,%params); - return $answerEvaluator; -} - -# -# The guts of the implicit plane checker -# -sub implicit_plane_check { - my $ans = shift; - my %params = @_; - my @errors = (); my $score = 0; - my $showHints = $params{showHints}; - $showHints = 0 if ($showPartialCorrectAnswers == 0 || isPreviewMode()); - my ($text,$latex); - my ($a,$b,$c,$d); - my ($N,$P) = ($ans->{N}->data,$ans->{P}->data); - - my $student_ans = trimString($ans->{student_ans}); - # - # Find the left- and right-hand sides - # - if ($student_ans !~ m/^(.*)=(.*)$/) { - push(@errors,"Your answer is not of the form 'ax + by + cz = d'") - if ($showHints); - } else { - my ($lhs,$rhs) = (trimString($1),trimString($2)); - push(@errors,"The left-hand side is blank") if $lhs eq ''; - push(@errors,"The right-hand side is blank") if $rhs eq ''; - if (scalar(@errors) == 0) { - # - # Do a syntax check on the two sides - # - my $cmp = fun_cmp("0",vars=>['x','y','z']); - my $hash = evaluateAnswer($cmp,$lhs,1); - if ($hash->{ans_message} eq "") { - $lhs = $hash->{student_ans}; - $text = $hash->{preview_text_string}; - $latex = $hash->{preview_latex_string}; - } else { - push(@errors,"Error evaluating left-hand side:"); - push(@errors,IndentError($hash->{student_ans})); - push(@errors,IndentError($hash->{ans_message})); - } - clearEvaluator($cmp); - $hash = evaluateAnswer($cmp,$rhs,1); - if ($hash->{ans_message} eq "") { - $rhs = $hash->{student_ans}; - $text .= " = ".$hash->{preview_text_string}; - $latex .= " = ".$hash->{preview_latex_string}; - } else { - push(@errors,"Error evaluating right-hand side:"); - push(@errors,IndentError($hash->{student_ans})); - push(@errors,IndentError($hash->{ans_message})); - } - clearEvaluator($cmp); - - if (scalar(@errors) == 0) { - # - # Use $cmp to evaluate student function to obtain coefficients - # - $cmp->{rh_ans}{evaluation_points} = [[0,0,0],[1,0,0],[0,1,0],[0,0,1]]; - $hash = evaluateAnswer($cmp,"($lhs)-($rhs)"); - # check for errors here, too? - $d = $hash->{ra_student_values}->[0]; - $a = $hash->{ra_student_values}->[1] - $d; - $b = $hash->{ra_student_values}->[2] - $d; - $c = $hash->{ra_student_values}->[3] - $d; - # - # Check that the student function really IS a plane - # - $cmp = fun_cmp("$a x + $b y + $c z + $d",vars => ['x','y','z']); - if (isCorrectAnswer($cmp,"($lhs)-($rhs)")) { - # - # Check that is is the RIGHT plane - # - $score = (areParallel($N,Point($a,$b,$c)) && - isCorrectAnswer(std_num_cmp(-$d),vDot($P,Point($a,$b,$c)))); - } else { - push(@errors, - "Your answer does not seem to be of the form 'ax + by + cz = d'") - if ($showHints); - } - } - $ans->setKeys( - student_ans => $lhs." = ".$rhs, - preview_text_string => $text, - preview_latex_string => $latex, - ); - } - } - if (scalar(@errors) == 0) { - $ans->score($score); - $ans->{ans_message} = $ans->{error_message} = ''; - $ans->{error} = 0; - } else { - $ans->score(0); - $ans->{error_message} = join("\n",@errors); - $ans->{ans_message} = join("\n",@errors); - $ans->{error} = 1; - } - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/pointAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/pointAnswer.pl deleted file mode 100755 index f441598abd..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/pointAnswer.pl +++ /dev/null @@ -1,300 +0,0 @@ -loadMacros( - "unionMacros.pl", - "unionUtils.pl", - "unionVectors.pl", - "answerUtils.pl", -); - -sub _pointAnswer_init {}; # don't reload this file - -###################################################################### -## -## Point (and Vector) answer checkers -## -## num_point_cmp() check for a point with numeric coordinates -## fun_point_cmp() check for a point with coordinate functions -## -## std_point_cmp() provide your own coordinate checker -## point_cmp() provide array of coordiante checkers - - -###################################################################### -# -# num_point_cmp(answer,options) -# -# where options are from among: -# -# extra_cmp => cmp an answer checker to use for extra coordinates -# supplied by the student (for syntax checking) -# -# cmp_params => [list] parameters to be passed to std_num_cmp -# for each coordinate -# -# format => string an sprintf format string used to format -# the answer (for display purposes only). -# Default: "%.6g" -# -# or any of the options to point_cmp (see below). -# -# This provides an answer checker that compares against a point -# or vector with numeric coordinates. -# -# e.g. num_point_cmp("(1,0,0)") -# - -sub num_point_cmp { - my $ans = shift; - std_point_cmp($ans,cmp => \&std_num_cmp, format => '%.6g', @_); -} - - -###################################################################### -# -# fun_point_cmp(answer,options) -# -# where options are from among: -# -# extra_cmp => cmp an answer checker to use for extra coordinates -# supplied by the student (for syntax checking) -# -# cmp_params => [list] parameters to be passed to fun_cmp -# for each coordinate -# -# vars => [list] the variables used in the functions -# -# or any of the options to point_cmp (see below). -# -# This provides an answer checker that compares against a point -# or vector with functional coordinates. -# -# e.g. fun_point_cmp("(t,t^2,t^3)", vars => 't'); -# - -sub fun_point_cmp { - my $ans = shift; - my %params = (cmp_params => [], @_); - if (defined($params{vars})) { - $params{cmp_params} = [vars => $params{vars}, @{$params{cmp_params}}]; - delete $params{vars}; - } - std_point_cmp($ans, cmp => \&fun_cmp, %params); -} - - -###################################################################### -# -# std_point_cmp(ans,options) -# -# where ans is the correct answer as a string (e.g. "(1,0,0)") and -# options are taken from among the following: -# -# cmp => \&cmp a reference to the answer checker to use for each -# endpoint (e.g., \&std_num_cmp, or ~~&std_num_cmp -# in a .pg file) -# -# cmp_params => [...] is a reference to a list of parameters for the -# answer checker (e.g., [vars => 'x']) -# -# default => ans is the answer to use for any extra coordinates -# supplied by the student (for syntax checking only) -# -# or any of the options allowed by point_cmp (see below) -# -# This returns an answer checker that compares the point using the -# given answer checker on each coordinate. -# -# e.g. std_point_cmp("(1,0,0)",cmp => ~~&strict_num_cmp, default => 0); -# -sub std_point_cmp { - my $ans = shift; $ans = $ans->answer if isVector($ans); - my %params = ( - cmp => \&std_num_cmp, - cmp_params => [], - default => 0, - @_ - ); - my ($cmp,$cmp_params) = ($params{cmp},$params{cmp_params}); - delete $params{cmp}; delete $params{cmp_params}; - my $default = $params{default}; delete $params{default}; - $params{extra_cmp} = &{$cmp}($default,@{$cmp_params}) - unless $params{extra_cmp}; - if ($ans =~ s/^(\[|\(|<)(.*)(>|\]|\))$/$2/) - {$params{left} = $1; $params{right} = $3} - my @answers = (); my $answer; - foreach $answer (split(',',$ans)) - {push(@answers,&{$cmp}($answer,@{$cmp_params}))} - point_cmp([@answers],%params); -} - - -###################################################################### -# -# point_cmp(cmps,options) -# -# where -# -# cmps is a reference to an array of answer checkers, one -# for each component of the answer -# -# and options are taken from: -# -# showHints => 0 or 1 determines if coordinate-by-coordinate -# hints are given (tells whether each -# coordinate is correct or not). -# (default: 1) -# -# showLengthHints => 0 or 1 determines if messages about incorrect -# number of components should be issued -# (default: 1) -# -# extra_cmp => cmp an answer checker to use for any extra -# coordinates supplied by the student -# (for syntax checking purposes) -# -# format => string sprintf format for the coordinates -# -# post_process => code routine to determine the score once -# the point passes the syntax checking -# of the individual components. This -# can be used to change the score -# after the fact (e.g., to check for -# parallel vectors, etc.) -# -sub point_cmp { - my $compref = shift; - my %params = @_; - set_default_options(\%params, - showHints => 1, - showLengthHints => 1, - extra_cmp => undef, - post_process => undef, - format => undef, - debug => 0, - left => '(', - right => ')', - ); - $params{components} = $compref; - $params{pattern} = "\\$params{left}(.*)\\$params{right}" - unless defined($params{pattern}); - - # - # Needed to get preview to work properly - # - my $type = "point"; my $hash; - foreach $hash (@{$compref}) { - if ((hashFor($hash))->{type} =~ m/number/) { - $type .= " (number)"; - last; - } - } - - my @ans = (); my @ans_point = (); - my $format = $params{format}; my $correct_ans; - foreach $hash (@{$compref}) { - $correct_ans = (hashFor($hash))->{correct_ans}; - push(@ans_point,$correct_ans); - $correct_ans = sprintf($format,$correct_ans) if ($format); - push(@ans,$correct_ans); - } - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => $params{left}.' '.join(", ",@ans).' '.$params{right}, - correct_point => [@ans_point], - components => $compref, - type => $type, - ); - $answerEvaluator->install_evaluator(\&point_check,%params); - return $answerEvaluator; -} - -###################################################################### -# -# The guts of the point checker -# -sub point_check { - my $ans = shift; - my %params = @_; - my @errors = (); my $score = 1; - my $showHints = defined($params{showHints})? - $params{showHints} : $showPartialCorrectAnswers; - my $showLengthHints = defined($params{showLengthHints})? - $params{showLengthHints} : $showPartialCorrectAnswers; - $showHints = $showLengthHints = 0 if isPreviewMode(); - - my $extra_cmp = $params{extra_cmp}; - $extra_comp = std_num_cmp(0) unless $extra_cmp; - my ($lp,$rp) = ($params{left},$params{right}); - my ($addparens) = 1; - my $format = $params{format}; - my (@new_ans,@new_ans_point); - - my $student_ans = trimString($ans->{student_ans}); - if ($student_ans !~ s/^$params{pattern}$/$1/) { - my %parens = ('(' => "parentheses", - '<' => "angle brackets", - '[' => "square brackets"); - $parens{$lp} = "the proper characters" unless defined($parens{$lp}); - push(@errors, - showHTML("Your answer is not enclosed in $parens{$lp}: $lp and $rp")); - $score = 0; - } else { - my $i = 0; my @answers = split(',',$student_ans); - my ($answer, $answer_full, $correct_cmp, @text, @latex); - - foreach $answer (@answers) { - $correct_cmp = (@{$ans->{components}})[$i++] || $extra_cmp; - $hash = evaluateAnswer($correct_cmp,$answer); - $answer_full = $answer; - if ($hash->{ans_message} eq "") { - $answer_full = $answer = $hash->{student_ans}; - $answer = sprintf($format,$answer) if ($format); - } else {$hash->{score} = 0} - push(@new_ans,$answer); push(@new_ans_point,$answer_full); - push(@text,StringOrBlank($hash->{preview_text_string},$answer)); - push(@latex,StringOrBlank($hash->{preview_latex_string},$answer)); - if ($hash->{score} != 1 || $correct_cmp == $extra_cmp) { - $score = 0; - if ($hash->{ans_message} ne "") { - push(@errors,"Error evaluating the ".NameForNumber($i). - " coordinate:"); - push(@errors,IndentError($answer)); - push(@errors,IndentError($hash->{ans_message})); - } else { - push(@errors,"The ".NameForNumber($i)." coordinate is incorrect.") - if ($showHints); - } - } - clearEvaluator($correct_cmp); - } - - if (scalar(@answers) != scalar(@{$ans->{components}})) { - push(@errors,"The number of coordinates is not correct.") - if ($showLengthHints); - $score = 0; - } - $ans->setKeys(student_ans => $lp . join(', ',@new_ans) . $rp) - if (scalar(@new_ans) > 0); - $ans->setKeys( - preview_text_string => $lp . join(', ',@text) . $rp, - preview_latex_string => $lp . join(', ',@latex) . $rp, - ); - } - - # make sure vectors aren't interpretted as HTML tags - $ans->setKeys(student_ans => showHTML($ans->{student_ans})); - - if (scalar(@errors) == 0) { - $ans->score($score); - $ans->{ans_message} = $ans->{error_message} = ''; - $ans->{answer_point} = [@new_ans_point]; - if ($params{post_process}) {$ans->score(&{$params{post_process}}($ans))} - } else { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = join("\n",@errors); - } - - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/unionAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/unionAnswer.pl deleted file mode 100755 index 3b3f1a6a0e..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/unionAnswer.pl +++ /dev/null @@ -1,56 +0,0 @@ -###################################################################### -# -# Implements answer checkers for unions of intervals -# - -loadMacros( - "intervalAnswer.pl", - "listAnswer.pl" -); - -sub _unionAnswer_init { - $INFINITY_UNION_MESSAGE = - "$BBOLD Note: $EBOLD ". - "If the answer includes more than one interval, write the intervals ". - "separated by the ${LQ}union$RQ symbol, ${BITALIC}U${EITALIC}. If needed, enter ". - "\\(\\infty\\) as ${BITALIC}$INFINITY_WORD${EITALIC} and \\(-\\infty\\) as ". - "${BITALIC}-$INFINITY_WORD${EITALIC}."; - - $INFINITY_UNION_MESSAGE = "" if $displayMode eq 'TeX'; -}; - -###################################################################### -# -# Usage: num_union_cmp(ans,options) -# fun_union_cmp(ans,options) -# -# where ans is the string indicating the union (e.g., "[1,3) U (-inf,-5)") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_union_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_interval_cmp, - type => "number interval union", - entry_type => 'interval', - list_type => 'union', - separator => ' U ', - split_defaults => [separator => 'U'], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_union_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_interval_cmp, - type => "function interval union", - entry_type => 'interval', - list_type => 'union', - separator => ' U ', - split_defaults => [separator => 'U'], - cmp_ans => "(0,1]", - fun_var_params(cmp_defaults => [showHints => 0],@_)); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/unorderedAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/unorderedAnswer.pl deleted file mode 100755 index 477ac31a18..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/unorderedAnswer.pl +++ /dev/null @@ -1,162 +0,0 @@ -########################################################################## -########################################################################## -## -## Routines for groups of answer blanks where the user can enter -## answers in any order in the blanks. -## - -loadMacros("answerUtils.pl"); - -sub _unorderedAnswer_init {}; # don't reload this file - -########################################################################## -# -# Collect a group of answer checkers for use with answers that can be given -# in any order. If N answer checkers are given, then the last N answer -# rules will be used. It is beter to use named rules and UNORDERED_NAMED_ANS -# below. Otherwise, be sure to use UNORDERED_ANS right after the answer -# rules for the answers you want to compare. -# -# Format: -# -# UNORDERED_ANS(checker1, checker2, ...); -# -# Example: -# -# BEGIN_TEXT -# The function \(f(x) = \frac{1}{x^2-$a}\) is defined except -# for \(x =\) \{ans_rule(10)\} and \(x =\) \{ans_rule(10)\}. -# END_TEXT -# -# UNORDERED_ANS(std_num_cmp(sqrt($a)), std_num_cmp(-sqrt($a))); -# -# (the student can enter the solutions in either order.) -# -sub UNORDERED_ANS { - my @cmp = @_; my @params = (); my $i; my $n = scalar(@cmp); - # - # The best thing would be to use the size of @PG_ANSWERS in place of - # $main::ans_rule_count, but we don't have access to that - # - foreach $i (1..$n) - {push(@params,ANS_NUM_TO_NAME($i+main::ans_rule_count()-$n),$cmp[$i-1])} - my @results = unordered_answer_list(@params); - while (scalar(@results) > 0) {shift(@results), ANS(shift(@results))} -} - -########################################################################## -# -# Collect a group of answer checkers for use with named answers that -# can be given in any order. -# -# Format: -# -# UNORDERED_NAMED_ANS(name1 => checker1, name2 => checker2, ...); -# -# Example: -# -# BEGIN_TEXT -# The function \(f(x) = \frac{1}{x^2-$a}\) is defined except -# for \(x =\) \{NAMED_ANS_RULE(A1,10)\} -# and \(x =\) \{NAMED_ANS_RULE(A2,10)\}. -# END_TEXT -# -# UNORDERED_NAMED_ANS( -# A1 => std_num_cmp(sqrt($a)), -# A2 => std_num_cmp(-sqrt($a)) -# ); -# -# (the student can enter the solutions in either blank.) -# -sub UNORDERED_NAMED_ANS { - NAMED_ANS(unordered_answer_list(@_)); -} - -########################################################################## -# -# Low-level routine for handling unordered collections of answer checkers -# -sub unordered_answer_list { - my %params = @_; my @args = @_; - my (@cmp,@ids); - while (scalar(@_) > 0) {push(@ids,shift); push(@cmp,shift)} - # - # Setup - # - my ($i,$j,$k); - my $n = scalar(@cmp); - my @cmpi = (0..$n-1); - my @skipped = (); - # - # Check that the answers exist (otherwise it's our first time through) - # - foreach $i (@ids) {return(@args) if (!defined($inputs_ref->{$i}))} - # - # Check each answer against the available answer checkers. - # Keep track of the ones that match and that don't. - # - ANSWER: foreach $i (0..$n-1) { - $k = 0; - foreach $j (@cmpi) { - if (isCorrectAnswer($cmp[$j],$inputs_ref->{$ids[$i]})) - {$ans[$i] = $j; splice(@cmpi,$k,1); next ANSWER} - $k++; - } - push(@skipped,$i); - } - # - # Check if the unmatched checkers are all blank checkers. - # If so, let them report blanks as correct answers. - # - my $blankOK = 1; - foreach $i (@cmpi) { - if (ref($cmp[$i]) ne "AnswerEvaluator" || - ($cmp[$i]->rh_ans)->{type} ne "blank") {$blankOK = 0; last} - } - if ($blankOK) {foreach $i (@cmpi) {($cmp[$i]->rh_ans)->{blankOK} = 1}} - # - # Assign the unmatching answers to umatched checkers - # - foreach $i (0..scalar(@skipped)-1) {$ans[$skipped[$i]] = $cmpi[$i]} - # - # Make the final list of answer checkers in their proper order - # - my (@list) = (); - foreach $i (0..$n-1) - {clearEvaluator($cmp[$ans[$i]]); push(@list,$ids[$i],$cmp[$ans[$i]])} - return (@list); -} - -################################################## -# -# AnswerChecker that allows a blank answer in a collection of unordered -# answer checkers. It will return "correct" for a blank answer only if -# all the other answers are correct. (The blankOK value is set by -# unordered_answer_list when this is true.) This lets you ask a question -# where the number of answers is not known (by the student) ahead of time. -# -sub blank_cmp { - my %params = @_; - $params{debug} = 0 unless defined($params{debug}); - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - type => "blank", blankOK => 0, - correct_ans => '', weight => 0 - ); - $answerEvaluator->install_pre_filter('reset'); # remove the blank filter - $answerEvaluator->install_evaluator(\&blank_cmp_check,%params); - $answerEvaluator->install_post_filter('reset'); # remove the blank filter - return $answerEvaluator; -} - -sub blank_cmp_check { - my $ans = shift; - my %params = @_; - $ans->{student_ans} = trimString($ans->{student_ans}); - if ($ans->{student_ans} eq "") {$ans->score($ans->{blankOK})} - else {$ans->score(0)} - return($ans); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/variableAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/variableAnswer.pl deleted file mode 100755 index 88f68bd8bc..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/variableAnswer.pl +++ /dev/null @@ -1,94 +0,0 @@ -sub _variableAnswer_init {}; # don't reload this file - -################################################## -# -# A match on a collection of comma- or space-separated -# strings, optionally enclosed in parentheses. -# The match can be case insensitive, and order insensitive. -# -# Usage: variable_cmp(ans,options) -# -# where ans is the correct answer string (or an array of the answers, -# which will be made into a comma-separated string), and options -# are taken from: -# -# ignore_case => 0 or 1 determines wether upper- and -# lower-case are to be distinguished or not -# (Default: 0) -# -# ignore_order => 0 or 1 determines whether the order of the -# answers matters or not -# (Default: 0) -# -# allow_parens => 0 or 1 determines whether parens are stripped -# from around the answer automatically -# (Default: 1) -# -# force_parens => 0 or 1 determines whether parens are -# required in the student's answer -# (Default: 0) - -sub variable_cmp { - my $answer = shift || ''; - $answer = join(',',@{$answer}) if ref($answer) eq 'ARRAY'; - $answer = trimString($answer); - my %params = @_; - set_default_options(\%params, - ignore_case => 0, - ignore_order => 0, - allow_parens => 1, - force_parens => 0, - ); - $answer = '('.$answer.')' if $params{force_parens} && $answer !~ m/^\(.*\)$/; - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug} || 0; - $answerEvaluator->ans_hash(type => "variable", correct_ans => $answer); - $answerEvaluator->install_evaluator(\&variable_cmp_check,%params); - return $answerEvaluator; -} - -# -# The guts of the checker -# -sub variable_cmp_check { - my $ans = shift; - my %option = @_; - my $student_ans = $ans->{student_ans}; - my $correct_ans = $ans->{correct_ans}; - $student_ans = trimString($student_ans); - $ans->setKeys( - student_ans => $student_ans, - original_student_ans => $student_ans, - preview_text_string => $student_ans, - preview_latex_string => $student_ans, - ans_message => '' - ); - if ($option{force_parens}) { - if ($student_ans !~ m/^\(.*\)$/) { - $ans->{ans_message} = "Your answer isn't enclosed in parentheses"; - $ans->{error} = 1; $ans->score(0); return $ans; - } - $option{allow_parens} = 1; - } - if ($option{allow_parens}) { - $student_ans =~ s/^\((.*)\)$/$1/; $correct_ans =~ s/^\((.*)\)$/$1/; - if ($student_ans =~ m/^\(|\)$/) { - $ans->{ans_message} = "Incorrect parentheses"; - $ans->{error} = 1; $ans->score(0); return $ans; - } - } - $student_ans =~ s/,/ /g; $correct_ans =~ s/,/ /g; - $student_ans =~ s/\s+/ /g; $correct_ans =~ s/\s+/ /g; - if ($option{ignore_case}) { - $student_ans = lc($student_ans); - $correct_ans = lc($correct_ans); - } - if ($option{ignore_order}) { - $student_ans = join(' ',lex_sort(split(' ',$student_ans))); - $correct_ans = join(' ',lex_sort(split(' ',$correct_ans))); - } - $ans->score($student_ans eq $correct_ans); - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/vectorAnswer.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/vectorAnswer.pl deleted file mode 100755 index f08a9dec80..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/vectorAnswer.pl +++ /dev/null @@ -1,11 +0,0 @@ -loadMacros("pointAnswer.pl","vectorUtils.pl"); - -# -# Just a renaming of the point comparisons. -# -sub num_vector_cmp {num_point_cmp(@_)} -sub fun_vector_cmp {fun_point_cmp(@_)} -sub std_vector_cmp {std_point_cmp(@_)} -sub vector_cmp {point_cmp(@_)} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/vectorUtils.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/vectorUtils.pl deleted file mode 100755 index f537c5bca4..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/vectorUtils.pl +++ /dev/null @@ -1,121 +0,0 @@ -##################################################################### -# -# Some utility routines that are useful in vector problems -# - -sub _vectorUtils_init {}; # don't reload this file - -################################################## - -# -# formats a vector name (should be used in math mode) -# -# Vectors will be in bold italics in HTML modes, and -# will be overlined in TeX modes. (Bold italic could also work in -# TeX modes, but the low resolution on screen made it less easy -# to distinguish the difference between bold and regular letters.) -# -sub Overline { - my $v = shift; - my $HTML = ''.$v.''; - MODES( - TeX => "\\overline{$v}", - HTML => $HTML, - HTML_tth => '\begin{rawhtml}'.$HTML.'\end{rawhtml}', - HTML_dpng => "\\overline{$v}", - ); -} - -# -# This gets a bold letter in TeX as well as HTML modes. -# Although \boldsymbol{} works fine on screen in latex2html mode, -# the PDF file produces non-bold letters. I haven't been able to -# track this down, so used \mathbf{} in TeX mode, which produces -# roman bold, not math-italic bold. -# -sub BoldMath { - my $v = shift; - my $HTML = ''.$v.''; - MODES( - TeX => "\\boldsymbol{$v}", # doesn't seem to work in TeX mode -# TeX => "\\mathbf{$v}", # gives non-italic bold in TeX mode - Latex2HTML => "\\boldsymbol{$v}", - HTML => $HTML, - HTML_tth => '\begin{rawhtml}'.$HTML.'\end{rawhtml}', - HTML_dpng => "\\boldsymbol{$v}", - ); -} - -# -# The coordinate unit vectors -# -#if ($main::displayMode eq "TeX") { - $i = BoldMath('i'); - $j = BoldMath('j'); - $k = BoldMath('k'); -#} else { -# $i = Overline(MODES(TeX=>'\imath',HTML=>'i')); -# $j = Overline(MODES(TeX=>'\jmath',HTML=>'j')); -# $k = Overline('k'); -#} - -# -# Grad sumbol -# -$GRAD = '\nabla '; - -# -# Create a non-zero point with the given number of coordinates -# with the given random range (which defaults to (-5,5,1)). -# -# non_zero_point(n,a,b,c) -# -sub non_zero_point { - my $n = shift; my $k = $n; my @v = (); - my $a = shift || -5; my $b = shift || $a + 10; my $c = shift || 1; - while ($k--) {push(@v,random($a,$b,$c))} - if (Norm(@v) == 0) {$v[random(0,$n-1,1)] = non_zero_random($a,$b,$c)} - return Point(@v); -} -sub non_zero_point2D {non_zero_point(2,@_)} -sub non_zero_point3D {non_zero_point(3,@_)} - -# -# Same but for Vectors -# -sub non_zero_vector {Vector(non_zero_point(@_))} -sub non_zero_vector2D {non_zero_vector(2,@_)} -sub non_zero_vector3D {non_zero_vector(3,@_)} - -# -# Form the string for a line given its point and vector -# -# Usage: line(P,V); or line(P,V,'t'); -# -# where P is the point and V the direction vector for the line, and -# t is the variable to use (default is 't'). -# -# Ex: Line([1,-3],[2,1]) produces Vectir("1+2t","-3+t"). -# Ex: Line(Point(1,-3),Vector(2,1)) produces Vector("1+2t","-3+t"). -# -sub Line { - my @p = Point(shift)->value; my @v = Vector(shift)->value; - my $t = shift; $t = 't' unless $t; - my @coords = (); - if ($#p != $#v) {die "Dimensions of point and vector don't match"} - foreach my $i (0..$#p) {push(@coords,FPOLY("$p[$i]+$v[$i]$t",$t))} - return Vector(@coords); -} - -# -# Creates a displayable string for a plane given its -# normal vector and a point on the plane. -# -# Usage: plane(N,P); -# -sub Plane { - my $N = Vector(shift); my $P = Point(shift); my @N = $N->value; - FPOLY("$N[0] x + $N[1] y + $N[2] z = ".vDot($N,$P)); -} - -1; diff --git a/OpenProblemLibrary/macros/CollegeOfIdaho/weightedGrader.pl b/OpenProblemLibrary/macros/CollegeOfIdaho/weightedGrader.pl deleted file mode 100755 index e294444051..0000000000 --- a/OpenProblemLibrary/macros/CollegeOfIdaho/weightedGrader.pl +++ /dev/null @@ -1,331 +0,0 @@ -loadMacros('unionUtils.pl'); - -sub _weightedGrader_init {}; # don't reload this file - -###################################################################### -# -# A weighted grader that allows you to assign arbitrary percentages -# to the various answers in a problem. It also allows you to indicate -# that answering one part correctly will give you credit for some -# other part(s). This way, if there are several parts leading up to -# a "goal" answer, and the student produces the goal answer by -# some other means, he can be given full credit for the problem anyway. -# -# -# WEIGHTED ANSWERS: -# -# Each problem is assigned a weight (the default is 1). The -# student's score is then the sum of the weights for his correct -# answers divided by the total of the weights for all answers. (To -# assign weights as percentages, use integers that add up to 100, eg, -# use 40 and 60 for the weights for two answers.) -# -# There are two ways to assign weights. The first is to use the -# WEIGHTED_ANS() routine (in place of ANS) to give an answer checker -# plus a weight. -# -# Example: -# -# WEIGHTED_ANS(std_num_cmp($ans1),2); -# -# This assigns a weight of 2 to the corresponding numeric answer. -# -# As with ANS(), WEIGHTED_ANS() can take more than one answer checker and -# weight. -# -# Example: -# -# WEIGHTED_ANS( -# std_num_cmp($ans1), 40, -# std_num_cmp($ans2), 60 -# ); -# -# This assigns 40% to the first answer and 60% to the second answer. -# -# The second way of assigning a weight is through the weight_ans() -# function. This takes a single answer checker and weight and returns -# a new answer checker of the same type that has the desired weight. -# Thus -# -# ANS(weight_ans(std_num_cmp($ans1),2)); -# -# is equivalent to the first example above. -# -# The main purpose for weighted_ans() is so that weights can be used -# with UNORDERED_ANS(), or in other places where you want to use the -# weighted answer checker directly. For example: -# -# UNORDERED_ANS( -# weight_ans(std_num_cmp($ans1),40), -# weight_ans(std_num_cmp($ans2),60), -# ); -# -# produces two answers whose order doesn't matter, but the student -# will get 40% for getting $ans1 and 60% for getting $ans2 (no matter -# what order they appear in). -# -# Note that the blank_cmp() answer checker has a weight of 0 by -# default. You can override this using weight_ans(); for example, -# weight_ans(blank_cmp(),1) makes the blank count the same as all -# the other answers. (If there are two or more non-blank answers, -# then having the blanks with weight 0 will allow the observant -# student to deduce the number of blank answers from the percentage -# for a single correct answer, provided all the non-blank answers are -# equally weighted). -# -# Once you have given weights to the answers, you also need to -# install the weighted grader. Do this using the command -# -# install_weighted_grader(); -# -# -# HAVING ONE ANSWER PROVIDE CREDIT FOR ANOTHER: -# -# You may want to have a correct answer for one problem automatically -# give credit for one or more other parts of the problem. For example -# If several parts are used to lead up to the "real" answer to the -# problem, and the student produces that final answer without doing -# the intermediate parts (perhaps using some other method), then you -# may want to give the student full credit for the problem anyway. -# You can do so in the following way. -# -# First, let us call the final answer the "goal" answer, and the -# answer that it would give automatic credit for the "optional" answer. -# -# The optional answer blank must be a named answer, e.g., -# -# BEGIN_TEXT -# You get credit for this answer: \{NAMED_ANS_RULE('optional',10)\} -# When you answer this one: \{ans_rule(10)\} -# END_TEXT -# NAMED_ANS('optional',std_num_cmp(5)); -# -# Then for the goal answer, in place of ANS, use CREDIT_ANS, listing the -# optional answer as the second argument: -# -# CREDIT_ANS(std_num_cmp(10),'optional'); -# -# You could also use NAMED_WEIGHTED_ANS for the optional answer, and -# supply a third argument for CREDIT_ANS, which is the weight for the -# goal answer. For example: -# -# NAMED_WEIGHTED_ANS('optional',std_num_cmp(5),20); -# CREDIT_ANS(std_num_cmp(10),'optional',80); -# -# This way, if the student gets the optional part right (but not the -# goal), he gets 20%, and if he gets the goal right, he gets 100%. -# -# One can use CREDIT_ANS to give credit for several other (named) -# answers at once by passing a list of names rather than a single one, -# as in: -# -# CREDIT_ANS(std_num_cmp(10),['optional1','optional2'],80); -# -# The weight_ans() routine, described in the section above, also can -# be used to give credit to another answer. In addition to the -# answer-checker and the weight, you can pass an answer name (or -# list of names) that should get credit when this one is right. -# For example -# -# ANS(weight_ans(std_num_cmp(10),80,'optional')); -# -# is equivalent to -# -# CREDIT_ANS(std_num_cmp(10),'optional',80); -# -# One caveat to keep in mind is that credit is given to the optional -# answer ONLY if the answer is left blank (or is actually correct). -# Credit will NOT be given if the answer is incorrect, even if the -# goal answer IS correct. -# -# When credit IS given, the blank answer is still marked as incorrect -# in the grey answer report at the top of the page, but the student -# gets awarded the points for that answer anyway (with no other -# indication). It is possible to cause the blank to be marked as -# correct, but this seemed confusing to the students. -# -# Once you have issued the various ANS calls, you also need to -# install the weighted grader. Do this using the command -# -# install_weighted_grader(); -# - -################################################## -# -# Issue ANS() calls for the weighted checkers -# -sub WEIGHTED_ANS { - my ($checker,$weight); - while (@_) { - $checker = shift; $weight = shift; - ANS(weight_ans($checker,$weight)); - } -} - -################################################## -# -# Issue NAMED_ANS() calls for the weighted checkers -# -sub NAMED_WEIGHTED_ANS { - my ($name,$checker,$weight); - while (@_) { - $name = shift; $checker = shift; $weight = shift; - NAMED_ANS($name,weight_ans($checker,$weight)); - } -} - -################################################## -# -# Issue an ANS() call for the checker, giving -# credit to the given answers. -# -sub CREDIT_ANS { - my $checker = shift; - my $credit = shift; - $credit = [$credit] if defined($credit) && ref($credit) ne "ARRAY"; - my $weight = shift; - ANS(weight_ans($checker,$weight,$credit)); -} - - -################################################## -# -# Either add a weight to an AnswerEvaluator, or return a -# new checker that adds the weight to its result. Also, -# add the "credit" field, if supplied. -# -sub weight_ans { - my $checker = shift; my $weight = shift; - my $credit = shift; - $credit = [$credit] if defined($credit) && ref($credit) ne "ARRAY"; - if (ref($checker) eq "AnswerEvaluator") { - $checker->{rh_ans}->{weight} = $weight; - $checker->{rh_ans}->{credit} = $credit if defined($credit); - return $checker; - } else { - my $newChecker = sub { - my $hash = &{$checker}(@_); - $hash->{weight} = $weight; - $checker->{rh_ans}->{credit} = $credit if defined($credit); - return $hash; - }; - return $newChecker; - } -} - - -################################################## -# -# This is the weighted grader. It uses an extra field added to the -# AnswerHash (named "weight") to tell how much weight to give each -# problem. The grader adds up the total weights for the correct -# answers. For partially correct ones, it uses the score for that -# answer to give a portion of that weight. For example, if the -# weight is 40 and the score for the answer is .5, then 20 is added -# to the total for the problem. (Note that most answer checkers only -# return 1 or 0, but they are allowed to return partial credit as -# well.) -# -# When the student's total is computed, it is divided by the sum of -# all the weights in order to determine the final score. -# -# It also uses a special field called "credit" that determines -# what other (named) answers are given full credit if the given -# answer is correct. This can be used to make "optional" answers, -# where getting the "final" answer right gives credit for the other parts. -# - -sub weighted_grader { - my $rh_evaluated_answers = shift; - my $rh_problem_state = shift; - my %form_options = @_; - my %answers = %{$rh_evaluated_answers}; - my %problem_state = %{$rh_problem_state}; - - my %problem_result = ( - score => 0, - errors => '', - type => 'weighted_grader', - msg => '', - ); - - if (scalar(keys(%answers)) == 0) { - $problem_result{msg} = "This problem did not ask any questions."; - return(\%problem_result,\%problem_state); - } - - return(\%problem_result,\%problem_state) - if (!$form_options{answers_submitted}); - - my ($score,$total) = (0,0); - my ($weight,$ans_name,$credit_name); - my (%credit); - - # - # Get the score for each answer - # (error if can't recognize the answer format). - # - foreach $ans_name (keys %answers) { - if (ref($answers{$ans_name}) =~ m/^(HASH|AnswerHash)$/) { - $credit{$ans_name} = $answers{$ans_name}->{score}; - } else { - die "Error at file ",__FILE__,"line ", __LINE__,": Answer |$ans_name| " . - "is not a hash reference\n" . $answers{$ans_name} . - "\nThis probably means that the answer evaluator for ". - "this answer is not working correctly."; - $problem_result{error} = - "Error: Answer $ans_name is not a hash: $answers{$ans_name}"; - } - } - - # - # Mark any optional answers as correct, if the goal answers - # are right and the optional ones are blank. - # - foreach $ans_name (keys %answers) { - if ($credit{$ans_name} == 1 && - defined($answers{$ans_name}->{credit})) { - foreach $credit_name (@{$answers{$ans_name}->{credit}}) { - $credit{$credit_name} = 1 - if (trimString($answers{$credit_name}->{student_ans}) eq ""); - } - } - } - - # - # Add up the weighted scores - # - foreach $ans_name (keys %answers) { - $weight = $answers{$ans_name}->{weight}; - $weight = 1 unless (defined($weight)); - $total += $weight; - $score += $weight * $credit{$ans_name}; - } - - $problem_result{score} = $score/$total if $total; - - # This gets rid of non-numeric scores - $problem_state{recorded_score} = 0 - unless (defined($problem_state{recorded_score}) && - isNumber($problem_state{recorded_score})); - - $problem_state{recorded_score} = $problem_result{score} - if ($problem_result{score} > $problem_state{recorded_score}); - - $problem_state{num_of_correct_ans}++ if ($score == $total); - $problem_state{num_of_incorrect_ans}++ if ($score < $total); - warn "Error in grading this problem: ". - "the score is larger than the total ($score > $total)" - if $score > $total; - - (\%problem_result, \%problem_state); -} - - -################################################## -# -# Syntactic sugar to avoid ugly ~~& construct in PG. -# -sub install_weighted_grader {install_problem_grader(\&weighted_grader)} - -1; diff --git a/OpenProblemLibrary/macros/Dartmouth/Dartmouthmacros.pl b/OpenProblemLibrary/macros/Dartmouth/Dartmouthmacros.pl deleted file mode 100755 index a1a6ce63d2..0000000000 --- a/OpenProblemLibrary/macros/Dartmouth/Dartmouthmacros.pl +++ /dev/null @@ -1,313 +0,0 @@ -#!/usr/bin/perl - -# this is equivalent to use strict, but can be used within the Safe compartment. -BEGIN{ - be_strict; -} - -## Some local macros - -sub trs_mod{ - my @inputs = @_; - my $a = $inputs[0]; - my $b = $inputs[1]; - my $zero = 1e-12; - if ($b < 0) {$b = - $b;} - -## Want mod(a,b) but perl and int are flawed - my $modvalue = $a; -# if ($a >= 0 && $a < $b) {$modvalue = $a;} - - while ($modvalue >= $b){$modvalue = $modvalue - $b;} - while ($modvalue < 0) {$modvalue = $modvalue + $b;} - if (abs($modvalue) <= $zero){$modvalue = 0;} - if (abs($modvalue - $b) <= $zero){$modvalue = 0;} -## Fudge for roundoff error - return $modvalue; -} - -## Compute the product of a scalar and a vector (scalar first) -sub scalar_mult_vector{ - ## Put the parameters passed into an array of values - my @vector = @_; - - ## Split off the first entry as the scalar, and the rest as the vector - my $scalar = $vector[0]; - my @vectorb = @vector[(1 .. $#vector )]; - - ## Initialize scalar multiple as empty vector - my @scalar_multiple=(); - - my $i; - for ($i=0; $i <= $#vectorb; $i++) - { - $scalar_multiple[$i] = $scalar * $vectorb[$i]; - } - return @scalar_multiple; -} - - - -## Compute the sum of two vectors -## Perl doesn't seem to have builtin array arithmetic -sub vector_sum { - ## Put the parameters passed into an array of values - my @vector = @_; - ## $#vector is the number of elements in vector minus 1 - my $halflength = ($#vector - 1)/2; - ## Slice the input into two equal length vectors - my @vectora = @vector[(0 .. $halflength)]; - my @vectorb = @vector[($halflength+1 .. $#vector )]; - - ## Initialize vector sum to empty array - my @vector_sum=(); - - my $i; - for ($i=0; $i <= $#vectora; $i++) - { - $vector_sum[$i] = $vectora[$i] + $vectorb[$i]; - } - return @vector_sum; -} - -## Compute the difference of two vectors -sub vector_diff{ - ## Put the parameters passed into an array of values - my @vector = @_; - ## $#vector is the number of elements in vector minus 1 - my $halflength = ($#vector - 1)/2; - ## Slice the input into two equal length vectors - my @vectora = @vector[(0 .. $halflength)]; - my @vectorb = @vector[($halflength+1 .. $#vector )]; - - return vector_sum(@vectora, scalar_mult_vector(-1, @vectorb)); -} - - -## Compute the length of a vector -sub vec_length { - ## Put the paramaters passed into an array of values - my @vector = @_; - - ## Initialize maximum value to first element - my $vector_length = 0; - - my $i; - for ($i=0; $i <= $#vector; $i++) - { - $vector_length = $vector_length + $vector[$i] * $vector[$i]; - } - $vector_length = sqrt($vector_length); - return $vector_length; -} - -sub vector_length { - my @vector = @_; - return vec_length(@vector); -} - -## Computes the dot product of two vectors (assumed of the same dimension) -sub dot_product { - ## Put the parameters passed into an array of values - my @vector = @_; - ## $#vector is the number of elements in vector minus 1 - my $halflength = ($#vector - 1)/2; - ## Split the input into two equal length vectors - my @vectora = @vector[(0 .. $halflength)]; - my @vectorb = @vector[($halflength+1 .. $#vector )]; - - ## Initialize dot product to zero - my $dot = 0; - - my $i; - for ($i=0; $i <= $#vectora; $i++) - { - $dot = $dot + $vectora[$i] * $vectorb[$i]; - } - return $dot; -} - -sub cross_product { - ## Put the parameters passed into an array of values - my @vector = @_; - - ## Slice the input into two equal length vectors - my @vectora = @vector[(0 .. 2)]; - my @vectorb = @vector[(3 .. 5)]; - - ## Initialize dot product to zero - my @cross = (); - - $cross[0] = $vectora[1]*$vectorb[2] - $vectora[2]*$vectorb[1]; - $cross[1] = $vectora[2]*$vectorb[0] - $vectora[0]*$vectorb[2]; - $cross[2] = $vectora[0]*$vectorb[1] - $vectora[1]*$vectorb[0]; - return @cross; -} - -## Compute the maximum value in a list -#sub max { -# ## Put the paramters passed into an array of values -# my @values = @_; -# -# ## Initialize maximum value to first element -# my $max = $values[0]; -# -# my $i; -# for ($i=1; $i <= $#values; $i++) -# { -# if ($values[$i] > $max) { -# $max = $values[$i]; -# } -# } -# return $max; -#} - -## Compute the minimum value in a list -#sub min { -# ## Put the paramters passed into an array of values -# my @values = @_; -# -# ## Initialize minimum value to first element -# my $min = $values[0]; -# -# my $i; -# for ($i=1; $i <= $#values; $i++) -# { -# if ($values[$i] < $min) { -# $min = $values[$i]; -# } -# } -# return $min -#} - - -## clean_scalar_string is invoked to make expressions like "$a x" look -## better when $a = 0, -1, 1 -## Usage: clean_scalar_string(scalar, "quoted string"); -## Example: clean_scalar_string(-1,"\pi") returns "-\pi" -sub clean_scalar_string{ - my $local_scalar = shift; - my $local_fixed_object = shift; - my $return_object; - - if ($local_scalar == 0) {$return_object = "0";} - elsif ($local_scalar == 1) {$return_object = "$local_fixed_object";} - elsif ($local_scalar == -1) {$return_object = "-$local_fixed_object";} - else {$return_object = "${local_scalar}${local_fixed_object}";} - return $return_object; -} - -## Computes the greatest common divisor of two integers -## Example: gcd(-300, 125) returns 25 -sub trs_gcd{ - my $a = shift; - my $b = shift; - my $c; - my $abs_a = abs($a); - my $abs_b = abs($b); - if ($abs_b == 0) {return $abs_a;} - else {$c = $abs_a % $abs_b; - return trs_gcd($abs_b, $c);} -} - -## reduced_fraction takes a pair of integers $numerator, $denominator -## ($denominator != 0) and returns an array of two elements @fraction -## $fraction[0] is the reduced numerator; $fraction[1] the reduced -## denominator -## Puts the sign of the fraction, if negative, in the numerator -## -## Usage: @fraction = reduced_fraction($numerator, $denominator) -## -sub reduced_fraction{ - my $local_numerator = shift; - my $local_denominator = shift; - my $sign_of_num = 1; - my $sign_of_denom = 1; - my $sign_of_quotient; - my @local_fraction = (); - - if ($local_numerator < 0) {$sign_of_num = -1;} - if ($local_denominator < 0) {$sign_of_denom = -1;} - $sign_of_quotient = $sign_of_num * $sign_of_denom; - - my $local_gcd = trs_gcd($local_numerator, $local_denominator); -## reduced numerator - $local_fraction[0] = ($sign_of_quotient * abs($local_numerator)) / $local_gcd; -## reduced denominator - $local_fraction[1] = abs($local_denominator) / $local_gcd; - return @local_fraction; -} - - -## Given Cartesian coordinates x and y returns an -## array the zeroth element which is the radius -## and the first element which is the argument -## 0 <= theta < 2*pi -## -## Returns r=0 theta=0 for the origin -## -sub coordinates_polar{ - my $x = shift; - my $y = shift; - my @polar=(); - my $radius = 0; - my $theta = 0; - my $pi = acos(-1); - - $radius = sqrt($x**2 + $y**2); - if ($radius != 0){ - if ($x == 0) {if ($y > 0) {$theta = $pi/2;} - else {$theta = 3*$pi/2;}} - else - { - if ($y > 0){if ($x > 0){$theta = atan($y/$x);} - else {$theta = $pi - atan(-$y/$x);}} - else {if ($x > 0){$theta = 2*$pi + atan($y/$x);} - else {$theta = $pi + atan($y/$x);}} - } - } - @polar=($radius, $theta); - return @polar; -} - - -## Cylindarical coordinates from polar routine -sub coordinates_cylindrical{ - my $x = shift; - my $y = shift; - my $z = shift; - my @cylindrical=(coordinates_polar($x, $y), $z); - return @cylindrical; -} - -## Spherical Coordinates from polar routine -## -sub coordinates_spherical{ - my $x = shift; - my $y = shift; - my $z = shift; - my $rho = 0; - my $phi = 0; - $rho = sqrt($x*$x + $y*$y + $z*$z); - my @npolar = (); - @npolar = coordinates_polar($x, $y); - my @spherical=(); - if ($x ==0 && $y == 0){if ($z >= 0) {$phi = 0;} - else {$phi = acos(-1);} - @spherical=($rho,0,$phi);} - else{ - $rho = sqrt($x*$x + $y*$y + $z*$z); - - @spherical=($rho, $npolar[1], acos($z/$rho)); - } - return @spherical; -} - - - - - -1; - - - diff --git a/OpenProblemLibrary/macros/FortLewis/AnswerFormatHelp.pl b/OpenProblemLibrary/macros/FortLewis/AnswerFormatHelp.pl deleted file mode 100755 index 8a077d39bb..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/AnswerFormatHelp.pl +++ /dev/null @@ -1,921 +0,0 @@ -=head1 NAME - -AnswerFormatHelp.pl - -=head1 SYNOPSIS - -Creates links for students to help documentation on formatting -answers and allows for custom help links. - -=head1 DESCRIPTION - -There are 16 predefined help links: angles, decimals, equations, -exponents, formulas, fractions, inequalities, intervals, limits, -logarithms, matrices, numbers, points, syntax, units, vectors. - -Usage: - - DOCUMENT(); - loadMacros("PGstandard.pl","AnswerFormatHelp.pl",); - TEXT(beginproblem()); - BEGIN_TEXT - \{ ans_rule(20) \} - \{ AnswerFormatHelp("formulas") \} $PAR - \{ ans_rule(20) \} - \{ AnswerFormatHelp("equations","help entering equations") \} $PAR - END_TEXT - ENDDOCUMENT(); - - -The first example use defaults and displays the help link right next to -the answer blank, which is recommended. The second example customizes -the link text displayed to the student, but the actual help document -is unaffected. - -=head1 AUTHOR - -Paul Pearson, Hope College, Department of Mathematics - -=cut - - - -########################### - - ########################### - # Site administration - # - # The only thing you may need to modify is the value - # of $helpurl near the end of this file. - # - ########################### - # Usage - # - # DOCUMENT(); - # loadMacros("PGstandard.pl","AnswerFormatHelp.pl",); - # TEXT(beginproblem()); - # BEGIN_TEXT - # \{ AnswerFormatHelp("formulas") \} $PAR - # \{ AnswerFormatHelp("equations","help entering equations") \} $PAR - # \{ AnswerFormatHelp("formulas","help (formulas)","http://webwork.someschool.edu/dir/subdir/") \} - # END_TEXT - # ENDDOCUMENT(); - # - # First example: use defaults. - # - # Second example: use customized text displayed to the student - # as the html link. - # - # Third example: additionally points to a particular URL where - # html help files are located. The URL must end with a forward slash. - # - # The third method is not recommended, as a universal, site-wide, - # or course-wide solution obtained by modifying the value of - # $helpdir in AnswerFormatHelp.pl is preferable to setting the - # URL in every individual PG file manually. - # - ########################### - -our ($syntaxHelpExists, $angleHelpExists, $decimalHelpExists, - $equationHelpExists, $exponentHelpExists, $formulaHelpExists, $fractionHelpExists, $inequalitiesHelpExists, - $intervalHelpExists, $limitsHelpExists, $logarithmsHelpExists, $numberHelpExists, $pointsHelpExists, - $unitsHelpExists, $vectorsHelpExists, - ) = (0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, ); - -sub _AnswerFormatHelp_init {}; # don't reload this file - -sub AnswerFormatHelp { - -# -# Define some local variables -# -my $helptype = shift; -my $customstring = shift; - -my $helpstring = ""; - -# If producing PTX output, omit any formatting help. -# (PTX output is used to produce non-interactive presentations of a problem, -# so there is no need to give syntax formatting help.) -if ($main::displayMode eq "PTX") { - return; -} - - - -#################################### -# Angles - -if ($helptype =~ m/angle/i) { - -if ($angleHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$angleHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (angles)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpAngles()';"); -} else { return $helpstring; } - - - - - - - - -#################################### -# Decimals - -} elsif ($helptype =~ m/decimal/i) { - -if ($decimalHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$decimalHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (decimals)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpDecimals()';"); -} else { return $helpstring; } - - - - - -######################################### -# Equations - -} elsif ($helptype =~ m/equation/i) { - -if ($equationHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$equationHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (equations)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpEquations()';"); -} else { return $helpstring; } - - - - - -############################################### -# Exponents - -} elsif ($helptype =~ m/exponent/i) { - -if ($exponentHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$exponentHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (exponents)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpExponents()';"); -} else { return $helpstring; } - - - - - - -####################################### -# Formulas - -} elsif ($helptype =~ m/formula/i) { - - my $local1UseBaseTenLogHelp = Context()->flags->get("useBaseTenLog"); - my $local2UseBaseTenLogHelp = main::Context()->flags->get("useBaseTenLog"); - my $globalUseBaseTenLogHelp = $useBaseTenLog; - - if ($local1UseBaseTenLogHelp == 1 || $local2UseBaseTenLogHelp == 1 || $globalUseBaseTenLogHelp == 1) { - $useBaseTenLogHelpString = "
  • Entering logarithms:
    In this question, use ln(x) for natural log, and log(x), logten(x) or log10(x) for the base 10 logarithm. Enter log base b as ln(x)/ln(b)

  • "; - } else { - $useBaseTenLogHelpString = "
  • Entering logarithms:
    Caution: In this question, use ln(x) or log(x) for natural log, and logten(x) or log10(x) for the base 10 logarithm. Enter log base b as ln(x)/ln(b)

  • "; - } - -if ($formulaHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - - $formulaHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (formulas)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpFormulas()';"); -} else { return $helpstring; } - - - - - - - - - -######################################### -# Fractions - -} elsif ($helptype =~ m/fraction/i) { - -if ($fractionHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$fractionHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (fractions)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpFractions()';"); -} else { return $helpstring; } - - - - - - - -########################################### -# Inequalities - -} elsif ($helptype =~ m/inequalit/i) { - -if ($inequalitiesHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$inequalitiesHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (inequalities)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpInequalities()';"); -} else { return $helpstring; } - - - - - - - - - - -####################################### -# Intervals - -} elsif ($helptype =~ m/interval/i) { - -if ($intervalHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$intervalHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (intervals)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpIntervals()';"); -} else { return $helpstring; } - - - - - - - -############################################## -# Limits - -} elsif ($helptype =~ m/limit/i) { - -if ($limitsHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$limitsHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (limits)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpLimits()';"); -} else { return $helpstring; } - - - - - - - - -############################################### -# Logarithms - -} elsif ($helptype =~ m/log/i) { - - my $local1UseBaseTenLogHelp = Context()->flags->get("useBaseTenLog"); - my $local2UseBaseTenLogHelp = main::Context()->flags->get("useBaseTenLog"); - my $globalUseBaseTenLogHelp = $useBaseTenLog; - - if ($local1UseBaseTenLogHelp == 1 || $local2UseBaseTenLogHelp == 1 || $globalUseBaseTenLogHelp == 1) { - $naturalLogHelpString = "
  • Entering natural logarithm: In this question, use ln(x)

  • "; - $basetenLogHelpString = "
  • Entering base 10 logarithm: In this question, use log(x), log10(x), or logten(x).

  • "; - } else { - $naturalLogHelpString = "
  • Caution: In this question log(x) is the same as ln(x)

  • Entering natural logarithm: In this question, use ln(x) or log(x)

  • "; - $basetenLogHelpString = "
  • Entering base 10 logarithm: In this question, use log10(x) or logten(x).

  • "; - } - -if ($logarithmsHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$logarithmsHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (logarithms)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpLogarithms()';"); -} else { return $helpstring; } - - - - - - - - - -########################################### -# Matrices - -} elsif ($helptype =~ m/matri/i) { - -if ($matricesHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$matricesHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (matrices)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpMatrices()';"); -} else { return $helpstring; } - - - - - - - - - - - -########################################## -# Numbers - -} elsif ($helptype =~ m/number/i) { - -if ($numberHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - - $numberHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (numbers)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpNumbers()';"); -} else { return $helpstring; } - - - - - - -####################################### -# Points - -} elsif ($helptype =~ m/point/i) { - -if ($pointsHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$pointsHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (points)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpPoints()';"); -} else { return $helpstring; } - - - - - - - - - -#################################### -# Units - -} elsif ($helptype =~ m/unit/i) { - -if ($unitsHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$unitsHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (units)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpUnits()';"); -} else { return $helpstring; } - - - - - - - -######################################## -# Vectors - -} elsif ($helptype =~ m/vector/i) { - -if ($vectorsHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$vectorsHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (vectors)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpVectors()';"); -} else { return $helpstring; } - - - - - - - - -######################################### -# Syntax (the catch all) - -#} elsif ($helptype =~ m/syntax/i) { -} else { - -if ($syntaxHelpExists != 1) { - -HEADER_TEXT(< - - -END_HEADER_TEXT - -$syntaxHelpExists = 1; -} - -if (!$customstring) { $helpstring = "help (syntax)"; } else { $helpstring = $customstring; } - -if ($main::displayMode ne "TeX") { - return htmlLink( "#", "$helpstring","onClick='return openhelpSyntax()';"); -} else { return $helpstring; } - -} - - - - - - -} # end AnswerFormatHelp - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/AnswerFormatHelpHTML.tgz b/OpenProblemLibrary/macros/FortLewis/AnswerFormatHelpHTML.tgz deleted file mode 100755 index bc7fbbae93..0000000000 Binary files a/OpenProblemLibrary/macros/FortLewis/AnswerFormatHelpHTML.tgz and /dev/null differ diff --git a/OpenProblemLibrary/macros/FortLewis/ConditionalHint.pl b/OpenProblemLibrary/macros/FortLewis/ConditionalHint.pl deleted file mode 100755 index e527f6bfa2..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/ConditionalHint.pl +++ /dev/null @@ -1,174 +0,0 @@ -sub _ConditionalHint_init {}; # don't reload this file - -=head1 ConditionalHint.pl - -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright � 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head2 NAME - -ConditionalHint.pl - Allows a hint to be revealed after a student -has entered an answer correctly. - -=head2 DESCRIPTION - -The subroutine ConditionalHint() allows a hint to be revealed -after a student has entered an answer correctly. It is useful -for multi-part questions in which a hint for answering one part -should not be revealed until a previous part has been answered -correctly. - -A subroutine IsAnswerCorrect() that returns 0 or 1 is also -provided. - -=head2 USAGE - -=head3 Synopsis of ConditionalHint - -loadMacros("ConditionalHint.pl"); - -$ans = Compute("x^2"); - -BEGIN_TEXT -Enter \( x^2 \) \{ ans_rule(20) \} -\{ -ConditionalHint( - ans_name=>$ans, - ans_number=>1, - html_hint=>"$BR ${BBOLD}Hint:${EBOLD} - \( \displaystyle \int x^2 \, dx = \frac{x^3}{3} + C.\) $BR", - tex_hint=>'', -); -\} -END_TEXT - -ANS( $ans->cmp() ); - - -=head3 Complete Working Example of ConditionalHint - -DOCUMENT(); - -loadMacros( -"PGstandard.pl", -"MathObjects.pl", -"ConditionalHint.pl", -); - -TEXT( beginproblem() ); - -################################### -# Setup - -Context("Numeric"); - -@answers = (); -$answers[1] = Compute("x^2"); -$answers[2] = Compute("4^3 / 3"); - -#$hint = IsAnswerCorrect(ans_name=>$answers[1],ans_number=>1); - -$myhint = ConditionalHint( -ans_name=>$answers[1], -ans_number=>1, -html_hint=>"$BR ${BBOLD}Hint:${EBOLD} \( \displaystyle \int x^2 \, dx = \frac{x^3}{3} + C.\) $BR" -); - - -################################## -# Main text - -Context()->texStrings; -BEGIN_TEXT -(a) Enter \( x^2 \). -\{ ans_rule(30) \} -$BR -$BR -(b) When you answer part (a) correctly, a hint will appear here with an integral formula. -$BR -$myhint -$BR -\( \displaystyle \int_0^4 \frac{x^3}{3} \, dx = \) -\{ans_rule(20)\} -END_TEXT -Context()->normalStrings; - - -################################## -# Answer evaluation - -$showPartialCorrectAnswers = 1; - -ANS( $answers[1]->cmp() ); -ANS( $answers[2]->cmp() ); - -COMMENT('When the first answer is correct, a hint appears with an integral formula (using ConditionalHint.pl).'); - -ENDDOCUMENT(); - -=head2 AUTHOR - -Paul Pearson - -=cut - - -sub ConditionalHint { - - my %options = ( - ans_name => '', - ans_number => '', - html_hint => '', - tex_hint => '', - @_ - ); - - my $showhint = $options{ans_name} ->cmp()->evaluate($inputs_ref->{ANS_NUM_TO_NAME( $options{ans_number} )})->{score}; - - my $hint; - - if ($showhint == 1) { - $hint = MODES(HTML=>$options{html_hint},TeX=>$options{tex_hint}); - } else { - $hint = ''; - } - - return $hint; - -} - - -sub IsAnswerCorrect { - - my %options = ( - ans_name => '', - ans_number => '', - @_ - ); - -# my $name_of_correct_answer = shift; -# my $answer_rule_number = shift; - -# return $name_of_correct_answer->cmp()->evaluate($inputs_ref->{ANS_NUM_TO_NAME($answer_rule_number)})->{score}; - - return $options{ans_name} ->cmp()->evaluate($inputs_ref->{ANS_NUM_TO_NAME( $options{ans_number} )})->{score}; - - -} - - -1; - diff --git a/OpenProblemLibrary/macros/FortLewis/JavaView.pl b/OpenProblemLibrary/macros/FortLewis/JavaView.pl deleted file mode 100755 index 881406c53a..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/JavaView.pl +++ /dev/null @@ -1,190 +0,0 @@ -sub _JavaView_init {}; # don't reload this file - -########################################################################### -# -# Macros for handling interactive 3D graphics via the LiveGraphics3D -# Java applet. The applet needs to be in the course html directory. -# (If it is in the system html area, you will need to change the -# default below or supply the jar option explicitly). -# -# The LiveGraphics3D applet displays a mathematica Graphics3D object -# that is stored in a .m file (or a compressed one). Use Mathematica -# to create one. (In the future, I plan to write a perl class that -# will create these for you on the fly. -- DPVC) -# -# The main routines are -# -# Live3Dfile load a data file -# Live3Ddata load raw Graphics3D data -# LiveGraphics3D access to all parameters -# - -# -# LiveGraphics3D(options) -# -# Options are from: -# -# file => name name of .m file to load -# -# archive => name name of a .zip file to load -# -# input => 3Ddata string containing Graphics3D data to -# be displayed by the applet -# -# size => [w,h] width and height of applet -# -# vars => [vars] hash of variables to pass as independent -# variables to the applet, togther with -# their initial values -# e.g., vars => [a=>1,b=>1] -# -# depend => [list] list of dependent variables to pass to -# the applet with their replacement strings -# (see LiveGraphics3D documentation) -# -# jar => URL where to find the live.jar file -# -# background=>"#RRGGBB" the background color to use (default is white) -# -# scale => n scaling factor for applet (default is 1.) -# -# image => file a file containing an image to use in TeX mode -# or when Java is disabled -# -# tex_size => ratio a scaling factor for the TeX image (as a portion -# of the line width). -# 1000 is 100%, 500 is 50%, etc. -# -# tex_center => 0 or 1 center the image in TeX mode or not -# -# parameters => [params] hash of additional parameters to pass to -# the JavaView applet. For example, -# parameters => [VISIBLE_FACES => "FRONT"] -# -# CAS => maple, Option when including plot data as an -# ASCII string. -# -# CAS can be either maple or mathematica -# -# If CAS => maple, then the JavaView applet -# expects maple plot geometry as an ASCII -# string "Plot3D( )" passed to it via -# input => or file => above. -# -# If CAS => mathematica, then the JavaView -# applet expects a mathematica graphics -# object as an ASCII string "Graphics3D[ ]" -# passed to it via input => or file => above. - -sub JavaView { - my %options = ( - size => [250,250], - jar => "${htmlURL}jv_lite.zip", # "${htmlURL}live.jar", - codebase => "${htmlURL}", # findAppletCodebase("jv_lite.jar"), - background => "#FFFFFF", - scale => 1., - tex_size => 500, - tex_center => 0, - CAS => maple, # CAS = Computer Algebra System - @_ - ); - my $out = ""; my $p; my %pval; - my $ratio = $options{tex_size} * (.001); - - if ($main::displayMode eq "TeX") { - # - # In TeX mode, include the image, if there is one, or - # else give the user a message about using it on line - # - if ($options{image}) { - $out = "\\includegraphics[width=$ratio\\linewidth]{$options{image}}"; - $out = "\\centerline{$out}" if $options{tex_center}; - $out .= "\n"; - } else { - $out = "\\vbox{ - \\hbox{[ This image is created by} - \\hbox{\\quad an interactive applet;} - \\hbox{you must view it on line ]} - }"; - } - } else { - my ($w,$h) = @{$options{size}}; - $out .= $bHTML if ($main::displayMode eq "Latex2HTML"); - # - # Put the applet in a table - # - $out .= qq{\n\n}; - $out .= qq{\n
    }; - # - # start the applet - # - $out .= qq{ - - - - - - }; - # - # include the file or data - # - $out .= qq{\n} - if ($options{archive}); - $out .= qq{\n} - if ($options{file}); - $out .= qq{\n} - if ($options{input}); - # - # include any extra Live3D parameters - # - if ($options{parameters}) { - my %pval = @{$options{parameters}}; - foreach $p (lex_sort(keys(%pval))) { - $out .= qq{\n}; - } - } - # - # End the applet and table - # - $out .= qq{} if ($options{image}); - $out .= "[Enable Java to make this image interactive]
    "; - $out .= "
    "; - $out .= "
    \n"; - $out .= $eHTML if ($main::displayMode eq "Latex2HTML"); - } - - return $out; -} - -# -# Syntactic sugar to make it easier to pass files and data to -# LiveGraphics3D. -# -sub JVfile { - my $file = shift; - JavaView(file => $file, @_); -} - -# -# Syntactic sugar to make it easier to pass raw Graohics3D data -# to LiveGraphics3D. -# -sub JVdata { - my $data = shift; - JavaView(input => $data, @_); -} - - -# -# A message you can use for a caption under a graph -# -$LIVEMESSAGE = MODES( - TeX => '', - Latex2HTML => - $BCENTER.$BSMALL."Drag the surface to rotate it".$ESMALL.$ECENTER, - HTML => $BCENTER.$BSMALL."Drag the surface to rotate it".$ESMALL.$ECENTER -); - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/JavaViewRectangularPlot3D.pl b/OpenProblemLibrary/macros/FortLewis/JavaViewRectangularPlot3D.pl deleted file mode 100755 index 72f1541ee7..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/JavaViewRectangularPlot3D.pl +++ /dev/null @@ -1,551 +0,0 @@ -sub _JavaViewRectangularPlot3D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","JavaView.pl"); - -########################################################################### -# -# JavaViewRectangularPlot3D.pl provides two macros for creating an -# interactive plot of a function of two variables z = f(x,y) in -# Rectangular (Cartesian) coordinates via the JavaView applet. -# The routine RectangularDomainPlot3D() takes a MathObject Formula of -# two variables defined over a rectangular domain and some plot options -# as input and returns a string of plot data that can be displayed -# using the JVdata() routine of the JavaView.pl macro. -# The routine AnnularDomainPlot3D works similarly for a function -# z = f(x,y) over an annular domain specified in polar by -# rmin < r < rmax and tmin < theta < tmax. -# -# JavaViewRectangularPlot3D.pl automatically loads -# MathObjects.pl and JavaView.pl. -# -########################################################################### -# -# The first routine is -# -# RectangularPlot3DRectangularDomain -# -# -# Usage: RectangularPlot3DRectangularDomain(options); -# -# Options are: -# -# function => $f, $f is a MathObjects Formula -# For example, in the setup section define -# -# Context("Numeric"); -# Context()->variables->add(s=>"Real",t=>"Real"); -# $a = random(1,3,1); -# $f = Formula("$a*s^2-2*t"); # use double quotes! -# -# before calling RectangularPlot3DRectangularDomain() -# -# xvar => "s", independent variable name, default "x" -# yvar => "t", independent variable name, default "y" -# -# xmin => -3, domain for xvar -# xmax => 3, -# -# ymin => -3, domain for yvar -# ymax => 3, -# -# xsamples => 20, deltax = (xmax - xmin) / xsamples -# ysamples => 20, deltay = (ymax - ymin) / ysamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "S", Capital letters may be easier to read -# yaxislabel => "T", -# zaxislabel => "Z", -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -####################################################################################### -####################################################################################### -# -# The second routine is -# -# RectangularPlot3DAnnularDomain create a plotstring from a function -# -# -# Usage: RectangularPlot3DAnnularDomain(options); -# -# Options are: -# -# function => $f, $f is a MathObjects Formula -# For example, in the setup section define -# -# Context("Numeric"); -# Context()->variables->add(y=>"Real",r=>"Real",t=>"Real"); -# $a = random(1,3,1); -# $f = Formula("$a*e^(- x^2 - y^2)"); # use double quotes! -# -# before calling RectangularPlot3DAnnularDomain() -# -# xvar => "x", independent variable name, default "x" -# yvar => "y", independent variable name, default "y" -# -# rvar => "r", independent variable name, default "r" -# tvar => "t", independent variable name, default "t" (for theta) -# -# rmin => -3, domain for rvar -# rmax => 3, -# -# tmin => -3, domain for tvar -# tmax => 3, -# -# rsamples => 20, deltar = (rmax - rmin) / rsamples -# tsamples => 20, deltat = (tmax - tmin) / tsamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "X", Capital letters may be easier to read -# yaxislabel => "Y", -# zaxislabel => "Z", -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -# Happy 3D graphing! - Paul Pearson -# - - - - - - - -$beginplot = "PLOT3D("; -$endplot = ")"; - - -########################################### -########################################### -# Begin RectangularPlot3DRectangularDomain - -sub RectangularPlot3DRectangularDomain { - -########################################### -# -# Set default options -# - -my %options = ( -function => Formula("1"), -xvar => 'x', -yvar => 'y', -xmin => -3, -xmax => 3, -ymin => -3, -ymax => 3, -xsamples => 20, -ysamples => 20, -axes => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -outputtype => 4, -@_ -); - - -############################################ -# -# Reset to Context("Numeric") just to be -# sure that everything will work properly. -# - -#Context("Numeric"); -#Context()->variables->are($options{xvar}=>"Real",$options{yvar}=>"Real"); - - -my $fsubroutine; -$options{function}->perlFunction('fsubroutine',["$options{xvar}","$options{yvar}"]); - -###################################################### -# -# Generate a plot data array z, which has two indices -# - -my $dx = ($options{xmax} - $options{xmin}) / $options{xsamples}; -my $dy = ($options{ymax} - $options{ymin}) / $options{ysamples}; - -my $x; -my $y; -my $z; -my $zmin = floor(sprintf("%.3f", fsubroutine($options{xmin},$options{ymin})->value )); -my $zmax = ceil(sprintf("%.3f", fsubroutine($options{xmin},$options{ymin})->value )); - - -foreach my $i (0..$options{xsamples}) { - $x[$i] = $options{xmin} + $i * $dx; - foreach my $j (0..$options{ysamples}) { - $y[$j] = $options{ymin} + $j * $dy; - # Use sprintf to round to three decimal places - $z[$i][$j] = sprintf("%.3f", fsubroutine($x[$i],$y[$j])->value ); - $y[$j] = sprintf("%.3f",$y[$j]); - if ($z[$i][$j]<$zmin) { $zmin = sprintf("%.2f",$z[$i][$j]); } - if ($z[$i][$j]>$zmax) { $zmax = sprintf("%.2f",$z[$i][$j]); } - } - $x[$i] = sprintf("%.3f",$x[$i]); -} - - - -########################################################################### -# -# Generate a plotstring from the plot data arrays x, y, z. -# -# The plotstring is a list of polygons -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstring, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the polygons in the plotstring -# - -my $plotstring = "MESH(["; - -foreach my $i (0..$options{xsamples}) { - $plotstring = $plotstring . "["; - foreach my $j (0..$options{ysamples}) { - - $plotstring = $plotstring . "[$x[$i],$y[$j],$z[$i][$j]]"; - - if ($j<$options{ysamples}) { $plotstring = $plotstring . ","; } - } - $plotstring = $plotstring . "]"; - if ($i<$options{xsamples}) { $plotstring = $plotstring . ","; } -} - -$plotstring = $plotstring . "])"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axes}==1) ) { - -$xmax = $options{xmax}; -$xmin = $options{xmin}; -$ymax = $options{ymax}; -$ymin = $options{ymin}; - -my $axisx; my $naxisx; -my $axisy; my $naxisy; -my $axisz; my $naxisz; - -if ($xmin*$xmax<0) { $axisx=0; } else { $axisx=$xmin; } -if ($ymin*$ymax<0) { $axisy=0; } else { $axisy=$ymin; } -if ($zmin*$zmax<0) { $axisz=0; } else { $axisz=$zmin; } - -$naxisx = $axisx-($xmax - $xmin)/50; -$naxisy = $axisy+($ymax - $ymin)/25; -$naxisz = $axisz-($zmax - $zmin)/50; - -foreach my $i (0..5) { - $xhere = sprintf( "%.1f", ($xmin + $i*($xmax - $xmin)/5) ); - $yhere = sprintf( "%.1f", ($ymin + $i*($ymax - $ymin)/5) ); - $zhere = sprintf( "%.1f", ($zmin + $i*($zmax - $zmin)/5) ); - - $plotoptions = $plotoptions . - "TEXT([$xhere,$naxisy,$naxisz],\'$xhere\')," . - "TEXT([$naxisx,$yhere,$naxisz],\'$yhere\')," . - "TEXT([$naxisx,$naxisy,$zhere],\'$zhere\'),"; -} - -my $xexpand = ($xmax - $xmin)/20; -my $yexpand = ($ymax - $ymin)/20; -my $zexpand = ($zmax - $zmin)/20; - -$xmax = $xmax + $xexpand; -$xmin = $xmin - $xexpand; -my $xlabel = $xmax+$xexpand; - -$ymax = $ymax + $yexpand; -$ymin = $ymin - $yexpand; -my $ylabel = $ymax+$yexpand; - -$zmax = $zmax + $zexpand; -$zmin = $zmin - $zexpand; -my $zlabel = $zmax+$zexpand; - -$plotoptions = $plotoptions . -"TEXT([$xlabel,$axisy,$axisz],\'X\')," . -"TEXT([$axisx,$ylabel,$axisz],\'Y\')," . -"TEXT([$axisx,$axisy,$zlabel],\'Z\')," . -"POLYGONS([[$xmin,$axisy,$axisz],[$xmax,$axisy,$axisz]])," . -"POLYGONS([[$axisx,$ymin,$axisz],[$axisx,$ymax,$axisz]])," . -"POLYGONS([[$axisx,$axisy,$zmin],[$axisx,$axisy,$zmax]])"; - -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstring; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstring . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstring . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End RectangularPlot3DRectangularDomain -############################################## -############################################## - - - - - - - - - - - -############################################# -############################################# -# Begin RectangularPlot3DAnnularDomain - - -sub RectangularPlot3DAnnularDomain { - -############################################# -# -# Set default options -# - -my %options = ( -function => Formula("1"), -xvar => "x", -yvar => "y", -rvar => "r", -tvar => "t", -rmin => 0.001, -rmax => 3, -tmin => 0, -tmax => 6.28, -rsamples => 20, -tsamples => 20, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -outputtype => 4, -@_ -); - - -############################################ -# -# Reset to Context("Numeric") just to be -# sure that everything will work properly. -# - -#Context("Numeric"); -#Context()->variables->are( -#$options{xvar}=>"Real", -#$options{yvar}=>"Real", -#$options{rvar}=>"Real", -#$options{tvar}=>"Real" -#); - -my $fsubroutine; -$options{function}->perlFunction('fsubroutine',["$options{xvar}","$options{yvar}"]); - - -###################################################### -# -# Generate a plot data array, which has two indices -# - -my $dr = ($options{rmax} - $options{rmin}) / $options{rsamples}; -my $dt = ($options{tmax} - $options{tmin}) / $options{tsamples}; - -my $t; -my $r; - -foreach my $i (0..$options{rsamples}) { $r[$i] = $options{rmin} + $i * $dr; } -foreach my $j (0..$options{tsamples}) { $t[$j] = $options{tmin} + $j * $dt; } - -my $x; -my $y; -my $z; - -my $zmin = floor(sprintf("%.3f", fsubroutine($r[0]*cos($t[0]),$r[0]*sin($t[0]))->value )); -my $zmax = ceil(sprintf("%.3f", fsubroutine($r[0]*cos($t[0]),$r[0]*sin($t[0]))->value )); - -foreach my $i (0..$options{tsamples}) { - foreach my $j (0..$options{rsamples}) { - $x[$i][$j] = $r[$j] * cos($t[$i]); - $y[$i][$j] = $r[$j] * sin($t[$i]); - # Use sprintf to round to three decimal places - $z[$i][$j] = sprintf("%.3f", fsubroutine($x[$i][$j],$y[$i][$j])->value ); - $x[$i][$j] = sprintf("%.3f",$x[$i][$j]); - $y[$i][$j] = sprintf("%.3f",$y[$i][$j]); - if ($z[$i][$j]<$zmin) { $zmin = sprintf("%.2f",$z[$i][$j]); } - if ($z[$i][$j]>$zmax) { $zmax = sprintf("%.2f",$z[$i][$j]); } - } -} - - -########################################################################### -# -# Generate a plotstring from the plot data arrays x, y, z. -# -# The plotstring is a list of polygons that -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstring, see -# http://www.maplesoft.com/support/help/AddOns/view.aspx?path=plot/structure -# -########################################### -# -# Generate the polygons in the plotstring -# - -my $plotstring = "MESH(["; - -foreach my $i (0..$options{tsamples}) { - $plotstring = $plotstring . "["; - foreach my $j (0..$options{rsamples}) { - - $plotstring = $plotstring . "[$x[$i][$j],$y[$i][$j],$z[$i][$j]]"; - - if ($j<$options{rsamples}) { $plotstring = $plotstring . ","; } - } - $plotstring = $plotstring . "]"; - if ($i<$options{tsamples}) { $plotstring = $plotstring . ","; } -} - -$plotstring = $plotstring . "])"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axes}==1) ) { - -$xmax = $options{rmax}; -$xmin = - $options{rmin}; -$ymax = $options{rmax}; -$ymin = - $options{rmin}; - -my $axisx; my $naxisx; -my $axisy; my $naxisy; -my $axisz; my $naxisz; - -if ($xmin*$xmax<0) { $axisx=0; } else { $axisx=$xmin; } -if ($ymin*$ymax<0) { $axisy=0; } else { $axisy=$ymin; } -if ($zmin*$zmax<0) { $axisz=0; } else { $axisz=$zmin; } - -$naxisx = $axisx-($xmax - $xmin)/50; -$naxisy = $axisy+($ymax - $ymin)/25; -$naxisz = $axisz-($zmax - $zmin)/50; - -foreach my $i (0..5) { - $xhere = sprintf( "%.1f", ($xmin + $i*($xmax - $xmin)/5) ); - $yhere = sprintf( "%.1f", ($ymin + $i*($ymax - $ymin)/5) ); - $zhere = sprintf( "%.1f", ($zmin + $i*($zmax - $zmin)/5) ); - - $plotoptions = $plotoptions . - "TEXT([$xhere,$naxisy,$naxisz],\'$xhere\')," . - "TEXT([$naxisx,$yhere,$naxisz],\'$yhere\')," . - "TEXT([$naxisx,$naxisy,$zhere],\'$zhere\'),"; -} - -my $xexpand = ($xmax - $xmin)/20; -my $yexpand = ($ymax - $ymin)/20; -my $zexpand = ($zmax - $zmin)/20; - -$xmax = $xmax + $xexpand; -$xmin = $xmin - $xexpand; -my $xlabel = $xmax+$xexpand; - -$ymax = $ymax + $yexpand; -$ymin = $ymin - $yexpand; -my $ylabel = $ymax+$yexpand; - -$zmax = $zmax + $zexpand; -$zmin = $zmin - $zexpand; -my $zlabel = $zmax+$zexpand; - -$plotoptions = $plotoptions . -"TEXT([$xlabel,$axisy,$axisz],\'X\')," . -"TEXT([$axisx,$ylabel,$axisz],\'Y\')," . -"TEXT([$axisx,$axisy,$zlabel],\'Z\')," . -"POLYGONS([[$xmin,$axisy,$axisz],[$xmax,$axisy,$axisz]])," . -"POLYGONS([[$axisx,$ymin,$axisz],[$axisx,$ymax,$axisz]])," . -"POLYGONS([[$axisx,$axisy,$zmin],[$axisx,$axisy,$zmax]])"; - -} - - - -#if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { -# $plotoptions = $plotoptions . "Axes->True,AxesLabel->" . -# "{$options{xaxislabel},$options{yaxislabel},$options{zaxislabel}}"; -#} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstring; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstring . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstring . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End RectangularPlot3DAnnularDomain -##################################################### -##################################################### - - - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsCylindricalPlot3D.pl b/OpenProblemLibrary/macros/FortLewis/LiveGraphicsCylindricalPlot3D.pl deleted file mode 100755 index 6147975aa5..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsCylindricalPlot3D.pl +++ /dev/null @@ -1,221 +0,0 @@ -sub _LiveGraphicsCylindricalPlot3D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","LiveGraphics3D.pl"); - -########################################################################### -# -# LiveGraphicsCylindricalPlot3D.pl provides a macros for creating an -# interactive plot of a function of two variables z = f(r,t) -# (where r is the radius and t=theta is the angle) via the LiveGraphics3D -# Java applet. The routine CylindricalPlot3D() takes a MathObject Formula -# of two variables defined over an annular domain and some plot options -# as input and returns a string of plot data that can be displayed -# using the Live3Ddata() routine of the LiveGraphics3D.pl macro. -# -# LiveGraphicsCylindricalPlot3D.pl automatically loads -# MathObjects.pl and LiveGraphics3D.pl. -# -########################################################################### -# -# The main routine is -# -# CylindricalPlot3D -# -# -# Usage: CylindricalPlot3D(options) -# -# Options are: -# -# function => $f, $f is a MathObjects Formula -# For example, in the setup section define -# -# Context("Numeric"); -# Context()->variables->add(r=>"Real",t=>"Real"); -# $a = random(1,3,1); -# $f = Formula("$a*r + t"); # use double quotes! -# -# before calling CylindricalPlot3D() -# -# rvar => "r", independent variable name, default "r" -# tvar => "t", independent variable name, default "t" -# -# rmin => 0, domain for rvar -# rmax => 3, -# -# tmin => -3, domain for tvar -# tmax => 3, -# -# rsamples => 20, deltar = (rmax - rmin) / rsamples -# tsamples => 20, deltat = (tmax - tmin) / tsamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "X", Capital letters may be easier to read -# yaxislabel => "Y", -# zaxislabel => "Z", -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -# Happy 3D graphing! -Paul Pearson -# -####################################################################################### - - - - - - - -$beginplot = "Graphics3D["; -$endplot = "]"; - -############################################# -# Begin CylindricalPlot3D - - -sub CylindricalPlot3D { - -############################################# -# -# Set default options -# - -my %options = ( -function => Formula("1"), -rvar => "r", -tvar => "t", -rmin => 0.001, -rmax => 3, -tmin => 0, -tmax => 6.28, -rsamples => 20, -tsamples => 20, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -outputtype => 4, -@_ -); - - -my $fsubroutine; -$options{function}->perlFunction('fsubroutine',["$options{rvar}","$options{tvar}"]); - - -###################################################### -# -# Generate a plotdata array, which has two indices -# - -my $rsamples1 = $options{rsamples} - 1; -my $tsamples1 = $options{tsamples} - 1; - -my $dr = ($options{rmax} - $options{rmin}) / $options{rsamples}; -my $dt = ($options{tmax} - $options{tmin}) / $options{tsamples}; - -my $r; -my $t; - -my $x; -my $y; -my $z; - -foreach my $i (0..$options{tsamples}) { - $t[$i] = $options{tmin} + $i * $dt; - foreach my $j (0..$options{rsamples}) { - $r[$j] = $options{rmin} + $j * $dr; - $x[$i][$j] = $r[$j] * cos($t[$i]); - $y[$i][$j] = $r[$j] * sin($t[$i]); - $z[$i][$j] = sprintf("%.3f", fsubroutine($r[$j],$t[$i])->value ); - $x[$i][$j] = sprintf("%.3f",$x[$i][$j]); - $y[$i][$j] = sprintf("%.3f",$y[$i][$j]); - } -} - - - -########################################################################### -# -# Generate a plotstring from the plotdata. -# -# The plotstring is a list of polygons that -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstring, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the polygons in the plotstring -# - -my $plotstructure = "{"; - -foreach my $i (0..$tsamples1) { - foreach my $j (0..$rsamples1) { - - $plotstructure = $plotstructure . "Polygon[{" . - "{$x[$i][$j],$y[$i][$j],$z[$i][$j]}," . - "{$x[$i+1][$j],$y[$i+1][$j],$z[$i+1][$j]}," . - "{$x[$i+1][$j+1],$y[$i+1][$j+1],$z[$i+1][$j+1]}," . - "{$x[$i][$j+1],$y[$i][$j+1],$z[$i][$j+1]}" . - "}]"; - - if (($i<$tsamples1) || ($j<$rsamples1)) { - $plotstructure = $plotstructure . "," - } - - } -} - -$plotstructure = $plotstructure . "}"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { - $plotoptions = $plotoptions . "Axes->True,AxesLabel->" . - "{$options{xaxislabel},$options{yaxislabel},$options{zaxislabel}}"; -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstructure; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstructure . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstructure . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End CylindricalPlot3D -##################################################### -##################################################### - - - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsParametricCurve3D.pl b/OpenProblemLibrary/macros/FortLewis/LiveGraphicsParametricCurve3D.pl deleted file mode 100755 index 15929a1b8c..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsParametricCurve3D.pl +++ /dev/null @@ -1,209 +0,0 @@ -sub _LiveGraphicsParametricCurve3D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","LiveGraphics3D.pl"); - -########################################################################### -# -# LiveGraphicsParametricCurve3D.pl provides a macros for creating an -# interactive plot of a vector field via the LiveGraphics3D Java applet. -# The routine ParametricCurve3D() takes three MathObject Formulas of -# 3 variables as input and returns a string of plot data that can be -# displayed using the Live3Ddata() routine of the LiveGraphics3D.pl macro. -# -# LiveGraphicsParametricCurve3D.pl automatically loads -# MathObjects.pl and LiveGraphics3D.pl. -# -########################################################################### -# -# The main routine is -# -# ParametricCurve3D() -# -# -# Usage: ParametricCurve3D(options); -# -# Options are: -# -# Fx => Formula("t*cos(t)"), F = < Fx, Fy, Fz > where Fx, Fy, Fz are each -# Fy => Formula("t*sin(t)"), functions of tvar -# Fz => Formula("t"), -# -# tvar => "t", independent variable name, default "t" -# tmin => -3, domain for tvar -# tmax => 3, -# tsamples => 3, deltat = (tmax - tmin) / tsamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "X", Capital letters may be easier to read -# yaxislabel => "Y", -# zaxislabel => "Z", -# -# orientation => 0, do not show any orientation arrows -# => 1, show only one arrow in the middle -# => 2, make the curve entirely of arrows -# -# curvecolor => "RGBColor[1.0,0.0,0.0]", -# curvethickness => 0.001, -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -# Happy 3D graphing! -Paul Pearson -# -####################################################################################### - - -$beginplot = "Graphics3D["; -$endplot = "]"; - - -########################################### -########################################### -# Begin ParametricCurve3D - -sub ParametricCurve3D { - -########################################### -# -# Set default options -# - -my %options = ( -Fx => Formula("1"), -Fy => Formula("1"), -Fz => Formula("1"), -tvar => 't', -tmin => -3, -tmax => 3, -tsamples => 20, -orientation => 0, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -curvecolor => "RGBColor[1.0,0.0,0.0]", -curvethickness => 0.001, -outputtype => 4, -@_ -); - - -my $Fxsubroutine; -my $Fysubroutine; -my $Fzsubroutine; - -$options{Fx}->perlFunction('Fxsubroutine',["$options{tvar}"]); -$options{Fy}->perlFunction('Fysubroutine',["$options{tvar}"]); -$options{Fz}->perlFunction('Fzsubroutine',["$options{tvar}"]); - - - -###################################################### -# -# Generate plot data -# - -my $dt = ($options{tmax} - $options{tmin}) / $options{tsamples}; - -my $t; - -# The curve data -foreach my $i (0..$options{tsamples}) { - $t[$i] = $options{tmin} + $i * $dt; - - $FX[$i] = sprintf("%.3f", (Fxsubroutine($t[$i])->value) ); - $FY[$i] = sprintf("%.3f", (Fysubroutine($t[$i])->value) ); - $FZ[$i] = sprintf("%.3f", (Fzsubroutine($t[$i])->value) ); - -} - -if ($options{orientation}>0) { -# -# The arrow head data -# -my $tmidindex = sprintf("%.0f", $options{tsamples}/2 ); - -} - - -########################################################################### -# -# Generate plotstructure from the plotdata. -# -# The plotstucture is a list of arrows (made of lines) that -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstructure, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the line segments in the plotstructure -# - -my $plotstructure = "{$options{curvecolor},Thickness[$options{curvethickness}],"; - -my $tsamples1 = $options{tsamples} - 1; - -foreach my $i (0..$tsamples1) { - - $plotstructure = $plotstructure . - "Line[{" . - "{$FX[$i],$FY[$i],$FZ[$i]}," . - "{$FX[$i+1],$FY[$i+1],$FZ[$i+1]}" . - "}]"; - - if ($i<$tsamples1) { $plotstructure = $plotstructure . "," } - -} - -$plotstructure = $plotstructure . "}"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { - $plotoptions = $plotoptions . "Axes->True,AxesLabel->" . - "{$options{xaxislabel},$options{yaxislabel},$options{zaxislabel}}"; -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstructure; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstructure . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstructure . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End ParametricCurve3D -############################################## -############################################## - - - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsParametricSurface3D.pl b/OpenProblemLibrary/macros/FortLewis/LiveGraphicsParametricSurface3D.pl deleted file mode 100755 index dc7166fff1..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsParametricSurface3D.pl +++ /dev/null @@ -1,270 +0,0 @@ -sub _LiveGraphicsParametricSurface3D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","LiveGraphics3D.pl"); - -########################################################################### -# -# LiveGraphicsParametricSurface3D.pl provides a macro for creating an -# interactive plot of a parametric surface via the LiveGraphics3D Java applet. -# The routine ParametricSurface3D() takes three MathObject Formulas of -# 2 variables as input and returns a string of plot data that can be -# displayed using the Live3Ddata() routine of the LiveGraphics3D.pl macro. -# -# LiveGraphicsParametricSurface3D.pl automatically loads -# MathObjects.pl and LiveGraphics3D.pl. -# -########################################################################### -# -# The main routine is -# -# ParametricSurface3D() -# -# -# Usage: ParametricSurface3D(options); -# -# Options are: -# -# Fx => Formula("cos(u)*cos(v)"), x-coordinate function -# Fy => Formula("sin(u)*cos(v)"), y-coordinate function -# Fz => Formula("sin(v)"), z-coordinate function -# F(u,v) = < Fx, Fy, Fz > -# = < Fx(u,v), Fy(u,v), Fz(u,v) > -# -# uvar => "u", parameter name, default "u" -# vvar => "v", parameter name, default "v" -# -# umin => -3, domain for uvar -# umax => 3, -# -# vmin => -3, domain for vvar -# vmax => 3, -# -# usamples => 3, deltau = (umax - umin) / usamples -# vsamples => 3, deltav = (vmax - vmin) / vsamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "X", Capital letters may be easier to read -# yaxislabel => "Y", -# zaxislabel => "Z", -# -# edges => 0, 1 displays edges of polygons, 0 hides them -# edgecolor => "RGBColor[0.2,0.2,0.2]", -# edgethickness => "Thickness[0.001]", -# -# mesh => 0, 1 displays open mesh, 0 displays filled polygons -# meshcolor => "RGBColor[0.7,0.7,0.7]", three values between 0 and 1 -# meshthickness => 0.001, -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -# Happy 3D graphing! -Paul Pearson -# -####################################################################################### - - -$beginplot = "Graphics3D["; -$endplot = "]"; - - -########################################### -########################################### -# Begin ParametricSurface3D - -sub ParametricSurface3D { - -########################################### -# -# Set default options -# - -my %options = ( -Fx => Formula("1"), -Fy => Formula("1"), -Fz => Formula("1"), -uvar => 'u', -vvar => 'v', -umin => -3, -umax => 3, -vmin => -3, -vmax => 3, -usamples => 20, -vsamples => 20, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -edges => 0, -edgecolor => "RGBColor[0.2,0.2,0.2]", -edgethickness => "Thickness[0.001]", -mesh => 0, -meshcolor => "RGBColor[0.7,0.7,0.7]", -meshthickness => 0.001, -outputtype => 4, -@_ -); - - -my $Fxsubroutine; -my $Fysubroutine; -my $Fzsubroutine; - -$options{Fx}->perlFunction('Fxsubroutine',["$options{uvar}","$options{vvar}"]); -$options{Fy}->perlFunction('Fysubroutine',["$options{uvar}","$options{vvar}"]); -$options{Fz}->perlFunction('Fzsubroutine',["$options{uvar}","$options{vvar}"]); - - - -###################################################### -# -# Generate plot data -# - -my $du = ($options{umax} - $options{umin}) / $options{usamples}; -my $dv = ($options{vmax} - $options{vmin}) / $options{vsamples}; - -my $u; -my $v; - -foreach my $i (0..$options{usamples}) { - $u[$i] = $options{umin} + $i * $du; - foreach my $j (0..$options{vsamples}) { - $v[$j] = $options{vmin} + $j * $dv; - - $FX[$i][$j] = sprintf("%.3f", (Fxsubroutine($u[$i],$v[$j])->value) ); - $FY[$i][$j] = sprintf("%.3f", (Fysubroutine($u[$i],$v[$j])->value) ); - $FZ[$i][$j] = sprintf("%.3f", (Fzsubroutine($u[$i],$v[$j])->value) ); - - $u[$i] = sprintf("%.3f",$u[$i]); - $v[$j] = sprintf("%.3f",$v[$j]); - } -} - - - -########################################################################### -# -# Generate plotstructure from the plotdata. -# -# The plotstucture is a list of arrows (made of lines) that -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstructure, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the polygons in the plotstructure -# - -my $plotstructure = "{"; - -if ($options{edges}==0 && $options{mesh}==0) { - $plotstructure = $plotstructure . "EdgeForm[],"; -} elsif ($options{edges}==1 && $options{mesh}==0) { - $plotstructure = $plotstructure . "EdgeForm[{$options{edgecolor},$options{edgethickness}}],"; -} - -if ($options{mesh}==1) { - $plotstructure = $plotstructure . "$options{meshcolor},Thickness[$options{meshthickness}],"; -} - -my $usamples1 = $options{usamples} - 1; -my $vsamples1 = $options{vsamples} - 1; - -if ($options{mesh}==0) { - -foreach my $i (0..$usamples1) { - foreach my $j (0..$vsamples1) { - - $plotstructure = $plotstructure . "Polygon[{" . - "{$FX[$i][$j],$FY[$i][$j],$FZ[$i][$j]}," . - "{$FX[$i+1][$j],$FY[$i+1][$j],$FZ[$i+1][$j]}," . - "{$FX[$i+1][$j+1],$FY[$i+1][$j+1],$FZ[$i+1][$j+1]}," . - "{$FX[$i][$j+1],$FY[$i][$j+1],$FZ[$i][$j+1]}" . - "}]"; - - if (($i<$usamples1) || ($j<$vsamples1)) { - $plotstructure = $plotstructure . "," - } - - } -} - -# end mesh == 0 -} else { -# begin mesh == 1 - -foreach my $i (0..$usamples1) { - foreach my $j (0..$vsamples1) { - - # this could be made more efficient - $plotstructure = $plotstructure . - "Line[{" . - "{$FX[$i][$j],$FY[$i][$j],$FZ[$i][$j]}," . - "{$FX[$i+1][$j],$FY[$i+1][$j],$FZ[$i+1][$j]}," . - "{$FX[$i+1][$j+1],$FY[$i+1][$j+1],$FZ[$i+1][$j+1]}," . - "{$FX[$i][$j+1],$FY[$i][$j+1],$FZ[$i][$j+1]}," . - "{$FX[$i][$j],$FY[$i][$j],$FZ[$i][$j]}" . - "}]"; - - if (($i<$usamples1) || ($j<$vsamples1)) { - $plotstructure = $plotstructure . "," - } - - } -} - - -} # end mesh == 1 - -$plotstructure = $plotstructure . "}"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { - $plotoptions = $plotoptions . "Axes->True,AxesLabel->" . - "{$options{xaxislabel},$options{yaxislabel},$options{zaxislabel}}"; -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstructure; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstructure . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstructure . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End ParametricSurface3D -############################################## -############################################## - - - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsRectangularPlot3D.pl b/OpenProblemLibrary/macros/FortLewis/LiveGraphicsRectangularPlot3D.pl deleted file mode 100755 index 4d0f47a25d..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsRectangularPlot3D.pl +++ /dev/null @@ -1,450 +0,0 @@ -sub _LiveGraphicsRectangularPlot3D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","LiveGraphics3D.pl"); - -########################################################################### -# -# LiveGraphicsRectangularPlot3D.pl provides two macros for creating an -# interactive plot of a function of two variables z = f(x,y) in -# Rectangular (Cartesian) coordinates via the LiveGraphics3D Java applet. -# The routine RectangularDomainPlot3D() takes a MathObject Formula of -# two variables defined over a rectangular domain and some plot options -# as input and returns a string of plot data that can be displayed -# using the Live3Ddata() routine of the LiveGraphics3D.pl macro. -# The routine AnnularDomainPlot3D works similarly for a function -# z = f(x,y) over an annular domain specified in polar by -# rmin < r < rmax and tmin < theta < tmax. -# -# LiveGraphicsRectangularPlot3D.pl automatically loads -# MathObjects.pl and LiveGraphics3D.pl. -# -########################################################################### -# -# The first routine is -# -# RectangularPlot3DRectangularDomain -# -# -# Usage: RectangularPlot3DRectangularDomain(options); -# -# Options are: -# -# function => $f, $f is a MathObjects Formula -# For example, in the setup section define -# -# Context("Numeric"); -# Context()->variables->add(s=>"Real",t=>"Real"); -# $a = random(1,3,1); -# $f = Formula("$a*s^2-2*t"); # use double quotes! -# -# before calling RectangularPlot3DRectangularDomain() -# -# xvar => "s", independent variable name, default "x" -# yvar => "t", independent variable name, default "y" -# -# xmin => -3, domain for xvar -# xmax => 3, -# -# ymin => -3, domain for yvar -# ymax => 3, -# -# xsamples => 20, deltax = (xmax - xmin) / xsamples -# ysamples => 20, deltay = (ymax - ymin) / ysamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "S", Capital letters may be easier to read -# yaxislabel => "T", -# zaxislabel => "Z", -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -####################################################################################### -####################################################################################### -# -# The second routine is -# -# RectangularPlot3DAnnularDomain create a plotstring from a function -# -# -# Usage: RectangularPlot3DAnnularDomain(options); -# -# Options are: -# -# function => $f, $f is a MathObjects Formula -# For example, in the setup section define -# -# Context("Numeric"); -# Context()->variables->add(y=>"Real",r=>"Real",t=>"Real"); -# $a = random(1,3,1); -# $f = Formula("$a*e^(- x^2 - y^2)"); # use double quotes! -# -# before calling RectangularPlot3DAnnularDomain() -# -# xvar => "x", independent variable name, default "x" -# yvar => "y", independent variable name, default "y" -# -# rvar => "r", independent variable name, default "r" -# tvar => "t", independent variable name, default "t" (for theta) -# -# rmin => -3, domain for rvar -# rmax => 3, -# -# tmin => -3, domain for tvar -# tmax => 3, -# -# rsamples => 20, deltar = (rmax - rmin) / rsamples -# tsamples => 20, deltat = (tmax - tmin) / tsamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "X", Capital letters may be easier to read -# yaxislabel => "Y", -# zaxislabel => "Z", -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -# Happy 3D graphing! - Paul Pearson -# - - - - - - - -$beginplot = "Graphics3D["; -$endplot = "]"; - - -########################################### -########################################### -# Begin RectangularPlot3DRectangularDomain - -sub RectangularPlot3DRectangularDomain { - -########################################### -# -# Set default options -# - -my %options = ( -function => Formula("1"), -xvar => 'x', -yvar => 'y', -xmin => -3, -xmax => 3, -ymin => -3, -ymax => 3, -xsamples => 20, -ysamples => 20, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -outputtype => 4, -@_ -); - - -############################################ -# -# Reset to Context("Numeric") just to be -# sure that everything will work properly. -# - -#Context("Numeric"); -#Context()->variables->are($options{xvar}=>"Real",$options{yvar}=>"Real"); - - -my $fsubroutine; -$options{function}->perlFunction('fsubroutine',["$options{xvar}","$options{yvar}"]); - -###################################################### -# -# Generate a plotdata array, which has two indices -# - -my $xsamples1 = $options{xsamples} - 1; -my $ysamples1 = $options{ysamples} - 1; - -my $dx = ($options{xmax} - $options{xmin}) / $options{xsamples}; -my $dy = ($options{ymax} - $options{ymin}) / $options{ysamples}; - -my $x; -my $y; - -my $z; - -foreach my $i (0..$options{xsamples}) { - $x[$i] = $options{xmin} + $i * $dx; - foreach my $j (0..$options{ysamples}) { - $y[$j] = $options{ymin} + $j * $dy; - # Use sprintf to round to three decimal places - $z[$i][$j] = sprintf("%.3f", fsubroutine($x[$i],$y[$j])->value ); - $y[$j] = sprintf("%.3f",$y[$j]); - } - $x[$i] = sprintf("%.3f",$x[$i]); -} - - - -########################################################################### -# -# Generate a plotstring from the plotdata. -# -# The plotstring is a list of polygons -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstring, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the polygons in the plotstring -# - -my $plotstructure = "{"; - -foreach my $i (0..$xsamples1) { - foreach my $j (0..$ysamples1) { - - $plotstructure = $plotstructure . "Polygon[{" . - "{$x[$i],$y[$j],$z[$i][$j]}," . - "{$x[$i+1],$y[$j],$z[$i+1][$j]}," . - "{$x[$i+1],$y[$j+1],$z[$i+1][$j+1]}," . - "{$x[$i],$y[$j+1],$z[$i][$j+1]}" . - "}]"; - - if (($i<$xsamples1) || ($j<$ysamples1)) { - $plotstructure = $plotstructure . "," - } - - } -} - -$plotstructure = $plotstructure . "}"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { - $plotoptions = $plotoptions . "Axes->True,AxesLabel->" . - "{$options{xaxislabel},$options{yaxislabel},$options{zaxislabel}}"; -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstructure; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstructure . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstructure . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End RectangularPlot3DRectangularDomain -############################################## -############################################## - - - - - - - - - - - -############################################# -############################################# -# Begin RectangularPlot3DAnnularDomain - - -sub RectangularPlot3DAnnularDomain { - -############################################# -# -# Set default options -# - -my %options = ( -function => Formula("1"), -xvar => "x", -yvar => "y", -rvar => "r", -tvar => "t", -rmin => 0.001, -rmax => 3, -tmin => 0, -tmax => 6.28, -rsamples => 20, -tsamples => 20, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -outputtype => 4, -@_ -); - - -############################################ -# -# Reset to Context("Numeric") just to be -# sure that everything will work properly. -# - -#Context("Numeric"); -#Context()->variables->are( -#$options{xvar}=>"Real", -#$options{yvar}=>"Real", -#$options{rvar}=>"Real", -#$options{tvar}=>"Real" -#); - -my $fsubroutine; -$options{function}->perlFunction('fsubroutine',["$options{xvar}","$options{yvar}"]); - - -###################################################### -# -# Generate a plotdata array, which has two indices -# - -my $rsamples1 = $options{rsamples} - 1; -my $tsamples1 = $options{tsamples} - 1; - -my $dr = ($options{rmax} - $options{rmin}) / $options{rsamples}; -my $dt = ($options{tmax} - $options{tmin}) / $options{tsamples}; - -my $t; -my $r; - -my $x; -my $y; - -my $z; - -foreach my $i (0..$options{tsamples}) { - $t[$i] = $options{tmin} + $i * $dt; - foreach my $j (0..$options{rsamples}) { - $r[$j] = $options{rmin} + $j * $dr; - $x[$i][$j] = $r[$j] * cos($t[$i]); - $y[$i][$j] = $r[$j] * sin($t[$i]); - $z[$i][$j] = sprintf("%.3f", fsubroutine($x[$i][$j],$y[$i][$j])->value ); - $x[$i][$j] = sprintf("%.3f",$x[$i][$j]); - $y[$i][$j] = sprintf("%.3f",$y[$i][$j]); - } -} - - - -########################################################################### -# -# Generate a plotstring from the plotdata. -# -# The plotstring is a list of polygons that -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstring, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the polygons in the plotstring -# - -my $plotstructure = "{"; - -foreach my $i (0..$tsamples1) { - foreach my $j (0..$rsamples1) { - - $plotstructure = $plotstructure . "Polygon[{" . - "{$x[$i][$j],$y[$i][$j],$z[$i][$j]}," . - "{$x[$i+1][$j],$y[$i+1][$j],$z[$i+1][$j]}," . - "{$x[$i+1][$j+1],$y[$i+1][$j+1],$z[$i+1][$j+1]}," . - "{$x[$i][$j+1],$y[$i][$j+1],$z[$i][$j+1]}" . - "}]"; - - if (($i<$tsamples1) || ($j<$rsamples1)) { - $plotstructure = $plotstructure . "," - } - - } -} - -$plotstructure = $plotstructure . "}"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { - $plotoptions = $plotoptions . "Axes->True,AxesLabel->" . - "{$options{xaxislabel},$options{yaxislabel},$options{zaxislabel}}"; -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstructure; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstructure . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstructure . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End RectangularPlot3DAnnularDomain -##################################################### -##################################################### - - - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsVectorField2D.pl b/OpenProblemLibrary/macros/FortLewis/LiveGraphicsVectorField2D.pl deleted file mode 100755 index dc4caa6e77..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsVectorField2D.pl +++ /dev/null @@ -1,231 +0,0 @@ -sub _LiveGraphicsVectorField2D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","LiveGraphics3D.pl"); - -########################################################################### -# -# LiveGraphicsVectorField2D.pl provides a macros for creating an -# interactive plot of a vector field via the LiveGraphics3D Java applet. -# The routine VectorField2D() takes two MathObject Formulas of -# 2 variables as input and returns a string of plot data that can be -# displayed using the Live3Ddata() routine of the LiveGraphics3D.pl macro. -# -# LiveGraphicsVectorField2D.pl automatically loads -# MathObjects.pl and LiveGraphics3D.pl. -# -########################################################################### -# -# The main routine is -# -# VectorField2D() -# -# -# Usage: VectorField2D(options); -# -# Options are: -# -# Fx => Formula("y"), F = < Fx, Fy, Fz > where Fx, Fy, Fz are each -# Fy => Formula("-x"), functions of 3 variables -# -# xvar => "r", independent variable name, default "x" -# yvar => "s", independent variable name, default "y" -# -# xmin => -3, domain for xvar -# xmax => 3, -# -# ymin => -3, domain for yvar -# ymax => 3, -# -# xsamples => 3, deltax = (xmax - xmin) / xsamples -# ysamples => 3, deltay = (ymax - ymin) / ysamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "R", Capital letters may be easier to read -# yaxislabel => "S", -# -# vectorcolor => "RGBColor[1.0,0.0,0.0]", -# vectorscale => 0.2, -# vectorthickness => 0.001, -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -# Happy 2D graphing! -Paul Pearson -# -####################################################################################### - - -$beginplot = "Graphics3D["; -$endplot = "]"; - - -########################################### -########################################### -# Begin VectorField2D - -sub VectorField2D { - -########################################### -# -# Set default options -# - -my %options = ( -Fx => Formula("1"), -Fy => Formula("1"), -xvar => 'x', -yvar => 'y', -xmin => -3, -xmax => 3, -ymin => -3, -ymax => 3, -xsamples => 20, -ysamples => 20, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -vectorcolor => "RGBColor[1.0,0.0,0.0]", -vectorscale => 0.2, -vectorthickness => 0.001, -outputtype => 4, -@_ -); - - -my $Fxsubroutine; -my $Fysubroutine; - -$options{Fx}->perlFunction('Fxsubroutine',["$options{xvar}","$options{yvar}"]); -$options{Fy}->perlFunction('Fysubroutine',["$options{xvar}","$options{yvar}"]); - - -###################################################### -# -# Generate plot data -# - -my $dx = ($options{xmax} - $options{xmin}) / $options{xsamples}; -my $dy = ($options{ymax} - $options{ymin}) / $options{ysamples}; - -my $xtail; -my $ytail; - -foreach my $i (0..$options{xsamples}) { - $xtail[$i] = $options{xmin} + $i * $dx; - foreach my $j (0..$options{ysamples}) { - $ytail[$j] = $options{ymin} + $j * $dy; - - $FX[$i][$j] = sprintf("%.3f", $options{vectorscale}*(Fxsubroutine($xtail[$i],$ytail[$j])->value) ); - $FY[$i][$j] = sprintf("%.3f", $options{vectorscale}*(Fysubroutine($xtail[$i],$ytail[$j])->value) ); - - $xtail[$i] = sprintf("%.3f",$xtail[$i]); - $ytail[$j] = sprintf("%.3f",$ytail[$j]); - - $xtip[$i][$j] = $xtail[$i] + sprintf("%.3f", $FX[$i][$j] ); - $ytip[$i][$j] = $ytail[$j] + sprintf("%.3f", $FY[$i][$j] ); - - $xleftbarb[$i][$j] = sprintf("%.3f", $xtail[$i] + 0.8*$FX[$i][$j] - 0.2*$FY[$i][$j] ); - $yleftbarb[$i][$j] = sprintf("%.3f", $ytail[$j] + 0.8*$FY[$i][$j] + 0.2*$FX[$i][$j] ); - - $xrightbarb[$i][$j] = sprintf("%.3f", $xtail[$i] + 0.8*$FX[$i][$j] + 0.2*$FY[$i][$j] ); - $yrightbarb[$i][$j] = sprintf("%.3f", $ytail[$j] + 0.8*$FY[$i][$j] - 0.2*$FX[$i][$j] ); - } -} - - - -########################################################################### -# -# Generate plotstructure from the plotdata. -# -# The plotstucture is a list of arrows (made of lines) that -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstructure, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the polygons in the plotstructure -# - -my $plotstructure = "{{{{$options{vectorcolor},EdgeForm[],Thickness[$options{vectorthickness}],"; - -foreach my $i (0..$options{xsamples}) { - foreach my $j (0..$options{ysamples}) { - - $plotstructure = $plotstructure . - "Line[{" . - "{$xtail[$i],$ytail[$j],0}," . - "{$xtip[$i][$j],$ytip[$i][$j],0}" . - "}]," . - "Line[{" . - "{$xleftbarb[$i][$j],$yleftbarb[$i][$j],0}," . - "{$xtip[$i][$j],$ytip[$i][$j],0}," . - "{$xrightbarb[$i][$j],$yrightbarb[$i][$j],0}" . - "}]"; - - if ( ($i<$options{xsamples}) || ($j<$options{ysamples}) ) { - $plotstructure = $plotstructure . "," - } - - } -} - -$plotstructure = $plotstructure . "}}}}"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { - - $plotoptions = $plotoptions . - "PlotRange->{{$options{xmin},$options{xmax}},{$options{ymin},$options{ymax}},{-0.1,0.1}}," . - "ViewPoint->{0,0,1000}," . - "ViewVertical->{0,1,0}," . - "Lighting->False," . - "AxesLabel->{$options{xaxislabel},$options{yaxislabel},Z}," . #; # . - "Axes->{True,True,False}"; - -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstructure; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstructure . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstructure . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End VectorField2D -############################################## -############################################## - - - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsVectorField3D.pl b/OpenProblemLibrary/macros/FortLewis/LiveGraphicsVectorField3D.pl deleted file mode 100755 index 4aba989243..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/LiveGraphicsVectorField3D.pl +++ /dev/null @@ -1,265 +0,0 @@ -sub _LiveGraphicsVectorField3D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","LiveGraphics3D.pl"); - -########################################################################### -# -# LiveGraphicsVectorField3D.pl provides a macros for creating an -# interactive plot of a vector field via the LiveGraphics3D Java applet. -# The routine VectorField3D() takes three MathObject Formulas of -# 3 variables as input and returns a string of plot data that can be -# displayed using the Live3Ddata() routine of the LiveGraphics3D.pl macro. -# -# LiveGraphicsVectorField3D.pl automatically loads -# MathObjects.pl and LiveGraphics3D.pl. -# -########################################################################### -# -# The main routine is -# -# VectorField3D() -# -# -# Usage: VectorField3D(options); -# -# Options are: -# -# Fx => Formula("y"), F = < Fx, Fy, Fz > where Fx, Fy, Fz are each -# Fy => Formula("-x"), functions of 3 variables -# Fz => Formula("z"), -# -# xvar => "r", independent variable name, default "x" -# yvar => "s", independent variable name, default "y" -# zvar => "t", independent variable name, default "z" -# -# xmin => -3, domain for xvar -# xmax => 3, -# -# ymin => -3, domain for yvar -# ymax => 3, -# -# zmin => -3, domain for zvar -# zmax => 3, -# -# xsamples => 3, deltax = (xmax - xmin) / xsamples -# ysamples => 3, deltay = (ymax - ymin) / ysamples -# zsamples => 3, deltaz = (zmax - zmin) / zsamples -# -# axesframed => 1, 1 displays framed axes, 0 hides framed axes -# -# xaxislabel => "R", Capital letters may be easier to read -# yaxislabel => "S", -# zaxislabel => "T", -# -# vectorcolor => "RGBColor[0.0,0.0,1.0]", -# vectorscale => 0.2, -# vectorthickness => 0.001, -# -# outputtype => 1, return string of only polygons (or mesh) -# 2, return string of only plotoptions -# 3, return string of polygons (or mesh) and plotoptions -# 4, return complete plot -# -# Happy 3D graphing! -Paul Pearson -# -####################################################################################### - - -$beginplot = "Graphics3D["; -$endplot = "]"; - - -########################################### -########################################### -# Begin VectorField3D - -sub VectorField3D { - -########################################### -# -# Set default options -# - -my %options = ( -Fx => Formula("1"), -Fy => Formula("1"), -Fz => Formula("1"), -xvar => 'x', -yvar => 'y', -zvar => 'z', -xmin => -3, -xmax => 3, -ymin => -3, -ymax => 3, -zmin => -3, -zmax => 3, -xsamples => 20, -ysamples => 20, -zsamples => 20, -axesframed => 1, -xaxislabel => "X", -yaxislabel => "Y", -zaxislabel => "Z", -vectorcolor => "RGBColor[0.0,0.0,1.0]", -vectorscale => 0.2, -vectorthickness => 0.001, -xavoid => 1000000, -yavoid => 1000000, -zavoid => 1000000, -outputtype => 4, -@_ -); - - -my $Fxsubroutine; -my $Fysubroutine; -my $Fzsubroutine; - -$options{Fx}->perlFunction('Fxsubroutine',["$options{xvar}","$options{yvar}","$options{zvar}"]); -$options{Fy}->perlFunction('Fysubroutine',["$options{xvar}","$options{yvar}","$options{zvar}"]); -$options{Fz}->perlFunction('Fzsubroutine',["$options{xvar}","$options{yvar}","$options{zvar}"]); - - - -###################################################### -# -# Generate plot data -# - -my $dx = ($options{xmax} - $options{xmin}) / $options{xsamples}; -my $dy = ($options{ymax} - $options{ymin}) / $options{ysamples}; -my $dz = ($options{zmax} - $options{zmin}) / $options{zsamples}; - -my $xtail; -my $ytail; -my $ztail; - -foreach my $i (0..$options{xsamples}) { - $xtail[$i] = $options{xmin} + $i * $dx; - foreach my $j (0..$options{ysamples}) { - $ytail[$j] = $options{ymin} + $j * $dy; - foreach my $k (0..$options{zsamples}) { - $ztail[$k] = $options{zmin} + $k * $dz; - - if ( $xtail[$i]==$options{xavoid} && $ytail[$j]==$options{yavoid} && $ztail[$k]==$options{zavoid} ) { - - $FX[$i][$j][$k] = 0; - $FY[$i][$j][$k] = 0; - $FZ[$i][$j][$k] = 0; - - } else { - - $FX[$i][$j][$k] = sprintf("%.3f", $options{vectorscale}*(Fxsubroutine($xtail[$i],$ytail[$j],$ztail[$k])->value) ); - $FY[$i][$j][$k] = sprintf("%.3f", $options{vectorscale}*(Fysubroutine($xtail[$i],$ytail[$j],$ztail[$k])->value) ); - $FZ[$i][$j][$k] = sprintf("%.3f", $options{vectorscale}*(Fzsubroutine($xtail[$i],$ytail[$j],$ztail[$k])->value) ); - - } - - $xtail[$i] = sprintf("%.3f",$xtail[$i]); - $ytail[$j] = sprintf("%.3f",$ytail[$j]); - $ztail[$k] = sprintf("%.3f",$ztail[$k]); - - $xtip[$i][$j][$k] = $xtail[$i] + sprintf("%.3f", $FX[$i][$j][$k] ); - $ytip[$i][$j][$k] = $ytail[$j] + sprintf("%.3f", $FY[$i][$j][$k] ); - $ztip[$i][$j][$k] = $ztail[$k] + sprintf("%.3f", $FZ[$i][$j][$k] ); - - $xleftbarb[$i][$j][$k] = sprintf("%.3f", $xtail[$i] + 0.8*$FX[$i][$j][$k] - 0.2*$FY[$i][$j][$k] ); - $yleftbarb[$i][$j][$k] = sprintf("%.3f", $ytail[$j] + 0.8*$FY[$i][$j][$k] + 0.2*$FX[$i][$j][$k] ); - - $xrightbarb[$i][$j][$k] = sprintf("%.3f", $xtail[$i] + 0.8*$FX[$i][$j][$k] + 0.2*$FY[$i][$j][$k] ); - $yrightbarb[$i][$j][$k] = sprintf("%.3f", $ytail[$j] + 0.8*$FY[$i][$j][$k] - 0.2*$FX[$i][$j][$k] ); - - $zbarb[$i][$j][$k] = sprintf("%.3f", $ztail[$k] + 0.8*$FZ[$i][$j][$k] ); - - } - } -} - - - -########################################################################### -# -# Generate plotstructure from the plotdata. -# -# The plotstucture is a list of arrows (made of lines) that -# LiveGraphics3D reads as input. -# -# For more information on the format of the plotstructure, see -# http://www.math.umn.edu/~rogness/lg3d/page_NoMathematica.html -# http://www.vis.uni-stuttgart.de/~kraus/LiveGraphics3D/documentation.html -# -########################################### -# -# Generate the polygons in the plotstructure -# - -my $plotstructure = "{{{{$options{vectorcolor},Thickness[$options{vectorthickness}],"; - -foreach my $i (0..$options{xsamples}) { - foreach my $j (0..$options{ysamples}) { - foreach my $k (0..$options{zsamples}) { - - $plotstructure = $plotstructure . - "Line[{" . - "{$xtail[$i],$ytail[$j],$ztail[$k]}," . - "{$xtip[$i][$j][$k],$ytip[$i][$j][$k],$ztip[$i][$j][$k]}" . - "}]," . - "Line[{" . - "{$xleftbarb[$i][$j][$k],$yleftbarb[$i][$j][$k],$zbarb[$i][$j][$k]}," . - "{$xtip[$i][$j][$k],$ytip[$i][$j][$k],$ztip[$i][$j][$k]}," . - "{$xrightbarb[$i][$j][$k],$yrightbarb[$i][$j][$k],$zbarb[$i][$j][$k]}" . - "}]"; - - if (($i<$options{xsamples}) || ($j<$options{ysamples}) || ($k<$options{zsamples})) { - $plotstructure = $plotstructure . "," - } - } - } -} - -$plotstructure = $plotstructure . "}}}}"; - - - -############################################## -# -# Add plot options to the plotoptions string -# - -my $plotoptions = ""; - -if ( ($options{outputtype}>1) || ($options{axesframed}==1) ) { - $plotoptions = $plotoptions . "Axes->True,AxesLabel->" . - "{$options{xaxislabel},$options{yaxislabel},$options{zaxislabel}}"; -} - - -#################################################### -# -# Return only the plotstring (if outputtype=>1), -# or only plotoptions (if outputtype=>2), -# or plotstring, plotoptions (if outputtype=>2), -# or the entire plot (default) (if outputtype=>4) - -if ($options{outputtype}==1) { - return $plotstructure; -} elsif ($options{outputtype}==2) { - return $plotoptions; -} elsif ($options{outputtype}==3) { - return "{" . $plotstructure . "," . $plotoptions . "}"; -} elsif ($options{outputtype}==4) { - return $beginplot . $plotstructure . "," . $plotoptions . $endplot; -} else { - return "Invalid outputtype (outputtype should be a number 1 through 4)."; -} - - - - -} # End VectorField3D -############################################## -############################################## - - - -1; diff --git a/OpenProblemLibrary/macros/FortLewis/MatrixUnimodular.pl b/OpenProblemLibrary/macros/FortLewis/MatrixUnimodular.pl deleted file mode 100755 index 7d5f6511e4..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/MatrixUnimodular.pl +++ /dev/null @@ -1,248 +0,0 @@ -sub _MatrixUnimodular_init {}; # don't reload this file - - -sub xgcd ($$) { - - # Extended greatest common divisor - # xgcd( a , b ) = ( d , x , y , s , t ) - # where - # gcd(a,b) = d = a (x + s k) + b (y + t k) for any integer k - - my ($a, $x, $y) = ( int(shift) , 1 , 0 ) ; - my ($b, $s, $t) = ( int(shift) , 0 , 1 ) ; - - my ($A,$B) = ($a,$b) ; - - while ( $b != 0 ) { - my $q = int( $a / $b ) ; - ($a, $x, $y, $b, $s, $t) = ($b, $s, $t, $a-$q*$b, $x-$q*$s, $y-$q*$t); - } - - if ($a < 0) { - return ( -$a, -$x, -$y, $s, $t ) ; } - else { - return ( $a, $x, $y, $s, $t ) ; - } -} - - - -sub unimodular_SL2Z_specific { - - # Unimodular 2x2 matrix in SL_2(Z) - # unimodular( a11, a21 ) - # returns a determinant 1 matrix object - # [ a11 a12 ] - # [ a21 a22 ] - # The inputs a11 and a12 must be relatively prime - # integers, and could be thought of as an eigenvector. - # If they are not relatively prime, then the identity - # matrix will be returned. - - my $a11 = shift; - my $a21 = shift; - - my @w = xgcd($a11,$a21); # "weights" - - if ($w[0] != 1) { return (1,0,0,1); } - # my $a12 = -($w[2]); - # my $a22 = $w[1]; - - @A = ( $a11, $a21, -($w[2]), $w[1] ); - - return @A; - -} - - - -sub unimodular_SL2Z { - - # Unimodular 2x2 matrix in SL_2(Z) - # unimodular ( A, B ) = ( a11, a21, a12, a22 ) - # where - # [ a11 a12 ] - # [ a21 a22 ] - # is a determinant one matrix with integer entries - # and the inputs A < B are the limits of the size of the entries - # If they are not relatively prime, then the identity - # matrix will be returned. - # Note: it returns a matrix listed by columns (not rows) - # so that you can easily use the columns as eigenvectors - - my $a11 = list_random(-7,-5,-3,-2,2,3,5,7); - my $a21 = list_random(-7,-5,-3,-2,2,3,5,7); - while ($a11 == $a21 || $a11 == -($a21)) { - $a21 = list_random(-7,-5,-3,-2,2,3,5,7); - }; - - my @w = xgcd($a11,$a21); - - # my $a12 = -($w[2]); - # my $a22 = $w[1]; - - my @A = ( $a11, $a21, -($w[2]), $w[1] ); - - return @A; - -} - - - -sub unimodular_GL2Z { - - # Unimodular 2x2 matrix in GL_2(Z) - # unimodular ( A, B ) = ( a11, a21, a12, a22, det ) - # where - # [ a11 a12 ] - # [ a21 a22 ] - # is a determinant 1 or -1 matrix with integer entries - # and det is the determinant. - # If they are not relatively prime, then the identity - # matrix will be returned. - # Note: it returns a matrix listed by columns (not rows) - # so that you can easily use the columns as eigenvectors. - - my $a11 = list_random(-7,-5,-3,-2,2,3,5,7); - my $a21 = list_random(-7,-5,-3,-2,2,3,5,7); - while ($a11 == $a21 || $a11 == -($a21)) { - $a21 = list_random(-7,-5,-3,-2,2,3,5,7); - }; - - my @w = xgcd($a11,$a21); - - # my $a12 = -($w[2]); - # my $a22 = $w[1]; - - my $s = random(-1,1,2); # randomize the sign for the first column - - my @A = ( $s * $a11, $s * $a21, -($w[2]), $w[1] ); - - return @A; - -} - - -sub unimodular_diagonalization_SL2Z { - - # input: two distinct integer eigenvalues (lambda1, lambda2) - # - # output: a single array with the following entries in order: - # 2x2 matrix listed by columns (A11,A21,A12,A22), - # first eigenvalue lambda1, first eigenvector (P11,P21), - # second eigenvalue lambda2, second eigenvector (P12,P22) - - my $lambda1 = shift; - my $lambda2 = shift; - - my @P = unimodular_SL2Z(); - - # A = P D P^(-1), where D is diagonal with $lambda1 and $lambda2 - - my @A = ( - $P[0] * $P[3] * $lambda1 - $P[1] * $P[2] * $lambda2, - $P[1] * $P[3] * $lambda1 - $P[1] * $P[3] * $lambda2, - $P[0] * $P[2] * $lambda2 - $P[0] * $P[2] * $lambda1, - $P[0] * $P[3] * $lambda2 - $P[1] * $P[2] * $lambda1 - ); - - return ( @A, @P, $lambda1, $lambda2 ); - -} - - -sub unimodular_diagonalization_GL2Z { - - # input: two distinct integer eigenvalues (lambda1, lambda2) - # - # output: a single array with the following entries in order: - # 2x2 matrix listed by columns (M11,M21,M12,M22), - # first eigenvalue lambda1, first eigenvector (P11,P21), - # second eigenvalue lambda2, second eigenvector (P12,P22) - - my $lambda1 = shift; - my $lambda2 = shift; - - my @P = unimodular_GL2Z(); - my $detP = $P[0] * $P[3] - $P[1] * $P[2]; - - # A = P D P^(-1), where D is diagonal [ [lambda1,0], [0,lambda1] ] - my @A = ( - $detP * $P[0] * $P[3] * $lambda1 - $detP * $P[1] * $P[2] * $lambda2, - $detP * $P[1] * $P[3] * $lambda1 - $detP * $P[1] * $P[3] * $lambda2, - $detP * $P[0] * $P[2] * $lambda2 - $detP * $P[0] * $P[2] * $lambda1, - $detP * $P[0] * $P[3] * $lambda2 - $detP * $P[1] * $P[2] * $lambda1 - ); - - return ( @A, @P, $lambda1, $lambda2 ); - -} - - - -############################################################### -# Versions with small integer entries - -sub small_unimodular_GL2Z { - - # Unimodular 2x2 matrix in GL_2(Z) - # unimodular ( A, B ) = ( a11, a21, a12, a22, det ) - # where - # [ a11 a12 ] - # [ a21 a22 ] - # is a determinant 1 or -1 matrix with integer entries - # and det is the determinant. - # If they are not relatively prime, then the identity - # matrix will be returned. - # Note: it returns a matrix listed by columns (not rows) - # so that you can easily use the columns as eigenvectors. - - my $a11 = list_random(-4,-3,-2,2,3,4); - my $a21 = list_random(-4,-3,-2,2,3,4); - while ($a11 == $a21 || $a11 == -($a21) || gcd($a11,$a21) != 1) { - $a21 = list_random(-4,-3,-2,2,3,4); - }; - - my @w = xgcd($a11,$a21); - - # my $a12 = -($w[2]); - # my $a22 = $w[1]; - - my $s = random(-1,1,2); # randomize the sign for the first column - - my @A = ( $s * $a11, $s * $a21, -($w[2]), $w[1] ); - - return @A; - -} - - -sub small_unimodular_diagonalization_GL2Z { - - # input: two distinct integer eigenvalues (lambda1, lambda2) - # - # output: a single array with the following entries in order: - # 2x2 matrix listed by columns (M11,M21,M12,M22), - # first eigenvalue lambda1, first eigenvector (P11,P21), - # second eigenvalue lambda2, second eigenvector (P12,P22) - - my $lambda1 = shift; - my $lambda2 = shift; - - my @P = small_unimodular_GL2Z(); - my $detP = $P[0] * $P[3] - $P[1] * $P[2]; - - # A = P D P^(-1), where D is diagonal [ [lambda1,0], [0,lambda1] ] - my @A = ( - $detP * $P[0] * $P[3] * $lambda1 - $detP * $P[1] * $P[2] * $lambda2, - $detP * $P[1] * $P[3] * $lambda1 - $detP * $P[1] * $P[3] * $lambda2, - $detP * $P[0] * $P[2] * $lambda2 - $detP * $P[0] * $P[2] * $lambda1, - $detP * $P[0] * $P[3] * $lambda2 - $detP * $P[1] * $P[2] * $lambda1 - ); - - return ( @A, @P, $lambda1, $lambda2 ); - -} - - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/FortLewis/PeriodicRerandomization.pl b/OpenProblemLibrary/macros/FortLewis/PeriodicRerandomization.pl deleted file mode 100755 index 514ac336d7..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/PeriodicRerandomization.pl +++ /dev/null @@ -1,185 +0,0 @@ -=head1 NAME - -PeriodicRerandomization.pl - forced re-randomization of the -problem every p submissions. - -=head1 SYNOPSIS - -Let p be a positive integer. This macro provides the ability -to force a re-randomization of the question every p attempts. -For example, this macro can be used to create a practice exercise -that provides a new version of the question every 4 attempts -and displays a solution every fourth attempt. - -=head1 DESCRIPTION - -Usage: - - DOCUMENT(); - loadMacros( - "PGstandard.pl", - "MathObjects.pl", - "PeriodicRerandomization.pl", - ); - TEXT(beginproblem()); - - PeriodicRerandomization("4"); - - $a = random(2,9,1); - do { $b = random(2,9,1); } until ($b != $a); - - BEGIN_TEXT - An equation for a line in the xy-plane with slope \( $a \) - and y-intercept \( $b \) is - $BR - $BR - \( y = \) \{ ans_rule(20) \} - $BR - $BR - END_TEXT - - PeriodicStatus( - "Generate a new version of this problem", - "You have ${attempts_modp_remaining} attempt(s) remaining - before you will receive a new version of this problem." - ); - - if ($attempts_modp == 0) { - BEGIN_TEXT - $BR - $BR - ${BBOLD}Solution:${EBOLD} Multiply \( x \) by \( $a \) - and add \( $b \). - $BR - $BR - END_TEXT - } - - ANS( Compute("$a x + $b")->cmp() ); - - COMMENT('Periodically re-randomizes.'); - - ENDDOCUMENT(); - - -The argument of C is a number p (the period) -that is the number of attempts allowed before the problem is -re-randomized. C must be called before -any random parameters are defined. - -The two arguments of C are (1) the text that will -appear on the button for a new version of the problem when the number -of attempts is 0 mod p, and (2) a message that will appear when the -number of attempts is not 0 mod N. If C has no -arguments, then it will use defaults. - -There are several globally defined variables. C<$rerand_period> is -the period p. C<$attempts_modp> is the total number of attempts modulo -the period except for when the problem is first accessed, in which case -its value is -p (the rationale behind this is that the case when the -problem is first accessed often needs to be handled separately.) -C<$attempts_modp_remaining> is the number of attempts -remaining before re-randomization. C<$problem_version_number> is the -floor function applied to the total number of attempts divided by the -period p. C<$newProblemSeed> is the problem seed for the current -version of the problem, whereas C<$problemSeed> is the original -problem seed (which does not change with different versions of the -problem). You can access the total number of attempts using -C<$envir{numOfAttempts}>, which is a sequence 0,0,1,2,3,... (not -0,1,2,3,...). - -=head1 AUTHOR - -Paul Pearson, Fort Lewis College, Department of Mathematics - -=cut - - - -########################### - - - -sub _PeriodicRerandomization_init {}; # don't reload this file - -sub PeriodicRerandomization { - - # - # define some global variables - # - - $rerand_period = shift; - $attempts_modp = ($envir{numOfAttempts}+1) % $rerand_period; - $attempts_modp_remaining = $rerand_period - $attempts_modp; - - # - # a correction because the $envir{numOfAttempts} progression is 0,0,1,2,3... instead of 0,1,2,3,... - # - if ( $envir{numOfAttempts}==0 && !defined(${$inputs_ref}{'hasBeenAttempted'}) ) { - - TEXT(MODES( - HTML => qq!!, - HTML_dpng => qq!!, - TeX => "") - ); - - $attempts_modp += -(1+$rerand_period); - $attempts_modp_remaining += 1; - - } else { - - TEXT(MODES( - HTML => qq!!, - HTML_dpng => qq!!, - TeX => "") - ); - - } - - $problem_version_number = floor($envir{'numOfAttempts'} / $rerand_period); - - if ($problemSeed < 2000) { - $newProblemSeed = $problemSeed + $problem_version_number; - } else { - $newProblemSeed = $problemSeed - $problem_version_number; - } - - $main::PG_random_generator -> srand( $newProblemSeed ); - - -} - - - - - -sub PeriodicStatus { - - my $button_text = shift; - my $status_text = shift; - - if ( !defined($button_text) ) { $button_text = "Generate a new version of this problem"; } - - if ( !defined($status_text) ) { - $status_text = - "You have ${attempts_modp_remaining} attempt(s) remaining before you will receive a new version of this problem."; - } - - - if ($attempts_modp == 0 && defined(${$inputs_ref}{'hasBeenAttempted'}) ) { - - TEXT(MODES( - HTML =>"", - HTML_dpng=>"", - TeX => "") - ); - - } else { - - TEXT(EV3($status_text)); - - } - -} - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/FortLewis/VectorField2D.pl b/OpenProblemLibrary/macros/FortLewis/VectorField2D.pl deleted file mode 100755 index 0f13640ab6..0000000000 --- a/OpenProblemLibrary/macros/FortLewis/VectorField2D.pl +++ /dev/null @@ -1,110 +0,0 @@ -sub _VectorField2D_init {}; # don't reload this file - -loadMacros("MathObjects.pl","PGgraphmacros.pl"); - - -sub VectorField2D { - -########################################### -# -# Set default options -# - -my %options = ( -graphobject => $gr, -Fx => Formula("1"), -Fy => Formula("1"), -xvar => 'x', -yvar => 'y', -xmin => -5, -xmax => 5, -ymin => -5, -ymax => 5, -xsamples => 10, -ysamples => 10, -vectorcolor => "blue", -vectorscale => 0.25, -vectorthickness => 2, -vectortipwidth => 0.08, -vectortiplength => 0.65, -xavoid=>1000000, -yavoid=>1000000, -@_ -); - - -my $Fxsubroutine; -my $Fysubroutine; - -$options{Fx}->perlFunction('Fxsubroutine',["$options{xvar}","$options{yvar}"]); -$options{Fy}->perlFunction('Fysubroutine',["$options{xvar}","$options{yvar}"]); - - -###################################################### -# -# Generate plot data -# - -my $dx = ($options{xmax} - $options{xmin}) / $options{xsamples}; -my $dy = ($options{ymax} - $options{ymin}) / $options{ysamples}; - -my $xtail; -my $ytail; -my $FX; -my $FY; -my $xtip; -my $xstem; -my $ystem; -my $xmidtip; -my $ymidtip; -my $xleftbarb; -my $xrightbarb; -my $yleftbarb; -my $yrightbarb; - - -foreach my $i (0..$options{xsamples}) { - $xtail[$i] = $options{xmin} + $i * $dx; - foreach my $j (0..$options{ysamples}) { - $ytail[$j] = $options{ymin} + $j * $dy; - - if (($options{xavoid} == $xtail[$i]) && ($options{yavoid} == $ytail[$j])) { - $FX[$i][$j] = 0; - $FY[$i][$j] = 0; - } else { - $FX[$i][$j] = $options{vectorscale}*(Fxsubroutine($xtail[$i],$ytail[$j])->value); - $FY[$i][$j] = $options{vectorscale}*(Fysubroutine($xtail[$i],$ytail[$j])->value); - } - - $xtip[$i][$j] = $xtail[$i] + $FX[$i][$j]; - $ytip[$i][$j] = $ytail[$j] + $FY[$i][$j]; - - $xstem[$i][$j] = $xtail[$i] + $options{vectortiplength}*$FX[$i][$j]; - $ystem[$i][$j] = $ytail[$j] + $options{vectortiplength}*$FY[$i][$j]; - - $xmidtip[$i][$j] = $xtail[$i] + (($options{vectortiplength} + 1)/2)*$FX[$i][$j]; - $ymidtip[$i][$j] = $ytail[$j] + (($options{vectortiplength} + 1)/2)*$FY[$i][$j]; - - $xleftbarb[$i][$j] = $xtail[$i] + $options{vectortiplength}*$FX[$i][$j] - $options{vectortipwidth}*$FY[$i][$j]; - $yleftbarb[$i][$j] = $ytail[$j] + $options{vectortiplength}*$FY[$i][$j] + $options{vectortipwidth}*$FX[$i][$j]; - - $xrightbarb[$i][$j] = $xtail[$i] + $options{vectortiplength}*$FX[$i][$j] + $options{vectortipwidth}*$FY[$i][$j]; - $yrightbarb[$i][$j] = $ytail[$j] + $options{vectortiplength}*$FY[$i][$j] - $options{vectortipwidth}*$FX[$i][$j]; - - - $options{graphobject}->moveTo($xtail[$i],$ytail[$j]); -# $options{graphobject}->lineTo($xstem[$i][$j],$ystem[$i][$j],$options{vectorcolor},$options{vectorthickness}); - $options{graphobject}->lineTo($xtip[$i][$j],$ytip[$i][$j],$options{vectorcolor},$options{vectorthickness}); - - $options{graphobject}->moveTo($xleftbarb[$i][$j],$yleftbarb[$i][$j]); - $options{graphobject}->lineTo($xtip[$i][$j],$ytip[$i][$j],$options{vectorcolor},$options{vectorthickness}); - $options{graphobject}->lineTo($xrightbarb[$i][$j],$yrightbarb[$i][$j],$options{vectorcolor},$options{vectorthickness}); -# $options{graphobject}->lineTo($xleftbarb[$i][$j],$yleftbarb[$i][$j],$options{vectorcolor},$options{vectorthickness}); -# $options{graphobject}->fillRegion([$xmidtip[$i][$j],$ymidtip[$i][$j],$options{vectorcolor}]); - - } -} - -} - -1; diff --git a/OpenProblemLibrary/macros/Hope/MatrixCheckers.pl b/OpenProblemLibrary/macros/Hope/MatrixCheckers.pl deleted file mode 100644 index 92cffd8628..0000000000 --- a/OpenProblemLibrary/macros/Hope/MatrixCheckers.pl +++ /dev/null @@ -1,467 +0,0 @@ -sub _MatrixCheckers_init {}; # don't reload this file - -=pod - -=head1 NAME - -MatrixCheckers.pl - -=head1 SYNOPSIS - -Provides subroutines for answer checking using MathObjects -matrices with real entries. (Sadly, it does not provide -routines for using matrices to win at the game of checkers.) - -=head1 DESCRIPTION - -First, load the C macro file. If the basis has -more than one vector, also load C. - -=over 12 - -=item C - -= back - -For a matrix that has a single column or row, the way to use the -answer checkers is the same as using a custom answer checker -inside of C~~&name_of_answer_checker_subroutine)> -such as - -=over 12 - -=item Ccmp( checker=~~&basis_checker_one_column ) );> - -=item Ccmp( checker=~~&unit_basis_checker_one_column ) );> - -=back - -The "one column" at the end of the checker name refers to the -fact that the student answer is a one-column matrix. The "unit -basis checker" ensures that the student answer has unit length. - -For answers that are a collection of column or row vectors, the -way to use the answer checkers is inside of a MultiAnswer object. -The macro C should also be loaded. -The answer checkers that should be used inside a MultiAnswer -object are: - -=over 12 - -=item C - -=item C - -=item C - -=item C - -=item C - -=back - -Here is an example of how to use these answer checkers. -In the setup section of the PG file we create two 3 x 1 MathObject -matrices with real-entries that serve as basis vectors. The object -C<$multians> takes the basis vectors as input and passes them -to the custom answer checker called by C...>. - -=over 12 - -$basis1 = Matrix([1/sqrt(2), 0, 1/sqrt(2)])->transpose; -$basis2 = Matrix([0,1,0])->transpose; - -$multians = MultiAnswer($basis1, $basis2)->with( - singleResult => 1, - separator => ', ', - tex_separator => ', ', - allowBlankAnswers=>0, - checker => ~~&orthonormal_basis_checker_columns, -); - -=back - -In the main text portion of the PG file, we use C<\{ $multians-ans_array(15) \}> -to create an array of text boxes that are 15 characters wide and have square -brackets around them to look like a matrix. The braces around the vectors, which -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 - -The answer evaluation section of the PG file is totally standard. - -=over 12 - -ANS( $multians->cmp ); - -=back - -The C should be used for -solutions to non-homogeneous systems of linear equations for -which the solution is essentially a point plus the span of -several linearly independent vectors. When using the parametric -plane checker, the first vector input always serves as a point -on the hyperplane (i.e., the first vector input is always a -particular solution), while the remaining vectors are a basis for -the hyperplane (i.e., they span the homogeneous solution set). - -=head1 AUTHORS - -Paul Pearson, Hope College, Department of Mathematics - -=cut - - - -################################################ - -loadMacros("MathObjects.pl",); # , will "parserMultiAnswer.pl" create an infinite loop? - - -################################################ - -sub concatenate_columns_into_matrix { - - my @c = @_; - my @temp = (); - for my $column (@c) { - push(@temp,Matrix($column)->transpose->row(1)); - } - return Matrix(@temp)->transpose; - -} - -########################################## - -sub basis_checker_one_column { - - my ( $correct, $student, $answerHash ) = @_; - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - #my $C = Matrix($context,$correct); - #my $S = Matrix($context,$student); - my $C = Vector($context,$correct); - my $S = Vector($context,$student); - return $S->isParallel($C); - -} - -########################################## - -sub basis_checker_columns { - - my ( $correct, $student, $answerHash ) = @_; - my @c = @{$correct}; - my @s = @{$student}; - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - return 0 if scalar(@s) < scalar(@c); # count the number of vector inputs - - my $C = concatenate_columns_into_matrix(@c); - my $S = concatenate_columns_into_matrix(@s); - - # Put $C and $S into the local context so that - # all of the computations that follow will also be in - # the local context. - $C = Matrix($context,$C); - $S = Matrix($context,$S); - - # Theorem: A^T A is invertible if and only if A has linearly independent columns. - - # Check that the professor's vectors are, in fact, linearly independent. - $CTC = ($C->transpose) * $C; - warn "Correct answer is a linearly dependent set." if ($CTC->det == 0); - - # Check that the student's vectors are linearly independent - if ( (($S->transpose) * $S)->det == 0) { - Value->Error("Your vectors are linearly dependent"); - return 0; - } - - # S = student, C = correct, X = change of basis matrix - # Solve S = CX for X using (C^T C)^{-1} C^T S = X. - $X = ($CTC->inverse) * (($C->transpose) * $S); - return $S == $C * $X; - -} - - -############################################# - -sub unit_basis_checker_one_column { - - my ( $correct, $student, $answerHash ) = @_; - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - my $C = Vector($context,$correct); - my $S = Vector($context,$student); - return ($S->isParallel($C) and norm($S)==1); - -} - -############################################### - -sub orthonormal_basis_checker_columns { - - my ( $correct, $student, $answerHash ) = @_; - my @c = @{$correct}; - my @s = @{$student}; - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - return 0 if scalar(@s) < scalar(@c); # count the number of vector inputs - - my $C = concatenate_columns_into_matrix(@c); - my $S = concatenate_columns_into_matrix(@s); - - # Put $C and $S into the local context so that - # all of the computations that follow will also be in - # the local context. - $C = Matrix($context,$C); - $S = Matrix($context,$S); - - # Theorem: A^T A is invertible if and only if A has linearly independent columns. - - # Check that the professor's vectors are, in fact, linearly independent. - $CTC = ($C->transpose) * $C; - warn "Correct answer is a linearly dependent set." if ($CTC->det == 0); - - # Check that the student's vectors are linearly independent - if ( (($S->transpose) * $S)->det == 0) { - Value->Error("Your vectors are linearly dependent"); - return 0; - } - - my $identity = Value::Matrix->I(scalar(@c)); - # Check that the student's vectors are orthonormal - if ( (($S->transpose) * $S) != $identity) { - Value->Error("Your vectors are not orthonormal (or you may need to enter more decimal places)"); - return 0; - } - - # S = student, C = correct, X = change of basis matrix - # Solve S = CX for X using (C^T C)^{-1} C^T S = X. - $X = ($CTC->inverse) * (($C->transpose) * $S); - return $S == $C * $X; - -} - -############################################## - -sub basis_checker_rows { - - my ( $correct, $student, $answerHash ) = @_; - my @c = @{$correct}; - my @s = @{$student}; - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - 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. - - # Put $C and $S into the local context so that - # all of the computations that follow will also be in - # the local context. - $C = Matrix($context,$C); - $S = Matrix($context,$S); - - # Theorem: A^T A is invertible if and only if A has linearly independent columns. - - # Check that the professor's vectors are, in fact, linearly independent. - $CTC = ($C->transpose) * $C; - warn "Correct answer is a linearly dependent set." if ($CTC->det == 0); - - # Check that the student's vectors are linearly independent - if ( (($S->transpose) * $S)->det == 0) { - Value->Error("Your vectors are linearly dependent"); - return 0; - } - - # S = student, C = correct, X = change of basis matrix - # Solve S = CX for X using (C^T C)^{-1} C^T S = X. - $X = ($CTC->inverse) * (($C->transpose) * $S); - return $S == $C * $X; - -} - -############################################# - -sub orthonormal_basis_checker_rows { - - my ( $correct, $student, $answerHash ) = @_; - my @c = @{$correct}; - my @s = @{$student}; - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - 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. - - # Put $C and $S into the local context so that - # all of the computations that follow will also be in - # the local context. - $C = Matrix($context,$C); - $S = Matrix($context,$S); - - # Theorem: A^T A is invertible if and only if A has linearly independent columns. - - # Check that the professor's vectors are, in fact, linearly independent. - $CTC = ($C->transpose) * $C; - warn "Correct answer is a linearly dependent set." if ($CTC->det == 0); - - # Check that the student's vectors are linearly independent - if ( (($S->transpose) * $S)->det == 0) { - Value->Error("Your vectors are linearly dependent"); - return 0; - } - - my $identity = Value::Matrix->I(scalar(@c)); - # Check that the student's vectors are orthonormal - if ( (($S->transpose) * $S) != $identity) { - Value->Error("Your vectors are not orthonormal (or you may need to enter more decimal places)"); - return 0; - } - - # S = student, C = correct, X = change of basis matrix - # Solve S = CX for X using (C^T C)^{-1} C^T S = X. - $X = ($CTC->inverse) * (($C->transpose) * $S); - return $S == $C * $X; - -} - -############################################## - -sub parametric_plane_checker_columns { - - my ( $correct, $student, $answerHash ) = @_; - my @c = @{$correct}; - my @s = @{$student}; - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - return 0 if scalar(@s) < scalar(@c); # count the number of vector inputs - - # pull off the first vector as the displacement vector - my $C0 = Matrix(shift(@c)); - my $S0 = Matrix(shift(@s)); - - # put the remaining vectors into the columns of a matrix. - my $C = concatenate_columns_into_matrix(@c); - my $S = concatenate_columns_into_matrix(@s); - - # Put $C and $S into the local context so that - # all of the computations that follow will also be in - # the local context. - $C0 = Matrix($context,$C0); - $S0 = Matrix($context,$S0); - $C = Matrix($context,$C); - $S = Matrix($context,$S); - - # Theorem: A^T A is invertible if and only if A has linearly independent columns. - - # Check that the professor's vectors are, in fact, linearly independent. - $CTC = ($C->transpose) * $C; - warn "Correct answer is a linearly dependent set." if ($CTC->det == 0); - - # Check that the student's vectors are linearly independent - if ( (($S->transpose) * $S)->det == 0) { - Value->Error("Your vectors are linearly dependent"); - return 0; - } - - # solve (S_0 = C_0 + C A) for the column vector A of weights using - # (S_0 - C_0) = C A - # C^T (S_0 - C_0) = C^T C A - # (C^T C)^{-1} C^T (S_0 - C_0) = A - my $A = ($CTC->inverse) * ($C->transpose) * ($S0 - $C0); - if ($S0 != $C0 + $C*$A) { - Value->Error("Your particular solution is incorrect"); - return 0; - } - - # S = student, C = correct, X = change of basis matrix - # Solve S = CX for X using (C^T C)^{-1} C^T S = X. - $X = ($CTC->inverse) * (($C->transpose) * $S); - return $S == $C * $X; - -} - - -######################################################## - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/Hope/MatrixReduce.pl b/OpenProblemLibrary/macros/Hope/MatrixReduce.pl deleted file mode 100644 index b286f1f71e..0000000000 --- a/OpenProblemLibrary/macros/Hope/MatrixReduce.pl +++ /dev/null @@ -1,349 +0,0 @@ -sub _MatrixReduce_init {}; # don't reload this file - -=pod - -=head1 NAME - -MatrixReduce.pl - reduced row echelon form, row -operations, and elementary matrices. - -=head1 SYNOPSIS - -Provides subroutines for elementary matrix -computations using MathObjects matrices. - -=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 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 Construct an n x n identity matrix: C<$E = identity_matrix(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);> 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<$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 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 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 - -=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()); - -# 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 = apply_fraction_to_matrix_entries($A); # try commenting this line out for different results - -$Arref = rref($A); - -$Aswitch = row_switch($A, 2, 3); - -$Amult = row_mult($A, 2, 4); - -$Aadd = row_add($A, 2, 1, 10); - -$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); - -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; - - -ENDDOCUMENT(); - -=back - -=head1 AUTHORS - -Paul Pearson, Hope College, Department of Mathematics - -with help from - -Davide Cervone, Union College, Department of Mathematics - -Michael Doob, University of Manitoba, Department of Mathematics - -=cut - - - - -# Observation: a randomly generated 3 x 4 matrix has a very high probability -# of having rank 3. So, trying to generate random 3 x 4 matrices until a -# matrix of rank < 3 appears would be a bad idea. Also, there is no -# guarantee that a randomly generated matrix will represent a consistent -# system; however, empirical evidence suggests that the majority of the -# time a random 3 x 4 matrix is produced, it is rank 3 and represents -# a consistent system. Also, with randomly chosen matrices, it is -# possible to get rows or columns of zeros, so watch out! - -loadMacros("MathObjects.pl","contextFraction.pl",); - -################################################ -# rref: input and output are MathObject matrices. -# Should be run in Fraction context for best results. - -sub rref { - my $M = shift; - my @m = $M->value; - my @m_reduced = rref_perl_array(@m); - 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; -} - -sub rref_perl_array { - - # input and output have form @A = ([1,2,3,4],[5,6,7,8]); - my @A = @_; - my $m = scalar(@A); # number of rows - my $n = scalar(@{$A[0]}); # number of columns - - my $r = -1; - my $i = -1; - for my $j (0..$n-1) { - $i = $r + 1; - while ($i < $m and $A[$i][$j] == 0) { - $i = $i + 1; - } - if ($i != $m) { - $r = $r + 1; - # row switch: - @A[$i,$r] = @A[$r,$i]; - # row mult: scale row $r so that $A[$r][$j] = 1. - my $lambda = $A[$r][$j]; - if ($lambda != 0) { - foreach my $rowval ( @{$A[$r]} ) { $rowval /= $lambda; } - } - # row add: - for my $k (0..$m-1) { - if ($k == $r) { next; } - my $lambda = $A[$k][$j]; - foreach my $p (0..$n-1) { $A[$k][$p] -= $lambda * $A[$r][$p]; } - } - } - } - return @A; -} - - -################################################ -# This was written by Davide Cervone. -# http://webwork.maa.org/moodle/mod/forum/discuss.php?d=2970 - -sub change_matrix_entry { - my $self = shift; my $index = shift; my $x = shift; - my $i = shift(@$index) - 1; - if (scalar(@$index)) {change_matrix_entry($self->{data}[$i],$index,$x);} - else {$self->{data}[$i] = Value::makeValue($x); - } -} - -################################################ - -sub identity_matrix { - my $n = shift; - return Value::Matrix->I($n); -} - -################################################ - -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) = @_; - 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); - } - 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); - -} - -######################################### - -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) = @_; - if ($i < 1 or $i > $n) { - warn "Index out of bounds in elem_row_mult(). Returning identity matrix."; - return Value::Matrix->I($n); - } - if ($s == 0) { - warn "Scaling by zero not allowed in elem_row_mult(). Returning identity matrix."; - return Value::Matrix->I($n); - } - my $M = Value::Matrix->I($n); # construct identity matrix - my @m = $M->value; - foreach my $rowval ( @{$m[$i - 1]} ) { $rowval *= $s; } - return Matrix(@m); - -} - -###################################### - -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) = @_; - 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); - } - if ($s == 0) { - warn "Scaling by zero not allowed in elem_matrix_row_add(). Returning identity matrix."; - return Value::Matrix->I($n); - } - my $M = Value::Matrix->I($n); # construct identity matrix - my @m = $M->value; - $m[$i - 1][$j - 1] = $s; - return Matrix(@m); - -} - - - -############################################## - -sub row_add { - - # put (row i) + s * (row j) into (row i) - - my $M = shift; # MathObject matrix - my $i = shift; # row index - my $j = shift; # row index - my $s = shift; # scaling factor - - my ($r,$c) = $M->dimensions; - if (($i < 1) or ($j < 1) or ($i > $r) or ($j > $r)) { - warn "i = $i, j = $j, r = $r. Index out of bounds in row_add(). Returning original matrix."; - return $M; - } - 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.) Returning original matrix."; - return $M; - } - - # my $E = elem_matrix_row_add($r, $i, $j, $s); - # return $E * $M; - - my @m = $M->value; - foreach my $k (0..$c-1) { - $m[$i - 1][$k] += $s * $m[$j - 1][$k]; - } - return Matrix(@m); - -} - -#################################### - -sub row_switch { - - my $M = shift; # MathObject matrix - my $i1 = shift; # index of row to be swapped, indexing starts at 1 - my $i2 = shift; # index of row to be swapped - my ($r,$c) = $M->dimensions; - if ($i1 < 1 or $i2 < 1 or $i1 > $r or $i2 > $r) { - warn "Index out of bounds in row switch. Returning original matrix."; - return $M; - } - my @m = $M->value; - @m[$i1 - 1,$i2 - 1] = @m[$i2 - 1,$i1 - 1]; - return Matrix(@m); - -} - -######################################### - -sub row_mult { - - my $M = shift; # MathObject matrix - my $i = shift; # index of row to be scaled - my $s = shift; # scaling factor - my ($r,$c) = $M->dimensions; - if ($i < 1 or $i > $r) { - warn "Index out of bounds in row multiplication. Returning original matrix."; - return $M; - } - 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); - -} - -#################################### - -sub apply_fraction_to_matrix_entries { - - my $M = shift; - my @m = $M->value; - my ($r,$c) = $M->dimensions; - foreach my $i (0..$r-1) { - foreach my $rowval ( @{$m[$i]} ) { $rowval = Fraction("$rowval"); } - } - return Matrix(@m); - -} - - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/Hope/MatrixUnits.pl b/OpenProblemLibrary/macros/Hope/MatrixUnits.pl deleted file mode 100644 index 4de7f471bc..0000000000 --- a/OpenProblemLibrary/macros/Hope/MatrixUnits.pl +++ /dev/null @@ -1,310 +0,0 @@ -=pod - -=head1 NAME - -MatrixUnits.pl - -=head1 SYNOPSIS - -Generates unimodular n x n (n=2,3,4) MathObject matrices with real entries. - -=head1 DESCRIPTION - -This macro provides some routines for generating n x n (n=2,3,4) -MathObject matrices with real entries that have determinant in the -group of units of the integers Z (i.e., are Z-invertible). A matrix -C<$M = GLnZ();> (for det = +1 or -1) or <$M = SLnZ();> (for det = -1) -has all integer entries, is invertible, and its inverse has all integer entries. -The matrix entries are intentionally chosen to be integers close to zero -(so, if you want unimodular matrices with larger integer entries, you -may want to copy the source code and modify it to suit your needs). -The basic idea is to multiply several elementary matrices of -determinant +1 or -1 together to get a unimodular matrix (although the -code below accomplishes this by elementary row operations rather -than by multiplication of elementary matrices). - -=over 12 - -=item To produce a determinant +1 or -1 MathObject matrix C<$A>, use C<$A = GL2Z();>, C<$A = GL3Z();>, C<$A = GL4Z();> - -=item To produce a determinant +1 MathObject matrix C<$A>, use C<$A = SL2Z();>, C<$A = SL3Z();>, C<$A = SL4Z();> - -=item For determinant +1 or -1 perl arrays, use C<@a = GL2Z_perl();>, C<@a = GL3Z_perl();>, C<@a = GL4Z_perl();> - -=item For determinant +1 perl arrays, use C<@a = SL2Z_perl();>, C<@a = SL3Z_perl();>, C<@a = SL4Z_perl();> - -=back - -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 -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 -MathObject matrix via C<@b = $A-value;> (in which case C<@b> is an -array of MathObject reals). - -Note: There is overlap between MatrixUnits.pl (written after MathObject -matrices were created) and MatrixUnimodular.pl (written before MathObject -matrices were created). MatrixUnimodular.pl was left unchanged to provide -legacy support for existing PG files. - -=head1 AUTHORS - -Paul Pearson, Hope College, Department of Mathematics - -=cut - -sub _MatrixUnits_init {}; # don't reload this file - -loadMacros("MathObjects.pl",); - -################################################ -# - -sub GL2Z { - my @a = GL2Z_perl(); - return Matrix(@a); -} - -sub GL2Z_perl { - - # Create an invertible 2 x 2 matrix with determinant either 1 or -1 - - my $a11 = random(-1,1,2); - my $a12 = non_zero_random(-3,3,1); - my $mult = non_zero_random(-2,2,1); - my $a21 = $mult * $a11; - my $b1 = random(-1,1,2); - my $a22 = $mult * $a12 + $b1; - - # $a_det = $a11 * $b1; - return ([$a11,$a12],[$a21,$a22]); - -} - -################################################### - -sub SL2Z { - my @a = SL2Z_perl(); - return Matrix(@a); -} - -sub SL2Z_perl { - - # Create an invertible 2 x 2 matrix with determinant 1 - - my $a11 = random(-1,1,2); - my $a12 = non_zero_random(-3,3,1); - my $mult = non_zero_random(-2,2,1); - my $a21 = $mult * $a11; - my $b1 = random(-1,1,2); - my $a22 = $mult * $a12 + $b1; - - my $a_det = $a11 * $b1; - - if ($a_det == 1) { - return ([$a11,$a12],[$a21,$a22]); - } else { # det = -1, so swap rows to make det = 1 - return ([$a21,$a22],[$a11,$a12]); - } - -} - -####################################################### - -sub GL3Z { - my @a = GL3Z_perl(); - return Matrix(@a); -} - -sub GL3Z_perl { - - # Create an invertible 3 x 3 matrix with determinant either 1 or -1 - - my $a11 = random(-2,2,1); - my $a21 = random(-1,1,2); - my $a31 = random(-1,1,2); - - my $b1 = random(-1,1,2); - my $a12 = $b1 * $a11; - my $m = random(-1,1,2); - my $a22 = $b1 * $a21 + $m; - my $a32 = $b1 * $a31; - - my $c = random(-1,1,1); - my $d = random(-1,1,2); - my $n = random(-1,1,2); - my $a13 = $c * $a11 + $d * $a12 + $n; - my $a23 = $c * $a21 + $d * $a22; - my $a33 = $c * $a31 + $d * $a32; - - # $det = - $a31 * $m * $n; - - return ([$a11,$a12,$a13],[$a21,$a22,$a23],[$a31,$a32,$a33]); - -} - - -######################################################## - -sub SL3Z { - my @a = SL3Z_perl(); - return Matrix(@a); -} - -sub SL3Z_perl { - - # Create an invertible 3 x 3 matrix with determinant 1 - - my $a11 = random(-2,2,1); - my $a21 = random(-1,1,2); - my $a31 = random(-1,1,2); - - my $b1 = random(-1,1,2); - my $a12 = $b1 * $a11; - my $m = random(-1,1,2); - my $a22 = $b1 * $a21 + $m; - my $a32 = $b1 * $a31; - - my $c = random(-1,1,1); - my $d = random(-1,1,2); - my $n = random(-1,1,2); - my $a13 = $c * $a11 + $d * $a12 + $n; - my $a23 = $c * $a21 + $d * $a22; - my $a33 = $c * $a31 + $d * $a32; - - my $det = - $a31 * $m * $n; - - if ($det == 1) { - return ([$a11,$a12,$a13],[$a21,$a22,$a23],[$a31,$a32,$a33]); - } else { # det = -1, so swap two rows so that det = +1. - return ([$a21,$a22,$a23],[$a11,$a12,$a13],[$a31,$a32,$a33]); - } - -} - - -####################################################### - -sub GL4Z { - my @a = GL4Z_perl(); - return Matrix(@a); -} - - -sub GL4Z_perl { - - # Create an invertible 4 x 4 matrix with determinant either 1 or -1 - - my @a = (); - $a[1][1] = random(-2,2,1); - $a[2][1] = random(-1,1,2); - $a[3][1] = random(-1,1,2); - $a[4][1] = random(-1,1,1); - - my $b1 = random(-1,1,2); - foreach my $i (1..4) { - $a[$i][2] = $b1 * $a[$i][1]; - } - my $p = random(-1,1,2); - $a[2][2] = $a[2][2] + $p; - - my $c = random(-1,1,1); - my $d = random(-1,1,2); - my $n = random(-1,1,2); - foreach my $i (1..4) { - $a[$i][3] = $c * $a[$i][1] + $d * $a[$i][2]; - } - my $n = random(-1,1,2); - $a[1][3] = $a[1][3] + $n; - - my $f = random(-1,1,2); - my $g = random(-1,1,1); - my $h = random(-1,1,1); - foreach my $i (1..4) { - $a[$i][4] = $f * $a[$i][1] + $g * $a[$i][2] + $h * $a[$i][3]; - } - my $q = random(-1,1,2); - $a[4][4] = $a[4][4] + $q; - - # my $det = - $a[3][1] * $p * $n * $q; - - return ( - [ $a[1][1], $a[1][2], $a[1][3], $a[1][4] ], - [ $a[2][1], $a[2][2], $a[2][3], $a[2][4] ], - [ $a[3][1], $a[3][2], $a[3][3], $a[3][4] ], - [ $a[4][1], $a[4][2], $a[4][3], $a[4][4] ] - ); - -} - - -sub SL4Z { - my @a = SL4Z_perl(); - return Matrix(@a); -} - -sub SL4Z_perl { - - # Create an invertible 4 x 4 matrix with determinant 1 - - my @a = (); - $a[1][1] = random(-2,2,1); - $a[2][1] = random(-1,1,2); - $a[3][1] = random(-1,1,2); - $a[4][1] = random(-1,1,1); - - my $b1 = random(-1,1,2); - foreach my $i (1..4) { - $a[$i][2] = $b1 * $a[$i][1]; - } - my $p = random(-1,1,2); - $a[2][2] = $a[2][2] + $p; - - my $c = random(-1,1,1); - my $d = random(-1,1,2); - my $n = random(-1,1,2); - foreach my $i (1..4) { - $a[$i][3] = $c * $a[$i][1] + $d * $a[$i][2]; - } - my $n = random(-1,1,2); - $a[1][3] = $a[1][3] + $n; - - my $f = random(-1,1,2); - my $g = random(-1,1,1); - my $h = random(-1,1,1); - foreach my $i (1..4) { - $a[$i][4] = $f * $a[$i][1] + $g * $a[$i][2] + $h * $a[$i][3]; - } - my $q = random(-1,1,2); - $a[4][4] = $a[4][4] + $q; - - my $det = - $a[3][1] * $p * $n * $q; - - if ($det == 1) { - - return ( - [ $a[1][1], $a[1][2], $a[1][3], $a[1][4] ], - [ $a[2][1], $a[2][2], $a[2][3], $a[2][4] ], - [ $a[3][1], $a[3][2], $a[3][3], $a[3][4] ], - [ $a[4][1], $a[4][2], $a[4][3], $a[4][4] ] - ); - - } else { # det = -1, so swap two rows to make it have det = 1 - - return ( - [ $a[1][1], $a[1][2], $a[1][3], $a[1][4] ], - [ $a[3][1], $a[3][2], $a[3][3], $a[3][4] ], - [ $a[2][1], $a[2][2], $a[2][3], $a[2][4] ], - [ $a[4][1], $a[4][2], $a[4][3], $a[4][4] ] - ); - - } - -} - -################################################## - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/Hope/PGgraphgrid.pl b/OpenProblemLibrary/macros/Hope/PGgraphgrid.pl deleted file mode 100644 index 674f7bcc59..0000000000 --- a/OpenProblemLibrary/macros/Hope/PGgraphgrid.pl +++ /dev/null @@ -1,133 +0,0 @@ -sub _PGgraphgrid_init {}; # don't reload this file - -=pod - -=head1 NAME - -PGgraphgrid.pl - -=head1 SYNOPSIS - -Provides subroutines plotting a parallelogram grid on a -graph object created by PGgraphmacros.pl. - -=head1 DESCRIPTION - -Load the C macro file. - -=over 12 - -=item C - -= back - -The macro C provides two different subroutines -for graphing grids: the first one draws the grid using parallel lines -and should be fast, and a second that draws each individual -parallelogram and should be slow (but will suffer less from clipping): - -=over 12 - -=item C - -=item C - -=back - -Here C<$gr> is a graph object created by C, -C<$B> is a two by two MathObjects matrix, the integers C<$hmin> and -C<$vmin> specify one corner of the grid, the integers C<$hmax> and -C<$vmax> specify the opposite corner of the grid, C<$color> is a string -that specifies the drawing color, and C<$linewidth> is a positive integer -for the line width. - -=over 12 - -=head1 AUTHORS - -Paul Pearson, Hope College, Department of Mathematics - -=cut - -################################################ - -loadMacros("MathObjects.pl","PGgraphmacros.pl",); - - -sub graphgrid1 { - # inputs - my $gr = shift; # graph object - my $B = shift; # 2x2 basis matrix with B = (b1 | b2), with basis vectors in columns - my $xmin = shift; my $ymin = shift; my $xmax = shift; my $ymax = shift; # (xmin,ymin) and (xmax,ymax) are two of the corners of the grid (must be integers) - my $color = shift; # grid color - my $linewidth = shift; # line thickness - - # standard coordinates for horizontal lines - my @hx = (); # horizontal lines, x values - my @hy = (); # horizontal lines, y values - foreach my $y ($ymin..$ymax) { - push(@hx, ($xmin,$xmax)); - push(@hy, ($y,$y)); - } - my $H = Matrix([ - [@hx], - [@hy] - ]); - my $BH = $B * $H; - my @bh = $BH->value; - # draw parallelogram grid on graph object - # "horizontal" lines - my $bh_ncol = ($BH->dimensions)[1]; - foreach my $i (0..$bh_ncol/2) { - $gr->moveTo($bh[0][2*$i],$bh[1][2*$i]); $gr->lineTo($bh[0][2*$i+1],$bh[1][2*$i+1],$color,$linewidth); - } - - # standard coordinates for vertical lines - my @vx = (); # vertical lines, x values - my @vy = (); # vertical lines, y values - foreach my $x ($xmin..$xmax) { - push(@vx, ($x,$x)); - push(@vy, ($ymin,$ymax)); - } - my $V = Matrix([ - [@vx], - [@vy] - ]); - my $BV = $B * $V; - my @bv = $BV->value; - # draw parallelogram grid on graph object - # "vertical" lines - my $bv_ncol = ($BV->dimensions)[1]; - foreach my $i (0..$bv_ncol/2) { - $gr->moveTo($bv[0][2*$i],$bv[1][2*$i]); $gr->lineTo($bv[0][2*$i+1],$bv[1][2*$i+1],$color,$linewidth); - } - -} # end graphgrid1 - - -sub graphgrid2 { - # inputs - my $gr = shift; # graph object - my $B = shift; # 2x2 basis matrix with B = (b1 | b2), with basis vectors in columns - my $xmin = shift; my $ymin = shift; my $xmax = shift; my $ymax = shift; # (xmin,ymin) and (xmax,ymax) are two of the corners of the grid (must be integers) - my $color = shift; # grid color - my $linewidth = shift; # line thickness - - foreach my $x ($xmin..$xmax) { - foreach my $y ($ymin..$ymax) { - my $X = Matrix([ - [$x,$x+1,$x+1,$x,$x], - [$y,$y,$y+1,$y+1,$y] - ]); - my $BX = $B * $X; - my @bx = $BX->value; - $gr->moveTo($bx[0][0], $bx[1][0]); - $gr->lineTo($bx[0][1], $bx[1][1], $color, $linewidth); - $gr->lineTo($bx[0][2], $bx[1][2], $color, $linewidth); - $gr->lineTo($bx[0][3], $bx[1][3], $color, $linewidth); - $gr->lineTo($bx[0][4], $bx[1][4], $color, $linewidth); - } - } -} # end graphgrid2 - -1; diff --git a/OpenProblemLibrary/macros/Hope/VectorListCheckers.pl b/OpenProblemLibrary/macros/Hope/VectorListCheckers.pl deleted file mode 100644 index 37af04759b..0000000000 --- a/OpenProblemLibrary/macros/Hope/VectorListCheckers.pl +++ /dev/null @@ -1,282 +0,0 @@ -sub _VectorListCheckers_init {}; # don't reload this file - -=pod - -=head1 NAME - -VectorListCheckers.pl - -=head1 SYNOPSIS - -Provides subroutines for answer checking lists MathObjects -vectors with real entries. - -=head1 DESCRIPTION - -First, load the C macro file. - -=over 12 - -=item C - -= back - -For a MathObject list of MathObject vectors, the way to use the -answer checkers is the same as using a custom answer checker -inside of C~~&name_of_answer_checker_subroutine)> -such as - -=over 12 - -=item Ccmp( checker=~~&basis_checker_list_of_vectors ) );> - -=item C + s * <0,1,0> + t * <0,0,1>")-cmp( checker=~~&affine_subspace_checker_vectors ) );> - -=back - -The "list of vectors" at the end of the checker name refers to the -fact that the student answer is a list of vectors. - -Here is an example of how to use these answer checkers. - -=over 12 - -DOCUMENT(); -loadMacros( -"PGstandard.pl", -"MathObjects.pl", -"VectorListCheckers.pl", -"PGcourse.pl", -); -$showPartialCorrectAnswers = 1; -TEXT(beginproblem()); - -Context('Vector'); - -$B = Matrix([[1,0,0],[0,1,0],[0,0,0]]); - -$answer = List(ColumnVector(1,0,0),ColumnVector(0,1,0)); - -Context()->texStrings; -BEGIN_TEXT -A basis for the column space of \( B = $B \) is -$BR -$BR -\{ $answer->ans_rule(60) \} -$BR -$BR -Enter your answer as a comma separated list of vectors, such as -\( \verb+<1,2,3>,<4,5,6>+ \). -END_TEXT -Context()->normalStrings; - -ANS( $answer->cmp(list_checker=>~~&basis_checker_list_of_vectors) ); - - - -ENDDOCUMENT(); - -=back - -The answer evaluation section of the PG file is totally standard. - -=over 12 - -ANS( $multians->cmp ); - -=back - -The C should be used for -solutions to non-homogeneous systems of linear equations for -which the solution is essentially a point plus the span of -several linearly independent vectors. When using the parametric -plane checker, the first vector input always serves as a point -on the hyperplane (i.e., the first vector input is always a -particular solution), while the remaining vectors are a basis for -the hyperplane (i.e., they span the homogeneous solution set). - -=head1 AUTHORS - -Paul Pearson, Hope College, Department of Mathematics - -=cut - - - -################################################ - -loadMacros("MathObjects.pl",); - -sub basis_checker_list_of_vectors { - - my ($correct, $student, $ansHash, $value) = @_; - my @c = @{$correct}; - my @s = @{$student}; - my $nc = scalar(@c); - my $ns = scalar(@s); - my $score = 0; - my @errors = (); - - return ($score,@errors) if $nc != $ns; - - if ($nc == 1) { - - if (Vector($s[0])->isParallel($c[0])) { return ($nc,@errors); } - - } else { - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - # put the correct vectors into the columns of a matrix $C - my @cor = (); - foreach my $i (0..$nc-1) { - push(@cor, Matrix($c[$i]) ); - } - my $C = Matrix(@cor)->transpose; - - # put the student vectors into the columns of a matrix $S - my @stu = (); - foreach my $i (0..$ns-1) { - push(@stu, Matrix($s[$i]) ); - } - my $S = Matrix(@stu)->transpose; - - - # Put $C and $S into the local context so that - # all of the computations that follow will also be in - # the local context. - $C = Matrix($context,$C); - $S = Matrix($context,$S); - - # Theorem: A^T A is invertible if and only if A has linearly independent columns. - - # Check that the professor's vectors are, in fact, linearly independent. - $CTC = ($C->transpose) * $C; - warn "Correct answer is a linearly dependent set." if ($CTC->det == 0); - - # Check that the student's vectors are linearly independent - if ( (($S->transpose) * $S)->det == 0) { - Value->Error("Your vectors are linearly dependent"); - return 0; - } - - # S = student, C = correct, X = change of basis matrix - # Solve S = CX for X using (C^T C)^{-1} C^T S = X. - $X = ($CTC->inverse) * (($C->transpose) * $S); - if ( $S == $C * $X ) { $score = $nc; }; - - return ($score,@errors); - - } - -} - -######################################################## - -$vector_list_column_syntax_angle_brackets = MODES(TeX=>'',HTML=> -"Enter a column vector such as -\\( \\left\\lbrack \\begin{array}{r} 1 \\\\ 2 \\end{array} \\right\\rbrack \\) -using the syntax \\( \\verb+<1,2>+ \\). -If there is more than one vector in your answer, enter -your answer as a comma separated list of vectors, such as -\\( \\verb+<1,2>,<3,4>+ \\)." -); - -######################################################## - -$vector_list_row_syntax_angle_brackets = MODES(TeX=>'',HTML=> -"Enter a row vector using the syntax \\( \\verb+<1,2>+ \\). -If there is more than one vector in your answer, enter -your answer as a comma separated list of vectors, such as -\\( \\verb+<1,2>,<3,4>+ \\)." -); - -######################################################## - -sub affine_subspace_checker_vectors { - - my ( $correct, $student, $ansHash ) = @_; - my @s = (); - my @c = (); - - # Most of the answer checking is done on integers - # or on decimals like 0.24381729, so we will set the - # tolerance accordingly in a local context. - my $context = Context()->copy; - $context->flags->set( - tolerance => 0.001, - tolType => "absolute", - ); - - # Get the variables from the context - my @vars = $context->variables->names; - - # Make an array of zeros the same length as @vars - my @zeros = (); - foreach (@vars) { push(@zeros,0); } - - # Evaluate the correct and student answers on the zero vector - my %h; - @h{@vars} = @zeros; - push(@c,Matrix($context,$correct->eval(%h))); - push(@s,Matrix($context,$student->eval(%h))); - - # Make standard basis vectors e_i with ith entry = 1 and all other entries = 0 - # and evaluate the correct and student answers on e_i - my @temp = (); - foreach my $i (0..$#vars) { - @temp = @zeros; - $temp[$i] = 1; - @h{@vars} = @temp; - my $c_temp = Matrix($context,$correct->eval(%h)); - my $s_temp = Matrix($context,$student->eval(%h)); - $c_temp = $c_temp - $c[0]; - $s_temp = $s_temp - $s[0]; - push(@c,$c_temp); - push(@s,$s_temp); - } - - # Put the results into the columns of matrices - my $C0 = Matrix($context,shift(@c))->transpose; # column vector = displacement vector - my $C = Matrix($context,@c)->transpose; # matrix columns = vectors that span the hyperplane - my $S0 = Matrix($context,shift(@s))->transpose; # column vector = displacement vector - my $S = Matrix($context,@s)->transpose; # matrix columns = vectors that span the hyperplane - - # Theorem: A^T A is invertible if and only if A has linearly independent columns. - - # Check that the professor's vectors are, in fact, linearly independent. - $CTC = ($C->transpose) * $C; - warn "Correct answer is a linearly dependent set." if ($CTC->det == 0); - - # Check that the student's vectors are linearly independent - if ( (($S->transpose) * $S)->det == 0) { - Value->Error("Your vectors do not span a (hyper) plane of the correct dimension"); - return 0; - } - - # solve (S_0 = C_0 + C A) for the column vector A of weights using - # (S_0 - C_0) = C A - # C^T (S_0 - C_0) = C^T C A - # (C^T C)^{-1} C^T (S_0 - C_0) = A - my $A = ($CTC->inverse) * ($C->transpose) * ($S0 - $C0); - if ($S0 != $C0 + $C*$A) { - #Value->Error("Your particular solution $S0 is incorrect"); - return 0; - } - - # S = student, C = correct, X = change of basis matrix - # Solve S = CX for X using (C^T C)^{-1} C^T S = X. - $X = ($CTC->inverse) * (($C->transpose) * $S); - return $S == $C * $X; - -} - - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/LaTech/SI_property_tables.pl b/OpenProblemLibrary/macros/LaTech/SI_property_tables.pl deleted file mode 100644 index c0d9c6f168..0000000000 --- a/OpenProblemLibrary/macros/LaTech/SI_property_tables.pl +++ /dev/null @@ -1,551 +0,0 @@ -# SI_property_tables.pl -# Rename this file (with .pl extension) and place it in your course macros directory, - -@SI_property_tables_names = ( -"SI_water_Tsat ", "SI_water_Psat ","SI_water_vf ","SI_water_vfg ","SI_water_vg ", "SI_water_uf ", "SI_water_ufg ", "SI_water_ug ", "SI_water_hf ", "SI_water_hfg ", "SI_water_hg ", "SI_water_sf ", "SI_water_sfg ", "SI_water_sg ", - -"SI_air_T ", "SI_air_h ", "SI_air_Pr ", "SI_air_u ", "SI_air_vr ", "SI_air_so ", - -"SI_superWater_0p1MPa_T ", "SI_superWater_0p1MPa_v ", "SI_superWater_0p1MPa_u ","SI_superWater_0p1MPa_h ","SI_superWater_0p1MPa_s ", - - - - -"SI_superWater_1p2MPa_T ", "SI_superWater_1p2MPa_v ", "SI_superWater_1p2MPa_u ","SI_superWater_1p2MPa_h ","SI_superWater_1p2MPa_s ", - -"SI_superWater_1p4MPa_T ", "SI_superWater_1p4MPa_v ", "SI_superWater_1p4MPa_u ","SI_superWater_1p4MPa_h ","SI_superWater_1p4MPa_s ", - -"SI_superWater_1p6MPa_T ", "SI_superWater_1p6MPa_v ", "SI_superWater_1p6MPa_u ","SI_superWater_1p6MPa_h ","SI_superWater_1p6MPa_s ", - -"SI_superWater_1p8MPa_T ", "SI_superWater_1p8MPa_v ", "SI_superWater_1p8MPa_u ","SI_superWater_1p8MPa_h ","SI_superWater_1p8MPa_s ", - - - - - - -"SI_superWater_10MPa_T ", "SI_superWater_10MPa_v ", "SI_superWater_10MPa_u ","SI_superWater_10MPa_h ","SI_superWater_10MPa_s ", - - - - - - -"SI_R134a_Tsat ", "SI_R134a_Psat ", "SI_R134a_vf ", "SI_R134a_vfg ", "SI_R134a_vg ", "SI_R134a_uf ", "SI_R134a_ufg ", "SI_R134a_ug ", "SI_R134a_hf ", "SI_R134a_hfg ", "SI_R134a_hg ", "SI_R134a_sf ", "SI_R134a_sfg ", "SI_R134a_sg ", - - - - - - - -"SI_superR134a_1p2MPa_T ", "SI_superR134a_1p2MPa_v ", "SI_superR134a_1p2MPa_u ", "SI_superR134a_1p2MPa_h ", "SI_superR134a_1p2MPa_s ", - -"SI_superR134a_1p2MPa_T ", "SI_superR134a_1p2MPa_v ", "SI_superR134a_1p2MPa_u ", "SI_superR134a_1p2MPa_h ", "SI_superR134a_1p2MPa_s ", - -"SI_superR134a_1p2MPa_T ", "SI_superR134a_1p2MPa_v ", "SI_superR134a_1p2MPa_u ", "SI_superR134a_1p2MPa_h ", "SI_superR134a_1p2MPa_s ", - - - - -"SI_superR134a_1p2MPa_T ", "SI_superR134a_1p2MPa_v ", "SI_superR134a_1p2MPa_u ", "SI_superR134a_1p2MPa_h ", "SI_superR134a_1p2MPa_s " -); - -####################################################### -####################################################### -### ### -### below are the lists of the tabulated values ### -### ### -####################################################### -####################################################### - -########################### -### ### -### saturated water ### -### ### -########################### - -@SI_water_Tsat = qw( -0.01 5.00 6.97 10.00 13.02 15.00 17.50 20.00 21.08 24.08 25.00 28.96 30.00 32.87 35.00 40.00 40.29 45.00 45.81 50.00 53.97 55.00 60.00 60.06 65.00 69.09 70.00 75.00 75.86 80.00 81.32 85.00 90.00 91.76 95.00 99.61 105.00 105.97 110.00 111.35 115.00 116.04 120.00 120.21 123.97 125.00 127.41 130.00 133.52 135.00 136.27 138.86 140.00 141.30 143.61 145.00 147.90 150.00 151.83 155.00 155.46 158.83 160.00 161.98 165.00 167.75 170.00 170.41 172.94 175.00 177.66 180.00 184.06 185.00 187.96 190.00 191.60 195.00 198.29 200.00 205.00 205.72 210.00 212.38 215.00 218.41 220.00 223.95 225.00 230.00 235.00 240.00 242.56 245.00 250.00 255.00 260.00 263.94 265.00 270.00 275.00 275.59 280.00 285.00 285.83 290.00 295.00 300.00 303.35 305.00 310.00 311.00 315.00 318.08 320.00 324.68 325.00 330.00 330.85 335.00 336.67 340.00 342.16 345.00 347.36 350.00 352.29 355.00 356.99 360.00 361.47 365.00 365.75 369.83 370.00 373.71 373.95 -); - -@SI_water_Psat = qw( -0.6117 0.8725 1.0000 1.2281 1.5000 1.7057 2.0000 2.3392 2.5000 3.0000 3.1698 4.0000 4.2469 5.0000 5.6291 7.3851 7.5000 9.5953 10.0000 12.3520 15.0000 15.7630 19.9470 20.0000 25.0430 30.0000 31.2020 38.5970 40.0000 47.4160 50.0000 57.8680 70.1830 75.0000 84.6090 100.0000 120.9000 125.0000 143.3800 150.0000 169.1800 175.0000 198.6700 200.0000 225.0000 232.2300 250.0000 270.2800 300.0000 313.2200 325.0000 350.0000 361.5300 375.0000 400.0000 415.6800 450.0000 476.1600 500.0000 543.4900 550.0000 600.0000 618.2300 650.0000 700.9300 750.0000 792.1800 800.0000 850.0000 892.6000 950.0000 1002.8000 1100.0000 1123.5000 1200.0000 1255.2000 1300.0000 1398.8000 1500.0000 1554.9000 1724.3000 1750.0000 1907.7000 2000.0000 2105.9000 2250.0000 2319.6000 2500.0000 2549.7000 2797.1000 3062.6000 3347.0000 3500.0000 3651.2000 3976.2000 4322.9000 4692.3000 5000.0000 5085.3000 5503.0000 5946.4000 6000.0000 6416.6000 6914.6000 7000.0000 7441.8000 7999.0000 8587.9000 9000.0000 9209.4000 9865.0000 10000.0000 10556.0000 11000.0000 11284.0000 12000.0000 12051.0000 12858.0000 13000.0000 13707.0000 14000.0000 14601.0000 15000.0000 15541.0000 16000.0000 16529.0000 17000.0000 17570.0000 18000.0000 18666.0000 19000.0000 19822.0000 20000.0000 21000.0000 21044.0000 22000.0000 22064.0000 -); - -@SI_water_vf = qw( -0.001 0.001 0.001 0.001 0.001001 0.001001 0.001001 0.001002 0.001002 0.001003 0.001003 0.001004 0.001004 0.001005 0.001006 0.001008 0.001008 0.00101 0.00101 0.001012 0.001014 0.001015 0.001017 0.001017 0.00102 0.001022 0.001023 0.001026 0.001026 0.001029 0.00103 0.001032 0.001036 0.001037 0.00104 0.001043 0.001047 0.001048 0.001052 0.001053 0.001056 0.001057 0.00106 0.001061 0.001064 0.001065 0.001067 0.00107 0.001073 0.001075 0.001076 0.001079 0.00108 0.001081 0.001084 0.001085 0.001088 0.001091 0.001093 0.001096 0.001097 0.001101 0.001102 0.001104 0.001108 0.001111 0.001114 0.001115 0.001118 0.001121 0.001124 0.001127 0.001133 0.001134 0.001138 0.001141 0.001144 0.001149 0.001154 0.001157 0.001164 0.001166 0.001173 0.001177 0.001181 0.001187 0.00119 0.001197 0.001199 0.001209 0.001219 0.001229 0.001235 0.00124 0.001252 0.001263 0.001276 0.001286 0.001289 0.001303 0.001317 0.001319 0.001333 0.001349 0.001352 0.001366 0.001384 0.001404 0.001418 0.001425 0.001447 0.001452 0.001472 0.001488 0.001499 0.001526 0.001528 0.00156 0.001566 0.001597 0.00161 0.001638 0.001657 0.001685 0.00171 0.001741 0.00177 0.001808 0.00184 0.001895 0.001926 0.002015 0.002038 0.002207 0.002217 0.002703 0.003106 -); - -@SI_water_vfg = qw( -205.999000 147.029000 129.189000 106.319000 87.962999 77.883999 66.988999 57.760998 54.240998 45.652997 43.338997 34.789996 32.877996 28.183995 25.203994 19.513992 19.231992 15.249990 14.668990 12.024988 10.018986 9.562885 7.665983 7.647083 6.192480 5.227678 5.038577 4.128074 3.992274 3.404271 3.239270 2.825068 2.358264 2.216163 1.979760 1.693057 1.417553 1.373952 1.208348 1.158347 1.034944 1.002643 0.890270 0.884719 0.792226 0.769055 0.717663 0.667010 0.604747 0.580715 0.560914 0.523141 0.507420 0.490249 0.461336 0.444915 0.412832 0.391389 0.373737 0.345384 0.341513 0.314499 0.305698 0.291496 0.271332 0.254409 0.241486 0.239235 0.225782 0.215469 0.202986 0.192713 0.176317 0.172766 0.162122 0.155219 0.150046 0.139741 0.130556 0.126053 0.113916 0.112274 0.103117 0.098410 0.093499 0.087530 0.084904 0.078755 0.077206 0.070296 0.064081 0.058478 0.055826 0.053416 0.048833 0.044678 0.040899 0.038162 0.037459 0.034319 0.031450 0.031130 0.028820 0.026407 0.026026 0.024188 0.022144 0.020255 0.019071 0.018507 0.016886 0.016576 0.015377 0.014500 0.013971 0.012738 0.012655 0.011419 0.011215 0.010251 0.009877 0.009145 0.008684 0.008087 0.007602 0.007065 0.006604 0.006064 0.005664 0.005055 0.004751 0.003994 0.003824 0.002787 0.002736 0.000941 0.000000 -); - -@SI_water_vg = qw( -206 147.03 129.19 106.32 87.964 77.885 66.99 57.762 54.242 45.654 43.34 34.791 32.879 28.185 25.205 19.515 19.233 15.251 14.67 12.026 10.02 9.5639 7.667 7.6481 6.1935 5.2287 5.0396 4.1291 3.9933 3.4053 3.2403 2.8261 2.3593 2.2172 1.9808 1.6941 1.4186 1.375 1.2094 1.1594 1.036 1.0037 0.89133 0.88578 0.79329 0.77012 0.71873 0.66808 0.60582 0.58179 0.56199 0.52422 0.5085 0.49133 0.46242 0.446 0.41392 0.39248 0.37483 0.34648 0.34261 0.3156 0.3068 0.2926 0.27244 0.25552 0.2426 0.24035 0.2269 0.21659 0.20411 0.19384 0.17745 0.1739 0.16326 0.15636 0.15119 0.14089 0.13171 0.12721 0.11508 0.11344 0.10429 0.099587 0.09468 0.088717 0.086094 0.079952 0.078405 0.071505 0.0653 0.059707 0.057061 0.054656 0.050085 0.045941 0.042175 0.039448 0.038748 0.035622 0.032767 0.032449 0.030153 0.027756 0.027378 0.025554 0.023528 0.021659 0.020489 0.019932 0.018333 0.018028 0.016849 0.015988 0.01547 0.014264 0.014183 0.012979 0.012781 0.011848 0.011487 0.010783 0.010341 0.009772 0.009312 0.008806 0.008374 0.007872 0.007504 0.00695 0.006677 0.006009 0.005862 0.004994 0.004953 0.003644 0.003106 -); - -@SI_water_uf = qw( -0 21.019 29.302 42.02 54.686 62.98 73.431 83.913 88.422 100.98 104.83 121.39 125.73 137.75 146.63 167.53 168.74 188.43 191.79 209.33 225.93 230.24 251.16 251.4 272.09 289.24 293.04 313.99 317.58 334.97 340.49 355.96 376.97 384.36 398 417.4 440.15 444.23 461.27 466.97 482.42 486.82 503.6 504.5 520.47 524.83 535.08 546.1 561.11 567.41 572.84 583.89 588.77 594.32 604.22 610.19 622.65 631.66 639.54 653.19 655.16 669.72 674.79 683.37 696.46 708.4 718.2 719.97 731 740.02 751.67 761.92 779.78 783.91 796.96 806 813.1 828.18 842.82 850.46 872.86 876.12 895.38 906.12 918.02 933.54 940.79 958.87 963.7 986.76 1010 1033.4 1045.4 1056.9 1080.7 1104.7 1128.8 1148.1 1153.3 1177.9 1202.9 1205.8 1228.2 1253.7 1258 1279.7 1306 1332.7 1350.9 1360 1387.7 1393.3 1416.1 1433.9 1445.1 1473 1475 1505.7 1511 1537.5 1548.4 1570.7 1585.5 1605.5 1622.6 1642.4 1660.2 1682.2 1699.1 1726.2 1740.3 1777.2 1785.8 1841.6 1844.5 1951.7 2015.7 -); - -@SI_water_ufg = qw( -2374.9 2360.8 2355.2 2346.6 2338.1 2332.5 2325.5 2318.4 2315.4 2306.9 2304.3 2293.1 2290.2 2282.1 2276 2261.9 2261.1 2247.7 2245.4 2233.4 2222.1 2219.1 2204.7 2204.6 2190.3 2178.5 2175.8 2161.3 2158.8 2146.6 2142.7 2131.9 2117 2111.8 2102 2088.2 2071.8 2068.8 2056.4 2052.3 2040.9 2037.7 2025.3 2024.6 2012.7 2009.5 2001.8 1993.4 1982.1 1977.3 1973.1 1964.6 1960.9 1956.6 1948.9 1944.2 1934.5 1927.4 1921.2 1910.3 1908.8 1897.1 1893 1886.1 1875.4 1865.6 1857.5 1856.1 1846.9 1839.4 1829.6 1820.9 1805.7 1802.1 1790.9 1783 1776.8 1763.6 1750.6 1743.7 1723.5 1720.6 1702.9 1693 1681.9 1667.3 1660.5 1643.2 1638.6 1616.1 1593.2 1569.8 1557.6 1545.7 1521.1 1495.8 1469.9 1448.9 1443.2 1415.7 1387.4 1384.1 1358.2 1328.1 1323 1296.9 1264.5 1230.9 1207.6 1195.9 1159.3 1151.8 1121.1 1096.6 1080.9 1041.3 1038.5 993.5 985.5 945.5 928.7 893.8 870.3 837.7 809.4 775.9 745.1 706.4 675.9 625.7 598.9 526.4 509 391.9 385.6 140.8 0 -); - -@SI_water_ug = qw( -2374.9 2381.8 2384.5 2388.7 2392.8 2395.5 2398.9 2402.3 2403.8 2407.9 2409.1 2414.5 2415.9 2419.8 2422.7 2429.4 2429.8 2436.1 2437.2 2442.7 2448 2449.3 2455.9 2456 2462.4 2467.7 2468.9 2475.3 2476.3 2481.6 2483.2 2487.8 2494 2496.1 2500.1 2505.6 2511.9 2513 2517.7 2519.2 2523.3 2524.5 2528.9 2529.1 2533.2 2534.3 2536.8 2539.5 2543.2 2544.7 2545.9 2548.5 2549.6 2550.9 2553.1 2554.4 2557.1 2559.1 2560.7 2563.5 2563.9 2566.8 2567.8 2569.4 2571.9 2574 2575.7 2576 2577.9 2579.4 2581.3 2582.8 2585.5 2586 2587.8 2589 2589.9 2591.7 2593.4 2594.2 2596.4 2596.7 2598.3 2599.1 2599.9 2600.9 2601.3 2602.1 2602.3 2602.9 2603.2 2603.1 2603 2602.7 2601.8 2600.5 2598.7 2597 2596.5 2593.7 2590.3 2589.9 2586.4 2581.8 2581 2576.5 2570.5 2563.6 2558.5 2555.8 2547.1 2545.2 2537.2 2530.4 2526 2514.3 2513.4 2499.2 2496.6 2483 2477.1 2464.5 2455.7 2443.2 2432 2418.3 2405.4 2388.6 2375 2351.9 2339.2 2303.6 2294.8 2233.5 2230.1 2092.4 2015.7 -); - -@SI_water_hf = qw( -0.001 21.02 29.303 42.022 54.688 62.982 73.433 83.915 88.424 100.98 104.83 121.39 125.74 137.75 146.64 167.53 168.75 188.44 191.81 209.34 225.94 230.26 251.18 251.42 272.12 289.27 293.07 314.03 317.62 335.02 340.54 356.02 377.04 384.44 398.09 417.51 440.28 444.36 461.42 467.13 482.59 487.01 503.81 504.71 520.71 525.07 535.35 546.38 561.43 567.75 573.19 584.26 589.16 594.73 604.66 610.64 623.14 632.18 640.09 653.79 655.77 670.38 675.47 684.08 697.24 709.24 719.08 720.87 731.95 741.02 752.74 763.05 781.03 785.19 798.33 807.43 814.59 829.78 844.55 852.26 874.87 878.16 897.61 908.47 920.5 936.21 943.55 961.87 966.76 990.14 1013.7 1037.5 1049.7 1061.5 1085.7 1110.1 1134.8 1154.5 1159.8 1185.1 1210.7 1213.8 1236.7 1263.1 1267.5 1289.8 1317.1 1344.8 1363.7 1373.1 1402 1407.8 1431.6 1450.2 1462 1491.3 1493.4 1525.8 1531.4 1559.4 1571 1594.6 1610.3 1631.7 1649.9 1671.2 1690.3 1714 1732.2 1761.5 1776.8 1817.2 1826.6 1888 1891.2 2011.1 2084.3 -); - -@SI_water_hfg = qw( -2500.9 2489.1 2484.4 2477.2 2470.1 2465.4 2459.5 2453.5 2451 2443.9 2441.7 2432.3 2429.8 2423 2417.9 2406 2405.3 2394 2392.1 2382 2372.3 2369.8 2357.7 2357.5 2345.4 2335.3 2333 2320.6 2318.4 2308 2304.7 2295.3 2282.5 2278 2269.6 2257.5 2243.1 2240.6 2229.7 2226 2216 2213.1 2202.1 2201.6 2191 2188.1 2181.2 2173.7 2163.5 2159.1 2155.4 2147.7 2144.3 2140.4 2133.4 2129.2 2120.3 2113.8 2108 2098 2096.6 2085.8 2082 2075.5 2065.6 2056.4 2048.8 2047.5 2038.8 2031.7 2022.4 2014.2 1999.6 1996.2 1985.4 1977.9 1971.9 1959 1946.4 1939.8 1920 1917.1 1899.7 1889.8 1878.8 1864.3 1857.4 1840.1 1835.4 1812.8 1789.5 1765.5 1753 1740.8 1715.3 1689 1661.8 1639.7 1633.7 1604.6 1574.5 1570.9 1543.2 1510.7 1505.2 1476.9 1441.6 1404.8 1379.3 1366.3 1325.9 1317.6 1283.4 1256.1 1238.5 1194.1 1191 1140.3 1131.3 1086 1067 1027.4 1000.5 963.4 931.1 892.7 857.4 812.9 777.8 720.1 689.2 605.5 585.5 450.4 443.1 161.5 0 -); - -@SI_water_hg = qw( -2500.9 2510.1 2513.7 2519.2 2524.7 2528.3 2532.9 2537.4 2539.4 2544.8 2546.5 2553.7 2555.6 2560.7 2564.6 2573.5 2574 2582.4 2583.9 2591.3 2598.3 2600.1 2608.8 2608.9 2617.5 2624.6 2626.1 2634.6 2636.1 2643 2645.2 2651.4 2659.6 2662.4 2667.6 2675 2683.4 2684.9 2691.1 2693.1 2698.6 2700.2 2706 2706.3 2711.7 2713.1 2716.5 2720.1 2724.9 2726.9 2728.6 2732 2733.5 2735.1 2738.1 2739.8 2743.4 2745.9 2748.1 2751.8 2752.4 2756.2 2757.5 2759.6 2762.8 2765.7 2767.9 2768.3 2770.8 2772.7 2775.2 2777.2 2780.7 2781.4 2783.8 2785.3 2786.5 2788.8 2791 2792 2794.8 2795.2 2797.3 2798.3 2799.3 2800.5 2801 2801.9 2802.2 2802.9 2803.2 2803 2802.7 2802.2 2801 2799.1 2796.6 2794.2 2793.5 2789.7 2785.2 2784.6 2779.9 2773.7 2772.6 2766.7 2758.7 2749.6 2742.9 2739.4 2727.9 2725.5 2715 2706.3 2700.6 2685.4 2684.3 2666 2662.7 2645.4 2637.9 2622 2610.8 2595.1 2581 2563.9 2547.7 2526.9 2510 2481.6 2466 2422.7 2412.1 2338.4 2334.3 2172.6 2084.3 -); - -@SI_water_sf = qw( -0 0.0763 0.1059 0.1511 0.1956 0.2245 0.2606 0.2965 0.3118 0.3543 0.3672 0.4224 0.4368 0.4762 0.5051 0.5724 0.5763 0.6386 0.6492 0.7038 0.7549 0.768 0.8313 0.832 0.8937 0.9441 0.9551 1.0158 1.0261 1.0756 1.0912 1.1346 1.1929 1.2132 1.2504 1.3028 1.3634 1.3741 1.4188 1.4337 1.4737 1.485 1.5279 1.5302 1.5706 1.5816 1.6072 1.6346 1.6717 1.6872 1.7005 1.7274 1.7392 1.7526 1.7765 1.7908 1.8205 1.8418 1.8604 1.8924 1.897 1.9308 1.9426 1.9623 1.9923 2.0195 2.0417 2.0457 2.0705 2.0906 2.1166 2.1392 2.1785 2.1875 2.2159 2.2355 2.2508 2.2831 2.3143 2.3305 2.3776 2.3844 2.4245 2.4467 2.4712 2.5029 2.5176 2.5542 2.5639 2.61 2.656 2.7018 2.7253 2.7476 2.7933 2.839 2.8847 2.9207 2.9304 2.9762 3.0221 3.0275 3.0681 3.1144 3.122 3.1608 3.2076 3.2548 3.2866 3.3024 3.3506 3.3603 3.3994 3.4299 3.4491 3.4964 3.4998 3.5516 3.5606 3.605 3.6232 3.6602 3.6848 3.7179 3.7461 3.7788 3.8082 3.8442 3.872 3.9165 3.9396 4.0004 4.0146 4.1071 4.1119 4.2942 4.407 -); - -@SI_water_sfg = qw( -9.1556 8.9487 8.869 8.7488 8.6314 8.5559 8.4621 8.3696 8.3302 8.2222 8.1895 8.051 8.0152 7.9176 7.8466 7.6832 7.6738 7.5247 7.4996 7.371 7.2522 7.2218 7.0769 7.0752 6.936 6.8234 6.7989 6.6655 6.643 6.5355 6.5019 6.4089 6.2853 6.2426 6.1647 6.0562 5.9319 5.91 5.8193 5.7894 5.7092 5.6865 5.6013 5.5968 5.5171 5.4956 5.4453 5.3919 5.32 5.2901 5.2645 5.2128 5.1901 5.1645 5.1191 5.0919 5.0356 4.9953 4.9603 4.9002 4.8916 4.8285 4.8066 4.7699 4.7143 4.6642 4.6233 4.616 4.5705 4.5335 4.4862 4.4448 4.3735 4.3572 4.3058 4.2705 4.2428 4.1847 4.1287 4.0997 4.0154 4.0033 3.9318 3.8923 3.8489 3.7926 3.7664 3.7016 3.6844 3.6028 3.5216 3.4405 3.3991 3.3596 3.2788 3.1979 3.1169 3.053 3.0358 2.9542 2.8723 2.8627 2.7898 2.7066 2.6927 2.6225 2.5374 2.4511 2.3925 2.3633 2.2737 2.2556 2.1821 2.1245 2.0881 1.9975 1.9911 1.8906 1.873 1.7857 1.7497 1.6756 1.6261 1.5585 1.5005 1.4326 1.3709 1.2942 1.2343 1.1373 1.086 0.9489 0.9164 0.7005 0.689 0.2496 0 -); - -@SI_water_sg = qw( -9.1556 9.0249 8.9749 8.8999 8.827 8.7803 8.7227 8.6661 8.6421 8.5765 8.5567 8.4734 8.452 8.3938 8.3517 8.2556 8.2501 8.1633 8.1488 8.0748 8.0071 7.9898 7.9082 7.9073 7.8296 7.7675 7.754 7.6812 7.6691 7.6111 7.5931 7.5435 7.4782 7.4558 7.4151 7.3589 7.2952 7.2841 7.2382 7.2231 7.1829 7.1716 7.1292 7.127 7.0877 7.0771 7.0525 7.0265 6.9917 6.9773 6.965 6.9402 6.9294 6.9171 6.8955 6.8827 6.8561 6.8371 6.8207 6.7927 6.7886 6.7593 6.7492 6.7322 6.7067 6.6837 6.665 6.6616 6.6409 6.6242 6.6027 6.5841 6.552 6.5447 6.5217 6.5059 6.4936 6.4678 6.443 6.4302 6.393 6.3877 6.3563 6.339 6.32 6.2954 6.284 6.2558 6.2483 6.2128 6.1775 6.1424 6.1244 6.1072 6.0721 6.0369 6.0017 5.9737 5.9662 5.9305 5.8944 5.8902 5.8579 5.821 5.8148 5.7834 5.745 5.7059 5.6791 5.6657 5.6243 5.6159 5.5816 5.5544 5.5372 5.4939 5.4908 5.4422 5.4336 5.3907 5.3728 5.3358 5.3108 5.2765 5.2466 5.2114 5.1791 5.1384 5.1064 5.0537 5.0256 4.9493 4.931 4.8076 4.8009 4.5439 4.407 -); - - -############### -### ### -### air ### -### ### -############### - - -@SI_air_T = qw( -200 210 220 230 240 250 260 270 280 285 290 295 298 300 305 310 315 320 325 330 340 350 360 370 380 390 400 410 420 430 440 450 460 470 480 490 500 510 520 530 540 550 560 570 580 590 600 610 620 630 640 650 660 670 680 690 700 710 720 730 740 750 760 780 800 820 840 860 880 900 920 940 960 980 1000 1020 1040 1060 1080 1100 1120 1140 1160 1180 1200 1220 1240 1260 1280 1300 1320 1340 1360 1380 1400 1420 1440 1460 1480 1500 1520 1540 1560 1580 1600 1620 1640 1660 1680 1700 1750 1800 1850 1900 1950 2000 2050 2100 2150 2200 2250 -); - -@SI_air_h = qw( -199.97 209.97 219.97 230.02 240.02 250.05 260.09 270.11 280.13 285.14 290.16 295.17 298.18 300.19 305.22 310.24 315.27 320.29 325.31 330.34 340.42 350.49 360.58 370.67 380.77 390.88 400.98 411.12 421.26 431.43 441.61 451.8 462.02 472.24 482.49 492.74 503.02 513.32 523.63 533.98 544.35 555.74 565.17 575.59 586.04 596.52 607.02 617.53 628.07 638.63 649.22 659.84 670.47 681.14 691.82 702.52 713.27 724.04 734.82 745.62 756.44 767.29 778.18 800.03 821.95 843.98 866.08 888.27 910.56 932.93 955.38 977.92 1000.55 1023.25 1046.04 1068.89 1091.85 1114.86 1137.89 1161.07 1184.28 1207.57 1230.92 1254.34 1277.79 1301.31 1324.93 1348.55 1372.24 1395.97 1419.76 1443.6 1467.49 1491.44 1515.42 1539.44 1563.51 1587.63 1611.79 1635.97 1660.23 1684.51 1708.82 1733.17 1757.57 1782 1806.46 1830.96 1855.5 1880.1 1941.6 2003.3 2065.3 2127.4 2189.7 2252.1 2314.6 2377.7 2440.3 2503.2 2566.4 -); - -@SI_air_Pr = qw( -0.3363 0.3987 0.469 0.5477 0.6355 0.7329 0.8405 0.959 1.0889 1.1584 1.2311 1.3068 1.3543 1.386 1.4686 1.5546 1.6442 1.7375 1.8345 1.9352 2.149 2.379 2.626 2.892 3.176 3.481 3.806 4.153 4.522 4.915 5.332 5.775 6.245 6.742 7.268 7.824 8.411 9.031 9.684 10.37 11.1 11.86 12.66 13.5 14.38 15.31 16.28 17.3 18.36 19.84 20.64 21.86 23.13 24.46 25.85 27.29 28.8 30.38 32.02 33.72 35.5 37.35 39.27 43.35 47.75 52.59 57.6 63.09 68.98 75.29 82.05 89.28 97 105.2 114 123.4 133.3 143.9 155.2 167.1 179.7 193.1 207.2 222.2 238 254.7 272.3 290.8 310.4 330.9 352.5 375.3 399.1 424.2 450.5 478 506.9 537.1 568.8 601.9 636.5 672.8 710.5 750 791.2 834.1 878.9 925.6 974.2 1025 1161 1310 1475 1655 1852 2068 2303 2559 2837 3138 3464 -); - -@SI_air_u = qw( -142.56 149.69 156.82 164 171.13 178.28 185.45 192.6 199.75 203.33 206.91 210.49 212.64 214.07 217.67 221.25 224.85 228.42 232.02 235.61 242.82 250.02 257.24 264.46 271.69 278.93 286.16 293.43 300.69 307.99 315.3 322.62 329.97 337.32 344.7 352.08 359.49 366.92 374.36 381.84 389.34 396.86 404.42 411.97 419.55 427.15 434.78 442.42 450.09 457.78 465.5 473.25 481.01 488.81 496.62 504.45 512.33 520.23 528.14 536.07 544.02 551.99 560.01 576.12 592.3 608.59 624.95 641.4 657.95 674.58 691.28 708.08 725.02 741.98 758.94 776.1 793.36 810.62 827.88 845.33 862.79 880.35 897.91 915.57 933.33 951.09 968.95 986.9 1004.76 1022.82 1040.88 1058.94 1077.1 1095.26 1113.52 1131.77 1150.13 1168.49 1186.95 1205.41 1223.87 1242.43 1260.99 1279.65 1298.3 1316.96 1335.72 1354.48 1373.24 1392.7 1439.8 1487.2 1534.9 1582.6 1630.6 1678.7 1726.8 1775.3 1823.8 1872.4 1921.3 -); - -@SI_air_vr = qw( -1707 1512 1346 1205 1084 979 887.8 808 738 706.1 676.1 647.9 631.9 621.2 596 572.3 549.8 528.6 508.4 489.4 454.1 422.2 393.4 367.2 343.4 321.5 301.6 283.3 266.6 251.1 236.8 223.6 211.4 200.1 189.5 179.7 170.6 162.1 154.1 146.7 139.7 133.1 127 121.2 115.7 110.6 105.8 101.2 96.92 92.84 88.99 85.34 81.89 78.61 75.5 72.56 69.76 67.07 64.53 62.13 59.82 57.63 55.54 51.64 48.08 44.84 41.85 39.12 36.61 34.31 32.18 30.22 28.4 26.73 25.17 23.72 23.29 21.14 19.98 18.896 17.886 16.946 16.064 15.241 14.47 13.747 13.069 12.435 11.835 11.275 10.747 10.247 9.78 9.337 8.919 8.526 8.153 7.801 7.468 7.152 6.854 6.569 6.301 6.046 5.804 5.574 5.355 5.147 4.949 4.761 4.328 3.994 3.601 3.295 3.022 2.776 2.555 2.356 2.175 2.012 1.864 -); - -@SI_air_so = qw( -1.29559 1.34444 1.39105 1.43557 1.47824 1.51917 1.55848 1.59634 1.63279 1.65055 1.66802 1.68515 1.69528 1.70203 1.71865 1.73498 1.75106 1.7669 1.78249 1.79783 1.8279 1.85708 1.88543 1.91313 1.94001 1.96633 1.99194 2.01699 2.04142 2.06533 2.0887 2.11161 2.13407 2.15604 2.1776 2.19876 2.21952 2.23993 2.25997 2.27967 2.29906 2.31809 2.33685 2.35531 2.37348 2.3914 2.40902 2.42644 2.44356 2.46048 2.47716 2.49364 2.50985 2.52589 2.54175 2.55731 2.57277 2.5881 2.60319 2.61803 2.6328 2.64737 2.66176 2.69013 2.71787 2.74504 2.7717 2.79783 2.82344 2.84856 2.87324 2.89748 2.92128 2.94468 2.9677 2.99034 3.0126 3.03449 3.05608 3.07732 3.09825 3.11883 3.13916 3.15916 3.17888 3.19834 3.21751 3.23638 3.2551 3.27345 3.2916 3.30959 3.32724 3.34474 3.362 3.37901 3.39586 3.41247 3.42892 3.44516 3.4612 3.47712 3.49276 3.50829 3.52364 3.53879 3.55381 3.56867 3.58335 3.5979 3.6336 3.6684 3.7023 3.7354 3.7677 3.7994 3.8303 3.8605 3.8901 3.9191 3.9474 -); - -############################# -### ### -### superheated water ### -### ### -############################# - - -#### #### -#### 0.1 MPa #### -#### #### - -@SI_superWater_0p1MPa_T = qw( -99.61 100 150 200 250 300 400 500 600 700 800 900 1000 1100 1200 1300 -); - -@SI_superWater_0p1MPa_v = qw( -1.6941 1.6959 1.9367 2.1724 2.4062 2.6389 3.1027 3.5655 4.0279 4.49 4.9519 5.4137 5.8755 6.3372 6.7988 7.2605 -); - -@SI_superWater_0p1MPa_u = qw( -2505.6 2506.2 2582.9 2658.2 2733.9 2810.7 2968.3 3132.2 3302.8 3480.4 3665 3856.7 4055 4259.8 4470.7 4687.2 -); - -@SI_superWater_0p1MPa_h = qw( -2675 2675.8 2776.6 2875.5 2974.5 3074.5 3278.6 3488.7 3705.6 3929.4 4160.2 4398 4642.6 4893.6 5150.6 5413.3 -); - -@SI_superWater_0p1MPa_s = qw( -7.3589 7.3611 7.6148 7.8356 8.0346 8.2172 8.5452 8.8362 9.0999 9.3424 9.5682 9.78 9.98 10.1698 10.3504 10.5229 -); - - - - - - - - - - - -#### #### -#### 1.2 MPa #### -#### #### - -@SI_superWater_1p2MPa_T = qw( -179.88 200 250 300 350 400 500 600 700 800 900 1000 1100 1200 1300 -); - -@SI_superWater_1p2MPa_v = qw( -0.16326 0.16934 0.19241 0.21386 0.23455 0.25482 0.29464 0.33395 0.37297 0.41184 0.45059 0.48928 0.52792 0.56652 0.60509 -); - -@SI_superWater_1p2MPa_u = qw( -2587.8 2612.9 2704.7 2789.7 2872.7 2955.5 3123.4 3296.3 3475.3 3661 3853.3 4052.2 4257.5 4468.7 4685.5 -); - -@SI_superWater_1p2MPa_h = qw( -2783.8 2816.1 2935.6 3046.3 3154.2 3261.3 3477 3697 3922.9 4155.2 4394 4639.4 4891 5148.5 5411.6 -); - -@SI_superWater_1p2MPa_s = qw( -6.5217 6.5909 6.8313 7.0335 7.2139 7.3793 7.6779 7.9456 8.1904 8.4176 8.6303 8.831 9.0212 9.2022 9.375 -); - -#### #### -#### 1.4 MPa #### -#### #### - -@SI_superWater_1p4MPa_T = qw( -195.04 200 250 300 350 400 500 600 700 800 900 1000 1100 1200 1300 -); - -@SI_superWater_1p4MPa_v = qw( -0.14078 0.14303 0.16356 0.18233 0.20029 0.21782 0.25216 0.28597 0.31951 0.35288 0.38614 0.41933 0.45247 0.48558 0.51866 -); - -@SI_superWater_1p4MPa_u = qw( -2591.8 2602.7 2698.9 2785.7 2869.7 2953.1 3121.8 3295.1 3474.4 3660.3 3852.7 4051.7 4257 4468.3 4685.1 -); - -@SI_superWater_1p4MPa_h = qw( -2788.9 2803 2927.9 3040.9 3150.1 3258.1 3474.8 3695.5 3921.7 4154.3 4393.3 4638.8 4890.5 5148.1 5411.3 -); - -@SI_superWater_1p4MPa_s = qw( -6.4675 6.4975 6.7488 6.9553 7.1379 7.3046 7.6047 7.873 8.1183 8.3458 8.5587 8.7595 8.9497 9.1308 9.3036 -); - -#### #### -#### 1.6 MPa #### -#### #### - -@SI_superWater_1p6MPa_T = qw( -201.37 225 250 300 350 400 500 600 700 800 900 1000 1100 1200 1300 -); - -@SI_superWater_1p6MPa_v = qw( -0.12374 0.13293 0.1419 0.15866 0.17459 0.19007 0.22029 0.24999 0.27941 0.30865 0.3378 0.36687 0.39589 0.42488 0.45383 -); - -@SI_superWater_1p6MPa_u = qw( -2594.8 2645.1 2692.9 2781.6 2866.6 2950.8 3120.1 3293.9 3473.5 3659.5 3852.1 4051.2 4256.6 4467.9 4684.8 -); - -@SI_superWater_1p6MPa_h = qw( -2792.8 2857.8 2919.9 3035.4 3146 3254.9 3472.6 3693.9 3920.5 4153.4 4392.6 4638.2 4890 5147.7 5410.9 -); - -@SI_superWater_1p6MPa_s = qw( -6.42 6.5537 6.6753 6.8864 7.0713 7.2394 7.541 7.8101 8.0558 8.2834 8.4965 8.6974 8.8878 9.0689 9.2418 -); - -#### #### -#### 1.8 MPa #### -#### #### - -@SI_superWater_1p8MPa_T = qw( -207.11 225 250 300 350 400 500 600 700 800 900 1000 1100 1200 1300 -); - -@SI_superWater_1p8MPa_v = qw( -0.11037 0.11678 0.12502 0.14025 0.1546 0.16849 0.19551 0.222 0.24822 0.27426 0.3002 0.32606 0.35188 0.37766 0.40341 -); - -@SI_superWater_1p8MPa_u = qw( -2597.3 2637 2686.7 2777.4 2863.6 2948.3 3118.5 3292.7 3472.6 3658.8 3851.5 4050.7 4256.2 4467.6 4684.5 -); - -@SI_superWater_1p8MPa_h = qw( -2795.9 2847.2 2911.7 3029.9 3141.9 3251.6 3470.4 3692.3 3919.4 4152.4 4391.9 4637.6 4889.6 5147.3 5410.6 -); - -@SI_superWater_1p8MPa_s = qw( -6.3775 6.4825 6.6088 6.8246 7.012 7.1814 7.4845 7.7543 8.0005 8.2284 8.4417 8.6427 8.8331 9.0143 9.1872 -); - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#### #### -#### 10 MPa #### -#### #### - -@SI_superWater_10MPa_T = qw( -311 325 350 400 450 500 550 600 650 700 800 900 1000 1100 1200 1300 -); - -@SI_superWater_10MPa_v = qw( -.018028 0.019877 0.02244 0.026436 0.029782 0.032811 0.035655 0.038378 0.041018 0.043597 0.048629 0.053547 0.058391 0.063183 0.067938 0.072667 -); - -@SI_superWater_10MPa_u = qw( -2545.2 2611.6 2699.6 2833.1 2944.5 3047 3145.4 3242 3338 3434 3628.2 3826.5 4029.9 4238.5 4452.4 4671.3 -); - -@SI_superWater_10MPa_h = qw( -2725.5 2810.3 2924 3097.5 3242.4 3375.1 3502 3625.8 3748.1 3870 4114.5 4362 4613.8 4870.3 5131.7 5398 -); - -@SI_superWater_10MPa_s = qw( -5.6159 5.7596 5.946 6.2141 6.4219 6.5995 6.7585 6.9045 7.0408 7.1693 7.4085 7.629 7.8349 8.0289 8.2126 8.3874 -); - - - - - -############################ -### ### -### saturated R-134a ### -### ### -############################ - - - - - -@SI_R134a_Tsat = qw( --40.00 -38.00 -36.95 -36.00 -34.00 -33.87 -32.00 -31.13 -30.00 -28.65 -28.00 -26.37 -26.00 -24.00 -22.32 -22.00 -20.00 -18.77 -18.00 -16.00 -15.60 -14.00 -12.73 -12.00 -10.09 -10.00 -8.00 -6.00 -5.38 -4.00 -2.00 -1.25 0.00 2.00 2.46 4.00 5.82 6.00 8.00 8.91 10.00 12.00 12.46 14.00 15.71 16.00 18.00 18.73 20.00 21.55 22.00 24.00 24.20 26.00 26.69 28.00 29.06 30.00 31.31 32.00 33.45 34.00 35.51 36.00 37.48 38.00 39.37 40.00 42.00 44.00 46.00 46.29 48.00 52.00 52.40 56.00 57.88 60.00 62.87 65.00 67.45 70.00 75.00 77.54 80.00 85.00 86.16 90.00 95.00 100.00 -); - -@SI_R134a_Psat = qw( -51.250 56.860 60.00 62.950 69.560 70.00 76.710 80.00 84.430 90.00 92.760 100.00 101.730 111.370 120.00 121.720 132.820 140.00 144.690 157.380 160.00 170.930 180.00 185.370 200.00 200.740 217.080 234.440 240.00 252.850 272.360 280.00 293.010 314.840 320.00 337.900 360.00 362.230 387.880 400.00 414.890 443.310 450.00 473.190 500.00 504.580 537.520 550.00 572.070 600.00 608.270 646.180 650.00 685.840 700.00 727.310 750.00 770.640 800.00 815.890 850.00 863.110 900.00 912.350 950.00 963.680 1000.00 1017.100 1072.800 1130.700 1191.000 1200.00 1253.600 1386.200 1400.00 1529.100 1600.00 1682.800 1800.00 1891.000 2000.00 2118.200 2365.800 2500.00 2635.300 2928.200 3000.00 3246.900 3594.100 3975.100 -); - -@SI_R134a_vf = qw( -0.0007054 0.0007083 0.0007098 0.0007112 0.0007142 0.0007144 0.0007172 0.0007185 0.0007203 0.0007223 0.0007234 0.0007259 0.0007265 0.0007297 0.0007324 0.0007329 0.0007362 0.0007383 0.0007396 0.000743 0.0007437 0.0007464 0.0007487 0.0007499 0.0007533 0.0007535 0.0007571 0.0007608 0.000762 0.0007646 0.0007684 0.0007699 0.0007723 0.0007763 0.0007772 0.0007804 0.0007841 0.0007845 0.0007887 0.0007907 0.000793 0.0007975 0.0007985 0.000802 0.0008059 0.0008066 0.0008113 0.000813 0.0008161 0.0008199 0.000821 0.0008261 0.0008266 0.0008313 0.0008331 0.0008366 0.0008395 0.0008421 0.0008458 0.0008478 0.000852 0.0008536 0.000858 0.0008595 0.0008641 0.0008657 0.00087 0.000872 0.0008786 0.0008854 0.0008924 0.0008934 0.0008996 0.000915 0.0009166 0.0009317 0.00094 0.0009498 0.0009639 0.000975 0.0009886 0.0010037 0.0010372 0.0010566 0.0010772 0.001127 0.0011406 0.0011932 0.0012933 0.0015269 -); - -@SI_R134a_vfg = qw( -0.360105 0.326612 0.310500 0.296799 0.270186 0.268576 0.246393 0.236812 0.225080 0.211908 0.205937 0.191814 0.188734 0.173220 0.161388 0.159217 0.146554 0.139402 0.135090 0.124677 0.122736 0.115224 0.109661 0.106610 0.099114 0.098763 0.091595 0.085041 0.083135 0.079039 0.073536 0.071582 0.068483 0.063836 0.062827 0.059558 0.055954 0.055614 0.051973 0.050410 0.048610 0.045498 0.044821 0.042615 0.040312 0.039941 0.037460 0.036595 0.035153 0.033475 0.033007 0.031008 0.030819 0.029145 0.028528 0.027405 0.026532 0.025780 0.024775 0.024260 0.023217 0.022837 0.021825 0.021505 0.020574 0.020253 0.019443 0.019080 0.017976 0.016939 0.015961 0.015822 0.015039 0.013350 0.013190 0.011839 0.011183 0.010484 0.009595 0.008975 0.008299 0.007638 0.006443 0.005879 0.005359 0.004359 0.004134 0.003406 0.002433 0.001103 -); - -@SI_R134a_vg = qw( -0.36081 0.32732 0.31121 0.29751 0.2709 0.26929 0.24711 0.23753 0.2258 0.21263 0.20666 0.19254 0.18946 0.17395 0.16212 0.15995 0.14729 0.14014 0.13583 0.12542 0.12348 0.11597 0.11041 0.10736 0.099867 0.099516 0.092352 0.085802 0.083897 0.079804 0.074304 0.072352 0.069255 0.064612 0.063604 0.060338 0.056738 0.056398 0.052762 0.051201 0.049403 0.046295 0.045619 0.043417 0.041118 0.040748 0.038271 0.037408 0.035969 0.034295 0.033828 0.031834 0.031646 0.029976 0.029361 0.028242 0.027371 0.026622 0.025621 0.025108 0.024069 0.023691 0.022683 0.022364 0.021438 0.021119 0.020313 0.019952 0.018855 0.017824 0.016853 0.016715 0.015939 0.014265 0.014107 0.012771 0.012123 0.011434 0.010559 0.00995 0.009288 0.008642 0.00748 0.006936 0.006436 0.005486 0.005275 0.004599 0.003726 0.00263 -); - -@SI_R134a_uf = qw( --0.036 2.475 3.798 4.992 7.517 7.68 10.05 11.15 12.59 14.31 15.13 17.21 17.69 20.25 22.4 22.82 25.39 26.98 27.98 30.57 31.09 33.17 34.83 35.78 38.28 38.4 41.03 43.66 44.48 46.31 48.96 49.97 51.63 54.3 54.92 56.99 59.44 59.68 62.39 63.62 65.1 67.83 68.45 70.57 72.93 73.32 76.08 77.1 78.86 81.02 81.64 84.44 84.72 87.26 88.24 90.09 91.59 92.93 94.79 95.79 97.87 98.66 100.83 101.55 103.69 104.45 106.45 107.38 110.32 113.28 116.26 116.7 119.26 125.33 125.94 131.49 134.43 137.76 142.33 145.77 149.78 154.01 162.53 166.99 171.4 180.77 183.04 190.89 202.4 218.72 -); - -@SI_R134a_ufg = qw( -207.4 206.04 205.32 204.67 203.29 203.2 201.91 201.3 200.52 199.57 199.12 197.98 197.72 196.3 195.11 194.88 193.45 192.57 192.01 190.56 190.27 189.09 188.16 187.62 186.21 186.14 184.64 183.13 182.67 181.61 180.08 179.5 178.53 176.97 176.61 175.39 173.94 173.8 172.19 171.45 170.56 168.92 168.54 167.26 165.82 165.58 163.88 163.25 162.16 160.81 160.42 158.65 158.48 156.87 156.24 155.05 154.08 153.22 152 151.35 149.98 149.46 148.01 147.54 146.1 145.58 144.23 143.6 141.58 139.52 137.42 137.11 135.29 130.88 130.43 126.28 124.04 121.46 117.83 115.05 111.73 108.14 100.6 96.47 92.23 82.67 80.22 71.29 56.47 29.19 -); - -@SI_R134a_ug = qw( -207.37 208.51 209.12 209.66 210.81 210.88 211.96 212.46 213.11 213.88 214.25 215.19 215.4 216.55 217.51 217.7 218.84 219.54 219.98 221.13 221.35 222.27 222.99 223.4 224.48 224.54 225.67 226.8 227.14 227.92 229.04 229.46 230.16 231.27 231.52 232.38 233.38 233.48 234.58 235.07 235.67 236.75 237 237.83 238.75 238.9 239.96 240.35 241.02 241.83 242.06 243.1 243.2 244.12 244.48 245.14 245.67 246.14 246.79 247.14 247.85 248.12 248.85 249.08 249.79 250.04 250.68 250.97 251.89 252.8 253.68 253.81 254.55 256.21 256.37 257.77 258.47 259.22 260.17 260.82 261.51 262.15 263.13 263.45 263.63 263.44 263.26 262.18 258.87 247.91 -); - -@SI_R134a_hf = qw( -0 2.515 3.841 5.037 7.566 7.73 10.1 11.21 12.65 14.37 15.2 17.28 17.76 20.33 22.49 22.91 25.49 27.08 28.09 30.69 31.21 33.3 34.97 35.92 38.43 38.55 41.19 43.84 44.66 46.5 49.17 50.18 51.86 54.55 55.16 57.25 59.72 59.97 62.69 63.94 65.43 68.18 68.81 70.95 73.33 73.73 76.52 77.54 79.32 81.51 82.14 84.98 85.26 87.83 88.82 90.69 92.22 93.58 95.47 96.48 98.6 99.4 101.61 102.33 104.51 105.29 107.32 108.26 111.26 114.28 117.32 117.77 120.39 126.59 127.22 132.91 135.93 139.36 144.07 147.62 151.76 156.13 164.98 169.63 174.24 184.07 186.46 194.76 207.05 224.79 -); - -@SI_R134a_hfg = qw( -225.86 224.61 223.95 223.35 222.09 222 220.81 220.25 219.52 218.65 218.22 217.16 216.92 215.59 214.48 214.26 212.91 212.08 211.55 210.18 209.9 208.79 207.9 207.38 206.03 205.96 204.52 203.07 202.62 201.6 200.11 199.54 198.6 197.07 196.71 195.51 194.08 193.94 192.35 191.62 190.73 189.09 188.71 187.42 185.98 185.73 184.01 183.38 182.27 180.9 180.49 178.69 178.51 176.85 176.21 174.99 173.98 173.08 171.82 171.14 169.71 169.17 167.66 167.16 165.64 165.1 163.67 163 160.86 158.67 156.43 156.1 154.14 149.39 148.9 144.38 141.93 139.1 135.11 132.02 128.33 124.32 115.85 111.16 106.35 95.44 92.63 82.35 65.21 33.58 -); - -@SI_R134a_hg = qw( -225.86 227.12 227.79 228.39 229.65 229.73 230.91 231.46 232.17 233.02 233.43 234.44 234.68 235.92 236.97 237.17 238.41 239.16 239.64 240.87 241.11 242.09 242.86 243.3 244.46 244.51 245.72 246.91 247.28 248.1 249.28 249.72 250.45 251.61 251.88 252.77 253.81 253.91 255.04 255.55 256.16 257.27 257.53 258.37 259.3 259.46 260.53 260.92 261.59 262.4 262.64 263.67 263.77 264.68 265.03 265.68 266.2 266.66 267.29 267.62 268.31 268.57 269.26 269.49 270.15 270.39 270.99 271.27 272.12 272.95 273.75 273.87 274.53 275.98 276.12 277.3 277.86 278.46 279.17 279.64 280.09 280.46 280.82 280.79 280.59 279.51 279.09 277.11 272.26 258.37 -); - -@SI_R134a_sf = qw( -0 0.01072 0.01634 0.02138 0.03199 0.03267 0.04253 0.04711 0.05301 0.06008 0.06344 0.07188 0.07382 0.08414 0.09275 0.09441 0.10463 0.11087 0.11481 0.12493 0.12693 0.13501 0.14139 0.14504 0.15457 0.15504 0.16498 0.17489 0.17794 0.18476 0.19459 0.19829 0.20439 0.21415 0.21637 0.22387 0.2327 0.23356 0.24323 0.24761 0.25286 0.26246 0.26465 0.27204 0.28023 0.28159 0.29112 0.29461 0.30063 0.30799 0.31011 0.31958 0.32051 0.32903 0.3323 0.33846 0.34345 0.34789 0.35404 0.3573 0.36413 0.3667 0.37377 0.37609 0.38301 0.38548 0.39189 0.39486 0.40425 0.41363 0.42302 0.42441 0.43242 0.45126 0.45315 0.47018 0.47911 0.4892 0.50294 0.5132 0.52509 0.53755 0.56241 0.57531 0.588 0.61473 0.62118 0.64336 0.67578 0.72217 -); - -@SI_R134a_sfg = qw( -0.96866 0.95511 0.94807 0.94176 0.92859 0.92775 0.9156 0.90999 0.90278 0.89419 0.89012 0.87995 0.87762 0.86527 0.85503 0.85307 0.84101 0.83368 0.82908 0.81729 0.81496 0.80561 0.79826 0.79406 0.78316 0.78263 0.7713 0.76008 0.75664 0.74896 0.73794 0.73381 0.72701 0.71616 0.71369 0.7054 0.69566 0.69471 0.6841 0.67929 0.67356 0.66308 0.66069 0.65266 0.64377 0.6423 0.63198 0.62821 0.62172 0.61378 0.61149 0.6013 0.6003 0.59115 0.58763 0.58102 0.57567 0.57091 0.56431 0.56082 0.55349 0.55074 0.54315 0.54066 0.53323 0.53058 0.52368 0.52049 0.51039 0.50027 0.49012 0.48863 0.47993 0.45941 0.45734 0.43863 0.42873 0.41749 0.40204 0.39039 0.37675 0.36227 0.33272 0.31695 0.30111 0.26644 0.25776 0.22674 0.17711 0.08999 -); - -@SI_R134a_sg = qw( -0.96866 0.96584 0.96441 0.96315 0.96058 0.96042 0.95813 0.9571 0.95579 0.95427 0.95356 0.95183 0.95144 0.94941 0.94779 0.94748 0.94564 0.94456 0.94389 0.94222 0.9419 0.94063 0.93965 0.93911 0.93773 0.93766 0.93629 0.93497 0.93458 0.93372 0.93253 0.9321 0.93139 0.93031 0.93006 0.92927 0.92836 0.92828 0.92733 0.92691 0.92641 0.92554 0.92535 0.9247 0.924 0.92389 0.9231 0.92282 0.92234 0.92177 0.9216 0.92088 0.92081 0.92018 0.91994 0.91948 0.91912 0.91879 0.91835 0.91811 0.91762 0.91743 0.91692 0.91675 0.91624 0.91606 0.91558 0.91536 0.91464 0.91391 0.91315 0.91303 0.91236 0.91067 0.9105 0.9088 0.90784 0.90669 0.90498 0.90359 0.90184 0.89982 0.89512 0.89226 0.88912 0.88117 0.87894 0.8701 0.85289 0.81215 -); - - - - - - - - -############################## -### ### -### superheated R-134a ### -### ### -############################## - - - - - -#### #### -#### 0.7 MPa #### -#### #### - -@SI_superR134a_0p7MPa_T = qw( -26.69 30 40 50 60 70 80 90 100 110 120 130 140 150 160 -); - -@SI_superR134a_0p7MPa_v = qw( -0.029361 0.029966 0.031696 0.033322 0.034875 0.036373 0.037829 0.03925 0.040642 0.04201 0.043358 0.044688 0.046004 0.047306 0.048597 -); - -@SI_superR134a_0p7MPa_u = qw( -244.48 247.48 256.39 265.2 274.01 282.87 291.8 300.82 309.95 319.19 328.55 338.04 347.66 357.41 367.29 -); - -@SI_superR134a_0p7MPa_h = qw( -265.03 268.45 278.57 288.53 298.42 308.33 318.28 328.29 338.4 348.6 358.9 369.32 379.86 390.52 401.31 -); - -@SI_superR134a_0p7MPa_s = qw( -0.9199 0.9313 0.9641 0.9954 1.0256 1.0549 1.0835 1.1114 1.1389 1.1658 1.1924 1.2186 1.2444 1.2699 1.2951 -); - -#### #### -#### 0.8 MPa #### -#### #### - -@SI_superR134a_0p8MPa_T = qw( -31.31 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 -); - -@SI_superR134a_0p8MPa_v = qw( -0.025621 0.027035 0.028547 0.029973 0.03134 0.032659 0.033941 0.035193 0.03642 0.037625 0.038813 0.039985 0.041143 0.04229 0.043427 0.044554 -); - -@SI_superR134a_0p8MPa_u = qw( -246.79 254.82 263.86 272.83 281.81 290.84 299.95 309.15 318.45 327.87 337.4 347.06 356.85 366.76 376.81 386.99 -); - -@SI_superR134a_0p8MPa_h = qw( -267.29 276.45 286.69 296.81 306.88 316.97 327.1 337.3 347.59 357.97 368.45 379.05 389.76 400.59 411.55 422.64 -); - -@SI_superR134a_0p8MPa_s = qw( -0.9183 0.948 0.9802 1.011 1.0408 1.0698 1.0981 1.1258 1.153 1.1798 1.2061 1.2321 1.2577 1.283 1.308 1.3327 -); - -#### #### -#### 0.9 MPa #### -#### #### - -@SI_superR134a_0p9MPa_T = qw( -35.51 40 50 60 70 80 90 100 110 120 130 140 150 160 170 180 -); - -@SI_superR134a_0p9MPa_v = qw( -0.022683 0.023375 0.024809 0.026146 0.027413 0.02863 0.029806 0.030951 0.032068 0.033164 0.034241 0.035302 0.036349 0.037384 0.038408 0.039423 -); - -@SI_superR134a_0p9MPa_u = qw( -248.85 253.13 262.44 271.6 280.72 289.86 299.06 308.34 317.7 327.18 336.76 346.46 356.28 366.23 376.31 386.52 -); - -@SI_superR134a_0p9MPa_h = qw( -269.26 274.17 284.77 295.13 305.39 315.63 325.89 336.19 346.56 357.02 367.58 378.23 389 399.88 410.88 422 -); - -@SI_superR134a_0p9MPa_s = qw( -0.9169 0.9327 0.966 0.9976 1.028 1.0574 1.086 1.114 1.1414 1.1684 1.1949 1.221 1.2467 1.2721 1.2972 1.3221 -); - - - - - - - - -#### #### -#### 1.2 MPa #### -#### #### - -@SI_superR134a_1p2MPa_T = qw( -46.29 50 60 70 80 90 100 110 120 130 140 150 160 170 180 -); - -@SI_superR134a_1p2MPa_v = qw( -0.016715 0.017201 0.018404 0.019502 0.020529 0.021506 0.022442 0.023348 0.024228 0.025086 0.025927 0.026753 0.027566 0.028367 0.029158 -); - -@SI_superR134a_1p2MPa_u = qw( -253.81 257.63 267.56 277.21 286.75 296.26 305.8 315.38 325.03 334.77 344.61 354.56 364.61 374.78 385.08 -); - -@SI_superR134a_1p2MPa_h = qw( -273.87 278.27 289.64 300.61 311.39 322.07 332.73 343.4 354.11 364.88 375.72 386.66 397.69 408.82 420.07 -); - -@SI_superR134a_1p2MPa_s = qw( -0.913 0.9267 0.9614 0.9938 1.0248 1.0546 1.0836 1.1118 1.1394 1.1664 1.193 1.2192 1.2449 1.2703 1.2954 -); - - - - - - - - - - - - - - - - -1; #required at end of file - a perl thing \ No newline at end of file diff --git a/OpenProblemLibrary/macros/LaTech/interpMacros.pl b/OpenProblemLibrary/macros/LaTech/interpMacros.pl deleted file mode 100644 index 36214df2ee..0000000000 --- a/OpenProblemLibrary/macros/LaTech/interpMacros.pl +++ /dev/null @@ -1,34 +0,0 @@ -# interpMacros.pl -# Rename this file (with .pl extension) and place it in your course macros directory, - -sub interpVals { - $arrayLength = ($#_ )/2; - @A_ARRAY = @_[0..($arrayLength-1)]; - @B_ARRAY = @_[$arrayLength .. ($#_-1)]; - $A_VAL = @_[$#_]; - $arrayLength2 = $#A_ARRAY; - - - for ($i = 1; $i < ($#A_ARRAY+1); $i++) { - if($A_VAL == $A_ARRAY[$i-1]) { - $B_VAL = $B_ARRAY[$i-1]; - last; - } elsif ($A_ARRAY[$i-1] < $A_VAL && $A_VAL < $A_ARRAY[$i]) { - $AL = $A_ARRAY[$i-1]; - $AR = $A_ARRAY[$i]; - $BL = $B_ARRAY[$i-1]; - $BR = $B_ARRAY[$i]; - $B_VAL = (($A_VAL-$AL)/($AR - $AL)*($BR-$BL))+$BL; - last; - } elsif($A_VAL == $A_ARRAY[$i]) { - $B_VAL = $B_ARRAY[$i]; - last; - } else { - $B_VAL = $B_ARRAY[0]; - } - } - - return $B_VAL; -} - -1; #required at end of file - a perl thing diff --git a/OpenProblemLibrary/macros/MC/draggableProof.pl b/OpenProblemLibrary/macros/MC/draggableProof.pl deleted file mode 100644 index 0b866be90d..0000000000 --- a/OpenProblemLibrary/macros/MC/draggableProof.pl +++ /dev/null @@ -1,291 +0,0 @@ -# Done: show possible choices in TeX mode -# To do: display student answers and correct answers in TeX mode properly. -# To do: put jquery.nestable.js in a universal spot on every webwork server. - -loadMacros("PGchoicemacros.pl"); - -sub _draggableProof_init { - PG_restricted_eval("sub DraggableProof {new draggableProof(\@_)}"); - - $courseHtmlUrl = $envir{htmlURL}; - -# if (-e "${wwHtmlDir}js/vendor/jquery/modules/jquery.nestable.js") { -# $scriptPath = $wwHtmlUrl.'js/vendor/jquery/modules/'; -# } else { -# $scriptPath = $courseHtmlUrl.'js/'; -# } - - # post global javascript - main::POST_HEADER_TEXT(main::MODES(TeX=>"", HTML=><<" END_SCRIPTS")); - - - - - END_SCRIPTS -} - -package draggableProof; - -my $n = 0; # number of nestable lists so far - -sub new { - $n++; - my $self = shift; my $class = ref($self) || $self; - my $proof = shift || []; my $extra = shift || []; - my %options = ( - SourceLabel => "Choose from these sentences:", - TargetLabel => "Your Proof:", - id => "P$n", - @_ - ); - my $lines = [@$proof,@$extra]; - my $numNeeded = scalar(@$proof); - my $numProvided = scalar(@$lines); - my @order = main::shuffle($numProvided); - my @unorder = main::invert(@order); - $self = bless { - lines => $lines, - numNeeded => $numNeeded, numProvided => $numProvided, - order => \@order, unordered => \@unorder, - proof => "$options{id}-".join(",$options{id}-",@unorder[0..$numNeeded-1]), - %options - }, $class; - $self->AnswerRule; - $self->ScriptAndStyles; - $self->GetAnswer; - return $self; -} - -sub lines {my $self = shift; return @{$self->{lines}}} -sub numNeeded {(shift)->{numNeeded}} -sub numProvided {(shift)->{numProvided}} -sub order {my $self = shift; return @{$self->{order}}} -sub unorder {my $self = shift; return @{$self->{unorder}}} - -# In principle, the styles (for example, color and size of the tiles) -# could be customized for each draggableProof instance. This is why -# each instance receives its own CSS container class. - -sub ScriptAndStyles { - my $self = shift; my $id = $self->{id}; - - main::POST_HEADER_TEXT(main::MODES(TeX=>"", HTML=><<" SCRIPT_AND_STYLE")); - - - - SCRIPT_AND_STYLE -} - -sub AnswerRule { - my $self = shift; - my $rule = main::ans_rule(1); - $self->{tgtAns} = ""; $self->{tgtAns} = $1 if $rule =~ m/id="(.*?)"/; - $self->{srcAns} = $self->{tgtAns}."-src"; - main::RECORD_FORM_LABEL($self->{srcAns}); # use this for release 2.13 and comment out for develop - #$main::PG->store_persistent_data; # uncomment this for develop and releases beyond 2.13 - my $ext = main::NAMED_ANS_RULE_EXTENSION($self->{srcAns},1,answer_group_name=>$self->{srcAns}.'-src'); - main::TEXT( main::MODES(TeX=>"", HTML=>'')); -} - -sub GetAnswer { - my $self = shift; my $previous; - - # Retrieve the previous state of the right column. - $previous = $main::inputs_ref->{$self->{tgtAns}} || ""; - $previous =~ s/$self->{id}-//g; $self->{previousTarget} = [split(/,/,$previous)]; - - # Calculate the complement of the right column. - my %prevTarget = map {$_ => 1} @{$self->{previousTarget}}; - my @diff = grep {not $prevTarget{$_}} (0..$self->{numProvided}-1); - - # If the previous state of the left column has been saved, use it. (This ensures that the tiles - # in the left column are kept in the same order that the user had arranged them). If it has not - # been saved, use the complement of the right column. - $previous = $main::inputs_ref->{$self->{srcAns}} || "$self->{id}-".join(",$self->{id}-",@diff); - $previous =~ s/$self->{id}-//g; $self->{previousSource} = [split(/,/,$previous)]; -} - -sub Print { - my $self = shift; - - if ($main::displayMode ne "TeX") { # HTML mode - - return join("\n", - '
    ', - $self->Source, - $self->Target, - '
    ', - '
    ', - ); - - } else { # TeX mode - - return join("\n", - $self->Source, - $self->Target, - ); - - } - -} - -sub Source { - my $self = shift; - return $self->Bucket("source",$self->{srcAns},$self->{SourceLabel},$self->{previousSource}); -} - -sub Target { - my $self = shift; - return $self->Bucket("target",$self->{tgtAns},$self->{TargetLabel},$self->{previousTarget}); -} - -sub Bucket { - my $self = shift; my $id = $self->{id}; - my ($name,$ans,$label,$previous) = @_; - - if ($main::displayMode ne "TeX") { # HTML mode - - my @lines = (); - push(@lines, '
    ', - '
    '.$label.'
    ', - '
    ' - ); - if (scalar @{$previous} > 0) { - push(@lines, '
      '); - foreach my $i (@{$previous}) { - push(@lines, '
    1. '.$self->{lines}[$self->{order}[$i]].'
    2. '); - } - push(@lines, '
    '); - } - push(@lines, - '
    ', - '
    ' - ); - return join("\n",@lines); - - } else { # TeX mode - - if (@{$previous}) { # array is nonempty - my @lines = ('\\begin{itemize}'); - foreach my $i (@{$previous}) { - push(@lines,'\\item '.$self->{lines}[$self->{order}[$i]] ) - } - push(@lines,'\\end{itemize}'); - return join("\n",@lines); - } else { - return ''; - } - } - -} - -sub cmp { - my $self = shift; - return main::str_cmp($self->{proof})->withPreFilter("erase")->withPostFilter(sub {$self->filter(@_)}); -} - -sub filter { - my $self = shift; my $ans = shift; - my @line = $self->lines; my @order = $self->order; - my $correct = $ans->{correct_ans}; $correct =~ s/$self->{id}-//g; - my $student = $ans->{student_ans}; $student =~ s/$self->{id}-//g; - my @correct = @line[map {@order[$_]} split(/,/,$correct)]; - my @student = @line[map {@order[$_]} split(/,/,$student)]; - $ans->{preview_latex_string} = "\\begin{array}{l}\\text{".join("}\\\\\\text{",@student)."}\\end{array}"; - $ans->{student_ans} = "(see preview)"; - $ans->{correct_ans_latex_string} = "\\begin{array}{l}\\text{".join("}\\\\\\text{",@correct)."}\\end{array}"; - $ans->{correct_ans} = join("
    ",@correct); - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/Michigan/hhAdditionalMacros.pl b/OpenProblemLibrary/macros/Michigan/hhAdditionalMacros.pl deleted file mode 100755 index 79dae756e4..0000000000 --- a/OpenProblemLibrary/macros/Michigan/hhAdditionalMacros.pl +++ /dev/null @@ -1,449 +0,0 @@ -# additional macros developed in conjunction with the problems from -# the consortium (hughes-hallett) calculus text -# -# written by Gavin LaRose, -# version 1.2 -# 1.2: added reduced_frac() -# 1.1: added numorfun_cmp() -# -# shufflemap -# produces a reference to a hash of indices from 0 to n-1 pointing to -# a random permutation of the same, and the index that points to 0 -# -# string_list_cmp -# check a comma-separated list of strings as student answers and -# check them with the standard str_cmp evaluator. this is really -# John Jones' number_list_cmp rewritten to be do string comparison -# -# integrand_fun_cmp -# an answer evaluator that allows checking functions with 'dt' in them; -# redundant now that the Parser allows checking with multicharacter -# variables -# -# numfun_cmp -# an answer evaluator that requires that the answer be -# simplified to the number given without returning an error if -# it's a function -# -# numorfun_cmp -# an answer evaluator that first checks to see if the student answer -# checks correctly as a number, and if so returns that result; if not, -# repeats the check with a function checker and returns that. options -# for the respective checkers are provided as separate hash references -# -# classify_cmp -# take a correct answer of the form "val=string, val=string.." -# and check a comma-separated list of student answers against these -# if the option 'order'=>'strict' is provided, requires that the -# answers be in the same order. any other options are passed to -# num_cmp to check the val entries; strings are checked with a -# standard str_cmp -# doesn't currently allow for passing in of a reference to an array -# of solutions to generate a list of evaluators, but that would be -# easy to add -# - -sub reduced_frac { - my ($num, $den) = @_; - - if ($num/$den == int($num/$den) ) { - return $num/$den; - } else { - for ( my $j = ($num > $den? $den: $num); $j>1; $j-- ) { - if ( $num/$j == int($num/$j) && $den/$j == int($den/$j) ) { - $num = $num/$j; $den = $den/$j; - } - } - if ( $den == 1 ) { - return $num; - } else { - return( "{$num" . '\over' . "$den}" ); - } - } -} - -sub shufflemap { - my $n = shift(); - my $m = $n - 1; - my $zeroind; - - my %map = (); - - my @vals = (0..$m); - for ( my $i=0; $i<$m; $i++ ) { - my $j = random(0,$m-$i,1); - $map{$i} = $vals[$j]; - $zeroind = $i if ( $map{$i} == 0 ); - splice(@vals, $j, 1); - } - $map{$m} = $vals[0]; - $zeroind = $m if ( $map{$m} == 0 ); - - return( { %map }, $zeroind ); -} - -sub string_list_cmp { - my $cList = shift(); - my %opts = @_; - - my $ordered = 0; - if ( defined( $opts{'ordered'} ) ) { - $ordered = 1 if ( $opts{'ordered'} ); - delete( $opts{'ordered'} ); - } - - my @cAns = split(/,\s*/, $cList); - - my @ansEval = (); - foreach ( @cAns ) { - push( @ansEval, str_cmp( $_, %opts ) ); - } - - return( sub { - my $sList = shift(); - my @sAns = split(/,\s*/, $sList); - - my $res = new AnswerHash; - - if ( $ordered ) { - if ( @sAns ) { - my $res = $ansEval[0]->evaluate( $sAns[0] ); - for( my $i=1; $i<@sAns && $i<@ansEval; $i++ ) { - $res = $res->AND( $ansEval[$i]->evaluate( $sAns[$i] ) ); - } - if ( @ansEval > @sAns ) { - $res->{'score'} = 0; - for( my $j=@sAns; $j<@ansEval; $j++ ) { - my $fakeRes = $ansEval[$j]->evaluate('something'); - $res->{'correct_ans'} .= "AND " . - $fakeRes->{'correct_ans'}; - } - } elsif ( @sAns > @ansEval ) { - $res->{'score'} = 0; - for( my $j=@ansEval; $j<@sAns; $j++ ) { - my $fakeRes = $ansEval[0]->evaluate( $sAns[$j] ); - $res->{'student_ans'} .= ", " . - $fakeRes->{'student_ans'}; - $res->{'original_student_ans'} .= ", " . - $fakeRes->{'original_student_ans'}; - } - } - return $res; - } else { - my $res = $ansEval[0]->evaluate(''); - for( my $i=1; $i<@ansEval; $i++ ) { - $res = $res->AND( $ansEval[$i]->evaluate('') ); - } - return $res; - } - } else { - # go through and check each in turn - my $res; - my @missedAnsEval = (); - my @doneStuAns = (); - my $stuAnsDone = ''; - for( my $i=0; $i<@ansEval; $i++ ) { - my $doneOne = 0; - for( my $j=0; $j<@sAns; $j++ ) { - next if ( $stuAnsDone =~ /\b$j\b/ ); - my $curRes = $ansEval[$i]->evaluate( $sAns[$j] ); - if ( $curRes->{'score'} ) { - if ( ref( $res ) ) { - $res = $res->AND($curRes); - $res->{'student_ans'} .= ", " . - $curRes->{'student_ans'}; - $res->{'original_student_ans'} .= ", " . - $curRes->{'original_student_ans'}; - } else { - $res = $curRes; - } - $stuAnsDone .= "$j,"; - $doneOne = 1; - push( @doneStuAns, $j ); - last; - } - } - push(@missedAnsEval, $i) if ( ! $doneOne ); - } - # did we miss any correct answers? - if ( @missedAnsEval ) { - $res->{'score'} = 0; - - foreach ( @missedAnsEval ) { - my $fakeRes = $ansEval[$_]->evaluate('something'); - $res->{'correct_ans'} .= " AND " . - $fakeRes->{'correct_ans'}; - } - } - # did we miss any student answers? - if ( @doneStuAns != @sAns ) { - $res->{'score'} = 0; - - for ( my $i=0; $i<@sAns; $i++ ) { - if ( $stuAnsDone !~ /\b$i\b/ ) { - $res->{'student_ans'} .= ", " . uc($sAns[$i]); - $res->{'original_student_ans'} .= ", " . $sAns[$i]; - $res->{'preview_latex_string'} .= "\\quad \\mbox{" . - uc($sAns[$i]) . "}"; - } - } - } - return $res; - } - } ); -} - -# integrand_fun_cmp -# an answer evaluator to allow checking functions with 'dt' in them -# -sub integrand_fun_cmp { - my $cAns = shift(); - my %opts = @_; - - if ( defined( $opts{'var'} ) && $opts{'var'} ) { - $differential = "d" . $opts{'var'}->[0]; # we assume the first - push( @{$opts{'var'}}, 'Q' ); # variable is the - } else { # independent variable - $differential = "dx"; - $opts{'var'} = [ 'x', 'Q' ]; - } - $cAns =~ s/$differential/Q/; - $eval = fun_cmp( $cAns, %opts ); - - return( sub { - my $sAns = shift(); - $sAns = '' if ( ! defined( $sAns ) ); - $sAns =~ s/$differential/Q/; - - my $ansHash = $eval->evaluate( $sAns ); - # replace Q with the correct differential - # are there missing values? - foreach my $v ( qw( correct_ans original_correct_ans student_ans - original_student_ans preview_text_string - preview_latex_string ) ) { - $ansHash->{$v} = '' if ( ! defined( $ansHash->{$v} ) ); - } - $ansHash->{'correct_ans'} =~ s/Q/$differential/; - $ansHash->{'original_correct_ans'} =~ s/Q/$differential/; - $ansHash->{'student_ans'} =~ s/Q/$differential/; - $ansHash->{'original_student_ans'} =~ s/Q/$differential/; - $ansHash->{'preview_text_string'} =~ s/Q/$differential/; - $ansHash->{'preview_latex_string'} =~ s/Q/$differential/; - - return $ansHash; - } ); -} - -# numfun_cmp -# an answer evaluator that requires that the answer be -# simplified to the number given without returning an error if -# it's a function -# -sub numfun_cmp { - my $cAns = shift(); - my %opts = @_; - - my $eval1 = fun_cmp( $cAns, @_ ); - my $eval2 = num_cmp( $cAns, @_ ); - - return( sub { - my $sAns = shift(); - my $ans1 = $eval1->evaluate( $sAns ); - my $ans2 = $eval2->evaluate( $sAns ); - - if ( $ans2->{'score'} ) { - return( $ans2 ); - } else { - $ans1->{'score'} = 0; - return( $ans1 ); - } - } ); -} - -# numorfun_cmp -# an answer evaluator that first checks to see if the student answer -# checks correctly as a number, and if so returns that result; if not, -# repeats the check with a function checker and returns that. -# -sub numorfun_cmp { - my $cAns = shift(); - my ( $numOptsRef, $funOptsRef ) = @_; - my $eval1 = fun_cmp( $cAns, %$funOptsRef ); - my $eval2 = num_cmp( $cAns, %$numOptsRef ); - - return( sub { - my $sAns = shift(); - my $ans1 = $eval1->evaluate( $sAns ); - my $ans2 = $eval2->evaluate( $sAns ); - - if ( $ans2->{'score'} ) { - return( $ans2 ); - } else { - return( $ans1 ); - } - } ); -} - -sub classify_num_cmp { - return classify_cmp( @_, 'classifymode'=>'num' ); -} -sub classify_fun_cmp { - return classify_cmp( @_, 'classifymode'=>'fun' ); -} -sub classify_cmp { - my $cAns = shift(); - my %opts = @_; - - my $classifymode = 'num'; - if ( defined( $opts{'classifymode'} ) ) { - $classifymode = $opts{'classifymode'}; - delete( $opts{'classifymode'} ); - } - - my $ordered = 0; - if ( defined( $opts{'order'} ) ) { - $ordered = 1 if ( $opts{'order'} eq 'strict' ); - delete( $opts{'order'} ); - } - - my @pts = split(/\s*,\s*/, $cAns); - - my @xeval; my @ceval; - foreach ( @pts ) { - my ( $x, $c ) = split(/\s*=\s*/); - $c = '' if ( ! defined($c) ); - if ( $classifymode eq 'num' ) { - push( @xeval, num_cmp( $x, %opts ) ); - } else { - push( @xeval, fun_cmp( $x, %opts ) ); - } - push( @ceval, str_cmp( $c ) ); - } - - return( sub { - my $sAns = shift(); - my @sAnsList = split(/\s*,\s*/, $sAns); - my @sAnsw = (); - foreach ( @sAnsList ) { - my ( $x, $c ) = split(/\s*=\s*/); - $x = 'none' if ( ! defined( $x ) ); - $c = '' if ( ! defined( $c ) ); - push( @sAnsw, [ $x, $c ] ); - } - - # now build a set of answer hashes, one for each student answer - my @results = (); - my @undone = (); # list of unused evaluators - my $nummatched = 0; # number of student answers matched - for( my $j=0; $j<@xeval; $j++ ) { - my $done = 0; - if ( $ordered ) { - if ( $j < @sAnsw ) { - my $x_hash = $xeval[$j]->evaluate($sAnsw[$j]->[0]); - my $c_hash = $xeval[$j]->evaluate($sAnsw[$j]->[1]); - my $res = $x_hash->AND($c_hash); - $res->{'preview_text_string'} = - $x_hash->{'preview_text_string'} . "=" . - $c_hash->{'preview_text_string'}; - $res->{'preview_latex_string'} = - $x_hash->{'preview_latex_string'} . "=" . - $c_hash->{'preview_latex_string'}; - $results[$j] = $res; - $nummatched++; - } else { - last; - } - } else { - for ( my $i=0; $i<@sAnsw; $i++ ) { - my $x_hash = $xeval[$j]->evaluate($sAnsw[$i]->[0]); - - if ( $x_hash->score() ) { - my $c_hash = $ceval[$j]->evaluate($sAnsw[$i]->[1]); - - my $res = $x_hash->AND($c_hash); - $res->{'preview_text_string'} = - $x_hash->{'preview_text_string'}; - $res->{'preview_latex_string'} = - $x_hash->{'preview_latex_string'}; - - if ( defined($c_hash->{preview_text_string}) && - $c_hash->{preview_text_string} ne '' ) { - $res->{preview_text_string} .= "=" . - $c_hash->{'preview_text_string'}; - $res->{preview_latex_string} .= "=" . - $c_hash->{'preview_latex_string'}; - } - $results[$i] = $res; - $done = 1; - $nummatched++; - } - last if ( $done ); - } - if ( ! $done ) { - push( @undone, [ $xeval[$j], $ceval[$j] ] ); - } - } - last if ( $nummatched == @sAnsw ); - } - # check any student answers we haven't yet matched - if ( $nummatched < @sAnsw ) { - for ( my $i=0; $i<@sAnsw; $i++ ) { - next if ( ref($results[$i]) ); - my ( $x_hash, $c_hash, $res ); - if ( @undone ) { - my $aeval = shift(@undone); - $x_hash = $aeval->[0]->evaluate($sAnsw[$i]->[0]); - $c_hash = $aeval->[1]->evaluate($sAnsw[$i]->[1]); - $res = $x_hash->AND( $c_hash ); - } else { - $res = new AnswerHash; - $res->{'correct_ans'} = 'none'; - $res->{'original_correct_ans'} = 'none'; - $res->{'student_ans'} = $sAnsw[$i]->[0] . "=" . - $sAnsw[$i]->[1]; - $res->{'original_student_ans'} = $sAnsw[$i]->[0] . "=" . - $sAnsw[$i]->[1]; - $res->{'score'} = 0; - $x_hash = $xeval[0]->evaluate($sAnsw[$i]->[0]); - $c_hash = $ceval[0]->evaluate($sAnsw[$i]->[1]); - } - $res->{'preview_text_string'} = - $x_hash->{'preview_text_string'}; - $res->{'preview_latex_string'} = - $x_hash->{'preview_latex_string'}; - if ( defined( $c_hash->{preview_text_string} ) && - $c_hash->{preview_text_string} ne '' ) { - $res->{preview_text_string} .= "=" . - $c_hash->{'preview_text_string'}; - $res->{preview_latex_string} .= "=" . - $c_hash->{'preview_latex_string'}; - } - $results[$i] = $res; - } - } - # and get any other results they didn't submit - my @neededResults = (); - foreach ( @undone ) { - my $r = $_->[0]->evaluate(''); - $r->AND( $_->[1]->evaluate('') ); - $r->{score} = 0; - push( @neededResults, $r ); - } - - # ok, now build a big and... - my $endres = ( defined($results[0]) ) ? $results[0] : new AnswerHash; - for ( my $i=1; $i<@results; $i++ ) { - $endres = $endres->AND( $results[$i] ); - } -# for ( my $i=0; $i<@neededResults; $i++ ) { -# $endres = $endres->AND( $neededResults[$i] ); -# } - $endres->{'student_ans'} = $sAns; - $endres->{'score'} = 0 if ( @xeval > @sAnsw ); - - return $endres; - } ); -} - -1; - diff --git a/OpenProblemLibrary/macros/Mizzou/MUHelp.pl b/OpenProblemLibrary/macros/Mizzou/MUHelp.pl deleted file mode 100644 index 3f9e185e2a..0000000000 --- a/OpenProblemLibrary/macros/Mizzou/MUHelp.pl +++ /dev/null @@ -1,45 +0,0 @@ -sub BBRED { - MODES(TeX => '{\\color{red} ', HTML => ''); -}; - -sub EBRED { - MODES( TeX => '}', HTML => ''); -}; - -$main::BBRED = BBRED(); -$main::EBRED = EBRED(); - -$badmessage = "$BBRED Something helpful should go here. Please inform your instructor that it is missing! $EBRED"; - -sub MUHelp { - my $type = shift; - return $badmessage unless defined($type); - - my $rString = ""; - my $typeOkay = 0; - - if ($type =~ m/sqrt/i) { - $rstring = "If necessary, type ${BBOLD}sqrt()${EBOLD} to enter a square root. For example, to enter \\(\\sqrt{7x}\\), you would type your answer as sqrt(7x). You must put parentheses around everything you want to take the square root of!!"; - $typeOkay = 1; - } elsif ($type =~ m/absvaleqn/i) { - $rstring = "Enter your answers as a comma separated list if there is more than one correct answer. Type ${BBOLD}no solution${EBOLD} if the equation has no solution."; - $typeOkay = 1; - } elsif ($type =~ m/logans/i) { - $rstring = "Give an exact answer. If necessary, type ${BBOLD}log(b,x)${EBOLD} to enter \\(\\log_b(x)\\) as an answer. For example, type ${BBOLD}log(3,5)${EBOLD} to enter \\(\\log_3(5)\\). You may also use ${BBOLD}ln(15)${EBOLD} for \\(\\ln(15)\\), and ${BBOLD}log10(20)${EBOLD} for \\(\\log(20)\\). Enter your answers as a comma separated list if there is more than one correct answer. Type ${BBOLD}no solution${EBOLD} if the equation has no solution."; - $typeOkay = 1; - } elsif ($type =~ m/lineareqns/i) { - $rstring = "If necessary, type ${BBOLD}no solutions${EBOLD} if the equation has no solution or type ${BBOLD}infinitely many${EBOLD} if there are infinitely many solutions."; - $typeOkay = 1; - } elsif ($type =~ m/quadeqns/i) { - $rstring = "Enter your answers as a comma separated list if there is more than one correct answer. Type ${BBOLD}no solution${EBOLD} if the equation has no solution. If necessary, type ${BBOLD}sqrt()${EBOLD} to enter a square root. For example, to enter \\(\\sqrt{7x}\\), you would type your answer as sqrt(7x). You must put parenthesis are everything you want to take the square root of!!"; - $typeOkay = 1; - } - - if ($typeOkay == 1) {return $rstring;}; - return $badmessage; -} - -1; - - -## Append .helpLink("exponents","Click here for further help.") to a string to add a help link. \ No newline at end of file diff --git a/OpenProblemLibrary/macros/NAU/PGnauBinpacking.pl b/OpenProblemLibrary/macros/NAU/PGnauBinpacking.pl deleted file mode 100755 index 5e9c735ded..0000000000 --- a/OpenProblemLibrary/macros/NAU/PGnauBinpacking.pl +++ /dev/null @@ -1,180 +0,0 @@ -sub PGnextfit{ - my($binsize,@input) = @_; - my(@list, $val); - - $sum=0; - $bin=1; - foreach $val(@input){ - $sum+=$val; - if ($sum >$binsize){ - $bin++; - $sum=$val; - } - push @list,$bin; -} -@list; -} - -sub PGdec_sort{ - my(@input) = @_; - my(@list, @newlist, $val, $newval); - - foreach $val(@input){ - $newval=-$val; - push @newlist, $newval; - } - @list1=num_sort @newlist; - foreach $val(@list1) { - $newval=-$val; - push @list, $newval; - } -@list; -} - -sub PGdnextfit{ - my($binsize,@input) = @_; - my(@list, @newlist, $val); - - @newlist=PGdec_sort(@input); - - $sum=0; - $bin=1; - foreach $val(@newlist){ - $sum+=$val; - if ($sum >$binsize){ - $bin++; - $sum=$val; - } - push @list,$bin; -} -@list; -} - -sub PGfirstfit{ - my($binsize,@input) = @_; - my(@list, @total, $val, $bin, $sum, $size); - - @total = (); - @list=(); - - foreach $val(@input){ - $bin=-1; - $size = scalar @total; - - do{$bin++; - if($bin > $size - 1){push @total,0;} - $sum=$total[$bin]+$val; - } - while($sum > $binsize); - - $total[$bin]+=$val; - push @list,$bin+1; - } - -@list; -} - -sub PGdfirstfit{ - my($binsize,@input) = @_; - my(@list,@newlist,@total,$bin,$val,$sum,$size); - - @total=(); - - @newlist=PGdec_sort(@input); - foreach $val(@newlist){ - $bin=-1; - $size=scalar@total; - do{$bin++; - if($bin>$size-1){push @total,0;} - $sum=$total[$bin]+$val; - - } - while( $sum > $binsize ); - $total[$bin] += $val; - push @list, $bin+1; - } -@list; -} - -sub PGworstfit{ - my( $binsize, @input )=@_; - my( $minbin, $bin, $sum, $minloc, $val, $minval, $a, @total, @bins ); - - @total = (0); - $minval = 0; - $minbin = 0; - $bin = 0; - - foreach $val( @input ){ - - $sum = $minval + $val; - - if( $sum > $binsize ){ - push @total, $val; - $bin = scalar@total - 1; - - if( $val < $minval ){ - $minbin = scalar@total - 1; - $minval = $val; - } else{ $minval = $total[$minbin]; - } - } else{ $total[$minbin] += $val; - $bin = $minbin; - if( $total[$minbin] < min( @total )){ - $minval = $total[$minbin]; - } - else{$minloc = -1; - $a = min( @total ); - do{$minloc++; - } until( $total[$minloc] == $a ); - $minbin = $minloc; - $minval = $total[$minbin]; - } - } push @bins, $bin+1; -} -@bins; -} - -sub PGdworstfit{ - my($binsize, @input)=@_; - my($minbin, $bin, $sum, $minloc, $val, $minval, $a, @total, @bins, @newlist ); - - @newlist = PGdec_sort(@input); - - @total = (0); - $minval = 0; - $minbin = 0; - $bin = 0; - - foreach $val( @newlist ){ - - $sum = $minval+$val; - - if( $sum > $binsize ){ - push @total, $val; - $bin = scalar@total-1; - - if( $val < $minval ){ - $minbin = scalar@total-1; - $minval = $val; - } else{ $minval = $total[ $minbin ]; - } - } else{ $total[ $minbin ] += $val; - $bin = $minbin; - if( $total[ $minbin ] < min( @total )){ - $minval = $total[ $minbin ]; - } - else{$minloc = -1; - $a = min( @total ); - do{$minloc++; - } until($total[ $minloc ] == $a); - $minbin = $minloc; - $minval = $total[ $minbin ]; - } - } push @bins, $bin+1; - - } -@bins; -} - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/NAU/PGnauGraphCatalog.pl b/OpenProblemLibrary/macros/NAU/PGnauGraphCatalog.pl deleted file mode 100755 index 828768a87e..0000000000 --- a/OpenProblemLibrary/macros/NAU/PGnauGraphCatalog.pl +++ /dev/null @@ -1,1260 +0,0 @@ -# All simple graphs with fewer than 8 vertices - -push @graph1, "0;"; - -push @graph2, "0 0;0 0;", - "0 1;1 0;"; - -push @graph3, "0 0 0;0 0 0;0 0 0;", - "0 0 1;0 0 0;1 0 0;", - "0 0 1;0 0 1;1 1 0;", - "0 1 1;1 0 1;1 1 0;"; - -push @graph4, "0 0 0 0;0 0 0 0;0 0 0 0;0 0 0 0;", - "0 0 0 1;0 0 0 0;0 0 0 0;1 0 0 0;", - "0 0 0 1;0 0 0 1;0 0 0 0;1 1 0 0;", - "0 0 0 1;0 0 0 1;0 0 0 1;1 1 1 0;", - "0 0 1 0;0 0 0 1;1 0 0 0;0 1 0 0;", - "0 0 1 1;0 0 0 1;1 0 0 0;1 1 0 0;", - "0 0 1 1;0 0 0 0;1 0 0 1;1 0 1 0;", - "0 0 1 1;0 0 0 1;1 0 0 1;1 1 1 0;", - "0 0 1 1;0 0 1 1;1 1 0 0;1 1 0 0;", - "0 0 1 1;0 0 1 1;1 1 0 1;1 1 1 0;", - "0 1 1 1;1 0 1 1;1 1 0 1;1 1 1 0;"; - -push @graph5, "0 0 0 0 0;0 0 0 0 0;0 0 0 0 0;0 0 0 0 0;0 0 0 0 0;", - "0 0 0 0 1;0 0 0 0 0;0 0 0 0 0;0 0 0 0 0;1 0 0 0 0;", - "0 0 0 0 1;0 0 0 0 1;0 0 0 0 0;0 0 0 0 0;1 1 0 0 0;", - "0 0 0 0 1;0 0 0 0 1;0 0 0 0 1;0 0 0 0 0;1 1 1 0 0;", - "0 0 0 0 1;0 0 0 0 1;0 0 0 0 1;0 0 0 0 1;1 1 1 1 0;", - "0 0 0 1 0;0 0 0 0 1;0 0 0 0 0;1 0 0 0 0;0 1 0 0 0;", - "0 0 0 1 1;0 0 0 0 1;0 0 0 0 0;1 0 0 0 0;1 1 0 0 0;", - "0 0 0 1 0;0 0 0 0 1;0 0 0 0 1;1 0 0 0 0;0 1 1 0 0;", - "0 0 0 1 1;0 0 0 0 0;0 0 0 0 0;1 0 0 0 1;1 0 0 1 0;", - "0 0 0 1 1;0 0 0 0 1;0 0 0 0 1;1 0 0 0 0;1 1 1 0 0;", - "0 0 0 1 1;0 0 0 0 1;0 0 0 0 0;1 0 0 0 1;1 1 0 1 0;", - "0 0 0 1 1;0 0 0 0 1;0 0 0 0 1;1 0 0 0 1;1 1 1 1 0;", - "0 0 0 1 1;0 0 0 1 1;0 0 0 0 0;1 1 0 0 0;1 1 0 0 0;", - "0 0 0 1 1;0 0 0 1 1;0 0 0 0 1;1 1 0 0 0;1 1 1 0 0;", - "0 0 0 1 1;0 0 0 1 1;0 0 0 0 0;1 1 0 0 1;1 1 0 1 0;", - "0 0 0 1 1;0 0 0 1 0;0 0 0 0 1;1 1 0 0 1;1 0 1 1 0;", - "0 0 0 1 1;0 0 0 1 1;0 0 0 0 1;1 1 0 0 1;1 1 1 1 0;", - "0 0 0 1 1;0 0 0 1 1;0 0 0 1 1;1 1 1 0 0;1 1 1 0 0;", - "0 0 0 1 1;0 0 0 1 1;0 0 0 1 1;1 1 1 0 1;1 1 1 1 0;", - "0 0 1 0 1;0 0 0 1 1;1 0 0 0 0;0 1 0 0 0;1 1 0 0 0;", - "0 0 1 0 1;0 0 0 1 0;1 0 0 0 1;0 1 0 0 0;1 0 1 0 0;", - "0 0 1 0 1;0 0 0 1 1;1 0 0 0 1;0 1 0 0 0;1 1 1 0 0;", - "0 0 1 0 1;0 0 0 1 1;1 0 0 0 1;0 1 0 0 1;1 1 1 1 0;", - "0 0 1 1 0;0 0 0 1 1;1 0 0 0 1;1 1 0 0 0;0 1 1 0 0;", - "0 0 1 1 1;0 0 0 1 1;1 0 0 0 1;1 1 0 0 0;1 1 1 0 0;", - "0 0 1 1 1;0 0 0 1 1;1 0 0 0 1;1 1 0 0 1;1 1 1 1 0;", - "0 0 1 1 1;0 0 0 0 1;1 0 0 1 1;1 0 1 0 0;1 1 1 0 0;", - "0 0 1 1 1;0 0 0 0 0;1 0 0 1 1;1 0 1 0 1;1 0 1 1 0;", - "0 0 1 1 1;0 0 0 0 1;1 0 0 1 1;1 0 1 0 1;1 1 1 1 0;", - "0 0 1 1 1;0 0 0 1 1;1 0 0 1 1;1 1 1 0 1;1 1 1 1 0;", - "0 0 1 1 1;0 0 1 1 1;1 1 0 0 1;1 1 0 0 0;1 1 1 0 0;", - "0 0 1 1 1;0 0 1 1 1;1 1 0 0 1;1 1 0 0 1;1 1 1 1 0;", - "0 0 1 1 1;0 0 1 1 1;1 1 0 1 1;1 1 1 0 1;1 1 1 1 0;", - "0 1 1 1 1;1 0 1 1 1;1 1 0 1 1;1 1 1 0 1;1 1 1 1 0;"; - -push @graph6, "0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;", - "0 0 0 0 0 1;0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;1 0 0 0 0 0;", - "0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;1 1 0 0 0 0;", - "0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 0;0 0 0 0 0 0;1 1 1 0 0 0;", - "0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 0;1 1 1 1 0 0;", - "0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;1 1 1 1 1 0;", - "0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 0;0 0 0 0 0 0;1 0 0 0 0 0;0 1 0 0 0 0;", - "0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 0;0 0 0 0 0 0;1 0 0 0 0 0;1 1 0 0 0 0;", - "0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 0;1 0 0 0 0 0;0 1 1 0 0 0;", - "0 0 0 0 1 1;0 0 0 0 0 0;0 0 0 0 0 0;0 0 0 0 0 0;1 0 0 0 0 1;1 0 0 0 1 0;", - "0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 0;1 0 0 0 0 0;1 1 1 0 0 0;", - "0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;1 0 0 0 0 0;0 1 1 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 0;0 0 0 0 0 0;1 0 0 0 0 1;1 1 0 0 1 0;", - "0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;1 0 0 0 0 0;1 1 1 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 0;1 0 0 0 0 1;1 1 1 0 1 0;", - "0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 1;0 0 0 0 0 1;1 0 0 0 0 1;1 1 1 1 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 0;0 0 0 0 0 0;1 1 0 0 0 0;1 1 0 0 0 0;", - "0 0 0 0 1 0;0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 1;1 1 0 0 0 0;0 0 1 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 0;1 1 0 0 0 0;1 1 1 0 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 1;1 1 0 0 0 0;1 0 1 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 0;0 0 0 0 0 0;1 1 0 0 0 1;1 1 0 0 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 0;1 1 0 0 0 1;1 0 1 0 1 0;", - "0 0 0 0 1 0;0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 1;1 1 0 0 0 1;0 0 1 1 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 1;1 1 0 0 0 0;1 1 1 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 0;1 1 0 0 0 1;1 1 1 0 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 0;0 0 0 0 0 1;0 0 0 0 0 1;1 1 0 0 0 1;1 0 1 1 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 1;0 0 0 0 0 1;1 1 0 0 0 1;1 1 1 1 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 0;1 1 1 0 0 0;1 1 1 0 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 0;0 0 0 0 0 1;1 1 1 0 0 0;1 1 0 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 0;1 1 1 0 0 1;1 1 1 0 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 0;0 0 0 0 0 1;1 1 1 0 0 1;1 1 0 1 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;1 1 1 1 0 0;1 1 1 1 0 0;", - "0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;0 0 0 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 0 1 0 0;0 0 0 0 1 0;0 0 0 0 0 1;1 0 0 0 0 0;0 1 0 0 0 0;0 0 1 0 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 0 0;0 1 0 0 0 0;1 1 0 0 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 0;0 0 0 0 0 1;1 0 0 0 0 0;0 1 0 0 0 0;1 0 1 0 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 0;0 0 0 0 0 0;1 0 0 0 0 1;0 1 0 0 0 0;1 0 0 1 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 0;0 1 0 0 0 0;1 1 1 0 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 0 1;0 1 0 0 0 0;1 1 0 1 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 0;0 0 0 0 0 1;1 0 0 0 0 1;0 1 0 0 0 0;1 0 1 1 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 1;0 1 0 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 0 1;0 1 0 0 0 1;1 1 0 1 1 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 1;0 1 0 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 0;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 0 1;1 1 0 0 0 0;0 1 0 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 0;1 1 0 0 0 0;1 1 1 0 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 0 1;1 1 0 0 0 0;1 1 0 1 0 0;", - "0 0 0 1 1 0;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 1;1 1 0 0 0 0;0 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 0 0;1 1 0 0 0 1;1 1 0 0 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 0;0 0 0 0 0 1;1 0 0 0 0 0;1 1 0 0 0 1;1 0 1 0 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 1;1 1 0 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 0;1 1 0 0 0 1;1 1 1 0 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 0 1;1 1 0 0 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 0 1;1 1 0 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 0;1 0 0 0 0 0;0 1 1 0 0 0;1 1 0 0 0 0;", - "0 0 0 1 0 0;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 0;0 1 1 0 0 0;0 1 1 0 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 0;0 0 0 0 1 0;1 0 0 0 0 1;0 1 1 0 0 0;1 0 0 1 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 0;0 1 1 0 0 0;1 1 1 0 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 0;1 0 0 0 0 1;0 1 1 0 0 0;1 1 0 1 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 0;1 0 0 0 0 0;0 1 1 0 0 1;1 1 0 0 1 0;", - "0 0 0 1 0 0;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 0;0 1 1 0 0 1;0 1 1 0 1 0;", - "0 0 0 1 0 1;0 0 0 0 1 0;0 0 0 0 1 0;1 0 0 0 0 1;0 1 1 0 0 1;1 0 0 1 1 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 1;0 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 0;0 1 1 0 0 1;1 1 1 0 1 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 0;1 0 0 0 0 1;0 1 1 0 0 1;1 1 0 1 1 0;", - "0 0 0 1 0 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 1;0 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 0 0;0 0 0 0 0 0;1 0 0 0 1 1;1 0 0 1 0 1;1 0 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 0 1;0 0 0 0 0 1;1 0 0 0 1 1;1 0 0 1 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 0 1;0 0 0 0 0 0;1 0 0 0 1 1;1 0 0 1 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 0 1;0 0 0 0 0 1;1 0 0 0 1 1;1 0 0 1 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 0;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 1;1 1 1 0 0 0;0 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 0;1 1 1 0 0 1;1 1 1 0 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 1 0;1 0 0 0 0 1;1 1 1 0 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 0;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 1;1 1 1 0 0 1;0 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 1 1;1 1 0 1 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 0;1 0 0 0 1 1;1 1 0 1 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 0;0 0 0 0 0 1;1 0 0 0 1 1;1 1 0 1 0 1;1 0 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 0 1;1 0 0 0 1 1;1 1 0 1 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 1 1;1 1 1 1 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 0 1 1;0 0 0 0 1 1;1 0 0 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 1;1 1 0 0 0 0;1 1 0 0 0 0;1 1 1 0 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 0;1 1 0 0 0 1;1 1 0 0 0 0;1 1 0 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 1;1 1 0 0 0 1;1 1 0 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 0;1 1 0 0 0 1;1 1 0 0 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 1;1 1 0 0 0 1;1 1 0 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 0;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 0 0;1 0 1 0 0 0;0 1 1 0 0 0;", - "0 0 0 1 1 1;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 0 0;1 0 1 0 0 0;1 1 1 0 0 0;", - "0 0 0 1 1 1;0 0 0 1 0 0;0 0 0 0 1 1;1 1 0 0 0 1;1 0 1 0 0 0;1 0 1 1 0 0;", - "0 0 0 1 1 0;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 0 1;1 0 1 0 0 0;0 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 0 0;0 0 0 0 1 0;1 1 0 0 0 1;1 0 1 0 0 1;1 0 0 1 1 0;", - "0 0 0 1 1 0;0 0 0 1 0 1;0 0 0 0 1 0;1 1 0 0 0 1;1 0 1 0 0 1;0 1 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 0 1;1 0 1 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 0 1;0 0 0 0 1 0;1 1 0 0 0 1;1 0 1 0 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 0;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 0 1;1 0 1 0 0 1;0 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 0 1;1 0 1 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 1 1;1 1 0 0 0 0;1 1 1 0 0 0;1 1 1 0 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 1 0;1 1 0 0 0 1;1 1 1 0 0 0;1 1 0 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 1 1;1 1 0 0 0 0;1 1 1 0 0 1;1 1 1 0 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 1;1 1 0 0 1 1;1 1 0 1 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 0;1 1 0 0 1 1;1 1 0 1 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 0;0 0 0 0 0 1;1 1 0 0 1 1;1 1 0 1 0 1;1 0 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 0 1;1 1 0 0 1 1;1 1 0 1 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 1 1;1 0 1 1 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 0;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 1 1;1 0 1 1 0 1;0 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 0 1;0 0 0 0 1 1;1 1 0 0 1 1;1 0 1 1 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 0 1 1;1 1 0 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 1 1 1;1 1 1 0 0 0;1 1 1 0 0 0;1 1 1 0 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 1 1 1;1 1 1 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 1 1 0;1 1 1 0 0 1;1 1 1 0 0 1;1 1 0 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 1 1 1;1 1 1 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 0 1 1 1;0 0 0 1 1 1;0 0 0 1 1 1;1 1 1 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 1 0 1 0;0 0 0 1 0 1;1 0 0 0 1 0;0 1 0 0 0 1;1 0 1 0 0 0;0 1 0 1 0 0;", - "0 0 1 0 1 1;0 0 0 1 0 1;1 0 0 0 1 1;0 1 0 0 0 0;1 0 1 0 0 0;1 1 1 0 0 0;", - "0 0 1 0 1 1;0 0 0 1 0 1;1 0 0 0 1 0;0 1 0 0 0 1;1 0 1 0 0 0;1 1 0 1 0 0;", - "0 0 1 0 1 1;0 0 0 1 0 0;1 0 0 0 1 1;0 1 0 0 0 0;1 0 1 0 0 1;1 0 1 0 1 0;", - "0 0 1 0 1 1;0 0 0 1 0 1;1 0 0 0 1 1;0 1 0 0 0 1;1 0 1 0 0 0;1 1 1 1 0 0;", - "0 0 1 0 1 1;0 0 0 1 0 1;1 0 0 0 1 1;0 1 0 0 0 0;1 0 1 0 0 1;1 1 1 0 1 0;", - "0 0 1 0 1 1;0 0 0 1 0 1;1 0 0 0 1 1;0 1 0 0 0 1;1 0 1 0 0 1;1 1 1 1 1 0;", - "0 0 1 0 1 1;0 0 0 1 1 1;1 0 0 0 1 0;0 1 0 0 0 1;1 1 1 0 0 0;1 1 0 1 0 0;", - "0 0 1 0 1 1;0 0 0 1 1 1;1 0 0 0 1 1;0 1 0 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 1 0 1 1;0 0 0 1 1 1;1 0 0 0 1 1;0 1 0 0 0 0;1 1 1 0 0 1;1 1 1 0 1 0;", - "0 0 1 0 1 1;0 0 0 1 1 1;1 0 0 0 1 0;0 1 0 0 0 1;1 1 1 0 0 1;1 1 0 1 1 0;", - "0 0 1 0 1 1;0 0 0 1 1 0;1 0 0 0 1 1;0 1 0 0 0 1;1 1 1 0 0 1;1 0 1 1 1 0;", - "0 0 1 0 1 1;0 0 0 1 1 1;1 0 0 0 1 1;0 1 0 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 1 0 1 1;0 0 0 1 1 1;1 0 0 0 1 1;0 1 0 0 1 1;1 1 1 1 0 0;1 1 1 1 0 0;", - "0 0 1 0 1 1;0 0 0 1 1 1;1 0 0 0 1 1;0 1 0 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 1 1 0 1;0 0 0 1 1 1;1 0 0 0 1 1;1 1 0 0 0 0;0 1 1 0 0 0;1 1 1 0 0 0;", - "0 0 1 1 0 1;0 0 0 1 1 1;1 0 0 0 1 0;1 1 0 0 0 1;0 1 1 0 0 0;1 1 0 1 0 0;", - "0 0 1 1 0 1;0 0 0 1 1 1;1 0 0 0 1 1;1 1 0 0 0 1;0 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 1 1 0 1;0 0 0 1 1 1;1 0 0 0 1 1;1 1 0 0 0 1;0 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 0;0 0 0 1 1 1;1 0 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 0;0 1 1 1 0 0;", - "0 0 1 1 1 1;0 0 0 1 1 1;1 0 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 1 1 1 1;0 0 0 1 1 1;1 0 0 0 1 0;1 1 0 0 0 1;1 1 1 0 0 1;1 1 0 1 1 0;", - "0 0 1 1 1 1;0 0 0 1 1 1;1 0 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 0 1 1 1;1 0 0 0 1 1;1 1 0 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 0 0 1 1;1 0 0 1 1 1;1 0 1 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 1 1 1 1;0 0 0 0 1 1;1 0 0 1 1 1;1 0 1 0 0 0;1 1 1 0 0 1;1 1 1 0 1 0;", - "0 0 1 1 1 1;0 0 0 0 1 1;1 0 0 1 1 1;1 0 1 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 0 0 0 1;1 0 0 1 1 1;1 0 1 0 1 1;1 0 1 1 0 0;1 1 1 1 0 0;", - "0 0 1 1 1 1;0 0 0 0 0 0;1 0 0 1 1 1;1 0 1 0 1 1;1 0 1 1 0 1;1 0 1 1 1 0;", - "0 0 1 1 1 1;0 0 0 0 0 1;1 0 0 1 1 1;1 0 1 0 1 1;1 0 1 1 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 0 0 1 1;1 0 0 1 1 1;1 0 1 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 0 1 1 1;1 0 0 1 1 1;1 1 1 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 1 1 1 1;1 1 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 0;1 1 1 1 0 0;", - "0 0 1 1 1 1;0 0 1 1 1 1;1 1 0 0 1 1;1 1 0 0 0 0;1 1 1 0 0 1;1 1 1 0 1 0;", - "0 0 1 1 1 1;0 0 1 1 1 0;1 1 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 1;1 0 1 1 1 0;", - "0 0 1 1 1 1;0 0 1 1 1 1;1 1 0 0 1 1;1 1 0 0 0 1;1 1 1 0 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 1 1 1 1;1 1 0 0 1 1;1 1 0 0 1 1;1 1 1 1 0 0;1 1 1 1 0 0;", - "0 0 1 1 1 1;0 0 1 1 1 1;1 1 0 0 1 1;1 1 0 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 0 1 1 1 1;0 0 1 1 1 1;1 1 0 1 1 1;1 1 1 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;", - "0 1 1 1 1 1;1 0 1 1 1 1;1 1 0 1 1 1;1 1 1 0 1 1;1 1 1 1 0 1;1 1 1 1 1 0;"; - -push @graph7, "0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;", - "0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;", - "0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 0;", - "0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 0 0 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 0 0 0 0 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 0 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 0 0 0 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 0 0 0 0 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;0 0 1 1 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 0 1 1 0 0 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;0 0 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 0 0 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 1 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;0 0 1 1 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;0 0 1 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 1 0 0 0 0;1 0 0 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 0 0 0 0 0;0 0 1 0 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 1 0 0 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 0 1 0 0 0 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;0 0 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 0 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 0 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 0 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 0 0 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 0 0 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 0 0 0 0;1 1 0 0 0 0 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 0 0 0 0;0 1 1 0 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 0;1 0 0 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 0 0 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 0 0 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 0 0 0 1 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 0 0 0 1;0 1 1 0 0 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 0 0 1 0 1 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;0 1 0 1 0 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 0 0 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 0 0 0 1 0 0;0 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 0 0 0 1 0 1;1 0 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 0 0 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 0 0 0 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 0 0 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 1 0 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 0;0 1 0 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 1 0 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;0 1 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;0 1 1 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 1 1 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 1 1 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;0 1 1 1 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 1 1 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 1 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 1 0 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 1 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 1 0 1;1 0 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 1 0 0 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 1 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 1 1 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 1 0 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 1 0 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 0 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 0 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 0 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 0 1 0 0 0 0;0 1 1 0 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 0 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 0 1 0 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 0 1 0 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;0 0 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 0 0 0 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 0 0 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 0 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 0 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 1 0 0 0 0 0;0 0 1 1 0 0 0;1 1 0 0 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 0;0 0 1 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 1 0 0 0 0 1;0 0 1 1 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 1;0 0 1 1 0 0 0;1 0 1 0 1 0 0;", - "0 0 0 0 1 0 0;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;0 0 1 1 0 0 0;0 0 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 0;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 1 0 0 0 0 1;0 0 1 1 0 0 1;1 0 0 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;0 0 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 1;0 0 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;0 0 1 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 1 0 0 0 0 1;0 0 1 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 1;0 0 1 1 0 0 1;1 0 1 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;0 0 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 1;0 0 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;0 0 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;1 0 1 1 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 1;1 0 1 1 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 0 1 1 0 0 0;0 0 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;1 0 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 1;1 0 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 0 1 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 0 1 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 0;1 0 1 1 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;1 0 1 1 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;1 0 1 1 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 1 0 0 0 0 1;1 0 1 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 0 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;1 0 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 0 1;1 0 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 0 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 0 1 1 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 0 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 1 0;1 1 0 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 0;1 0 1 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 0 1 0 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 0;0 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 0 1 0 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 0 1 0 1 0 1;0 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 1;1 0 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 0 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 0;0 0 1 1 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 1 1;0 0 1 1 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;0 0 1 1 1 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 1 0 0 0 1 1;0 0 1 1 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 1 1;0 0 1 1 1 0 1;1 0 1 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;0 0 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 1 1;0 0 1 1 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;0 0 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 0;1 1 1 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 0;1 1 1 0 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 0;1 0 1 1 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 0 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 0;1 0 1 1 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 1 0 0 0 1 1;1 0 1 1 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 0 1 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 0 1 1 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 0 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 0 0;1 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 0 1 0 1 1 0;", - "0 0 0 0 1 1 0;0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;0 0 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 1 1 0 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 1 1 1 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;0 1 0 0 0 0 0;0 0 1 0 0 0 0;1 1 0 0 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 0;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;0 0 1 0 0 0 0;1 0 0 1 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;0 0 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;0 0 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;0 0 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 1;0 0 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;0 0 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;0 0 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 0 1 0 0 0 0;0 1 1 0 0 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 0 0 0;0 0 1 1 0 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;0 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 0 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 0 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 0 0 0;1 0 1 1 0 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;0 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 0 1 0 0 0 1;1 1 0 0 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 0 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 0 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 0 1 0 0 0;0 1 0 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 0 1 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 0;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 0 1 0 0 0;1 0 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 0 1 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 0 1 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 0;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 0 1 0 0 1;1 0 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 0 1 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 0;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 0 1 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 0 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 0 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 0;0 0 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;0 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 1 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 1 0 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 1;0 0 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;0 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;0 1 0 0 0 0 0;1 1 0 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 1 0 1 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 0 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 1 0 1 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 0 1 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 1 0 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 0 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 1 1 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 1 1 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 1 1 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 1 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 1 1 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 0 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 0 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 0 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 1 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;0 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 1 1;1 1 0 1 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;0 1 0 0 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;0 1 0 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;0 1 0 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 0;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 0;0 0 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 0;0 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 0;1 0 1 1 0 0 0;", - "0 0 0 1 1 0 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;1 1 0 0 0 0 1;0 1 1 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;0 1 1 0 0 0 0;1 0 1 0 1 0 0;", - "0 0 0 1 1 0 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;0 1 1 0 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;1 1 0 0 0 0 0;0 1 1 0 0 0 1;1 1 0 0 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 0;0 1 1 0 0 0 1;1 0 1 0 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;1 1 0 0 0 0 1;0 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;0 1 1 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 1 0 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;0 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;1 1 0 0 0 0 1;0 1 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 1 0 0 0 0 0;0 1 0 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 0 0 0;0 1 0 1 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 0;1 1 0 0 0 0 1;0 1 0 1 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 0 0;0 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 1 0 0 0 0 1;0 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 0 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 0 1 0 0 0;0 1 0 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 0 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 1 0 0 0 0 0;1 1 0 1 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 0;1 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 0 0;1 1 0 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 0;1 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;1 1 0 0 0 0 0;0 1 1 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 1 0;1 1 0 0 0 0 1;0 1 1 1 0 0 0;1 0 1 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 0;0 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;1 1 0 0 0 0 1;0 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;1 1 0 0 0 0 0;0 1 1 1 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 1 0 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 0;0 1 1 1 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 0;0 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 0;1 1 0 0 0 0 1;0 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;0 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 0 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;0 1 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;0 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 0;1 0 1 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 1 1;1 0 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 0 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 0;1 1 1 0 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 0;0 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 0 0;0 1 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 0 0 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;0 1 1 0 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;0 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 0 1;0 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 0 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;0 1 1 0 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;0 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;0 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 0 0 0;0 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 0 0 0 0;0 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 0 0 0;1 0 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 0;0 1 1 0 0 0 0;1 1 0 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 1 0;0 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 1 0;0 1 1 0 0 0 1;1 1 0 1 0 0 0;1 0 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 0 1 0 0 0;1 0 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 0 0;1 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 0;0 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 0 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 0 0;1 1 0 1 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 0 0;1 1 0 1 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 0 0;1 1 0 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 0;0 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 0 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 0;1 1 0 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 0;0 1 1 0 0 1 0;1 1 0 0 1 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 0 1;0 1 1 0 0 1 0;1 1 0 0 1 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 0;1 1 0 0 1 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 0;1 1 0 0 1 0 1;0 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 0 0;0 1 1 0 0 1 1;1 1 0 0 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 0 0;0 1 1 0 0 1 1;1 1 0 0 1 0 1;1 0 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 1;0 1 0 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 1;0 0 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 0;1 1 0 0 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 0;0 1 1 0 0 1 1;1 1 0 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 0 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 0;0 1 1 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 1 1;0 1 1 0 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;0 1 1 0 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 0;0 1 1 0 0 1 1;0 1 1 0 1 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 0 0 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 1 1;0 1 1 0 1 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 0;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;0 1 1 0 1 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;0 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 1 1;0 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;0 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;0 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 0;1 0 0 1 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 1;1 0 0 0 0 1 0;0 1 1 0 0 1 1;1 0 0 1 1 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 0 0 1 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 1;1 0 0 0 0 1 0;0 1 1 0 0 1 0;1 0 0 1 1 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 1 0;1 0 0 1 1 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 0;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 0 0 1 1 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 0 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 0;1 0 0 1 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 1;1 0 0 0 0 1 0;0 1 1 0 0 1 1;1 0 0 1 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 0 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 0 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 0 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;0 1 1 0 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;0 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;0 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 0;1 1 1 0 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 0;1 1 1 0 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;0 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 1 0 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;0 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 0;1 1 0 1 1 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 0;1 1 0 1 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 1 0 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 0 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;0 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 0 0 1 0 0 0;0 1 1 0 0 0 0;0 1 1 0 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 0 0 1 0 0 0;0 1 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 0 0 1 0 0 1;0 1 1 0 0 0 0;1 0 0 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 0;1 0 0 1 0 0 0;0 1 1 0 0 0 1;1 1 0 0 0 1 0;", - "0 0 0 1 1 0 0;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 0 0 1 0 0 0;0 1 1 0 0 0 1;0 1 1 0 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 0 0 1 0 0 0;0 1 1 0 0 0 1;1 0 0 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 0 0 1 0 0 0;0 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 0 0 1 0 0 1;0 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 0 0 1 0 0 0;0 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 0 0 1 0 0 0;0 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 0;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 0 0 1 0 0 1;0 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 0 0 1 0 0 1;0 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 0 0 1 0 0 0;0 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 0 0 1 0 0 1;0 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 0 0 1 0 0 1;0 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 0 0;0 0 0 0 0 0 0;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 0 0 1 1 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 0 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 0;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 0 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 0 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 0 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 0 0 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 0 0 1 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 0;1 0 0 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 0;1 0 0 0 1 1 1;1 0 0 1 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 0 0 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 0;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 0;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 1 0 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 0 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 0 0 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 1 1 0;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;0 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 0;1 1 1 0 0 0 0;0 1 1 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 0;0 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 0;1 1 1 0 0 0 1;0 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 1 0;1 1 1 0 0 0 1;0 1 1 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 0 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 0;1 1 1 0 0 0 1;0 1 1 1 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 0;0 0 0 0 1 1 0;1 0 0 0 0 1 1;1 1 1 0 0 0 1;0 1 1 1 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 1;0 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 0;1 1 1 0 0 0 1;0 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 1 1;1 1 1 0 0 0 1;0 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 1;0 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 0;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;1 1 1 0 0 1 0;1 1 0 1 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 0;1 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 1 0 1;1 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 0 1;1 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 0 1 1;1 1 1 0 0 1 1;0 1 1 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 1 1;0 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 1 0 1 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 1 0 1 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 1 0 1 0 0 0;1 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 0;1 1 1 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 0;1 1 1 0 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 1 0 1 0 0 1;1 1 1 0 0 0 1;1 0 1 0 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 1 0 1 0 0 1;1 1 1 0 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 1;0 1 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 1;0 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 0;1 1 0 1 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 0;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 0 1;1 1 0 1 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 1 1 0;1 1 0 1 0 0 1;1 1 0 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 0;1 1 0 1 0 0 1;1 1 0 1 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 0;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 0 1 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 0;1 1 0 1 0 0 1;1 1 0 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 0 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 0 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 0 1 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 1 1 0;1 1 0 1 0 0 1;1 0 1 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 1 1 0;1 1 0 1 0 0 1;1 0 1 1 0 0 1;0 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 0;0 0 0 0 0 1 0;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 0 1 1 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 0 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 1 1 0;1 1 0 1 0 0 1;1 0 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 0;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 0 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 0 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 0;1 0 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 0;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 0 1;1 0 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 0 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 1 1;1 0 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 0 1 1;1 0 0 0 1 1 1;1 1 0 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 1 1 0;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 0;1 0 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 0 1 1 1;0 0 0 0 1 1 1;1 0 0 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 0;1 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 0;1 1 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 0;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 1 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 0 0;1 1 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 0 1;1 1 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 0 1 1;1 1 0 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 0;1 0 1 0 0 0 0;0 1 1 0 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 1 0 0 0 0;1 1 0 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 0;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 1 0 0 0 0;1 0 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;0 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 0 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;0 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 0;1 0 1 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 0;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 1 0 0 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 0 1 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 0 0;0 0 0 0 1 1 1;1 1 0 0 0 1 0;1 0 1 0 0 0 1;1 0 1 1 0 0 1;1 0 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 0 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 0 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 0 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 0;1 0 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 0 1;0 0 0 0 1 1 1;1 1 0 0 0 1 0;1 0 1 0 0 0 1;1 0 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 0 0;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 0 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 0 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 0 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 0;1 0 1 0 0 0 0;0 1 1 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 1 0;1 0 1 0 0 0 1;0 1 1 1 0 0 0;1 1 0 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 0;1 0 1 0 0 0 1;0 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 0;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 1 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 0 1 1 0 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 1 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 0;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 0;0 1 1 1 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 0;0 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 0;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 0;1 0 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 0;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 0 0;1 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 0;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 0 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 1 1;1 0 1 0 0 1 1;0 1 1 1 1 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 0 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;0 1 1 1 1 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;0 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 0 1 1;1 0 1 0 0 1 1;0 1 1 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;0 1 1 1 1 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;0 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 0;1 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 0 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 0 0;1 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 1;1 1 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 0;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 0;1 1 0 0 0 1 1;1 1 1 0 0 0 0;1 1 0 1 0 0 1;1 1 0 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 0;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 0 0;1 1 0 1 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 0;0 0 0 0 1 0 0;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 1 1 1 0;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;0 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 0 0;1 1 0 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 0;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 0;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 0;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 0;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 1 1 0;1 1 0 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 0 1;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 0 0;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 0;0 0 0 0 0 0 1;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 0 1;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 0 1;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 0 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 0 1;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 1 0;1 0 1 1 1 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 0 0;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 0 1 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 0 1;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 0 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 0 1 1;1 1 0 0 1 1 1;1 1 0 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 1;1 1 1 0 0 0 0;0 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 0;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 0;1 1 1 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 0 1 1 1 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 0;1 1 1 0 0 0 1;0 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 0;0 0 0 0 1 1 0;1 1 0 0 1 0 1;1 0 1 1 0 0 1;1 1 1 0 0 0 1;1 0 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 1 0 1;1 0 1 1 0 0 1;1 1 1 0 0 0 1;0 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 1 0 1;1 0 1 1 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 0;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 1;1 1 1 0 0 0 1;0 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 0 1;1 0 1 1 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 0 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 0 1 1 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 0;1 0 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 0 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 0 1 1 0 1 1;0 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 0;1 1 0 0 1 1 1;1 0 1 1 0 1 1;0 1 1 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 0 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 0 1 1 0 1 1;0 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 0 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 0 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 0;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 0;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 0;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 0 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 0 0 0 0;1 1 1 1 0 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 0;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 0 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 1 0;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 1 1;1 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 0 1 1 1 1;0 0 0 1 1 1 1;0 0 0 1 1 1 1;1 1 1 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 0 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 0;1 0 1 0 0 0 0;0 1 0 1 0 0 0;1 1 1 0 0 0 0;", - "0 0 1 0 1 0 1;0 0 0 1 0 1 0;1 0 0 0 1 0 1;0 1 0 0 0 1 0;1 0 1 0 0 0 1;0 1 0 1 0 0 0;1 0 1 0 1 0 0;", - "0 0 1 0 1 0 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 0;0 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 1 0 1 0 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 0;1 0 1 0 0 0 1;0 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 1 0 1 0 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 0 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 1;0 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 0;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 0;1 0 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 0;1 0 1 0 0 0 0;1 1 1 0 0 0 1;1 1 1 0 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 0;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 0 0 1;1 0 1 1 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 0 0;1 1 1 0 0 0 1;1 1 1 1 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 0;1 0 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 0;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 1;1 1 0 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 0;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 1;1 0 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 0 1;1 1 1 0 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 0;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 0;1 1 0 1 0 0 0;0 1 1 1 0 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 0;1 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 0;1 0 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 1 0 1 1 0;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 0 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 0;1 0 1 0 0 0 0;1 1 0 1 0 0 1;1 1 1 0 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 0 0;0 1 0 0 0 1 1;1 0 1 0 0 0 0;1 1 0 1 0 0 1;1 1 0 1 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 0;1 1 0 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 0 1;1 0 0 0 1 1 1;0 1 0 0 0 0 0;1 0 1 0 0 1 1;1 0 1 0 1 0 0;1 1 1 0 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 0 0;1 0 0 0 1 1 1;0 1 0 0 0 0 0;1 0 1 0 0 1 1;1 0 1 0 1 0 1;1 0 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 0 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 1 1;1 0 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 0 1;1 0 0 0 1 1 1;0 1 0 0 0 0 0;1 0 1 0 0 1 1;1 0 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 0 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 1 1;1 0 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 0 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 0;1 0 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 0;0 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 0 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 0;1 0 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 0;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 1 1;1 1 1 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 0 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 0 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 0 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 1 1 0 0 0 0;1 1 0 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 0;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 0;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 0 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 0;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 0 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 0 0;1 0 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 1;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 0 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 0;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 0 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 0;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 0 1 1 0 0 1;1 1 0 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 0;1 0 0 0 1 1 1;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 0 1 1 0 0 1;1 0 1 0 1 1 0;", - "0 0 1 0 1 1 0;0 0 0 1 1 0 1;1 0 0 0 1 1 0;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 0 1 1 0 0 1;0 1 0 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 0 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 1;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 0 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 0;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 0 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 0 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 0;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 0;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 0;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 0;0 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 0 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 0;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;0 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 1 1;1 0 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 0;0 1 0 0 0 1 1;1 1 1 0 0 1 1;1 0 1 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 0 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 1 1;1 0 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 1 1 0;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 0 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;0 1 0 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;0 1 1 0 0 0 0;1 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;0 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;0 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 0;0 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 0;1 1 0 0 0 1 1;0 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 0 1 1 1 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;0 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;0 1 1 0 0 1 1;1 1 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 0 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;0 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 0 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 0;0 1 1 1 0 0 0;1 1 1 1 0 0 0;", - "0 0 1 1 1 0 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 0;1 1 1 0 0 0 1;0 1 1 1 0 0 0;1 1 1 0 1 0 0;", - "0 0 1 1 1 0 1;0 0 0 1 1 1 1;1 0 0 0 1 1 0;1 1 0 0 0 1 1;1 1 1 0 0 0 1;0 1 1 1 0 0 0;1 1 0 1 1 0 0;", - "0 0 1 1 1 0 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;0 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 0 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;0 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 0;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;0 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 0;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 0;1 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 0 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 0 1;1 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 0 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 1 1 0;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 1 1 1;1 0 0 1 1 1 1;1 0 1 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 0 0 1 1 1;1 0 0 1 1 1 1;1 0 1 0 0 1 1;1 1 1 0 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 1 1 1;1 0 0 1 1 1 1;1 0 1 0 0 1 0;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 0 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 1 1 1;1 0 0 1 1 1 1;1 0 1 0 0 1 1;1 1 1 0 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 1 1 1;1 0 0 1 1 1 1;1 0 1 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 1 1 1;1 0 0 1 1 1 1;1 0 1 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 0 1 1;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 0 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 0 0 0 1 1;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 0 1 1 0 0 0;1 1 1 1 0 0 1;1 1 1 1 0 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 0 1 1;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 0 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 0 0 1;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 0 1 1 0 1 1;1 0 1 1 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 0 0 0 0 0;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 0 1 1 0 1 1;1 0 1 1 1 0 1;1 0 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 0 0 1;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 0 1 1 0 1 1;1 0 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 0 1 1;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 0 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 0 1 1 1;1 0 0 1 1 1 1;1 0 1 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 0 1 1 1 1;1 0 0 1 1 1 1;1 1 1 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 0 1 1 1;1 1 0 0 0 0 0;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 0 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 0;1 1 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 0 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 0 1 1 1;1 1 0 0 0 0 1;1 1 1 0 0 1 1;1 1 1 0 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 0 1;1 1 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 1 1;1 0 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 0 1 1 1;1 1 0 0 0 1 1;1 1 1 0 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 0;1 1 1 1 1 0 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 0 1;1 1 1 1 0 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 0 1 1 1;1 1 0 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 0 1 1 1 1 1;0 0 1 1 1 1 1;1 1 0 1 1 1 1;1 1 1 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;", - "0 1 1 1 1 1 1;1 0 1 1 1 1 1;1 1 0 1 1 1 1;1 1 1 0 1 1 1;1 1 1 1 0 1 1;1 1 1 1 1 0 1;1 1 1 1 1 1 0;"; diff --git a/OpenProblemLibrary/macros/NAU/PGnauGraphics.pl b/OpenProblemLibrary/macros/NAU/PGnauGraphics.pl deleted file mode 100755 index 08bdca45a0..0000000000 --- a/OpenProblemLibrary/macros/NAU/PGnauGraphics.pl +++ /dev/null @@ -1,324 +0,0 @@ - -sub _PGnauGraphics_init {}; #don't reload this file -# loadMacros('PGunion.pl'); -loadMacros('PGgraphmacros.pl','unionTables.pl','PGchoicemacros.pl'); - -###################################### -#Name: Plot -#Input: Graphical object -#Optional Input: tex_size => n: control size of tex output. n = 10*percentage of page (i.e. 200 = 20% of page) -# note that in single column, the images are twice as big, so 200 = 40% of page -# single => 0,1: turn on or off single column display which makes pictures fit (default = 0) -#Output: An image with appropriate width and height of the graphical object -###################################### -sub Plot{ - my ($pic, @options) = @_; - my ($tex, $val, $plot, @size, $width, $height); - - while (scalar @options > 0){ - $val = shift @options; - if ($val eq 'single'){ - if (shift @options == 1){$tex = 200}; - } elsif ($val eq 'tex_size'){ - $tex = shift @options; - } else { - shift @options; - } - } - - $tex = 400 unless defined ($tex); - ($width, $height) = @{$pic->size()}; - - $plot = image(insertGraph($pic), width=>$width, height=>$height, tex_size=>$tex, extra_html_tags=>'border=0'); -}; - - -###################################### -#Name: checkbox_table -#Input: [values list], [answer list i.e. 1,0,1,0] -#Optional Input: border => n - width of border in table (default = 0) -# tex_size => n - control size of tex output for pictures (defualt = 10 * 95 / # of columns) -# geometry =>[r,c] - the number of rows and columns desired in the table (default = 2,2) -# labels => [@list] - a list of labels that appear beside the checkboxes. (default is blank) -# [..] - other answer lists, included as many as desired. Note the lists must use the same -# values as the others and answer lists must be in the same basic order. -#Output: A table to be displayed and the answer evaluator. -# The table should be placed between BEGIN_TEXT and END_TEXT. -# The evaluator should be placed after END_TEXT by itself. -###################################### -sub checkbox_table{ - my ($val_ref, $ans_ref, @options) = @_; - - my ($i, $j, $k, @ans, $lab, $cols, $name, @vals, $rows, @rule, $size, $true, $val1, $val2, $val3, $count, - @labels, @names, @order, @other, $quest, $table, $total, $answer, $length, $letter, $tex_size, @ansrow, - @valrow, @answer, %row_opts); - - @vals = @{$val_ref}; - @ans0 = @{$ans_ref}; - $count = 1; - - while (@options){ - $val1 = shift @options; - if ($val1 eq 'border'){ - $border = shift @options; - } elsif ($val1 eq 'tex_size'){ - $single = shift @options; - } elsif ($val1 eq 'geometry'){ - $val1 = shift @options; - ($rows, $cols) = @{$val1}; - } elsif ($val1 eq 'labels'){ - $val1 = shift @options; - @labels = @{$val1}; - } else { - $val2 = "ans$count"; - @$val2 = @{$val1}; - $count++; - } - } - - $border = 0 unless defined ($border); - $rows = 2 unless defined ($rows); - $cols = 2 unless defined ($cols); - $tex_size = int(95 / $cols) * 10 unless defined ($tex_size); - - $size = scalar @vals; - $val1 = int (($size - 1)/$cols) + 1; - if ($val1 != $rows){ return ("Incorrect geometry or input.", 0);} - for ($i = 0; $i < $count; $i++){ - $val = "ans$i"; - if (scalar @$val != $size){ return ("Values and answers do not match.",0);} - } - - $length = scalar @labels; - $total = $count * $size; - if ($total == $size){ - for ($i = $length; $i < $total; $i++){ - push @labels, ''; - } - } else { - while ($length < $total){ - $loc = int($length / $size) + 1; - push @labels, "$loc"; - $length++; - } - } - - @order = NchooseK($size, $size); - @vals = @vals[@order]; - for ($i = 0; $i < $count; $i++){ - $val1 = "ans$i"; - $val2 = "labels$i"; - $j = $i * $size; - $k = $j + $size - 1; - @$val1 = @$val1[@order]; - @$val2 = @labels[$j..$k]; - @$val2 = @$val2[@order]; - } - - for ($i = 0; $i < $size; $i++){ - if ($vals[$i] =~ /WWPlot=HASH/){ - $vals[$i] = Plot($vals[$i], tex_size=>$tex_size); - } - } - - for ($i = 0; $i < $count; $i++){ - $true = ''; - $val1 = "ans$i"; - $val2 = "labels$i"; - $val3 = "rule$i"; - $name = "CheckBoxName$i"; - for ($j = 0; $j < $size; $j++){ - $letter = $ALPHABET[$j]; - if ($$val2[$j] eq ''){ - $lab = $letter; - } else { - $lab = "$letter: ". "$$val2[$j]"; - } - $$val3[$j] = ($j ? NAMED_ANS_CHECKBOX_OPTION($name, $letter, $lab): - NAMED_ANS_CHECKBOX($name, $letter, $lab)); - # TeX-mode uses \item, but we're not in a list, so remove it - # and put a fake box in front - $$val3[$j] =~ s/\\item/\$\\Box\$ / if $displayMode eq 'TeX'; - $true .= $letter if $$val1[$j] == 1; - } - push @answer, LABELED_ANS($name =>checkbox_cmp($true)); - } - - $table = BeginTable(spacing=>5, border=>$border, center=>0); - %row_opts = (separation=>0); - for ($i = 0; $i < $rows; $i++){ - $j = 0; - @valrow = (); - while (@vals && $j < $cols){ - push @valrow, shift @vals; - $j++; - } - $table .= AlignedRow([@valrow], %row_opts); - for ($j = 0; $j < $count + 1; $j++){ - @ansrow = (); - $val1 = "rule$j"; - $k = 0; - while (@$val1 && $k < $cols){ - push @ansrow, shift @$val1; - $k++; - } - $table .= AlignedRow([@ansrow], %row_opts); - } - if ($rows > 1 && $i > 0){ - $table .= TableSpace(16,8); - } - } - $table .= EndTable(); - unshift @answer, ($table); - - return @answer; -}; - - -###################################### -#Name: radio_table -#Input: [values list], [answer list i.e. 1,0,1,0] -#Optional Input: border => n - width of border in table (default = 0) -# tex_size => n - control size of tex output for pictures (defualt = 10 * 95 / # of columns) -# geometry =>[r,c] - the number of rows and columns desired in the table (default = 2,2) -# labels => [@list] - a list of labels that appear beside the radio buttons. (default is blank) -# [..] - other answer lists, included as many as desired. Note the lists must use the same -# values as the others and answer lists must be in the same basic order. -#Output: A table to be displayed and the answer evaluator. -# The table should be placed between BEGIN_TEXT and END_TEXT. -# The evaluator should be placed after END_TEXT by itself. -###################################### -sub radio_table{ - my ($val_ref, $ans_ref, @options) = @_; - - my ($i, $j, $k, @ans, $lab, $cols, $name, @vals, $rows, @rule, $size, $true, $val1, $val2, $val3, $count, - @labels, @names, @order, @other, $quest, $table, $total, $answer, $length, $letter, $tex_size, @ansrow, - @valrow, @answer, %row_opts); - - @vals = @{$val_ref}; - @ans0 = @{$ans_ref}; - $count = 1; - - while (@options){ - $val1 = shift @options; - if ($val1 eq 'border'){ - $border = shift @options; - } elsif ($val1 eq 'tex_size'){ - $tex_size = shift @options; - } elsif ($val1 eq 'geometry'){ - $val1 = shift @options; - ($rows, $cols) = @{$val1}; - } elsif ($val1 eq 'labels'){ - $val1 = shift @options; - @labels = @{$val1}; - } else { - $val2 = "ans$count"; - @$val2 = @{$val1}; - $count++; - } - } - - $border = 0 unless defined ($border); - $rows = 2 unless defined ($rows); - $cols = 2 unless defined ($cols); - $tex_size = int(95 / $cols) * 10 unless defined ($tex_size); - - $size = scalar @vals; - $val1 = int (($size - 1)/$cols) + 1; - if ($val1 != $rows){ return ("Incorrect geometry or input.", 0);} - for ($i = 0; $i < $count; $i++){ - $val1 = "ans$i"; - if (scalar @$val1 != $size){ return ("Values and answers do not match.",0);} - $total = 0; - for ($j = 0; $j < $size; $j++){ - $total += $$val1[$j]; - } - if ($total != 1){ return ("Wrong number of correct answers for radio button.",0);} - } - - $length = scalar @labels; - $total = $count * $size; - if ($total == $size){ - for ($i = $length; $i < $total; $i++){ - push @labels, ''; - } - } else { - while ($length < $total){ - $loc = int($length / $size) + 1; - push @labels, "$loc"; - $length++; - } - } - - @order = NchooseK($size, $size); - @vals = @vals[@order]; - for ($i = 0; $i < $count; $i++){ - $val1 = "ans$i"; - $val2 = "labels$i"; - $j = $i * $size; - $k = $j + $size - 1; - @$val1 = @$val1[@order]; - @$val2 = @labels[$j..$k]; - @$val2 = @$val2[@order]; - } - - for ($i = 0; $i < $size; $i++){ - if ($vals[$i] =~ /WWPlot=HASH/){ - $vals[$i] = Plot($vals[$i], tex_size=>$tex_size); - } - } - - for ($i = 0; $i < $count; $i++){ - $true = ''; - $val1 = "ans$i"; - $val2 = "labels$i"; - $val3 = "rule$i"; - $name = "CheckBoxName$i"; - for ($j = 0; $j < $size; $j++){ - $letter = $ALPHABET[$j]; - if ($$val2[$j] eq ''){ - $lab = $letter; - } else { - $lab = "$letter: ". "$$val2[$j]"; - } - $$val3[$j] = ($j ? NAMED_ANS_RADIO_EXTENSION($name, $letter, $lab): - NAMED_ANS_RADIO($name, $letter, $lab)); - # TeX-mode uses \item, but we're not in a list, so remove it - # and put a fake box in front - $$val3[$j] =~ s/\\item/\$\\Box\$ / if $displayMode eq 'TeX'; - $true .= $letter if $$val1[$j] == 1; - } - push @answer, LABELED_ANS($name =>checkbox_cmp($true)); - } - - $table = BeginTable(spacing=>5, border=>$border, center=>0); - %row_opts = (separation=>0); - for ($i = 0; $i < $rows; $i++){ - $j = 0; - @valrow = (); - while (@vals && $j < $cols){ - push @valrow, shift @vals; - $j++; - } - $table .= AlignedRow([@valrow], %row_opts); - for ($j = 0; $j < $count + 1; $j++){ - @ansrow = (); - $val1 = "rule$j"; - $k = 0; - while (@$val1 && $k < $cols){ - push @ansrow, shift @$val1; - $k++; - } - $table .= AlignedRow([@ansrow], %row_opts); - } - if ($rows > 1 && $i > 0){ - $table .= TableSpace(16,8); - } - } - $table .= EndTable(); - unshift @answer, ($table); - - return @answer; -}; - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/NAU/PGnauGraphtheory.pl b/OpenProblemLibrary/macros/NAU/PGnauGraphtheory.pl deleted file mode 100755 index e5b5d12c9b..0000000000 --- a/OpenProblemLibrary/macros/NAU/PGnauGraphtheory.pl +++ /dev/null @@ -1,1748 +0,0 @@ -# PGgraphtheory.pl - -loadMacros("PGnauGraphics.pl", - ); - - -##################################################### Nandor - -###################################### -#Name: GRmatrix_graph -#Input: -#Output: -###################################### -sub GRmatrix_graph { -my $graph = pop @_; -$graph =~ s/\A\s*//; -$graph =~ s/;\s*\Z//; -my @m=split /\s*[;]\s*/ , $graph; -my $size=scalar @m; -my @matrix=(); -my ($i, $j, @r); -for ($i=0; $i<$size ; $i++) { - @r=split /\s+/, $m[$i]; - for ($j=0; $j<$size;$j++) { - $matrix[$i][$j]=$r[$j]; - } -} -@matrix; -} - - -###################################### -#Name: GRgraph_matrix -#Input: -#Output: -###################################### -sub GRgraph_matrix { -my @matrix = @_; -my $size = scalar @matrix; -my $graph=''; -my ($i,$j); -for ($i=0;$i<$size;$i++) { - for ($j=0;$j<$size;$j++) { - $graph .= " $matrix[$i][$j]"; - } - $graph .= ";"; -} -$graph; -} - - -###################################### -#Name: GRshuffledgraph_graph -#Input: -#Output: -###################################### -sub GRshuffledgraph_graph { -my $graph = pop @_; -my @matrix = &GRmatrix_graph($graph); -my ($i,$j,$size,@shmatrix); -$size=scalar @matrix; -@perm = shuffle($size); -# @invperm inverted_shuffle(@perm); -for ($i=0;$i<$size;$i++) { - for ($j=0;$j<$size;$j++) { - $shmatrix[$perm[$i]][$perm[$j] ] = $matrix[$i][$j]; - } -} -$graph = &GRgraph_matrix(@shmatrix); -} - - -###################################### -#Name: GRgraph_size_random -#Input: -#Output: -###################################### -sub GRgraph_size_random { -my ($size,$prob)=@_; -my ($i,$j,$ra,@matrix); -for ($i=0;$i<$size;$i++) { - $matrix[$i][$i]=0; - for ($j=0;$j<$i;$j++) { - $ra=random(0,1,.05); - if ($ra <= $prob) { - $matrix[$i][$j]=1; - $matrix[$j][$i]=1; - } - else { - $matrix[$i][$j]=0; - $matrix[$j][$i]=0; - } - } -} -&GRgraph_matrix(@matrix); -} - - -###################################### -#Name: GRgraph_size_random_weight_dweight -#Input: -#Output: -###################################### -sub GRgraph_size_random_weight_dweight { -my ($size,$prob,$weight,$dweight)=@_; -my ($i,$j,$ra,@matrix); -for ($i=0;$i<$size;$i++) { - $matrix[$i][$i]=0; - for ($j=0;$j<$i;$j++) { - $ra=random(0,1,.05); - if ($ra <= $prob) { - $matrix[$i][$j]=$weight; - $matrix[$j][$i]=$weight; - $weight += $dweight; - } - else { - $matrix[$i][$j]=0; - $matrix[$j][$i]=0; - } - } -} -&GRgraph_matrix(@matrix); -} - - -###################################### -#Name: GRlabel_vertex_labels -#Input: -#Output: -###################################### -sub GRlabel_vertex_labels { -my ($vertex,$labels) = @_; -substr $labels,$vertex,1; -} - - -###################################### -#Name: GRlabels_vertices_labels -#Input: -#Output: -###################################### -sub GRlabels_vertices_labels { -my $labels = pop @_; -my @vertices = @_; -my $result = ''; -my $a; -foreach $a (@vertices) { - $result .= GRlabel_vertex_labels($a,$labels).' '; -} -$result; -} - - -###################################### -#Name: GRvertex_label_labels -#Input: -#Output: -###################################### -sub GRvertex_label_labels { -my ($label,$labels) = @_; -index $labels, $label; -} - - -###################################### -#Name: GRvertices_labels_size -#Input: -#Output: -###################################### -sub GRvertices_labels_size { -my ($labels,$size) = @_; -my ($i, $result); -$result=""; -for ($i=0;$i<$size ;$i++) { - $result .= (substr $labels,$i,1) . ", "; -} -if (length $result >2) { - chop $result; - chop $result; -} -"{".$result."}"; -} - - -###################################### -#Name: GRedges_graph -#Input: -#Output: -###################################### -sub GRedges_graph { -my ($graph) = @_; -my @matrix=&GRmatrix_graph($graph); - -} - - -###################################### -#Name: GRedgesstr_graph_labels -#Input: -#Output: -###################################### -sub GRedgesstr_graph_labels { -my $labels = pop @_; -my $graph = pop @_; -my @matrix=&GRmatrix_graph($graph); -my ($i,$j,$size,$result,$li,$lj); -$result=""; -$size=scalar @matrix; -for($i=0;$i<$size;$i++) { - for($j=$i+1;$j<$size;$j++) { - if ($matrix[$i][$j]==1) { - $li=substr $labels, $i,1; - $lj=substr $labels, $j,1; - $result .= "{$li,$lj}, "; - } - } -} -if (length $result >2) { - chop $result; - chop $result; -} -"{".$result."}"; -} - - -###################################### -#Name: GRdegree_graph_vertex -#Input: -#Output: -###################################### -sub GRdegree_graph_vertex { -my $vert = pop @_; -my $graph = pop @_; -my @matrix=GRmatrix_graph($graph); -my ($i,$j,$size,$result); -$result=0; -$size=scalar @matrix; - for($j=0;$j<$size;$j++) { - if ($matrix[$vert][$j]==1) { - $result++; - } - } -$result; -} - - -###################################### -#Name: GRdegrees_graph -#Input: -#Output: -###################################### -sub GRdegrees_graph { -my $graph = pop @_; -my @matrix=&GRmatrix_graph($graph); -my ($i,$j,$size,@result); -$size=scalar @matrix; - for($i=0;$i<$size;$i++) { - push @result, &GRdegree_graph_vertex($graph,$i); - } -@result; -} - - -###################################### -#Name: GRncomponents_graph -#Input: -#Output: -###################################### -sub GRncomponents_graph { -my $graph = pop @_; -my @matrix = &GRmatrix_graph($graph); -my $size = scalar @matrix; -my ($i,$j,$k,$result,$con); -$result=$size; -for ($i=0;$i<$size;$i++) { - $con=0; - for ($j=$i+1;$j<$size;$j++) { - if ($matrix[$i][$j] != 0) { - $con++; - for ($k=0;$k<$size;$k++) { - $matrix[$j][$k] += $matrix[$i][$k]; - $matrix[$k][$j] += $matrix[$k][$i]; - } - } - } - if ($con>0) { - $result--; - } -} -$result; -} - - -###################################### -#Name: GRpic_graph_labels -#Input: -#Output: -###################################### -sub GRpic_graph_labels { -my $labels = pop @_; -my $graph = pop @_; -my @matrix=&GRmatrix_graph($graph); -my ($i,$j,$k,$lab); -my $pic = init_graph(-1.5,-1.5,1.5,1.5,'pixels'=>[220,220]); -my $size=scalar @matrix; -my $gap=2.0*3.14/$size; -for ($i=0;$i<$size;$i++) { - $pic->stamps(closed_circle(cos($i*$gap),sin($i*$gap),"blue")); - $lab=new Label(1.25*cos($i*$gap),1.25*sin($i*$gap), - &GRlabel_vertex_labels($i,$labels),'blue','center','center'); - $pic->lb($lab); -} -for ($i=0;$i<$size;$i++) { - for ($j=$i+1;$j<$size;$j++) { - if ($matrix[$i][$j]!=0) { - $pic->moveTo(cos($i*$gap),sin($i*$gap)); - $pic->lineTo(cos($j*$gap),sin($j*$gap),1); - } - } -} -$pic; -} - - -###################################### -#Name: GRlabelpic_graph(_labels) -#Input: -#Output: -###################################### -sub GRlabelpic_graph { -my $labels = pop @_; -my $graph = pop @_; -my @matrix=&GRmatrix_graph($graph); -my ($i,$j,$k,$lab); -my $pic = init_graph(-1.5,-1.5,1.5,1.5,'pixels'=>[300,300]); -my $size=scalar @matrix; -my $gap=2.0*3.14/$size; -for ($i=0;$i<$size;$i++) { - $pic->stamps(closed_circle(cos($i*$gap),sin($i*$gap),"blue")); - $lab=new Label(1.2*cos($i*$gap),1.2*sin($i*$gap), - &GRlabel_vertex_labels($i,$labels),'blue','center','center'); - $pic->lb($lab); -} -for ($i=0;$i<$size;$i++) { - for ($j=$i+1;$j<$size;$j++) { - if ($matrix[$i][$j]!=0) { - $pic->moveTo(cos($i*$gap),sin($i*$gap)); - $pic->lineTo(cos($j*$gap),sin($j*$gap),9); - $lab=new Label(.25*cos($i*$gap)+.75*cos($j*$gap),.25*sin($i*$gap)+.75*sin($j*$gap), - $matrix[$i][$j],'red','center','center'); - $pic->lb($lab); - } - } -} -$pic; -} - - -###################################### -#Name: GRnearestnbr_graph_vertex -#Input: -#Output: -###################################### -sub GRnearestnbr_graph_vertex { -my $origvert = pop @_; -my $graph = pop @_; -my @matrix = &GRmatrix_graph($graph); -my ($i,$j,$size,@path,$weight,@used,$min,$vert,$newvert); -$used[$origvert]=1; -@path=($origvert); -$weight=0; -$size=scalar @matrix; -$vert=$origvert; -while ($size>scalar @path) { - $min=0; - for ($i=0;$i<$size;$i++) { - my $a = $matrix[$vert][$i]; - if ($i == $vert || defined $used[$i] || $a==0) { - next; - } - if ($min == 0 || $a < $min) { - $min = $a; - $newvert = $i; - } - } - push @path, $newvert; - $used[$newvert]=1; - $weight += $matrix[$vert][$newvert]; - $vert = $newvert; -} -push @path, $origvert; -$weight += $matrix[$vert][$origvert]; -push @path, $weight; -@path; -} - - -###################################### -#Name: GRkruskal_graph -#Input: -#Output: -###################################### -sub GRkruskal_graph { -my $graph2 = pop @_; -my $graph=$graph2; -my @matrix = &GRmatrix_graph($graph); -my $size = scalar @matrix; -my $ncomp = &GRncomponents_graph($graph); -my $tree = &GRgraph_size_random($size,-1); -my $ntreecomp = &GRncomponents_graph($tree); -my @treematrix = &GRmatrix_graph($tree); -my ($i,$j,@path,$weight,$treeweight); -$weight=0; -my @weights=(); -my @treeweights=(); -for ($i=0;$i<$size;$i++) { - for ($j=$i+1;$j<$size;$j++) { - if ($matrix[$i][$j] != 0) { - push @weights, $matrix[$i][$j]; - } - } -} -@weights = num_sort(@weights); -while (0 < scalar @weights) { - $weight = shift @weights; - for ($i=0;$i<$size;$i++) { - for ($j=$i+1;$j<$size;$j++) { - if ($matrix[$i][$j] == $weight) { - $matrix[$i][$j] = 0; - $matrix[$j][$i] = 0; - $treematrix[$i][$j] = $weight; - $treematrix[$j][$i] = $weight; - $tree = &GRgraph_matrix(@treematrix); - my $nntreecomp = &GRncomponents_graph($tree); - if ($nntreecomp < $ntreecomp) { - $ntreecomp=$nntreecomp; - $treeweight += $weight; - push @treeweights, $weight; - } - else { - $treematrix[$i][$j] = 0; - $treematrix[$j][$i] = 0; - $tree = &GRgraph_matrix(@treematrix); - } - last; - } - } - } -} - -unshift @treeweights, ($tree,$treeweight); -@treeweights; -} - - -###################################### -#Name: GRtex_braces -#Input: -#Output: -###################################### -sub GRtex_braces { - my $a = shift; - $a =~ s/{/ $LB /g; - $a =~ s/}/ $RB /g; - $a; -} - - -###################################### -#Name: GRvertexlist_edgesstr_labels -#Input: -#Output: -###################################### -sub GRvertexlist_edgesstr_labels { - my ($edgesstr,$labels) = @_; - $edgesstr = uc $edgesstr; - $edgesstr =~ s /[^$labels]//g; - split '', $edgesstr; -} - - -###################################### -#Name: GRgraph_size_labels_edgesstr -#Input: -#Output: -###################################### -sub GRgraph_size_labels_edgesstr { -my ($size, $labels, $edgesstr) = @_; -my ($i,$j,$graphm); -# create a graph with no edges -for ($i=0;$i<$size;$i++) { - for ($j=0;$j<$size;$j++) { - $graphm[$i][$j] = 0; - } -} -#clean up the edges string -my @pairs = GRvertexlist_edgesstr_labels($edgesstr,$labels); -my $nedges = (scalar @pairs)/2; -# erase a vertex if not even -if ( $nedges != int($nedges) ) { - pop @pairs; - $nedges = (scalar @pairs)/2; -} -# add th edges to the empty graph -while (scalar @pairs) { - $i=shift @pairs; - $j=shift @pairs; - $i=GRvertex_label_labels($i,$labels); - $j=GRvertex_label_labels($j,$labels); - $graphm[$i][$j]=1; - $graphm[$j][$i]=1; -} -$graphin= GRgraph_matrix(@graphm); -} - -####################################################################### Edgar Fisher - - -###################################### -#Name: VertDegree -#Input: Vertex number followed by adjacency matrix for a graph -#Output: Degree of the indicated vertex -###################################### -sub VertDegree{ - my ($vert, @matrix) = @_; - my ($i, $size, $degree); - - $size = scalar @matrix; - $degree = 0; - - for ($i = 0; $i < $size; $i++){ - if ($matrix[$vert][$i] != 0){ - $degree++; - } - } - - $degree; -}; - - -###################################### -#Name: GRgraph_degrees -#Input: List of degree values -#Output: A graph with the indicated degree values if possible or 'DNE' if not. -###################################### -sub GRgraph_degrees{ - my (@input) = @_; - my ($i, $j, @adj, $ind, $val, $size, $graph, @degrees); - - @degrees = reverse num_sort(@input); - - $size = scalar @degrees; - - if ($degrees[0] >= $size){ - $graph = 'DNE'; - } else { - @adj = GRmatrix_graph(GRgraph_size_random($size, -1)); - $val = 0; - $loc = 0; - - while ($loc < $size && $val == 0){ - $val = $degrees[$loc] - VertDegree($loc, @adj); - $ind = $loc + 1; - while ($val > 0 && $ind < $size){ - if (VertDegree($ind, @adj) < $degrees[$ind]){ - $adj[$loc][$ind] = 1; - $adj[$ind][$loc] = 1; - $ind++; - $val--; - } else { - $ind++; - } - } - $loc++; - } - - if ($val != 0){ - $graph = 'DNE'; - } else { - $graph = GRgraph_matrix(@adj); - } - } - $graph; -}; - - -###################################### -#Name: ChangeWeight -#Input: Vertex numbers to change, weight to change the edge to and graph -#Output: A graph with the edge between the indicated vertices changed to weight -###################################### -sub ChangeWeight{ - my ($i, $j, $weight, $graph) = @_; - my (@matrix); - - @matrix = GRmatrix_graph($graph); - $matrix[$i][$j] = $weight; - $matrix[$j][$i] = $weight; - - $graph = GRgraph_matrix(@matrix); -}; - - -###################################### -#Name: GRhaseulercircuit_graph -#Input: Graph -#Output: A list containing a value and message. The value is 0 if it is not -#an Euler circuit and 1 otherwise. The message relays why it is not. -###################################### -sub GRhaseulercircuit_graph{ - my ($graph) = @_; - my ($val, $comp, @answer, @degrees, $message, $isCircuit); - - $isCircuit = 1; - $message = "Good"; - $comp = GRncomponents_graph($graph); - - if ($comp != 1){ - $isCircuit = 0; - $message = "Not connected"; - } - - @degrees = GRdegrees_graph($graph); - - foreach $val (@degrees){ - if ($val%2 != 0){ - $isCircuit = 0; - $message = "Incorrect degrees"; - } - } - - @answer = ($isCircuit, $message); -}; - - -###################################### -#Name: GRhaseulertrail_graph -#Input: Graph -#Output: A list containing a value and message. The value is 0 if it is not -#an Euler path and 1 otherwise. The message relays why it is not. -###################################### -sub GRhaseulertrail_graph{ - my ($graph) = @_; - my ($val, $odd, $comp, $even, $total, @answer, @degrees, $message, $isCircuit); - - $isCircuit = 1; - $message = "Good"; - $comp = GRncomponents_graph($graph); - - if ($comp != 1){ - $isCircuit = 0; - $message = "Not connected"; - } - - @degrees = GRdegrees_graph($graph); - - $odd = 0; - $even = 0; - $total = scalar @degrees; - - foreach $val (@degrees){ - if ($val % 2 == 0){ - $even++; - } else { - $odd++; - } - } - - if ($even != $total && $odd != 2){ - $isCircuit = 0; - $message = "Incorrect degrees"; - } - - @answer = ($isCircuit, $message); -}; - - -###################################### -#Name: GReulercircuit_size -#Input: Size of graph -#Output: A graph that is an Euler Circuit -###################################### - -sub GReulercircuit_size{ - my ($size) = @_; - - my ($i, @deg, $pic, $comp, $diff, $graph, @lowdeg); - - if ($size <= 4){ - die "Incorrect Size"; - } - - do { - @deg = (); - for ($i = 0; $i < $size; $i++){ - push @deg, random(2,$size - 1,2); - } - $graph = GRgraph_degrees(@deg); - $comp = GRncomponents_graph($graph); - } while ($graph eq 'DNE' || $comp > 1); - - $graph = GRshuffledgraph_graph($graph); - -}; - -###################################### -#Name: GReulertrail_size -#Input: Size of graph -#Output: A graph that is an Euler Path but not an Euler Circuit -###################################### - -sub GReulertrail_size{ - my ($size) = @_; - - my ($i, @deg, $pic, $comp, $diff, $temp, $graph, @index, @lowdeg); - -=pod - for ($i = 0; $i < $size; $i++){ - push @lowdeg, 2; - } - - $comp = 2; - while ($graph eq 'DNE' || $comp > 1){ - @deg = (); - for ($i = 0; $i < $size; $i++){ - push @deg, random($lowdeg[$i],$size - 1,2); - } - $graph = GRgraph_degrees(@deg); - $comp = GRncomponents_graph($graph); - - do { - $diff = random (0, $size - 1, 1); - } until ($lowdeg[$diff] < $size - 2); - $lowdeg[$diff] += 2; - } -=cut - - if ($size <= 4) { - die "Incorrect size"; - } - do { - @deg = (); - for ($i = 0; $i < $size; $i++){ - push @deg, random(2,$size - 1,2); - } - $graph = GRgraph_degrees(@deg); - $comp = GRncomponents_graph($graph); - } while ($graph eq 'DNE' || $comp > 1); - - do { - @index = NchooseK($size,2); - $temp = ChangeWeight(@index, 0, $graph); - } while ($temp eq $graph); - - $graph = GRshuffledgraph_graph($temp); -}; - - - -###################################### -#Name: GRnoneuler_size -#Input: Size of graph -#Output: A graph that is not an Euler path -###################################### - -sub GRnoneuler_size{ - my ($size) = @_; - - my ($i, @ans, @deg, $pic, $diff, $graph, @lowdeg); - - for ($i = 0; $i < $size; $i++){ - push @lowdeg, 2; - } - - @ans = (1,''); - -=pod - while ($graph eq 'DNE' || $ans == 1){ - @deg = (); - for ($i = 0; $i < $size; $i++){ - push @deg, random($lowdeg[$i],$size - 1,1); - } - $graph = GRgraph_degrees(@deg); - if ($graph ne 'DNE'){ - @ans = GRiseulertrail_graph($graph); - } - - do { - $diff = random (0, $size - 1, 1); - } until ($lowdeg[$diff] < $size - 1); - $lowdeg[$diff]++; - } -=cut - - do { - @deg = (); - @ans = (1,''); - for ($i = 0; $i < $size; $i++){ - push @deg, random(2,$size - 1,1); - } - $graph = GRgraph_degrees(@deg); - if ($graph ne 'DNE'){ - @ans = GRhaseulertrail_graph($graph); - } - } while ($graph eq 'DNE' || $ans[0] == 1); - - $graph = GRshuffledgraph_graph($graph); -}; - - -###################################### -#Name: Matr_graph_Mult -#Input: -#Output: -###################################### -sub Matr_graph_Mult { - my($graph1, $graph2) = @_; - - my ($i, $j, $k, @M1, @M2, $sum, $Gnew, @Mnew, $size); - - @M1 = GRmatrix_graph($graph1); - @M2 = GRmatrix_graph($graph2); - $size = scalar @M1; - - for ($i = 0; $i < $size; $i++){ - for ($j = 0; $j < $size; $j++){ - $sum = 0; - for ($k = 0; $k < $size; $k++){ - $sum += $M1[$i][$k] * $M2[$k][$j]; - } - $Mnew[$i][$j] = $sum; - } - } - - $Gnew = GRgraph_matrix(@Mnew); -} - - -###################################### -#Name: GRhascircuit_graph -#Input: A graph -#Output: 1 if the graph has a circuit, 0 otherwise -###################################### -sub GRhascircuit_graph{ - my ($graph) = @_; - - my ($i, $j, $sum, $size, @matrix, $hasCircuit); - - @matrix = GRmatrix_graph($graph); - $size = scalar @matrix; - - for ($i = 0; $i < $size; $i++){ - $sum = 0; - for ($j = 0; $j < $size; $j++){ - $sum += $matrix[$i][$j]; - } - if ($sum == 1){ - for ($j = 0; $j < $size; $j++){ - $matrix[$i][$j] = 0; - $matrix[$j][$i] = 0; - } - $i = -1; - } - } - - for ($i = 0; $i < $size; $i++){ - for ($j = $i + 1; $j < $size; $j++){ - if ($matrix[$i][$j] != 0){ - $hasCircuit = 1; - last; - } - } - } - - $hasCircuit; -}; - - -###################################### -#Name: GRistree_graph -#Input: A graph -#Output: A list containing a 1 if it is a tree, 0 if not a tree and a -# message about why it was not a tree. -###################################### -sub GRistree_graph{ - my ($graph) = @_; - - my (@ans); - - @ans = (1,'Good'); - if (GRhascircuit_graph($graph)){ - @ans = (0, 'Has a circuit.'); - } elsif (GRncomponents_graph($graph) > 1){ - @ans = (0, 'Not connected.'); - } - @ans; -} - - -###################################### -#Name: GRisforest_graph -#Input: A graph -#Output: A list containing a 1 if it is a forest, 0 if not a forest and a -# message about why it was not a forest. -###################################### -sub GRisforest_graph{ - my ($graph) = @_; - - my (@ans); - - @ans = (1, 'Good'); - - if (GRhascircuit_graph($graph)){ - @ans = (0, 'Has a circuit'); - } - - @ans; -} - - -###################################### -#Name: GoodEdge -#Input: A graph -#Output: A 1 if the edge should be placed in a sorted edges based graph -# 0 if not. -###################################### -sub GoodEdge { - my ($graph) = @_; - - my ($i, @adj, $ans, $sum, $val, $size); - - @adj = GRmatrix_graph($graph); - - $size = scalar @adj; - $ans = 1; - $sum = 0; - - for ($i = 0; $i < $size; $i++){ - $val = VertDegree($i, @adj); - $sum += $val; - if ($val > 2){ - $ans = 0; - } - } - - if ($sum < 2 * $size && GRhascircuit_graph($graph)){ - $ans = 0; - } - - $ans; -}; - - -###################################### -#Name: GRsortededges_graph -#Input: A graph -#Output: A list of the values of the edges included in the graph -# in the order of their inclusion. -###################################### -sub GRsortededges_graph{ - my ($graph) = @_; - my ($i, $j, $val, $size, @path, @matrix, @weights, @newmatrix, $newgraph); - - @matrix = GRmatrix_graph($graph); - $size = scalar @matrix; - - for ($i = 0; $i < $size; $i++){ - $newmatrix[$i][$i] = 0; - for ($j = $i + 1; $j < $size; $j++){ - $newmatrix[$i][$j] = 0; - $newmatrix[$j][$i] = 0; - push @weights, $matrix[$i][$j]; - } - } - - @weights = num_sort(@weights); - - @path = (); - - do { - $val = shift @weights; - for ($i = 0; $i < $size; $i++){ - for ($j = $i + 1; $j < $size; $j++){ - if ($val == $matrix[$i][$j]){ - $newmatrix[$i][$j]++; - $newmatrix[$j][$i]++; - $newgraph = GRgraph_matrix(@newmatrix); - if (GoodEdge($newgraph)){ - push @path, $val; - } else { - $newmatrix[$i][$j]--; - $newmatrix[$j][$i]--; - } - } - } - } - - } while (scalar @path < $size && scalar @weights > 0); - - @path; -}; - - -###################################### -#Name: GRiseulertrail_graph_vertices -#Input: A graph and list of selected vertices -#Output: A list containing a 1 if the indicated list of -# vertices is an euler path in the graph or 0 if -# it is not and a message indicating the reason. -###################################### -sub GRiseulertrail_graph_vertices{ - my ($graph, @verts) = @_; - - my($i, $j, @adj, @ans, $sum, $size); - - @adj = GRmatrix_graph($graph); - $size = scalar @adj; - @ans = (1, 'Good'); - - $i = shift @verts; - do { - $j = shift @verts; - if ($adj[$i][$j] == 0){ - @ans = (0, 'Incorrect edge included'); - } - $adj[$i][$j] = 0; - $adj[$j][$i] = 0; - $i = $j; - } while ($ans[0] == 1 && scalar @verts > 0); - - $sum = 0; - for ($i = 0; $i < $size; $i++){ - for ($j = $i + 1; $j < $size; $j++){ - $sum += $adj[$i][$j]; - } - } - - if ($ans[0] != 0 && $sum != 0){ - @ans = (0, 'Not all edges used'); - } - - @ans; -} - - -###################################### -#Name: GRiseulercircuit_graph_vertices -#Input: A graph and list of selected vertices -#Output: A list containing a 1 if the indicated list of -# vertices is an euler circuit in the graph or 0 if -# it is not and a message indicating the reason. -###################################### -sub GRiseulercircuit_graph_vertices{ - my ($graph, @verts) = @_; - - my($i, $j, @adj, @ans, $sum, $size); - - @adj = GRmatrix_graph($graph); - $size = scalar @verts; - - @ans = GRiseulertrail_graph_vertices($graph, @verts); - - if ($ans[0] != 0 && $verts[0] != $verts[$size - 1]){ - @ans = (0, 'Not an Euler circuit'); - } - - @ans; -} - -###################################### -#Name: GRforest_size -#Input: Size of the desired graph to be a forest -#Output: A graph that is a forest -###################################### -sub GRforest_size{ - my ($size) = @_; - - my ($i, $j, $new, $graph); - - $graph = GRtree_size($size); - - do { - ($i, $j) = NchooseK($size, 2); - $new = ChangeWeight($i, $j, 0, $graph); - } while ($new eq $graph); - - $new; -} - -###################################### -#Name: GRtree_size -#Input: size of desired tree -#Output: A graph that is a tree -###################################### -sub GRtree_size{ - my ($size) = @_; - - my ($i, $j, @adj, @used, @avail, $graph, @shuffle); - - for ($i = 0; $i < $size; $i++){ - for ($j = 0; $j < $size; $j++){ - $adj[$i][$j] = 0; - } - } - $graph = GRgraph_matrix(@adj); - - @avail = (0..$size-1); - @shuffle = NchooseK($size, $size); - @avail = @avail[@shuffle]; - - $j = pop @avail; - push @used, $j; - do { - $j = pop @avail; - $i = random(0, scalar @used - 1,1); - $graph = ChangeWeight($used[$i], $j, 1, $graph); - push @used, $j; - } while (scalar @avail > 0); - - $graph; -}; - - -###################################### -#Name: GRisbipartite_graph -#Input: Graph object -#Output: A 1 is the graph is bipartite, 0 otherwise -###################################### -sub GRisbipartite_graph{ - my ($graph) = @_; - - my ($i, @adj, $ans, $col, $done, $size, $vert, @color, @verts, $colored, $orig_col, @Vertcolor); - @adj = GRmatrix_graph($graph); - $size = scalar @adj; - - for ($i = 0; $i < $size; $i++){ - push @Vertcolor, 0; - } - $ans = 1; - @color = (1,2); - - do { - $i = 0; - while ($Vertcolor[$i] != 0){ - $i++; - } - $Vertcolor[$i] = $color[0]; - push @verts, $i; - - do { - $vert = shift @verts; - $orig_col = $Vertcolor[$vert]; - for ($i = 0; $i < $size; $i++){ - if ($adj[$vert][$i] != 0){ - $col = $Vertcolor[$i]; - if ($orig_col == $col){ - $ans = 0; - last; - } elsif ($col == 0){ - push @verts, $i; - $Vertcolor[$i] = $color[2 - $orig_col]; - } - } - } - } while (scalar @verts > 0 && $ans == 1); - - $done = 1; - for ($i = 0; $i < $size; $i++){ - $done *= $Vertcolor[$i]; - } - } while ($done == 0 && $ans == 1); - - $ans; -} - -###################################### -#Name: GRbipartite_size -#Input: Size of the desired graph -#Output: A graph that has is bipartite -###################################### -sub GRbipartite_size{ - my ($size) = @_; - - my($i, @P1, @P2, $s1, $s2, $max, $rnd1, $rnd2, $count, $graph); - - $graph = GRgraph_size_random($size, -1); - - $s1 = random(2, $size - 2, 1); - $s2 = $size - $s1; - - if ($s1 > $s2){ - $max = $s1; - } else { - $max = $s2; - } - - $count = random($max, 3 * $s1*$s2 / 4, 1); - - do { - $ind1 = random(0, $s1 - 1, 1); - $ind2 = $s1 + random(0, $s2 - 1, 1); - $graph = ChangeWeight($ind1, $ind2, 1, $graph); - @deg = GRdegrees_graph($graph); - $sum = 0; - for ($i = 0; $i < $size; $i++){ - $sum += $deg[$i]; - } - } while ($sum < 2 * $count); - - $graph = GRshuffledgraph_graph($graph); -}; - - -###################################### -#Name: GRdijkstra_graph_vertex_vertex -#Input: A graph, starting vertex and ending vertex for Dijkstra's algorithm -#Output: Two lists, the first, a list of distances from the start vertex -# to every other vertex. The second a list of the vertices preceding -# the current vertex to achieve the shortest path. -###################################### -sub GRdijkstra_graph_vertex_vertex{ - my ($graph, $start, $end) = @_; - - my ($i, @adj, $ind, $loc, $min, $new, @dist, $done, $left, @path, @prev, $size, @used, @avail, $weight); - - @adj = GRmatrix_graph($graph); - $size = scalar @adj; - for ($i = 0; $i < $size; $i++){ - $dist[$i] = -1; - $prev[$i] = -1; - } - $dist[$start] = 0; - - @avail = (0..$size -1 ); - $left = scalar @avail; - - - while ($left > 0){# && $new != $end){ - $min = 1000000000; - for ($i = 0; $i < $left; $i++){ - $loc = $avail[$i]; - if ($dist[$loc] >= 0 && $dist[$loc] < $min){ - $new = $loc; - $ind = $i; - $min = $dist[$new]; - } - } - push @used, $new; - splice @avail, $ind, 1; - @used = num_sort(@used); - - for ($i = 0; $i < $size; $i++){ - $weight = $adj[$new][$i]; - if ($weight != 0){ - if ($dist[$i] > $min + $weight || $dist[$i] < 0){ - $dist[$i] = $min + $weight; - $prev[$i] = $new; - } - } - } - $left = scalar @avail; - }; - - $loc = $end; - while ($loc != $start){ - unshift @path, $loc; - $loc = $prev[$loc]; - } - unshift @path, $start; - - ($dist[$end], @path); -}; - - -###################################### -#Name: GRgraphpic_dim_random_labels_weight_dweight -#Input: Dimensions of the table in (row, column) format, a value to determine -# the probability of an edge existing between vertices, labels for the vertices, -# starting weight and weight difference for edge weights. -#Output: A graph with weights and a picture of the graph in tabular format. -###################################### -sub GRgraphpic_dim_random_labels_weight_dweight { - my ($row, $col, $prob, $labels, $weight, $dweight) = @_; - - my ($i, $j, $u, $v, $x, $y, $up, @adj, $lab, $loc, $pic, $val, $base, $grid, $poss, $rght, - $rtdn, $rtup, $size, $new_x, $new_y, $shift, $down_y, $orig_x, $orig_y, $rght_x); - - @adj = GRmatrix_graph(GRgraph_size_random($row * $col, -1)); - - $base = 10; - $grid = 20; - $shift = $grid / 15; - $width = ($col - 1) * $grid; - $height = ($row - 1) * $grid; - $pic = init_graph(0, 0, ($col - .25) * $grid, ($row - .25) * $grid,'pixels'=>[7 * $width, 7 * $height]); - - for ($i = 0; $i < $row; $i++) { - for ($j = 0; $j < $col; $j++){ - $x = $base + $grid * $j; - $y = $base + $grid * $i; - $pic->stamps(closed_circle($x, $y,"blue")); - $lab = new Label($x - $shift, $y + 2 * $shift, GRlabel_vertex_labels($i + $row * $j,$labels),'blue','center','center'); - $pic->lb($lab); - } - } - - for ($i = 0; $i < $row; $i++) { - for ($j = 0; $j < $col; $j++) { - $loc = $j * $row + $i; - $rght = $loc + $row; - $up = $loc + 1; - $rtup = $loc + $row + 1; - $rtdn = $loc + $row - 1; - - if ($j + 1 < $col && random(0,1,.01) <= $prob){ - $count++; - $adj[$loc][$rght] = 1; - $adj[$rght][$loc] = 1; - } - if ($i + 1 < $row && random(0,1,.01) <= $prob){ - $count++; - $adj[$loc][$up] = 1; - $adj[$up][$loc] = 1; - } - if ($i + 1 < $row && $j + 1 < $col && random(0,1,.01) <= $prob){ - $count++; - $adj[$loc][$rtup] = 1; - $adj[$rtup][$loc] = 1; - } - if ($i - 1 >= 0 && $j + 1 < $col && random(0,1,.01) <= $prob){ - $count++; - $adj[$loc][$rtdn] = 1; - $adj[$rtdn][$loc] = 1; - } - } - } - - for ($i = 0; $i < $count; $i++){ - push @weights, $weight + $i * $dweight; - } - @weights = @weights[NchooseK($count, $count)]; - - $u = .3333; - $v = 1 - $u; - - for ($i = 0; $i < $row * $col; $i++) { - for ($j = $i + 1; $j < $row * $col; $j++) { - if ($adj[$i][$j] != 0){ - if ($dweight == 0){ - $val = random(2, 15, 1); - } else { - $val = shift @weights; - } - $adj[$i][$j] = $val; - $adj[$j][$i] = $val; - $orig_x = $base + int($i / $row) * $grid; - $orig_y = $base + $i % $row * $grid; - $new_x = $base + int($j / $row) * $grid; - $new_y = $base + $j % $row * $grid; - $pic->moveTo($orig_x, $orig_y); - $pic->lineTo($new_x, $new_y, 9); - $lab=new Label($u * $orig_x + $v * $new_x, $u * $orig_y + $v * $new_y + $shift, $val, 'red', 'center', 'center'); - $pic->lb($lab); - } - } - } - - $graph = GRgraph_matrix(@adj); - - $graph, $pic; -}; - -###################################### -#Name: GRhamiltonian_size -#Input: Size of the desired graph (5 or greater) -#Output: A graph that has is hamiltonian -###################################### -sub GRhamiltonian_size{ - my ($size) = @_; - - my($i, $j, @adj, $low, $comp, $high, $count, $edges, $graph); - - @adj = GRmatrix_graph(GRgraph_size_random($size,-1)); - $comp = $size * ($size - 1) / 2; - if ($size <= 5){ - $low = $size + 1; - $high = $comp - 1; - } else { - $low = int ($comp / 3) + 1; - $high = int($comp / 2) + 1; - } - $edges = int random ($low, $high, 1); - - for ($i = 0; $i < $size; $i++){ - $j = ($i + 1) % $size; - $adj[$i][$j] = 1; - $adj[$j][$i] = 1; - } - - $count = $size; - while ($count < $edges){ - ($i, $j) = NchooseK($size,2); - if ($adj[$i][$j] == 0){ - $adj[$i][$j] = 1; - $adj[$j][$i] = 1; - $count++; - } - } - - $graph = GRshuffledgraph_graph(GRgraph_matrix(@adj)); -}; - - -###################################### -#Name: GRnonhamiltonian_size_type -#Input: Size of the desired graph and a type of non hamiltonian graph: -# odd: Has two cycles joined by a single edge -# even: Has a vertex of degree one -# If the odd type, size must be at least 6 -#Output: A graph that has is not hamiltonian of the specified type -###################################### -sub GRnonhamiltonian_size_type{ - my ($size, $type) = @_; - - my($i, $j, $v1, $v2, @adj, $val, $count, $edges, $graph, $ncomp, $split); - - $type = $type % 2; - @adj = GRmatrix_graph(GRgraph_size_random($size,-1)); - - if ($type == 0){ - $ncomp = ($size - 1) * ($size - 2) / 2; - $edges = random ($size + 1, 2 * $ncomp / 3 + 1, 1); - - for ($i = 0; $i < $size - 1; $i++){ - $j = $i + 1; - $adj[$i][$j] = 1; - $adj[$j][$i] = 1; - } - $adj[$size - 2][0] = 1; - $adj[0][$size - 2] = 1; - - $count = $size; - do { - ($i, $j) = NchooseK($size - 1,2); - if ($adj[$i][$j] == 0){ - $adj[$i][$j] = 1; - $adj[$j][$i] = 1; - $count++; - } - } while ($count < $edges); - } else { #type 1 - if ($size < 6){die} - $split = int ($size / 2); - - for ($i = 0; $i < $split; $i++){ - $j = ($i + 1) % $split; - $adj[$i][$j] = 1; - $adj[$j][$i] = 1; - } - for ($i = $split; $i < $size; $i++){ - $j = ($i + 1) % $size; - $adj[$i][$j] = 1; - $adj[$j][$i] = 1; - } - $adj[$size - 1][$split] = 1; - $adj[$split][$size - 1] = 1; - - $edges = ($size + 1) + ($size - 6); - $count = $size + 1; - - $maxtry=4; # Nandor: I don't know if this is needed, just to be sure - while ($count < $edges && $maxtry>0){ # - $maxtry--; # - $v1 = random (0, $split - 1, 1); - $v2 = ($v1 + 2) % $split; - if ($adj[$v1][$v2] == 0){ - $adj[$v1][$v2] = 1; - $adj[$v2][$v1] = 1; - $count++; - } - if ($adj[$v1 + $split][$v2 + $split] == 0){ - $adj[$v1 + $split][$v2 + $split] = 1; - $adj[$v2 + $split][$v1 + $split] = 1; - $count++; - } - } - } - $graph = &GRshuffledgraph_graph(GRgraph_matrix(@adj)); -# $graph = &GRgraph_matrix(@adj); -}; - - -###################################### -#Name: GRcycle_size -#Input: Size for cycle -#Output: A picture of a graph that is a cycle (with no labels). -###################################### -sub GRcycle_size{ - my ($size) = @_; - - my($i, $j, $gap, $pic); - - $pic = init_graph(-1.5,-1.5,1.5,1.5,'pixels'=>[220,220]); - $gap = 2.0 * 3.14 / $size; - - for ($i = 0; $i < $size; $i++) { - $j = ($i + 1) % $size; - $pic->stamps(closed_circle(cos($i * $gap),sin($i * $gap),"blue")); - $pic->moveTo(cos($i * $gap),sin($i * $gap)); - $pic->lineTo(cos($j * $gap),sin($j * $gap), 1); - } - $pic; -}; - - -###################################### -#Name: GRcomplete_size -#Input: Size for complete graph -#Output: A picture of a complete graph (with no labels). -###################################### -sub GRcomplete_size{ - my($size) = @_; - - my($i, $j, $gap, $pic); - $pic = init_graph(-1.5,-1.5,1.5,1.5,'pixels'=>[220,220]); - $gap = 2.0 * 3.14 / $size; - - for ($i = 0; $i < $size; $i++) { - $pic->stamps(closed_circle(cos($i * $gap),sin($i * $gap),"blue")); - for ($j = $i + 1; $j < $size; $j++){ - $pic->moveTo(cos($i * $gap),sin($i * $gap)); - $pic->lineTo(cos($j * $gap),sin($j * $gap), 1); - } - } - $pic; -} - -###################################### -#Name: GRwheel_size -#Input: Size for wheel -#Output: A picture of a graph that is a wheel (with no labels). -###################################### -sub GRwheel_size{ - my ($size) = @_; - - my($i, $j, $gap, $pic); - - $pic = init_graph(-1.5,-1.5,1.5,1.5,'pixels'=>[220,220]); - $gap = 2.0 * 3.14 / $size; - - $pic->stamps(closed_circle(0,0,"blue")); - for ($i = 0; $i < $size; $i++) { - $j = ($i + 1) % $size; - $pic->stamps(closed_circle(cos($i * $gap),sin($i * $gap),"blue")); - $pic->moveTo(cos($i * $gap), sin($i * $gap)); - $pic->lineTo(cos($j * $gap), sin($j * $gap), 1); - $pic->moveTo(0,0); - $pic->lineTo(cos($i * $gap), sin($i * $gap), 1); - } - $pic; -}; - - -###################################### -#Name: GRcompletebipartite_size_size -#Input: Size for upper and size for lower bipartite graph -#Output: A picture of a bipartite graph with size and -# size labels on top and bottem (and no labels). -###################################### -sub GRcompletebipartite_size_size{ - my($m, $n) = @_; - - my($i, $j, $low, $pic, $diff, $high, @shft, $width, $x_max); - - $low = 5; - $high = 20; - $width = 20; - @shft = (10,10); - - $diff = $m - $n; - - if ($diff == 0){ - $x_max = $m * $width + $shft[0]; - } elsif ($diff % 2 == 0 && $diff > 0){ - $x_max = $m * $width + $shft[0]; - $shft[1] += $width * $diff / 2; - } elsif ($diff % 2 == 0) { - $x_max = $n * $width + $shft[0]; - $shft[0] += -$width * $diff / 2; - } elsif ($diff > 0) { - $x_max = $m * $width + $shft[0]; - $shft[1] += ($width / 2) * $diff; - } else { - $x_max = $n * $width + $shft[0]; - $shft[0] += (-$width / 2)* $diff; - } - - $pic = init_graph(0,0,$x_max,25,'pixels'=>[220,220]); - - for ($i = 0; $i < $m; $i++){ - $pic->stamps(closed_circle($i * $width + $shft[0], $high,"blue")); - } - for ($j = 0; $j < $n; $j++){ - $pic->stamps(closed_circle($j * $width + $shft[1], $low,"blue")); - } - - for ($i = 0; $i < $m; $i++){ - for ($j = 0; $j < $n; $j++){ - $pic->moveTo($i * $width + $shft[0], $high); - $pic->lineTo($j * $width + $shft[1], $low, 1); - } - } - $pic; -}; - - -###################################### -#Name: GRcube -#Input: None -#Output: A picture of a graph that is a flattened cube. -###################################### -sub GRcube{ - my($i, $j, $S1, $min, $pic, @prev, @PREV, $Step, @large, @small); - - $min = 5; - $S1 = 30; - $Step = 7.5; - - $pic = init_graph(0, 0, $S1 + 2 * $min, $S1 + 2 * $min, 'pixels'=>[220,220]); - - push @large, $min, $min + $S1, $min + $S1, $min; - push @small, $min + $Step, $min + $S1 - $Step, $min + $S1 - $Step, $min + $Step; - - @PREV = ($large[0], $large[0]); - @prev = ($small[0], $small[0]); - - for ($i = 0; $i < 4; $i++){ - $j = ($i + 1) % 4; - $pic->stamps(closed_circle($large[$i], $large[$j],"blue")); - $pic->stamps(closed_circle($small[$i], $small[$j],"blue")); - $pic->moveTo(@PREV); - $pic->lineTo($large[$i], $large[$j]); - $pic->lineTo($small[$i], $small[$j],1); - $pic->lineTo(@prev); - @PREV = ($large[$i], $large[$j]); - @prev = ($small[$i], $small[$j]); - } - $pic; -}; - - -###################################### -#Name: GRpetersen -#Input: None -#Output: A picture of the Petersen graph. -###################################### -sub GRpetersen{ - my($i, $R1, $R2, $gap, $pic, $val, @prev, $val2, $shift); - - $R1 = 1; - $R2 = 2; - $pic = init_graph(-$R2 - .5,-$R2 - .5,$R2 + .5,$R2+ .5,'pixels'=>[220,220]); - $gap = 2.0 * 3.14 / 5; - $shift = $PI/10; - - @prev = ($R2 * cos ($shift), $R2 * sin($shift)); - for ($i = 0; $i < 5; $i++) { - $val = $i * $gap + $shift; - $val2 = ($i + 2) % 5 * $gap + $shift; - $pic->stamps(closed_circle($R1 * cos($val),$R1 * sin($val),"blue")); - $pic->stamps(closed_circle($R2 * cos($val),$R2 * sin($val),"blue")); - $pic->moveTo(@prev); - $pic->lineTo($R2 * cos($val),$R2 * sin($val)); - $pic->lineTo($R1 * cos($val),$R1 * sin($val), 1); - $pic->lineTo($R1 * cos($val2),$R1 * sin($val2), 1); - @prev = ($R2 * cos($val), $R2 * sin($val)); - } - $pic->moveTo(@prev); - $pic->lineTo($R2 * cos ($shift), $R2 * sin($shift)); - - $pic; -} - - -###################################### -#Name: GRdodecahedron -#Input: None -#Output: A picture of a graph that is a flattened dodecahedron. -###################################### -sub GRdodecahedron{ - my($i, $P1, $P2, $P3, $P4, $gap, $pic, $val, $val2, $shift, $space); - - $P1 = 4; - $P2 = 2.75; - $P3 = 2; - $P4 = 1; - - $pic = init_graph(-$P1 - .5,-$P1 - .5,$P1 + .5,$P1 + .5,'pixels'=>[220,220]); - $gap = 2.0 * 3.14 / 5; - $space = $gap / 2; - $shift = $PI/10; - - @prev = ($P1 * cos ($shift), $P1 * sin($shift)); - for ($i = 0; $i < 5; $i++) { - $val = $i * $gap + $shift; - $val2 = $val + $space; - $pic->stamps(closed_circle($P1 * cos($val),$P1 * sin($val),"blue")); - $pic->stamps(closed_circle($P2 * cos($val),$P2 * sin($val),"blue")); - $pic->stamps(closed_circle($P3 * cos($val2),$P3 * sin($val2),"blue")); - $pic->stamps(closed_circle($P4 * cos($val2),$P4 * sin($val2),"blue")); - $pic->moveTo(@prev); - $pic->lineTo($P1 * cos($val),$P1 * sin($val)); - $pic->lineTo($P2 * cos($val),$P2 * sin($val), 1); - $pic->lineTo($P3 * cos($val2),$P3 * sin($val2), 1); - $pic->lineTo($P4 * cos($val2),$P4 * sin($val2), 1); - $pic->lineTo($P4 * cos($val2 - $gap),$P4 * sin($val2 - $gap), 1); - $pic->moveTo($P2 * cos($val),$P2 * sin($val)); - $pic->lineTo($P3 * cos($val2 - $gap),$P3 * sin($val2 - $gap), 1); - @prev = ($P1 * cos($val), $P1 * sin($val)); - } - $pic->moveTo(@prev); - $pic->lineTo($P1 * cos ($shift), $P1 * sin($shift)); - - $pic; -} - - -###################################### -#Name: GRvertices_labels_labels -#Input: string of labels, string of all graph labels -#Output: Returns a list of vertex numbers corresponding to the -# labels in the first string. -###################################### -sub GRvertices_labels_labels{ - my ($labels, $orig_labels) = @_; - - my ($i, $label, @vertices); - - for ($i = 0; $i < length $labels; $i++){ - $label = substr $labels, $i, 1; - push @vertices, GRvertex_label_labels($label, $orig_labels); - } - - @vertices; -} - - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/NAU/PGnauScheduling.pl b/OpenProblemLibrary/macros/NAU/PGnauScheduling.pl deleted file mode 100755 index 2f678c971f..0000000000 --- a/OpenProblemLibrary/macros/NAU/PGnauScheduling.pl +++ /dev/null @@ -1,1027 +0,0 @@ - - - - -##################################################################### -# -# Name : ListProcEval (answer evaluator for the scheduling .pg files) -# -##################################################################### - -sub ListProcEval { -my (@input) = @_; - sub { - my $in = shift; - my $score=0; - my $ans_hash; - my $plot_input; - my $bad_input = 0; - my $student_answer = ""; - my @coordinates_weights = split ",",$input[0]; - - my $i = 0; my $max_weight = 0; - while ($i < int(scalar @coordinates_weights)){ - if ($i % 3 == 2){$max_weight += $coordinates_weights[$i];} - $i++; - } - - # form the correct string ... - my @temp = split ",", $input[scalar @input - 1]; - my $proc_label = shift @temp; - my $correct = join ",",@temp; - - # check for 'bad' input ... - $in = uc $in; - $in =~ s/ //g; - my $temp_word = $in; - - $temp_word =~ s/T//g; - for ($i = 0; $i < 10; $i++){$temp_word =~ s/$i//g;} - $temp_word =~ s/,//g; - - if ($temp_word ne ""){ - $bad_input = 1; - } else { - $temp_word = $in; - $temp_word = uc $temp_word; - if ($temp_word =~ "TT" || $temp_word =~ "T,T"){ - $bad_input = 1; - } else { - @temp = split ",", $temp_word; - for ($i = 0; $i < scalar @temp; $i++){ - if ($temp[$i] =~ "T"){ - $temp[$i] =~ s/T//g; - if ($temp[$i] > (scalar @coordinates_weights)/3){ - $bad_input = 1; - } - } else { - if ($temp[$i] > $max_weight){$bad_input = 1;} - } - } - } - } - if ($bad_input){ - $score = 0; - $student_answer = 'The answer format is not correct'; - } else { - $plot_input = join ",", $proc_label, $in; - if ($correct eq $in) { - $score = 1; - } - my $pic = DrawProcessorSchedule($input[0], $plot_input); - $pic->gifName($pic->gifName()."-$plot_input"); - $student_answer = Plot($pic); - } - $ans_hash = { score => $score, - correct_ans => $correct, - student_ans => $student_answer, - preview_latex_string => "$in", -# ans_message => ($score) ? ' ' : ' ' - }; - $ans_hash; - } -} - - - - -############################################################# -# -# Name : MakeArrow -# -# Input : $i_1 = the 'tail' x_coordinate in pixels -# $i_2 = the 'tail' y_coordinate in pixels -# $j_1 = the 'head' x_coordinate in pixels -# $j_2 = the 'head' y_coordinate in pixels -# $size = the arrowhead size, in pixels -# $pic = the graphics object to modify -# -# Output : $pic -# -############################################################# - -sub MakeArrow { - my ($i_1, $i_2, $j_1, $j_2, $size, $pic) = @_; - my ($diff1, $diff2, $angle); - $angle = atan2($j_2 - $i_2, $j_1 - $i_1); - $diff1 = $size * (cos($angle) + sin($angle)); - $diff2 = $size * (cos($angle) - sin($angle)); - $pic->moveTo($i_1,$i_2); - $pic->lineTo($j_1,$j_2, 1); - $pic->lineTo($j_1 - $diff1, $j_2 + $diff2, 1); - $pic->lineTo($j_1 - $diff2, $j_2 - $diff1, 1); - $pic->lineTo($j_1, $j_2, 1); -} - - - - -#################################################################################################### -# -# Name : DrawSchedule -# -# Input : $coordinates = "row (0, ... , 10), column (0, ... , 10), weight, ... , ... " -# The order determines the vertex index and label used. -# $connections = "vertex index (tail), vertex index (head), ... , ... " -# The vertex indices are taken according to the ordering in -# $coordinates. -# -# Output : $pic = the graphics object. -# -#################################################################################################### - -sub DrawSchedule { - my ($coordinates, $connections) = @_; - my (@coordinates_weights, @connections, $y_coor_min, $x_coor_max, $pic, - $circleradius, $grid_space, $window_scale, $xlower, $xupper, $ylower, $yupper, - @scaled_coordinates_weights, $offset_1, $offset_2, $this_angle, $arrow_size, - $i_1, $i_2, $j_1, $j_2,$label_x,$label_y,$diff, $vert_label, @circle,$i); - @coordinates_weights = split ",", $coordinates; - @connections = split ",", $connections; - $i = 0; - while ($i < scalar @connections){$connections[$i]--;$i++;} - # find the coordinate extremes. - $y_coor_min = 0; - $x_coor_max = 0; - $i = 0; - while ($i < scalar @coordinates_weights){ - if ( $coordinates_weights[$i+1] > $x_coor_max ){$x_coor_max = $coordinates_weights[$i+1]} - if ( $coordinates_weights[$i] > $y_coor_min ){$y_coor_min = $coordinates_weights[$i]} - $i+=3; - } - # check the coordinates, and reply with error if necessary - if (abs($y_coor_min)>10||abs($x_coor_max)>10){ - die "The absolute value of any coordinate cannot exceed '10' "; - } - #scaling factors - $circleradius = 15; - $grid_space = 15; - $window_scale = 3; - # setup graph. The size is essentially fixed here. - $xlower = - $grid_space; - $xupper = $x_coor_max * $grid_space + $grid_space; - $ylower = -$y_coor_min * $grid_space - $grid_space; - $yupper = $grid_space; - $pic = init_graph($xlower, $ylower, $xupper, $yupper, 'pixels'=>[$window_scale*($xupper-$xlower),$window_scale*($yupper-$ylower)]); - # scale the coordinates - $i = 0; - while ($i < scalar @coordinates_weights){ - $scaled_coordinates_weights[$i+1] = $coordinates_weights[$i+1] * $grid_space; - $scaled_coordinates_weights[$i] = -$coordinates_weights[$i] * $grid_space; - $i += 3; - } - # add the arrows - $this_angle = 0; - $i = 0; - while ($i < scalar @connections){ - $offset_1 = $connections[$i]*3; - $offset_2 = $connections[$i+1]*3; - $arrow_size = 1.5; - $this_angle = atan2($scaled_coordinates_weights[$offset_2]-$scaled_coordinates_weights[$offset_1], - $scaled_coordinates_weights[$offset_2+1]-$scaled_coordinates_weights[$offset_1+1]); - $i_1 = $scaled_coordinates_weights[$offset_1+1] + $circleradius * cos($this_angle)/$window_scale; - $i_2 = $scaled_coordinates_weights[$offset_1] + $circleradius * sin($this_angle)/$window_scale; - $j_1 = $scaled_coordinates_weights[$offset_2+1] - $circleradius * cos($this_angle)/$window_scale; - $j_2 = $scaled_coordinates_weights[$offset_2] - $circleradius * sin($this_angle)/$window_scale; - MakeArrow($i_1, $i_2, $j_1, $j_2, $arrow_size, $pic); - $i+=2; - } - # add the vertex circles - $i = 0; - while($i < scalar @coordinates_weights){ - $label_x = $scaled_coordinates_weights[$i+1]; - $label_y = $scaled_coordinates_weights[$i]; - $diff = $circleradius/(1.5*$window_scale); - $vert_label = $i / 3 + 1; - $circle[$i] = new Circle($label_x,$label_y, $circleradius,'black','none'); - $pic->stamps($circle[$i]); - $pic->lb(new Label($label_x, $label_y + $diff, "T$vert_label",'black','center','center')); - $pic->lb(new Label($label_x, $label_y, $coordinates_weights[$i+2],'black','center','center')); - $i += 3; - } - # the output - $pic; -} - - - - -###################################################################################################### -# -# Name : ListProc (to determine the schedule from a priority list) -# -# Input : $coordinates = "row (0, ... , 10), column (0, ... , 10), weight, ... , ... " -# The order determines the vertex index and label used. -# $connections = "vertex index (tail), vertex index (head), ... , ... " -# The vertex indices are taken according to the ordering in -# $coordinates. -# $order = The 'priority list' needed to realize the scheduling (p.79 textbook). -# $machine_count = The number of available machines or processors. -# -# Output : @done_list = The 'raw' scheduling solution, indexed by machine: -# "vertex index, time, vertex index, time, ... -# The last indexed entry is the algorithm time. -# -##################################################################################################### - -sub ListProc { - my ($coordinates, $connections, $order, $machine_count)=@_; - my (@coordinates_weights, @weights, @connections, @order, $task_count, @not_ready, @ready, @machines, @dependence_count, - @done_list, $time, $done, $i, $j, $k, $l, $max_weight, $onlist, $assigned, $done_count, @machine_info, @these_dones); - - #intialize and setup - ##################################################### - @coordinates_weights = split ",", $coordinates; - $i = 0; - $j = 0; - $max_weight = 0; - while ($i < int(scalar @coordinates_weights)){ - if ($i % 3 == 2){ - $weights[$j] =$coordinates_weights[$i]; - $max_weights += $weights[$j]; - $j++; - } - $i++; - } - @connections = split ",", $connections; - $i = 0; - while ($i < scalar @connections){$connections[$i]--;$i++;} - $order =~ s/T//g; - @order = split ",", $order; - $i = 0; while ($i < scalar @order){$order[$i]--;$i++;} - $task_count = scalar @weights; - $i = 0; while ($i < $machine_count){$machines[$i] = "-1,-2";$i++;} - $i = 0; - while ($i < $machine_count){ - $done_list[$i] = join "", ("P",$i+1); - $i++; - } - $i = 0; - while ($i < $task_count){ - $dependence_count[$i] = 0; - $not_ready[$i] = $i; - $ready[$i] = -1; - $i++; - } - $j = 1; - while ($j < scalar @connections){ - $dependence_count[$connections[$j]]++; - $j+=2; - } - # done intializing - ########################################## - $time = 0; - $done = 0; - $done_count = 0; - while ($time < $max_weights && !$done){ - # update @ready - $i = 0; - while ($i < $task_count){ - # check if the current task is already on the 'ready' list - $onlist = 0; - $j = 0; - while ($j < $task_count && !$onlist){ - if ($ready[$j] == $i){$onlist = 1;} - $j++; - } - # check readyness against current dependencies - if (!$onlist && $dependence_count[$i] == 0){ - unshift @ready, $i; - pop @ready; - } - $i++; - } - $j = 0; - while ($j < $task_count){ - # check if the highest priority task is on the 'ready' list - $k = 0; - while ($k < $task_count){ - if ($order[$j] == $ready[$k]){ - # check for an empty machine - $l = 0; - $assigned = 0; - while ($l < $machine_count && !$assigned){ - @machine_info = split ",", $machines[$l]; - if ($machine_info[0] == -1){ - # add the ready task to this machine - $assigned = 1; - $machines[$l] = join ",",($ready[$k],$weights[$ready[$k]]); - # change the dependence count to '-1' to indicate processing. - $dependence_count[$ready[$k]] = -1; - #remove the ready task from the list - splice @ready, $k, 1; - #free a vacant spot - push @ready, -1; - } - $l++; - } - } - $k++; - } - $j++; - } - $time++; - # update the remaining processing times - $i = 0; - while ($i < $machine_count){ - @machine_info = split ",", $machines[$i]; - if ($machine_info[0] != -1){ - $machine_info[1]--; - # update - $machines[$i] = join ",", ($machine_info[0], $machine_info[1]); - } - $i++ - } - # check for 'freed' machines - $i = 0; - while ($i < $machine_count){ - @machine_info = split ",", $machines[$i]; - if ($machine_info[0] != -1){ - if ($machine_info[1] == 0){ - # found a 'done' task - $done_list[$i] = join ",", $done_list[$i], ($machine_info[0]+1, $time); - push @these_dones, $machine_info[0]; - $machines[$i] = "-1,-2"; - $done_count++; - } - } - $i++ - } - # check for stop condition, otherwise, update dependencies if necessary. - if ($done_count == $task_count){ - $done = 1; - } - else{ - while (scalar @these_dones > 0){ - $j = pop @these_dones; - $k = 0; - while ($k < scalar @connections){ - if ($connections[$k] == $j){$dependence_count[$connections[$k+1]]--;} - $k+=2; - } - } - } - } - # build the 'slack time' answer format - # - # add the "T's" to the labels - $i = 0; - while ($i < scalar @done_list){ - $j = 1; - @this_list = split ",",$done_list[$i]; - while ($j < scalar @this_list){ - $this_list[$j] = join "", ("T", $this_list[$j]); - $j+=2; - } - $done_list[$i] = join ",", @this_list; - $i++; - } - # cycle through each machine, and correct the format - $i = 0; - while ($i < scalar @done_list){ - @this_list = split ",",$done_list[$i]; - # add any slack time before the first task - $offset = 0; - if (scalar @this_list >= 3){ - $this_task = $this_list[1]; - $this_done = $this_list[2]; - $this_task =~ s/T//g; - if ($this_done - $weights[$this_task-1] > 0){ - $temp = shift @this_list; - unshift @this_list, ($this_done - $weights[$this_task-1]); - unshift @this_list, $temp; - $offset++; - } - } - #check this machine schedule for slack time. Enter '-1' if no slack time. - $j = 1 + $offset; - while ($j < (scalar @this_list-3)){ - # check for slack time - $next_task = $this_list[$j+2]; - $next_task =~ s/T//g; - $this_done = $this_list[$j+1]; - $next_done = $this_list[$j+3]; - $next_weight = $weights[$next_task - 1]; - if ($next_done - $next_weight == $this_done){ - #no slack time - $this_list[$j + 1] = -1; - } - else{ - #enter slack time - $this_list[$j+1] = ($next_done - $next_weight)-$this_done; - } - $j+=2; - } - # check for slack time on the last task - if (scalar @this_list == 1){push @this_list, $time;} - if (scalar @this_list > 2){ - if ($this_list[(scalar @this_list) - 1]==$time){ - $this_list[(scalar @this_list) - 1]=-1; - }else{ - $this_list[(scalar @this_list) - 1] = $time - $this_list[(scalar @this_list) - 1]; - } - } - #update - $j = 2 + $offset; - while ($j < scalar @this_list){ - if ($this_list[$j] == -1){ - splice @this_list, $j, 1; - $j++; - } - else{$j+=2;} - } - # final update - $done_list[$i] = join ",",@this_list; - $i++; - } - push @done_list, $time; - @done_list; -} - - - - -########################################################### -# To generate a randomly ordered list of distinct integers -########################################################### - -sub RandomIntList{ - my ($max) = @_; - my ($i, $list, @integer_list, @list, $index); - while (scalar @integer_list < $max){ - push @integer_list, (scalar @integer_list + 1); - } - $i = 0; - while ($i < $max){ - $index = random(0,$max - $i - 1,1); - push @list, $integer_list[$index]; - splice @integer_list,$index,1; - $i++; - } - $list = join ",",@list; - $list; -} - - - - -############################################################################################################################ -# -# Name : FindPathsSchedule ( Works only for appropriate schedule type graphs ) -# -# Input : $start_vertex = An index indicating the 'start vertex' to consider. Indices here START WITH ZERO. -# $connections = The connectivity vector to use to determine the paths. Indices here START WITH ZERO. -# -# Output : @path_list = stores all possible paths from the 'start' vertex. -# -############################################################################################################################ - -sub FindPathsSchedule{ - my ($start_vertex, $connections) = @_; - my (@path_list, $i, $j, $k, $path_update, $list_count, $this_vertex, $previous_vertex, @these_connections, - $first_pass, @this_path, $count, @update, @connections); - - # initialize - @connections = split ",",$connections; - @path_list = ("$start_vertex"); - $count = 0; - $path_update = 1; - while ($path_update){ - $list_count = scalar @path_list; - for ($j = 0; $j < $list_count; $j++){ - @this_path = split ",", $path_list[$j]; - $this_vertex = $this_path[scalar @this_path - 1]; - $previous_vertex = $this_path[scalar @this_path - 2]; - # find all possible connections for '$this_vertex' - $k = 1; - @these_connections = (); - while ($k < scalar @connections){ - if ($connections[$k-1] == $this_vertex){ - push @these_connections, $connections[$k]; - } - $k+=2; - } - $first_pass = 1; - $update[$j] = 0; - for ($i = 0; $i < scalar @these_connections; $i++){ - $update[$j] = 1; - if ($first_pass){ - $first_pass = 0; - $path_list[$j] = join ",", $path_list[$j], $these_connections[$i]; - }else{ - push @path_list, (join ",", @this_path); - $path_list[scalar @path_list - 1] = join ",",$path_list[scalar @path_list - 1], $these_connections[$i]; - } - } - } - # check if any paths were updated - while (scalar @update > 0){ - $path_update = 0; - $temp = pop @update; - if ($temp == 1){$path_update = 1;} - } - # to avoid infinite loops only. - $count++; - if ($count > 1000){ - die "Infinite loop, perhaps ... "; - $path_update = 0; - } - } - @path_list; -} - - - - -########################################################################################################### -# -# Name : CritList ( Produces a priority list according to the critical path scheduling algorithm ) -# -# Input : $coordinates = "row (0, ... , 10), column (0, ... , 10), weight, ... , ... " -# The order determines the vertex index and label used. -# $connections = "vertex index (tail), vertex index (head), ... , ... " -# The vertex indices are taken according to the ordering in -# $coordinates. -# -# Output : $priority_list = The priority list to use, according to the critical path -# algorithm. (The tasks are numerically labeled beginning with -# 1 according to the order of $coordinates.) -# -##################################################################################################### - -sub CritList{ - my ($coordinates, $connections) = @_; - my ($priority_list, @coordinates_weights, @these_coordinates_weights, @weights, $task_count, $i, $j, $k, $max_weights, - $scheduled_count, @left_vertices, $this_vertex, @these_paths, @this_path, $path_weight, - $max_path_weight, $max_path_index, $done, $prioritized, @connection_array, $current_vertex, - @temp, @temp_list, $max_vertex, $independent, $crit_path, $these_connections, $these_coordinates, $temp_word); - - #intialize and setup - ##################################################### - @coordinates_weights = split ",", $coordinates; - @these_coordinates_weights = @coordinates_weights; - $i = 0; - $j = 0; - $max_weight = 0; - while ($i < int(scalar @coordinates_weights)){ - if ($i % 3 == 2){ - $weights[$j] =$coordinates_weights[$i]; - $max_weights += $weights[$j]; - $j++; - } - $i++; - } - $task_count = scalar @weights; - @connection_array = split ",", $connections; - $priority_list = ""; - # end intialize and setup - ##################################################### - $scheduled_count = 0; - while ($scheduled_count < $task_count){ - # update priority list - $these_connections = join ",", @connection_array; - $these_coordinates = join ",", @these_coordinates_weights; - $crit_path = CritPath($these_coordinates, $these_connections); - # remove the path weight, which is of no current use - @temp = split ";", $crit_path; - # convert labels to numbers - $temp[0] =~ s/T//g; - @this_path = split ",", $temp[0]; - $this_task = shift (@this_path); - @temp_list = split ",", $priority_list; - push @temp_list, $this_task; - $priority_list = join ",", @temp_list; - # remove the connection corresponding to the critical path - $j = 0; - while ($j < scalar @connection_array){ - if ($connection_array[$j] == $this_task){ - splice @connection_array, $j, 2; - }else{ - $j+=2; - } - } - # update coordinates to indicate which tasks are already prioritized ... - splice @these_coordinates_weights, ($this_task - 1)*3, 3, (-1,-1,-1); - ### to temporarily force a stop - $scheduled_count++; - if ($scheduled_count > 1000){ - die "possible infinite loop"; - } - } - # add the "T" notation ... - $priority_list = join "", "T", $priority_list; - $priority_list =~ s/,/,T/g; - $priority_list; -} - - - - -########################################################################################################## -# -# Name : CritPath ( Finds the critical path in a schedule type graph. 'Ties' are broken by comparing -# the task labels of the first task at which two paths disagree. The smaller label -# is taken as the critical path. ) -# -# Input : $coordinates = "row (0, ... , 10), column (0, ... , 10), weight, ... , ... " -# The order determines the vertex index and label used. -# If -1,-1,-1 appears for any vertex, that task is not considered -# as part of the graph. -# $connections = "vertex index (tail), vertex index (head), ... , ... " -# The vertex indices are taken according to the ordering in -# $coordinates. -# -# Output : $crit_path = "task_label1, task_label2, ... , $task_labeln; total_weight -# -########################################################################################################### - -sub CritPath{ - my ($coordinates, $connections) = @_; - my (@coordinates_weights, @weights, $task_count, $i, $j, $k, - $scheduled_count, @left_vertices, $this_vertex, @these_paths, @this_path, $path_weight, - $max_path_weight, $max_path_index, $done, $prioritized, @connection_array, $current_vertex, - @temp, $max_vertex, $independent); - - #intialize and setup - ##################################################### - @coordinates_weights = split ",", $coordinates; - $i = 0; - $j = 0; - while ($i < int(scalar @coordinates_weights)){ - if ($i % 3 == 2){ - $weights[$j] =$coordinates_weights[$i]; - $j++; - } - $i++; - } - $task_count = scalar @weights; - @connection_array = split ",", $connections; - $i = 0; - while ($i < scalar @connection_array){$connection_array[$i]--;$i++;} - # end intialize and setup - ##################################################### - - # find all possible starting vertices ... - @left_vertices=(); - $j = 0; - while ($j < scalar @connection_array){ - $this_vertex = $connection_array[$j]; - push @left_vertices, $this_vertex; - # check if the vertex was already found - for ($k = 0; $k < scalar @left_vertices - 1; $k++){ - if ($this_vertex == $left_vertices[$k]){ - pop @left_vertices; - $k = scalar @left_vertices - 1; - } - } - # check if we have a right vertex - $k = 1; - while ($k < scalar @connection_array){ - if ($connection_array[$k]==$this_vertex){ - pop @left_vertices; - $k = scalar @connection_array; - }else{ - $k+=2; - } - } - $j+=2; - } - # check for 'independent' tasks - for ($j = 0; $j < $task_count; $j++){ - $independent = 1; - # check if the vertex is connected - for ($k = 0; $k < scalar @connection_array; $k++){ - if ($connection_array[$k] == $j){ - $independent = 0; - } - } - # add this independent task - if ($independent && $coordinates_weights[$j * 3] != -1){push @left_vertices, $j;} - } - # find ALL paths ... - $j = 0; - @these_paths = (); - while ($j < scalar @left_vertices){ - $this_vertex = $left_vertices[$j]; - push @these_paths, FindPathsSchedule($this_vertex, (join ",",@connection_array)); - $j ++; - } - # determine the CRITICAL path ... - $j = 0; - $max_path_weight = 0; - $max_path_index = 0; - while ($j < scalar @these_paths){ - @this_path = split ",", $these_paths[$j]; - $current_vertex = $this_path[0]; - @temp = split ",", $these_paths[$max_path_index]; - $max_vertex = shift @temp; - $path_weight = 0; - while (scalar @this_path > 0){ - $this_vertex = pop @this_path; - $path_weight = $path_weight + $weights[$this_vertex]; - } - if ($path_weight > $max_path_weight){ - $max_path_weight = $path_weight; - $max_path_index = $j; - } - else{ - if ($path_weight == $max_path_weight){ - if ($current_vertex < $max_vertex){ - $max_path_weight = $path_weight; - $max_path_index = $j; - } - if ($current_vertex == $max_vertex){ - # the paths need to be compared according to vertex labels ... - @temp = split ",", $these_paths[$max_path_index]; - $k = 0; - while ($k < scalar @temp && $k < scalar @this_path){ - if ($this_path[$k] < $temp[$k]){ - $max_path_weight = $path_weight; - $max_path_index = $j; - $k = scalar @temp - 1; - } - if ($this_path[$k] > $temp[$k]){ - $k = scalar @temp - 1; - } - $k++; - } - } - } - } - $j++; - } - # update and output - $crit_path = $these_paths[$max_path_index]; - @temp = split ",", $crit_path; - $j = 0; - while ($j < scalar @temp){ - $temp[$j]++; - $j++; - } - $crit_path = join ",", @temp; - $crit_path = join "", "T", $crit_path; - $crit_path =~ s/,/,T/g; - $crit_path = join ";", $crit_path, $max_path_weight; - $crit_path; -} - - - - -####################################################################################################### -# -# Name : PickSchedule -# -# Input : $random_flag = if zero, empty or out-of-range, a random choice is made. If positive -# and in-range, this indexes the graph to use. If "easy", then an 'easy' -# graph is chosen. -# -# Output : $graph_info = $coordinates ; $connections where: -# -# $coordinates = "row (0, ... , 10), column (0, ... , 10), weight, ... , ... " -# The order determines the vertex index and label used. -# $connections = "vertex index (tail), vertex index (head), ... , ... " -# The vertex indices are taken according to the ordering in -# $coordinates. -# -######################################################################################################## - -sub PickSchedule{ - - my ($random_flag) = @_; - my ($temp,$coordinates, $connections, $this_graph, $graph_info, @vertex_data, @connect_data, @vertices_weights, $i); - - # screen the input variable ... - if ($random_flag){ - $temp = $random_flag; - if ($temp =~ "easy" || $temp =~ "Easy"){ - $random_flag =~ "easy"; - } else { - for ($i = 0; $i < 10; $i++){$temp =~ s/$i//g;} - if ($temp ne ""){$random_flag = 0;} - } - } - - # load some possible graphs ... - $vertex_data[0] = "2,0,9,1,1,9,3,1,9,0,2,9,2,2,9,4,2,9,1,3,9,3,3,9"; - $connect_data[0] = "1,2,1,3,1,5,2,4,2,5,3,6,4,7,5,7,5,8,6,8,1,7"; - - $vertex_data[1] = "1,0,14,0,3,14,1,6,36,2,3,14,4,0,4,3,1,6,3,5,16,5,1,4,5,3,4,5,5,16"; - $connect_data[1] = "1,2,1,4,5,6,5,8,6,4,8,9,9,10,4,7,2,3,4,3"; - - $vertex_data[2] = "1,0,8,1,1,6,2,2,9,2,3,4,0,2,5,0,3,3,0,0,5,0,1,7"; - $connect_data[2] = "1,2,2,3,3,4,5,6,2,5"; - - $vertex_data[3] = "0,0,2,1,0,1,2,0,1,3,0,1,1,3,3,2,4,3,3,3,3,4,2,3,0,2,8"; - $connect_data[3] = "1,9,4,5,4,6,4,7,4,8"; - - $vertex_data[4] = "2,0,13,5,0,18,0,2,12,4,2,9,2,3,8,5,4,20"; - $connect_data[4] = "1,3,1,4,2,4,3,5,4,5,2,6"; - - $vertex_data[5] = "1,0,1,1,1,1,0,2,1,2,2,1,1,3,1,2,4,1,2,5,1"; - $connect_data[5] = "1,2,2,3,2,4,4,5,3,5,4,6,6,7"; - - $vertex_data[6] = "1,0,1,0,1,1,2,1,1,1,2,1,0,3,1,2,3,1,2,4,1"; - $connect_data[6] = "1,2,1,3,2,4,3,4,2,5,3,6,6,7"; - - $vertex_data[7] = "0,0,10,1,0,3,2,0,8,0,3,15,1,3,4,2,3,20"; - $connect_data[7] = "2,4,2,5,3,5"; - - $vertex_data[8] = "0,0,8,2,0,9,4,0,3,6,0,10,1,2,12,4,2,5,2,4,6,6,4,10"; - $connect_data[8] = "1,5,2,5,3,6,4,8,5,7,5,8"; - - $vertex_data[9] = "0,0,8,0,2,6,0,4,3,2,0,5,2,2,2,2,4,9,4,0,7"; - $connect_data[9] = "1,2,4,2,4,5,2,3,5,3,5,6"; - - $vertex_data[10] = "0,0,7,0,2,6,0,4,7,2,0,2,2,2,13,2,4,6,4,0,1,4,2,5,4,4,8"; - $connect_data[10] = "1,2,4,5,7,5,7,8,2,3,2,6,5,6,8,9"; - - $vertex_data[11] = "1,0,8,0,1,5,2,1,6,1,2,5,0,3,7"; - $connect_data[11] = "1,2,1,3,2,4,2,5,3,4"; - - $vertex_data[12] = "0,0,12,2,0,9,4,0,7,0,2,13,2,2,15,0,4,20,4,3,10"; - $connect_data[12] = "1,4,2,5,3,5,4,6,5,6,3,7,7,6"; - - $vertex_data[13] = "1,0,8,1,1,6,2,2,9,2,4,4,0,2,5,0,4,3,0,0,5,0,1,7"; - $connect_data[13] = "1,2,2,5,2,3,5,6,3,4"; - - $vertex_data[14] = "0,0,3,1,0,2,2,0,2,3,0,2,1,3,4,2,4,4,3,3,4,4,2,4,0,2,9"; - $connect_data[14] ="1,9,4,5,4,6,4,7,4,8"; - - $vertex_data[15] = "0,0,1,1,0,1,2,0,1,3,0,1,4,0,10,5,0,10,6,0,10,0,3,3,1,3,3,2,3,3,3,3,3,1.5,4.5,10"; - $connect_data[15] = "1,8,1,9,1,10,1,11,8,12,9,12,10,12,11,12"; - - $vertex_data[16] = "1,0,14,0,3,14,1,6,36,2,3,14,4,0,4,3,1,6,3,5,16,5,1,4,5,3,4,5,5,16,4,6,20,0,7,30,2,7,10"; - $connect_data[16] = "1,2,1,4,5,6,5,8,6,4,8,9,9,10,4,7,2,3,4,3,10,11,7,11,3,12,3,13,7,13"; - - $vertex_data[17] = "0,0,4,2,0,5,4,0,3,1.5,4,3,1,2,8,3,2,7"; - $connect_data[17] = "1,5,2,5,3,6,5,4,6,4"; - - $vertex_data[18] = "0,0,3,0,3,5,0,6,8,2,0,4,2,3,6,2,6,2"; - $connect_data[18] = "1,2,1,5,4,5,2,3,5,3,5,6"; - - $vertex_data[19] = "2,0,5,4,0,9,0,0,3,2,3,5,2,6,3,4,6,2,4,3,5"; - $connect_data[19] = "1,4,2,4,2,7,4,5,4,6"; - - $vertex_data[20] = "0,0,8,2,1,10,0,2,12,2,3,15,0,4,5,2,5,9,2,7,8,0,6,13"; - $connect_data[20] = "1,3,2,3,2,4,3,5,4,6,5,6,5,8,6,7"; - - $vertex_data[21] = "0,0,7,0,3,9,0,6,5,2,0,8,2,3,6,2,6,7"; - $connect_data[21] = "1,2,4,5,2,3,5,3,5,6,2,6"; - - # ************ ADD ANY NEW GRAPHS HERE ******************** - - # pick the graph - if ($random_flag){ - if ($random_flag =~ "easy"){ - $this_graph = list_random(3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19,20,21,22)-1; - # randomize the weights - @vertices_weights = split ",",$vertex_data[$this_graph]; - $i = 2; - while ($i < scalar @vertices_weights){ - $vertices_weights[$i] = list_random(1,2,5); - $i+=3; - } - $vertex_data[$this_graph] = join ",", @vertices_weights; - } - else { - if ($random_flag >= 1 && $random_flag <= scalar @vertex_data){ - $this_graph = $random_flag - 1; - }else { - $this_graph = random(0, (scalar @vertex_data) - 1, 1); - } - } - } else { - $this_graph = random(0,(scalar @vertex_data)-1, 1); - @vertices_weights = split ",",$vertex_data[$this_graph]; - $i = 2; - while ($i < scalar @vertices_weights){ - $vertices_weights[$i] = random(1,9,1); - $i+=3; - } - $vertex_data[$this_graph] = join ",", @vertices_weights; - } - - # randomize the connectivity (?) - - # ready to output - $coordinates = $vertex_data[$this_graph]; - $connections = $connect_data[$this_graph]; - $graph_info = join ";", $coordinates, $connections; - $graph_info; -} - - - - -####################################################################################################### -# -# Name : DrawProcessorSchedule -# -# Input : $coordinates = "row (0, ... , 10), column (0, ... , 10), weight, ... , ... " -# The order determines the vertex index and label used. The -# weights are needed to draw the schedules. -# $done_list = The scheduling solution, for the current processor. -# -# Output : $schedule_graph = graphics object. -# -######################################################################################################## - -sub DrawProcessorSchedule{ - - my ($coordinates, $done_list) = @_; - my ($i, $j, $max_weights, $window_size, $xlower, $xupper, $ylower, $yupper, $yuppermargin, $pic, @coordinates_weights, - @weights, @done_array, $total_time, $this_task_index, @break_lines, @scaled_break_lines, - @shade_region, $label, $lower_region, $processor_label, $margin); - - @coordinates_weights = split ",", $coordinates; - $i = 0; $j = 0; - $max_weight = 0; - while ($i < int(scalar @coordinates_weights)){ - if ($i % 3 == 2){ - $weights[$j] =$coordinates_weights[$i]; - $max_weights += $weights[$j]; - $j++; - } - $i++; - } - @done_array = split ",", $done_list; - $processor_label = shift @done_array; - - # setup graph. The size is essentially fixed here. - $window_size = 4; - $yuppermargin = 1; - $margin = 5; - $xlower = - $margin; - $xupper = 100 + $margin; - $ylower = -$margin; - $yupper = 8 + $yuppermargin; - $pic = init_graph($xlower, $ylower, $xupper, $yupper, 'pixels'=>[$window_size*($xupper-$xlower),$window_size*($yupper- $ylower)]); - $pic->moveTo($xlower + $margin,$ylower + $margin); - $pic->lineTo($xupper - $margin, $ylower + $margin, 1); - $pic->lineTo($xupper - $margin, $yupper - $yuppermargin, 1); - $pic->lineTo($xlower + $margin, $yupper - $yuppermargin, 1); - $pic->lineTo($xlower + $margin, $ylower + $margin, 1); - - # determine the total processing time, build the 'break_lines' and 'shade region' array - $i = 0; $total_time = 0; - while ($i < scalar @done_array){ - if ($done_array[$i] =~ "T"){ - $this_task_index = $done_array[$i]; - $this_task_index =~ s/T//g; - $this_task_index--; - $total_time += $weights[$this_task_index]; - $break_lines[$i] = $total_time; - $shade_region[$i] = 1; - } else { - $total_time += $done_array[$i]; - $break_lines[$i] = $total_time; - $shade_region[$i] = 0; - } - $i++; - } - # build the 'scaled break lines' array ... - $i = 0; - while ($i < @break_lines){ - $scaled_break_lines[$i] = (($xupper - $margin)-($xlower + $margin)) / $total_time * $break_lines[$i]; - $i++; - } - # draw the processor schedule - $label = new Label($xlower + $margin / 2, (($yupper - $yuppermargin) + ($ylower + $margin)) * 2 / 3, $processor_label, 'black', 'center', 'center'); - $pic->lb($label); - $label = new Label($xlower + $margin, $ylower + $margin * 3 / 4, "0", 'black', 'center', 'center'); - $pic->lb($label); - for ($i = 0; $i < scalar @scaled_break_lines; $i++){ - $pic->moveTo($scaled_break_lines[$i], $ylower + $margin); - $pic->lineTo($scaled_break_lines[$i], $yupper - $yuppermargin); - if ($shade_region[$i] == 1){ - if ($i == 0) {$lower_region = 0;} else {$lower_region = $scaled_break_lines[$i - 1];} - $label = new Label(($scaled_break_lines[$i] + $lower_region) / 2, (($yupper - $yuppermargin) + ($ylower + $margin)) *2 / 3, $done_array[$i], 'black', 'center', 'center'); - $pic->lb($label); - $pic->fillRegion([($scaled_break_lines[$i] + $lower_region) / 2, (($yupper - $yuppermargin) + ($ylower + $margin)) *2 / 3,'gray']); - } - $label = new Label($scaled_break_lines[$i], $ylower + $margin * 3 / 4, $break_lines[$i], 'black', 'center', 'center'); - $pic->lb($label); - } - $pic; -} - - - - -### -# Name : FindMinimum -### - -sub FindMinimum{ - my (@list) = @_; - my ($minval, $i); - $minval = $list[0]; - $i = 0; - while ($i < scalar @list){ - if ($list[$i] < $minval){$minval = $list[$i];} - $i++; - } - $minval; -} -1; diff --git a/OpenProblemLibrary/macros/NAU/PGnauSet.pl b/OpenProblemLibrary/macros/NAU/PGnauSet.pl deleted file mode 100755 index 6727a676d2..0000000000 --- a/OpenProblemLibrary/macros/NAU/PGnauSet.pl +++ /dev/null @@ -1,314 +0,0 @@ -loadMacros("PGnauGraphics.pl", - ); - -####################################################################################################### -# -# Name : DrawVenn2 -# -# Input : -# -# $input_word = 'region1_info, region2_info, ...' -# The information to use, arranged in order by region. -# Use "_fill_" to shade the corresponding region, otherwise -# the input is treated as label. -# -# @options = 'Arrow indicated options' in any order. -# -# labels=>'Set1 label, Set2 label, U'. Set1 is on the -# left. Leaving any or all entries blank will -# produce no label for that item. Default is -# 'A,B,U' -# -# Output : $diagram = a graphics object, the diagram to plot. Use 'Plot' in the -# .pg file. -# -######################################################################################################## -sub DrawVenn2{ - my (@input_word)=@_; - my ($i, $xlower, $xupper, $ylower, $yupper, $diagram, $window_scale, $axis_label, $margin, $radius, - $x1, $y1, $x2, $y2, $label_string, @labels, $optional_labels, $this_x, $this_y, @input, $templ, - $tempu, $tempr2); - - # check for optional labels; - $i = 0; - while ($i < scalar @input_word - 1){ - if ($input_word[$i] eq "labels"){ - $optional_labels = 1; - $label_string = $input_word[$i+1]; - splice (@input_word, $i, 2); - $label_string =~ s/ //g; - @labels = split ",", $label_string; - } - $i++; - } - if (!$optional_labels){@labels = ('A','B','U');} - @input = split ",", $input_word[0]; - for ($i = 0; $i < scalar @input; $i++){ - $input[$i] =~ s/ //g; - } - $margin = 5; - $xlower = -$margin; - $xupper = 100 + $margin; - $ylower = -$margin; - $yupper = 2/3*(($xupper-$margin)-($xlower+$margin)) + $margin; - $window_scale = 3.5; - $diagram = init_graph($xlower, $ylower, $xupper, $yupper, 'pixels'=>[$window_scale*($xupper-$xlower),$window_scale*($yupper- $ylower)]); - $diagram-> moveTo($xlower+$margin,$ylower+$margin); - $diagram-> lineTo($xupper-$margin,$ylower+$margin); - $diagram-> lineTo($xupper-$margin,$yupper-$margin); - $diagram-> lineTo($xlower+$margin,$yupper-$margin); - $diagram-> lineTo($xlower+$margin,$ylower+$margin); - $axislabels = new Label(95, 7, $labels[2], 'black', 'center', 'center'); - $diagram-> lb($axislabels); - $radius = ($xupper -$xlower - 2 * $margin)/4; - $x1 = ($xupper -$xlower - 2 * $margin)/3; - $y1 = ($yupper+$ylower)/2; - $x2 = ($xupper -$xlower - 2 * $margin)/3*2; - $y2 = ($yupper+$ylower)/2; - - # draw and label the first circle - $templ = $x1 - $radius; - $tempu = $x1 + $radius; - $tempr2 = $radius**2; - $circle1 = FEQ("sqrt($tempr2-(x-$x1)^2)+$y1 for x in <$templ,$tempu> using color:red and weight:2"); - $circle2 = FEQ("-sqrt($tempr2-(x-$x1)^2)+$y1 for x in <$templ,$tempu> using color:red and weight:2"); - ($c1ref, $c2ref) = plot_functions($diagram, $circle1, $circle2); - $axislabels = new Label($x1 - (2+sqrt(2))/4*$radius, $y1 + (2+sqrt(2))/4*$radius, $labels[0], 'red', 'center', 'center'); - $diagram-> lb($axislabels); - - # draw and label the second circle - $templ = $x2 - $radius; - $tempu = $x2 + $radius; - $tempr2 = $radius**2; - $circle1 = FEQ("sqrt($tempr2-(x-$x2)^2)+$y2 for x in <$templ,$tempu> using color:blue and weight:2"); - $circle2 = FEQ("-sqrt($tempr2-(x-$x2)^2)+$y2 for x in <$templ,$tempu> using color:blue and weight:2"); - ($c1ref, $c2ref) = plot_functions($diagram, $circle1, $circle2); - $axislabels = new Label($x2 + (2+sqrt(2))/4*$radius, $y2 + (2+sqrt(2))/4*$radius, $labels[1], 'blue', 'center', 'center'); - $diagram-> lb($axislabels); - $i = 0; - while ($i < scalar @input && $i < 4){ - if ($i == 0){ - $this_x = (($xupper - $margin)-($xlower + $margin))/2; - $this_y = (($xupper - $margin)-($xlower + $margin))/3; - } - if ($i == 1){ - $this_x = $x1 - sqrt(2)/4*$radius; - $this_y = $y1 + sqrt(2)/4*$radius; - } - if ($i == 2){ - $this_x = $x2 + sqrt(2)/4*$radius; - $this_y = $y2 + sqrt(2)/4*$radius; - } - if ($i == 3){ - $this_x = $x2 + (2+sqrt(2))/4*$radius; - $this_y = $y2 - (2+sqrt(2))/4*$radius; - } - if ($input[$i] eq "_fill_"){ - - $diagram->fillRegion([$this_x,$this_y,'gray']); - }else{ - $axislabels = new Label($this_x, $this_y, $input[$i], 'black', 'center', 'center'); - $diagram-> lb($axislabels); - } - $i++; - } - $diagram; -} - - -####################################################################################################### -# -# Name : DrawVenn3 -# -# Input : -# -# $input_word = 'region1_info, region2_info, ...' -# The information to use, arranged in order by region. -# Use "_fill_" to shade the corresponding region, otherwise -# the input is treated as label. -# -# @options = 'Arrow indicated options' in any order. -# -# labels=>'Set1 label, Set2 label, Set 3 label, U'. Set1 is on the -# left. Leaving any or all entries blank will -# produce no label for that item. Default is -# 'A,B,C,U' -# -# Output : $diagram = a graphics object, the diagram to plot. Use 'Plot' in the -# .pg file. -# -######################################################################################################## -sub DrawVenn3{ - my (@input_word)=@_; - my ($i, $xlower, $xupper, $ylower, $yupper, $diagram, $window_scale, $axis_label, $margin, $radius, - $x1, $y1, $x2, $y2, $x3, $y3, $x4, $y4, $label_string, @labels, $optional_labels, $this_x, $this_y, - @input, $templ, $tempu, $tempr2, $tempo); - - # check for optional labels; - $i = 0; - while ($i < scalar @input_word - 1){ - if ($input_word[$i] eq "labels"){ - $optional_labels = 1; - $label_string = $input_word[$i+1]; - splice (@input_word, $i, 2); - $label_string =~ s/ //g; - @labels = split ",", $label_string; - } - $i++; - } - if (!$optional_labels){@labels = ('A','B','C','U');} - @input = split ",", $input_word[0]; - for ($i = 0; $i < scalar @input; $i++){ - $input[$i] =~ s/ //g; - } - $margin = 5; - $xlower = -$margin; - $xupper = 100 + $margin; - $ylower = -$margin; - $yupper = (4 + sqrt(3))/6*(($xupper-$margin)-($xlower+$margin)) + $margin; - $window_scale = 3.5; - $diagram = init_graph($xlower, $ylower, $xupper, $yupper, 'pixels'=>[$window_scale*($xupper-$xlower),$window_scale*($yupper- $ylower)]); - $diagram-> moveTo($xlower+$margin,$ylower+$margin); - $diagram-> lineTo($xupper-$margin,$ylower+$margin); - $diagram-> lineTo($xupper-$margin,$yupper-$margin); - $diagram-> lineTo($xlower+$margin,$yupper-$margin); - $diagram-> lineTo($xlower+$margin,$ylower+$margin); - $axislabels = new Label(95, 7, $labels[3], 'black', 'center', 'center'); - $diagram-> lb($axislabels); - $radius = ($xupper -$xlower - 2 * $margin)/4; - $x1 = ($xupper -$xlower - 2 * $margin)/3; - $y1 = (($xupper - $margin)-($xlower+$margin))*(2+sqrt(3))/6; - $x3 = ($xupper -$xlower - 2 * $margin)/3*2; - $y3 = (($xupper - $margin)-($xlower+$margin))*(2+sqrt(3))/6; - $x2 = ($xupper -$xlower - 2 * $margin)/2; - $y2 = (($xupper - $margin)-($xlower+$margin))/3; - - # draw and label the first circle - $templ = $x1 - $radius; - $tempu = $x1 + $radius; - $tempr2 = $radius**2; - $circle1 = FEQ("sqrt($tempr2-(x-$x1)^2)+$y1 for x in <$templ,$tempu> using color:red and weight:2"); - $circle2 = FEQ("-sqrt($tempr2-(x-$x1)^2)+$y1 for x in <$templ,$tempu> using color:red and weight:2"); - ($c1ref, $c2ref) = plot_functions($diagram, $circle1, $circle2); - $axislabels = new Label($x1 - (2+sqrt(2))/4*$radius, $y1 + (2+sqrt(2))/4*$radius, $labels[0], 'red', 'center', 'center'); - $diagram-> lb($axislabels); - - # draw and label the second circle - $templ = $x2 - $radius; - $tempu = $x2 + $radius; - $tempr2 = $radius**2; - $circle1 = FEQ("sqrt($tempr2-(x-$x2)^2)+$y2 for x in <$templ,$tempu> using color:blue and weight:2"); - $circle2 = FEQ("-sqrt($tempr2-(x-$x2)^2)+$y2 for x in <$templ,$tempu> using color:blue and weight:2"); - ($c1ref, $c2ref) = plot_functions($diagram, $circle1, $circle2); - $axislabels = new Label($x2 - (2+sqrt(2))/4*$radius, $y2 - (2+sqrt(2))/4*$radius, $labels[1], 'blue', 'center', 'center'); - $diagram-> lb($axislabels); - - # draw and label the third circle - $templ = $x3 - $radius; - $tempu = $x3 + $radius; - $tempr2 = $radius**2; - $circle1 = FEQ("sqrt($tempr2-(x-$x3)^2)+$y3 for x in <$templ,$tempu> using color:orange and weight:2"); - $circle2 = FEQ("-sqrt($tempr2-(x-$x3)^2)+$y3 for x in <$templ,$tempu> using color:orange and weight:2"); - ($c1ref, $c2ref) = plot_functions($diagram, $circle1, $circle2); - $axislabels = new Label($x3 + (2+sqrt(2))/4*$radius, $y3 + (2+sqrt(2))/4*$radius, $labels[2], 'orange', 'center', 'center'); - $diagram-> lb($axislabels); - $x4 = $x2; - $y4 = $y2 + (($xupper-$margin)-($xlower+$margin))/(3*sqrt(3)); - $i = 0; - while ($i < scalar @input && $i < 8){ - if ($i == 0){ - $this_x = $x4; - $this_y = $y4; - } - if ($i == 1){ - $this_x = $x4; - $this_y = $y4 + (($xupper-$margin)-($xlower+$margin))/(6*sqrt(3)); - } - if ($i == 2){ - $this_x = $x1 + (($xupper-$margin)-($xlower+$margin))/12; - $this_y = $y1 - (($xupper-$margin)-($xlower+$margin))*sqrt(3)/12; - } - if ($i == 3){ - $this_x = $x3 - (($xupper-$margin)-($xlower+$margin))/12; - $this_y = $y3 - (($xupper-$margin)-($xlower+$margin))*sqrt(3)/12; - } - if ($i == 4){ - $this_x = $x3 + sqrt(2)/4 * $radius; - $this_y = $y3 + sqrt(2)/4 * $radius; - } - if ($i == 5){ - $this_x = $x1 - sqrt(2)/4 * $radius; - $this_y = $y1 + sqrt(2)/4*$radius; - } - if ($i == 6){ - $this_x = $x2; - $this_y = $y2 - $radius / 2 ; - } - if ($i == 7){ - $this_x = $xupper - $margin - $radius/2; - $this_y = $ylower + $margin + $radius/2; - } - if ($input[$i] eq "_fill_"){ - $diagram->fillRegion([$this_x,$this_y,'gray']); - }else{ - $axislabels = new Label($this_x, $this_y, $input[$i], 'black', 'center', 'center'); - $diagram-> lb($axislabels); - } - $i++; - } - $diagram; -} - - -##################################### -# -# Name : Venn2answers -# -# -##################################### -sub Venn2answers{ - - my ($labels) = @_; - my (@set, $i); - - @set = split ",", $labels; - if (scalar @set < 3){die "Too few labels, exiting ...";} - for ($i = 0; $i < scalar @set; $i++){$set[$i] =~ s/ //g;} - - $combo[0] = "0,\\( $set[0] \\cap $set[1] \\), \\( \\overline{\\overline{$set[0]} \\cup \\overline{$set[1]}} \\)"; - $combo[1] = "1,\\( $set[0] \\cap \\overline{$set[1]}\\), \\( \\overline{\\overline{$set[0]} \\cup $set[1] } \\)"; - $combo[2] = "2,\\( \\overline{$set[0]} \\cap $set[1]\\), \\( \\overline{$set[0] \\cup \\overline{$set[1]}} \\)"; - $combo[3] = "3,\\( \\overline{$set[0] \\cup $set[1]}\\), \\(\\overline{$set[0]} \\cap \\overline{$set[1]} \\)"; - - @combo; -} - - -##################################### -# -# Name : Venn3answers -# -# -##################################### -sub Venn3answers{ - - my ($labels) = @_; - my (@set, $i); - - @set = split ",", $labels; - if (scalar @set < 4){die "Too few labels, exiting ...";} - for ($i = 0; $i < scalar @set; $i++){$set[$i] =~ s/ //g;} - - $combo[0] = "0,\\( $set[0] \\cap $set[1] \\cap $set[2] \\), \\( \\overline{\\overline{$set[0]} \\cup \\overline{$set[1]} \\cup \\overline{$set[2]}}\\), \\( \\overline{\\overline{$set[0]} \\cup \\overline{$set[1]}} \\cap $set[2] \\)"; - $combo[1] = "1,\\( $set[0] \\cap $set[2] \\cap \\overline{$set[1]} \\), \\(\\overline{\\overline{$set[0]} \\cup \\overline{$set[2]} \\cup $set[1] } \\), \\( \\overline{\\overline{$set[0]} \\cup \\overline{$set[2]}} \\cap \\overline{$set[1]} \\)"; - $combo[2] = "2,\\( $set[0] \\cap $set[1] \\cap \\overline{$set[2]} \\), \\(\\overline{\\overline{$set[0]} \\cup \\overline{$set[1]} \\cup $set[2] } \\), \\( \\overline{\\overline{$set[0]} \\cup \\overline{$set[1]}} \\cap \\overline{$set[2]} \\)"; - $combo[3] = "3,\\( $set[1] \\cap $set[2] \\cap \\overline{$set[0]} \\), \\(\\overline{\\overline{$set[1]} \\cup \\overline{$set[2]} \\cup $set[0] } \\), \\( \\overline{\\overline{$set[1]} \\cup \\overline{$set[2]}} \\cap \\overline{$set[0]} \\)"; - $combo[4] = "4,\\( $set[2] \\cap (\\overline{$set[0] \\cup $set[1]}) \\), \\( $set[2] \\cap \\overline{$set[0]} \\cap \\overline{$set[1]} \\), \\( \\overline{\\overline{$set[2]} \\cup $set[0] \\cup $set[1]} \\)"; - $combo[5] = "5,\\( $set[0] \\cap (\\overline{$set[2] \\cup $set[1]}) \\), \\( $set[0] \\cap \\overline{$set[2]} \\cap \\overline{$set[1]} \\), \\( \\overline{\\overline{$set[0]} \\cup $set[2] \\cup $set[1]} \\)"; - $combo[6] = "6,\\( $set[1] \\cap (\\overline{$set[0] \\cup $set[2]}) \\), \\( $set[1] \\cap \\overline{$set[0]} \\cap \\overline{$set[2]} \\), \\( \\overline{\\overline{$set[1]} \\cup $set[0] \\cup $set[2]} \\)"; - $combo[7] = "7,\\( \\overline{$set[0] \\cup $set[1] \\cup $set[2]} \\), \\( \\overline{$set[0]} \\cap \\overline{$set[1]} \\cap \\overline{$set[2]} \\), \\( (\\overline{$set[0] \\cup $set[1]} ) \\cap \\overline{$set[2]}\\)"; - - @combo; -} - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/NAU/PGnauStats.pl b/OpenProblemLibrary/macros/NAU/PGnauStats.pl deleted file mode 100755 index 1952b2b6ab..0000000000 --- a/OpenProblemLibrary/macros/NAU/PGnauStats.pl +++ /dev/null @@ -1,1144 +0,0 @@ -loadMacros("PGnauGraphics.pl", - ); - - -################################ -#Name: MeanDev -#Input: List of data values -#Output: List containing mean and standard deviation -################################ -sub MeanDev{ - my (@list) = @_; - my($i, $dev, $sum, $mean, $size, @values); - - $sum = 0; - $size = scalar @list; - foreach $val (@list){ - $sum += $val; - } - $mean = $sum/$size; - - $sum = 0; - foreach $val (@list){ - $sum += ($val - $mean)**2; - } - $dev = sqrt($sum / ($size - 1)); - - @values = ($mean, $dev); -} - - -################################ -#Name: Median -#Input: List of data values -#Output: Value of the median -################################ -sub Median{ - my(@list) = @_; - - my ($med, $size); - - @list = num_sort(@list); - $size = scalar @list; - - if ($size % 2 == 0){ - $med = ($list[$size/2] + $list[$size/2-1])/2; - } else { - $med = $list[$size/2]; - } -} - - -################################ -#Name: FiveNum -#Input: List of data values -#Output: List containing the five number summary in order from Low to High -################################ -sub FiveNum{ - my(@list) = @_; - - my ($L, $Q1, $Q2, $Q3, $H, $val, $size, $lower, $upper, @values); - - @list = num_sort(@list); - $size = scalar @list; - - $L = $list[0]; - $H = $list[$size-1]; - $Q2 = Median(@list); - $val = $size / 2; - if ($size % 2 == 0){ - $lower = $val - 1; - $upper = $val; - } else{ - $lower = $val - 1; - $upper = $val + 1; - } - - $Q1 = Median(@list[0..$lower]); - $Q3 = Median(@list[$upper..$size - 1]); - - @values = ($L, $Q1, $Q2, $Q3, $H); -} - - -################################ -#Name: Mode -#Input: List of data values -#Optional Input: 'Max_Modes'=> num limits the maximum number of modes to num -#Output: List containing the mode or the list ("None") if the number of modes -# is more than num, -################################ -sub Mode{ - my (@list) = @_; - - my($max, $val, $size, $count, @modes, $preval, $max_modes); - - $max_modes = scalar @list; - - if ($list[0] eq 'Max_Modes'){ - shift @list; - $max_modes = shift @list; - } - - @list = num_sort(@list); - - $count = -1; - $max = 0; - $preval = $list[0]; - - foreach $val (@list){ - if ($val == $preval){ - $count++; - } else{ - $count = 0; - } - if ($count > $max){ - @modes = (); - push @modes, $val; - $max = $count; - } elsif ($count == $max){ - push @modes, $val; - } - $preval = $val; - } - - if (scalar @modes > $max_modes){ - ("None"); - } else { - @modes; - } -}; - - -###################################### -#Name: isstring -#Input: A variable -#Output: A 1 if the variable is a string, 0 otherwise -###################################### -sub isstring { - my ($val) = @_; - - my($ans, $range); - - $range = join '', ('a'..'z','A'..'Z'); - - $ans = 0; - if (length $val == 0 || $val =~ /[$range]/){ - $ans = 1; - } - - $ans; -}; - -######################################################################################### -# -# Name : BoxPlot -# Input : $min = the minimum value in the dataset -# $q1 = the first quartile value -# $q2 = the second quartile (median) value -# $q3 = the third quartile value -# $max = the maximum value in the dataset -# Options = array of numbers to be plotted with tickmarks, -# perhaps interspersed with the options below -# in any order. -# -# Options :: The following may be passed AFTER the five numbers -# (together with the labels, without regard to order): -# -# (1) Use "wmin=>number" (no quotes really) to set the -# lower viewing window. Default is '$min' -# -# (2) Use "wmax=>number" (no quotes really) to set the -# upper viewing window. Default is '$max' -# -# (3) Use "horzlabels=>number" to set the approximate -# number of horizontal labels. Default is 10. -# -# (4) Use "axis=>0" to hide the horizontal axis. -# Default is to plot the axis. -# -# (5) Use labels such as 'a','b' and so on to label any -# of the five numbers with the desired string. -# Default is no labels. -# -# Output : $pic = a graphics object. -# -##################################################################################### -sub BoxPlot { - my ($min, $q1, $q2, $q3, $max, @optional_labels) = @_; - my $pic; - my ($horizontal_label_count, $plotaxis, @qlabels, @nlabels, $i, $skip, - $xlower, $xupper, $ylower, $yupper, $rough_step, $n, - $a, $rounded_winmin, @labels, $label_step, $label_count, @scaledfive, $axis_labels); - - #initialize to default - my $winmin = $min; - my $winmax = $max; - $horizontal_label_count = 10; - $plotaxis = 1; - - @qlabels = (); - @nlabels = (); - - while (scalar @optional_labels > 0){ - $val = shift @optional_labels; - if (isstring($val)){ - if ($val eq "winmin"){ - $winmin = shift @optional_labels; - } elsif($val eq "winmax") { - $winmax = shift @optional_labels; - } elsif($val eq "horzlabels") { - $horizontal_label_count = shift @optional_labels; - if ($horizontal_label_count == 0){ - $horizontal_label_count = 10; - } - } elsif($val eq "axis") { - $plotaxis = shift @optional_labels; - if ($plotaxis != 1) { - $plotaxis = 0; - } - } else { - push @qlabels, $val; - } - } else { - push @nlabels, $val; - } - } - - # error check - if ($winmin >= $winmax){die "the requested window settings are unusable";} - - # setup graph. The size is essentially fixed here. - $xlower = -20; - $xupper = 120; - $yupper = 20; - - if ($plotaxis == 1){ - $ylower = -10; - } else{ - $ylower = 0; - } - - $pic = init_graph($xlower, $ylower, $xupper, $yupper, 'pixels'=>[5*($xupper-$xlower),5*($yupper- $ylower)]); - - $winsize = $winmax - $winmin; - - # determine a suitable label step size. Approximately 10 'nice' labels are needed. - $n = 0; - - $rough_step = $winsize/$horizontal_label_count; - - if ($rough_step < 1){ - while ($rough_step < 1){ - $n--; - $rough_step = $rough_step * 10; - } - } elsif ($rough_step > 10){ - while ($rough_step > 10){ - $n++; - $rough_step = $rough_step / 10; - } - } - - # optimize the step size. The 'larger' is taken to avoid too many labels. - if ($rough_step <= 2){$a = 2;} - elsif ($rough_step > 5){$a = 10;} - else {$a = 5;} - - # actually set the 'label_step' - $label_step = $a * 10 ** $n; - if ($label_step == 0){die "Error determining the label step size";} - - # next, the upper and lower window values are adjusted to 'nice' numbers, if needed. - $rounded_winmin = int ($winmin / $label_step) * $label_step - $label_step; - - #Build the 'labels' array. There should be about 10 - $i = 0; - @labels = ($rounded_winmin); - do{ - $i++; - push @labels, $rounded_winmin + $label_step * $i; - } while ($rounded_winmin + $label_step * $i <= $winmax); - - $label_count = $i; - $rounded_winmax = $labels[$i]; - - #scale the input data for plotting - push @scaledfive, ($min - $winmin)*100/$winsize; - push @scaledfive, ($q1 - $winmin)*100/$winsize; - push @scaledfive, ($q2 - $winmin)*100/$winsize; - push @scaledfive, ($q3 - $winmin)*100/$winsize; - push @scaledfive, ($max - $winmin)*100/$winsize; - - - #add the horizontal labels and axis to the picture. Labeling starts at '$lower_window'. - if($plotaxis == 1){ - - $pic->moveTo($xlower,0); - $pic->lineTo($xupper,0,1); - - for ($i = 0; $i <= $label_count; $i++){ - $xval = ($labels[$i]-$winmin)*100/$winsize; - - if ($xval < 115 && $xval > -15 ){ - $pic->moveTo($xval,0); - $pic->lineTo($xval,-1,1); - $axislabels = new Label($xval, -3, $labels[$i], 'black', 'center', 'center'); - $pic->lb($axislabels); - } - } - } - - #add the optional labels to the picture. - for ($i = 0; $i < scalar @nlabels; $i++){ - $xval = ($nlabels[$i] - $winmin)*100 / $winsize; - - if ($nlabels[$i] >= $winmin && $nlabels[$i] <= $winmax){ - $pic->moveTo($xval,0); - $pic->lineTo($xval,1,1); - $axislabels = new Label($xval, 5, $nlabels[$i], 'black', 'center', 'center'); - $pic->lb($axislabels); - } - } - - $center = $yupper/2; - - #add the optional quartile labels - for ($i = 0; $i < scalar @qlabels && $i < 5; $i++){ - if ($qlabels[$i] ne ''){ - $pic->moveTo($scaledfive[$i],$center + 4); - $pic->lineTo($scaledfive[$i],$center + 5,1); - $axislabels = new Label($scaledfive[$i], $center + 8, $qlabels[$i], 'black', 'center', 'center'); - $pic->lb($axislabels); - } - } - - # draw data lines - $pic->stamps(closed_circle($scaledfive[0], $center, "black")); - $pic->moveTo($scaledfive[0], $center); - $pic->lineTo($scaledfive[1], $center,1); - $pic->moveTo($scaledfive[3], $center,1); - $pic->lineTo($scaledfive[4], $center,1); - $pic->stamps(closed_circle($scaledfive[4], $center, "black")); - - # draw quart boxes - $pic->moveTo($scaledfive[1],$center - 2); - $pic->lineTo($scaledfive[3],$center - 2,1); - $pic->lineTo($scaledfive[3],$center + 2,1); - $pic->lineTo($scaledfive[1],$center + 2,1); - $pic->lineTo($scaledfive[1],$center - 2,1); - - # draw median lines - $pic->moveTo($scaledfive[2], $center - 2); - $pic->lineTo($scaledfive[2], $center + 2,1); - $pic; -} - -################# -# Name : SpecialData -# Input : A string containing the type of data desired (Exp, Sym, Bi, Norm, Left, Right) -# and any optional input. -# Optional Input : count => n : the number of data values to generate (default = 500) -# mean => n : The mean for the normal, left or right distributions (default = 0) -# dev => n : The standard deviation for the normal, left or right distributions (default = 1) -# min => n : The minimum value for the data (default = 0) -# max => n : The maximum value for the data (default = 100) -# Output : A list of data values with the special properties. -################# - -sub SpecialData{ - my($type, %options) = @_; - - my($x, $sd, @dat, $max, $min, $mean, $count, $range); - - my ($B, $B1, $B2); - @dat = (); - - $type = lc $type; - - if (defined $options{count}){$count = $options{count};} - else {$count = 500;} - if (defined $options{mean}){$mean = $options{mean};} - else {$mean = 0;} - if (defined $options{dev}){$sd = $options{dev};} - else {$sd = 1;} - if (defined $options{min}){$min = $options{min};} - else {$min = 0} - if (defined $options{max}){$max = $options{max};} - else {$max = 100;} - - $range = $max - $min; - - if ($type eq 'exp'){ - $B = random (.7, 10, .05); - for ($i = 0; $i < $count; $i++){ - $val = random(0, .99, .01); - push @dat, -$B * ln(1 - $val); - } - } elsif ($type eq 'bi'){ - $B1 = random(0, $range / 5, 1); - $B2 = random(9 * $range / 10, $range,1); - - for ($i = 0; $i < $count/5; $i++){ - push @dat, $min + random($B1 - $range / 10, $B1 + $range/10, 1); - } - for ($i = 0; $i < 2*$count/5; $i++){ - push @dat, $min + random($B2 - $range / 10, $B2 + $range/10, 1); - } - for ($i = 0; $i < $count; $i++){ - push @dat, random($min,$max, 1); - } - } else { - for ($i = 0; $i < $count; $i++){ - $val = RandomNormalNumber(mean=>$mean, dev=>$sd); - push @dat, $val; - } - - if ($type eq 'norm' || $type eq 'sym'){ - @dat = num_sort(@dat); - $low = $dat[0]; - $high = $dat[$count - 1]; - $diff = $high - $low; - for ($i = 0; $i < $count; $i++){ - $dat[$i] = $range * ($dat[$i] - $low)/ $diff + $min; - } - } else { - for ($i = 0; $i < $count; $i++){ - $dat[$i] = $dat[$i]**2; - } - @dat = num_sort(@dat); - $low = $dat[0]; - $high = $dat[$count - 1]; - $diff = $high - $low; - for ($i = 0; $i < $count; $i++){ - $dat[$i] = $range * ($dat[$i] - $low) / $diff + $min; - if ($type eq 'left'){ - $dat[$i] = -$dat[$i] + $min + $max; - } - } - } - } - - @dat; -} - - -###################################### -#Name: PercentStemAndLeaf -#Input: List of percentage values (anything from 0 to 100). -#Optional Input: At beginning of list: -# all => 1,0 turns on or off the display of all columns of the -# table (useful if data is within a small range). -# sort => 1,0 turns on or off sorted data rows. -# single=>1,0 displays answers as a single cell in the table or -# determines the number from the data. -#Output: A list containing two strings. The first is a string that represents -# the html table to be displayed and the second is the TeX table to be -# displayed. -###################################### -sub PercentStemAndLeaf{ - my(@dat) = @_; - - my($i, $j, $s1, $s2, $all, $low, $max, $num, $row, $cols, $high, @list, $sort, $var1, $var2, , $tex_table, $html_table); - - while (isstring($dat[0]) == 1){ - $var1 = shift @dat; - if ($var1 eq 'all'){ - $all = shift @dat; - } elsif($var1 eq 'sort'){ - $sort = shift @dat; - } elsif ($var1 eq 'single'){ - if (shift @dat == 0){ - $cols = -1; - } - } else { - shift @dat; - } - } - - $all = 1 unless defined ($all); - $sort = 1 unless defined ($sort); - $cols = 3 unless defined ($cols); - - for ($i = 0; $i < scalar @dat; $i++){ - $num = int($dat[$i] / 10); - $row = "row$num"; - push @$row, $dat[$i] % 10; - } - - $max = 0; - for ($i = 0; $i <= 10; $i++){ - $j = 10 - $i; - $var1 = "row$i"; - $var2 = "row$j"; - $s1 = scalar @$var1; - $s2 = scalar @$var2; - if ($s1 != 0){$high = $i;} - if ($s2 != 0){$low = $j;} - if ($s1 > $max){$max = $s1;} - } - if ($all){ - $low = 0; - $high = 10; - } - if ($cols == -1){ - $cols = $max; - } - $tex_cols = $cols - 1; - - $align = 'r|'; - for ($i = 0; $i < $tex_cols - 1; $i++){ - $align .= 'l'; - } - - $html_table = begintable($cols); - $tex_table = "\\begin{tabular}{$align}"; - - for ($i = $high; $i >= $low; $i--){ - $var1 = "row$i"; - - @list = @$var1; - if ($sort){ - @list = num_sort(@list); - } - if ($cols == 3){ - @list = (join '', @list); - } - $html_table .= row($i, ' ', @list); - $tex_table .= "\n $i"; - for ($j = 1; $j < $tex_cols; $j++){ - if (scalar @list > 0){ - $var2 = shift @list; - } else { - $var2 = ''; - } - $tex_table .= "\&$var2 "; - } - $tex_table .= "\\\\\n"; - } - $html_table .= endtable(); - $tex_table .= '\end{tabular}'; - ($html_table, $tex_table); -} - - -### -# Name : RoundStep -### -sub RoundStep{ - my ($num, $val) = @_; - my ($remainder); - #qualify the input - $minus = 0; - if($num < 0){$num = -$num; $minus = 1;} - $val = abs ($val); - $remainder = ($num / $val - int ($num / $val)) * $val; - if ($remainder < ($val / 2)){ - $num = int ($num / $val) * $val; - }else{ - $num = (int ($num / $val) + 1) * $val; - } - if ($minus){$num = -$num;} - $num; -} - - -### -# Name : CeilingStep -### -sub CeilingStep{ - my ($num, $val) = @_; - #qualify the input - $minus = 0; - if($num < 0){$num = -$num; $minus = 1;} - $val = abs ($val); - $num = (int ($num / $val) + 1) * $val; - if ($minus){$num = -$num;} - $num; -} - - -### -# Name : FloorStep -### -sub FloorStep{ - my ($num, $val) = @_; - #qualify the input - $minus = 0; - if($num < 0){$num = -$num; $minus = 1;} - $val = abs ($val); - $num = int ($num / $val) * $val; - if ($minus){$num = -$num;} - $num; -} - - -############################################################################# -# Name : Histogram -# Input : @data = "the data" -# Options = may be interspersed in the data in any order. -# -# Options :: The following may be passed AFTER the standard input -# above (without regard to order): -# -# (1) Use "labelcells=>1" (no quotes really) to include -# frequency labels at the top of each cell. -# The default is no labels. -# -# (2) Use "labelcount=>number" (no quotes really) to -# set an approximate number of labels to use on the -# vertical axis. Default is 5. -# -# (3) Use "title=>string" to add a title, centered at -# the top. -# -# (4) Use "axislabel=>string" to add an axis label, -# centered at the bottom. -# -# (5) Use "bins=>number" to specify approximately how -# many bins should be used. Default is 10. -# -# Note : any extra numbers input at the end will be -# taken as data. -# -# Output : $pic = a graphics object -############################################################################# -sub Histogram { - my (@data) = @_; - my $pic; - my ($key, $cell_labels, $target_label_count, $xlower, $xupper, $ylower, - $yupper, @division_values, $frequency, $j, @frequencies, $maxfreq, - @scaled_division_values, $division_count, $axis_labels, $rough_step, - $n, $step, $scaled_step, $this_label, $i, $bins); - $key = 0; - - # check for options - for ($i = 0; $i < scalar @data; $i++){ - $cut = 0; - $val = $data[$i]; - if ($val eq 'labelcells'){ - if ($data[$i+1] != 0){$cell_labels = 1;} - $cut = 1; - } elsif ($val eq 'labelcount'){ - $target_label_count = $data[$i+1]; - $cut = 1; - } elsif ($val eq 'title'){ - $title = $data[$i+1]; - $cut = 1; - } elsif ($val eq 'axislabel'){ - $axislabel = $data[$i+1]; - $cut = 1; - } elsif ($val eq 'bins'){ - $bins = $data [$i+1]; - $cut = 1; - } - - if ($cut == 1){ - splice (@data, $i, 2); - $i--; - } - } - - $cell_labels = 0 unless defined ($cell_labels); - $target_label_count = 5 unless defined ($target_label_count); - $title = '' unless defined ($title); - $axislabel = '' unless defined ($axislabel); - $bins = 10 unless defined ($bins); - - # setup graph. The size is essentially fixed here. - $window_size = 5; - $xlower = -20; - $xupper = 120; - $ylower = -20; - $yupper = 70; - $pic = init_graph($xlower, $ylower, $xupper, $yupper, 'pixels'=>[$window_size*($xupper-$xlower),$window_size*($yupper- $ylower)]); - # find the data range - $min = min (@data); - $max = max (@data); - $rough_step = ($max - $min) / $bins; - #find a 'nice' width - $n = 0; - - if(int($rough_step) > 10){ - while ($rough_step > 10){ - $n++; - $rough_step = $rough_step / 10; - } - } elsif(int($rough_step) < 1){ - while ($rough_step < 1){ - $n--; - $rough_step = $rough_step * 10; - } - } - - if ($rough_step <= 2){$a = 2;} - elsif ($rough_step > 2 && $rough_step <= 5){$a = 5;} - else {$a = 10;} - $width = $a * 10 ** $n; - # round - $min = FloorStep($min,$width); - $max = CeilingStep($max,$width); - # setup the cell division values - @division_values = ($min); - $i = 0; - while ($min + $width * $i < $max){ - $i++; - push @division_values, $min + $width * $i; - } - $division_count = $i; - # calculate the frequencies for each cell - $maxfreq = 1; - for ($i = 0; $i < $division_count; $i++){ - #determine the current frequency - $j = 0; - $frequency = 0; - while ($j < scalar @data){ - if ($data[$j] >= $division_values[$i] && $data[$j] < $division_values[$i+1]){ - $frequency++; - } - $j++; - } - push @frequencies, $frequency; - if ($frequencies[$i] > $maxfreq){ - $maxfreq = $frequencies[$i]; - } - } - #scaling transformations. The given $min and $max are used for this. - $i = 0; - while ($i <= $division_count){ - $scaled_division_values[$i] = ($division_values[$i]-$min)*100/($max-$min); - if ($i != $division_count){ - $scaled_frequencies[$i] = ($frequencies[$i])*50/($maxfreq); - } - $i++; - } - $pic->moveTo($scaled_division_values[0],0); - $pic->lineTo($scaled_division_values[$division_count],0,1); - $pic->lineTo($scaled_division_values[$division_count],55,1); - $pic->lineTo($scaled_division_values[0],55,1); - $pic->lineTo($scaled_division_values[0],0,1); - #label the division points, and draw the cells - $i = 0; - while ($i < $division_count){ - $pic->moveTo($scaled_division_values[$i],0); - $axislabels = new Label($scaled_division_values[$i], -3, $division_values[$i], 'black', 'center', 'center'); - $pic->lb($axislabels); - $pic->lineTo($scaled_division_values[$i],$scaled_frequencies[$i],1); - $pic->lineTo($scaled_division_values[$i+1],$scaled_frequencies[$i],1); - $pic->lineTo($scaled_division_values[$i+1],0,1); - if($frequencies[$i] > 0){ - $pic->fillRegion([($scaled_division_values[$i]+$scaled_division_values[$i+1])/2,0.000001,'gray']); - } - #add the optional cell labels - if ($cell_labels == 1){ - if ($frequencies[$i] > 0){ - $axislabels = new Label(($scaled_division_values[$i]+$scaled_division_values[$i+1])/2, $scaled_frequencies[$i]+3, $frequencies[$i], 'black', 'center', 'center'); - $pic->lb($axislabels); - } - } - $i++; - } - # add the last division label if it is not too far to the right - if ($scaled_division_values[$division_count] <= 110){ - $pic->moveTo($scaled_division_values[$division_count],0); - $axislabels = new Label($scaled_division_values[$division_count], -3, $division_values[$division_count], 'black', 'center', 'center'); - $pic->lb($axislabels); - } - #find 'nice' frequency axis labels - $n = 0; - $rough_step = $maxfreq / $target_label_count; - if(int($rough_step) > 10){ - while ($rough_step > 10){ - $n++; - $rough_step = $rough_step / 10; - } - } elsif(int($rough_step) < 1){ - $rough_step = -1; - } - - if ($rough_step < 0){$a = 1;} - elsif ($rough_step > 0 && $rough_step <= 2){$a = 2;} - elsif ($rough_step > 2 && $rough_step <= 5){$a = 5;} - else {$a = 10;} - - $step = $a * 10 ** $n; - $scaled_step = $step * 50 / $maxfreq; - # add the tickmarks and labels - $i = 1; - while ($i <= $target_label_count && $scaled_step * $i < 55){ - #draw tick marks - $pic->moveTo($scaled_division_values[0],$scaled_step*$i); - $pic->lineTo($scaled_division_values[0]-1,$scaled_step*$i); - #create this label - $this_label = $step*$i; - $axislabels = new Label($scaled_division_values[0]-3, $scaled_step*$i + 1.25, $this_label, 'black', 'center', 'center'); - $pic->lb($axislabels); - $i++; - } - # add the title, if needed. - if ($title ne ''){ - $axislabels = new Label(50, ($yupper + 50)/2, $title, 'black', 'center', 'center'); - $pic->lb($axislabels); - } - # add bottom label, if needed. - if ($axislabel ne ''){ - $axislabels = new Label(50, $ylower/2, $axislabel, 'black', 'center', 'center'); - $pic->lb($axislabels); - } - $pic; -} - - -################################ -#Name: RandomNormalNumber -#Input: None required -#Optional Input: 'mean' => the mean of the normal distribution being used. (default = 0) -# 'dev' => the standard deviation of the normal distribution being used. (default = 1) -#Output: A number that is related to the standard normal curve with the given mean and deviation. -################################ -sub RandomNormalNumber{ - my (@options) = @_; - - my($dev, $val, $mean, $norm_num); - - while (scalar @options > 0){ - $val = shift @options; - if ($val eq 'mean'){ - $mean = shift @options; - } elsif ($val eq 'dev'){ - $dev = shift @options; - } else { - shift @options; - } - } - $mean = 0 unless defined ($mean); - $dev = 1 unless defined ($dev); - - $num = random(.01,.99,.01); - if ($num >= .5){ - $norm_num = normal_distr($num-.5, mean=>$mean, deviation=>$dev); - } else { - $norm_num = 2*$mean - normal_distr(.5 - $num, mean=>$mean, deviation=>$dev); - } - $norm_num; -} - -################################ -#Name: Scatterplot -#Input: Correlation coefficient -#Optional Input: 'count'=> Number of data points to be calculated. -# 'mean' => the mean of the normal distribution being used. -# 'dev' => the standard deviation of the normal distribution being used. -# 'slope'=> the slope of the scatterplot. -#Output: A picture of the scatterplot with given slope and correlation coefficient. -################################ -sub Scatterplot{ - my ($corr, @options) = @_; - - my ($i, $y1, $y2, $dev, $max, $min, $pic, $val, $mean, $circle, $count, @x_vals, @y_vals); - - while (scalar @options > 0 ){ - $val = shift @options; - if ($val eq 'count'){ - $count = shift @options; - } elsif ($val eq 'mean'){ - $mean = shift @options; - } elsif ($val eq 'dev'){ - $dev = shift @options; - } elsif ($val eq 'slope'){ - $slope = shift @options; - } else { - shift @options; - } - } - - $count = 100 unless defined ($count); - $mean = 0 unless defined ($mean); - $dev = 1 unless defined ($dev); - $slope = 1 unless defined ($slope); - - $min = 0; - $max = 2; - for ($i = 0; $i < $count; $i++){ - $val = RandomNormalNumber('mean'=>$mean, 'dev'=>$dev); - $y1 = RandomNormalNumber('mean'=>$mean, 'dev'=>$dev); - $y2 = sqrt(1 - $corr**2) * $val + $slope * $corr * $y1; - if ($y1 < $min){ - $min = $y1; - } elsif ($y1 > $max){ - $max = $y1; - } - if ($y2 < $min){ - $min = $y2; - } elsif ($y2 > $max){ - $max = $y2; - } - push @x_vals, $y1; - push @y_vals, $y2; - } - - $min = $min - 1; - $max = $max + 1; - $pic=""; - $pic = init_graph($min, $min, $max, $max, 'pixels'=>[200,200]); - for ($i = 0; $i < $count; $i++){ - $circle = new Circle($x_vals[$i], $y_vals[$i], 2, 'black', 'none'); - $pic->stamps($circle); - } - $pic; -} - -###################################### -#Name: PieChart -#Input: List of values as: decimals - which should add to 1. If it -# sums to less than one, the function will make up the difference. -# or numbers > 1 - will be treated as numerators for fractions. The sum of the -# numbers will be the denominator and fractions will be placed on the -# circle plot. -#Optional Input: percent => 0, 1 : Off or on display of percentage values (regardless of input manner) (default = 0) -# labels => 1, 0 : On or off label display. (default = 1) -# values => 1, 0 : On or off value display. (default = 1) -#Output: A picture of the corresponding circle plot -###################################### -sub PieChart{ - my @list = @_; - - my($i, @R, @G, @B, $v1, $v2, $Bot, $lab, $pic, $Top, $val, $mark, $angle, $denom, - $total, $value, $labels, $radius, @values, $percent, $lett_adj, $line_adj); - - while (isstring($list[0])){ - $val = shift @list; - if ($val eq 'percent'){ - $percent = shift @list; - } elsif ($val eq 'labels'){ - $labels = shift @list; - } elsif ($val eq 'values'){ - $value = shift @list; - } else { - shift @list; - } - } - $labels = 1 unless defined ($labels); - $value = 1 unless defined ($values); - $percent = 0 unless defined ($percent); - - $total = 0; - foreach $val (@list){ - $total += $val; - } - - if ($total <= 0){return "Incorrect values"} - if ($total > 2){ - $denom = $total; - } elsif ($total < 1 ){ - push @list, 1 - $total; - $denom = 1; - } else {$denom = 1;} - - foreach $val (@list){ - if ($percent == 1 && $total < 2){ - $lab = 100 * $val; - push @values, "$lab%"; - } elsif ($percent == 1){ - $lab = 100 * $val / $total; - push @values, "$lab%"; - } elsif ($denom != 1) { - $i = gcd($val, $total); - $v1 = $val/$i; - $v2 = $denom/$i; - push @values, "$v1/$v2"; - } else { - push @values, $val; - } - push @radlist, $val / $total * 2 * $PI; - } - - $radius = 25; - $v1 = -3/2 * $radius; - $v2 = 3/2 * $radius; - $val = $radius ** 2; - $pic = init_graph($v1, $v1, $v2, $v2, 'pixels'=>[300,300]); - $Top = FEQ("sqrt($val-(x)^2) for x in <-$radius,$radius> using color:blue and weight:2"); - $Btm = FEQ("-sqrt($val-(x)^2) for x in <-$radius,$radius> using color:blue and weight:2"); - plot_functions($pic, $Top, $Btm); - - $angle = 0; - $total = scalar @list; - $dc = int (255 / ($total + 1)); - for ($i = 1; $i <= $total; $i++){ - push @R, 255 - $i * $dc; - push @B, $i * $dc; - push @G, 0; - } - $i = 0; - foreach $val (@radlist){ - $pic->moveTo(0,0); - $pic->lineTo($radius * cos($angle), $radius * sin($angle), 1); - $angle += $val; - $mark = $angle - $val / 2; - $v1 = $radius * cos ($mark); - $v2 = $radius * sin ($mark); - $lett_adj = 2/3; - $line_adj = 7/8; - $pic->new_color("col$i", $R[$i], $G[$i], $B[$i]); - $pic->fillRegion([$v1 / 2, $v2 / 2, "col$i"]); - if ($value == 1){ - $lab = new Label(1.3 * $v1, 1.3 * $v2, "$values[$i]", 'black', 'center', 'center'); - $pic->lb($lab); - $pic->moveTo($line_adj * $v1, $line_adj * $v2); - $pic->lineTo(1.1 * $v1, 1.1 * $v2,1); - } - if ($labels == 1){ - $lab = new Label($lett_adj * $v1, $lett_adj * $v2, $ALPHABET[$i % 26], 'yellow', 'center', 'center'); - $pic->lb($lab); - } - $i++; - } - - $pic; -} - -########################################################################################################### -# -# Name : DrawNormalDist -# -# Input : lower_z_point, upper_z_point (use -INF or INF for infinite values) -# -# Optional Input : After first two points. -# $lower_label = the lower label to use. Default is 'a' -# $upper_label = the upper label to use. Default is 'b' -# (if only want upper label, must input a lower label) -# outside => n - 0, 1 to shade between or outside the numbers Default = 0; -# title => 'title string' - will display a title if desired. -# mean => n - will display the given mean (default display nothing) -# -########################################################################################################### -sub DrawNormalDist{ - my ($low, $high, @opt) = @_; - - my ($x1, $x2, $y1, $y2, $pic, $x_val, $y_lab, $y_min, $y_tit, $y_val, $scale, - $margin, $xlower, $xupper, $ylower, $yupper, $low_pt, $high_pt, $title, - $label, $outside, $trace, $x_slope, $x_int, $y_slope, $y_int, @pt_labels, - $mean_val); - - ########################### - # begin intialize and setup - # initialize to default - # check for options - while (@opt){ - $val = shift @opt; - if ($val eq 'outside'){$outside = shift @opt;} - elsif ($val eq 'title'){$title = shift @opt;} - elsif ($val eq 'mean'){$mean_val = shift @opt;} - else {push @pt_labels, $val;} - } - if (scalar @pt_labels == 0){push @pt_labels, 'a', 'b';} - elsif (scalar @pt_labels == 1){push @pt_labels, 'b';} - $outside = 0 unless defined ($outside); - $title = '' unless defined ($title); - - if ($low eq '-INF'){$low_pt = -3.5;} - else {$low_pt = $low;} - if ($high eq 'INF'){$high_pt = 3.5;} - else {$high_pt = $high;} - # end initialize and setup - ########################## - - # setup the graph. The size is essentially fixed here. - $scale = 4; - $margin = 5; - $xlower = - $margin; - $xupper = 100 + $margin; - $ylower = -$margin; - $yupper = 50 + $margin; - $x1 = $xlower + $margin; - $x2 = $xupper - $margin; - $y1 = $ylower + $margin + 2.5; - $y2 = $yupper - $margin + 2.5; - - $pic = init_graph($xlower, $ylower, $xupper, $yupper, 'pixels'=>[$scale * ($xupper-$xlower), $scale * ($yupper-$ylower)]); -# $pic->moveTo($x1, $y1); -# $pic->lineTo($x2, $y1, 1); -# $pic->lineTo($x2, $y2, 1); -# $pic->lineTo($x1, $y2, 1); -# $pic->lineTo($x1, $y1, 1); - - $x_slope = ($x2 - $x1 - 2 * $margin)/6; - $x_int = $x1 + $margin + $x_slope * 3; - $y_slope = ($y2 - $y1 - 2 * $margin) * sqrt(2 * $PI); - $y_int = $y1 + $margin; - $y_min = $y1 + $margin; - - $trace = FEQ("1/sqrt(2*$PI)*exp(-((x-$x_int)/$x_slope)^2/2)*$y_slope + $y_int for x in <$x1, $x2> using color:black and weight:1"); - plot_functions($pic, $trace); - - $pic->moveTo($x1, $y_min); - $pic->lineTo($x2, $y_min); - - # add labels to the independent axis - $y_lab = $y1 + 3 * $margin / 4; - - $x_val = $low_pt * $x_slope + $x_int; - $y_val = 1/sqrt(2 * $PI) * exp(-($low_pt)**2/2) * $y_slope + $y_int; - $label = new Label($x_val, $y_lab, $pt_labels[0],'black','center','center'); - $pic->lb($label); - $pic->moveTo($x_val, $y_min); - $pic->lineTo($x_val, $y_min - 1, 1); - $pic->moveTo($x_val, $y_min); - $pic->lineTo($x_val, $y_val, 'gray'); - - $x_val = $high_pt * $x_slope + $x_int; - $y_val = 1/sqrt(2*$PI)*exp(-($high_pt)**2/2) * $y_slope + $y_int; - $label = new Label($x_val, $y_lab, $pt_labels[1],'black','center','center'); - $pic->lb($label); - $pic->moveTo($x_val, $y_min); - $pic->lineTo($x_val, $y_min - 1, 1); - $pic->moveTo($x_val, $y_min); - $pic->lineTo($x_val, $y_val, 'gray'); - - if (defined $mean_val){ - $x_val = ($xupper + $xlower) / 2; - $pic->moveTo($x_val, $y_min); - $pic->lineTo($x_val, $y_min - 1, 1); - $label = new Label($x_val, $y_lab, $mean_val,'black','center','center'); - $pic->lb($label); - } - if ($title ne ''){ - $y_tit = $y_lab - $margin - 1; - $y_val = $y_lab - $margin + 1; - $x_val = ($xupper + $xlower) / 2; - $label = new Label($x_val, $y_tit, $title,'black','center','center'); - $pic->lb($label); - } - - # shading - if ($outside == 1){ - $pic->fillRegion([($low_pt - 3.25)/2 * $x_slope + $x_int, $y_min + 0.0001, 'gray']); - $pic->fillRegion([($high_pt + 3.25)/2 * $x_slope + $x_int, $y_min + 0.0001, 'gray']); - }elsif ($high_pt - $low_pt > 0.05){ - $pic->fillRegion([($high_pt + $low_pt)/2 * $x_slope + $x_int, $y_min + 0.0001, 'gray']); - } - $pic; -} - -1; \ No newline at end of file diff --git a/OpenProblemLibrary/macros/NAU/readme.txt b/OpenProblemLibrary/macros/NAU/readme.txt deleted file mode 100755 index 5d8a759af6..0000000000 --- a/OpenProblemLibrary/macros/NAU/readme.txt +++ /dev/null @@ -1,9 +0,0 @@ -unionTables.pl is a version that eliminates some warnings in the -graph theory problems. It defines a few uninitialized variables. -Here is an example: - -$bhtml=$ehtml=''; - -This may not be the best solution for this issue. - -Nandor \ No newline at end of file diff --git a/OpenProblemLibrary/macros/PCC/PCCfactor.pl b/OpenProblemLibrary/macros/PCC/PCCfactor.pl deleted file mode 100644 index f22a7c38f8..0000000000 --- a/OpenProblemLibrary/macros/PCC/PCCfactor.pl +++ /dev/null @@ -1,428 +0,0 @@ -# The subroutine factoringMethodsis takes arguments -# ($a[0], $a[1], $b[0], $b[1], $var[0], $var[1]) -# corresponding to a factored polynomial -# (a[0] $var[0]+b[0] $var[1])(a[1] $var[0]+b[1] $var[1]) -# @a and @b should be integers, and doctored in the problem code so -# that gcd(@a) and gcd(@b) equal 1. -# @var should be variables that have been added to the context. -# $var[1] is optional. - -# The output includes various methods to factor the polynomial, -# including: -# * number sense method -# * formula method (perfect squares and difference of squares) -# * ac method -# * guess-and-check with generic rectangles method - -# If $var[1] is omitted and $var[0] is something like 'xy', then both -# letters should be variables added to the context, and the expanded -# (unfactored) versions of polynomials will be shown like -# Ax^2y^2 + Bxy + C -# instead of -# A(xy)^2 + Bxy + C -# and the factorization is -# (a[0] $var[2] $var[3] + b[0])(a[1] $var[2] $var[3] + b[1]) -# where $var[2] and $var[3] are the individual letters form the given $var[0] - -loadMacros( - "MathObjects.pl", - "PGgraphmacros.pl" -); - - -sub factoringMethods { - my (@a,@b,@var); - ($a[0],$b[0],$a[1],$b[1],$var[0],$var[1]) = @_; - if (!(defined($var[1]))) {$var[1] = 1;} - - my $outputString; - - my @co = ($a[0]*$a[1], $a[0]*$b[1] + $b[0]*$a[1], $b[0]*$b[1]); - my @sq = (sqrt(abs($co[0])), ($co[1]>=0)?'+':'-', sqrt(abs($co[2]))); - my $ac = $co[0]*$co[2]; - my @acbreakdown = ($a[0]*$b[1], $b[0]*$a[1]); - - if (length($var[0])==2) { - $var[2] = substr $var[0], 0, 1; - $var[3] = substr $var[0], 1, 1; - $var[0]=1; - } else { - $var[2] = 1; - $var[3] = 1; - } - - my $expanded = Formula("$co[0]*$var[0]^2*$var[2]^2*$var[3]^2 + $co[1]*$var[0]*$var[1]*$var[2]*$var[3] + $co[2]*$var[1]^2") -> reduce; - my @factor = (Formula("$a[0]*$var[0]*$var[2]*$var[3] + $b[0]*$var[1]") -> reduce, Formula("$a[1]*$var[0]*$var[2]*$var[3] + $b[1]*$var[1]") -> reduce); - - my $factored = Formula("($factor[0])*($factor[1])") -> reduce; - my $factoredSquare = "", $factoredSquareOutput=""; - if ( ($a[0]==$a[1])&&($b[0]==$b[1]) ) { - $factoredSquare = Formula("($factor[0])**2") -> reduce; - $factoredSquareOutput = '\newline &='.$factoredSquare->TeX; - }; - - $outputString .= 'We will factor \('.$expanded->TeX.'\) where \(a='.$co[0].'\), \(b='.$co[1].'\), and \(c='.$co[2].'\).'.$PAR; - - ### Attach an explanation if the quadratic is a perfect square - if ( (($co[1])**2 - 4*$co[0]*$co[2] == 0) and - ($co[0] > 0) and - ($co[2] > 0) and - ($sq[0]== int($sq[0])) and - ($sq[2] == int($sq[2])) - ){ - my @term = (Formula("$sq[0]*$var[0]*$var[2]*$var[3]")->reduce, - Formula("$sq[0]*$var[0]*$var[2]*$var[3]")->reduce, - Formula("$sq[2]*$var[1]")->reduce, - Formula("$sq[2]*$var[1]")->reduce); - $outputString .= $BBOLD. - 'Perfect Square Formula: '. - $EBOLD. - 'The formula for perfect square trinomials is \[(a \pm b)^2=a^2 \pm 2ab + b^2\] Note that the polynomial \('. - $expanded->TeX. - '\) matches the formula'. - "'". - 's right side, and we have: \[\begin{aligned}[t]'. - $expanded->TeX. - '&=\left('. - $term[0]->TeX. - '\right)^2'. - $sq[1]. - '2\left('. - $term[1]->TeX. - '\right)\left('. - $term[2]->TeX. - '\right)+\left('. - $term[3]->TeX. - '\right)^2\newline &='. - $factored->TeX. - $factoredSquareOutput. - '\end{aligned}\]'. - $PAR; - - }; - - ### Attach an explanation if the quadratic is a difference of squares - if ( ($co [1] == 0) and - ($co [0] > 0) and - ($co [2] < 0) and - ($sq [0] == int($sq[0])) and - ($sq [2] == int($sq[2])) - ){ - my @term = (Formula("$sq[0]*$var[0]*$var[2]*$var[3]")->reduce, - Formula("$sq[2]*$var[1]")->reduce); - $outputString .= $BBOLD. - 'Difference of Squares Formula: '. - $EBOLD. - 'The formula for a difference of squares is \[a^2-b^2 = (a+b)(a-b)\] Note that the polynomial \('. - $expanded->TeX. - '\) matches the formula'. - "'". - 's right side, and we have: \[\begin{aligned}[t]'. - $expanded->TeX. - '&=\left('. - $term[0]->TeX. - '\right)^2-\left('. - $term[1]->TeX. - '\right)^2\newline &='. - $factored->TeX. - '\end{aligned}\]'. - $PAR; - - }; - - ### Attach an explanation if one can factor mentally. - if ($co[0] == 1) - {$outputString .= $BBOLD. - 'Mental Method: '. - $EBOLD. - 'When we factor a polynomial in the form of \(x^2+bx+c\), \(x^2+bxy+cy^2\), or \(x^2y^2+bxy+c\) we can use mental math to factor. We need to find two numbers whose product is \(c\), and whose sum is \(b\).'. - $PAR. - 'For \('. - $expanded->TeX. - '\), since the product of \('. - $b[0]. - '\) and \('. - $b[1]. - '\) is \('. - $co[2]. - '\), and the sum of \('. - $b[0]. - '\) and \('. - $b[1]. - '\) is \('. - $co[1]. - '\), the factorization is \[\begin{aligned}[t]'. - $expanded->TeX. - '&='. - $factored->TeX. - $factoredSquareOutput. - '\end{aligned}\]'. - $PAR; - - }; - - ### Attach an explanation of the AC method. - my $sqac = int(sqrt(abs($ac))); - my @smallfactor; - my @largefactor; - for my $i (1..$sqac) { - my $j = $ac/$i; - if ($j == int($j)) {push(@smallfactor,$i,-$i); push(@largefactor,$j,-$j);}; - }; - my $factorPairsOutput = ""; - for my $i (0..$#smallfactor) { - $factorPairsOutput .= $smallfactor[$i].'('.$largefactor[$i].')'; - if ($i%4 == 3) {$factorPairsOutput .= '\newline';} else {$factorPairsOutput .= '&';} - }; - - my @term = (Formula("$co[0]*$var[0]^2*$var[2]^2*$var[3]^2")->reduce, - Formula("$acbreakdown[0]*$var[0]*$var[1]*$var[2]*$var[3]")->reduce, - Formula("$acbreakdown[1]*$var[0]*$var[1]*$var[2]*$var[3]")->reduce, - Formula("$co[2]*$var[1]^2")->reduce, - Formula("$a[0]*$var[0]*$var[2]*$var[3]")->reduce, - Formula("$b[0]*$var[1]")->reduce, - ); - $acStep1 = Compute("$term[0]+$term[1]+$term[2]+$term[3]")->reduce; - $acStep2 = Compute("$term[4]*($factor[1])+$term[5]*($factor[1])")->reduce; - $outputString .= $BBOLD. - 'AC Method: '. - $EBOLD. - 'In this method, we first calculate the product of \(a\) and \(c\) in \(ax^2+bx+c\). For this problem, we have \(('. - $co[0]. - ') \cdot ('. - $co[2]. - ') = '. - $ac. - '\).'. - $PAR. - 'The number \('. - $ac. - '\) can be factored into the product of two numbers in the following ways: \[\begin{array}{lll}'. - $factorPairsOutput. - '\end{array}\] Looking at the corresponding sums, we want a sum of \('. - $co[1]. - '\), and this sum happens with \('. - (($acbreakdown[0]>0) ? '' : '('). - $acbreakdown[0]. - (($acbreakdown[0]>0) ? '' : ')'). - '+'. - (($acbreakdown[1]>0) ? '' : '('). - $acbreakdown[1]. - (($acbreakdown[1]>0) ? '' : ')'). - '='. - $co[1]. - '\) So we write \[\begin{aligned}[t]'. - $expanded->TeX. - '&='. - $acStep1->TeX. - '\newline &='. - $acStep2->TeX. - '\newline &='. - $factored->TeX. - $factoredSquareOutput. - '\end{aligned}\]'; - - ### Explanation that uses rectangle diagrams - $outputString .= $BBOLD. - 'Generic Rectangles Method: '. - $EBOLD. - 'First, we set up generic rectangles by putting the first term and third term in \('. - $expanded->TeX. - '\) into the top left and bottom right rectangles: '. - $PAR; - - #### Make two pictures ### - my $xmin = 0; #The viewing window - my $xmax = 10; - my $ymin = 0; - my $ymax = 10; - @picture = ( ); - my $xStart = 2; - my $xEnd = 8; - my $yStart = 2; - my $yEnd = 8; - my @x = ($xStart,$xEnd); - my @y = ($yStart,$yEnd); - my $gap = $ymax/100; - - my $box1, $box2, $box3, $box4, $box1Up, $box1Left, $box2Up, $box3Left; - - for (my $i=0;$i<=1;$i++) { - $picture[$i] = init_graph($xmin,$ymin,$xmax,$ymax, - pixels=>[400,400]); - - $picture[$i]->moveTo($x[0],$y[0]); - $picture[$i]->lineTo($x[1],$y[0], blue,3); - $picture[$i]->lineTo($x[1],$y[1], blue,3); - $picture[$i]->lineTo($x[0],$y[1], blue,3); - $picture[$i]->lineTo($x[0],$y[0], blue,3); - - $picture[$i]->moveTo($x[0],$ymax/2); - $picture[$i]->lineTo($x[1],$ymax/2, blue,2); - - $picture[$i]->moveTo($xmax/2,$y[0]); - $picture[$i]->lineTo($xmax/2,$y[1], blue,2); - - $squareLength = $x[1]-$x[0]; - if ($var[0] ne "1") { - $box1 = ($co[0]==1) ? "$var[0]^2" : "$co[0]$var[0]^2"; - } else { - $box1 = ($co[0]==1) ? "$var[2]^2$var[3]^2" : "$co[0]$var[2]^2$var[3]^2"; - } - $picture[$i]->lb( new Label($x[0]+$squareLength/4, $y[1]-$squareLength/4,"$box1",'black','center','middle')); - - my $box4Num = ""; - my $box4Var = ""; - if ($var[1] ne "1") { - if ($co[2]==1) {$box4Num = "";} - elsif ($co[2]==-1) {$box4Num = "-";} - else {$box4Num = "$co[2]";} - $box4Var = "$var[1]^2"; - } else { - if ($co[2]==1) {$box4Num = "1";} - elsif ($co[2]==-1) {$box4Num = "-1";} - else {$box4Num = "$co[2]";} - } - $box4 = $box4Num.$box4Var; - $picture[$i]->lb( new Label($x[1]-$squareLength/4,$y[0]+$squareLength/4,"$box4",'black','center','middle')); - } - - if ($acbreakdown[1]==1) { - if ($var[0] ne "1") { - $box2 = ($var[1] eq "1") ? "$var[0]" : "$var[0]$var[1]"; - } else { - $box2 = "$var[2]$var[3]"; - } - } elsif ($acbreakdown[1]==-1) { - if ($var[0] ne "1") { - $box2 = ($var[1] eq "1") ? "-$var[0]" : "-$var[0]$var[1]"; - } else { - $box2 = "-$var[2]$var[3]"; - } - } else { - if ($var[0] ne "1") { - $box2 = ($var[1] eq "1") ? "$acbreakdown[1]$var[0]" : "$acbreakdown[1]$var[0]$var[1]"; - } else { - $box2 = "$acbreakdown[1]$var[2]$var[3]"; - } - } - $picture[1]->lb( new Label($x[1]-$squareLength/4,$y[1]-$squareLength/4,"$box2",'black','center','middle')); - - if ($acbreakdown[0]==1) { - if ($var[0] ne "1") { - $box3 = ($var[1] eq "1") ? "$var[0]" : "$var[0]$var[1]"; - } else { - $box3 = "$var[2]$var[3]"; - } - } elsif ($acbreakdown[0]==-1) { - if ($var[0] ne "1") { - $box3 = ($var[1] eq "1") ? "-$var[0]" : "-$var[0]$var[1]"; - } else { - $box3 = "-$var[2]$var[3]"; - } - } else { - if ($var[0] ne "1") { - $box3 = ($var[1] eq "1") ? "$acbreakdown[0]$var[0]" : "$acbreakdown[0]$var[0]$var[1]"; - } else { - $box3 = "$acbreakdown[0]$var[2]$var[3]"; - } - } - $picture[1]->lb( new Label($x[0]+$squareLength/4,$y[0]+$squareLength/4,"$box3",'black','center','middle')); - - if ($b[0]==1) {$box2Up = ($var[1] eq "1") ? "$b[0]" : "$var[1]";} - elsif ($b[0]==-1) {$box2Up = ($var[1] eq "1") ? "$b[0]" : "-$var[1]";} - else {$box2Up = ($var[1] eq "1") ? "$b[0]" : " $b[0]$var[1]";} - $picture[1]->lb( new Label($x[1]-$squareLength/4,$y[1]+$gap,"$box2Up", 'black','center','bottom')); - - if ($b[1]==1) {$box3Left = ($var[1] eq "1") ? "$b[1]" : "$var[1]";} - elsif ($b[1]==-1) {$box3Left = ($var[1] eq "1") ? "$b[1]" : "-$var[1]";} - else {$box3Left = ($var[1] eq "1") ? "$b[1]" : " $b[1]$var[1]";} - $picture[1]->lb( new Label($x[0]-$gap,$y[0]+$squareLength/4,"$box3Left", 'black','right','middle')); - - if ($var[0] ne "1") { - $box1Up = ($a[0]==1) ? "$var[0]" : "$a[0]$var[0]"; - } else { - $box1Up = ($a[0]==1) ? "$var[2]$var[3]" : "$a[0]$var[2]$var[3]"; - } - $picture[1]->lb( new Label($x[0]+$squareLength/4,$y[1]+$gap,"$box1Up", 'black','center','bottom')); - - if ($var[0] ne "1") { - $box1Left = ($a[1]==1) ? "$var[0]" : "$a[1]$var[0]"; - } else { - $box1Left = ($a[1]==1) ? "$var[2]$var[3]" : "$a[1]$var[2]$var[3]"; - } - $picture[1]->lb( new Label($x[0]-$gap,$y[1]-$squareLength/4,"$box1Left", 'black','right','middle')); - - $alt1 = "The graph has four generic rectangles. The top left rectangle has $box1 in it, and the bottom right rectangle has $box4 in it."; - - $alt2 = "The graph has four generic rectangles. The top left rectangle has $box1 in it; the top right rectangle has $box2 in it; the bottom left rectangle has $box3 in it; and the bottom right rectangle has $box4 in it. $box1Up is marked above the top left rectangle; $box1Left is marked to the left of the top left rectangle; $box2Up is marked above the top right rectangle; $box3Left is marked to the left of the bottom left rectangle."; - - #### End two pictures ### - - my $factorPairsAOutput = ""; - my @smallfactorA; - my @largefactorA; - for my $i (1..$sq[0]) { - my $j = $co[0]/$i; - if ($j == int($j)) {push(@smallfactorA,$i); push(@largefactorA,$j);}; - }; - my $factorPairsAOutput = ""; - for my $i (0..$#smallfactorA) { - $factorPairsAOutput .= $smallfactorA[$i].'('.$largefactorA[$i].')'; - if ($i%4 == 3) {$factorPairsAOutput .= '\newline';} else {$factorPairsAOutput .= '&';} - }; - my $factorPairsCOutput = ""; - my @smallfactorC; - my @largefactorC; - for my $i (1..$sq[2]) { - my $j = $co[2]/$i; - if ($j == int($j)) {push(@smallfactorC,$i,-$i); push(@largefactorC,$j,-$j);}; - }; - my $factorPairsCOutput = ""; - for my $i (0..$#smallfactorC) { - $factorPairsCOutput .= $smallfactorC[$i].'('.$largefactorC[$i].')'; - if ($i%4 == 3) {$factorPairsCOutput .= '\newline';} else {$factorPairsCOutput .= '&';} - }; - - $coOrNot = ($var[1] ne "1") ? "\'s coefficient" : ""; - - $outputString .= $BCENTER. - image(insertGraph( $picture[0] ), tex_size=>400, height=>400, width=>400, extra_html_tags=>"alt= '$alt1' title= '$alt1'"). - $ECENTER. - $PAR. - "The first term\'s coefficient, \\(". - $co[0]. - '\), can be factored into the product of two numbers (where the first factor is positive) in the following ways: \[\begin{array}{llll}'. - $factorPairsAOutput. - '\end{array}\]'. - $PAR. - "The third term$coOrNot, \\(". - $co[2]. - '\), can be factored into the product of two numbers in the following ways: \[\begin{array}{llll}'. - $factorPairsCOutput. - '\end{array}\]'. - $PAR. - 'We put each pair into the corresponding places next to those generic rectangles, and try to match the area of those rectangles with \('. - $expanded->TeX. - '\) by guess-and-check. The following generic rectangles show the solution:'. - $PAR. - $BCENTER. - image(insertGraph( $picture[1] ), tex_size=>400, height=>400, width=>400, extra_html_tags=>"alt= '$alt2' title= '$alt2'"). - $ECENTER. - $PAR. - 'Dimensions of those generic rectangles in the diagram give us the solution: \[\begin{aligned}[t]'. - $expanded->TeX. - '&='.$factored->TeX. - $factoredSquareOutput. - '\end{aligned}\]'; - - return $outputString; - -} - - -#### -# End sub factoringMethods -#### - -1; diff --git a/OpenProblemLibrary/macros/PCC/PCCgraphMacros.pl b/OpenProblemLibrary/macros/PCC/PCCgraphMacros.pl deleted file mode 100755 index 3f4c90ced0..0000000000 --- a/OpenProblemLibrary/macros/PCC/PCCgraphMacros.pl +++ /dev/null @@ -1,117 +0,0 @@ -=head1 NAME - -PCCgraphMacros.pl - A collection of tools found to be useful for graphs during the development of the PCC problem library. - -=head1 DESCRIPTION - -This file implements graphing subroutines developed for use in problems originally developed for a PCC problem library - -To use it, load the macro file: - - loadMacros( - "PGstandard.pl", - "MathObjects.pl", - "PCCgraphMacros.pl", - ); - - -=cut - -loadMacros("PCCmacros.pl",); - -############################### -#Some standard values -################################ -sub xPixels{240;} #number of hor pixels in the graph object - -sub yPixels{240;} #number of ver pixels in the graph object - -sub xScreen{240;} #number of hor pixels used on screen - -sub yScreen{240;} #number of ver pixels used on screen - -sub TeXscalar{400;} #400=>image is at 40.0% in hardcopy - -sub EnlargeImageStatementPGML{KeyboardInstructions('If you would like to enlarge the graph or make numbers and letters easier to read, you may click on it to open it in a new window. You may then enlarge that window with your mouse. To further enlarge the image, use your browser\'s zoom capabilities. On a PC this is usually *ctrl shift +* (and zooming out is *ctrl -*). On an Apple computer this is usually *apple shift +* (and zooming out is *apple -*).');}; - -sub EnlargeImageStatement{KeyboardInstructions('If you would like to enlarge the graph or make numbers and letters easier to read, you may click on it to open it in a new window. You may then enlarge that window with your mouse. To further enlarge the image, use your browser\'s zoom capabilities. On a PC this is usually $BBOLD ctrl shift +$EBOLD (and zooming out is $BBOLD ctrl -$EBOLD). On an Apple computer this is usually $BBOLD apple shift +$EBOLD (and zooming out is $BBOLD apple -$EBOLD).');}; - -############################### -#Name: NiceGraphParameters -#Input: References to two arrays: one with all the x "action" in a graph, the other with the y "action" -#Optional Input: centerOrigin, centerXaxis, centerYaxis, buffer, roughTickNum, roughTickNumX, roughTickNumY -#Output: A reference to an array (xmin, xmax, ymin, ymax, xticknumber, yticknumber). These parameters will make a nice looking scale for a graph that captures all of the x and y data. -################################ -sub NiceGraphParameters{ - my $xact = shift; - my $yact = shift; - my %options = @_; - my @xaction = @{$xact}; - my @yaction = @{$yact}; - - if ($options{centerYaxis} or $options{centerOrigin}) - {my @temp = (); - for my $i (@xaction) - {push(@temp, -$i);} - @xaction = (@xaction,@temp); - } - - if ($options{centerXaxis} or $options{centerOrigin}) - {my @temp = (); - for my $i (@yaction) - {push(@temp, -$i);} - @yaction = (@yaction,@temp); - } - - my(@max, @min); - $max[0] = max(@xaction); - $min[0] = min(@xaction); - $max[1] = max(@yaction); - $min[1] = min(@yaction); - - my @roughTickNum; - $roughTickNum[0] = 10; - $roughTickNum[1] = 10; - for my $i (0,1) {$roughTickNum[$i] = $options{roughTickNum} if (defined $options{roughTickNum});}; - $roughTickNum[0] = $options{roughTickNumX} if (defined $options{roughTickNumX}); - $roughTickNum[1] = $options{roughTickNumY} if (defined $options{roughTickNumY}); - - my $buffer = 1.618; - $buffer = $options{buffer} if (defined $options{buffer}); - - @pixels = (xPixels(), yPixels()); - - my (@center, @left, @right, @tickexp, %tickcoefs, @tickcoef, @marksep, @ticknum, @scale, @adjRoughTickNum); - for my $i (0,1) { - $center[$i] = ($max[$i]+$min[$i])/2; - $low[$i] = $center[$i]+$buffer*($min[$i]-$center[$i]); - $high[$i] = $center[$i]+$buffer*($max[$i]-$center[$i]); - $tickexp[$i] = round(log(($high[$i]-$low[$i])/$roughTickNum[$i])/log(10)); - if ($i==0) {$adjRoughTickNum[0] = min($roughTickNum[0], $pixels[0]/((abs($tickexp[0])+3)*10));}; - if ($i==1) {$adjRoughTickNum[1] = min($roughTickNum[1], $pixels[1]/((abs($tickexp[1])+1)*10));}; - %tickcoefs = (); - %tickcoefs = ( - .2 => abs(.2*10**$tickexp[$i]-($high[$i]-$low[$i])/$adjRoughTickNum[$i]), - .5 => abs(.5*10**$tickexp[$i]-($high[$i]-$low[$i])/$adjRoughTickNum[$i]), - 1 => abs( 1*10**$tickexp[$i]-($high[$i]-$low[$i])/$adjRoughTickNum[$i]), - 2 => abs( 2*10**$tickexp[$i]-($high[$i]-$low[$i])/$adjRoughTickNum[$i]), - 5 => abs( 5*10**$tickexp[$i]-($high[$i]-$low[$i])/$adjRoughTickNum[$i])); - $tickcoef[$i] = .2; - for my $key (.2, .5, 1, 2, 5) - # division by 1.7 is a hack to make ticks more frequent - # this may need redoing if problems are reported - {$tickcoef[$i] = $key if ($tickcoefs{$key}<$tickcoefs{$tickcoef[$i]}/1.7);}; - $marksep[$i] = 10**($tickexp[$i])*$tickcoef[$i]; - $ticknum[$i] = 2*ceil(($high[$i]-$low[$i])/2/$marksep[$i]); - $max[$i] = ceil($high[$i]/$marksep[$i])*$marksep[$i]; - $min[$i] = $max[$i] - $marksep[$i]*$ticknum[$i]; - @temp = (); - }; - - my @xticks = map { $marksep[0] * 2*$_ } (int($min[0]/$marksep[0]/2+0.1))..(int($max[0]/$marksep[0]/2-0.1)); - my @yticks = map { $marksep[1] * 2*$_ } (int($min[1]/$marksep[1]/2+0.1))..(int($max[1]/$marksep[1]/2-0.1)); - - return ($min[0], $max[0], $min[1], $max[1], $ticknum[0], $ticknum[1], \@xticks, \@yticks, $marksep[0], $marksep[1]); -} - -1; diff --git a/OpenProblemLibrary/macros/PCC/PCCmacros.pl b/OpenProblemLibrary/macros/PCC/PCCmacros.pl deleted file mode 100755 index bd249bac9f..0000000000 --- a/OpenProblemLibrary/macros/PCC/PCCmacros.pl +++ /dev/null @@ -1,950 +0,0 @@ -=head1 NAME - -PCCmacros.pl - A collection of tools found to be useful during the development of the PCC problem library. - -=head1 DESCRIPTION - -This file implements subroutines developed for use in problems originally developed for a PCC problem library - -To use it, load the macro file: - - loadMacros( - "PGstandard.pl", - "MathObjects.pl", - "PCCmacros.pl", - ); - - -=cut - -############################### -#Name: perlround -#Input: a number to round, then a place to round to. e.g. 2=>hundredths, 0=>whole, -1=>tens -#Output: $x rounded to the $n place. This attempts to overcome quirks with rounding when the cut part is like 0.005 -################################ -sub perlround { - # number to round - my $x = shift; - # place to round. e.g. 2=>hundredths, 0=>whole, -1=>tens - my $n = shift; - # "fix" non-integer input - $n = int($n); - # corresponding integer to round up or down - my $X = $x*10**$n; - $X = ($X-sprintf("%.0f", $X) eq 0.5) ? int($X+1) : sprintf("%.0f", $X); - return $X/10**$n; -} - - -############################### -#Name: RandomName -#Input: None required -#Optional Input: 'sex' => male or female -#Output: A name that is randomly chosen from the list maintained here. -# If 'sex' is specified, the name is from the list of that sex. -################################ -sub RandomName{ -my %options = @_; - -my($sex, $name, @femlist, @malelist); - -$sex = $options{'sex'} if defined ($options{'sex'}); - -$sex = 'both' unless defined($sex); - -@femlist = ('Adrian', - 'Alisa', - 'Alyson', - 'Amber', - 'Annaly', - 'Ashley', - 'Barbara', - 'Bobbi', - 'Briana', - 'Candi', - 'Carly', - 'Carmen', - 'Casandra', - 'Charity', - 'Charlotte', - 'Cheryl', - 'Dawn', - 'Diane', - 'dMarie', - 'Donna', - 'Eileen', - 'Elishua', - 'Emily', - 'Fabrienne', - 'Hannah', - 'Haley', - 'Heather', - 'Holli', - 'Irene', - 'Izabelle', - 'Janieve', - 'Jenny', - 'Jessica', - 'Julie', - 'Kara', - 'Kayla', - 'Kandace', - 'Katherine', - 'Kim', - 'Kristen', - 'Kylie', - 'Laney', - 'Laurie', - 'Lesley', - 'Lily', - 'Lindsay', - 'Lisa', - 'Maria', - 'Martha', - 'Maygen', - 'Morah', - 'Michele', - 'Nenia', - 'Nina', - 'Olivia', - 'Page', - 'Penelope', - 'Perlia', - 'Priscilla', - 'Rebecca', - 'Renee', - 'Rita', - 'Ronda', - 'Samantha', - 'Sarah', - 'Selena', - 'Sharell', - 'Sharnell', - 'Sherial', - 'Stephanie', - 'Subin', - 'Sydney', - 'Tammy', - 'Teresa', - 'Tiffany', - 'Tracei', - 'Virginia', - 'Wendy'); - -@malelist = ('Aaron', - 'Aleric', - 'Alejandro', - 'Andrew', - 'Anthony', - 'Benjamin', - 'Blake', - 'Brandon', - 'Brent', - 'Carl', - 'Chris', - 'Cody', - 'Connor', - 'Corey', - 'Daniel', - 'Dave', - 'Dennis', - 'Derick', - 'Devon', - 'Douglas', - 'Emiliano', - 'Eric', - 'Evan', - 'Farshad', - 'Gosheven', - 'Grant', - 'Gregory', - 'Gustav', - 'Hayden', - 'Henry', - 'Huynh', - 'Ivan', - 'James', - 'Jay', - 'Jeff', - 'Jerry', - 'Jon', - 'Joseph', - 'Joshua', - 'Ken', - 'Kenji', - 'Kimball', - 'Kurt', - 'Marc', - 'Matthew', - 'Michael', - 'Nathan', - 'Neil', - 'Nicholas', - 'Parnell', - 'Peter', - 'Phil', - 'Randi', - 'Ravi', - 'Ross', - 'Ryan', - 'Scot', - 'Sean', - 'Sebastian', - 'Shane', - 'Stephen', - 'Thanh', - 'Tien', - 'Timothy', - 'Wenwu', - 'Will'); - -if ($sex eq 'both') - {$name = list_random(@femlist,@malelist);} -elsif ($sex eq 'female') - {$name = list_random(@femlist);} -elsif ($sex eq 'male') - {$name = list_random(@malelist);} - -$name; -} - -############################### -# -# Name: RandomVariableName -# -# Input: None required -# -# Optional Input: 'type' => variable, constant, integer, or function -# -# Output: A variable name like x, y, t, c, etc. that is randomly chosen from the list maintained here. -# If 'type' is specified, the name is from the list of that type. -# -# Sample use: -################################ -sub RandomVariableName{ -my %options = @_; - -my($type, $name, @varlist, @constlist, @intlist, @functionlist); - -$type = $options{'type'} if defined ($options{'type'}); - -$type = 'all' unless defined($type); - -@varlist = ('x', - 'y', - # 'z', - 'r', - # 's', - 't',); - -@constlist = ('a', - 'b', - 'c', - 'A', - 'B', - 'C',); - -@intlist = ( #'i', potential for context confusion with complex - #'j', and vector - #'k', - 'm', - 'n', - 'p', - 'q',); - -@functionlist = ('f', - 'g', - 'h', - #'k', - 'F', - 'G', - 'H', - 'K',); - -if ($type eq 'all') - {$name = list_random(@varlist,@constlist,@intlist);} -elsif ($type eq 'variable') - {$name = list_random(@varlist);} -elsif ($type eq 'constant') - {$name = list_random(@constlist);} -elsif ($type eq 'integer') - {$name = list_random(@intlist);} -elsif ($type eq 'function') - {$name = list_random(@functionlist);} - -$name; -} - -############################### -# -# Name: xPower -# -# This is just an auxiliary subroutine for the polynomial macros that follow -# -################################ - - -sub xPower{ - my ($power,$maxpower,$order) = @_; - if ($order eq 'ascending') {return $power;} - else {return ($maxpower-$power);}; -} - - - -############################### -# -# Name: PolyString -# -# Input: an arrays -# -# PolyString(~~@poly1) -# -# Arrays elements should be numerical or Math Objects. The idea is that -# these are coefficients of a polynomial, either starting from the -# constant term or from the leading term. -# -# Optional Input: order=>ascending or descending, defaults to descending -# order is ignored if output=>array -# Optional Input: var=> some string, defaulting to "x" -# -# Output: an polynomial string using var and ready to be fed to Compute -# -# For example, given -# -# @poly1 = (1,2); # represents x+2 -# @poly2 = (3,-1,0,4); -# -# PolyString(~~@poly1); -# -# 'x^1+2x^0' -# -# PolyString(~~@poly2,order=>ascending,var=>'z'); -# -# '3x^0+-1x^1+0x^2+4x^3' -# -# Note that instance of +-, x^0, x^1, 0x^n, may be present, -# but Math Objectification will handle that. -# -# Note: If you intend to feed the output string to Compute() or Formula(), -# you should set the context reductions appropriately and apply ->reduce -# *twice* to remove excess parentheses from negative coefficients. -# -################################ - -sub PolyString{ - -my ($xref,%options) = @_; -my @local_x = @{$xref}; - -my ($order, $var, $exponentVar, $string); - -if (defined ($options{'order'})) { - $order = $options{'order'}; - } - else {$order = 'descending'}; - -if (defined ($options{'var'})) { - $var = $options{'var'}; - } - else {$var = 'x'}; - -# new, cmh 6/18/13 -if (defined ($options{'exponentVar'})) { - $exponentVar = $options{'exponentVar'}; - } - else {$exponentVar = ''}; - -# original 6/18/13 -# $string = "$local_x[0]".$var.'^'.xPower(0,$#local_x,$order); -# for my $i (1..$#local_x) { -# $string = $string.'+'."$local_x[$i]".$var.'^'.xPower($i,$#local_x,$order); -# } - -# modified, cmh 6/18/13 -$string = "$local_x[0]".$var.'^{'.xPower(0,$#local_x,$order).$exponentVar.'}'; - for my $i (1..$#local_x) { - $string = $string.'+'."$local_x[$i]".$var.'^{'.xPower($i,$#local_x,$order).$exponentVar.'}'; - } -return $string; -} - -############################### -# -# Name: PolyMult -# -# Input: two arrays, for example -# -# PolyMult(~~@poly1, ~~@poly2) -# -# Arrays elements should be numerical or Math Objects -# where multiplication and addition are allowed. The idea is that -# these are coefficients of a polynomial, either starting from the -# constant term or from the leading term. -# -# Optional Input: order=>ascending or descending, defaults to descending -# order is ignored if output=>array -# Optional Input: var=> some string, defaulting to "x" -# Optional Input: output=>array, simplified, unsimplified -# which is either an array of coefficients, an expanded and simplified -# polynomial string using var and ready to be fed to Compute, or the same -# but unsimplified. Default is array. -# cmh, 6/18/13 -# Optional Input: exponentVar=> some string, defaulting to '' -# to be used if the polynomial looks like, for example -# x^{3n}+x^{2n}+x^{n} -# -# Output: described in Optional Input -# -# For example, given -# -# @poly1 = (1,2); # represents x+2 -# @poly2 = (3,-1,4); # represents 3x^2-x+4 -# -# PolyMult(~~@poly1, ~~@poly2, -# order=>ascending, var=>'z', output=>unsimplified) -# -# '3z^3+-1z^2+4z+6z^2+-2z^1+8z^0' -# -# Note that instance of +- may be present, but Math Objectification -# will handle that. -# -# -# Given -# -# @poly1 = (Fraction(1,3),2); # represents 1/3x+2 -# @poly2 = (Fraction(1,2),Fraction(-1,3)); # represents 1/2x-1/3 -# -# PolyMult(~~@poly1, ~~@poly2) -# -# (Frac(1,6),Frac(8,9),Frac(-1,9)) -# -# -# Note: If you intend to feed the output string to Compute() or Formula(), -# you should set the context reductions appropriately and apply ->reduce -# *twice* to remove excess parentheses from negative coefficients. -# -################################ - -sub PolyMult{ -my ($xref,$yref,%options) = @_; -my @local_x = @{$xref}; -my @local_y = @{$yref}; - -my ($order, $var, $output, @productArray, $productString); - -$order = $options{'order'} if defined ($options{'order'}); -$order = 'descending' unless defined ($options{'order'}); - -$var = $options{'var'} if defined ($options{'var'}); -$var = 'x' unless defined ($options{'var'}); - -$output = $options{'output'} if defined ($options{'output'}); -$output = 'array' unless defined ($options{'output'}); - -# cmh, 6/18/13 -$exponentVar= $options{'exponentVar'} if defined ($options{'exponentVar'}); -$exponentVar = '' unless defined ($options{'exponentVar'}); - -if ($output eq 'array' or $output eq 'simplified'){ - for my $i (0..($#local_x+$#local_y)) { - $productArray[$i]= 0; - foreach my $j (0..$i) { - if ($j <= $#local_x and $i-$j <= $#local_y) - {$productArray[$i] = $productArray[$i]+$local_x[$j]*$local_y[$i-$j];} - } - } - - if ($output eq 'array') {return @productArray;} - - if ($output eq 'simplified') { - return PolyString(\@productArray,order=>$order,var=>$var,exponentVar=>$exponentVar); #cmh, 6/18/13 - } -} - -if ($output eq 'unsimplified') { - $productString = ''; - for my $i (0..$#local_x) { - for my $j (0..$#local_y) { -# $productString = $productString.'+'.$local_x[$i]*$local_y[$j].$var.'^'.xPower($i+$j,$#local_x+$#local_y,$order); -# cmh, 6/18/13 - $productString = $productString.'+'.$local_x[$i]*$local_y[$j].$var.'^{'.xPower($i+$j,$#local_x+$#local_y,$order).$exponentVar.'}'; - } - } - return $productString; -} -} - -# -# Accepts an array of the form that PolyTerms outputs -# Inserts '&' between array entries for use in array environments -# Converts Math Object entries to their TeX form -# Inserts a spacing command after nonempty entries - the command -# \setlength\arraycolsep{0em} should precede the \begin{array} -# $Spacing should be something like "\hspace{0.4em}" -# Also creates an alignment string for the \begin{array} command -# $align should be "r", "c", or "l" -# Not part of PGpolynomialMacros.pl -# - -sub PolyTermsTeXPrep{ - my ($temp, $spacing, $align) = @_; - my @terms = @{$temp}; - my @void = (); - $alignment = "$align"; - foreach my $i (0..$#terms-1) { - @void = splice(@terms,2*$i+1,0,('&')); - if ($terms[2*$i] ne '') - {if (($terms[2*$i] ne '-') && ($terms[2*$i] ne '+')) {$terms[2*$i] = Compute($terms[2*$i])->TeX} - else {$terms[2*$i] = '{}'.$terms[2*$i].'{}';}; - $terms[2*$i] = $terms[2*$i].$spacing;}; - $alignment = $alignment."$align"; - }; - $terms[$#terms] = Compute($terms[$#terms])->TeX; - $terms[$#terms] = $terms[$#terms].$spacing; - return (@terms,$alignment); - -} - -# PolyTerms(~~@coefficientArray,x,order) - -# -# Accepts an array containing the coefficients of a polynomial -# in descending order -# Assumes coefficients have numerical values, but may be Math Objects -# Returns an array of size 2n-1 of the form -# (a_nx^n, +/-, |a_{n-1}| x^{n-1}, +/-, ...) -# where the variable is x is the second argument, the terms are Math Objects -# Formulas, and the +/- are Perl strings. -# The third argument can be "ascending" to reverse the order -# If a_j is 0, then the corresponding term and preceding sign are empty -# strings. -# Trims beginning and ending, so that first and last terms are nonzero -# Not part of PGpolynomialMacros.pl -# - -#=cut - - -sub PolyTerms{ - my ($temp, $v, $order) = @_; - my @poly = @{$temp}; - while (($poly[0] == 0) && ($#poly != 0)) {shift(@poly);}; - my $degree = $#poly; - - while (($poly[$#poly] == 0) && ($#poly != 0)) {pop(@poly);}; - my $lowestdegree = $degree - $#poly; - - if ($order eq "ascending") {@poly = reverse(@poly);}; - - my @terms = (); - my @abspoly = (); - foreach my $i (0..$#poly) { - $abspoly[$i] = abs($poly[$i]); - my $j = $#poly-$i+$lowestdegree; - if ($order eq "ascending") {$j = $i+$lowestdegree;}; - if ($i == 0) { if ($abspoly[$i] != 1){ - if ($j == 0) - {$terms[2*$i] = Compute("$poly[$i]")->reduce;} - elsif ($j == 1) - {$terms[2*$i] = Compute("$poly[$i] $v")->reduce;} - else - {$terms[2*$i] = Compute("$poly[$i] $v^{$j}")->reduce;}; - } - elsif ($poly[$i] == 1){ - if ($j == 0) - {$terms[2*$i] = Compute("1")->reduce;} - elsif ($j == 1) - {$terms[2*$i] = Compute("$v")->reduce;} - else - {$terms[2*$i] = Compute("$v^{$j}")->reduce;}; - } - else{ - if ($j == 0) - {$terms[2*$i] = Compute("-1")->reduce;} - elsif ($j == 1) - {$terms[2*$i] = Compute("-$v")->reduce;} - else - {$terms[2*$i] = Compute("-$v^{$j}")->reduce;}; - }; - - } - elsif ($j > 1) { - if ($poly[$i] > 0) {$terms[2*$i-1] = '+'; - if ($poly[$i] != 1){ - $terms[2*$i] = Compute("$poly[$i] $v^{$j}"); - } - else {$terms[2*$i] = Compute("$v^{$j}");}; - } - elsif ($poly[$i] == 0) - {$terms[2*$i-1] = ''; $terms[2*$i] = '';} - elsif ($poly[$i] == -1) {$terms[2*$i-1] = '-'; - $terms[2*$i] = Compute("$v^{$j}");} - else {$terms[2*$i-1] = '-'; - $terms[2*$i] = Compute("$abspoly[$i] $v^{$j}");}; - } - elsif ($j == 1) { - if ($poly[$i] > 0) {$terms[2*$i-1] = '+'; - if ($poly[$i] != 1){ - $terms[2*$i] = Compute("$poly[$i] $v"); - } - else {$terms[2*$i] = Compute("$v");} - } - elsif ($poly[$i] == 0) - {$terms[2*$i-1] = ''; $terms[2*$i] = '';} - elsif ($poly[$i] == -1){$terms[2*$i-1] = '-'; - $terms[2*$i] = Compute("$v");} - else {$terms[2*$i-1] = '-'; - $terms[2*$i] = Compute("$abspoly[$i] $v");}; - } - else { - if ($poly[$i] > 0) {$terms[2*$i-1] = '+'; - $terms[2*$i] = Compute("$poly[$i]");} - elsif ($poly[$i] == 0) - {$terms[2*$i-1] = ''; $terms[2*$i] = '';} - else {$terms[2*$i-1] = '-'; - $terms[2*$i] = Compute("$abspoly[$i]");}; - }; - }; - - return @terms; -} - -############################### -# Name: PolySub -sub PolySub{ -my ($xref,$yref,%options) = @_; -my @local_x = @{$xref}; -my @local_y = @{$yref}; -foreach my $y (@local_y) { $y = $y * -1; } -return PolyAdd(\@local_x, \@local_y, %options); -} - - -############################### -# Name: PolyAdd -# -# Input: two arrays, for example -# -# PolyAdd(~~@poly1, ~~@poly2) -# -# Arrays elements should be numerical or Math Objects -# where addition are allowed. The idea is that -# these are coefficients of a polynomial, either starting from the -# constant term or from the leading term. -# -# Optional Input: order=>ascending or descending, defaults to descending -# order is ignored if output=>array -# Optional Input: var=> some string, defaulting to "x" -# Optional Input: subtract=> 1 will subtract; default 0 -# Optional Input: output=>array, simplified, unsimplified -# which is either an array of coefficients, a simplified -# polynomial string using var and ready to be fed to Compute, or the same -# but unsimplified with like terms near each other. Default is array. -# -# Output: described in Optional Input -# -# For example, given -# -# @poly1 = (1,2); # represents x+2 (assuming descending) -# @poly2 = (3,-1,4); # represents 3x^2-x+4 -# -# PolyAdd(~~@poly1, ~~@poly2,var=>'z', output=>unsimplified) -# -# '3z^2+1z+-1z+2+4' -# -# Note that instance of +- may be present, but Math Objectification -# will handle that. -# -# -# Given -# -# @poly1 = (Fraction(1,3),2); # represents 1/3+2x (assuming ascending) -# @poly2 = (Fraction(1,2),0,Fraction(-1,3)); -# -# PolyAdd(~~@poly1, ~~@poly2,order=>ascending) -# -# (Frac(5,6),2,Frac(-1,3)) -# -# -# Note: If you intend to feed the output string to Compute() or Formula(), -# you should set the context reductions appropriately and apply ->reduce -# *twice* to remove excess parentheses from negative coefficients. -# -################################ - -sub PolyAdd{ -my ($xref,$yref,%options) = @_; -my @local_x = @{$xref}; -my @local_y = @{$yref}; - -my ($order, $var, $output, @sumArray, $sumString, $subtract, $plusMinus); - -$order = $options{'order'} if defined ($options{'order'}); -$order = 'descending' unless defined ($options{'order'}); - -$var = $options{'var'} if defined ($options{'var'}); -$var = 'x' unless defined ($options{'var'}); - -$subtract = $options{'subtract'} if defined ($options{'subtract'}); -$subtract = 0 unless defined ($options{'subtract'}); - -$output = $options{'output'} if defined ($options{'output'}); -$output = 'array' unless defined ($options{'output'}); - -my $Nx = max($#local_x,$#local_y)-($#local_x); -my $Ny = max($#local_x,$#local_y)-($#local_y); - -if ($order eq 'ascending') { - for my $i (1..$Nx) - {push(@local_x,0);} - for my $i (1..$Ny) - {push(@local_y,0);} -} -else { - for my $i (1..$Nx) - {unshift(@local_x,0);} - for my $i (1..$Ny) - {unshift(@local_y,0);} -} - - -if ($output eq 'array' or $output eq 'simplified') { - for my $i (0..$#local_x) { - $sumArray[$i]= $local_x[$i]+(-1)**($subtract)*$local_y[$i]; - } - - if ($output eq 'array') {return @sumArray;} - if ($output eq 'simplified') {return PolyString(\@sumArray,order=>$order,var=>$var);} -} - - -if ($output eq 'unsimplified') { - if ($subtract == 1) - {$plusMinus = '-'} - else {$plusMinus = '+'}; - $sumString = ''; - for my $i (0..$#local_x) { - $sumString = $sumString.'+'.$local_x[$i].$var.'^'.xPower($i,$#local_x,$order).$plusMinus.$local_y[$i].$var.'^'.xPower($i,$#local_x,$order); - } - return $sumString; -} - -} - - -############################### -# Name: numberWord -# -# Input: either a whole number <100 or a fraction with numerator and denominator less than 100. If fraction, set denominator parameter. -# Optional Input: capital => 1 (default is 0) -# Optional Input: denominator=> 4 (default is 1) -# numberWord($n) -# -# Output: a string spelling that number -# -################################ - -sub numberWord{ -my ($n,%options, $return, $capital, $denominator, $numerator) = @_; - -$capital = $options{'capital'} if defined ($options{'capital'}); -$denominator = $options{'denominator'} if defined ($options{'denominator'}); - -$capital= 0 unless defined($capital); -$denominator = 1 unless defined($denominator); - -return $n if (abs($n*$denominator) > 99); - -my %wordNumber = (0=>'zero', - 1=>'one', - 2=>'two', - 3=>'three', - 4=>'four', - 5=>'five', - 6=>'six', - 7=>'seven', - 8=>'eight', - 9=>'nine', - 10=>'ten', - 11=>'eleven', - 12=>'twelve', - 13=>'thirteen', - 14=>'fourteen', - 15=>'fifteen', - 16=>'sixteen', - 17=>'seventeen', - 18=>'eighteen', - 19=>'nineteen', - 20=>'twenty', - 30=>'thirty', - 40=>'forty', - 50=>'fifty', - 60=>'sixty', - 70=>'seventy', - 80=>'eighty', - 90=>'ninety'); - -my %wordFraction = ( - 1=>'first', - 2=>'second', - 3=>'third', - 4=>'fourth', - 5=>'fifth', - 6=>'sixth', - 7=>'seventh', - 8=>'eighth', - 9=>'ninth', - 12=>'twelfth', - 20=>'twentieth', - 30=>'thirtieth', - 40=>'fortieth', - 50=>'fiftieth', - 60=>'sixtieth', - 70=>'seventieth', - 80=>'eightieth', - 90=>'ninetieth'); - -$return = ''; -if ($n < 0) {$return = 'negative ';}; - -$n = abs($n); - -if ($denominator == 1) { - if (($n <= 20) or ($n%10 == 0)) {$return = $return.$wordNumber{$n}} - else {$return = $return.numberWord($n-($n%10)).'-'.numberWord($n%10)}; -} - -else { - $numerator = round($n*$denominator); - $return = $return.numberWord($numerator); - if (($denominator == 2) and ($numerator != 1)) - {$return = $return.' halves';} - elsif (($denominator == 2) and ($numerator == 1)) - {$return = $return.' half'} - elsif (defined($wordFraction{$denominator})) - {$return = $return.' '.$wordFraction{$denominator}; - $return = $return.'s' if ($numerator != 1)} - elsif (defined($wordNumber{$denominator})) - {$return = $return.' '.$wordNumber{$denominator}.'th'; - $return = $return.'s' if ($numerator != 1)} - else - {$return = $return.' '.numberWord($denominator-$denominator%10).'-'.$wordFraction{$denominator%10}; - $return = $return.'s' if ($numerator != 1)} -} - - -$return = ucfirst($return) if ($capital == 1); -return $return; - -} - -# radicalListCheck -# -# Used when multiple answers are needed (e.g for quadratic) equations -# that may or may *not* contain radicals. -# -# Sample use: -# -# ANS($ans->cmp(limits => [1,2],list_checker => ~~&radicalListCheck)); -# -# -sub radicalListCheck { - my ($correct,$student,$ansHash,$value) = @_; - my $score = 0; # number of correct student answers - my @errors = (); # stores error messages - my ($i, $j, $k); # loop counters - return (0, @errors) if $ansHash->{isPreview}; - my $fullStudent = $ansHash->{student_formula}; - #in line below, correct_ans seems the right thing to do. In some problems, this is blank, so I'm just going with correct_value - # for those problems. But changing to correct_value broke other problems... so the conditional hack - my $fullCorrect = ($ansHash->{correct_ans}) ? Formula($ansHash->{correct_ans}) : Formula($ansHash->{correct_value}); - - my @fullStudentValue = $fullStudent->value; - my @fullCorrectValue = $fullCorrect->value; - my $n = scalar(@fullStudentValue); # number of student answers - my $m = scalar(@fullCorrectValue); # number of correct answers - - # - # Loop though the student answers - ## - for ($i = 0; $i < $n; $i++) { - my $ith = Value::List->NameForNumber($i+1); - my $p = $fullStudentValue[$i]; # i-th student answer - - # - # Check that the student's answer is an assignment (or whatever the right type) - # - my $assingmentMessageGiven = 0; - my $nosolutionMessageGiven = 0; - if (defined($p)) { - if($p eq "no real solutions") - { - push(@errors,"This equation does have some solutions."); - $nosolutionMessageGiven = 1; - } - elsif($p->type ne "Assignment") { - push(@errors,"Your $ith entry should be written $var=_____"); - $assingmentMessageGiven = 1; - } - } - - # - # Check that the answer hasn't been given before - # - for ($j = 0, $used = 0; $j < $i; $j++) { - if ($p == $fullStudentValue[$j]) { - push(@errors,"Your $ith solution is the same as a previous one"); - $used = 1; last; - } - } - - # - # If not already used, check that it is a correct and reduced answer - # - if (!$used) { - my ($numericallyCorrect, $reduced); - for ($k = 0, $numericallyCorrect = 0; ($k < $m) ; $k++) { - $q = $fullCorrectValue[$k]; - if (Formula($q) == Formula($p)) { - $numericallyCorrect = 1; - my ($setSqrt, $setRoot) = (Context()->flag("setSqrt"), Context()->flag("setRoot")); - Context()->flags->set(checkSqrt => $setSqrt, checkRoot => $setRoot, bizarroAdd => 1, bizarroSub => 1, bizarroMul => 1, bizarroDiv => 1); - delete $p->{test_values}; - delete $q->{test_values}; - if ($p == $q) {$reduced = 1} else {$reduced = 0}; # check if form is correct - - Context()->flags->set(checkSqrt => 0, checkRoot => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0); - $score++ if ($numericallyCorrect and $reduced); - push(@errors,"Your $ith solution is not fully simplified") if ($numericallyCorrect and !$reduced and ($n>1)); - push(@errors,"Your solution is not fully simplified") if ($numericallyCorrect and !$reduced and ($n==1)); - last; - } - } - push(@errors,"Your $ith solution is not correct") if (!$numericallyCorrect and ($n>1) and !$assingmentMessageGiven ); - push(@errors,"Your solution is not correct") if (!$numericallyCorrect and ($n==1) and !$assingmentMessageGiven and !$nosolutionMessageGiven ); - } - } - - # - # Check that there are the right number of answers - # - if (!$ansHash->{isPreview}) { - #push(@errors,"Do you have all the solutions?") if (($n < 2) and ($score < 2) and !$nosolutionMessageGiven ); - #push(@errors,"Should a quadratic equation have more than two solutions?") if ($n > 2); - push(@errors,"Do you have all the solutions?") if (($n < $m) and ($score < $m) and !$nosolutionMessageGiven ); - push(@errors,"Should this equation have more than $m solution?") if ($n > $m and $m==1); - push(@errors,"Should this equation have more than $m solutions?") if ($n > $m and $m>1); - - } - return ($score,@errors); - } - -# Keyboard instructions should only be displayed in HTML output -sub KeyboardInstructions { - my $in = shift; - if ($displayMode =~ /HTML/) {return $in} -} - - -1; diff --git a/OpenProblemLibrary/macros/PCC/README b/OpenProblemLibrary/macros/PCC/README deleted file mode 100755 index 6b1e4d321b..0000000000 --- a/OpenProblemLibrary/macros/PCC/README +++ /dev/null @@ -1,4 +0,0 @@ -This folder contains macros for collections of problems developed at Portland Communit College. These problems are placed in webwork-open-problem-library/Contrib/PCC/, and OPL maintainers may eventually copy them to within webwork-open-problem-library/PCC/ as well. - -Currently the only github user who should be allowed to change files here is Alex-Jordan or cmhughes. - diff --git a/OpenProblemLibrary/macros/PCC/SolveLinearEquationPCC.pl b/OpenProblemLibrary/macros/PCC/SolveLinearEquationPCC.pl deleted file mode 100755 index 6c4916f293..0000000000 --- a/OpenProblemLibrary/macros/PCC/SolveLinearEquationPCC.pl +++ /dev/null @@ -1,165 +0,0 @@ -#These three subroutines uniformize how all our "solve this equation" problems are handled. - -loadMacros("PCCmacros.pl"); - -sub _init_SolveLinearEquationPCCDevel { - - #Possibly add initialization code here - #sub routine is not required, but prevents the macro from being re-loaded - -} - - - -sub contextSetup { - my ($vRef, $aRef) = @_; - Context("LimitedFraction"); - Context()->flags->set(requirePureFractions=>1); - Context()->flags->set(showMixedNumbers=>0); - %varAddHash = (); - for my $i (0..$#{$vRef}) { - $varAddHash{$vRef->[$i]} = 'Real'; - }; - Context()->variables->are(%varAddHash); - parser::Assignment->Allow; - Context()->flags->set(reduceConstantFunctions=>0, - formatStudentAnswer=>"parsed", - reduceConstants=>0, - formatStudentAnswer=>parsed, - reduceFractions=>0, - showExtraParens=>0); - Context()->operators->redefine(','); - Context()->lists->set(Set => {open => "{", close => "}"}); - Context()->parens->set("{" => {type => "Set", close => "}"}); - my @eqTypes = (); - for my $i (keys %varAddHash) { - push (@eqTypes, $i.' = ___'); - }; - my $n = $#eqTypes; - my $eqTypesString = $eqTypes[0]; - if ($n == 1) {$eqTypesString .= ' or '.$eqTypes[1];}; - if ($n > 1) { - for my $i (1..$n - 1) {$eqTypesString .= ', '.$eqTypes[$i];}; - $eqTypesString .= ', or '.$eqTypes[$n]; - }; - Context()->{error}{msg}{"The left side of an assignment must be a variable or function"} - = "Your answer should be in the form $eqTypesString"; - Context()->{error}{msg}{"The right side of an assignment must not include the variable being defined"} - = "The right side must not include the variable being defined"; - Context()->strings->add("no solution"=>{}, - "no solutions"=>{alias=>'no solution'}, - "none"=>{alias=>'no solution'}, - "all real numbers"=>{}, - "infinite number of solutions"=>{alias=>'all real numbers'}, - "infinite"=>{alias=>'all real numbers'}, - "infinite solutions"=>{alias=>'all real numbers'} - ); - my @ansEqArray = (); - for my $i (0..$#{$aRef}) { - my $tempString = $vRef->[$i].' = '.$aRef->[$i]; - push (@ansEqArray , Formula("$tempString")); - }; - @eqTypes = (); - for my $i (0..$#{$vRef}) { - push (@eqTypes, $vRef->[$i].' = ___'); - }; - return (\@ansEqArray,\@eqTypes); -}; - - -sub instructions { - my ($vRef) = @_; - %varAddHash = (); - for my $i (0..$#{$vRef}) { - $varAddHash{$vRef->[$i]} = 'Real'; - }; - my @eqTypes = (); - for my $i (keys %varAddHash) { - push (@eqTypes, '[|'.$i.' = ___|]*'); - }; - my $eqTypesString = join(', ',@eqTypes); - #"Solve the following linear equation; the answer could be in the form $eqTypesString, [|no solution|]*, or [|all real numbers|]*." - 'Solve the equation.'.KeyboardInstructions(q! (The answer might be "[|no solution|]*" or "[|all real numbers|]*".)!) -}; - - -sub answerCheck { - my ($ansEqRef, $eqTypesRef) = @_; - for my $i (0..$#{$ansEqRef}) { - $ansType[$i] = $eqTypesRef->[$i]; - $ansEq[$i] = $ansEqRef->[$i]; - ANS($ansEq[$i]->cmp( - cmp_class=>"a variable assignment of the form $ansType[$i]", - correct_ans_latex_string => '\\{' . (Compute($ansEq[$i])->value)[1] . '\\}', - correct_ans => '{' . (Compute($ansEq[$i])->value)[1] . '}', - )->withPostFilter( - AnswerHints( - #(Compute($ansEq[$i])->value)[1] => ["You have the solution, but the answer to this question should be in the form $ansType[$i]" , replaceMessage => 1], - #sub { - # my ($correct, $student, $ansHash) =@_; - # return (Compute($ansHash->{original_student_ans})->class eq 'Set'); - #} => ["You are trying to give the solution set in set notation, but the answer to this question should be in the form $ansType[$i]" , replaceMessage => 1], - #sub { - # my ($correct, $student, $ansHash) =@_; - # return (Compute($ansHash->{original_student_ans})->class eq 'Set' and $student == (Compute($ansEq[$i])->value)[1]); - #} => ["You have the solution set, but the answers to this question should be in the form $ansType[$i]" , replaceMessage => 1], - $ansEq[$i] => ["Good. And the solution set for this equation is {" . (Compute($ansEq[$i])->value)[1] . "}.", replaceMessage => 1, checkCorrect =>1], - ) - ) - ->withPostFilter(sub { - my $ansHash = shift; - if ($ansHash->{score}) { - my ($svar,$sfrac) = $ansHash->{student_value}->value; - if (Value::classMatch($sfrac,'Fraction')) { - if (($sfrac->value)[1] == 1 or ($sfrac->value)[0] == 0) { - $ansHash->{score} = 0; - $ansHash->{ans_message} = "Your fraction is not reduced"; - } else { - my $correctAnswer = $ansHash->{correct_value}; - my ($cvar,$cfrac) = Compute("$correctAnswer")->value; - $cfrac = Fraction($cfrac); - my $check = $cfrac->cmp->evaluate($sfrac->string); - $ansHash->{score} = $check->{score}; - $ansHash->{ans_message} = $check->{ans_message}; - } - } - } - #if we make it through all the pre-2017 answer checking, now check for answers like 12 or {12} (as opposed to x=12) and award credit - elsif (Value::classMatch($ansHash->{student_value},'Fraction') or Value::classMatch($ansHash->{student_value},'Real')) { - my $simpleans = Fraction((Compute($ansEq[$i])->value)[1]); - my $simplecheck = $simpleans->cmp->evaluate($ansHash->{student_value}->string); - $ansHash->{score} = $simplecheck->{score}; - $ansHash->{ans_message} = $simplecheck->{ans_message}; - if (Value::classMatch($ansHash->{student_value},'Fraction')) { - if (($ansHash->{student_value}->value)[1] == 1 or ($ansHash->{student_value}->value)[0] == 0) { - $ansHash->{score} = 0; - $ansHash->{ans_message} = "Your fraction is not reduced"; - } - } - } - elsif (Value::classMatch($ansHash->{student_value},'Set')) { - my $correctsol = (Compute($ansEq[$i])->value)[1]; - my $check = $correctsol->cmp->evaluate(($ansHash->{student_value}->value)[0]->string); - $ansHash->{score} = $check->{score}; - $ansHash->{ans_message} = $check->{ans_message}; - } - return $ansHash; - }) - ); - }; -}; - - -sub summary { - my ($ansEq, $left, $right) = @_; - my ($var, $ans) = (Compute($ansEq)->value); - my $ansEqTeX = $ansEq->TeX; - my $leftTeX = $left->TeX; - my $rightTeX = $right->TeX; - my $ansTeX = $ans->TeX; - "The solution to this equation is [`$ans`]. To stress that this is a value assigned to [`$var`], some report [`$ansEqTeX`]. We can also say that the solution set is [`\\{$ansTeX\\}`], or that [`$var\\in\\left\\{$ansTeX\\right\\}`]. If we substitute [`$ansTeX`] in for [`$var`] in the original equation [`$leftTeX=$rightTeX`], the equation will be true. Please check this on your own; it is an important habit. " - -}; - - -1; diff --git a/OpenProblemLibrary/macros/PCC/SystemsOfLinearEquationsProblemPCC.pl b/OpenProblemLibrary/macros/PCC/SystemsOfLinearEquationsProblemPCC.pl deleted file mode 100755 index 3679842525..0000000000 --- a/OpenProblemLibrary/macros/PCC/SystemsOfLinearEquationsProblemPCC.pl +++ /dev/null @@ -1,789 +0,0 @@ -loadMacros('PCCmacros.pl'); - -sub SystemOfLinearEquationsProblemSetup { - -#markers for zero-values used in formatting -$zeroa = ($a == Fraction(0,1)) ? 0 : 1; -$zerob = ($b == Fraction(0,1)) ? 0 : 1; -$zeroc = ($c == Fraction(0,1)) ? 0 : 1; -$zerod = ($d == Fraction(0,1)) ? 0 : 1; -$zeroe = ($e == Fraction(0,1)) ? 0 : 1; -$zerof = ($f == Fraction(0,1)) ? 0 : 1; - - -#How will the equations be displayed? -# This is tricky - we can't use Formula reductions or the fractions will be converted to decimals. So special treatment is given to "0x", "1x", and "-1x". Also, care is taken to prevent "--x" from becoming "+x" when it should just be "x". -Context()->flags->set(reduceConstants=>0,showExtraParens=>0); - - -$asided = $a*(-1)**$xside1; -$bsided = $b*(-1)**$yside1; -$csided = $c*(-1)**$xside2; -$dsided = $d*(-1)**$yside2; -$esided = $e*(-1)**$cside1; -$fsided = $f*(-1)**$cside2; - - -Context()->variables->are($x=>'Real', $y=>'Real', constant=>'Real'); - -($left1, $right1, $left2, $right2, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided, $bsided, $csided, $dsided, $esided, $fsided, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 1, [0,1,2], [0,1,2], [0,1,2], [0,1,2]); - - - -# How to enter answers? -$EnterAnswers = (($x eq "x" and $y eq "y") or ($x eq "y" and $y eq "x")) ? "Give your answer as an ordered pair or in the form *x=[|___|] and y=[|___|] *." : "Give your answer in the form *$x = [|___|] and $y = [|___|] *."; -$EnterAnswers = $EnterAnswers." If there are no solutions, you may enter *no solutions* or *none*. If there are infinitely many solutions, you may enter *infinitely many solutions*."; -$EnterAnswers = KeyboardInstructions($EnterAnswers); - - -$determinant = (($a)*($d)-($b)*($c)); -if ($determinant != 0) { - $xsol = (($d)*(-$e)+(-$b)*(-$f))/$determinant; - $ysol = ((-$c)*(-$e)+($a)*(-$f))/$determinant; -} -}; - -sub shuffle { - my $array = shift; - my $i; - for my $i (0..@$array-1) { - my $j = random($i,@$array-1,1); - next if ($i == $j); - @$array[$i,$j] = @$array[$j,$i]; - } - }; - -sub makeSides { - my ($Asided, $Bsided, $Csided, $Dsided, $Esided, $Fsided, $Xside1, $Yside1, $Cside1, $Xside2, $Yside2, $Cside2, $Zeroa, $Zerob, $Zeroc, $Zerod, $Zeroe, $Zerof, $X, $Y, $Constant, $Shuffle, $LeftOrder1, $RightOrder1, $LeftOrder2, $RightOrder2) = @_; - - Context()->normalStrings; - my $ax, $by, $cx, $dy; - - if ($Asided == Fraction(1,1)) {$ax = Formula("$X")} - elsif ($Asided == Fraction(-1,1)) {$ax = Formula("-$X")} - else {$ax = Formula("$Asided $X")}; - if ($Bsided == Fraction(1,1)) {$by = Formula("$Y")} - elsif ($Bsided == Fraction(-1,1)) {$by = Formula("-$Y")} - else {$by = Formula("$Bsided $Y")}; - if ($Csided == Fraction(1,1)) {$cx = Formula("$X")} - elsif ($Csided == Fraction(-1,1)) {$cx = Formula("-$X")} - else {$cx = Formula("$Csided $X")}; - if ($Dsided == Fraction(1,1)) {$dy = Formula("$Y")} - elsif ($Dsided == Fraction(-1,1)) {$dy = Formula("-$Y")} - else {$dy = Formula("$Dsided $Y")}; - - my @leftTerms1 = ("(1-$Xside1)*$X*$Zeroa", "(1-$Yside1)*$Y*$Zerob", "(1-$Cside1)*$Constant*$Zeroe"); - my @rightTerms1 = ("$Xside1*$X*$Zeroa", "$Yside1*$Y*$Zerob", "$Cside1*$Constant*$Zeroe"); - my @leftTerms2 = ("(1-$Xside2)*$X*$Zeroc", "(1-$Yside2)*$Y*$Zerod", "(1-$Cside2)*$Constant*$Zerof"); - my @rightTerms2 = ("$Xside2*$X*$Zeroc", "$Yside2*$Y*$Zerod", "$Cside2*$Constant*$Zerof"); - - - if ($Shuffle) { - shuffle( $LeftOrder1 ); - shuffle( $RightOrder1 ); - shuffle( $LeftOrder2 ); - shuffle( $RightOrder2 ); - }; - - my $Left1 = Formula("$leftTerms1[${$LeftOrder1}[0]]+$leftTerms1[${$LeftOrder1}[1]]+$leftTerms1[${$LeftOrder1}[2]]")->reduce; - my $Right1 = Formula("$rightTerms1[${$RightOrder1}[0]]+$rightTerms1[${$RightOrder1}[1]]+$rightTerms1[${$RightOrder1}[2]]")->reduce; - $Left1 = $Left1->substitute($X=>$ax, $Y=>$by, $Constant=>Formula("$Esided")); - $Right1 = $Right1->substitute($X=>$ax, $Y=>$by, $Constant=>Formula("$Esided")); - - $Left2 = Formula("$leftTerms2[${$LeftOrder2}[0]]+$leftTerms2[${$LeftOrder2}[1]]+$leftTerms2[${$LeftOrder2}[2]]")->reduce; - $Right2 = Formula("$rightTerms2[${$RightOrder2}[0]]+$rightTerms2[${$RightOrder2}[1]]+$rightTerms2[${$RightOrder2}[2]]")->reduce; - $Left2 = $Left2->substitute($X=>$cx, $Y=>$dy, $Constant=>Formula("$Fsided")); - $Right2 = $Right2->substitute($X=>$cx, $Y=>$dy, $Constant=>Formula("$Fsided")); - - Context()->texStrings; - - return ($Left1, $Right1, $Left2, $Right2, $LeftOrder1, $RightOrder1, $LeftOrder2, $RightOrder2); -}; - - - -sub SystemOfLinearEquationsProblemSolutionSetup { - -$Attack = ""; #the steps to solve the problem. - -############################################## -#If any of the coefficients are 0... -Context()->texStrings; -if ($a == Fraction(0,1)) { - $ysol1 = -$e/$b; - if ($c == Fraction(0,1)) { - $ysol2 = -$f/$d; - if ($ysol1 == $ysol2) { - $Attack = $Attack."These two equations both say that [`$y=$ysol1`] and say nothing about [`$x`]. There are infinitely many solutions to the system: [`$x`] can be any real number, paired up with [`$y=$ysol1`]."; - } - else { - $Attack = $Attack."The first equation says that [`$y=$ysol1`] and the second says that [`$y=$ysol2`]. These cannot both simultaneously be true, so the system has no solutions."; - }; - } - elsif ($d == Fraction(0,1)) { - $xsol2 = -$f/$c; - $Attack = $Attack."The first equation says that [`$y=$ysol1`] and the second says that [`$x=$xsol2`]. So there is a unique solution: [`$x=$xsol2, $y=$ysol1`]"; - } - else{ - $Attack = $Attack." The first equation immediately allows us to solve for [`$y`]: [`$y = $ysol`]. We can substitute this into the second equation to solve for [`$x`] to find that [`$x=$xsol`]. - -"; - - }; -} -elsif ($b == Fraction(0,1)) { - $xsol1 = -$e/$a; - if ($d == Fraction(0,1)) { - $xsol2 = -$f/$c; - if ($xsol1 == $xsol2) { - $Attack = $Attack."These two equations both say that [`$x=$xsol1`] and say nothing about [`$y`]. There are infinitely many solutions to the system: [`$y`] can be any real number, paired up with [`$x=$xsol1`]."; - } - else { - $Attack = $Attack."The first equation says that [`$x=$xsol1`] and the second says that [`$x=$xsol2`]. These cannot both simultaneously be true, so the system has no solutions."; - }; - } - elsif ($c == Fraction(0,1)) { - $ysol2 = -$f/$d; - $Attack = $Attack."The first equation says that [`$x=$xsol1`] and the second says that [`$y=$ysol2`]. So there is a unique solution: [`$x=$xsol1, $y=$ysol2`]"; - } - else{ - $Attack = $Attack." The first equation immediately allows us to solve for [`$x`]: [`$x = $xsol`]. We can substitute this into the second equation to solve for [`$y`] to find that [`$y=$ysol`]. - -"; - - }; -} -elsif ($c == Fraction(0,1)) { - $Attack = $Attack." The second equation immediately allows us to solve for [`$y`]: [`$y = $ysol`]. We can substitute this into the second equation to solve for [`$x`] to find that [`$x=$xsol`]. - -"; -} -elsif ($d == Fraction(0,1)) { - $Attack = $Attack." The second equation immediately allows us to solve for [`$x`]: [`$x = $xsol`]. We can substitute this into the second equation to solve for [`$y`] to find that [`$y=$ysol`]. - -"; -} -else{ - -############################################## -#Now assuming no coefficients are 0... - -############################################## -#clear denominators, if necessary - -$aden = ($a->value)[1]; -$bden = ($b->value)[1]; -$cden = ($c->value)[1]; -$dden = ($d->value)[1]; -$eden = ($e->value)[1]; -$fden = ($f->value)[1]; - -$lcd1 = lcm($aden,$bden); $lcd1=lcm($lcd1, $eden); -$lcd2 = lcm($cden,$dden); $lcd2=lcm($lcd2, $fden); - -if ($lcd1 == 1 and $lcd2 == 1) { - $Attack = $Attack."These equations have no fractions; let's try to keep it that way."; } -else{ - $Attack = $Attack."If an equation involves fractions, it is helpful to clear denominators by multiplying both sides of the equation by a common multiple of the denominators."; -}; - - -if ($lcd1 == 1 and $lcd2 == 1) { -} -elsif ($lcd1 == 1) { - $Attack = $Attack." - - [```\\left\\{\\begin{aligned} - $left1 & = $right1\\\\ - $lcd2\\left($left2\\right) & = $lcd2\\left($right2\\right) - \\end{aligned}\\right.```] - - "; -} -elsif ($lcd2 == 1) { - $Attack = $Attack." - - [```\\left\\{\\begin{aligned} - $lcd1\\left($left1\\right) & = $lcd1\\left($right1\\right)\\\\ - $left2 & = $right2 - \\end{aligned}\\right.```] - - "; -} -else { - $Attack = $Attack." - - [```\\left\\{\\begin{aligned} - $lcd1\\left($left1\\right) & = $lcd1\\left($right1\\right)\\\\ - $lcd2\\left($left2\\right) & = $lcd2\\left($right2\\right) - \\end{aligned}\\right.```] - - "; -}; - - - -$asided = $asided*$lcd1; -$bsided = $bsided*$lcd1; -$csided = $csided*$lcd2; -$dsided = $dsided*$lcd2; -$esided = $esided*$lcd1; -$fsided = $fsided*$lcd2; - - -($left1, $right1, $left2, $right2, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided, $bsided, $csided, $dsided, $esided, $fsided, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2); - - -$Attack = $Attack." - - [```\\left\\{ - \\begin{aligned} - $left1 & = $right1\\\\ - $left2 & = $right2 - \\end{aligned} - \\right.```] - -"; - - - -# Does any equation have 1 or -1 as a coefficient? Use substitution. - -############################################################# -#if no coefficient is 1, and some coefficient is -1 then flip that term to the other side. - -if ($asided != Fraction(1,1) and $bsided != Fraction(1,1) and $csided != Fraction(1,1) and $dsided != Fraction(1,1)) - {$swap = 0; - if ($asided == Fraction(-1,1)) - { - $xside1 = 1-$xside1; - $asided = $asided*(-1); - $swap = 1; - } - elsif ($bsided == Fraction(-1,1)) - { - $yside1 = 1-$yside1; - $bsided = $bsided*(-1); - $swap = 1; - } - elsif ($csided == Fraction(-1,1)) - { - $xside2 = 1-$xside2; - $csided = $csided*(-1); - $swap = 1; - } - elsif ($dsided == Fraction(-1,1)) - { - $yside2 = 1-$yside2; - $dsided = $dsided*(-1); - $swap = 1; - }; - - if ($swap ==1 ) { - ($left1, $right1, $left2, $right2, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided, $bsided, $csided, $dsided, $esided, $fsided, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2); - - $Attack = $Attack."Since there is a coefficient of [`-1`], it is wise to add the corresponding term to both sides of that equation, so that we have a coefficient of [`1`] to work with. - - [```\\left\\{ - \\begin{aligned} - $left1 & = $right1\\\\ - $left2 & = $right2 - \\end{aligned} - \\right.```] - -"; - } - }; - - -############################################################# -#if only the second equation has a 1 for one of its coefficients, swap equations; this is more to simplify the code of the solution. - -if ($asided != Fraction(1,1) and $bsided != Fraction(1,1) and ($csided == Fraction(1,1) or $dsided == Fraction(1,1))) - { - ($a, $b, $c, $d, $e, $f) = ($c, $d, $a, $b, $f, $e); - - ($lcd1, $lcd2) = ($lcd2, $lcd1); - - ($zeroa, $zeroc) = ($zeroc, $zeroa); - ($zerob, $zerod) = ($zerod, $zerob); - ($zeroe, $zerof) = ($zerof, $zeroe); - - ($xside1,$yside1,$cside1,$xside2,$yside2,$cside2) = ($xside2,$yside2,$cside2,$xside1,$yside1,$cside1); - - ($asided, $csided)= ($csided, $asided); - ($bsided, $dsided)= ($dsided, $bsided); - ($esided, $fsided)= ($fsided, $esided); - - ($leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = ($leftOrd2, $rightOrd2, $leftOrd1, $rightOrd1); - -($left1, $right1, $left2, $right2, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided, $bsided, $csided, $dsided, $esided, $fsided, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2); - - - $Attack = $Attack." Since the second equation has a coefficient of [`1`], it is convenient to change the order of the equations. - - [```\\left\\{\\begin{aligned} - $left1 & = $right1\\\\ - $left2 & = $right2 - \\end{aligned}\\right.```] - -"; - - }; - - -############################################################# -#If the first equation has 1x on either side... - -if ($asided == Fraction(1,1)) { - ($xside1,$yside1,$cside1) = ($xside1, 1-$xside1, 1-$xside1); - $asided = $a*$lcd1*(-1)**$xside1; - $bsided = $b*$lcd1*(-1)**$yside1; - $esided = $e*$lcd1*(-1)**$cside1; - - ($leftOrd1, $rightOrd1) = ([0, 1, 2], [0, 1, 2]); - - ($left1, $right1, $left2, $right2, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided, $bsided, $csided, $dsided, $esided, $fsided, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2); - - $left1 = $left1->reduce; - $right1 = $right1->reduce; - $left2 = $left2->reduce; - $right2 = $right2->reduce; - $left2 = ($xside1 == 0) ? $left2->substitute($x=>$right1) : $left2->substitute($x=>$left1); - $right2 = ($xside1 == 0) ? $right2->substitute($x=>$right1) : $right2->substitute($x=>$left1); - Context()->normalStrings; - $left2 = Formula($left2); - $right2 = Formula($right2); - Context()->texStrings; - $leftStepLinear = $left2->D("$y")->reduce; - $leftStepConstant = $left2->eval($y=>0)->reduce; - $rightStepLinear = $right2->D("$y")->reduce; - $rightStepConstant = $right2->eval($y=>0)->reduce; - $leftStep = Formula("$leftStepLinear $y + $leftStepConstant")->reduce; - $rightStep = Formula("$rightStepLinear $y + $rightStepConstant")->reduce; - $leftNextStep = Formula("($leftStepLinear-$rightStepLinear)$y")->reduce; - $rightNextStep = Formula("$rightStepConstant-$leftStepConstant")->reduce; - Context()->flags->set(showExtraParens=>1); - $Attack = $Attack. - -"Since one of the coefficients of [`$x`] is [`1`], it is wise to solve for the [`$x`] in terms of the other variable and then use substitution to complete the problem. - - [``` - \\begin{aligned} - $left1 & = $right1 &\\text{(from the first equation)} - \\end{aligned} - ```] - -which we can substitute in for [`$x`] into the second equation: - - [```\\begin{aligned} - $left2 & = $right2 &\\text{(from the second equation)}\\\\ - $leftStep & = $rightStep\\\\"; - - Context()->flags->set(showExtraParens=>0); - if ($leftStep ne $leftNextStep or $rightStep ne $rightNextStep) { - $Attack = $Attack."$leftNextStep & = $rightNextStep\\\\"; - } - - if ($determinant == 0 and ($leftNextStep != $rightNextStep)) { - $Attack = $Attack."\\end{aligned}```] - -Since this is a false equation no matter what the values of [`$x`] and [`$y`] are, the system has no solutions."; - } - elsif ($determinant == 0 and ($leftNextStep == $rightNextStep)) { - $Attack = $Attack."\\end{aligned}```] - -Since this is a true equation no matter what the values of [`$x`] and [`$y`] are, the system has infinitely many solutions - just pick any value for [`$y`] and then let [`$left1 = $right1`]."; - } - else { - $Attack = $Attack." - $y &= $ysol - \\end{aligned} - ```]"; - - Context()->normalStrings; - $left1 = Formula($left1); - $right1 = Formula($right1); - Context()->texStrings; - $leftStepConstant = $left1->eval($y=>0,$x=>0)->reduce; - $rightStepConstant = $right1->eval($y=>0,$x=>0)->reduce; - $leftStepLinear = Fraction($left1->D("$y")->eval($x=>0),1)*$ysol; - $rightStepLinear = Fraction($right1->D("$y")->eval($x=>0),1)*$ysol; - Context()->normalStrings; - $leftStep = ($xside1 == 0) ? $left1 : Formula("$leftStepLinear+$leftStepConstant"); - $rightStep = ($xside1 == 1) ? $right1 : Formula("$rightStepLinear+$rightStepConstant"); - $leftNextStep = ($xside1 == 0) ? $x : $xsol; - $rightNextStep = ($xside1 == 1) ? $x : $xsol; - - Context()->texStrings; - - $Attack = $Attack." - -We can substitute this for [`$y`] back into the first equation to find [`$x`]."; - $right1 = "$bsided\\left($ysol\\right)+$esided" if ($xside1 == 0); - $left1 = "$bsided\\left($ysol\\right)+$esided" if ($xside1 == 1); - $Attack = $Attack." - - [``` - \\begin{aligned} - $left1 & = $right1 &\\text{(from the first equation, after we had solved for }$x\\text{ in terms of} $y\\text{)}\\\\ - $leftStep &= $rightStep\\\\ - $leftNextStep &= $rightNextStep - \\end{aligned} - ```] - -So the solution is [`$x=$xsol, $y=$ysol`]."; - - } -} -############################################################# -#If the first equation has 1y on either side... - elsif ($bsided == Fraction(1,1)) { - ($xside1,$yside1,$cside1) = (1-$yside1, $yside1, 1-$yside1); - $asided = $a*$lcd1*(-1)**$xside1; - $bsided = $b*$lcd1*(-1)**$yside1; - $esided = $e*$lcd1*(-1)**$cside1; - - ($leftOrd1, $rightOrd1) = ([0, 1, 2], [0, 1, 2]); - - ($left1, $right1, $left2, $right2, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided, $bsided, $csided, $dsided, $esided, $fsided, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2); - - $left1 = $left1->reduce; - $right1 = $right1->reduce; - $left2 = $left2->reduce; - $right2 = $right2->reduce; - $left2 = ($yside1 == 0) ? $left2->substitute($y=>$right1) : $left2->substitute($y=>$left1); - $right2 = ($yside1 == 0) ? $right2->substitute($y=>$right1) : $right2->substitute($y=>$left1); - Context()->normalStrings; - $left2 = Formula($left2); - $right2 = Formula($right2); - Context()->texStrings; - $leftStepLinear = $left2->D("$x")->reduce; - $leftStepConstant = $left2->eval($x=>0)->reduce; - $rightStepLinear = $right2->D("$x")->reduce; - $rightStepConstant = $right2->eval($x=>0)->reduce; - $leftStep = Formula("$leftStepLinear $x + $leftStepConstant")->reduce; - $rightStep = Formula("$rightStepLinear $x + $rightStepConstant")->reduce; - $leftNextStep = Formula("($leftStepLinear-$rightStepLinear)$x")->reduce; - $rightNextStep = Formula("$rightStepConstant-$leftStepConstant")->reduce; - Context()->flags->set(showExtraParens=>1); - $Attack = $Attack. - -"Since one of the coefficients of [`$y`] is [`1`], it is wise to solve for [`$y`] in terms of the other variable and then use substitution to complete the problem. - - [``` - \\begin{aligned} - $left1 & = $right1 &\\text{(from the first equation)} - \\end{aligned} - ```] - -which we can substitute in for [`$y`] into the second equation: - - [```\\begin{aligned} - $left2 & = $right2 &\\text{(from the second equation)}\\\\ - $leftStep & = $rightStep\\\\"; - - Context()->flags->set(showExtraParens=>0); - if ($leftStep ne $leftNextStep or $rightStep ne $rightNextStep) { - $Attack = $Attack."$leftNextStep & = $rightNextStep\\\\"; - } - - Context()->flags->set(showExtraParens=>1); - if ($determinant == 0 and ($leftNextStep != $rightNextStep)) { - $Attack = $Attack."\\end{aligned}```] - -Since this is a false equation no matter what the values of [`$x`] and [`$y`] are, the system has no solutions."; - } - elsif ($determinant == 0 and ($leftNextStep == $rightNextStep)) { - $Attack = $Attack."\\end{aligned}```] - -Since this is a true equation no matter what the values of [`$x`] and [`$y`] are, the system has infinitely many solutions - just pick any value for [`$x`] and then let [`$left1 = $right1`]."; - } - else { - $Attack = $Attack." - $x &= $xsol - \\end{aligned} - ```]"; - - Context()->normalStrings; - $left1 = Formula($left1); - $right1 = Formula($right1); - Context()->texStrings; - $leftStepConstant = $left1->eval($y=>0,$x=>0)->reduce; - $rightStepConstant = $right1->eval($y=>0,$x=>0)->reduce; - $leftStepLinear = Fraction($left1->D("$x")->eval($y=>0),1)*$xsol; - $rightStepLinear = Fraction($right1->D("$x")->eval($y=>0),1)*$xsol; - Context()->normalStrings; - $leftStep = ($yside1 == 0) ? $left1 : Formula("$leftStepLinear+$leftStepConstant"); - $rightStep = ($yside1 == 1) ? $right1 : Formula("$rightStepLinear+$rightStepConstant"); - $leftNextStep = ($yside1 == 0) ? $y : $ysol; - $rightNextStep = ($yside1 == 1) ? $y : $ysol; - - Context()->texStrings; - - $Attack = $Attack." - -We can substitute this back for [`$x`] into the first equation to find [`$y`]."; - $right1 = "$asided\\left($xsol\\right)+$esided" if ($yside1 == 0); - $left1 = "$asided\\left($xsol\\right)+$esided" if ($yside1 == 1); - $Attack = $Attack." - - [``` - \\begin{aligned} - $left1 & = $right1 &\\text{(from the first equation, after we had solved for }$y\\text{ in terms of }$x\\text{)}\\\\ - $leftStep &= $rightStep\\\\ - $leftNextStep &= $rightNextStep - \\end{aligned} - ```] - -So the solution is [`$x=$xsol, $y=$ysol`]."; - - } -} -############################################################# -#If no coefficient is 1 or -1, we use elimination -else{ - ($xside1,$yside1,$cside1) = (0, 0, 1); - ($xside2,$yside2,$cside2) = (0, 0, 1); - - $asided = $a*$lcd1*(-1)**$xside1; - $bsided = $b*$lcd1*(-1)**$yside1; - $csided = $c*$lcd2*(-1)**$xside2; - $dsided = $d*$lcd2*(-1)**$yside2; - $esided = $e*$lcd1*(-1)**$cside1; - $fsided = $f*$lcd2*(-1)**$cside2; - - $lcmx = lcm(int("$asided"), int("$csided")); - $lcmy = lcm(int("$bsided"), int("$dsided")); - - $eliminateYscalar1 = Real(int($lcmy/$bsided)); - $eliminateYscalar2 = -Real(int($lcmy/$dsided)); - - $eliminateXscalar1 = Real(int($lcmx/$asided)); - $eliminateXscalar2 = -Real(int($lcmx/$csided)); - - ($left1, $right1, $left2, $right2, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided, $bsided, $csided, $dsided, $esided, $fsided, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2); - - $left1Step = ($eliminateYscalar1 == 1) ? $left1 : "$eliminateYscalar1\\left($left1\\right)"; - $right1Step = ($eliminateYscalar1 == 1) ? $right1 : "$eliminateYscalar1\\left($right1\\right)"; - $left2Step = ($eliminateYscalar2 == 1) ? $left2 : "$eliminateYscalar2\\left($left2\\right)"; - $right2Step = ($eliminateYscalar2 == 1) ? $right2 : "$eliminateYscalar2\\left($right2\\right)"; - - - ($left1NextStep, $right1NextStep, $left2NextStep, $right2NextStep, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided*$eliminateYscalar1, $bsided*$eliminateYscalar1, $csided*$eliminateYscalar2, $dsided*$eliminateYscalar2, $esided*$eliminateYscalar1, $fsided*$eliminateYscalar2, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, [0,1,2], [0,1,2], [0,1,2], [0,1,2]); - - Context()->normalStrings; - $leftLastStep = Formula("($asided*$eliminateYscalar1+$csided*$eliminateYscalar2)*$x")->reduce->reduce; - $rightLastStep = Formula("$esided*$eliminateYscalar1+$fsided*$eliminateYscalar2")->reduce->reduce; - Context()->texStrings; - - $Attack = $Attack." Since no coefficients are [`1`] or [`-1`], we choose to use the method of elimination, also known as the addition method. The first step is to algebraically rearrange both equations into the standard form with all variables aligned on the left and constants on the right. - - [```\\left\\{\\begin{aligned} - $left1 & = $right1\\\\ - $left2 & = $right2 - \\end{aligned}\\right.```] - -Then we identify a common multiple of the [`$y`] coefficients. In this case, a common multiple is [`$lcmy`]. We rescale each equation so that the coefficients of [`$y`] become [`$lcmy`] and [`-$lcmy`]. - - [```\\left\\{\\begin{aligned} - $left1Step & = $right1Step\\\\ - $left2Step & = $right2Step - \\end{aligned}\\right.```] - - - [```\\left\\{\\begin{aligned} - $left1NextStep & = $right1NextStep\\\\ - $left2NextStep & = $right2NextStep - \\end{aligned}\\right.```] - -And now if we add left sides and right sides: - - [```\\begin{aligned} - $leftLastStep & = $rightLastStep\\\\ - \\end{aligned}```] - -"; - - if ($determinant == Fraction(0,1) and ($leftLastStep != $rightLastStep)) - {$Attack = $Attack."Since this is a false equation no matter what [`$x`] and [`$y`] are, the system has no solutions."} - elsif ($determinant == Fraction(0,1) and ($leftLastStep == $rightLastStep)) - {$Attack = $Attack."Since this equation is true no matter what [`$x`] and [`$y`] are, the system has infinitely solutions; Just let [`$x`] take any value and then use either equation to solve for [`$y`]. The pair that you get from this will satisfy both equations."} - else{ - $left1Step = ($eliminateXscalar1 == 1) ? $left1 : "$eliminateXscalar1\\left($left1\\right)"; - $right1Step = ($eliminateXscalar1 == 1) ? $right1 : "$eliminateXscalar1\\left($right1\\right)"; - $left2Step = ($eliminateXscalar2 == 1) ? $left2 : "$eliminateXscalar2\\left($left2\\right)"; - $right2Step = ($eliminateXscalar2 == 1) ? $right2 : "$eliminateXscalar2\\left($right2\\right)"; - - ($left1NextStep, $right1NextStep, $left2NextStep, $right2NextStep, $leftOrd1, $rightOrd1, $leftOrd2, $rightOrd2) = makeSides($asided*$eliminateXscalar1, $bsided*$eliminateXscalar1, $csided*$eliminateXscalar2, $dsided*$eliminateXscalar2, $esided*$eliminateXscalar1, $fsided*$eliminateXscalar2, $xside1, $yside1, $cside1, $xside2, $yside2, $cside2, $zeroa, $zerob, $zeroc, $zerod, $zeroe, $zerof, $x, $y, "constant", 0, [0,1,2], [0,1,2], [0,1,2], [0,1,2]); - - Context()->normalStrings; - $leftLastStep = Formula("($bsided*$eliminateXscalar1+$dsided*$eliminateXscalar2)*$y")->reduce->reduce; - $rightLastStep = Formula("$esided*$eliminateXscalar1+$fsided*$eliminateXscalar2")->reduce->reduce; - Context()->texStrings; - - $Attack = $Attack."So [`$x = $xsol`]. Now we go back to an earlier stage and take the same steps to eliminate [`$x`]. - - [```\\left\\{\\begin{aligned} - $left1Step & = $right1Step\\\\ - $left2Step & = $right2Step - \\end{aligned}\\right.```] - - - [```\\left\\{\\begin{aligned} - $left1NextStep & = $right1NextStep\\\\ - $left2NextStep & = $right2NextStep - \\end{aligned}\\right.```] - -And now if we add left sides and right sides: - - [```\\begin{aligned} - $leftLastStep & = $rightLastStep\\\\ - \\end{aligned}```] - -So [`$y=$ysol`]. So the solution to the system is [`$x=$xsol, $y=$ysol`]. - -"; - } -}; -}#end of else that begins after 0 coefficients are ruled out - - -Context()->normalStrings; -}; - - - - - - - - -# -# Set up the LinearSystems context -# -sub _SystemsOfLinearEquationsProblemPCC_init { - my $context = $main::context{LinearSystems} = Parser::Context->getCopy("LimitedFraction"); - - $context->flags->set( - reduceFractions => 0, - showMixedNumbers=>0, - showExtraParens=>0 ); - $context->parens->redefine('{'); - $context->operators->redefine(',',using=>',',from=>'Numeric'); - $context->operators->redefine('and',using=>',',from=>'Numeric'); - $context->operators->set( - ','=>{string=>' and ',TeX=>'\hbox{ and }'}, - 'and'=>{string=>' and ',TeX=>'\hbox{ and }'} - ); - $context->lists->set(List => {separator => " and "}); - - $context->strings->add("no solutions"=>{}, - "no solution"=>{alias=>'no solutions'}, - "none"=>{alias=>'no solutions'}, - "infinitely many solutions"=>{}, - "infinitely many"=>{alias=>'infinitely many solutions'}, - "infinite"=>{alias=>'infinitely many solutions'}, - ); - - $context->lists->set(Point => {open => "(", close => ")"}); - $context->parens->set("(" => {type => "Point", close => ")"}); - -}; - - - -sub solutionChecker { - my ($correct,$student,$ans,$nth,$value) = @_; - if ($correct->type eq "Assignment") { - my ($svar,$sfrac) = $student->value; # get the variable and fraction - #return 0 unless Value::classMatch($sfrac,'Fraction') && Fraction($sfrac)->isReduced; - if(Value::classMatch($sfrac,'Fraction')) - { - return 0 unless $sfrac->isReduced; - } - } - return $correct == $student; - }; - - - -sub extraChecker { - my ($student,$ansHash,$nth,$value) = @_; - if($student eq "no solutions") - { - $student->context->setError("This system of equations does have a solution","",undef,undef,$Value::CMP_WARNING) - unless $ans->{isPreview}; - return; - } - if($student eq "infinitely many solutions") - { - $student->context->setError("This system of equations has exactly one solution","",undef,undef,$Value::CMP_WARNING) - unless $ans->{isPreview}; - return; - } - if($student eq "infinity") - { - $student->context->setError("Infinity is a term for the concept of a *single* arbitrarily large number. Do you mean to say 'infinitely many solutions'?","",undef,undef,$Value::CMP_WARNING) - unless $ans->{isPreview}; - return; - } - if (($student->type ne "Assignment" && $ansHash->{student_formula}->type ne "Assignment")) { - if (($x ne "x") or ($y ne "y") or ($student->type ne "Point" && $ansHash->{student_formula}->type ne "Point")) { - $student->context->setError("Each part of your solution should be written in the form variable = value","",undef,undef,$Value::CMP_WARNING) - unless $ans->{isPreview}; - return; - } else { - $student->context->setError("This is a trigger to check in Point context","",undef,undef,$Value::CMP_WARNING) - unless $ans->{isPreview}; - return; - } - } - my ($svar,$sfrac) = $student->value; # get the variable and fraction - if (Value::classMatch($sfrac,'Fraction') && !$sfrac->isReduced) { - $student->context->setError("Your $nth $value is not reduced","",undef,undef,$Value::CMP_WARNING) - unless $ans->{isPreview}; - return; - } - return Value::Real->typeMatch($student); - }; - - -sub postFilterForPoints { - my $ansHash = shift; - if (!($ansHash->{score}) and ($ansHash->{ans_message} eq "This is a trigger to check in Point context")) { - my $studentString = $ansHash->{original_student_ans}; - my $check = $pointAns->cmp()->evaluate($studentString); - $ansHash = $check; - if ($check->{score} == 1) - { - my ($studx,$study) = $ansHash->{student_value}->value; - $ansHash->{score} = $check->{score} ; - $ansHash->{ans_message} = $check->{ans_message}; - $ansHash->{correct_ans_latex_string} = $pointAns->TeX; - if(Value::classMatch($study,'Fraction')) - { - if (!($study->isReduced) or ($study->value)[1] == 1) - { - $ansHash->{score} = 0; - $ansHash->{ans_message} = "Your second entry is not reduced"; - }; - }; - if(Value::classMatch($studx,'Fraction')) - { - if (!($studx->isReduced) or ($studx->value)[1] == 1) - { - $ansHash->{score} = 0; - $ansHash->{ans_message} = "Your first entry is not reduced"; - }; - }; - } - } - return $ansHash; - }; - - - - - -1; diff --git a/OpenProblemLibrary/macros/PCC/contextAssignmentForm.pl b/OpenProblemLibrary/macros/PCC/contextAssignmentForm.pl deleted file mode 100644 index 1bd54e0f33..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextAssignmentForm.pl +++ /dev/null @@ -1,96 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# 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 -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -Describe this context here. - -=head1 DESCRIPTION - -=cut - -loadMacros( - "MathObjects.pl", - "bizarroArithmetic.pl", - "parserAssignment.pl", -); - -sub _contextAssignmentForm_init { - my $context = $main::context{AssignmentForm} = Parser::Context->getCopy("Numeric"); - $context->{name} = "AssignmentForm"; - parser::Assignment->Allow($context); - parser::Assignment->Function($context,"f"); - $context->flags->set( - reduceConstants =>0, - reduceConstantFunctions => 0, - formatStudentAnswer => "parsed", - checkSqrt => 0, - checkRoot => 0, - setSqrt => exp(1)/main::ln(2), - wrongFormMessage => 'Your answer is algebraically equivalent to the correct answer, but not in the expected form. Maybe it is not fully simplified. Maybe something is not completely factored. Maybe it is not in the expected form for some other reason.', - - ); - $context->variables->are(x=>'Real',y=>'Real'); - $context->noreduce('(-x)+y','(-x)-y'); - $context->operators->set( - '+' => {class => 'bizarro::BOP::add', isCommand => 1}, # override + - '-' => {class => 'bizarro::BOP::subtract', isCommand => 1}, # override - - ' ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '*' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '* ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - ' *' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '/ ' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ' /' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '//' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ); - $context->functions->set(sqrt=>{class=>'assignmentForm::Function::numeric'}); # override sqrt() - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - $student = $ans->{student_formula}; - my $setSqrt = Context()->flag("setSqrt"); - Context()->flags->set(checkSqrt => $setSqrt, bizarroAdd => 1, bizarroSub => 1, bizarroMul => 1, bizarroDiv => 1); - delete $correct->{test_values}; - delete $student->{test_values}; - my $OK = ($correct == $student); - Context()->flags->set(checkSqrt => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0); - Value::Error(Context()->flag('wrongFormMessage')) unless $OK; - return $OK; - }; -} - - -########################### -# -# Subclass the numeric functions -# -package assignmentForm::Function::numeric; -our @ISA = ('Parser::Function::numeric'); - -# -# Override sqrt() to return a special value times x when evaluated -# -sub sqrt { - my $self = shift; - my $x = shift; - my $value = $self->context->flag("checkSqrt"); - return $value+1 if $value && $x == 1; # force sqrt(1) to be incorrect - return $value+1 if $value && $x == $value; # force sqrt(sqrt(x)) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkRoot"); # force sqrt(root(n,x))) to be incorrect - return $value*$x if $value; - return $self->SUPER::sqrt($x); -} diff --git a/OpenProblemLibrary/macros/PCC/contextFiniteSolutionSets.pl b/OpenProblemLibrary/macros/PCC/contextFiniteSolutionSets.pl deleted file mode 100644 index 4827db03c7..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextFiniteSolutionSets.pl +++ /dev/null @@ -1,309 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# 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 -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -contextFiniteSolutionSets.pl - Allows several common input forms for a finite -solution set. - -After setting the context to "FiniteSolutionSets", make an answer like: -Formula("1,2") -Formula("1/2,2/3,3/4") -Formula("sqrt(2),(1+sqrt(2))/3, -(sqrt(2)+3sqrt(5))/6") -List(Formula("1")) - -Note that if it is a singleton, the structure is different. - -Then students can enter the answer in any number of ways. Like: -1,2 -x=1,2 -x=1,x=2 -x=1 or 2 -x=1 or x=2 -{1,2} - -The "x" should be the only context variable, and it should be what the student -needed to solve for. - -If the answer is not submitted like {1,2}, and if the flag preferSetNotation is -set to 1 (which is the default) then there is a message that this is the -preferred form (but the student still gets credit). - -Answers like 2/3 and (1+sqrt(2))/3 need to be in that form, so the problem -author needs to be careful to not do things like Formula("1/2,$a/$b,3/4") -where $a and $b have a nontrivial common divisor. - -No decimals are allowed. - -=head1 DESCRIPTION - -=cut - -loadMacros( - "MathObjects.pl", - "bizarroArithmetic.pl", - "parserAssignment.pl", - "contextFraction.pl", - "parserRoot.pl", - "PGinfo.pl", -); - -sub _contextFiniteSolutionSets_init { - my $context = $main::context{FiniteSolutionSets} = Parser::Context->getCopy("Interval"); - $context->{name} = "FiniteSolutionSets"; - Parser::Number::NoDecimals($context); - parser::Assignment->Allow($context); - $context->flags->set( - reduceConstants =>0, - reduceConstantFunctions => 0, - formatStudentAnswer => "parsed", - checkSqrt => 0, - checkRoot => 0, - useBizarro => 1, - setSqrt => exp(1)/main::ln(2), - setRoot => exp(2)/main::ln(3), - wrongFormMessage => 'Your answer is numerically correct, but not in the expected form', - preferSetNotation => 1, - #tolerance => 0.00001, - ); - $context->noreduce('(-x)+y','(-x)-y'); - $context->operators->set( - '+' => {class => 'bizarro::BOP::add', isCommand => 1}, # override + - '-' => {class => 'bizarro::BOP::subtract', isCommand => 1}, # override - - ' ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '*' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '* ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - ' *' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '/ ' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ' /' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '//' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ); - $context->operators->undefine('^','**'); - $context->parens->undefine('|'); - $context->operators->redefine('or',using=>',',from=>'Numeric'); - $context->operators->redefine(',or',using=>',',from=>'Numeric'); - $context->operators->redefine(', or',using=>',',from=>'Numeric'); - $context->operators->redefine('and',using=>',',from=>'Numeric'); - $context->operators->redefine(',and',using=>',',from=>'Numeric'); - $context->operators->redefine(', and',using=>',',from=>'Numeric'); - $context->strings->add("no real solutions"=>{}, - "no real solution"=>{alias=>'no real solutions'}, - "none"=>{alias=>'no real solutions'}, - "no solution"=>{alias=>'no real solutions'}, - "no solutions"=>{alias=>'no real solutions'}, - #Hack. Investigate making all of this be a constant. - "{}" =>{alias=>'no real solutions'}, - "{ }" =>{alias=>'no real solutions'}, - "{ }" =>{alias=>'no real solutions'}, - "{ }" =>{alias=>'no real solutions'}, - "infinitely many solutions"=>{}, - "infinitely many"=>{alias=>'infinitely many solutions'}, - "infinite solutions"=>{alias=>'infinitely many solutions'}, - "infinite"=>{alias=>'infinitely many solutions'}, - ); - $context->functions->set(sqrt=>{class=>'finiteSolutionSets::Function::numeric'}); # override sqrt() - $context->functions->add(identity => {class => 'finiteSolutionSets::Function::numeric'}); - parser::Root->Enable($context); - $context->functions->set(root=>{class=>'finiteSolutionSets::Function::numeric2'}); # override root() - $context->lists->set(List => {open => "{", close => "}"}); - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - if ($student->class eq 'String') {return 1 if $correct == $student}; - $student = $ans->{student_formula}; - $student = Formula("identity($student)"); #ensure student is parsed as Formula object - my $setSqrt = Context()->flag("setSqrt"); - my $setRoot = Context()->flag("setRoot"); - my $useBizarro = (Context()->flag("useBizarro")) ? 1 : 0; - Context()->flags->set(checkSqrt => $setSqrt, checkRoot => $setRoot, bizarroAdd => $useBizarro, bizarroSub => $useBizarro, bizarroMul => $useBizarro, bizarroDiv => $useBizarro); - delete $correct->{test_points}; - delete $student->{test_points}; - delete $correct->{test_values}; - delete $student->{test_values}; - my $OK = ($correct == $student); - Context()->flags->set(checkSqrt => 0, checkRoot => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0); - Value::Error(Context()->flag('wrongFormMessage')) unless $OK; - return $OK; - }; - $context->{cmpDefaults}{Formula}{requireParenMatch} = 0; - $context->{cmpDefaults}{Formula}{list_checker} = sub { - my ($correct,$student,$ansHash,$value) = @_; - my $score = 0; # number of correct student answers - my @errors = (); # stores error messages - my $i, $j, $k; # loop counters - my $studentFormula = $ansHash->{student_formula}; - - # Student answer needs to be a set, list, a single number, or a single assignment - my $studentTypeOK = 0; - if ($studentFormula->type eq 'Set') {$studentTypeOK = 1}; - if ($studentFormula->type eq 'List') {$studentTypeOK = 1}; - if ($studentFormula->type eq 'Assignment') {$studentTypeOK = 1}; - if ($studentFormula->class eq 'Formula' and $studentFormula->type eq 'Number') { - if ($studentFormula->isConstant) {$studentTypeOK = 1}; - } - if ($studentFormula->type eq 'String') {return(0)}; - - push(@errors,'Your answer is not a list of numbers or assignments') unless $studentTypeOK; - return (0,@errors) unless $studentTypeOK; - - # Array of the student answers - my @studentanswers; - if ($studentFormula->type eq 'Assignment') {@studentanswers = ($studentFormula)}; - if ($studentFormula->class eq 'Formula' and $studentFormula->type eq 'Number') {@studentanswers = ($studentFormula)}; - if ($studentFormula->type eq 'List') {@studentanswers = $studentFormula->value}; - if ($studentFormula->type eq 'Set') {@studentanswers = $studentFormula->value}; - my $n = scalar(@studentanswers); # number of student answers - - # Array of the correct answers - my @correctanswers = $ansHash->{correct_value}->value; - my $m = scalar(@correctanswers); # number of correct answers - - # - # Loop though the student answers - ## - for ($i = 0; $i < $n; $i++) { - my $ith = ($n == 1) ? '' : Value::List->NameForNumber($i+1); - my $p = $studentanswers[$i]; # i-th student answer - # - # Check that the student's answer is a number or assignment - # - if ($p->class ne "Formula" or $p->type ne "Number" and $p->type ne "Assignment") { - push(@errors,"Your $ith answer is not a number or assignment"); - $score--; next; - } - if ($p->class eq "Formula" and $p->type eq "Number") { - if (not($p->isConstant)) { - push(@errors,"Your $ith answer is not a number or assignment"); - $score--; next; - } - } - # - # For assignments, grab number - # - my $q = ($p->type eq "Assignment") ? Formula((split('=',$p))[1]) : $p; - # - # Check that the number isn't an unreduced fraction - # - $mycontext = Context(); - Context("Fraction"); - Context()->flags->set(reduceFractions => 0); - my $qfrac = Compute("$q"); - if ($qfrac->class eq 'Fraction') { - do {push(@errors,$q->string." is not a reduced fraction"); $score--; next;} unless $qfrac->isReduced; - do {push(@errors,$q->string." can be simplified"); $score--; next;} unless (($qfrac->value)[1] != 1); - }; - Context($mycontext); - # - # Check that the number hasn't been given before - # - for ($j = 0, $used = 0; $j < $i; $j++) { - my $r = $studentanswers[$j]; - if ($r->class eq "Formula" and $r->type eq "Number" and $r == $q) { - push(@errors,"Your $ith answer is the same as a previous one") unless $ansHash->{isPreview}; - $used = 1; $score--; last; - } - if ($r->type eq "Assignment") { - $r = Formula((split('=',$r))[1]); - if ($r == $q) { - push(@errors,"Your $ith answer is the same as a previous one") unless $ansHash->{isPreview}; - $used = 1; $score--; last; - } - } - } - # - # If not already used, compare to each of the correct answers - # and increase the score if there is a match. If there is no - # match, take note if the failure is because of form - # - if (!$used) { - my $qcmp; - for ($k = 0, $match = 0, $badform = 0; $k < $m; $k++) { - my $s = $correctanswers[$k]; - $qcmp = $s->cmp->evaluate($q->string); - if ($qcmp->{score}) {$match = 1; last;}; - if ($qcmp->{ans_message} eq Context()->flag('wrongFormMessage')) {$badform = 1;}; - } - if ($match == 1) {$score++} else { - $score--; - do { - if ($badform == 1) {push(@errors, Context()->flag('wrongFormMessage') =~ s/Your answer/Your $ith answer/gr)} - elsif ($n != 1) {push(@errors, "Your $ith answer is not correct")} - } unless $ansHash->{isPreview} - } - } - } - # - # Check that there are the right number of answers - # - if (!$ansHash->{isPreview}) { - push(@errors,"You need to provide more numbers") if $n < $m and $score == $n; - push(@errors,"You have given too many answers") if $score > $m; - } - # - # Express a preference for formatting - # - if ($studentFormula->type ne 'Set' and $m == $score and Context()->flags->get('preferSetNotation') == 1 ) - {push(@errors,"The preferred notation for the solution set is${BR}\\(\\left\\{" . join(',',map{$_->TeX}(@correctanswers)) . "\\right\\}\\)" )}; - return ($score,@errors); - }; -} - - -########################### -# -# Subclass the numeric functions -# -package finiteSolutionSets::Function::numeric; -our @ISA = ('Parser::Function::numeric'); - -# -# Override sqrt() to return a special value times x when evaluated -# -sub sqrt { - my $self = shift; - my $x = shift; - my $value = $self->context->flag("checkSqrt"); - return $value+1 if $value && $x == 1; # force sqrt(1) to be incorrect - return $value+1 if $value && $x == $value; # force sqrt(sqrt(x)) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkRoot"); # force sqrt(root(n,x))) to be incorrect - return $value*$x if $value; - return $self->SUPER::sqrt($x); -} - -sub identity { - my $self = shift; - my $x = shift; - return $x; -} - -package finiteSolutionSets::Function::numeric2; -our @ISA = ('parser::Root::Function::numeric2'); -# -# Override root(n,) to return a special value times x when evaluated -# -sub root { - my $self = shift; - my ($n,$x) = @_; - my $value = $self->context->flag("checkRoot"); - return $value if $value && $x == 1; # force root(n,1) to be incorrect - return $value if $value && $n == 1; # force root(1,x) to be incorrect - return $value if $value && $x == $value; # force root(n,root(n,x)) to be incorrect - return $value if $value && $x == $self->context->flag("checkSqrt"); # force root(n,sqrt(x)) to be incorrect - return $value*$x if $value; - return $self->SUPER::root($n,$x); -} - diff --git a/OpenProblemLibrary/macros/PCC/contextForm.pl b/OpenProblemLibrary/macros/PCC/contextForm.pl deleted file mode 100644 index 78f87abd60..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextForm.pl +++ /dev/null @@ -1,140 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# 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 -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -This context distinguishes between "forms" of an expression by using bizarro -arithmetic. - -For example, the answer could be "(x+1)(x+2)". Bizarro arithmetic always has -commutative and associative addidition and multiplication, so it would be OK -to anwer with "(2+x)(x+1)". - -But this context initally only uses bizarro with multiplication and division. -So "x^2+3x+2" will not evaluate to the same as "(x+1)(x+2)". - -The reverse works as well: the answer could be "x^2+3x+2" and "(x+1)(x+2)" -will not be accepted. - -In general if you have a problem where "form" matters, try this context. It -may not always work for you out of the box. But even then you may be able to -adjust the bizarro details to make it work. - -For example if you wanted to factor x^2+2x+1 and you declare "(x+1)^2" to be -the answer, at first it will not accept "(x+1)(x+1)". Because bizarro exponents -are not activated. But you could activate them (or deactivate bizarro multiplication -and division while activating bizarro addition and subtraction) and then -"(x+1)^2" and "(x+1)(x+1)" would be equivalent, yet distinct from "x^2+2x+1". - - -=head1 DESCRIPTION - -=cut - -loadMacros( - "MathObjects.pl", - "bizarroArithmetic.pl", - "parserRoot.pl", -); - -sub _contextForm_init { - my $context = $main::context{Form} = Parser::Context->getCopy("Numeric"); - Parser::Number::NoDecimals($context); - $context->{name} = "Form"; - $context->flags->set( - reduceConstants =>0, - reduceConstantFunctions => 0, - formatStudentAnswer => "parsed", - checkSqrt => 0, - checkRoot => 0, - setSqrt => exp(1)/main::ln(2), - setRoot => exp(2)/main::ln(3), - wrongFormMessage => 'Your answer is algebraically equivalent to the correct answer, but not in the expected form. Maybe it is not fully simplified. Maybe something is not completely factored or expanded. Maybe it is not in the expected form for some other reason.', - - ); - $context->noreduce('(-x)+y','(-x)-y'); - $context->operators->set( - '*' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '* ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - ' *' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '/ ' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ' /' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ); - $context->functions->set(sqrt=>{class=>'form::Function::numeric'}); # override sqrt() - parser::Root->Enable($context); - $context->functions->set(root=>{class=>'form::Function::numeric2'}); # override root() - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - $student = $ans->{student_formula}; - my $setSqrt = Context()->flag("setSqrt"); - my $setRoot = Context()->flag("setRoot"); - Context()->flags->set(checkSqrt => $setSqrt, checkRoot => $setRoot, bizarroAdd => 1, bizarroSub => 1, bizarroMul => 1, bizarroDiv => 1); - delete $correct->{test_values}; - delete $student->{test_values}; - my $OK = ($correct == $student); - Context()->flags->set(checkSqrt => 0, checkRoot => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0); - Value::Error(Context()->flag('wrongFormMessage')) unless $OK; - return $OK; - }; - $context->strings->add( - 'not a real number' => {}, - 'not real' => {alias => 'not a real number'}, - 'does not exist' => {alias => 'not a real number'}, - "doesn't exist" => {alias => 'not a real number'}, - ); -} - - -########################### -# -# Subclass the numeric functions -# -package form::Function::numeric; -our @ISA = ('Parser::Function::numeric'); - -# -# Override sqrt() to return a special value times x when evaluated -# -sub sqrt { - my $self = shift; - my $x = shift; - my $value = $self->context->flag("checkSqrt"); - return $value+1 if $value && $x == 1; # force sqrt(1) to be incorrect - return $value+1 if $value && $x == $value; # force sqrt(sqrt(x)) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkRoot"); # force sqrt(root(n,x))) to be incorrect - return $value*$x if $value; - return $self->SUPER::sqrt($x); -} - -package form::Function::numeric2; -our @ISA = ('parser::Root::Function::numeric2'); -# -# Override root(n,) to return a special value times x when evaluated -# -sub root { - my $self = shift; - my ($n,$x) = @_; - my $value = $self->context->flag("checkRoot"); - return $value if $value && $x == 1; # force root(n,1) to be incorrect - return $value if $value && $n == 1; # force root(1,x) to be incorrect - return $value if $value && $x == $value; # force root(n,root(n,x)) to be incorrect - return $value if $value && $x == $self->context->flag("checkSqrt"); # force root(n,sqrt(x)) to be incorrect - return $value*$x if $value; - return $self->SUPER::root($n,$x); -} - diff --git a/OpenProblemLibrary/macros/PCC/contextInequalitySetBuilder.pl b/OpenProblemLibrary/macros/PCC/contextInequalitySetBuilder.pl deleted file mode 100755 index 5666abfcf5..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextInequalitySetBuilder.pl +++ /dev/null @@ -1,384 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2013 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 -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - - -=head1 NAME - -Context("InequalitySetBuilder"), Context("InequalitySetBuilder-Only") - Provides -contexts that allow sets to be specified using set-builder notation and inequalities. - -=head1 DESCRIPTION - -Implements contexts that provides for sets described using set-builder -notation with inequalities. (This actually is a special way of -creating Intervals, Sets, and Unions, and they can be used together -with standard interval notation.) There are two such contexts: -Context("InequalitySetBuilder"), in which both intervals and sets -formed by inequalities are defined, and -Context("InequalitySetBuilder-Only"), which allows only set-builder -notation (not intervals or point sets). - -=head1 USAGE - - loadMacros("contextInequalitySetBuilder.pl"); - - Context("InequalitySetBuilder"); - $S1 = Compute("{ x : 1 < x <= 4 }"); - $S2 = SetBuilder("(1,4]"); # force interval to be inequality - - Context("InequalitySetBuilder-Only"); - $S1 = Compute("{ x : 1 < x <= 4 }"); - $S2 = SetBuilder("(1,4]"); # generates an error - - $S3 = Compute("{ x : x < -2 or x > 2 }"); # forms the Union (-inf,-2) U (2,inf) - $S4 = Compute("{ x : x > 2 and x <= 4 }"); # forms the Interval (2,4] - $S5 = Compute("{ x : x = 1 }"); # forms the Set {1} - $S6 = Compute("{ x : x != 1 }"); # forms the Union (-inf,1) U (1,inf) - -The InequalitySetBuilder contexts accept the flags for the -Inequalities contexts from the contextInequalities.pl file (see its -documentation for details). - -Set-builder and interval notation both can coexist side by side, but -you may wish to convert from one to the other. Use SetBuilder() to -convert from an Interval, Set or Union to an Inequality, and use -Interval() to convert from an Inequality object to one in interval -notation. For example: - - $I0 = Compute("(1,2]"); # the interval (1,2] - $I1 = SetBuilder($I1); # the set { x : 1 < x <= 2 } - - $I0 = Compute("{ x : 1 < x <= 2 }"); # the set { x : 1 < x <= 2 } - $I1 = Interval($I0); # the interval (1,2] - -Note that sets and intervals can be compared and combined -regardless of the format, so $I0 == $I1 is true in either example -above. - -Since SetBuilder objects are actually Interval objects in disguise, -the variable used to create them doesn't matter. That is, - - $I0 = Compute("{ x : 1 < x <= 2 }"); - $I1 = Compute("{ y : 1 < y <= 2 }"); - -would both produce the same interval, so $I0 == $I1 would be true in -this case. If you need to distinguish between these two, use - - $I0 == $I1 && $I0->{varName} eq $I1->{varName} - -instead. - -Note that the "such that" symbol is ":" since the vertical line is -already in use for absolute values. If you wish to use "|" rather -than ":", you can do that, but must then use "abs()" to obtain -absolute values. To enable the vertical line as "such that", use - - InequalitySetBuilder::UseVerticalSuchThat(); - -prior to setting the context to one of the set-builder contexts. This -will disable ":" and enable "|" as such-that rather than absolute-value. - -=cut - -loadMacros("contextInequalities.pl"); - -sub _contextInequalitySetBuilder_init {InequalitySetBuilder::Init()} - -package InequalitySetBuilder; - -sub Init { - # - # Make a new context from an old one and add SetBuilder notation - # - my $addSetBuilder = sub { - my ($new,$old) = @_; - my $context = $main::context{$new} = Parser::Context->getCopy($old); - $context->{name} = $new; - $context->operators->add( - ":" => {precedence => .25, associativity => "left", type => "bin", string => " : ", - class => "InequalitySetBuilder::BOP::suchthat"}, - "_suchthat" => {alias => ":", hidden => 1}, - ); - $context->operators->set( - "+" => {class => "InequalitySetBuilder::BOP::add"}, - "-" => {class => "InequalitySetBuilder::BOP::subtract"}, - "U" => {class => "InequalitySetBuilder::BOP::union"}, - ); - $context->lists->set(Set => {class => "InequalitySetBuilder::List::Set"}); - $context->{precedence}{InequalitySetBuilder} = $context->{precedence}{Inequality} + .5; - $context->{value}{SetBuilder} = "InequalitySetBuilder::SetBuilder"; - $context->{value}{InequalitySetBuilder} = "InequalitySetBuilder::SetBuilder"; - $context->{value}{InequalitySetBuilderInterval} = "InequalitySetBuilder::Interval"; - $context->{value}{InequalitySetBuilderUnion} = "InequalitySetBuilder::Union"; - $context->{value}{InequalitySetBuilderSet} = "InequalitySetBuilder::Set"; - $context->{error}{msg}{"You are not allowed to use intervals or sets in this context"} = - "You are not allowed to use intervals in this context"; - return $context; - }; - - # - # Make the two new contexts - # - &{$addSetBuilder}("InequalitySetBuilder","Inequalities"); - &{$addSetBuilder}("InequalitySetBuilder-Only","Inequalities-Only")->flags->set(noSets => 1); - - # - # Define the SetBuilder() constructor - # - main::PG_restricted_eval('sub SetBuilder {Value->Package("SetBuilder")->new(@_)}'); -} - -sub UseVerticalSuchThat { - my $adjust = sub { - my $context = $main::context{$_[0]}; - $context->operators->remove(":"); - $context->operators->set( - "|" => {precedence => .25, associativity => "left", type => "bin", string => " | ", TeX => ' \mid ', - class => "InequalitySetBuilder::BOP::suchthat"}, - "_suchthat" => {alias => "|", hidden => 1}, - ); - $context->parens->remove("|"); - }; - &{$adjust}("InequalitySetBuilder"); - &{$adjust}("InequalitySetBuilder-Only"); -} - -################################################## -# -# A class for making set-builder sets by hand -# -package InequalitySetBuilder::SetBuilder; -our @ISA = ('Value'); - -sub new { - my $self = shift; my $class = ref($self) || $self; - my $context = (Value::isContext($_[0]) ? shift : $self->context); - my $S = shift; my $x = shift; - $S = Value::makeValue($S,context=>$context); - if (Value::classMatch($S,"InequalitySetBuilder")) { - if (defined($x)) {$S->{varName} = $x; $S->updateParts} - return $S; - } - $x = ($context->variables->names)[0] unless $x; - $S = bless $S->inContext($context), $context->Package("InequalitySetBuilder".$S->type); - $S->{varName} = $x; $S->{reduceSets} = $S->{"is".$S->Type} = 1; - $S->updateParts; - return $S; -} - -################################################## -################################################## -# -# The Parser object that holds the set-builder notation -# (and also allows point-set notation) -# -package InequalitySetBuilder::List::Set; -our @ISA = ('Parser::List::Set'); - -sub _check { - my $self = shift; my $arg = $self->{coords}[0]; - if ($self->{type}{length} == 1 && $arg->{isSuchThat}) { - $self->{type}{list} = 0; - $self->{type} = $arg->{type}; - $self->{isSetBuilder} = 1; - } else { - $self->SUPER::_check(@_); - $self->Error("You are not allowed to use point sets in this context") - if $self->context->flag("noSets"); - } -} - -sub eval { - my $self = shift; - if ($self->{isSetBuilder}) { - my $set = $self->{coords}[0]->{rop}->eval; - return $set->demote if $set->string eq $self->context->flag("noneWord"); - $set->{isSetBuilder} = $set->{isInequality} = 1; - bless $set, $self->Package("InequalitySetBuilder".$set->type); - return $set; - } else { - return $self->SUPER::eval(@_); - } -} - -sub canBeInUnion {1} - -################################################## -################################################## -# -# The such-that operator -# -package InequalitySetBuilder::BOP::suchthat; -our @ISA = ('Parser::BOP'); - -sub _check { - my $self = shift; - $self->Error("%s should follow a variable name",$self->{bop}) - unless $self->{lop}->class eq 'Variable'; - $self->Error("The condition for the set should be an inequality") - unless $self->{rop}{isInequality}; - $self->Error("The variable of the condition to the right of %s should match the variable on the left",$self->{bop}) - unless $self->{lop}{name} eq $self->{rop}{varName}; - $self->{type} = $self->{rop}->typeRef; - $self->{isSuchThat} = 1; - delete $self->{equation}{variables}{$self->{lop}{name}}; - $self->{lop} = Inequalities::DummyVariable->new($self->{equation},$self->{lop}{name},$self->{lop}{ref}); -} - -# -# Make sure it is only used in set braces -# -sub eval { - my $self = shift; - $self->Error("'%s' can only appear within set-builder notation (did you forget braces?)",$self->{bop}); -} - -################################################## -# -# Give a warning about adding sets -# -package InequalitySetBuilder::BOP::add; -our @ISA = ('Inequalities::BOP::add'); - -sub _check { - my $self = shift; - $self->SUPER::_check(@_); - $self->Error("Can't add sets (do you mean to use a union?)") - if $self->{lop}{isSetBuilder} || $self->{rop}{isSetBuilder}; -} - -################################################## -# -# Handle subtraction of sets -# -package InequalitySetBuilder::BOP::subtract; -our @ISA = ("Inequalities::BOP::subtract"); - -sub _check { - my $self = shift; - if ($self->{lop}{isSetBuilder} && $self->{rop}{isSetBuilder}) - {$self->{isSetBuilder} = 1} else {$self->SUPER::_check(@_)} -} - -################################################## -# -# Handle unions of sets -# -package InequalitySetBuilder::BOP::union; -our @ISA = ("Parser::BOP::union"); - -sub _check { - my $self = shift; - if ($self->{lop}{isSetBuilder} && $self->{rop}{isSetBuilder}) - {$self->{isSetBuilder} = 1} else {$self->SUPER::_check(@_)} -} - -################################################## -################################################## -# -# Common function for the classes below -# - -package InequalitySetBuilder::common; -our @ISA = (); - -sub _updateParts { - my $self = shift; - $self->{isSetBuilder} = 1; -} - -sub _apply { - my $self = shift; my $I = shift; - $I->{isSetBuilder} = 1; - bless $I, $self->Package("InequalitySetBuilder".$I->type); - return $I; -} - -sub _string { - my $self = shift; my $string = shift; - my $bop = $self->context->operators->get("_suchthat") || {}; - while ($bop->{alias}) {$bop = $self->context->operators->get($bop->{alias})} - return '{ '.$self->{varName}.($bop->{string}||' : ').$string.' }'; -} - -sub _TeX { - my $self = shift; my $string = shift; - my $bop = $self->context->operators->get("_suchthat") || {}; - while ($bop->{alias}) {$bop = $self->context->operators->get($bop->{alias})} - return '\{ '.$self->{varName}.($bop->{TeX}||$bop->{string}||' : ').$string.' \}'; -} - -sub _typeMatch { - my ($self,$other,$result) = @_; - return 0 if ($result && $other->class eq 'Inequality'); - return $result; -} - -sub class {"InequalitySetBuilder"} -sub cmp_class {"a Set in Set-Builder Notation"} -sub showClass {"a Set in Set-Builder Notation"} - - -################################################## -# -# Special Inequalities::Interval subclass that -# prints using set-builder notation. -# -package InequalitySetBuilder::Interval; -our @ISA = ('InequalitySetBuilder::common', 'Inequalities::Interval'); - -sub updateParts {my $self = shift; $self->_updateParts($self->SUPER::updateParts)} -sub apply {my $self = shift; $self->_apply($self->SUPER::apply(@_))} -sub string {my $self = shift; $self->_string($self->SUPER::string)} -sub TeX {my $self = shift; $self->_TeX($self->SUPER::TeX)} -sub typeMatch {my $self = shift; my $other = Value::makeValue(shift); - $self->_typeMatch($other,$self->SUPER::typeMatch($other))} - -################################################## -# -# Special Inequalities::Union subclass that -# prints using set-builder notation. -# - -package InequalitySetBuilder::Union; -our @ISA = ('InequalitySetBuilder::common', 'Inequalities::Union'); - -sub updateParts {my $self = shift; $self->_updateParts($self->SUPER::updateParts)} -sub apply {my $self = shift; $self->_apply($self->SUPER::apply(@_))} -sub string {my $self = shift; $self->_string($self->SUPER::string)} -sub TeX {my $self = shift; $self->_TeX($self->SUPER::TeX)} -sub typeMatch {my $self = shift; my $other = Value::makeValue(shift); - $self->_typeMatch($other,$self->SUPER::typeMatch($other))} - -################################################## -# -# Special Inequalities::Set subclass that -# prints using set-builder notation. -# - -package InequalitySetBuilder::Set; -our @ISA = ('InequalitySetBuilder::common', 'Inequalities::Set'); - -sub updateParts {my $self = shift; $self->_updateParts($self->SUPER::updateParts)} -sub apply {my $self = shift; $self->_apply($self->SUPER::apply(@_))} -sub string {my $self = shift; $self->_string($self->SUPER::string)} -sub TeX {my $self = shift; $self->_TeX($self->SUPER::TeX)} -sub typeMatch {my $self = shift; my $other = Value::makeValue(shift); - $self->_typeMatch($other,$self->SUPER::typeMatch($other))} - -################################################## - -1; diff --git a/OpenProblemLibrary/macros/PCC/contextLimitedFactor.pl b/OpenProblemLibrary/macros/PCC/contextLimitedFactor.pl deleted file mode 100755 index 192fbe7cd9..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextLimitedFactor.pl +++ /dev/null @@ -1,68 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2013 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader: pg/macros/parserPrime.pl,v 1.2 2009/10/03 15:58:49 dpvc Exp $ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -Context file to check that the students answer is completely factored -when compared with the answer. - -The flag factorableObject defaults to 'polynomial', but it could be set to say, 'rational expression', etc. It is used in error messages. - -=cut - -loadMacros( - "bizarroArithmetic.pl", - -); - -# -# Set up the LimitedFactor context -# -sub _contextLimitedFactor_init { - my $context = $main::context{LimitedFactor} = Parser::Context->getCopy("Numeric"); - $context->operators->set( - '+' => {class => 'bizarro::BOP::add', isCommand => 1}, - '-' => {class => 'bizarro::BOP::subtract', isCommand => 1}, - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, - ); - - main::PG_restricted_eval('sub root {Parser::Function->call("root",@_)}'); - $context->functions->add( - root => {class => 'my::Function::numeric2'}, - ); - $context->flags->set(limits => [1,5]); # no negatives in the radicals - $context->flags->set(factorableObject => 'polynomial'); - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - $student = $ans->{student_formula}; - $correct = $correct->{original_formula} if defined $correct->{original_formula}; - # check for equivalence when bizarro arithmetic is enforced - Context()->flags->set(bizarroSub=> 1,bizarroAdd=> 1, bizarroDiv=> 1); - delete $correct->{test_values}; - delete $student->{test_values}; - my $OK = ($correct == $student); # check if equal when arithmetic is replace by bizarro arithmetic - Context()->flags->set(bizarroSub=> 0,bizarroAdd=> 0, bizarroDiv=> 0); - $factorableObject = Context()->flag("factorableObject"); - Value::Error("Your answer is equivalent to the $factorableObject in the correct answer, but not completely factored or simplified") unless $OK; - return $OK; - }; - -} - - - -1; diff --git a/OpenProblemLibrary/macros/PCC/contextLimitedRadical.pl b/OpenProblemLibrary/macros/PCC/contextLimitedRadical.pl deleted file mode 100755 index 3371440414..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextLimitedRadical.pl +++ /dev/null @@ -1,143 +0,0 @@ -=head1 NAME - -contextLimitedRadical.pl - defines a root(n,x) function for n-th root of x, and -allows for specification of forms of radical answers - -=head1 DESCRIPTION - -This file enables a context LimitedRadical in which students and pg-authors may use root(n,x) for the nth root of x. The function checks for n=0, and also allows for negative x only if n is an odd integer. - -By default, the limits for Formula answer checking are [0,5]. If it is important to distinguish sqrt(x^2) as |x| rather than x, then the limits should be changed to include a region where they differ. - -The context requires that radical expressions (using either sqrt or root) meet the form of the professor's answer with respect to simplification and rationalization of denominators. - -To accomplish this, Math Objects like "root(3,2)" should be created as Formula("root(3,2)"), not Compute("root(3,2)"), or they will be treated as Reals and evaluated as decimals. Compute("root(3,x)") should be fine though, since it is unambiguously a Formula. - -Student answers are first compared to the correct answer in the usual way to see if they are at least numerically correct. - -Then to check that expressions are fully simplified, during a check, both sqrt(x) and root(n,x) are temporarily replaced by certain functions. These values should be some number not likely to arise in student answers. Also +, -, *, and / are temporarily replaced with bizarro arithmetic (from bizarroArithmetic.pl) that nearly make for a bizarro field structure on R. If the student and author answers disagree after this change, then the student's answer is not in form of the author's answer. - -For example "1+2" is not equivalent to "3" under bizarro addition. "2sqrt(2)" is not equivalent to "sqrt(8)" with the bizarro arithmetic. - -Any radical applied to 1 is declared unsimplified as well. - -If a student's answer is numerically equal to the author's, but not equal under bizarro arithmetic, students get the message that their answer is not fully simplified. So care should be taken by the problem author when defining answers. - -The near-field structure of the bizarro versions means that "1+sqrt(2)+sqrt(3)" can be entered as "sqrt(3)+sqrt(2)+1", etc, and not be declared "unsimplified". Also "(1+sqrt(2))/2" can be entered as "1/2+sqrt(2)/2" without being declared "unsimplified". However, "(1+2sqrt(2))/2" *will* be declared different from "1/2+sqrt(2)", even though both are generally considered fully simplified. If an author can foresee this arising, consider using parserOneOf.pl to allow for multiple forms of the correct answer. - -Technically both "sqrt(3)/3" and "1/sqrt(3)" are fully simplified, although you may want rational denominators. This context will declare them equal but not equivalent. The author should either accept both as correct using parserOneOf.pl, or give the message that the student's answer does not have a rational denominator using answerHints.pl. - -Note that there is nothing here that is actually doing any reducing of radical quantities or honestly checking for "simplified" answers - so authors need to make sure their answers are reduced and simplified. For example, if the correct answer is "sqrt(2)" and the student enters "sqrt(3)+sqrt(3)" or "4sqrt(2)/2", they will just be told they are wrong, not that their (incorrect) answer is unsimplified. - - -=cut -loadMacros( - "contextLimitedPowers.pl", - "bizarroArithmetic.pl", - -); - -# -# Set up the LimitedRadical context -# -sub _contextLimitedRadical_init { - my $context = $main::context{LimitedRadical} = Parser::Context->getCopy("Numeric"); - Parser::Number::NoDecimals($context); - $context->flags->set(setSqrt => exp(1)/ln(2), setRoot => exp(1)/ln(2)); - $context->operators->set( - '+' => {class => 'bizarro::BOP::add', isCommand => 1}, # override + - '-' => {class => 'bizarro::BOP::subtract', isCommand => 1}, # override - - '*' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '* ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - ' *' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '/ ' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ' /' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '//' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / -); - $context->functions->set(sqrt=>{class=>'my::Function::numeric'}); # override sqrt() - main::PG_restricted_eval('sub root {Parser::Function->call("root",@_)}'); - $context->functions->add( - root => {class => 'my::Function::numeric2'}, - ); - $context->flags->set(limits => [0,5]); # no negatives in the radicals - $context->flags->set(reduceConstantFunctions=>0, - reduceConstants=>0, - formatStudentAnswer =>parsed, - checkSqrt => 0, - checkRoot => 0); - LimitedPowers::OnlyPositiveIntegers($context); # don't allow powers of 1/2, 1/3, etc - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - $student = $ans->{student_formula}; - $correct = $correct->{original_formula} if defined $correct->{original_formula}; - $student = Formula("$student"); $correct = Formula("$correct"); #ensure both are Formula objects - my ($setSqrt, $setRoot) = (Context()->flag("setSqrt"), Context()->flag("setRoot")); - Context()->flags->set(checkSqrt => $setSqrt, checkRoot => $setRoot, bizarroAdd => 1, bizarroSub => 1, bizarroMul => 1, bizarroDiv => 1); - delete $correct->{test_values}; - delete $student->{test_values}; - my $OK = (($correct == $student) or ($student == $correct)); # check if equal when sqrt's are replaced by 1 - Context()->flags->set(checkSqrt => 0, checkRoot => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0); - Value::Error("You must simplify your answer further") unless $OK; - return $OK; - }; -} - -########################### -# -# Create root(n, x) -# - -package my::Function::numeric2; -our @ISA = ('Parser::Function::numeric2'); - - -sub root { - my $self = shift; my $n = shift; my $x = shift; - $self->Error("Can't take 0th roots") if ($n == 0); - $self->Error("Can't take general roots of negative numbers") if (($x < 0) and (($n-1)/2 != int(($n-1)/2))); - my $value = $self->context->flag("checkRoot"); - return $value+1 if $value && $x == 1; # force root(n,1) to be incorrect - return $value+1 if $value && $x == $value; # force root(n,root(m,x))) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkSqrt"); # force root(n,sqrt(x))) to be incorrect - return $value*$x if $value; - return ($x)**(1/$n) if (($x > 0) and ($n != 0)); - return -(abs($x)**(1/$n)) if (($x < 0) and (($n-1)/2 == int(($n-1)/2))); -} - - -sub TeX { - my $self = shift; - my ($n,$x) = ($self->{params}[0],$self->{params}[1]); - return '\sqrt['.$n->TeX."]{".$x->TeX."}"; -} - - - -########################### -# -# Subclass the numeric functions -# -package my::Function::numeric; -our @ISA = ('Parser::Function::numeric'); - -# -# Override sqrt() to return a special value times x when evaluated -# -sub sqrt { - my $self = shift; - my $x = shift; - my $value = $self->context->flag("checkSqrt"); - return $value+1 if $value && $x == 1; # force sqrt(1) to be incorrect - return $value+1 if $value && $x == $value; # force sqrt(sqrt(x)) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkRoot"); # force sqrt(root(n,x))) to be incorrect - return $value*$x if $value; - return $self->SUPER::sqrt($x); -} - - - - - -1; diff --git a/OpenProblemLibrary/macros/PCC/contextLimitedRadicalComplex.pl b/OpenProblemLibrary/macros/PCC/contextLimitedRadicalComplex.pl deleted file mode 100644 index 54ea79c3c2..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextLimitedRadicalComplex.pl +++ /dev/null @@ -1,166 +0,0 @@ -=head1 NAME -contextLimitedRadical.pl - defines a root(n,x) function for n-th root of x, and -allows for specification of forms of radical answers, like simplified radicals or -with rational denominators - -=head1 DESCRIPTION - -This file enables a context LimitedRadical in which students and pg-authors may use -root(n,x) for the nth root of x. The function makes sure n!=0, and also allows for -negative x only if n is an odd integer. - -By default, the limits for Formula answer checking are [0,5] to avoid negatives in -radicals. If it is important to distinguish sqrt(x^2) as |x| rather than x, then the -limits should be changed to include a region where they differ. - -The context requires that radical expressions (using either sqrt() or root()) meet -the form of the author's answer with respect to simplification and rationalization -of denominators. - -To accomplish this, Math Objects like "root(3,2)" should be created as Formula("root(3,2)"), -not Compute("root(3,2)"), or they will be treated as Reals and evaluated as decimals. -Compute("root(3,x)") should be fine though, since it is unambiguously a Formula. - -Student answers are first compared to the correct answer in the usual way to see if -they are at least numerically correct. Then to check that expressions are fully -simplified, during a check, both sqrt(x) and root(n,x) are temporarily replaced by certain -other functions. Also +, -, *, and / are temporarily replaced with bizarro arithmetic -(from bizarroArithmetic.pl) that nearly make for a bizarro field structure on R. -If the student and author answers disagree after this change, then the student's answer -is not in form of the author's answer. - -For example "1+2" is not equivalent to "3" under bizarro addition. "2sqrt(2)" is not -equivalent to "sqrt(8)" with the bizarro arithmetic. Any radical applied to 1 is declared -unsimplified as well. - -If a student's answer is numerically equal to the author's, but not equal under bizarro -arithmetic, students get the message that their answer is not fully simplified. So care -should be taken by the problem author when defining answers. - -The near-field structure of the bizarro versions means that "1+sqrt(2)+sqrt(3)" can be -entered as "sqrt(3)+sqrt(2)+1", etc, and not be declared "unsimplified". Also "(1+sqrt(2))/2" -can be entered as "1/2+sqrt(2)/2" without being declared "unsimplified". However, -"(1+2sqrt(2))/2" *will* be declared different from "1/2+sqrt(2)", even though both are -generally considered fully simplified. If an author can foresee this arising, consider -using parserOneOf.pl to allow for multiple forms of the correct answer. - -Technically both "sqrt(3)/3" and "1/sqrt(3)" are fully simplified, although you may want -rational denominators. This context will declare them equal but not equivalent. The author -should either accept both as correct using parserOneOf.pl, or give the message that the -student's answer does not have a rational denominator using answerHints.pl. - -Note that there is nothing here that is actually doing any reducing of radical quantities -or honestly checking for "simplified" answers - so authors need to make sure their answers -are reduced and simplified. Also if the correct answer is "sqrt(2)" and the student -enters "sqrt(3)+sqrt(3)" or "4sqrt(2)/2", they will just be told they are wrong, not that -their (incorrect) answer is unsimplified. - - -=cut -loadMacros( - "contextLimitedPowers.pl", - "bizarroArithmetic.pl", - -); - -# -# Set up the LimitedRadical context -# -sub _contextLimitedRadicalComplex_init { - my $context = $main::context{LimitedRadicalComplex} = Parser::Context->getCopy("Complex"); - Parser::Number::NoDecimals($context); - $context->flags->set(setSqrt => exp(1)/ln(2), setRoot => exp(1)/ln(2)); - $context->operators->set( - '+' => {class => 'bizarro::BOP::add', isCommand => 1}, # override + - '-' => {class => 'bizarro::BOP::subtract', isCommand => 1}, # override - - '*' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '* ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - ' *' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '/ ' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ' /' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '//' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / -); - $context->functions->set(sqrt=>{class=>'my::Function::numeric'}); # override sqrt() - main::PG_restricted_eval('sub root {Parser::Function->call("root",@_)}'); - $context->functions->add( - root => {class => 'my::Function::numeric2'}, - ); - $context->flags->set(limits => [0,5]); # no negatives in the radicals - $context->flags->set(reduceConstantFunctions=>0, - reduceConstants=>0, - formatStudentAnswer =>parsed, - checkSqrt => 0, - checkRoot => 0); - LimitedPowers::OnlyPositiveIntegers($context); # don't allow powers of 1/2, 1/3, etc - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - $student = $ans->{student_formula}; - $correct = $correct->{original_formula} if defined $correct->{original_formula}; - $student = Formula("$student"); $correct = Formula("$correct"); #ensure both are Formula objects - my ($setSqrt, $setRoot) = (Context()->flag("setSqrt"), Context()->flag("setRoot")); - Context()->flags->set(checkSqrt => $setSqrt, checkRoot => $setRoot, bizarroAdd => 1, bizarroSub => 1, bizarroMul => 1, bizarroDiv => 1); - delete $correct->{test_values}, $student->{test_values}; - my $OK = (($correct == $student) or ($student == $correct)); # check if equal when sqrt's are replaced by 1 - Context()->flags->set(checkSqrt => 0, checkRoot => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0); - Value::Error("You must simplify your answer further") unless $OK; - return $OK; - }; -} - -########################### -# -# Create root(n, x) -# - -package my::Function::numeric2; -our @ISA = ('Parser::Function::numeric2'); - - -sub root { - my $self = shift; my $n = shift; my $x = shift; - $self->Error("Can't take 0th roots") if ($n == 0); - $self->Error("Can't take general roots of negative numbers") if (($x < 0) and (($n-1)/2 != int(($n-1)/2))); - my $value = $self->context->flag("checkRoot"); - return $value+1 if $value && $x == 1; # force root(n,1) to be incorrect - return $value+1 if $value && $x == $value; # force root(n,root(m,x))) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkSqrt"); # force root(n,sqrt(x))) to be incorrect - return $value*$x if $value; - return ($x)**(1/$n) if (($x > 0) and ($n != 0)); - return -(abs($x)**(1/$n)) if (($x < 0) and (($n-1)/2 == int(($n-1)/2))); -} - - -sub TeX { - my $self = shift; - my ($n,$x) = ($self->{params}[0],$self->{params}[1]); - return '\sqrt['.$n->TeX."]{".$x->TeX."}"; -} - - - -########################### -# -# Subclass the numeric functions -# -package my::Function::numeric; -our @ISA = ('Parser::Function::numeric'); - -# -# Override sqrt() to return a special value times x when evaluated -# -sub sqrt { - my $self = shift; - my $x = shift; - my $value = $self->context->flag("checkSqrt"); - return $value+1 if $value && $x == 1; # force sqrt(1) to be incorrect - return $value+1 if $value && $x == $value; # force sqrt(sqrt(x)) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkRoot"); # force sqrt(root(n,x))) to be incorrect - return $value*$x if $value; - return $self->SUPER::sqrt($x); -} - - - -1; diff --git a/OpenProblemLibrary/macros/PCC/contextPercent.pl b/OpenProblemLibrary/macros/PCC/contextPercent.pl deleted file mode 100755 index 688587a50a..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextPercent.pl +++ /dev/null @@ -1,529 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright � 2000-2013 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 -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -F - Context for entering percentages using C<%> - -=head1 DESCRIPTION - -This file implements a context in which students can enter percentage -values using C<%>. There are a number of options that allow you to control -the number of decimal places, and whether mathematical operations are allowed. - -To use the context, put - - loadMacros("contextPercent.pl"); - -at the top of your problem file, and then issue the - - Context("Percent"); - -command to select the context. To create a Percent value, use - - $m = Compute("10%"); - -or - - $m = Percent(.1); - -and so on. Both of these produce equivalent values (10%). - -There are also three limited contexts that can be obtained using one of the -following: - - Context("LimitedPercent"); - Context("LimitedPercent-strict"); - Context("Percent-strict"); - -The C context allows you to use percentages rather freely -within an expression. For example - - Compute("10% + 20%"); - -would produce the same result as C, and so would -either of - - Compute("10% + .2"); - Compute("(5+5)% + (2^2*ln(e*5))%"); - -In this way, C is treated essentially as C<(n/100)>. - -You can enforce more strstrictive rules using the C or -C contexts. The first of these allows -operations within the number that preceeds the percent sign, but does -not allow you to combine percentages with real numbers or other -percentages. In this way, C<(5+5)%> is legal, but C<5% + 5%> is not. - -The C context disallows all computations, and -only allows you to enter percentages as a single numeric constant -followed by a percent sign. - -Finally, the C context allows you to combine percent -values, but the numeric part of the percent must be a constant, not an -expression. For example, C<10% + 5%> is allowed, but not C<(10+5)%>. - -As with all MathObjects, you obtain an answer checker for a Percent -object via the C method, e.g., - - ANS($m->cmp); - -There are a number of options that you can supply to control the -details of the answer checker, listed below. These can also be set as -flags within the Context so that they affect all Percent objects that -you create. For example, - - ANS($m->cmp(forceDecimals=>1)); - -or - - Context()->flags->set(forceDecimals => 1); - ANS($m->cmp) - -would both produce answer checkers for C<$m> where the student would -have to enter decimals that include any trailing zeros. - -=over - -=item C 1 >>> - -This controls the number of decimal places to use in output of Percent -objects, and the number that a student will be required to enter (when -the appropriate flags are given, as described below). The -C is set by default to be C<.0005>, so if you change the -number of decimal places, you should change the C to -correspond to it (two more zeros than C). For example - - Context()->flags->set( - decimalPlaces => 2, - tolerance => .00005, - ); - -The default value is 1 decimal place. - -=item C 0 >>> - -This value controls whether students are forced to enter the full -number of decimals, even if there are trailing zeros. When set to 0 -(the default for the C, C, and -C contexts), trailing zeros may be left off, so -C could be entered as just C<10%> by a student. -When set to 1 (the default for the C context), -if C is 2, then C would require the -student to enter C<10.00%> rather than C<10%> or C<10.0%>. It really -only makes sense to set this in the C context, -since in the other two, percent values can be computed, and so the -number of decimal places is not really known. - -=item C 0 >>> - -This determines whether students are allowed to enter decimals beyond -the number given by the C flag. If set to 0 (the -default for teh C, C, and C -contexts), any number of decimals are allowed (but the C -determines what values are meaningfull), while if set to 1 (the -default for the C context), students are not -allowed to enter more than the required number of decimals and receive -a warning if they do. - -=item C 1 >>> - -This determines whether the output for Percent objects will have -trailing zeros removed. When set to 1 (the default for the -C, C, and C contexts), if -C is 2, then C<10.00%> will display as C<10%> and -C<10.30%> will display as C<10.3%>. When set to 0 (the default for -C), trailing zeros will be retained, so the -output will always have exactly C decimal values, even -if they are zero. - -=item C 1 >>> - -This determines whether real numbers can be combined and compared with -percentages. When set to 1 (the default for the C context), -students could enter C as either C<10%> or C<.1> or -C>>, while when set to 0 (the default for the Limited -and strict contexts), students must enter percentages using the -percent symbol (C<%>). - -=item C 0 >>> - -This controls whether the number preceding a percent sign can be a -computation or whether it must be a numeric constant. When set to 0 -(the default for the C and C contexts), -students can use formulas to create a percentage value, e.g., -C>> for C<15%>. When set to 1 (the default for the -C and C context), only numeric -constants can be followed by the percent sign. Note that setting this -flag to 1 also requires that you set C and -C to 1 as well. - -=back - -The default C of .0005 works properly only if your original -percent values have no more than 1 decimal place. If you were to do - - $m = Compute("10.15%"); - -for example, then $m would print as C<10.2%>, but neither a student -answer of C<10.1%> nor of C<10.2%> would be marked correct. That is -because neither of these are less than .0005 away from the correct -answer of C<10.15%> (remember that C<10.15%> is .1015). If you create -percentages that have more decimal places than the usual one place, -you may want to round or truncate them. Percent objects have two -methods for accomplishing this: C and C, which -produce rounded or truncated copies of the original Percent object: - - $m = Compute("34.127%")->round; # produces 34.13% - $m = Compute("34.127%")->truncate; # produces 34.12% - -These are particularly helpful if you are producing percentages via -computations. - -=cut - -loadMacros("MathObjects.pl"); - -sub _contextPercent_init {Percent::Init()} - -package Percent; - -# -# Initialization sets up a Perent() constructor and -# creates the percent contexts. -# -sub Init { - my $context = $main::context{Percent} = Parser::Context->getCopy("Numeric"); - $context->{name} = "Percent"; - - $context->operators->add( - '%' => {precedence => 5.5, associativity => "right", type => "unary", - TeX => "\\%", class => 'Percent::UOP::percent'}, - ); - $context->{parser}{Number} = "Percent::Number"; - $context->{value}{Percent} = "Percent::Percent"; - $context->flags->set( - decimalPlaces => 1, # number of decimal places to show or require - tolerance => .0005, - tolType => "absolute", - promoteReals => 1, # treat .1 as 10% or not - strictPercent => 0, # allow (5+5)% or just 10% (used by LimitedPercent contexts) - forceDecimals => 0, # require at least the full number of decimals or not - noExtraDecimals => 0, # prevent extra decimals or not - trimTrailingZeros => 1, # remove trailing zeros in display - ); - - $context = $main::context{LimitedPercent} = $context->copy; - $context->{name} = "LimitedPercent"; - $context->flags->set( - promoteReals => 0, - reduceConstants => 0, - reduceConstantFunctions => 0, - ); - $context->operators->set( - '+' => {class => 'LimitedPercent::BOP::add'}, - '-' => {class => 'LimitedPercent::BOP::subtract'}, - '*' => {class => 'LimitedPercent::BOP::multiply'}, - '* ' => {class => 'LimitedPercent::BOP::multiply'}, - ' *' => {class => 'LimitedPercent::BOP::multiply'}, - ' ' => {class => 'LimitedPercent::BOP::multiply'}, - '/' => {class => 'LimitedPercent::BOP::divide'}, - ' /' => {class => 'LimitedPercent::BOP::divide'}, - '/ ' => {class => 'LimitedPercent::BOP::divide'}, - '^' => {class => 'LimitedPercent::BOP::power'}, - '**' => {class => 'LimitedPercent::BOP::power'}, - 'u+' => {class => 'LimitedPercent::UOP::plus'}, - 'u-' => {class => 'LimitedPercent::UOP::minus'}, - ); - $context->lists->set( - AbsoluteValue => {class => 'LimitedPercent::List::AbsoluteValue'}, - ); - - $context = $main::context{"LimitedPercent-strict"} = $context->copy; - $context->{name} = "LimitedPercent-strict"; - $context->flags->set( - strictPercent => 1, - noExtraDecimals => 1, - trimTrailingZeros => 0, - ); - - $context = $main::context{"Percent-strict"} = Parser::Context->getCopy("Percent"); - $context->{name} = "Percent-strict"; - $context->flags->set( - strictPercent => 1, - promoteReals => 0, - reduceConstants => 0, - reduceConstantFunctions => 0, - ); - - main::PG_restricted_eval('sub Percent {Value->Package("Percent")->new(@_)}'); -} - -###################################################################### -###################################################################### -# -# When creating Number objects in the Parser, keep Percent objects. -# -package Percent::Number; -our @ISA = ('Parser::Number'); - -sub new { - my $self = shift; my $equation = shift; my $value = shift; - my $context = $equation->{context}; - if (Value::classMatch($value,"Percent")) { - # - # Put it back into a Value object, but must unmark it - # as a Real temporarily to avoid an infinite loop. - # - $value->{isReal} = 0; - $value = $self->Item("Value")->new($equation,[$value]); - $value->{value}{isReal} = 1; - } else { - $value = $self->SUPER::new($equation,$value,@_); - } - return $value; -} - -################################################## -# -# This class implements the percent symbol. -# It checks that its operand is a numeric constant -# in the correct format, and produces -# a Percent object when evaluated. -# -package Percent::UOP::percent; -our @ISA = ('Parser::UOP'); - -sub _check { - my $self = shift; - my $context = $self->context; - my $op = $self->{op}; my $value = $op->{value_string}; - $self->Error("'%s' can only be used with numeric constants",$self->{uop}) - unless !$context->flag("strictPercent") || ($op->type eq 'Number' && $op->class eq 'Number'); - $self->Error("Can't take percent of a percent") if $op->{isPercent}; - $self->{ref} = $op->{ref}; # highlight the number, not the operator - my $n = $context->flag("decimalPlaces"); - if (defined($value)) { - $self->Error("Your percentage value must have no more than %s decimal place%s",$n,($n == 1 ? "" : "s")) - if $value =~ m/\.\d{$n}\d/ && $context->flag('noExtraDecimals'); - $self->Error("Your percentage value requires %s decimal place%s",$n,($n == 1 ? "" : "s")) - if $n && $context->flag("forceDecimals") && $value !~ m/\.\d{$n,}$/; - } - $self->{type} = {%{$op->typeRef}}; - $self->{isPercent} = 1; -} - -sub _eval { - my $self = shift; my $value = (shift) / 100; - Value->Package("Percent")->make($self->context,$value,@_); -} - -# -# Use the Percent MathObject to produce the output formats -# -sub string {(shift)->eval->string} -sub TeX {(shift)->eval->TeX} -sub perl {(shift)->eval->perl} - - -###################################################################### -###################################################################### -# -# This is the MathObject class for Percent objects. -# It is basically a Real(), but one that stringifies -# and texifies itself to include the percent symbol, -# and evaluates to its value divided by 100. -# -package Percent::Percent; -our @ISA = ('Value::Real'); - -# -# We need to override the new() and make() methods -# so that the Percent object will be counted as -# a Value object. If we aren't promoting Reals, -# produce an error message. -# -sub new { - my $self = shift; my $class = ref($self) || $self; - my $context = (Value::isContext($_[0]) ? shift : $self->context); - my $x = shift; - Value::Error("You can't use %s as a percentage",lc(Value::showClass($x))) - if !$self->getFlag("promoteReals",1) && Value::isRealNumber($x) && !Value::classMatch($x,"Percent"); - $self = bless $self->SUPER::new($context,$x,@_), $class; - $self->{isReal} = $self->{isValue} = $self->{isPercent} = 1; - return $self; -} - -sub make { - my $self = shift; my $class = ref($self) || $self; - my $x = (Value::isContext($_[0]) ? $_[1] : $_[0]); - $self = bless $self->SUPER::make(@_), $class; - $self->{isReal} = $self->{isValue} = $self->{isPercent} = 1; - return $self; -} - -sub round { - my $self = shift; my $format = "%.".$self->getFlag("decimalPlaces")."f"; - my $s = ($self->value >= 0 ? "" : "-"); - return $self->make((($s.main::prfmt(CORE::abs($self->value*100),$format)) + 0)/100); -} - -sub truncate { - my $self = shift; my $d = $self->getFlag("decimalPlaces"); - my $n = $self->value*100; $n =~ s/(\.\d{$d}).*/$1/; - return $self->make(($n+0)/100); -} - -# -# Format the output as a percent value. -# -sub format { - my $self = shift; my $type = shift; - my $format = "%.".$self->getFlag("decimalPlaces")."f"; - my $s = ($self->value >= 0 ? "" : "-"); - my $c = main::prfmt(CORE::abs($self->value*100),$format); - if ($self->getFlag('trimTrailingZeros')) {$c =~ s/(\.\d*?)0+$/$1/; $c =~ s/\.$//} - return $s.$c.($type eq "TeX" ? "\\%" : "%"); -} - -sub string {(shift)->format("string")} -sub TeX {(shift)->format("TeX")} - -# -# Override the class name to get better error messages -# -sub cmp_class {"a Percentage Value"} - -# -# Check for whether we want to work with reals as percents -# -sub typeMatch { - my $self = shift; my $other = shift; my $ans = shift; - return $self->SUPER::typeMatch($other,$ans,@_) if $self->getFlag("promoteReals"); - return Value::classMatch($other,'Percent'); -} - -###################################################################### -###################################################################### -# -# LimitedPercent contexts -# - -################################################## -# -# Handle common checking for BOPs -# -package LimitedPercent::BOP; - -# -# Do original check and then if the operands are numbers, its OK. -# Otherwise report an error. -# -sub _check { - my $self = shift; - my $super = ref($self); $super =~ s/LimitedPercent/Parser/; - &{$super."::_check"}($self); - return unless $self->{lop}{isPercent} || $self->{rop}{isPercent}; - my $bop = $self->{def}{string} || $self->{bop}; - $self->Error("In this context, '%s' can only be used with Numbers",$bop); -} - -############################################## -# -# Now we get the individual replacements for the operators -# that we don't want to allow. We inherit everything from -# the original Parser::BOP class, except the _check -# routine, which comes from LimitedPercent::BOP above. -# - -package LimitedPercent::BOP::add; -our @ISA = qw(LimitedPercent::BOP Parser::BOP::add); - -############################################## - -package LimitedPercent::BOP::subtract; -our @ISA = qw(LimitedPercent::BOP Parser::BOP::subtract); - -############################################## - -package LimitedPercent::BOP::multiply; -our @ISA = qw(LimitedPercent::BOP Parser::BOP::multiply); - -############################################## - -package LimitedPercent::BOP::divide; -our @ISA = qw(LimitedPercent::BOP Parser::BOP::divide); - -############################################## - -package LimitedPercent::BOP::divide; -our @ISA = qw(LimitedPercent::BOP Parser::BOP::divide); - -############################################## - -package LimitedPercent::BOP::power; -our @ISA = qw(LimitedPercent::BOP Parser::BOP::power); - -############################################## -############################################## -# -# Now we do the same for the unary operators -# - -package LimitedPercent::UOP; - -sub _check { - my $self = shift; - my $super = ref($self); $super =~ s/LimitedPercent/Parser/; - &{$super."::_check"}($self); - return unless $self->{op}{isPercent}; - my $uop = $self->{def}{string} || $self->{uop}; - $self->Error("In this context, '%s' can only be used with Numbers",$uop); -} - -############################################## - -package LimitedPercent::UOP::plus; -our @ISA = qw(LimitedPercent::UOP Parser::UOP::plus); - -############################################## - -package LimitedPercent::UOP::minus; -our @ISA = qw(LimitedPercent::UOP Parser::UOP::minus); - -############################################## -############################################## -# -# Absolute value does vector norm, so we -# trap that as well. -# - -package LimitedPercent::List::AbsoluteValue; -our @ISA = qw(Parser::List::AbsoluteValue); - -sub _check { - my $self = shift; - $self->SUPER::_check; - return unless $self->{coords}[0]{isPercent}; - $self->Error("In this context, absolute values can only be used with Numbers"); -} - -############################################## -############################################## - -###################################################################### - -1; diff --git a/OpenProblemLibrary/macros/PCC/contextRationalExponent.pl b/OpenProblemLibrary/macros/PCC/contextRationalExponent.pl deleted file mode 100755 index ef28a53f44..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextRationalExponent.pl +++ /dev/null @@ -1,133 +0,0 @@ -=head1 NAME - -contextRationalExponent.pl - allows the requirement of simplified rational exponent answers. May be a misnomer since nothing requires exponents to be rational. - -=head1 DESCRIPTION - -This code is a copy of contextLimitedRadical.pl, with the following changes: -- no need for contextLimitedPowers.pl, since we need to use bizarro power, so that macro is removed -- name of the context changed to RationalExponent -- set class of ^ and ** to bizarro -- removed LimitedPowers::OnlyPositiveIntegers($context) call -- set bizarroPow flags in answer checking (on, then off) -- undefined sqrt and root (after root is added to the context). These can be redefined by a problem author if desired. -- added special messages if sqrt or root are used when they were undefined - -=cut -loadMacros( - "bizarroArithmetic.pl", - -); - -# -# Set up the RationalExponent context -# -sub _contextRationalExponent_init { - my $context = $main::context{RationalExponent} = Parser::Context->getCopy("Numeric"); - Parser::Number::NoDecimals($context); - $context->flags->set(setSqrt => exp(1)/ln(2), setRoot => exp(1)/ln(2)); - $context->operators->set( - '+' => {class => 'bizarro::BOP::add', isCommand => 1}, # override + - '-' => {class => 'bizarro::BOP::subtract', isCommand => 1}, # override - - '*' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '* ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - ' *' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '/ ' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ' /' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '//' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '**' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef}, # override ** - '^' => {class => 'bizarro::BOP::power', isCommand => 1, perl=>undef}, # override ^ -); - $context->functions->set(sqrt=>{class=>'my::Function::numeric'}); # override sqrt() - main::PG_restricted_eval('sub root {Parser::Function->call("root",@_)}'); - $context->functions->add( - root => {class => 'my::Function::numeric2'}, - ); - $context->flags->set(limits => [0,5]); # no negatives in the radicals - $context->flags->set(reduceConstantFunctions=>0, - reduceConstants=>0, - formatStudentAnswer =>parsed, - checkSqrt => 0, - checkRoot => 0); - $context->functions->undefine('sqrt'); - $context->functions->undefine('root'); - $context->{error}{msg}{"Function 'root' is not allowed in this context"} - = "Please use rational exponents, not root or sqrt"; - $context->{error}{msg}{"Function 'sqrt' is not allowed in this context"} - = "Please use rational exponents, not sqrt or root"; - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - $student = $ans->{student_formula}; - $correct = $correct->{original_formula} if defined $correct->{original_formula}; - $student = Formula("$student"); $correct = Formula("$correct"); #ensure both are Formula objects - my ($setSqrt, $setRoot) = (Context()->flag("setSqrt"), Context()->flag("setRoot")); - Context()->flags->set(checkSqrt => $setSqrt, checkRoot => $setRoot, bizarroAdd => 1, bizarroSub => 1, bizarroMul => 1, bizarroDiv => 1, bizarroPow => 1); - delete $correct->{test_values}; - delete $student->{test_values}; - my $OK = (($correct == $student) or ($student == $correct)); # check if equal when sqrt's are replaced by 1 - Context()->flags->set(checkSqrt => 0, checkRoot => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0, bizarroPow => 0); - Value::Error("You must simplify your answer further") unless $OK; - return $OK; - }; - -} - -########################### -# -# Create root(n, x) -# - -package my::Function::numeric2; -our @ISA = ('Parser::Function::numeric2'); - - -sub root { - my $self = shift; my $n = shift; my $x = shift; - $self->Error("Can't take 0th roots") if ($n == 0); - $self->Error("Can't take general roots of negative numbers") if (($x < 0) and (($n-1)/2 != int(($n-1)/2))); - my $value = $self->context->flag("checkRoot"); - return $value+1 if $value && $x == 1; # force root(n,1) to be incorrect - return $value+1 if $value && $x == $value; # force root(n,root(m,x))) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkSqrt"); # force root(n,sqrt(x))) to be incorrect - return $value*$x if $value; - return ($x)**(1/$n) if (($x > 0) and ($n != 0)); - return -(abs($x)**(1/$n)) if (($x < 0) and (($n-1)/2 == int(($n-1)/2))); -} - - -sub TeX { - my $self = shift; - my ($n,$x) = ($self->{params}[0],$self->{params}[1]); - return '\sqrt['.$n->TeX."]{".$x->TeX."}"; -} - - - -########################### -# -# Subclass the numeric functions -# -package my::Function::numeric; -our @ISA = ('Parser::Function::numeric'); - -# -# Override sqrt() to return a special value times x when evaluated -# -sub sqrt { - my $self = shift; - my $x = shift; - my $value = $self->context->flag("checkSqrt"); - return $value+1 if $value && $x == 1; # force sqrt(1) to be incorrect - return $value+1 if $value && $x == $value; # force sqrt(sqrt(x)) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkRoot"); # force sqrt(root(n,x))) to be incorrect - return $value*$x if $value; - return $self->SUPER::sqrt($x); -} - - - - - -1; diff --git a/OpenProblemLibrary/macros/PCC/contextRestrictedDomains.pl b/OpenProblemLibrary/macros/PCC/contextRestrictedDomains.pl deleted file mode 100644 index 0878528e7f..0000000000 --- a/OpenProblemLibrary/macros/PCC/contextRestrictedDomains.pl +++ /dev/null @@ -1,204 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright @copy; 2000-2018 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader$ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -Describe this context here. - -=head1 DESCRIPTION - -=cut - -loadMacros( - "MathObjects.pl", - "bizarroArithmetic.pl", - "contextInequalitySetBuilder.pl", - "PGinfo.pl", -); - -sub _contextRestrictedDomains_init { - my $context = $main::context{RestrictedDomains} = Parser::Context->getCopy("InequalitySetBuilder"); - $context->{name} = "RestrictedDomains"; - $context->flags->set( - reduceConstants =>0, - reduceConstantFunctions => 0, - formatStudentAnswer => "parsed", - checkSqrt => 0, - checkRoot => 0, - setSqrt => exp(1)/main::ln(2), - wrongFormMessage => 'Your answer is algebraically equivalent to the correct answer, but not in the expected form. Maybe it is not fully simplified. Maybe something is not completely factored. Maybe it is not in the expected form for some other reason.', - useBizarro => 1, - expressionWeight => 0.9, - ); - $context->noreduce('(-x)+y','(-x)-y'); - $context->operators->set( - '+' => {class => 'bizarro::BOP::add', isCommand => 1}, # override + - '-' => {class => 'bizarro::BOP::subtract', isCommand => 1}, # override - - ' ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '*' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '* ' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - ' *' => {class => 'bizarro::BOP::multiply', isCommand => 1}, # override * - '/' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '/ ' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ' /' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - '//' => {class => 'bizarro::BOP::divide', isCommand => 1}, # override / - ); - $context->operators->redefine('for',using=>',',from=>'Numeric'); - $context->operators->redefine(',for',using=>',',from=>'Numeric'); - $context->operators->redefine(', for',using=>',',from=>'Numeric'); - $context->operators->redefine('where',using=>',',from=>'Numeric'); - $context->operators->redefine(',where',using=>',',from=>'Numeric'); - $context->operators->redefine(', where',using=>',',from=>'Numeric'); - $context->operators->add('≠' => {precedence => .5, associativity => 'left', type => 'bin', string => ' != ', TeX => '\ne ', -class => 'Inequalities::BOP::inequality', eval => 'evalNotEqualTo'}); - $context->functions->set(sqrt=>{class=>'restrictedDomains::Function::numeric'}); # override sqrt() - $context->functions->add(identity => {class => 'restrictedDomains::Function::numeric'}); - $context->{cmpDefaults}{Formula}{checker} = sub { - my ($correct,$student,$ans) = @_; - return 0 if $ans->{isPreview} || $correct != $student; - $student = $ans->{student_formula}; - $student = Formula("identity($student)"); #ensure student is parsed as Formula object - my $setSqrt = Context()->flag("setSqrt"); - my $useBizarro = Context()->flag("useBizarro"); - Context()->flags->set(checkSqrt => $setSqrt, bizarroAdd => $useBizarro, bizarroSub => $useBizarro, bizarroMul => $useBizarro, bizarroDiv => $useBizarro); - delete $correct->{test_values}; - delete $student->{test_values}; - my $OK = ($correct == $student); - Context()->flags->set(checkSqrt => 0, bizarroAdd => 0, bizarroSub => 0, bizarroMul => 0, bizarroDiv => 0); - Value::Error(Context()->flag('wrongFormMessage')) unless $OK; - return $OK; - }; - $showPartialCorrectAnswers = 1; - $context->{cmpDefaults}{Formula}{showLengthHints} = 0; - $context->{cmpDefaults}{Formula}{list_checker} = sub { - my ($correct,$student,$ansHash,$value) = @_; - my $score = 0; # number of correct student answers - my @errors = (); # stores error messages - my $i; # loop counters - my $studentFormula = $ansHash->{student_formula}; - # Student answer needs to be a List consisting of a Formula/Real and then 0 or more Union/Interval/Sets - if ($studentFormula->class ne 'Formula') { - push(@errors,"Your answer isn't an expression with a possibly restricted domain"); - return (0,@errors); - } - if ($studentFormula->type ne 'Number' and $studentFormula->type ne 'List') { - push(@errors,"Your answer isn't an expression with a possibly restricted domain"); - return (0,@errors); - } - my $studentList = $studentFormula; - if ($studentFormula->type eq 'Number') {$studentList = Compute("$studentFormula, (-inf,inf)");}; - # Check that first item in list is a Formula that returns a number - if (($studentList->value)[0]->type ne 'Number') {push(@errors,"The first part of your answer is not an expression")}; - $domainsCount = scalar($studentList->value) - 1; - # Identify student answers like "1/x for x != 1, 2" and change to "1/x for x!=1, x!=2" - my $alteredStudentList = Formula(($studentList->value)[0].", ".($studentList->value)[1]); - for $i (2..$domainsCount) { - my $dom = ($studentList->value)[$i]; - my $prevdom = ($alteredStudentList->value)[$i-1]; - if ($dom->type eq 'Number' and $prevdom->class eq 'Formula' and $prevdom->type eq 'Interval') { - if (defined $prevdom->{tree}->{bop}) { - if ($prevdom->{tree}->{bop} eq '!=') { - my $x = (Context()->variables->variables)[0]; - $alteredStudentList = Formula($alteredStudentList->string.", $x != $dom"); - } - else {$alteredStudentList = Formula($alteredStudentList->string.", $dom");} - } - else {$alteredStudentList = Formula($alteredStudentList->string.", $dom");} - } - else {$alteredStudentList = Formula($alteredStudentList->string.", $dom");} - } - $studentList = $alteredStudentList; - for $i (1..$domainsCount) { - my $dom = ($studentList->value)[$i]; - if ($dom->type ne 'Interval' and $dom->type ne 'Union' and $dom->type ne 'Set') { - push(@errors,"Unable to understand how '".$dom->string."' describes the domain"); - return(0,@errors); - } - } - my $studentExpression = ($studentList->value)[0]; - my $studentDomain = Interval("(-inf,inf)"); - for my $i (1..$domainsCount) { - $studentDomain = $studentDomain->intersect(Interval(($studentList->value)[$i]->string)); - } - $studentDomain = Compute("$studentDomain"); - - # Set correct answer parts - my $correctFormula = $ansHash->{correct_value}; - my $correctScore = scalar ($correctFormula->value); - my $correctExpression = ($correctFormula->value)[0]; - my $correctDomain = ($correctFormula->value)[1]; - $correctDomain = Compute("$correctDomain"); - # If the correct answer has (-inf, inf) as the domain, don't print that in the correct answer - if ($correctDomain == Interval("(-inf,inf)")) { - $ansHash->{correct_ans_latex_string} = $correctExpression->TeX; - } - my $trueDomain = $correctDomain; - if (defined($correctFormula->{trueDomain})) {$trueDomain = $correctFormula->{trueDomain};} - - # Check math expression - my $expression_cmp = $correctExpression->cmp->evaluate($studentExpression->string); - my $expressionOK = $expression_cmp->{score}; - push(@errors,'Your expression is not correct') unless ($expressionOK or $expression_cmp->{ans_message} or $ansHash->{isPreview}); - push(@errors,$expression_cmp->{ans_message}) if $expression_cmp->{ans_message}; - - # Check student's domain - my $domainOK = 1; - my $trueUnion = Interval($trueDomain); - my $studentUnion = Interval($studentDomain); - my $correctUnion = Interval($correctDomain); - if (!$correctUnion->contains($studentUnion) or !$studentUnion->contains($trueUnion)) { - $domainOK = 0; - push(@errors,'Your domain is not correct') unless ($studentFormula->type eq 'Number' or $ansHash->{isPreview}); - push(@errors,'You need to specify the restricted domain') if ($studentFormula->type eq 'Number' and $expressionOK); - }; - - my $allCorrectScore = ($ansHash->{student_formula}->type eq 'Number') ? 2 : scalar ($ansHash->{student_value}->value); - my $expressionWeight = Context()->flag("expressionWeight"); - $score += $expressionWeight*$allCorrectScore if $expressionOK; - $score += (1-$expressionWeight)*$allCorrectScore if $domainOK; - #$score = $score + $domainsCount if $domainOK; - - return ($score,@errors); - }; -} - - -########################### -# -# Subclass the numeric functions -# -package restrictedDomains::Function::numeric; -our @ISA = ('Parser::Function::numeric'); - -# -# Override sqrt() to return a special value times x when evaluated -# -sub sqrt { - my $self = shift; - my $x = shift; - my $value = $self->context->flag("checkSqrt"); - return $value+1 if $value && $x == 1; # force sqrt(1) to be incorrect - return $value+1 if $value && $x == $value; # force sqrt(sqrt(x)) to be incorrect - return $value+1 if $value && $x == $self->context->flag("checkRoot"); # force sqrt(root(n,x))) to be incorrect - return $value*$x if $value; - return $self->SUPER::sqrt($x); -} - -sub identity { - my $self = shift; - my $x = shift; - return $x; -} diff --git a/OpenProblemLibrary/macros/PCC/parserRoot.pl b/OpenProblemLibrary/macros/PCC/parserRoot.pl deleted file mode 100755 index 13dad24d52..0000000000 --- a/OpenProblemLibrary/macros/PCC/parserRoot.pl +++ /dev/null @@ -1,78 +0,0 @@ -################################################################################ -# WeBWorK Online Homework Delivery System -# Copyright © 2000-2013 The WeBWorK Project, http://openwebwork.sf.net/ -# $CVSHeader: pg/macros/parserPrime.pl,v 1.2 2009/10/03 15:58:49 dpvc Exp $ -# -# This program is free software; you can redistribute it and/or modify it under -# the terms of either: (a) the GNU General Public License as published by the -# Free Software Foundation; either version 2, or (at your option) any later -# version, or (b) the "Artistic License" which comes with this package. -# -# This program is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the -# Artistic License for more details. -################################################################################ - -=head1 NAME - -parserRoot.pl - defines a root(n,x) function for n-th root of x. - -=head1 DESCRIPTION - -This file defines the code necessary to add a root(n,x) function to -any context that performs the n-th root of x. For example, -Compute("root(3,27)") would return the equivalent of Real(3). - -To accomplish this, put the line - - loadMacros("parserRoot.pl"); - -at the beginning of your problem file, then set the Context to the one -you wish to use in the problem. Then use the command: - - parser::Root->Enable; - -(You can also pass the Enable command a pointer to a context if you -wish to alter a context other than the current one.) - -Once that is done, you (and students) can enter roots by using the -root() function. You can use root() both within Formula() and -Compute() calls, and in Perl expressions, such as - - $n = root(3,27); - -to obtain n-th roots. - -=cut - -sub _parserRoot_init { - main::PG_restricted_eval('sub root {Parser::Function->call("root",@_)}'); -} - -package parser::Root; - -sub Enable { - my $self = shift; - my $context = shift; - $context = main::Context() unless Value::isContext($context); - $context->functions->add( - root => {class => 'parser::Root::Function::numeric2'}, - ); -} - -package parser::Root::Function::numeric2; -our @ISA = ('Parser::Function::numeric2'); - -sub root { - shift; my $n = shift; my $x = shift; - return $x**(1/$n); -} - -sub TeX { - my $self = shift; - my ($n,$x) = ($self->{params}[0],$self->{params}[1]); - return '\sqrt['.$n->TeX."]{".$x->TeX."}"; -} - -1; diff --git a/OpenProblemLibrary/macros/PCC/pccTables.pl b/OpenProblemLibrary/macros/PCC/pccTables.pl deleted file mode 100755 index 20952b7f13..0000000000 --- a/OpenProblemLibrary/macros/PCC/pccTables.pl +++ /dev/null @@ -1,5 +0,0 @@ -# This is just a redirect to niceTables.pl, which was originally called pccTables.pl - -loadMacros("niceTables.pl"); - -1; diff --git a/OpenProblemLibrary/macros/TCNJ/Generic.pl b/OpenProblemLibrary/macros/TCNJ/Generic.pl deleted file mode 100755 index a84594ff7a..0000000000 --- a/OpenProblemLibrary/macros/TCNJ/Generic.pl +++ /dev/null @@ -1,191 +0,0 @@ -#R. Byerly, Texas Tech University, 2005 - -sub _Generic{} #Don't reload this file. - - -#Documentation: - -=head1 Generic Answer Checker - -(Based on David Cervone's vector_cmp. See -doc/parser/extensions/8-answer.pg under the webwork2 directory.) -Place in macros directory and load this file (Generic.pl) using the -loadMacros command. (To just copy it into a pg file, replace -occurences of "\&" by "~~&".) - - Usage: - - ANS( generic_cmp(, ) ); -where is a parser object or syntactically correct string -for a parser object. - -Mandatory arguments: - - type => -where is a recognized parser type (e.g., Number, Point, Vector, -Matrix, etc.) - - checker => -where is a reference to a subroutine that will be passed (in -order) the parsed (and, if possible, evaluated) student answer, the -parsed professor's answer, and a reference to the answer hash. (The -last is so that it can return error messages if desired.) In simple -evaluators, the last two are typically not used. - -Optional arguments: - - correct_ans => -where is a string that will show up as the correct -answer when solutions are available. - - variables_allowed => 0 or 1 -(default 0 (false). Determines whether student's answer is allowed to -contain variables. In this case the checker must take care of -evaluating it.) - - length => n -where n is the number of elements in an expected answer of type list, -vector, or points. Returns error message to student if answer of wrong -length is entered. - - -####################Example:########################## -DOCUMENT(); # This should be the first executable line in the problem. - -loadMacros( -"PG.pl", -"PGbasicmacros.pl", -"PGanswermacros.pl", -"Parser.pl", -"Generic.pl", -); - -TEXT(&beginproblem); - -Context("Vector"); -$A=Vector(1,2,1); -$B=Vector(1,3,1); -$C=Vector(1,4,1); - -BEGIN_TEXT -Show that the vectors \(\{$A->TeX\}, \{$B->TeX\}, \{$C->TeX\}\) do -not span \(R^3\) by giving a vector not in their span: -\{ans_rule()\} -END_TEXT - -#Easy to get by guessing! - -sub check{ - my $stu=shift(); - $x1=$stu->extract(1); $x3=$stu->extract(3); - $x1 != $x3; #any vectors with different 1st and 3rd coordinates -} - -ANS(generic_cmp("23",type => 'Vector', length => 3, checker => ~~&check)); - -ENDDOCUMENT(); # This should be the last executable line in the problem. - -####################End of Example######################## - -=cut - -#End of Documentation - - - -#From parserUtils.pl: -sub protectHTML { - my $string = shift; - $string =~ s/&/\&/g; - $string =~ s//\>/g; - $string; - -} - - - -sub generic_cmp { - my $v = shift; - my %opts=@_; - die "generic_cmp requires an argument as answer" unless defined $v; - die "generic_cmp requires a checker" unless defined $opts{'checker'}; - die "generic_cmp requires a type" unless defined $opts{'type'}; - $v=Formula($v); - - $opts{'correct_ans'}=$v->string if(!defined($opts{'correct_ans'})); - $opts{'variables_allowed'}=0 if (!defined($opts{'variables_allowed'})); - $opts{$opts{'type'}}=$v; - - my $ans = new AnswerEvaluator; - - $ans->ans_hash( (%opts) ); -# $ans->install_evaluator(~~&generic_cmp_check); - $ans->install_evaluator(\&generic_cmp_check); - return $ans; -} - -sub generic_cmp_check { - my $ans = shift; - my $type=$ans->{type};my $v = $ans->{$type}; - $ans->score(0); # assume failure - my $f = Parser::Formula($ans->{student_ans}); - my @vars = (keys(%{$f->{variables}})); - if(@vars == 0){ - $V = Parser::Evaluate($f); - }elsif($ans->{variables_allowed}){ - $V=$f; #if there are variables in the students answer, let the checker have it. - }else{ - $ans->{ans_message} = $ans->{error_message} = - "Your answer contains variables ".join(",", @vars); - return $ans; - } - if (defined $V) { - $V = Formula($V) unless Value::isValue($V); # make sure we can call Value methods - $ans->{preview_latex_string} = $V->TeX; - $ans->{preview_text_string} = $V->string; - $ans->{student_ans} = $V->string; - #Some checks: - if (($type eq 'List') && !($V->type eq 'List')){ - $V=List($V); #promote single element to list - } - - if(defined $ans->{length}){ - if ($ans->{length} != $V->length){ - $ans->{ans_message}=$ans->{error_message}= - "Wrong number of entries in answer"; - return $ans; - } - } - - if (($V->type eq $type) ) { - $ans->score(1) if ($ans->{checker}->($V,$v,$ans)); - - } else { - $ans->{ans_message} = $ans->{error_message} = - "Your answer doesn't seem to be a $type (it looks like ".Value::showClass($V).")" - unless $inputs_ref->{previewAnswers}; - } - } else { - # - # Student answer evaluation failed. - # Report the error, with formatting, if possible. - # - my $context = Context(); - my $message = $context->{error}{message}; - if ($context->{error}{pos}) { - my $string = $context->{error}{string}; - my ($s,$e) = @{$context->{error}{pos}}; - $message =~ s/; see.*//; # remove the position from the message - $ans->{student_ans} = protectHTML(substr($string,0,$s)) . - '' . - protectHTML(substr($string,$s,$e-$s)) . - '' . - protectHTML(substr($string,$e)); - } - $ans->{ans_message} = $ans->{error_message} = $message; - } - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/UBC/RserveClient.pl b/OpenProblemLibrary/macros/UBC/RserveClient.pl deleted file mode 100644 index e8effff4c0..0000000000 --- a/OpenProblemLibrary/macros/UBC/RserveClient.pl +++ /dev/null @@ -1,218 +0,0 @@ -=head1 NAME - -RserveClient.pl - Macros for evaluating R code on an Rserve server - -=head1 SYNPOSIS - -=head1 SYNOPSIS - - loadMacros('RserveClient.pl'); - - rserve_start(); - my @rnorm = rserve_eval("rnorm(15, mean=$m, sd=$sd)"); - rserve_eval(data(stackloss)); - my @coeff = rserve_eval('lm(stack.loss ~ stack.x, stackloss)$coeff'); - rserve_finish(); - - -=head1 DESCRIPTION - -The macros in this file provide access to facilities of L, -optionally located on another server, by using the -L protocol. - -B Before you can use these macros, you will need to -configure the location of your Rserve host by adding it to -C<$pg{specialPGEnvironmentVars}{Rserve}{host}>, for instance by -appending the following line to F: - - $pg{specialPGEnvironmentVars}{Rserve} = {host => "localhost"}; - -Without this configuration in place, Rserve macros will only print out -a warning about missing configuration and return C. - -=head1 MACROS - -The macros in this file set up a connection to the R server and -pass a string parameter to R for evaluation. The resulting -vector is returned as a perl array object. - -=over 4 - -=item rserve_eval REXPR - -Evaluates an R expression, given as text string in REXPR, on the -L server and returns its result -as a Perl representation of the L object. -Multiple calls within the same problem share the R session and the -object workspace. - -=item rserve_query - -Evaluates an R expression, given as text string in REXPR, in a -single-use session on the L -server and returns its result as a Perl representation of the -L object. - -This function is different from C in that each call is -completely self-enclosed and its R session is discarded after it -returns. - -=item rserve_start, rserve_finish - -Start up and close the current connection to the Rserve server. In -normal use, these functions are completely optional because the first -call to C will call start the session if one is not -already open. Similarly, the current session will be closed in its -destructor when the current question goes out of scope. - -Other than backward compatibility, the only reason for using these -functions is to start a new clean session within a single problem, -which shouldn't be a common occurrence. - -=item rserve_start_plot [IMG_TYPE] - -Opens a new R graphics device to capture subsequent graphics output in -a temporary file on the R server. IMG_TYPE can be 'png', 'jpg', or -'pdf', with 'png' as the default. Returns the name of the remote file. - - -=item rserve_finish_plot REMOTE_NAME - -Closes the R graphics capture to file REMOTE_NAME, transfers the file -to WebWork's temporary file area, and returns the name of the local -file that can then be used by the image macro. - -=item rserve_get_file REMOTE_NAME, [LOCAL_NAME] - -Transfer the file REMOTE_NAME from the R server to WebWork's temporary -file area, and returns the name of the local file that can then be -used by the C macro. If LOCAL_NAME is not specified, the -filename portion of the REMOTE_NAME is used. - -=back - - -=head1 DEPENDENCIES - -Requires perl 5.010 or newer and CPAN module Statistics::R::IO, which -has to be loaded in WebWork's Safe compartment by adding it to -${pg}{modules}. - - -=cut - - -my $rserve; # Statistics::R::IO::Rserve instance - - -sub _rserve_warn_no_config { - my @trace = split /\n/, Value::traceback(); - my ($function, $line, $file) = $trace[0] =~ /^\s*in ([^ ]+) at line (\d+) of (.*)/; - - $PG->warning_message('Calling ' . $function . - ' is disabled unless Rserve host is configured in $pg{specialPGEnvironmentVars}{Rserve}{host}') -} - - -sub rserve_start { - _rserve_warn_no_config && return unless $Rserve->{host}; - - $rserve = Statistics::R::IO::Rserve->new(server => $Rserve->{host}, _usesocket => 1); - - # Keep R's RNG reproducible for this problem - $rserve->eval("set.seed($problemSeed)") -} - - -sub rserve_finish { - $rserve->close() if $rserve; - undef $rserve -} - - -sub rserve_eval { - _rserve_warn_no_config && return unless $Rserve->{host}; - - my $query = shift; - - rserve_start unless $rserve; - - my $result = $rserve->eval($query); - _unref_rexp($result) -} - - -sub rserve_query { - _rserve_warn_no_config && return unless $Rserve->{host}; - - my $query = shift; - $query = "set.seed($problemSeed)\n" . $query; - my $rserve_client = Statistics::R::IO::Rserve->new(server => $Rserve->{host}, _usesocket => 1); - my $result = $rserve_client->eval($query); - $rserve_client->close; - _unref_rexp($result) -} - - -## Returns a REXP's Perl representation, dereferencing it if it's an -## array reference -## -## `REXP::to_pl` returns a string scalar for Symbol, undef for Null, -## and an array reference to contents for all vector types. This -## function is a utility wrapper to make it easy to assign a Vector's -## representation to an array variable, while still working sensibly -## for non-arrays. -sub _unref_rexp { - my $rexp = shift; - - my $value = $rexp->to_pl; - if (ref($value) eq ref([])) { - @{$value} - } else { - $value - } -} - -sub rserve_start_plot { - _rserve_warn_no_config && return unless $Rserve->{host}; - - my $device = shift // 'png'; - - die "Unsupported image type $device" unless $device =~ /^(png|pdf|jpg)$/; - my $remote_image = (rserve_eval("tempfile(fileext='.$device')"))[0]; - - $device =~ s/jpg/jpeg/; - rserve_eval("$device('$remote_image')"); - - $remote_image -} - - -sub rserve_finish_plot { - _rserve_warn_no_config && return unless $Rserve->{host}; - - my $remote_image = shift or die "Missing remote image name"; - - rserve_eval("dev.off()"); - - rserve_get_file($remote_image) -} - - -sub rserve_get_file { - _rserve_warn_no_config && return unless $Rserve->{host}; - - my $remote = shift or die "Missing remote file name"; - my $local = shift // $PG->fileFromPath($remote); - - $local = $PG->surePathToTmpFile($local); - - $rserve->get_file($remote, $local); - - $local -} - - -1; diff --git a/OpenProblemLibrary/macros/UBC/regrfnsPG.pl b/OpenProblemLibrary/macros/UBC/regrfnsPG.pl deleted file mode 100644 index 706bd065ee..0000000000 --- a/OpenProblemLibrary/macros/UBC/regrfnsPG.pl +++ /dev/null @@ -1,98 +0,0 @@ - -# functions for simple linear regression in perl - -#/* "a","b" are the two endpoints of the range of -# random integers to be produced. */ -# inputs $a,$b are integers -#sub rndgen -#{ local($a,$b,$r); -# $a=$_[0]; $b=$_[1]; -# $r=rand($b-$a+1); # in (0,b-a+1) -# #print $a," ",$b," ",$r,"\n"; -# $r=int($r)+$a; # between a,b inclusive -# $r; -#} - -# N (size of population) and ssize (sample size) are inputs -sub isamp -{ local($N,$ssize,$N1,$j,$k,$temp,@x,@xsub); - $N=$_[0]; $ssize=$_[1]; $N1=$N-1; - @x= 0..$N1; - for($j=0; $j<$ssize; $j++) - { #$k=rndgen($j,$N1); - $k=random($j,$N1,1); # using function in WebWork - $temp=$x[$j]; $x[$j]=$x[$k]; $x[$k]=$temp; - } - #for($j=0; $j<$ssize; $j++) { $xsub[$j]=$x[$j]; } - @xsub=@x[0..($ssize-1)]; - @xsub; -} - -# simple linear regression -# input xvec, yvec : assumed as one vector here -# output ($n,$xbar,$ybar,$sx2,$sy2,$sxy,$b0,$b1,$sse,$mse,$x0); -sub lsreg -{ local($n,$i,$xi,$yi,$sx1,$sx2,$sy1,$sy2,$sxy,$xbar,$ybar,$b0,$b1,$sse,$mse); - $n=($#_+1)/2; - #print $n,"\n"; - $sx1=0; $sx2=0; $sy1=0; $sy2=0; $sxy=0; - for($i=0;$i<$n;$i++) - { #$xi=$xx[$i]; $yi=$yy[$i]; - $xi=$_[$i]; $yi=$_[$i+$n]; - $sx1=$sx1+$xi; $sx2=$sx2+$xi*$xi; - $sy1=$sy1+$yi; $sy2=$sy2+$yi*$yi; - $sxy=$sxy+$xi*$yi; - } - $xbar=$sx1/$n; $ybar=$sy1/$n; - $sx2=($sx2-$n*$xbar**2)/($n-1); - $sy2=($sy2-$n*$ybar**2)/($n-1); - $sxy=($sxy-$n*$xbar*$ybar)/($n-1); - $b1=$sxy/$sx2; $b0=$ybar-$b1*$xbar; - $sse=($sy2-$sxy**2/$sx2)*($n-1); - $mse=$sse/($n-2); - @out=($n,$xbar,$ybar,$sx2,$sy2,$sxy,$b0,$b1,$sse,$mse); - @out; -} - -# subpopulation mean at x0 -#sqrt(mse*(1/n+(x0-xbar)^2/((n-1)*sx2))) -# input : output of lsreg, plus x0 -# or ($n,$xbar,$ybar,$sx2,$sy2,$sxy,$b0,$b1,$sse,$mse,$x0); -# output: point estimate and SE -sub subpopmeanint -{ local($n,$xbar,$sx2,$b0,$b1,$x0,$muygx,$semu,@out); - $n=$_[0]; - $xbar=$_[1]; - $sx2=$_[3]; - $b0=$_[6]; - $b1=$_[7]; - $mse=$_[9]; - $x0=$_[10]; - $muygx=$b0+$b1*$x0; - $semu= sqrt($mse*(1/$n+($x0-$xbar)**2/(($n-1)*$sx2))); - @out=($muygx,$semu); - @out; -} - -#prediction interval at x=x0 -#sqrt(mse*(1+1/n+(x0-xbar)^2/((n-1)*sx2))) -# input : output of lsreg, plus x0 -# ($n,$xbar,$ybar,$sx2,$sy2,$sxy,$b0,$b1,$sse,$mse,$x0); -# output: point estimate and SE -sub predint -{ local($n,$xbar,$sx2,$b0,$b1,$x0,$muygx,$semu,@out); - $n=$_[0]; - $xbar=$_[1]; - $sx2=$_[3]; - $b0=$_[6]; - $b1=$_[7]; - $mse=$_[9]; - $x0=$_[10]; - $muygx=$b0+$b1*$x0; - $semu= sqrt($mse*(1+1/$n+($x0-$xbar)**2/(($n-1)*$sx2))); - @out=($muygx,$semu); - @out; -} - -1; #return true - diff --git a/OpenProblemLibrary/macros/UMass-Amherst/algebraMacros.pl b/OpenProblemLibrary/macros/UMass-Amherst/algebraMacros.pl deleted file mode 100755 index e2f289e942..0000000000 --- a/OpenProblemLibrary/macros/UMass-Amherst/algebraMacros.pl +++ /dev/null @@ -1,359 +0,0 @@ -# algebraMacros.pl - -# define any custom subroutines you want and then use them in -# problems by including the file in loadMacros() calls. - -sub _init_macro_template { - - #Possibly add initialization code here - #sub routine is not required, but prevents the macro from being re-loaded - -} - - - -######################## display macros for cyclic and dihedral groups - -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}"; - -}; - -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" - - - # if you want to display the Quaternion group using some other notation, comment the statement above and uncomment one of these (or add your own): - - # return "H_8" - - # return "Q" - -}; - - - - - -######################## subroutines - - -# fisher-yates shuffle -# argument: reference to a list -# shuffles list in-place -# returns: nothing -sub fyshuffle -{ - my $array = shift; - my $i = @$array; - while ( --$i ) - { - my $j = random( 0, $i, 1 ); - @$array[$i,$j] = @$array[$j,$i]; - } -}; - - -# extended euclidean algorithm -# by Dick Lane -# http://webwork.maa.org/moodle/mod/forum/discuss.php?d=2286 -# arguments: a, b -# returns: d, x, y, s, t where gcd( a, b ) = d = a( x + sk ) + b( y + tk ) for all k -sub xgcd ($$) { - my ( $a, $b, $x, $y, $s, $t ) = ( @_, 1, 0, 0, 1 ); - - while ( $b ) { - my $q = int( $a / $b ) ; - ( $a, $x, $y, $b, $s, $t ) = ( $b, $s, $t, $a - $q * $b, $x - $q * $s, $y - $q * $t ); - } - - return [ $a, $x, $y, $s, $t ]; -}; - - - -# subroutine to decompose a permutation into a product of disjoint cycles. -# argument: reference to a list containing the permutation data -# that is, element 0 of the list is f(0), element 1 is f(1), etc -# returns: mathObject list of lists representing the cycle decomposition of the permutation -sub disjointCycles -{ - $p = shift; - - # create a hash representing the permutation - %pHash = (); - $pHash{ $_ } = $$p[ $_ ] foreach ( 0 .. -1 + scalar @$p ); - - @cycles = ( ); - while( keys %pHash ) - { - # start with first available key - @c = ( keys %pHash )[ 0 ]; - - # keep looping through set, adding elements to the current cycle (c) - # until we get back to the beginning of c - push @c, delete $pHash{ $c[ -1 ] } while $pHash{ $c[ -1 ] } != $c[ 0 ]; - - delete $pHash{ $c[ -1 ] }; - - # add c to cycles if it's not just a 1-cycle - push @cycles, [ map { $_ + 1 } @c ] if @c > 1; - } - - # making the lists of lists must be done carefully in order to get the behavior we want - # we want the cycles (Lists within the List) to always have parentheses, even if there's - # only one. But we want the outer List to never have parentheses - if( scalar @cycles > 1 ) - { - $result = List( map { List( join( ',', @$_ ) ) } ( @cycles ) ); - } - elsif (scalar @cycles > 0 ) - { - $result = List( map { List( '(' . join( ',', @$_ ) . ')' ) } @cycles ); - } else { - Context()->strings->add(id => {alias => "identity"}); - $result = List( ( id ) ); - } - return $result; -}; - - -# subroutine to decompose a permutation into a product of transpositions ( 2-cycles ). -# argument: mathObject list of lists representing the cycle decomposition of the permutation (i.e., the output of disjointCycles() ) -# returns: mathObject list of lists representing the permutation as a list of transpositions -sub cyclesToTranspositions -{ - my $listOfCycles = shift; - - @transpositions = ( ); - - # decompose each cycle ( a_1 a_2 ... a_n ) into ( a_1 a_2 )( a_1 a_3 ) ... ( a_1 a_n ) - foreach ( $listOfCycles->value ) - { - my @cycle = $_->value; - foreach ( 1 .. @cycle - 1 ) - { - push @transpositions, List( @cycle[ 0, $_ ] ); - } - } - - # unlike disjointCycles, this subroutine is only used in a problem where the permutation has order > 4, so there will always be more than one element. Therefore we don't have to do any weird stuff to format the list (make parentheses display correctly) - return List( @transpositions ); -}; - - - -# subroutine to determine the order of a permutation -# argument: a list of lists representing a permutation in the form of a product of disjoint cycles ( i.e., the output of disjointCycles() ) -# returns: mathObject integer representing the order of the permutation -sub cycleOrder -{ - my $p = shift; # list of lists - - my $order = 1; - $order = lcm( $order, $_->length ) foreach $p->value; - - return Compute( $order ); -}; - - - -# subroutine to determine the parity of a permutation -# argument: a list of lists representing a permutation in the form of a product of disjoint cycles (i.e., the output of disjointCycles() ) -# returns: TRUE if the permutation is even, FALSE if the permutation is odd -sub isEven -{ - my $p = shift; - - my $numberEvenCycles = scalar grep { $_->length % 2 == 0 } $p->value; - - return !( $numberEvenCycles % 2 ); - -}; - - - - - - -######################## frequently-used custom checkers - -# you can call these when checking answers like this: -# -# ANS( $f->cmp( checker => ~~&modChecker ) ); - -# make sure the external variables are available; for instance, in order to use -# the modChecker subroutine, you must have the variable $modulus defined in -# your problem - - - -############################################################################### -# modChecker -############################################################################### -# Type: checker -# Used in: RingsDefinition3.pg -# RingsDefinition4.pg -# RingsDefinition5.pg -# RingsDefinition7.pg -# RingsQuotientPolynomial3.pg -# RingsQuotientPolynomial4.pg -# RingsIdealsHomomorphisms2.pg -# Purpose: Compare the student's answer (a number) to the correct -# answer (a number), modulo $modulus -# External variables: -# $modulus -# Comment: This checker is defined in algebraMacros.pl because it's used -# in so many problems -############################################################################### - sub modChecker - { - my ( $correct, $student, $ansHash ) = @_; # get correct and student MathObjects - return ( $student % $modulus == $correct % $modulus ? 1 : 0 ); - }; -############################################################################### - - - - - -############################################################################### -# checkCycles -############################################################################### -# Type: checker -# Used in: Permutations1.pg -# Permutations2.pg -# Permutations5.pg -# Permutations6.pg -# Purpose: Compare a student's cycle to a correct cycle, see if they -# represent the same permutation (i.e. ( 1 2 3 ) = ( 2 3 1 ) ) -# External variables: -# Comment: This checker is defined in algebraMacros.pl because it's used -# in so many problems -############################################################################### - sub checkCycles - { - my ( $correct , $student , $ansHash ) = @_; - - @studentCycle = $student->value; - @correctCycle = $correct->value; - - # immediately fail if the cycles are not the same length - return 0 if @studentCycle != @correctCycle; - - # try each offset, see if the lists are the same - foreach ( 0 .. @studentCycle - 1 ) - { - my $i = 0; - $i++ while $i < @studentCycle && $studentCycle[ $i ] == $correctCycle[ -$_ + $i ]; - return 1 if $i == @studentCycle; - } - - return 0; - }; -############################################################################### - - -############################################################################### -# checkListOfTranspositions -############################################################################### -# Type: checker -# Used in: Permutations4.pg -# Permutations8.pg -# Purpose: Compare a student's sequence of transpositions to a correct -# sequence of transpositions, see if they represent the same -# permutation -# External variables: -# @x - list of elements in the set on which permutations are -# defined -# Comment: This checker is defined in algebraMacros.pl because it's used -# in so many problems -############################################################################### - sub checkListOfTranspositions - { - my ( $correct, $student, $ansHash, $value ) = @_; - - # create hashes which will be used to store the student and correct permutations - my( %studentPerm, %correctPerm ); - $studentPerm{ $_ } = $correctPerm{ $_ } = $_ foreach ( 1 .. @x ); - - # apply each of the correct transpositions in succession, from right to left - foreach ( 1 .. @$correct ) - { - # read the transposition ( a, b ) as a pair of integers - $a = ( $correct->[ -$_ ]->value )[ 0 ]->value; - $b = ( $correct->[ -$_ ]->value )[ 1 ]->value; - - # switch a and b elements of the hash - @correctPerm{ $a, $b } = @correctPerm{ $b, $a }; - } - - # apply each of the student's transpositions in succession, from right to left - foreach ( 1 .. @$student ) - { - # check to make sure this entry is a list of 2 elements, both of which are positive integers in the set @x - return( 0, "one of your entries is not a well-defined transposition on this set" ) if $student->[ -$_ ]->length != 2 || scalar grep{ !Value::isReal( $_ ) || $_ != int $_ || $_ < 1 || $_ > @x } $student->[ -$_ ]->value; - - # read the transposition ( a, b ) as a pair of integers - $a = ( $student->[ -$_ ]->value )[ 0 ]->value; - $b = ( $student->[ -$_ ]->value )[ 1 ]->value; - - # switch elements a and b of the hash - @studentPerm{ $a, $b } = @studentPerm{ $b, $a }; - } - - # read the two hashes to make sure they're the same. if not, the answer is wrong - return scalar ( grep{ $studentPerm{ $_ } != $correctPerm{ $_ } } ( 1 .. @x ) ) ? (0) : (scalar(@$student)); - - # an alternative way to do this would be to compute the permutation represented by the correct list of transpositions, then do the inverse of the student's list of transpositions (work left-to-right instead of right-to-left) on the same list, and see if it results in the identity. - }; -############################################################################### - - - - - - -1; #required at end of file - a perl thing diff --git a/OpenProblemLibrary/macros/UW-Stout/stoutFactoringGraders.pl b/OpenProblemLibrary/macros/UW-Stout/stoutFactoringGraders.pl deleted file mode 100644 index c9f6b67323..0000000000 --- a/OpenProblemLibrary/macros/UW-Stout/stoutFactoringGraders.pl +++ /dev/null @@ -1,823 +0,0 @@ -=head1 NAME - -stoutFactoringGraders.pl - -=head1 SYNOPSIS - -TODO - -=head1 DESCRIPTION - -TODO - -=head1 AUTHOR - -Alex Basyrov, basyrova@uwstout.edu - -=cut - -loadMacros( -"MathObjects.pl", -"stoutUtils.pl", -); - -## the old sub -- should phase it out -sub GradeFactors { - # force the use of object-oriented grader - return FactoredFormula(shift)->cmp(); -} - -## used internally, don't know if it is useful in general -sub FormulaUpToConstant{ - my $string_formula = shift; - my $ref_factored_formula = new stoutFormulaUpToConstant($string_formula); - return $ref_factored_formula; -} - -## the defalt way to create factored formula answer checker -sub FactoredFormula{ - my $string_formula = shift; - my $ref_factored_formula = new stoutFactoredFormula($string_formula); - return $ref_factored_formula; -} - -## the default way to create factored formula that is correct up to -## a constant factor answer checker -sub FactoredFormulaUpToConstant{ - my $string_formula = shift; - my $ref_factored_formula = new stoutFactoredFormula($string_formula, up_to_constant => 1); - return $ref_factored_formula; -} - -## the default way to create factored rational formula answer checker -## the option -sub FactoredRationalFormula{ - my $string_rational_formula = shift; - my $ref_factored_rational_formula = new stoutFactoredRationalFormula($string_rational_formula); - return $ref_factored_rational_formula; -} - -package stoutFormulaUpToConstant; - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - }; - bless $self, $class; -} - -sub theEvaluator { - my $hr_ans = shift; - - # store the options we got from the call to cmp() method - my %options = @_; - - my $ans = $hr_ans->{correct_ans}->{Formula}; - my $context = $hr_ans->{correct_ans}->{Context}; - my $fans = main::Formula($context, $ans); - - my $student_ans = $hr_ans->{student_ans}; - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - - # need to do syntax error check before going furhter - if ( defined $hr_ans->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - - stoutDebugShowHashRef("Answer hash", $hr_ans); - - if ( !($hr_ans->{error_message} eq "") ){ - return $hr_ans; - } - } - # we should be safe to use student answer, since it should have passed parser - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - my $result = stoutUtils::matchUpToConstant($context, $ans, $student_ans); - - if ( (defined $result) && (abs($result) == 1) ){ - $ans_hash->{up_to_constant} = $result; - $ans_hash->{score} = 1; - return $ans_hash; - } else { - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Your answer seems to be off by a non-trivial constant"); - return $ans_hash; - } - - - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Incorrect"); - - return $hr_ans; -} - -sub cmp { - my $self = shift; - my %options = @_; - - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator( - 'correct_ans' => $self, - 'type' => 'Formula Up To Constant', - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(\&theEvaluator, %options); - return $ans; -} - - -package stoutFactoredFormula; - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -# the following might be a good idea -# so that the factored formula has all -# the MathObjects reduction goodness built in -# but something goes wrong with the inheritance, etc... -# our @ISA = ( 'Value::Formula' ); - -# sub new { - # my $class = shift; - # my $f = $class->SUPER::new(@_); - # my $string_formula = shift; - # $f->{Formula} = $string_formula; - # return $f; -# } - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - $options{up_to_constant} = 0 unless defined $options{up_to_constant}; - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - UpToConstant => $options{up_to_constant}, - }; - bless $self, $class; -} - -# looks like it is easier to make a new reduce method -# instead of trying to inherit from MathObjects -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $context = $self->{Context}; - - stoutDebugShowHashRef("Context inherited by formula", $context->{flags}); - - my $new_formula = main::Formula($context, $original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub theEvaluator { - my $hr_ans = shift; - - # store the options we got from the call to cmp() method - my %options = @_; - - # the options we could use: - # $options{constant_factors} = keep|combine - # $options{exponents} = integers|positive|fractions|constant|any - # $options{factors} = strict_polynomials|strict_terms|any - - $options{constant_factors} = 'keep' unless defined $options{constant_factors}; - $options{exponents} = 'any' unless defined $options{exponents}; - $options{factors} = 'any' unless defined $options{factors}; - $options{up_to_constant} = 0 unless defined $options{up_to_constant}; - - stoutDebugShowHashRef("Options ", \%options); - - my $ans = $hr_ans->{correct_ans}->{Formula}; - my $context = $hr_ans->{correct_ans}->{Context}; - my $fans = main::Formula($context, $ans); - my @factorspowers = makefactors($fans->{tree}); - - stoutDebugShowArrayRef("Factors and powers", \@factorspower); - - if ($options{constant_factors} eq 'combine'){ - @factorspowers = combineConstantFactors($context, @factorspowers); - } - - my @factors = (); - my @powers = (); - my $i; - for ($i = 0; $i < scalar @factorspowers; $i+=2){ - push @factors, $factorspowers[$i]; - push @powers, $factorspowers[$i+1]; - } - - stoutDebugShowArrayRef("Factors", \@factors); - stoutDebugShowArrayRef("Powers", \@powers); - - my $numCorrectFactors = scalar @factors; - - my $student_ans = $hr_ans->{student_ans}; - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - - # need to do syntax error check before going furhter - if ( defined $hr_ans->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($hr_ans->{error_message} eq "") ){ - return $hr_ans; - } - } - # we should be safe to use student answer, since it should have passed parser - - my $can_proceed = 0; - if ( $ans_hash->{score} == 1) { - $can_proceed = 1; - } elsif ($options{up_to_constant}){ - my $result = stoutUtils::matchUpToConstant($context, $ans, $student_ans); - if ((defined $result) && (abs($result)==1)){ - $can_proceed = 1; - } - } - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - if ( $can_proceed ) { - # since we got here, student's answer is equivalent to the correct one - my $sans = main::Formula($context, $student_ans); - my @sfactorspowers = makefactors($sans->{tree}); - my @sfactors=(); - my @spowers=(); - if ($options{constant_factors} eq 'combine'){ - @sfactorspowers = combineConstantFactors($context, @sfactorspowers); - } - for ($i = 0; $i < scalar @sfactorspowers; $i+=2){ - push @sfactors, $sfactorspowers[$i]; - push @spowers, $sfactorspowers[$i+1]; - } - - stoutDebugShowArrayRef("Student Factors", \@sfactors); - stoutDebugShowArrayRef("Student Powers", \@spowers); - - my $snumCorrectFactors = scalar @sfactors; - - if ( $snumCorrectFactors < $numCorrectFactors) { - $ans_hash->setKeys( 'ans_message' => "Make sure that your answer is factored completely!" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - if ( $snumCorrectFactors > $numCorrectFactors) { - $ans_hash->setKeys( 'ans_message' => "Your answer is equivalent to the correct one, but it has too many factors. Does your answer contain repeated factors?" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - - # since we got here, both answers have the same number of factors - # we can compare each correct factor to student's and see what we get - my $correctFactors = 0; - my $accumulatedSign = 1; - my $accumulatedFactor = 1; - my $offByConstant = 0; - for ($i = 0; $i < scalar @factors; $i++) { - $cfactor = $factors[$i]; - $cpower = main::Formula($context, $powers[$i]); - for ($j = 0; $j < scalar @sfactors; $j++) { - $sfactor = $sfactors[$j]; - $spower = main::Formula($context, $spowers[$j]); - my $result = stoutUtils::matchUpToConstant($context, $cfactor, $sfactor); - if ((defined $result) && (abs($result)==1)){ - # only factors correct up to negative are counted - # but that could be changed to 'up to a constant', if needed - if ( $cpower == $spower){ - $correctFactors++; - $accumulatedFactor *= ($result)**($cpower); - } - } else { - # factor is close to correct one, but it differes by a non-trivial constant - $offByConstant = 1; - } - } - } - - if ( ($correctFactors == $numCorrectFactors) && ($accumulatedFactor == 1) ) { - $ans_hash->{score} = 1; - return $ans_hash; - } - - if ( ($options{up_to_constant}) && ($correctFactors == $numCorrectFactors) ){ - $ans_hash->{up_to_constant} = $accumulatedFactor; - $ans_hash->{score} = 1; - return $ans_hash; - } - - if ($offByConstant == 1){ - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Your answer is algebraically equivalent to the correct one, but some factors are off by a non-trivial constant. Are your factors reasonably simplified?"); - return $ans_hash; - } - - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Your answer is algebraically equivalent to the correct one, but you want to factor it completely."); - } - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator( - 'correct_ans' => $self, - 'type' => 'Factored Formula', - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(\&theEvaluator, up_to_constant => $self->{UpToConstant}, %options); - return $ans; -} - -sub makefactors { - my $me = shift; - - my $bop = $me->{bop}; - - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - my $uop = $me->{uop}; - if (defined $uop){ - $uop =~ s/^\s+//; - $uop =~ s/\s+$//; - } else { - $uop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("UOP", $uop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - stoutDebugShowVar("OP", $me->{op}->string) if defined $me->{op}; - - if ( ($bop eq "*") ) { - return ( makefactors($me->{lop}), makefactors($me->{rop}) ); - } elsif ( ( $bop eq "^") || ($bop eq "**") ) { - # the factor has exponent! - my $base = $me->{lop}->string; - my $exponent = $me->{rop}->string; - return ($base, $exponent); - } elsif ( ($bop eq "" ) && ($uop eq 'u-') ) { - # there is no binary operation - could have unitary - # need to figure out what to do here - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } else { - # the factor has no exponent - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } -} - -sub combineConstantFactors{ - my $context = shift; - my @inputFactorsPowers = @_; - - stoutDebugShowArrayRef("Factors and powers", \@inputFactorsPowers); - - my @outputFactorsPowers; - my $i; - my $factor; - my $power; - my $f_factor; - my $constant = 1; - for ($i = 0; $i < scalar @inputFactorsPowers; $i+=2){ - $factor = $inputFactorsPowers[$i]; - $power = $inputFactorsPowers[$i+1]; - $f_factor = ::Formula($context,"($factor)^($power)"); - if ($f_factor->isConstant){ - $constant *= $f_factor->value; - } else { - push @outputFactorsPowers, $factor; - push @outputFactorsPowers, $power; - } - } - if ($constant != 1){ - push @outputFactorsPowers, $constant; - push @outputFactorsPowers, 1; - } - - # debug - stoutDebugShowArrayRef("Output Factors and Powers", \@outputFactorsPowers); - - return @outputFactorsPowers; -} - -sub stringify { - my $self = shift; - return $self->string; -} - -sub string{ - my $self = shift; - return $self->{Formula}; -} - -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - my $strFormula = $self->{Formula}; - return main::Formula($context, $strFormula)->TeX(); -} - - -package stoutFactoredRationalFormula; - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - - -sub new { - my $class = shift; - my $string_formula = shift; - - my $self = { Formula => $string_formula}; - - bless $self, $class; -} - -sub findNumeratorDenominator { - my $me = shift; - - my $bop = $me->{bop}; - - if (defined $bop) { - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - my $uop = $me->{uop}; - if (defined $uop){ - $uop =~ s/^\s+//; - $uop =~ s/\s+$//; - } else { - $uop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("UOP", $uop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - stoutDebugShowVar("OP", $me->{op}->string) if defined $me->{op}; - - if ($bop eq "/" ) { - return { - 'numerator' => $me->{lop}->string, - 'denominator' => $me->{rop}->string, - 'error_code' => 0, - }; - } elsif ($bop eq "*") { - $l_res = findNumeratorDenominator($me->{lop}); - $r_res = findNumeratorDenominator($me->{rop}); - - if ( ( !( $l_res->{denominator} eq "") ) && ( !( $r_res->{denominator} eq "") ) ) { - # two denominators or something else is wrong! - return { - 'numerator' => $me->string, - 'denominator' => 'error', - 'error_code' => 1 - }; - } - return { - 'numerator' => "(". $l_res->{numerator} .")*(". $r_res->{numerator} .")", - 'denominator' => $l_res->{denominator} . $r_res->{denominator}, - 'error_code' => 0, - }; - } elsif ($uop eq "u-"){ - $u_res = findNumeratorDenominator($me->{op}); - return { - 'numerator' => "-(". $u_res->{numerator} .")", - 'denominator' => $u_res->{denominator}, - 'error_code' => $u_res->{error_code}, - }; - } else { - return { - 'numerator' => $me->string, - 'denominator' => "", - 'error_code' => 0, - }; - } -} - -sub checkNumeratorDenominator{ - my $string_formula = shift; - - if ($string_formula =~ /(.*)\/(.*)/ ) { - # bad news - there is a division operation in the string somewhere - # need to dig deeper - my $formula = ::Formula($string_formula); - return detectRational($formula->{tree}); - } else { - return 0; # no problems detected - } -} - -sub detectRational{ - my $me = shift; - my $bop = $me->{bop}; - if (defined $bop) { - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - my $uop = $me->{uop}; - if (defined $uop){ - $uop =~ s/^\s+//; - $uop =~ s/\s+$//; - } else { - $uop = ""; - } - - if ($bop eq "/" ) { - # bad news -- we have division operation - my $string = $me->string; - my $f_string = ::Formula($string); - if ($f_string->isConstant){ - return 0; - } else { - return 1; - } - } elsif( !($bop eq "") ) { - # there is some binary operation - my $l_result = 0; - $l_result = detectRational($me->{lop}) if (defined $me->{lop}); - my $r_result = 0; - $r_result = detectRational($me->{rop}) if (defined $me->{rop}); - return ($l_result+$r_result); - } elsif( !($uop eq "") ) { - # there is some unitary operation - my $u_result = 0; - $u_result = detectRational($me->{op}) if (defined $me->{op}); - return $u_result; - } -} - -sub theEvaluator { - my $hr_ans_init = shift; - - # debug! - # warn(::pretty_print($hr_ans_init)); - - my %options = @_; - - $options{strict_match} = 0 unless defined $options{strict_match}; - $options{numerator_factored} = 0 unless defined $options{numerator_factored}; - - my $ans = $hr_ans_init->{correct_ans}; - $ans = "" unless (defined $ans); - my $fans = ::Formula($ans); - my $student_ans = $hr_ans_init->{student_ans}; - $student_ans = "" unless (defined $student_ans); - - my $hr_ans = $fans->cmp()->evaluate($student_ans); - # warn ::pretty_print($hr_ans); - - if ( defined $hr_ans->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($hr_ans->{error_message} eq "") ){ - return $hr_ans; - } - } - - my $ans_numerator=""; - my $ans_denominator=""; - - my $sans_numerator=""; - my $sans_denominator=""; - - $fans = ::Formula($ans); - my $ans_split = findNumeratorDenominator($fans->{tree}); - # TODO: work with error status of the above call - if ( $ans_split->{error_code} > 0 ){ - # we have something wrong in the correct answer!!! - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Something is wrong with the correct answer. Please let your professor know. Thanks!" ); - warn "Something is wrong with the correct answer. Please let your professor know. Thanks!"; - # warn ::pretty_print($ans_split); - return $hr_ans; - } - - $ans_numerator = $ans_split->{numerator}; - $ans_denominator = $ans_split->{denominator}; - if ($ans_denominator eq ""){ - $ans_denominator = "1"; - } - - # debug! - # warn "Numerator: |". ::pretty_print($ans_numerator) ."|"; - # warn "Denominator: |". ::pretty_print($ans_denominator) ."|"; - - - my $fsans = ::Formula($student_ans); - my $sans_split = findNumeratorDenominator($fsans->{tree}); - # TODO: work with error status of the above call - # warn ::pretty_print($sans_split); - - if ( $sans_split->{error_code} > 0 ){ - # we have something wrong in the rational expression - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like a single rational expression" ); - return $hr_ans; - } - $sans_numerator = $sans_split->{numerator}; - $sans_denominator = $sans_split->{denominator}; - if ($sans_denominator eq ""){ - $sans_denominator = "1"; - } - - # TODO: check if student answer is in fact a 'flat' rational expression - - my $snum_check = checkNumeratorDenominator($sans_numerator); - my $sden_check = checkNumeratorDenominator($sans_denominator); - - if ( ($snum_check + $sden_check) != 0){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like a valid rational expression. Are there parenthesis missing around numerator/denominator?" ); - return $hr_ans; - } - - # debug! - #warn "Student Numerator: |". ::pretty_print($sans_numerator) ."|"; - #warn "Student Denominator: |". ::pretty_print($sans_denominator) ."|"; - - $student_formula = ::Formula($student_ans); - $correct_student_formula = ::Formula( "( $sans_numerator )/( $sans_denominator )"); - # $result_hash = $correct_student_formula->cmp()->evaluate($student_formula); - - if ( $student_formula != $correct_student_formula ) { - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Does your answer miss parenthesis around numerator or denominator?" ); - return $hr_ans; - } - - if ($hr_ans->{score} == 0 ) { - # student answer is not equivalent to the correct one - # no need to go into the details - return $hr_ans; - } - - if ($options{numerator_factored}==1){ - # both numerator and denominator must be in factored form - if ($options{strict_match}==1){ - $numerator_hash = main::FactoredFormula($ans_numerator)->cmp()->evaluate($sans_numerator); - $denominator_hash = main::FactoredFormula($ans_denominator)->cmp()->evaluate($sans_denominator); - } else { - $numerator_hash = main::FactoredFormulaUpToConstant($ans_numerator)->cmp()->evaluate($sans_numerator); - $denominator_hash = main::FactoredFormulaUpToConstant($ans_denominator)->cmp()->evaluate($sans_denominator); - } - } else { - # here we expect only denominator to be factored, and numerator could be in any form - if ($options{strict_match}==1){ - $numerator_hash = main::Formula($ans_numerator)->cmp()->evaluate($sans_numerator); - $denominator_hash = main::FactoredFormula($ans_denominator)->cmp()->evaluate($sans_denominator); - } else { - $numerator_hash = main::FormulaUpToConstant($ans_numerator)->cmp()->evaluate($sans_numerator); - $denominator_hash = main::FactoredFormulaUpToConstant($ans_denominator)->cmp()->evaluate($sans_denominator); - } - } - my $total_result = 1; - $total_result * $numerator_hash->{up_to_constant} if (defined $numerator_hash->{up_to_constant}); - $total_result * $denominator_hash->{up_to_constant} if (defined $denominator_hash->{up_to_constant}); - - # debug - # warn $total_result; - - if ($total_result == 1){ - $hr_ans->{score} = 0.5*($numerator_hash->{score} + $denominator_hash->{score}); - # debug - # warn "Score: ". $hr_ans->{score}; - return $hr_ans; - } else { - # for now this means that something was really wrong, - # but I don't see how we could even end up here - $hr_ans->{score} = 0; - return $hr_ans; - } - - #we must return the reference to answer hash -} - -sub cmp { - my $self = shift; - my %options = @_; - my $ans = new AnswerEvaluator( - 'correct_ans' => $self->{Formula}, - 'type' => 'Factored Rational Formula', - ); - $ans->install_evaluator('erase'); - $ans->install_evaluator(\&theEvaluator, %options); - return $ans; -} diff --git a/OpenProblemLibrary/macros/UW-Stout/stoutRestrictiveGraders.pl b/OpenProblemLibrary/macros/UW-Stout/stoutRestrictiveGraders.pl deleted file mode 100644 index bfb9a8e7c3..0000000000 --- a/OpenProblemLibrary/macros/UW-Stout/stoutRestrictiveGraders.pl +++ /dev/null @@ -1,230 +0,0 @@ -=head1 NAME - -stoutRestrictiveGraders.pl - -=head1 SYNOPSIS - -TODO - -=head1 DESCRIPTION - -TODO - -=head1 AUTHOR - -Alex Basyrov, basyrova@uwstout.edu - -=cut - - -loadMacros( -"MathObjects.pl", -); - - -sub MonomialFormula{ - my $string_formula = shift; - my $ref_monomial_formula = new stoutMonomialFormula($string_formula); - return $ref_monomial_formula; -} - -package stoutMonomialFormula; - -sub new { - my $class = shift; - my $string_formula = shift; - my $self = { Formula => $string_formula }; - bless $self, $class; -} - -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $new_formula = ::Formula($original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub onlyDigits{ - $string = shift; - - if ($string=~ /^(-|\+|)(\d+)$/){ - return 1; - } else { - return 0; - } -} - -sub theEvaluator { - my $hr_ans = shift; - my %options = @_; - my $ans = $hr_ans->{correct_ans}; - my $fans = ::Formula($ans); - my @factorspowers = makefactors($fans->{tree}); - # warn "Factors and powers: ". join (",",@factorspowers); - my @factors = (); - my @powers = (); - my $i; - for ($i = 0; $i < scalar @factorspowers; $i+=2){ - push @factors, $factorspowers[$i]; - push @powers, $factorspowers[$i+1]; - } - - #warn "Factors: ". join (",",@factors); - #warn "Powers: ". join (",",@powers); - - my $numCorrectFactors = scalar @factors; - # my $var = shift; - - my $student_ans = $hr_ans->{student_ans}; - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = ::Formula($ans)->cmp()->evaluate($student_ans); - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - if ( $ans_hash->{score} == 1) { - # since we got here, student's answer is equivalent to the correct one - my $sans = ::Formula($student_ans); - my @sfactorspowers = makefactors($sans->{tree}); - my @sfactors=(); - my @spowers=(); - for ($i = 0; $i < scalar @sfactorspowers; $i+=2){ - push @sfactors, $sfactorspowers[$i]; - push @spowers, $sfactorspowers[$i+1]; - } - #warn "Student Factors: ". join (",",@factors); - #warn "Student Powers: ". join (",",@powers); - my $snumCorrectFactors = scalar @sfactors; - - if ( $snumCorrectFactors < $numCorrectFactors) { - $ans_hash->setKeys( 'ans_message' => "Make sure that your answer is simplified completely!" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - if ( $snumCorrectFactors > $numCorrectFactors) { - $ans_hash->setKeys( 'ans_message' => "Your answer is equivalent to the correct one, but it has too many terms. Did you simplify your answer completely?" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - - # since we got here, both answers have the same number of factors - # we can compare each correct factor to student's and see what we get - my $correctFactors = 0; - my $accumulatedSign = 1; - my $accumulatedFactor = 1; - my $offByConstant = 0; - for ($i = 0; $i < scalar @factors; $i++) { - $cfactor = $factors[$i]; - $cpower = ::Formula($powers[$i]); - for ($j = 0; $j < scalar @sfactors; $j++) { - $sfactor = $sfactors[$j]; - $spower = ::Formula($spowers[$j]); - if (!onlyDigits($spowers[$j])){ - # numeric exponent is not simplified - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Exponents need to be completely simplified."); - return $ans_hash; - } - my $context = ::Context()->copy; - $context->flags->set(no_parameters=>0); - $pname='Az'; - $context->variables->add($pname=>'Parameter'); - my $c0 = ::Formula($context,$pname); - $fstudent = ::Formula($context,$sfactor); - if ($fstudent->isConstant){ - if (!onlyDigits($sfactor)){ - # numeric factor is not simplified - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Constants need to be completely simplified."); - return $ans_hash; - } - } - $fcorrect = ::Formula($context,"$c0 * ($cfactor)"); - if ( $fcorrect == $fstudent ) { - # warn "Matched: $student vs. $correct"; - # warn "Value of constant: ". $context->variables->value('C0'); - $parameterValue = $context->variables->value($pname); - if ( abs($parameterValue) == 1){ - # only factors correct up to negative are counted - # but that could be changed to 'up to a constant', if needed - if ( $cpower == $spower){ - $correctFactors++; - $accumulatedFactor *= ($parameterValue)**($cpower); - } - } else { - # factor is close to correct one, but it differes by a non-trivial constant - $offByConstant = 1; - } - } - } - } - - if ( ($correctFactors == $numCorrectFactors) && ($accumulatedFactor == 1) ) { - $ans_hash->{score} = 1; - return $ans_hash; - } - - if ($offByConstant == 1){ - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Your answer is algebraically equivalent to the correct one, but it does not appear to be completely simplified."); - return $ans_hash; - } - - $ans_hash->{score} = 0; - $ans_hash->setKeys( 'ans_message' =>"Your answer is algebraically equivalent to the correct one, but it does not appear to be completely simplified."); - } - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - -sub cmp { - my $self = shift; - my %options = @_; - my $ans = new AnswerEvaluator( - 'correct_ans' => $self->{Formula}, - 'type' => 'Factored Formula', - ); - $ans->install_evaluator(\&theEvaluator); - return $ans; -} - -sub makefactors { - my $me = shift; - - my $bop = $me->{bop}; - #$bop =~ s/^~~s+//; - #$bop =~ s/~~s+$//; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - #warn "Expression: |". $me->string ."|"; - #$warn "BOP: |". $bop ."|"; - #warn "LOP: |". $me->{lop}->string ."|"; - #warn "ROP: |". $me->{rop}->string ."|"; - - if ( ($bop eq "*") ) { - return ( makefactors($me->{lop}), makefactors($me->{rop}) ); - } else { - if ( ( $bop eq "^") || ($bop eq "**") ) { - # the factor has exponent! - my $base = $me->{lop}->string; - my $exponent = $me->{rop}->string; - return ($base, $exponent); - } else { - # the factor has no exponent - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } - } -} diff --git a/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGraders.pl b/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGraders.pl deleted file mode 100644 index 8348025b0d..0000000000 --- a/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGraders.pl +++ /dev/null @@ -1,1456 +0,0 @@ -=head1 NAME - -stoutSpecialGraders.pl - -=head1 SYNOPSIS - -TODO - -=head1 DESCRIPTION - -TODO - -=head1 AUTHOR - -Alex Basyrov, basyrova@uwstout.edu - -=cut - - -loadMacros( -"MathObjects.pl", -"stoutUtils.pl", -); - -# the formula that is a sum of terms, the terms are compared individually -sub SumOfTermsFormula{ - my $string_formula = shift; - my $ref = new stoutSumOfTermsFormula($string_formula); - return $ref; -} - -# put arbitrary constants into the context -sub PutArbitraryConstants{ - my $count = shift; - $count = 9 unless defined $count; - foreach $param (1..$count){ - Context()->variables->add("C$param" => 'Real'); - } -} - -# put indeterminant parameters into the context -sub PutIndeterminantParameters{ - my $count = shift; - $count = 25 unless defined $count; - $count--; - my @names = ('A'..'Z'); - my $i; - foreach $i (0..$count){ - my $param = $names[$i]; - Context()->variables->add($param => 'Real'); - } -} - -# the formula that represent a general solution to linear high order diff equation -sub LinearGeneralSolution{ - my $string_formula = shift; - my $ref = new stoutLinearGeneralSolution($string_formula); - return $ref; -} - - -sub GenericEquation{ - my $string_formula = shift; - my $ref = new stoutGenericEquation($string_formula); - return $ref; -} - -sub SlopeInterceptEquation{ - my $string_formula = shift; - my $ref = new stoutSlopeInterceptEquation($string_formula); - return $ref; -} - -sub CircleStandardEquation{ - my $string_formula = shift; - my $ref = new stoutCircleStandardEquation($string_formula); - return $ref; -} - -package stoutSumOfTermsFormula; - - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - }; - bless $self, $class; -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - - -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $context = $self->{Context}; - - stoutDebugMessage("Context inherited by formula:".main::pretty_print($context->{flags})); - - my $new_formula = main::Formula($context, $original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - $options{factored_terms} = 0 unless defined $options{factored_terms}; - - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $context = $hr_ans->{correct_value}->{Context}; - my $fans = main::Formula($context, $ans); - my @terms = maketerms($fans->{tree}); - - stoutDebugShowArrayRef("Terms", \@terms); - - my $numCorrectTerms = scalar @terms; - my $student_ans = $hr_ans->{student_ans}; - - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - stoutDebugShowHashRef("ans_hash variable", $ans_hash); - stoutDebugShowVar("isPreview in hash", $ans_hash->{isPreview}); - my $isPreview = 0; - if (defined $ans_hash->{isPreview}){ - if ( !($ans_hash->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - return $ans_hash; - } - } - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - # see if we have to go on - my $can_proceed = 0; - if ( $ans_hash->{score} == 1) { - # the student answer is algebraically equivalent to the correct answer - # can look into the structure of student answer - $can_proceed = 1; - } - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - if ( $can_proceed ) { - # since we got here, student's answer is equivalent to the correct one - my $sans = main::Formula($context, $student_ans); - my @sterms = maketerms($sans->{tree}); - - stoutDebugShowArrayRef("Student Terms", \@sterms); - - my $snumTerms = scalar @sterms; - my $numTerms = scalar @terms; - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $fans->TeX; - $ans_hash->{preview_latex_string} = $sans->TeX; - $ans_hash->{preview_text_string} = $sans->string; - if ($snumTerms != $numTerms){ - $ans_hash->setKeys( 'ans_message' => "Your answer is algebraically equivalent to the correct one, but it is not in the correct form. Make sure that you simplify the answer and that the answer is in the requested form" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - - my %matched; - - my ($i, $j); - - my $correctTerms = 0; - - for ($i = 0; $i < $numTerms; $i++) { - $cTerm = $terms[$i]; - - next if $matched{$i}; - - for ($j = 0; $j < $snumTerms; $j++) { - $sTerm = $sterms[$j]; - my $result = undef; - if ($options{factored_terms}){ - $cTermFactored = new stoutFactoredFormula($cTerm); - $ans_hash_term = $cTermFactored->cmp()->evaluate($sTerm); - $result = $ans_hash_term->{score}; - } else { - $result = stoutUtils::matchUpToConstant($context, $cTerm, $sTerm); - } - if ((defined $result) && ($result==1)){ - $matched{$i} = 1; - $correctTerms++; - } else { - # factor is close to correct one, but it differes by a non-trivial constant - } - } - } - - if ($correctTerms==$numTerms){ - $ans_hash->{score} = 1; - return $ans_hash; - } else { - $ans_hash->setKeys( 'ans_message' => "Your answer is algebraically equivalent to the correct one, but it is not in the correct form. Make sure that you simplify the answer and that the answer is in the requested form" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - } - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Sum of Terms Formula", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - my $strFormula = $self->{Formula}; - return main::Formula($context, $strFormula)->TeX(); -} - - -package stoutLinearGeneralSolution; -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - }; - bless $self, $class; -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $context = $self->{Context}; - - stoutDebugMessage("Context inherited by formula:".main::pretty_print($context->{flags})); - - my $new_formula = main::Formula($context, $original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - my $isPreview = 0; - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - - my $constName; - my %possibleConsts; - foreach $constName ('A'..'Z'){ - $possibleConsts{$constName} = 1; - } - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $context = $hr_ans->{correct_value}->{Context}; - my $fans = main::Formula($context, $ans); - my @terms = maketerms($fans->{tree}); - - stoutDebugShowArrayRef("Terms", \@terms); - - my $numCorrectTerms = scalar @terms; - my $student_ans = $hr_ans->{student_ans}; - - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - stoutDebugShowHashRef("ans_hash variable", $ans_hash); - stoutDebugShowVar("isPreview in hash", $ans_hash->{isPreview}); - if (defined $ans_hash->{isPreview}){ - if ( !($ans_hash->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - return $ans_hash; - } - } - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - # since we got here, student's answer is equivalent to the correct one - my $sans = main::Formula($context, $student_ans); - my @sterms = maketerms($sans->{tree}); - - stoutDebugShowArrayRef("Student Terms", \@sterms); - - my $snumTerms = scalar @sterms; - my $numTerms = scalar @terms; - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $fans->TeX; - $ans_hash->{preview_latex_string} = $sans->TeX; - $ans_hash->{preview_text_string} = $sans->string; - #if ($snumTerms != $numTerms){ - # $ans_hash->setKeys( 'ans_message' => "Please make sure that there is one arbitrary constant per term in your answer."); - # $ans_hash->{score} = 0; - # return $ans_hash; - #} - - my %matched; - my %smatched; - - my ($i, $j); - - my $correctTerms = 0; - - my %sConstantsUsed; - my %cConstantsUsed; - - my $diagMessage = ""; - - for ($i = 0; $i < $numTerms; $i++) { - $cTerm = $terms[$i]; - stoutDebugShowVar("Correct term", $cTerm); - #next if $matched{$i}; - - for ($j = 0; $j < $snumTerms; $j++) { - next if $matched{$i}; # to skip the rest of the work when we found the match - next if $smatched{$j}; # to skip the rest of the work when we found the match - $sTerm = $sterms[$j]; - stoutDebugShowVar("Student term", $sTerm); - my $result = undef; - $cTermFormula = main::Formula($context,$cTerm); - # @cFactors = makefactors($cTermFormula->{tree}); - $hr_cVars = $cTermFormula->{variables}; - @cConsts = (); - foreach $vn (keys %{$hr_cVars}){ - if ($vn =~ /C([0-9])+/){ - push (@cConsts, $vn); - } elsif ($possibleConsts{$vn}){ - push (@cConsts, $vn); - } - } - #at this stage @cConsts contains all the arbitrary constants that the correct term had - #there should be only one, but we will not assume that for now - stoutDebugShowHashRef("Correct vars", $hr_cVars); - my $cConstantCount = scalar (@cConsts); - foreach $ac (@cConsts){ - stoutDebugMessage("Found constant: ", $ac); - #$cTermFormulaDiff = $cTermFormula->D($ac); - #stoutDebugMessage("The derivative result: ", $cTermFormula, " -> ", $cTermFormulaDiff); - $cTermFormula = $cTermFormula->substitute($ac=>1); - $cConstantsUsed{$ac}=1; - } - stoutDebugShowArrayRef("Correct term factors",\@cFactors); - - $sTermFormula = main::Formula($context,$sTerm); - @sFactorsPowers = makefactors($sTermFormula->{tree}); - @sFactors = (); - @sPowers = (); - my $k; - for ($k = 0; $k < scalar (@sFactorsPowers); $k+=2){ - push (@sFactors, $sFactorsPowers[$k]); - push (@sPowers, $sFactorsPowers[$k+1]); - } - $hr_sVars = $sTermFormula->{variables}; - @sConsts = (); - foreach $vn (keys %{$hr_sVars}){ - if ($vn =~ /C([0-9])+/){ - push (@sConsts, $vn); - } elsif ($possibleConsts{$vn}){ - push (@sConsts, $vn); - } - } - stoutDebugShowHashRef("Student vars", $hr_sVars); - my $sConstantCount = scalar (@sConsts); - foreach $ac (@sConsts){ - stoutDebugMessage("Found constant: ", $ac); - my $thisConstantMatched = 0; - # this would verify that the arbitrary constant comes in as a separate factor in the term - #$sTermFormulaDiff = $sTermFormula->D($ac); - #stoutDebugMessage("Student derivative result: ", $sTermFormula, " -> ", $sTermFormulaDiff); - foreach $fac (@sFactors){ - next if $thisConstantMatched; - my $res = stoutUtils::matchUpToConstant($context, $ac, $fac); - if (defined $res){ - $thisConstantMatched = 1; - $fac = main::Formula($context,$fac)->substitute($ac=>1)->string; - } - } - # $sTermFormula = $sTermFormula->substitute($ac=>1); - if (!$thisConstantMatched){ - $ans_hash->setKeys( 'ans_message' => "Arbitrary constant $ac does not appear to be properly used in your answer" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - $sConstantsUsed{$ac}=1; - @sTermsFP = (); - for ($k = 0; $k < scalar (@sFactors); $k++){ - push (@sTermsFP, "(".$sFactors[$k].")^(".$sPowers[$k].")"); - } - $sTermFormula = main::Formula($context, join("*",@sTermsFP)); - } - - stoutDebugShowArrayRef("Student term factors w/o powers",\@sFactors); - - stoutDebugMessage("Matching |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - - if ($cConstantCount != 0){ - $result = stoutUtils::matchUpToConstant($context, $cTermFormula->string, $sTermFormula->string, non_trivial=>1); - } else { - $result = undef; - # no constant in the correc term - should be no constant in the student term - $res_hash = $cTermFormula->cmp()->evaluate($sTerm); - if ($res_hash->{score}==1){ - $result = 1; - } - } - - if ($sConstantCount != $cConstantCount) { - stoutDebugMessage("Did not match $cTerm with $sTerm due to constant count mismatch"); - if (defined $result){ - $ans_hash->setKeys( 'ans_message' => "You might want to double-check the use of arbitrary constants in your answer" ); - } - $result = undef; - } - - if (defined $result){ - $matched{$i} = 1; - $smatched{$j} = 1; - $correctTerms++; - stoutDebugMessage("MATCHED |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - } else { - stoutDebugMessage("FAILED to match |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - } - } - } - - $sConstantsCheck = 1; - $cConstantsCheck = 1; - foreach $c (keys %sConstantsUsed){ - if ( !(defined $cConstantsUsed{$c}) ){ - $sConstantsCheck = 0; - } - } - - foreach $c (keys %cConstantsUsed){ - if ( !(defined $sConstantsUsed{$c}) ){ - $cConstantsCheck = 0; - } - } - - if ( ($correctTerms==$numTerms) && ($numTerms == $snumTerms) ){ - if ($sConstantsCheck*$cConstantsCheck == 1){ - $ans_hash->{score} = 1; - return $ans_hash; - } else { - $ans_hash->setKeys( 'ans_message' => "Your answer is close to being correct, but you need to check your use of arbitrary constants" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - } elsif ($snumTerms < $numTerms) { - $ans_hash->setKeys( 'ans_message' => "Does your answer contain all the terms? Also please make sure there is only one arbitrary constant per term" ); - $ans_hash->{score} = 0; - return $ans_hash; - } elsif ($snumTerms > $numTerms) { - $ans_hash->setKeys( 'ans_message' => "Does your answer contain any unnecessary terms? Also please make sure there is only one arbitrary constant per term" ); - $ans_hash->{score} = 0; - return $ans_hash; - } else { - $ans_hash->{score} = 0; - return $ans_hash; - } - - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - # stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Linear General Solutuion", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - #stoutDebugShowVar("Expression", $me->string); - #stoutDebugShowVar("BOP", $bop); - #stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - #stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -sub makefactors { - my $me = shift; - - my $bop = $me->{bop}; - - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - my $uop = $me->{uop}; - if (defined $uop){ - $uop =~ s/^\s+//; - $uop =~ s/\s+$//; - } else { - $uop = ""; - } - - #stoutDebugShowVar("Expression", $me->string); - #stoutDebugShowVar("BOP", $bop); - #stoutDebugShowVar("UOP", $uop); - #stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - #stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - #stoutDebugShowVar("OP", $me->{op}->string) if defined $me->{op}; - - if ( ($bop eq "*") ) { - return ( makefactors($me->{lop}), makefactors($me->{rop}) ); - } elsif ( ( $bop eq "^") || ($bop eq "**") ) { - # the factor has exponent! - my $base = $me->{lop}->string; - my $exponent = $me->{rop}->string; - return ($base, $exponent); - } elsif ( ($bop eq "" ) && ($uop eq 'u-') ) { - # there is no binary operation - could have unitary - # need to figure out what to do here - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } else { - # the factor has no exponent - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } -} - - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - my $strFormula = $self->{Formula}; - return main::Formula($context, $strFormula)->TeX(); -} - -package stoutGenericEquation; - - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - my $result = $class->getSides($string_formula); - - if ($result->{error}){ - warn "This: |$string_formula| does not look like an equation"; - } - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - LHS => $result->{lhs}, - RHS => $result->{rhs}, - }; - bless $self, $class; -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $isPreview = 0; - - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $ans_tex = $hr_ans->{correct_value}->TeX; - my $clhs = $hr_ans->{correct_value}->{LHS}; - my $crhs = $hr_ans->{correct_value}->{RHS}; - - my $context = $hr_ans->{correct_value}->{Context}; - - my $student_ans = $hr_ans->{student_ans}; - - my $sresult = $self->getSides($student_ans); - stoutDebugShowHashRef("Student result", $sresult); - - if ($sresult->{error}){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like an equation" ); - return $hr_ans; - } - - my $slhs = $sresult->{lhs}; - my $srhs = $sresult->{rhs}; - - my $ans_hash = main::Formula($context, $clhs)->cmp()->evaluate($slhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the left hand side of your equation. ".$err ); - return $ans_hash; - } - } - - $ans_hash = main::Formula($context, $crhs)->cmp()->evaluate($srhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the right hand side of your equation. ".$err ); - return $ans_hash; - } - } - - my $sans_string = $slhs .'='.$srhs; - my $sans_tex = main::Formula($context, $slhs)->TeX .'='. main::Formula($context, $srhs)->TeX; - - $ans_hash = $hr_ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $ans_tex; - $ans_hash->{preview_latex_string} = $sans_tex; - $ans_hash->{preview_text_string} = $sans_string; - - - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - my $match = stoutUtils::matchUpToConstant($context, "($clhs)-($crhs)","($slhs)-($srhs)", non_trivial => 1 ); - - if ( defined $match ){ - $ans_hash->{score} = 1; - return $ans_hash; - } - - # my $f_clhs = main::Formula($clhs); - # my $f_crhs = main::Formula($crhs); - - # $res_l = $f_clhs->cmp()->evaluate($slhs); - # $res_r = $f_crhs->cmp()->evaluate($srhs); - - # if ( ($res_l->{score} == 1) && ($res_r->{score} = 1) ){ - # $hr_ans->{score} = 1; - # return $hr_ans; - # } - - # $res_l = $f_clhs->cmp()->evaluate($srhs); - # $res_r = $f_crhs->cmp()->evaluate($slhs); - - # if ( ($res_l->{score} == 1) && ($res_r->{score} = 1) ){ - # $hr_ans->{score} = 1; - # return $hr_ans; - # } - - - $ans_hash->{score} = 0; - - return $ans_hash; -} - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Generic Equation", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub hasEquality{ - my $self = shift; - my $equation = shift; - - $equation = "" unless (defined $equation); - - my $lhs = ""; - my $rhs = ""; - - if ($equation=~/(.+)=(.+)/){ - # we have equality sign in the formula - $lhs = $1; - $rhs = $2; - return { - lhs => $lhs, - rhs => $rhs, - error => 0, - }; - } else { - # there is no equality sign, need to complain - return { - lhs => undef, - rhs => undef, - error => 1, - }; - } -} - -sub getSides{ - my $self = shift; - my $equation = shift; - - stoutDebugShowVar("Equation string", $equation); - - my $result = $self->hasEquality($equation); - - stoutDebugShowHashRef("Result hash", $result); - - if ( !($result->{error}) ){ - # check to see if there are more equality signs in the mix - $lhs_result = $self->hasEquality($result->{lhs}); - $rhs_result = $self->hasEquality($result->{rhs}); - stoutDebugShowHashRef("LHS Result hash", $lhs_result); - stoutDebugShowHashRef("RHS Result hash", $rhs_result); - - if ( ( !($lhs_result->{error}) ) || ( !($rhs_result->{error}) ) ){ - # no error in either means there are - # equality signs in that 'side' - # that is bad! - return { - lhs => undef, - rhs => undef, - error => 1, - }; - } else { - # we've got no more equality signs - # everything is good - return { - lhs => $result->{lhs}, - rhs => $result->{rhs}, - error => 0, - }; - } - } - - return { - lhs => undef, - rhs => undef, - error => 1, - }; - -} - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; - -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - - my $lhsFormula = main::Formula($context, $self->{LHS}); - my $rhsFormula = main::Formula($context, $self->{RHS}); - - my $res = $lhsFormula->TeX() . '=' . $rhsFormula->TeX(); - - return $res; -} - -package stoutSlopeInterceptEquation; -@stoutSlopeInterceptEquation::ISA = qw(stoutGenericEquation); - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - my $context = main::Context()->copy(); - - my $result = $class->getSides($string_formula); - - if ($result->{error}){ - warn "This: |$string_formula| does not look like an equation"; - } - - my $lhs = $result->{lhs}; - $lhs =~ s/^\s+//; - $lhs =~ s/\s+$//; - - my $rhs = $result->{rhs}; - $rhs =~ s/^\s+//; - $rhs =~ s/\s+$//; - - if ($lhs ne "y"){ - warn "This |$string_formula| should have y as its left hand side. Got |$lhs|."; - } - - my $fans = main::Formula($context, $rhs); - my @terms = maketerms($fans->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - warn "This |$string_formula| should have at most 2 terms as its right hand side. Got |$rhs|."; - } - - my $const_term = 0; - my $slope_term = 0; - my $error_in_slope_term = 0; - - for my $tt (@terms){ - my $f_tt = main::Formula($context, $tt); - if ($f_tt->isConstant){ - $const_term++; - }else{ - my $f_m = $f_tt->substitute('x'=>1); - my $f_mm = main::Formula($context, "$f_m * x"); - if ($f_mm == $f_tt){ - $slope_term++; - }else{ - $error_in_slope_term++; - } - } - } - - if ( ($const_term > 1) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it contains two constant terms on its left hand side. Got |$rhs|."; - } - - if ( ($slope_term > 1) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it contains two terms with x on its left hand side. Got |$rhs|."; - } - - if ( ($error_in_slope_term > 0) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it does not seem to contain a simplified slope term. Got |$rhs|."; - } - - my $self = { - Formula => $string_formula, - Context => $context, - LHS => $result->{lhs}, - RHS => $result->{rhs}, - }; - bless $self, $class; - -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Slope Intercept Equation", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $isPreview = 0; - - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $ans_tex = $hr_ans->{correct_value}->TeX; - my $clhs = $hr_ans->{correct_value}->{LHS}; - my $crhs = $hr_ans->{correct_value}->{RHS}; - - my $context = $hr_ans->{correct_value}->{Context}; - - my $student_ans = $hr_ans->{student_ans}; - - my $sresult = $self->getSides($student_ans); - stoutDebugShowHashRef("Student result", $sresult); - - if ($sresult->{error}){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like an equation" ); - return $hr_ans; - } - - my $slhs = $sresult->{lhs}; - my $srhs = $sresult->{rhs}; - - my $ans_hash = main::Formula($context, $clhs)->cmp()->evaluate($slhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the left hand side of your equation. ".$err ); - return $ans_hash; - } - } - - $ans_hash = main::Formula($context, $crhs)->cmp()->evaluate($srhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the right hand side of your equation. ".$err ); - return $ans_hash; - } - } - - my $sans_string = $slhs .'='.$srhs; - my $sans_tex = main::Formula($context, $slhs)->TeX .'='. main::Formula($context, $srhs)->TeX; - - $ans_hash = $hr_ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $ans_tex; - $ans_hash->{preview_latex_string} = $sans_tex; - $ans_hash->{preview_text_string} = $sans_string; - $ans_hash->{score} = 0; # just in case - - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - # now we check if student answer looks like slope intercept equation - - $slhs =~ s/^\s+//; - $slhs =~ s/\s+$//; - - $srhs =~ s/^\s+//; - $srhs =~ s/\s+$//; - - if ($slhs ne "y"){ - $ans_hash->setKeys( 'ans_message' => "Your answer should contain y as the left hand side of the equation" ); - return $ans_hash; - } - - my $fans = main::Formula($context, $srhs); - my @terms = maketerms($fans->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 2 terms as its right hand side." ); - return $ans_hash; - } - - my $const_term = 0; - my $slope_term = 0; - my $error_in_slope_term = 0; - - for my $tt (@terms){ - my $f_tt = main::Formula($context, $tt); - if ($f_tt->isConstant){ - $const_term++; - }else{ - my $f_m = $f_tt->substitute('x'=>1); - my $f_mm = main::Formula($context, "$f_m * x"); - if ($f_mm == $f_tt){ - $slope_term++; - }else{ - $error_in_slope_term++; - } - } - } - - if ( ($const_term > 1) ){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 1 constant term in its right hand side." ); - return $ans_hash; - } - - if ( ($slope_term > 1) ){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 1 term with x in its right hand side." ); - return $ans_hash; - } - - if ( ($error_in_slope_term > 0) ){ - $ans_hash->setKeys( 'ans_message' => "It looks like the term with x in your answer could be simplified. " ); - return $ans_hash; - } - - # if we're at this stage, the equation should look like slope-intercept equation. - # we now do a simple check of the equation - - my $match = stoutUtils::matchUpToConstant($context, "($clhs)-($crhs)","($slhs)-($srhs)", non_trivial => 0 ); - - if ( defined $match ){ - $ans_hash->{score} = 1; - return $ans_hash; - } - - $ans_hash->{score} = 0; # again to be sure - - return $ans_hash; -} - -package stoutCircleStandardEquation; -@stoutCircleStandardEquation::ISA = qw(stoutGenericEquation); diff --git a/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGradersCircle.pl b/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGradersCircle.pl deleted file mode 100644 index 7207bb7e1e..0000000000 --- a/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGradersCircle.pl +++ /dev/null @@ -1,1634 +0,0 @@ -=head1 NAME - -stoutSpecialGradersCircle.pl - -=head1 SYNOPSIS - -TODO - -=head1 DESCRIPTION - -TODO - -=head1 AUTHOR - -Alex Basyrov, basyrova@uwstout.edu - -=cut - - -loadMacros( -"MathObjects.pl", -"stoutUtils.pl", -); - -# the formula that is a sum of terms, the terms are compared individually -sub SumOfTermsFormula{ - my $string_formula = shift; - my $ref = new stoutSumOfTermsFormula($string_formula); - return $ref; -} - -# put arbitrary constants into the context -sub PutArbitraryConstants{ - my $count = shift; - $count = 9 unless defined $count; - foreach $param (1..$count){ - Context()->variables->add("C$param" => 'Real'); - } -} - -# put indeterminant parameters into the context -sub PutIndeterminantParameters{ - my $count = shift; - $count = 25 unless defined $count; - $count--; - my @names = ('A'..'Z'); - my $i; - foreach $i (0..$count){ - my $param = $names[$i]; - Context()->variables->add($param => 'Real'); - } -} - -# the formula that represent a general solution to linear high order diff equation -sub LinearGeneralSolution{ - my $string_formula = shift; - my $ref = new stoutLinearGeneralSolution($string_formula); - return $ref; -} - - -sub GenericEquation{ - my $string_formula = shift; - my $ref = new stoutGenericEquation($string_formula); - return $ref; -} - -sub SlopeInterceptEquation{ - my $string_formula = shift; - my $ref = new stoutSlopeInterceptEquation($string_formula); - return $ref; -} - -sub CircleStandardEquation{ - my $string_formula = shift; - my $ref = new stoutCircleStandardEquation($string_formula); - return $ref; -} - -package stoutSumOfTermsFormula; - - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - }; - bless $self, $class; -} - -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $context = $self->{Context}; - - stoutDebugMessage("Context inherited by formula:".main::pretty_print($context->{flags})); - - my $new_formula = main::Formula($context, $original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - $options{factored_terms} = 0 unless defined $options{factored_terms}; - - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $context = $hr_ans->{correct_value}->{Context}; - my $fans = main::Formula($context, $ans); - my @terms = maketerms($fans->{tree}); - - stoutDebugShowArrayRef("Terms", \@terms); - - my $numCorrectTerms = scalar @terms; - my $student_ans = $hr_ans->{student_ans}; - - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - stoutDebugShowHashRef("ans_hash variable", $ans_hash); - stoutDebugShowVar("isPreview in hash", $ans_hash->{isPreview}); - my $isPreview = 0; - if (defined $ans_hash->{isPreview}){ - if ( !($ans_hash->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - return $ans_hash; - } - } - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - # see if we have to go on - my $can_proceed = 0; - if ( $ans_hash->{score} == 1) { - # the student answer is algebraically equivalent to the correct answer - # can look into the structure of student answer - $can_proceed = 1; - } - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - if ( $can_proceed ) { - # since we got here, student's answer is equivalent to the correct one - my $sans = main::Formula($context, $student_ans); - my @sterms = maketerms($sans->{tree}); - - stoutDebugShowArrayRef("Student Terms", \@sterms); - - my $snumTerms = scalar @sterms; - my $numTerms = scalar @terms; - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $fans->TeX; - $ans_hash->{preview_latex_string} = $sans->TeX; - $ans_hash->{preview_text_string} = $sans->string; - if ($snumTerms != $numTerms){ - $ans_hash->setKeys( 'ans_message' => "Your answer is algebraically equivalent to the correct one, but it is not in the correct form. Make sure that you simplify the answer and that the answer is in the requested form" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - - my %matched; - - my ($i, $j); - - my $correctTerms = 0; - - for ($i = 0; $i < $numTerms; $i++) { - $cTerm = $terms[$i]; - - next if $matched{$i}; - - for ($j = 0; $j < $snumTerms; $j++) { - $sTerm = $sterms[$j]; - my $result = undef; - if ($options{factored_terms}){ - $cTermFactored = new stoutFactoredFormula($cTerm); - $ans_hash_term = $cTermFactored->cmp()->evaluate($sTerm); - $result = $ans_hash_term->{score}; - } else { - $result = stoutUtils::matchUpToConstant($context, $cTerm, $sTerm); - } - if ((defined $result) && ($result==1)){ - $matched{$i} = 1; - $correctTerms++; - } else { - # factor is close to correct one, but it differes by a non-trivial constant - } - } - } - - if ($correctTerms==$numTerms){ - $ans_hash->{score} = 1; - return $ans_hash; - } else { - $ans_hash->setKeys( 'ans_message' => "Your answer is algebraically equivalent to the correct one, but it is not in the correct form. Make sure that you simplify the answer and that the answer is in the requested form" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - } - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Sum of Terms Formula", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - my $strFormula = $self->{Formula}; - return main::Formula($context, $strFormula)->TeX(); -} - - - -package stoutLinearGeneralSolution; -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - }; - bless $self, $class; -} - -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $context = $self->{Context}; - - stoutDebugMessage("Context inherited by formula:".main::pretty_print($context->{flags})); - - my $new_formula = main::Formula($context, $original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - my $isPreview = 0; - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - - my $constName; - my %possibleConsts; - foreach $constName ('A'..'Z'){ - $possibleConsts{$constName} = 1; - } - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $context = $hr_ans->{correct_value}->{Context}; - my $fans = main::Formula($context, $ans); - my @terms = maketerms($fans->{tree}); - - stoutDebugShowArrayRef("Terms", \@terms); - - my $numCorrectTerms = scalar @terms; - my $student_ans = $hr_ans->{student_ans}; - - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - stoutDebugShowHashRef("ans_hash variable", $ans_hash); - stoutDebugShowVar("isPreview in hash", $ans_hash->{isPreview}); - if (defined $ans_hash->{isPreview}){ - if ( !($ans_hash->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - return $ans_hash; - } - } - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - # since we got here, student's answer is equivalent to the correct one - my $sans = main::Formula($context, $student_ans); - my @sterms = maketerms($sans->{tree}); - - stoutDebugShowArrayRef("Student Terms", \@sterms); - - my $snumTerms = scalar @sterms; - my $numTerms = scalar @terms; - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $fans->TeX; - $ans_hash->{preview_latex_string} = $sans->TeX; - $ans_hash->{preview_text_string} = $sans->string; - #if ($snumTerms != $numTerms){ - # $ans_hash->setKeys( 'ans_message' => "Please make sure that there is one arbitrary constant per term in your answer."); - # $ans_hash->{score} = 0; - # return $ans_hash; - #} - - my %matched; - my %smatched; - - my ($i, $j); - - my $correctTerms = 0; - - my %sConstantsUsed; - my %cConstantsUsed; - - my $diagMessage = ""; - - for ($i = 0; $i < $numTerms; $i++) { - $cTerm = $terms[$i]; - stoutDebugShowVar("Correct term", $cTerm); - #next if $matched{$i}; - - for ($j = 0; $j < $snumTerms; $j++) { - next if $matched{$i}; # to skip the rest of the work when we found the match - next if $smatched{$j}; # to skip the rest of the work when we found the match - $sTerm = $sterms[$j]; - stoutDebugShowVar("Student term", $sTerm); - my $result = undef; - $cTermFormula = main::Formula($context,$cTerm); - # @cFactors = makefactors($cTermFormula->{tree}); - $hr_cVars = $cTermFormula->{variables}; - @cConsts = (); - foreach $vn (keys %{$hr_cVars}){ - if ($vn =~ /C([0-9])+/){ - push (@cConsts, $vn); - } elsif ($possibleConsts{$vn}){ - push (@cConsts, $vn); - } - } - #at this stage @cConsts contains all the arbitrary constants that the correct term had - #there should be only one, but we will not assume that for now - stoutDebugShowHashRef("Correct vars", $hr_cVars); - my $cConstantCount = scalar (@cConsts); - foreach $ac (@cConsts){ - stoutDebugMessage("Found constant: ", $ac); - #$cTermFormulaDiff = $cTermFormula->D($ac); - #stoutDebugMessage("The derivative result: ", $cTermFormula, " -> ", $cTermFormulaDiff); - $cTermFormula = $cTermFormula->substitute($ac=>1); - $cConstantsUsed{$ac}=1; - } - stoutDebugShowArrayRef("Correct term factors",\@cFactors); - - $sTermFormula = main::Formula($context,$sTerm); - @sFactorsPowers = makefactors($sTermFormula->{tree}); - @sFactors = (); - @sPowers = (); - my $k; - for ($k = 0; $k < scalar (@sFactorsPowers); $k+=2){ - push (@sFactors, $sFactorsPowers[$k]); - push (@sPowers, $sFactorsPowers[$k+1]); - } - $hr_sVars = $sTermFormula->{variables}; - @sConsts = (); - foreach $vn (keys %{$hr_sVars}){ - if ($vn =~ /C([0-9])+/){ - push (@sConsts, $vn); - } elsif ($possibleConsts{$vn}){ - push (@sConsts, $vn); - } - } - stoutDebugShowHashRef("Student vars", $hr_sVars); - my $sConstantCount = scalar (@sConsts); - foreach $ac (@sConsts){ - stoutDebugMessage("Found constant: ", $ac); - my $thisConstantMatched = 0; - # this would verify that the arbitrary constant comes in as a separate factor in the term - #$sTermFormulaDiff = $sTermFormula->D($ac); - #stoutDebugMessage("Student derivative result: ", $sTermFormula, " -> ", $sTermFormulaDiff); - foreach $fac (@sFactors){ - next if $thisConstantMatched; - my $res = stoutUtils::matchUpToConstant($context, $ac, $fac); - if (defined $res){ - $thisConstantMatched = 1; - $fac = main::Formula($context,$fac)->substitute($ac=>1)->string; - } - } - # $sTermFormula = $sTermFormula->substitute($ac=>1); - if (!$thisConstantMatched){ - $ans_hash->setKeys( 'ans_message' => "Arbitrary constant $ac does not appear to be properly used in your answer" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - $sConstantsUsed{$ac}=1; - @sTermsFP = (); - for ($k = 0; $k < scalar (@sFactors); $k++){ - push (@sTermsFP, "(".$sFactors[$k].")^(".$sPowers[$k].")"); - } - $sTermFormula = main::Formula($context, join("*",@sTermsFP)); - } - - stoutDebugShowArrayRef("Student term factors w/o powers",\@sFactors); - - stoutDebugMessage("Matching |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - - if ($cConstantCount != 0){ - $result = stoutUtils::matchUpToConstant($context, $cTermFormula->string, $sTermFormula->string, non_trivial=>1); - } else { - $result = undef; - # no constant in the correc term - should be no constant in the student term - $res_hash = $cTermFormula->cmp()->evaluate($sTerm); - if ($res_hash->{score}==1){ - $result = 1; - } - } - - if ($sConstantCount != $cConstantCount) { - stoutDebugMessage("Did not match $cTerm with $sTerm due to constant count mismatch"); - if (defined $result){ - $ans_hash->setKeys( 'ans_message' => "You might want to double-check the use of arbitrary constants in your answer" ); - } - $result = undef; - } - - if (defined $result){ - $matched{$i} = 1; - $smatched{$j} = 1; - $correctTerms++; - stoutDebugMessage("MATCHED |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - } else { - stoutDebugMessage("FAILED to match |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - } - } - } - - $sConstantsCheck = 1; - $cConstantsCheck = 1; - foreach $c (keys %sConstantsUsed){ - if ( !(defined $cConstantsUsed{$c}) ){ - $sConstantsCheck = 0; - } - } - - foreach $c (keys %cConstantsUsed){ - if ( !(defined $sConstantsUsed{$c}) ){ - $cConstantsCheck = 0; - } - } - - if ( ($correctTerms==$numTerms) && ($numTerms == $snumTerms) ){ - if ($sConstantsCheck*$cConstantsCheck == 1){ - $ans_hash->{score} = 1; - return $ans_hash; - } else { - $ans_hash->setKeys( 'ans_message' => "Your answer is close to being correct, but you need to check your use of arbitrary constants" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - } elsif ($snumTerms < $numTerms) { - $ans_hash->setKeys( 'ans_message' => "Does your answer contain all the terms? Also please make sure there is only one arbitrary constant per term" ); - $ans_hash->{score} = 0; - return $ans_hash; - } elsif ($snumTerms > $numTerms) { - $ans_hash->setKeys( 'ans_message' => "Does your answer contain any unnecessary terms? Also please make sure there is only one arbitrary constant per term" ); - $ans_hash->{score} = 0; - return $ans_hash; - } else { - $ans_hash->{score} = 0; - return $ans_hash; - } - - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - # stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Linear General Solutuion", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - #stoutDebugShowVar("Expression", $me->string); - #stoutDebugShowVar("BOP", $bop); - #stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - #stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -sub makefactors { - my $me = shift; - - my $bop = $me->{bop}; - - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - my $uop = $me->{uop}; - if (defined $uop){ - $uop =~ s/^\s+//; - $uop =~ s/\s+$//; - } else { - $uop = ""; - } - - #stoutDebugShowVar("Expression", $me->string); - #stoutDebugShowVar("BOP", $bop); - #stoutDebugShowVar("UOP", $uop); - #stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - #stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - #stoutDebugShowVar("OP", $me->{op}->string) if defined $me->{op}; - - if ( ($bop eq "*") ) { - return ( makefactors($me->{lop}), makefactors($me->{rop}) ); - } elsif ( ( $bop eq "^") || ($bop eq "**") ) { - # the factor has exponent! - my $base = $me->{lop}->string; - my $exponent = $me->{rop}->string; - return ($base, $exponent); - } elsif ( ($bop eq "" ) && ($uop eq 'u-') ) { - # there is no binary operation - could have unitary - # need to figure out what to do here - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } else { - # the factor has no exponent - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } -} - - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - my $strFormula = $self->{Formula}; - return main::Formula($context, $strFormula)->TeX(); -} - -package stoutGenericEquation; -@stoutGenericEquation::ISA =qw(Value); - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - my $result = $class->getSides($string_formula); - - if ($result->{error}){ - warn "This: |$string_formula| does not look like an equation"; - } - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - LHS => $result->{lhs}, - RHS => $result->{rhs}, - }; - bless $self, $class; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $isPreview = 0; - - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $ans_tex = $hr_ans->{correct_value}->TeX; - my $clhs = $hr_ans->{correct_value}->{LHS}; - my $crhs = $hr_ans->{correct_value}->{RHS}; - - my $context = $hr_ans->{correct_value}->{Context}; - - my $student_ans = $hr_ans->{student_ans}; - - my $sresult = $self->getSides($student_ans); - stoutDebugShowHashRef("Student result", $sresult); - - if ($sresult->{error}){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like an equation" ); - return $hr_ans; - } - - my $slhs = $sresult->{lhs}; - my $srhs = $sresult->{rhs}; - - my $ans_hash = main::Formula($context, $clhs)->cmp()->evaluate($slhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the left hand side of your equation. ".$err ); - return $ans_hash; - } - } - - $ans_hash = main::Formula($context, $crhs)->cmp()->evaluate($srhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the right hand side of your equation. ".$err ); - return $ans_hash; - } - } - - my $sans_string = $slhs .'='.$srhs; - my $sans_tex = main::Formula($context, $slhs)->TeX .'='. main::Formula($context, $srhs)->TeX; - - $ans_hash = $hr_ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $ans_tex; - $ans_hash->{preview_latex_string} = $sans_tex; - $ans_hash->{preview_text_string} = $sans_string; - - - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - my $match = stoutUtils::matchUpToConstant($context, "($clhs)-($crhs)","($slhs)-($srhs)", non_trivial => 1 ); - - if ( defined $match ){ - $ans_hash->{score} = 1; - return $ans_hash; - } - - # my $f_clhs = main::Formula($clhs); - # my $f_crhs = main::Formula($crhs); - - # $res_l = $f_clhs->cmp()->evaluate($slhs); - # $res_r = $f_crhs->cmp()->evaluate($srhs); - - # if ( ($res_l->{score} == 1) && ($res_r->{score} = 1) ){ - # $hr_ans->{score} = 1; - # return $hr_ans; - # } - - # $res_l = $f_clhs->cmp()->evaluate($srhs); - # $res_r = $f_crhs->cmp()->evaluate($slhs); - - # if ( ($res_l->{score} == 1) && ($res_r->{score} = 1) ){ - # $hr_ans->{score} = 1; - # return $hr_ans; - # } - - - $ans_hash->{score} = 0; - - return $ans_hash; -} - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Generic Equation", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub hasEquality{ - my $self = shift; - my $equation = shift; - - $equation = "" unless (defined $equation); - - my $lhs = ""; - my $rhs = ""; - - if ($equation=~/(.+)=(.+)/){ - # we have equality sign in the formula - $lhs = $1; - $rhs = $2; - return { - lhs => $lhs, - rhs => $rhs, - error => 0, - }; - } else { - # there is no equality sign, need to complain - return { - lhs => undef, - rhs => undef, - error => 1, - }; - } -} - -sub getSides{ - my $self = shift; - my $equation = shift; - - stoutDebugShowVar("Equation string", $equation); - - my $result = $self->hasEquality($equation); - - stoutDebugShowHashRef("Result hash", $result); - - if ( !($result->{error}) ){ - # check to see if there are more equality signs in the mix - $lhs_result = $self->hasEquality($result->{lhs}); - $rhs_result = $self->hasEquality($result->{rhs}); - stoutDebugShowHashRef("LHS Result hash", $lhs_result); - stoutDebugShowHashRef("RHS Result hash", $rhs_result); - - if ( ( !($lhs_result->{error}) ) || ( !($rhs_result->{error}) ) ){ - # no error in either means there are - # equality signs in that 'side' - # that is bad! - return { - lhs => undef, - rhs => undef, - error => 1, - }; - } else { - # we've got no more equality signs - # everything is good - return { - lhs => $result->{lhs}, - rhs => $result->{rhs}, - error => 0, - }; - } - } - - return { - lhs => undef, - rhs => undef, - error => 1, - }; - -} - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; - -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - - my $lhsFormula = main::Formula($context, $self->{LHS}); - my $rhsFormula = main::Formula($context, $self->{RHS}); - - my $res = $lhsFormula->TeX() . '=' . $rhsFormula->TeX(); - - return $res; -} - -package stoutSlopeInterceptEquation; -@stoutSlopeInterceptEquation::ISA = qw(stoutGenericEquation); - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - my $context = main::Context()->copy(); - - my $result = $class->getSides($string_formula); - - if ($result->{error}){ - warn "This: |$string_formula| does not look like an equation"; - } - - my $lhs = $result->{lhs}; - $lhs =~ s/^\s+//; - $lhs =~ s/\s+$//; - - my $rhs = $result->{rhs}; - $rhs =~ s/^\s+//; - $rhs =~ s/\s+$//; - - if ($lhs ne "y"){ - warn "This |$string_formula| should have y as its left hand side. Got |$lhs|."; - } - - my $fans = main::Formula($context, $rhs); - my @terms = maketerms($fans->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - warn "This |$string_formula| should have at most 2 terms as its right hand side. Got |$rhs|."; - } - - my $const_term = 0; - my $slope_term = 0; - my $error_in_slope_term = 0; - - for my $tt (@terms){ - my $f_tt = main::Formula($context, $tt); - if ($f_tt->isConstant){ - $const_term++; - }else{ - my $f_m = $f_tt->substitute('x'=>1); - my $f_mm = main::Formula($context, "$f_m * x"); - if ($f_mm == $f_tt){ - $slope_term++; - }else{ - $error_in_slope_term++; - } - } - } - - if ( ($const_term > 1) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it contains two constant terms on its left hand side. Got |$rhs|."; - } - - if ( ($slope_term > 1) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it contains two terms with x on its left hand side. Got |$rhs|."; - } - - if ( ($error_in_slope_term > 0) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it does not seem to contain a simplified slope term. Got |$rhs|."; - } - - my $self = { - Formula => $string_formula, - Context => $context, - LHS => $result->{lhs}, - RHS => $result->{rhs}, - }; - bless $self, $class; - -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $isPreview = 0; - - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $ans_tex = $hr_ans->{correct_value}->TeX; - my $clhs = $hr_ans->{correct_value}->{LHS}; - my $crhs = $hr_ans->{correct_value}->{RHS}; - - my $context = $hr_ans->{correct_value}->{Context}; - - my $student_ans = $hr_ans->{student_ans}; - - my $sresult = $self->getSides($student_ans); - stoutDebugShowHashRef("Student result", $sresult); - - if ($sresult->{error}){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like an equation" ); - return $hr_ans; - } - - my $slhs = $sresult->{lhs}; - my $srhs = $sresult->{rhs}; - - my $ans_hash = main::Formula($context, $clhs)->cmp()->evaluate($slhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the left hand side of your equation. ".$err ); - return $ans_hash; - } - } - - $ans_hash = main::Formula($context, $crhs)->cmp()->evaluate($srhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the right hand side of your equation. ".$err ); - return $ans_hash; - } - } - - my $sans_string = $slhs .'='.$srhs; - my $sans_tex = main::Formula($context, $slhs)->TeX .'='. main::Formula($context, $srhs)->TeX; - - $ans_hash = $hr_ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $ans_tex; - $ans_hash->{preview_latex_string} = $sans_tex; - $ans_hash->{preview_text_string} = $sans_string; - $ans_hash->{score} = 0; # just in case - - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - # now we check if student answer looks like slope intercept equation - - $slhs =~ s/^\s+//; - $slhs =~ s/\s+$//; - - $srhs =~ s/^\s+//; - $srhs =~ s/\s+$//; - - if ($slhs ne "y"){ - $ans_hash->setKeys( 'ans_message' => "Your answer should contain y as the left hand side of the equation" ); - return $ans_hash; - } - - my $fans = main::Formula($context, $srhs); - my @terms = maketerms($fans->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 2 terms as its right hand side." ); - return $ans_hash; - } - - my $const_term = 0; - my $slope_term = 0; - my $error_in_slope_term = 0; - - for my $tt (@terms){ - my $f_tt = main::Formula($context, $tt); - if ($f_tt->isConstant){ - $const_term++; - }else{ - my $f_m = $f_tt->substitute('x'=>1); - my $f_mm = main::Formula($context, "$f_m * x"); - if ($f_mm == $f_tt){ - $slope_term++; - }else{ - $error_in_slope_term++; - } - } - } - - if ( ($const_term > 1) ){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 1 constant term in its right hand side." ); - return $ans_hash; - } - - if ( ($slope_term > 1) ){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 1 term with x in its right hand side." ); - return $ans_hash; - } - - if ( ($error_in_slope_term > 0) ){ - $ans_hash->setKeys( 'ans_message' => "It looks like the term with x in your answer could be simplified. " ); - return $ans_hash; - } - - # if we're at this stage, the equation should look like slope-intercept equation. - # we now do a simple check of the equation - - my $match = stoutUtils::matchUpToConstant($context, "($clhs)-($crhs)","($slhs)-($srhs)", non_trivial => 0 ); - - if ( defined $match ){ - $ans_hash->{score} = 1; - return $ans_hash; - } - - $ans_hash->{score} = 0; # again to be sure - - return $ans_hash; -} - -package stoutCircleStandardEquation; -@stoutCircleStandardEquation::ISA = qw(stoutGenericEquation); - - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - my $context = main::Context()->copy(); - - my $result = $class->getSides($string_formula); - - if ($result->{error}){ - warn "This: |$string_formula| does not look like an equation"; - } - - my $lhs = $result->{lhs}; - $lhs =~ s/^\s+//; - $lhs =~ s/\s+$//; - - my $rhs = $result->{rhs}; - $rhs =~ s/^\s+//; - $rhs =~ s/\s+$//; - - my $rhs_formula = main::Formula($context, $rhs); - if ( !($rhs_formula->isConstant) ){ - warn "This: |$string_formula| should have radius (constant) as its right hand side."; - } - - my $lhs_formula = main::Formula($context, $lhs); - my @terms = maketerms($lhs_formula->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - warn "This |$string_formula| should have at most 2 terms as its left hand side. Got |$lhs|."; - } - - my $self = { - Formula => $string_formula, - Context => $context, - LHS => $result->{lhs}, - RHS => $result->{rhs}, - }; - bless $self, $class; - -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $isPreview = 0; - - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $ans_tex = $hr_ans->{correct_value}->TeX; - my $clhs = $hr_ans->{correct_value}->{LHS}; - my $crhs = $hr_ans->{correct_value}->{RHS}; - - my $context = $hr_ans->{correct_value}->{Context}; - - my $student_ans = $hr_ans->{student_ans}; - - my $sresult = $self->getSides($student_ans); - stoutDebugShowHashRef("Student result", $sresult); - - if ($sresult->{error}){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like an equation" ); - return $hr_ans; - } - - my $slhs = $sresult->{lhs}; - my $srhs = $sresult->{rhs}; - - my $ans_hash = main::Formula($context, $clhs)->cmp()->evaluate($slhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the left hand side of your equation. ".$err ); - return $ans_hash; - } - } - - $ans_hash = main::Formula($context, $crhs)->cmp()->evaluate($srhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the right hand side of your equation. ".$err ); - return $ans_hash; - } - } - - my $sans_string = $slhs .'='.$srhs; - my $sans_tex = main::Formula($context, $slhs)->TeX .'='. main::Formula($context, $srhs)->TeX; - - $ans_hash = $hr_ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $ans_tex; - $ans_hash->{preview_latex_string} = $sans_tex; - $ans_hash->{preview_text_string} = $sans_string; - $ans_hash->{score} = 0; # just in case - - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - # now we check if student answer looks like equation of circle in standard form - - $slhs =~ s/^\s+//; - $slhs =~ s/\s+$//; - - $srhs =~ s/^\s+//; - $srhs =~ s/\s+$//; - - my $srhs_formula = main::Formula($context, $srhs); - if ( !($srhs_formula->isConstant) ){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have radius as its right hand side"); - return $ans_hash; - } - - my $slhs_formula = main::Formula($context, $slhs); - my @terms = maketerms($slhs_formula->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 2 terms as its left hand side"); - return $ans_hash; - } - - my $score_rhs = 0; - my $score_lhs = 0; - - my $crhs_formula = main::Formula($context, $crhs); - if ($crhs_formula == $srhs_formula){ - $score_rhs = 1; - } - - my $lhs_hash = main::SumOfTermsFormula($clhs)->cmp(factored_terms=>1)->evaluate($slhs); - - $score_lhs = $lhs_hash->{score}; - - $ans_hash->{score} = ($score_rhs + $score_lhs)/2; - - return $ans_hash; -} - diff --git a/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGradersLine.pl b/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGradersLine.pl deleted file mode 100644 index 89a8c3a19e..0000000000 --- a/OpenProblemLibrary/macros/UW-Stout/stoutSpecialGradersLine.pl +++ /dev/null @@ -1,1460 +0,0 @@ -=head1 NAME - -stoutSpecialGradersLine.pl - -=head1 SYNOPSIS - -TODO - -=head1 DESCRIPTION - -TODO - -=head1 AUTHOR - -Alex Basyrov, basyrova@uwstout.edu - -=cut - - -loadMacros( -"MathObjects.pl", -"stoutUtils.pl", -); - -# the formula that is a sum of terms, the terms are compared individually -sub SumOfTermsFormula{ - my $string_formula = shift; - my $ref = new stoutSumOfTermsFormula($string_formula); - return $ref; -} - -# put arbitrary constants into the context -sub PutArbitraryConstants{ - my $count = shift; - $count = 9 unless defined $count; - foreach $param (1..$count){ - Context()->variables->add("C$param" => 'Real'); - } -} - -# put indeterminant parameters into the context -sub PutIndeterminantParameters{ - my $count = shift; - $count = 25 unless defined $count; - $count--; - my @names = ('A'..'Z'); - my $i; - foreach $i (0..$count){ - my $param = $names[$i]; - Context()->variables->add($param => 'Real'); - } -} - -# the formula that represent a general solution to linear high order diff equation -sub LinearGeneralSolution{ - my $string_formula = shift; - my $ref = new stoutLinearGeneralSolution($string_formula); - return $ref; -} - - -sub GenericEquation{ - my $string_formula = shift; - my $ref = new stoutGenericEquation($string_formula); - return $ref; -} - -sub SlopeInterceptEquation{ - my $string_formula = shift; - my $ref = new stoutSlopeInterceptEquation($string_formula); - return $ref; -} - -sub CircleStandardEquation{ - my $string_formula = shift; - my $ref = new stoutCircleStandardEquation($string_formula); - return $ref; -} - -package stoutSumOfTermsFormula; - - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - }; - bless $self, $class; -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $context = $self->{Context}; - - stoutDebugMessage("Context inherited by formula:".main::pretty_print($context->{flags})); - - my $new_formula = main::Formula($context, $original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - $options{factored_terms} = 0 unless defined $options{factored_terms}; - - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $context = $hr_ans->{correct_value}->{Context}; - my $fans = main::Formula($context, $ans); - my @terms = maketerms($fans->{tree}); - - stoutDebugShowArrayRef("Terms", \@terms); - - my $numCorrectTerms = scalar @terms; - my $student_ans = $hr_ans->{student_ans}; - - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - stoutDebugShowHashRef("ans_hash variable", $ans_hash); - stoutDebugShowVar("isPreview in hash", $ans_hash->{isPreview}); - my $isPreview = 0; - if (defined $ans_hash->{isPreview}){ - if ( !($ans_hash->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - return $ans_hash; - } - } - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - # see if we have to go on - my $can_proceed = 0; - if ( $ans_hash->{score} == 1) { - # the student answer is algebraically equivalent to the correct answer - # can look into the structure of student answer - $can_proceed = 1; - } - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - if ( $can_proceed ) { - # since we got here, student's answer is equivalent to the correct one - my $sans = main::Formula($context, $student_ans); - my @sterms = maketerms($sans->{tree}); - - stoutDebugShowArrayRef("Student Terms", \@sterms); - - my $snumTerms = scalar @sterms; - my $numTerms = scalar @terms; - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $fans->TeX; - $ans_hash->{preview_latex_string} = $sans->TeX; - $ans_hash->{preview_text_string} = $sans->string; - if ($snumTerms != $numTerms){ - $ans_hash->setKeys( 'ans_message' => "Your answer is algebraically equivalent to the correct one, but it is not in the correct form. Make sure that you simplify the answer and that the answer is in the requested form" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - - my %matched; - - my ($i, $j); - - my $correctTerms = 0; - - for ($i = 0; $i < $numTerms; $i++) { - $cTerm = $terms[$i]; - - next if $matched{$i}; - - for ($j = 0; $j < $snumTerms; $j++) { - $sTerm = $sterms[$j]; - my $result = undef; - if ($options{factored_terms}){ - $cTermFactored = new stoutFactoredFormula($cTerm); - $ans_hash_term = $cTermFactored->cmp()->evaluate($sTerm); - $result = $ans_hash_term->{score}; - } else { - $result = stoutUtils::matchUpToConstant($context, $cTerm, $sTerm); - } - if ((defined $result) && ($result==1)){ - $matched{$i} = 1; - $correctTerms++; - } else { - # factor is close to correct one, but it differes by a non-trivial constant - } - } - } - - if ($correctTerms==$numTerms){ - $ans_hash->{score} = 1; - return $ans_hash; - } else { - $ans_hash->setKeys( 'ans_message' => "Your answer is algebraically equivalent to the correct one, but it is not in the correct form. Make sure that you simplify the answer and that the answer is in the requested form" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - } - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Sum of Terms Formula", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - my $strFormula = $self->{Formula}; - return main::Formula($context, $strFormula)->TeX(); -} - - - -package stoutLinearGeneralSolution; -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - }; - bless $self, $class; -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - - -sub reduce{ - my $self = shift; - my $original_formula = $self->{Formula}; - my $context = $self->{Context}; - - stoutDebugMessage("Context inherited by formula:".main::pretty_print($context->{flags})); - - my $new_formula = main::Formula($context, $original_formula)->reduce; - $self->{Formula} = $new_formula->string; - return $self; -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - my $isPreview = 0; - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - - my $constName; - my %possibleConsts; - foreach $constName ('A'..'Z'){ - $possibleConsts{$constName} = 1; - } - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $context = $hr_ans->{correct_value}->{Context}; - my $fans = main::Formula($context, $ans); - my @terms = maketerms($fans->{tree}); - - stoutDebugShowArrayRef("Terms", \@terms); - - my $numCorrectTerms = scalar @terms; - my $student_ans = $hr_ans->{student_ans}; - - # the following verifies that student answer would parse - # could use - # my $test = Parser::Formula($student_ans); - # if (defined $test) { #proceed with the use of $student_ans } - # but this method produces answer hash with any syntax errors - # being highlighted - my $ans_hash = main::Formula($context, $ans)->cmp()->evaluate($student_ans); - stoutDebugShowHashRef("ans_hash variable", $ans_hash); - stoutDebugShowVar("isPreview in hash", $ans_hash->{isPreview}); - if (defined $ans_hash->{isPreview}){ - if ( !($ans_hash->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - return $ans_hash; - } - } - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - - # since we got here, student's answer is equivalent to the correct one - my $sans = main::Formula($context, $student_ans); - my @sterms = maketerms($sans->{tree}); - - stoutDebugShowArrayRef("Student Terms", \@sterms); - - my $snumTerms = scalar @sterms; - my $numTerms = scalar @terms; - - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $fans->TeX; - $ans_hash->{preview_latex_string} = $sans->TeX; - $ans_hash->{preview_text_string} = $sans->string; - #if ($snumTerms != $numTerms){ - # $ans_hash->setKeys( 'ans_message' => "Please make sure that there is one arbitrary constant per term in your answer."); - # $ans_hash->{score} = 0; - # return $ans_hash; - #} - - my %matched; - my %smatched; - - my ($i, $j); - - my $correctTerms = 0; - - my %sConstantsUsed; - my %cConstantsUsed; - - my $diagMessage = ""; - - for ($i = 0; $i < $numTerms; $i++) { - $cTerm = $terms[$i]; - stoutDebugShowVar("Correct term", $cTerm); - #next if $matched{$i}; - - for ($j = 0; $j < $snumTerms; $j++) { - next if $matched{$i}; # to skip the rest of the work when we found the match - next if $smatched{$j}; # to skip the rest of the work when we found the match - $sTerm = $sterms[$j]; - stoutDebugShowVar("Student term", $sTerm); - my $result = undef; - $cTermFormula = main::Formula($context,$cTerm); - # @cFactors = makefactors($cTermFormula->{tree}); - $hr_cVars = $cTermFormula->{variables}; - @cConsts = (); - foreach $vn (keys %{$hr_cVars}){ - if ($vn =~ /C([0-9])+/){ - push (@cConsts, $vn); - } elsif ($possibleConsts{$vn}){ - push (@cConsts, $vn); - } - } - #at this stage @cConsts contains all the arbitrary constants that the correct term had - #there should be only one, but we will not assume that for now - stoutDebugShowHashRef("Correct vars", $hr_cVars); - my $cConstantCount = scalar (@cConsts); - foreach $ac (@cConsts){ - stoutDebugMessage("Found constant: ", $ac); - #$cTermFormulaDiff = $cTermFormula->D($ac); - #stoutDebugMessage("The derivative result: ", $cTermFormula, " -> ", $cTermFormulaDiff); - $cTermFormula = $cTermFormula->substitute($ac=>1); - $cConstantsUsed{$ac}=1; - } - stoutDebugShowArrayRef("Correct term factors",\@cFactors); - - $sTermFormula = main::Formula($context,$sTerm); - @sFactorsPowers = makefactors($sTermFormula->{tree}); - @sFactors = (); - @sPowers = (); - my $k; - for ($k = 0; $k < scalar (@sFactorsPowers); $k+=2){ - push (@sFactors, $sFactorsPowers[$k]); - push (@sPowers, $sFactorsPowers[$k+1]); - } - $hr_sVars = $sTermFormula->{variables}; - @sConsts = (); - foreach $vn (keys %{$hr_sVars}){ - if ($vn =~ /C([0-9])+/){ - push (@sConsts, $vn); - } elsif ($possibleConsts{$vn}){ - push (@sConsts, $vn); - } - } - stoutDebugShowHashRef("Student vars", $hr_sVars); - my $sConstantCount = scalar (@sConsts); - foreach $ac (@sConsts){ - stoutDebugMessage("Found constant: ", $ac); - my $thisConstantMatched = 0; - # this would verify that the arbitrary constant comes in as a separate factor in the term - #$sTermFormulaDiff = $sTermFormula->D($ac); - #stoutDebugMessage("Student derivative result: ", $sTermFormula, " -> ", $sTermFormulaDiff); - foreach $fac (@sFactors){ - next if $thisConstantMatched; - my $res = stoutUtils::matchUpToConstant($context, $ac, $fac); - if (defined $res){ - $thisConstantMatched = 1; - $fac = main::Formula($context,$fac)->substitute($ac=>1)->string; - } - } - # $sTermFormula = $sTermFormula->substitute($ac=>1); - if (!$thisConstantMatched){ - $ans_hash->setKeys( 'ans_message' => "Arbitrary constant $ac does not appear to be properly used in your answer" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - $sConstantsUsed{$ac}=1; - @sTermsFP = (); - for ($k = 0; $k < scalar (@sFactors); $k++){ - push (@sTermsFP, "(".$sFactors[$k].")^(".$sPowers[$k].")"); - } - $sTermFormula = main::Formula($context, join("*",@sTermsFP)); - } - - stoutDebugShowArrayRef("Student term factors w/o powers",\@sFactors); - - stoutDebugMessage("Matching |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - - if ($cConstantCount != 0){ - $result = stoutUtils::matchUpToConstant($context, $cTermFormula->string, $sTermFormula->string, non_trivial=>1); - } else { - $result = undef; - # no constant in the correc term - should be no constant in the student term - $res_hash = $cTermFormula->cmp()->evaluate($sTerm); - if ($res_hash->{score}==1){ - $result = 1; - } - } - - if ($sConstantCount != $cConstantCount) { - stoutDebugMessage("Did not match $cTerm with $sTerm due to constant count mismatch"); - if (defined $result){ - $ans_hash->setKeys( 'ans_message' => "You might want to double-check the use of arbitrary constants in your answer" ); - } - $result = undef; - } - - if (defined $result){ - $matched{$i} = 1; - $smatched{$j} = 1; - $correctTerms++; - stoutDebugMessage("MATCHED |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - } else { - stoutDebugMessage("FAILED to match |". $cTermFormula->string ."| with |".$sTermFormula->string."|"); - } - } - } - - $sConstantsCheck = 1; - $cConstantsCheck = 1; - foreach $c (keys %sConstantsUsed){ - if ( !(defined $cConstantsUsed{$c}) ){ - $sConstantsCheck = 0; - } - } - - foreach $c (keys %cConstantsUsed){ - if ( !(defined $sConstantsUsed{$c}) ){ - $cConstantsCheck = 0; - } - } - - if ( ($correctTerms==$numTerms) && ($numTerms == $snumTerms) ){ - if ($sConstantsCheck*$cConstantsCheck == 1){ - $ans_hash->{score} = 1; - return $ans_hash; - } else { - $ans_hash->setKeys( 'ans_message' => "Your answer is close to being correct, but you need to check your use of arbitrary constants" ); - $ans_hash->{score} = 0; - return $ans_hash; - } - } elsif ($snumTerms < $numTerms) { - $ans_hash->setKeys( 'ans_message' => "Does your answer contain all the terms? Also please make sure there is only one arbitrary constant per term" ); - $ans_hash->{score} = 0; - return $ans_hash; - } elsif ($snumTerms > $numTerms) { - $ans_hash->setKeys( 'ans_message' => "Does your answer contain any unnecessary terms? Also please make sure there is only one arbitrary constant per term" ); - $ans_hash->{score} = 0; - return $ans_hash; - } else { - $ans_hash->{score} = 0; - return $ans_hash; - } - - - $hr_ans = $ans_hash; # copy the hash from above - return $hr_ans; -} - - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - # stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Linear General Solutuion", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - #stoutDebugShowVar("Expression", $me->string); - #stoutDebugShowVar("BOP", $bop); - #stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - #stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -sub makefactors { - my $me = shift; - - my $bop = $me->{bop}; - - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - my $uop = $me->{uop}; - if (defined $uop){ - $uop =~ s/^\s+//; - $uop =~ s/\s+$//; - } else { - $uop = ""; - } - - #stoutDebugShowVar("Expression", $me->string); - #stoutDebugShowVar("BOP", $bop); - #stoutDebugShowVar("UOP", $uop); - #stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - #stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - #stoutDebugShowVar("OP", $me->{op}->string) if defined $me->{op}; - - if ( ($bop eq "*") ) { - return ( makefactors($me->{lop}), makefactors($me->{rop}) ); - } elsif ( ( $bop eq "^") || ($bop eq "**") ) { - # the factor has exponent! - my $base = $me->{lop}->string; - my $exponent = $me->{rop}->string; - return ($base, $exponent); - } elsif ( ($bop eq "" ) && ($uop eq 'u-') ) { - # there is no binary operation - could have unitary - # need to figure out what to do here - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } else { - # the factor has no exponent - my $base = $me->string; - my $exponent = 1; - return ($base, $exponent); - } -} - - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - my $strFormula = $self->{Formula}; - return main::Formula($context, $strFormula)->TeX(); -} - -package stoutGenericEquation; - - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - - my $result = $class->getSides($string_formula); - - if ($result->{error}){ - warn "This: |$string_formula| does not look like an equation"; - } - - my $context = main::Context()->copy(); - my $self = { - Formula => $string_formula, - Context => $context, - LHS => $result->{lhs}, - RHS => $result->{rhs}, - }; - bless $self, $class; -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $isPreview = 0; - - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $ans_tex = $hr_ans->{correct_value}->TeX; - my $clhs = $hr_ans->{correct_value}->{LHS}; - my $crhs = $hr_ans->{correct_value}->{RHS}; - - my $context = $hr_ans->{correct_value}->{Context}; - - my $student_ans = $hr_ans->{student_ans}; - - my $sresult = $self->getSides($student_ans); - stoutDebugShowHashRef("Student result", $sresult); - - if ($sresult->{error}){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like an equation" ); - return $hr_ans; - } - - my $slhs = $sresult->{lhs}; - my $srhs = $sresult->{rhs}; - - my $ans_hash = main::Formula($context, $clhs)->cmp()->evaluate($slhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the left hand side of your equation. ".$err ); - return $ans_hash; - } - } - - $ans_hash = main::Formula($context, $crhs)->cmp()->evaluate($srhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the right hand side of your equation. ".$err ); - return $ans_hash; - } - } - - my $sans_string = $slhs .'='.$srhs; - my $sans_tex = main::Formula($context, $slhs)->TeX .'='. main::Formula($context, $srhs)->TeX; - - $ans_hash = $hr_ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $ans_tex; - $ans_hash->{preview_latex_string} = $sans_tex; - $ans_hash->{preview_text_string} = $sans_string; - - - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - my $match = stoutUtils::matchUpToConstant($context, "($clhs)-($crhs)","($slhs)-($srhs)", non_trivial => 1 ); - - if ( defined $match ){ - $ans_hash->{score} = 1; - return $ans_hash; - } - - # my $f_clhs = main::Formula($clhs); - # my $f_crhs = main::Formula($crhs); - - # $res_l = $f_clhs->cmp()->evaluate($slhs); - # $res_r = $f_crhs->cmp()->evaluate($srhs); - - # if ( ($res_l->{score} == 1) && ($res_r->{score} = 1) ){ - # $hr_ans->{score} = 1; - # return $hr_ans; - # } - - # $res_l = $f_clhs->cmp()->evaluate($srhs); - # $res_r = $f_crhs->cmp()->evaluate($slhs); - - # if ( ($res_l->{score} == 1) && ($res_r->{score} = 1) ){ - # $hr_ans->{score} = 1; - # return $hr_ans; - # } - - - $ans_hash->{score} = 0; - - return $ans_hash; -} - -# need this to correctly detect 'preview answers' vs. 'submit/check answers' mode -sub getPG { - my $self = shift; - (main::PG_restricted_eval(shift))[0]; -# eval ('package main; '.shift); # faster -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Generic Equation", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - -sub hasEquality{ - my $self = shift; - my $equation = shift; - - $equation = "" unless (defined $equation); - - my $lhs = ""; - my $rhs = ""; - - if ($equation=~/(.+)=(.+)/){ - # we have equality sign in the formula - $lhs = $1; - $rhs = $2; - return { - lhs => $lhs, - rhs => $rhs, - error => 0, - }; - } else { - # there is no equality sign, need to complain - return { - lhs => undef, - rhs => undef, - error => 1, - }; - } -} - -sub getSides{ - my $self = shift; - my $equation = shift; - - stoutDebugShowVar("Equation string", $equation); - - my $result = $self->hasEquality($equation); - - stoutDebugShowHashRef("Result hash", $result); - - if ( !($result->{error}) ){ - # check to see if there are more equality signs in the mix - $lhs_result = $self->hasEquality($result->{lhs}); - $rhs_result = $self->hasEquality($result->{rhs}); - stoutDebugShowHashRef("LHS Result hash", $lhs_result); - stoutDebugShowHashRef("RHS Result hash", $rhs_result); - - if ( ( !($lhs_result->{error}) ) || ( !($rhs_result->{error}) ) ){ - # no error in either means there are - # equality signs in that 'side' - # that is bad! - return { - lhs => undef, - rhs => undef, - error => 1, - }; - } else { - # we've got no more equality signs - # everything is good - return { - lhs => $result->{lhs}, - rhs => $result->{rhs}, - error => 0, - }; - } - } - - return { - lhs => undef, - rhs => undef, - error => 1, - }; - -} - -# the following is almost useless so far, as the overload does not work here -sub stringify { - my $self = shift; - return $self->string; - -} - -# the following is almost useless so far, as the overload does not work here -sub string{ - my $self = shift; - return $self->{Formula}; -} - -# the following is almost useless so far, as the overload does not work here -sub TeX{ - my $self = shift; - my $context = $self->{Context}; - - my $lhsFormula = main::Formula($context, $self->{LHS}); - my $rhsFormula = main::Formula($context, $self->{RHS}); - - my $res = $lhsFormula->TeX() . '=' . $rhsFormula->TeX(); - - return $res; -} - -package stoutSlopeInterceptEquation; -@stoutSlopeInterceptEquation::ISA = qw(stoutGenericEquation); - -# the easier debug block -# it uses functions from stoutUtils package, -# but allows local setting of debugging constant -our $stoutDebug = 0; - -sub stoutDebugMessage{ - stoutUtils::stoutDebugMessage(@_) if $stoutDebug; -} - -sub stoutDebugShowVar{ - stoutUtils::stoutDebugShowVar(@_) if $stoutDebug; -} - -sub stoutDebugShowArrayRef{ - stoutUtils::stoutDebugShowArrayRef(@_) if $stoutDebug; -} - -sub stoutDebugShowHashRef{ - stoutUtils::stoutDebugShowHashRef(@_) if $stoutDebug; -} - -sub new { - my $class = shift; - my $string_formula = shift; - my %options = @_; - my $context = main::Context()->copy(); - - my $result = $class->getSides($string_formula); - - if ($result->{error}){ - warn "This: |$string_formula| does not look like an equation"; - } - - my $lhs = $result->{lhs}; - $lhs =~ s/^\s+//; - $lhs =~ s/\s+$//; - - my $rhs = $result->{rhs}; - $rhs =~ s/^\s+//; - $rhs =~ s/\s+$//; - - if ($lhs ne "y"){ - warn "This |$string_formula| should have y as its left hand side. Got |$lhs|."; - } - - my $fans = main::Formula($context, $rhs); - my @terms = maketerms($fans->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - warn "This |$string_formula| should have at most 2 terms as its right hand side. Got |$rhs|."; - } - - my $const_term = 0; - my $slope_term = 0; - my $error_in_slope_term = 0; - - for my $tt (@terms){ - my $f_tt = main::Formula($context, $tt); - if ($f_tt->isConstant){ - $const_term++; - }else{ - my $f_m = $f_tt->substitute('x'=>1); - my $f_mm = main::Formula($context, "$f_m * x"); - if ($f_mm == $f_tt){ - $slope_term++; - }else{ - $error_in_slope_term++; - } - } - } - - if ( ($const_term > 1) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it contains two constant terms on its left hand side. Got |$rhs|."; - } - - if ( ($slope_term > 1) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it contains two terms with x on its left hand side. Got |$rhs|."; - } - - if ( ($error_in_slope_term > 0) ){ - warn "This |$string_formula| does not look like slope-intercept formula: it does not seem to contain a simplified slope term. Got |$rhs|."; - } - - my $self = { - Formula => $string_formula, - Context => $context, - LHS => $result->{lhs}, - RHS => $result->{rhs}, - }; - bless $self, $class; - -} - -sub context{ - my $self = shift; - return $self->{Context}; -} - -sub cmp { - my $self = shift; - my %options = @_; - - # debug: - stoutDebugShowHashRef("Options in cmp", \%options); - - my $ans = new AnswerEvaluator; - # $ans->{debug} = 1; - $ans->ans_hash( - type => "Slope Intercept Equation", - correct_ans => $self->{Formula}, - correct_value => $self, - ); - $ans->install_evaluator('erase'); - - # the following passes the %options we got here to the answer evaluator - $ans->install_evaluator(sub { - my $ans = shift; - my $inputs = $self->getPG('${main::inputs_ref}'); - if ( defined $inputs->{action} ){ - $ans->{isPreview} = $inputs->{previewAnswers} || ($inputs->{action} =~ m/^Preview/); - } else { - $ans->{isPreview} = $inputs->{previewAnswers}; - } - $ans->{correct_value}->theEvaluator($ans, @_); - }, - %options); - return $ans; -} - - - -sub maketerms { - my $me = shift; - my %options = @_; - $options{sign} = 1 unless defined $options{sign}; - - my $bop = $me->{bop}; - if (defined $bop){ - $bop =~ s/^\s+//; - $bop =~ s/\s+$//; - } else { - $bop = ""; - } - - stoutDebugShowVar("Expression", $me->string); - stoutDebugShowVar("BOP", $bop); - stoutDebugShowVar("LOP", $me->{lop}->string) if defined $me->{lop}; - stoutDebugShowVar("ROP", $me->{rop}->string) if defined $me->{rop}; - - if ($bop eq "+") { - return ( maketerms($me->{lop}), maketerms($me->{rop}) ); - } elsif ($bop eq "-"){ - return ( maketerms($me->{lop}), maketerms($me->{rop}, sign => -1) ); - } else { - if ( $options{sign} == 1){ - return $me->string; - } elsif ($options{sign} == -1){ - return "-1*(".$me->string.")"; - } else { - stoutDebugMessage("something is wrong..."); - } - } -} - -sub theEvaluator { - my $self = shift; - my $hr_ans = shift; - stoutDebugShowHashRef("hr_ans variable", $hr_ans); - stoutDebugShowVar("Preview", $hr_ans->{isPreview}); - - # store the options we got from the call to cmp() method - my %options = @_; - - # debug - stoutDebugShowHashRef("Options", \%options); - - my $isPreview = 0; - - if (defined $hr_ans->{isPreview}){ - if ( !($hr_ans->{isPreview} eq "")){ - $isPreview = 1; - } - } - stoutDebugShowVar('isPreview',$isPreview); - - my $ans = $hr_ans->{correct_value}->{Formula}; - my $ans_tex = $hr_ans->{correct_value}->TeX; - my $clhs = $hr_ans->{correct_value}->{LHS}; - my $crhs = $hr_ans->{correct_value}->{RHS}; - - my $context = $hr_ans->{correct_value}->{Context}; - - my $student_ans = $hr_ans->{student_ans}; - - my $sresult = $self->getSides($student_ans); - stoutDebugShowHashRef("Student result", $sresult); - - if ($sresult->{error}){ - $hr_ans->{score} = 0; - $hr_ans->setKeys( 'ans_message' => "Your answer does not look like an equation" ); - return $hr_ans; - } - - my $slhs = $sresult->{lhs}; - my $srhs = $sresult->{rhs}; - - my $ans_hash = main::Formula($context, $clhs)->cmp()->evaluate($slhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the left hand side of your equation. ".$err ); - return $ans_hash; - } - } - - $ans_hash = main::Formula($context, $crhs)->cmp()->evaluate($srhs); - # need to do syntax error check before going furhter - if ( defined $ans_hash->{error_message} ){ - # error message was set - is that enough? - # TODO: might need to check $hr_ans->{error_flag} - # warn ::pretty_print($hr_ans); - if ( !($ans_hash->{error_message} eq "") ){ - $err = $ans_hash->{error_message}; - $ans_hash->setKeys( 'ans_message' => "Error in the right hand side of your equation. ".$err ); - return $ans_hash; - } - } - - my $sans_string = $slhs .'='.$srhs; - my $sans_tex = main::Formula($context, $slhs)->TeX .'='. main::Formula($context, $srhs)->TeX; - - $ans_hash = $hr_ans; - $ans_hash->{student_ans} = $student_ans; - $ans_hash->{original_student_ans} = $student_ans; - $ans_hash->{correct_ans} = $ans; - $ans_hash->{correct_ans_latex_string} = $ans_tex; - $ans_hash->{preview_latex_string} = $sans_tex; - $ans_hash->{preview_text_string} = $sans_string; - $ans_hash->{score} = 0; # just in case - - # we should be safe to use student answer, since it should have passed parser - - # if it is a preview, we don't need to waste time - # doing full answer check; return what we've got now - if ($isPreview){ - return $ans_hash; - } - - # now we check if student answer looks like slope intercept equation - - $slhs =~ s/^\s+//; - $slhs =~ s/\s+$//; - - $srhs =~ s/^\s+//; - $srhs =~ s/\s+$//; - - if ($slhs ne "y"){ - $ans_hash->setKeys( 'ans_message' => "Your answer should contain y as the left hand side of the equation" ); - return $ans_hash; - } - - my $fans = main::Formula($context, $srhs); - my @terms = maketerms($fans->{tree}); - - my $count = scalar @terms; - - if ($count > 2){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 2 terms as its right hand side." ); - return $ans_hash; - } - - my $const_term = 0; - my $slope_term = 0; - my $error_in_slope_term = 0; - - for my $tt (@terms){ - my $f_tt = main::Formula($context, $tt); - if ($f_tt->isConstant){ - $const_term++; - }else{ - my $f_m = $f_tt->substitute('x'=>1); - my $f_mm = main::Formula($context, "$f_m * x"); - if ($f_mm == $f_tt){ - $slope_term++; - }else{ - $error_in_slope_term++; - } - } - } - - if ( ($const_term > 1) ){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 1 constant term in its right hand side." ); - return $ans_hash; - } - - if ( ($slope_term > 1) ){ - $ans_hash->setKeys( 'ans_message' => "Your answer should have at most 1 term with x in its right hand side." ); - return $ans_hash; - } - - if ( ($error_in_slope_term > 0) ){ - $ans_hash->setKeys( 'ans_message' => "It looks like the term with x in your answer could be simplified. " ); - return $ans_hash; - } - - # if we're at this stage, the equation should look like slope-intercept equation. - # we now do a simple check of the equation - - my $match = stoutUtils::matchUpToConstant($context, "($clhs)-($crhs)","($slhs)-($srhs)", non_trivial => 0 ); - - if ( defined $match ){ - $ans_hash->{score} = 1; - return $ans_hash; - } - - $ans_hash->{score} = 0; # again to be sure - - return $ans_hash; -} - -package stoutCircleStandardEquation; -@stoutCircleStandardEquation::ISA = qw(stoutGenericEquation); diff --git a/OpenProblemLibrary/macros/UW-Stout/stoutUtils.pl b/OpenProblemLibrary/macros/UW-Stout/stoutUtils.pl deleted file mode 100644 index 6de65b7372..0000000000 --- a/OpenProblemLibrary/macros/UW-Stout/stoutUtils.pl +++ /dev/null @@ -1,203 +0,0 @@ -=head1 NAME - -stoutUtils.pl - -=head1 SYNOPSIS - -TODO - -=head1 DESCRIPTION - -TODO - -=head1 AUTHOR - -Alex Basyrov, basyrova@uwstout.edu - -=cut - - -loadMacros( -"MathObjects.pl", -); - -package stoutUtils; -=head1 NAME - -Stout Utils - -=head1 DESCRIPTION - -These are sub's that are used in various other packages - -=over - -=cut - -=item $stoutDebug - -Set - $stoutDebug = 0 -to supress debug messages; -otherwise debug messages will be shown. - -=cut - -our $stoutDebug = 1; - -=item stoutDebugMessage -Usage: - stoutDebugMessage('text 1', 'text 2', ...); - -=cut - -sub stoutDebugMessage{ - # return undef if (!$stoutDebug); - main::WARN_MESSAGE(@_); - return undef; -} - -=item stoutDebugShowVar - -Usage: - stoutDebugShowVar("description", $variable); - -=cut - -sub stoutDebugShowVar{ - # return undef if (!$stoutDebug); - my $name = shift; - my $value = shift; - $value = "" unless defined $value; - main::WARN_MESSAGE($name.": |$value|"); - return undef; -} - -=item stoutDebugShowArrayRef - -Usage: - stoutDebugShowArrayRef("description", $arrayReference); - -=cut - -sub stoutDebugShowArrayRef{ - # return undef if (!$stoutDebug); - my $name = shift; - my $value = shift; - my $list_of_values = join("|,\n|",@{$value}); - $list_of_values = "" unless defined $list_of_values; - main::WARN_MESSAGE($name.": |".$list_of_values."|\n"); - return undef; -} - -=item stoutDebugShowHashRef - -Usage: - stoutDebugShowHashRef("description",$hashReference); - -This will show hash keyes and their values without special treatment of references, arrays, or hashes stored within the $hashReference. - -=cut - -sub stoutDebugShowHashRef{ - # return undef if (!$stoutDebug); - my $name = shift; - my $value = shift; - my @keys = keys %{$value}; - my @msg; - - push (@msg, $name.":"); - - for my $key (@keys){ - my $val = $value->{$key}; - $val = "" unless defined $val; - push(@msg, $key ." => |$val|"); - } - main::WARN_MESSAGE(@msg); - return undef; -} - -=item matchUpToConstant - -Usage: - matchUpToConstant($context, "Correct answer", "Student answer"); - matchUpToConstant($context, "Correct answer", "Student answer", non_trivial=>1); - -We take Context, correct answer, and student answer, and see if they are equal -- up to a negative sign. - -Add non_trivial=>1 at the end to match up to a non-trivial constant, but this will use numeric matching: it takes longer, uses notrivial things, and the result will be approximate. - -=cut - -sub matchUpToConstant{ - # we take Context, correct answer, and student answer, and see if they are equal -- up to a constant - # add non_trivial=>1 at the end to use more sophisticated matching - my $context = shift; - my $correct = shift; - my $student = shift; - # the rest of the parameters are options, they go into this hash - my %options = @_; - - # set the default values for options if they are not already specified - $options{non_trivial} = 0 unless defined $options{non_trivial}; - - #let's be optimistic and hope for an easy match: - my $f_student = main::Formula($context,$student); - my $fp_correct = main::Formula($context,$correct); # positive version - my $fn_correct = main::Formula($context,"-($correct)"); #negative version - - if ($f_student == $fp_correct){ - return 1; - } elsif ($f_student == $fn_correct){ - return -1; - } - - #if we're here, the easy match did not work for some reason - #we have to use adaptive paramters to find non-trivial constant - #that makes student answer equal to the correct answer - #this is an expensive process that sometimes goes wrong - #FOR NOW WE DO NOT USE IT, unless we ask for it explicitly! - - if ($options{non_trivial}){ - # they should explicitly ask for this method of matching! - my $a_context = $context->copy(); - $a_context->flags->set(no_parameters=>0); - $pname='Az'; - $a_context->variables->add($pname=>'Parameter'); - my $c0 = main::Formula($a_context,$pname); - my $fstudent = main::Formula($a_context,$student); - my $fcorrect = main::Formula($a_context,"$c0 * ($correct)"); - - #if any of the answers are zero, we have to handle them carefully - if ($fstudent->isZero) { - if ($fcorrect->isZero){ - # student answer is correctly zero - return 1; - } else { - # student answer is zero when it should not be - # the match failed, we return undef - return undef; - } - } - if ( $fcorrect == $fstudent ) { - $parameterValue = $a_context->variables->value($pname); - # this value is numerically generated. - # even if it should be an integer, - # it could be some close enough decimal - return $parameterValue; - } - } - - # matching failed -- we return undef - return undef; -} - -=back - -=head1 AUTHOR - -Written by Alex Basyrov, basyrova@uwstout.edu - -=cut - -1; diff --git a/OpenProblemLibrary/macros/UniSiegen/logicMacros.pl b/OpenProblemLibrary/macros/UniSiegen/logicMacros.pl deleted file mode 100644 index c89cd6172a..0000000000 --- a/OpenProblemLibrary/macros/UniSiegen/logicMacros.pl +++ /dev/null @@ -1,5 +0,0 @@ -sub negate { - return "\\sim"; - #return "\\lnot"; -}; - diff --git a/OpenProblemLibrary/macros/Union/1-README.txt b/OpenProblemLibrary/macros/Union/1-README.txt deleted file mode 100755 index ea4804fa23..0000000000 --- a/OpenProblemLibrary/macros/Union/1-README.txt +++ /dev/null @@ -1,230 +0,0 @@ -UNION-DEVELOPED MACRO FILES: - -These are macro support files developed at Union College for use with -our problems. There are a number of new answer checkers, some vector -routines, and some miscellaneous utilities. Those whose names end in -Answer.pl are answer checkers, those ending in Grader.pl are graders, -and those ending in Utils.pl are collections of utilities. Most of -the answer checkers are obsolete in light of the new MathObjects -library (developed at Union) that is now part of the main WeBWorK2 -distribution, but are included here for compatibility with old problem -files that use them. As we update the problems to use MathObjects, -these older macro files will be removed. - -The file PGunion.pl includes most of the general utilities, but the -problems load the specific answer checkers and graders as needed. - -The unionProblem.pl file implements a table that puts a border around -the main text of the problem and a background color that helps -separate the the problem from the rest of the text on the screen. -(See the comments in that file for details). This is no longer -needed, as the global.conf file can be used to put in such borders -automatically. The file no longer includes the borders by default -(since that meant that problems would get TWO borders). - -All the files below contain comments that explain how to use them, so -check the individual files for details. - - - GENERAL UTILITIES: - - PGunion.pl Simply includes a number of the other files - that make most of the union utilities available. - - PGstandard.pl Just contains a bunch of loadMacros() calls - in order to make it easier to load the - standard ones without having to remember - them all. - - - unionMacros.pl Some extra macros like $BCENTER/$ECENTER and - so on that implement some additional HTML features. - - unionUtils.pl Some miscellaneous sometimes-useful functions. - - unionProblem.pl Implements the table around the main text of - the problem. (Obsolete.) - - unionMessages.pl Defines variables that hold standard - messages like how to enter "infinity", or - other such remarks. This way they can be - guaranteed to be common from file to file, - and can be over-ridden on a course-by-course - basis. (Using PGcourse.pl or a copy of this - file in the course's templates/macros directory.) - - - - IMAGE ROUTINES: - - unionImage.pl Implements an easier interface to including - images in your problems. - - altPlotMacros.pl Some alternatives to the standard plot macros - that allows more sophisticated functions to - be called during graph creation. - (Obsolete: the plot macros now use MathObjects) - - ANSWER CHECKERS: - - Better versions of most of these are available as part of the new - MathObjects library that is included with WeBWorK2. See the - documentation at http://wwebwork.maa.org/ and the various - MathObjects extensions in pg/macros for more information. - - - compositionAnswer.pl An answer checker for function compositions. - (Obsolete: use pg/macros/answerComposition.pl) - - diffquotientAnswer.pl An answer checker for difference quotients - where the student must reduce sufficiently - to remove the delta-x in the denominator. - (Obsolete: use pg/macros/parserDifferenceQuotient.pl) - - infiniteAnswer.pl An answer checker that handles numbers and - a variety of forms of infinity, including - -INF, which is not possible with the - current string/number checker. - (Obsolete: use MathObjects Infinity object) - - integerAnswer.pl An answer checker that requires the student - to enter an integer. - (Obsolete: use MathObjects LimitedNumeric - context with NoDecimals) - - intervalAnswer.pl An answer checker for intervals (including - ones that have infinite endpoints). - Currently it doesn't do unions, but this - could be added. - (Obsolete: use MathObjects Interval and - Union objects) - - lineAnswer.pl An answer checker for parametric lines - given by a point and direction vector. The - checker recognizes the line using any point - on the line, and any parallel vector. - (Obsolete: use pg/macros/parserParametricLine.pl) - - listAnswer.pl Answer checkers for general lists of - items. This is used as the basis of other - checkers, such as the vector and point - checkers. - (Obsolete: use MathObjects List object) - - parallelAnswer.pl An answer checker for a vector parallel to - a given vector, with an option of - specifying that the vector must be in the - same direcion (not the opposite direction). - (Obsolete: use MathObjects Vector object - with parallel=>1 option). - - planeAnswer.pl An answer checker for the implicit form of - a plane. It will recognize the plane no - matter how it is entered, as long as it is - linear. - (Obsolete: use pg/macros/parserImplicitPlane.pl) - - pointAnswer.pl An answer checker that recognizes a point - or vector, either numeric or with equations - as its coordinates. - (Obsolete: use MathObjects Point object) - - unionAnswer.pl Answer checker for unions of intervals. - (Obsolete: use MathObjects Union object) - - unorderedAnswer.pl A collection of routines that let the - student enter answers in any order. For - example, you can ask "f(x) is defined - except for x = ___ and x = ___" and the - student can enter the answers in either - order. - (pg/macros/parserMultiAnswer.pl may be a - better alternative in some cases) - - variableAnswer.pl An answer checker for ordered or unordered - lists of comma- or space-separated words, - optionally enclosed in parentheses. I used - it to ask for the names of the variables - used in multivariable functions, for - example. - (Obsolete: use MathObjects List object in - String context) - - vectorAnswer.pl Alternative names for the routines in - pointAnswer.pl, which handles both points - and vectors. - (Obsolete: use MathObjects Vector object) - - answerUtils.pl General utility routines used by the - answer checkers above. - (MathObjects provide better alternatives.) - - - GRADERS: - - weightedGrader.pl A grader that allows you to assign weights - to the various answers in a question, rather - than having them all equally weighted. It - also provides the ability to have one answer - give credit for one or more other answer. - - - MATCH-LIST ROUTINES: - - imageChoice.pl A match-list subclass that matches images - against the questions. The images are - displayed in a table where you can specify - the number of columns, the sizes of the - images, and so on. - - choiceUtils.pl Contains an alternate print_q method that - uses tables to create the list of questions, - so that they will format better when a long - line wraps to the next line. - - - VECTOR UTILITIES: - - Most of these are no longer needed, as their functionality has - been incorporated into the MathObjects library. - - - unionVectors.pl Implements a vector and point class that - lets you manipulate and display points and - vectors conveniently. If used as a module, - it can overload the operators to make this - work even better, however that is not - required. - (Obsolete: this became the MathObjects - Vector object) - - vectorUtils.pl Some utilities for working with vectors. - (Obslete: use pg/macros/parserVectorUtils.pl) - - - MISCELLANEOUS: - - unionInclude.pl Implements a simple means of including one - PG file into another (so that if several - problems use a common portion, it can be - kept in a single file that is included into - the individual problem files). It also - implements a mechanism for creating problem - sets where the problems are presented in - random order. - - piecewiseFunctions.pl Provides a means of producing piece-wise - defined functions that work for both TeX - and HTML modes from the same code. - (Obsolete: use pg/macros/contextPiecewiseFunction.pl) - - courseHeaders.pl A file that implements a standard header for - both paper and screen headers, so only one - file is needed for both. This was not - really meant for distribution, but you might - find it useful anyway. Note that it uses - Union-specific naming conventions for - courses, so can not be used directly by - other universities. - - diff --git a/OpenProblemLibrary/macros/Union/PGunion.pl b/OpenProblemLibrary/macros/Union/PGunion.pl deleted file mode 100755 index 7cd768fef0..0000000000 --- a/OpenProblemLibrary/macros/Union/PGunion.pl +++ /dev/null @@ -1,15 +0,0 @@ -# -# Load most of the interesting code developed at Union. -# - -loadMacros( - "unionMacros.pl", - "unionUtils.pl", - "unionProblem.pl", - "unionImage.pl", - "unionLists.pl", - "unionTables.pl", - "unionMessages.pl", -); - -1; diff --git a/OpenProblemLibrary/macros/Union/answerUtils.pl b/OpenProblemLibrary/macros/Union/answerUtils.pl deleted file mode 100755 index 84ce6e3824..0000000000 --- a/OpenProblemLibrary/macros/Union/answerUtils.pl +++ /dev/null @@ -1,101 +0,0 @@ -########################################################################## -# -# Utility routines for answer checkers -# -######################################################################### - -sub _answerUtils_init {}; # don't load again - -loadMacros("unionUtils.pl"); - -# -# Evaluate an answer checker with a given student answer. -# (works with old- and new-style answer checkers) -# -sub evaluateAnswer { - my ($ans_evaluator,$student_answer,$skipblanks) = @_; - return if (!$ans_evaluator); - return if ($skipblanks && trimString($student_answer) eq ''); - clearEvaluator($ans_evaluator); - if (ref($ans_evaluator) eq 'AnswerEvaluator') { - $ans_evaluator->{rh_ans}{ans_label} = "" unless defined($ans_evaluator->{rh_ans}{ans_label}); - return($ans_evaluator->evaluate($student_answer)); - } elsif (ref($ans_evaluator) eq 'CODE' ) { - return(&$ans_evaluator($student_answer)); - } else { - warn "There is a problem using the answer evaluator"; - } -} - -# -# Call an answer evaluator (works for new- and old-style checkers) -# -sub isCorrectAnswer { - my $cmp = shift; my $correct = $cmp->{rh_ans}->{correct_ans}; - my $hash = evaluateAnswer($cmp,@_,1); - $cmp->{rh_ans}->{correct_ans} = $correct; - return(0) unless defined($hash); - return($hash->{score} == 1); -} - -# -# Clear the error condition for an answer evaluator -# -sub clearEvaluator { - my $hash = hashFor(shift); - return(0) unless defined($hash); - $hash->setKeys( - ans_message => '', - preview_text_string => '', - preview_latex_string => '', - original_student_ans => '', - student_ans => '', - ); - $hash->score(0); -} - -# -# Get the answer hash for a given evaluator -# -sub hashFor { - my ($ans_evaluator) = @_; - if (ref($ans_evaluator) eq 'AnswerEvaluator') {return $ans_evaluator->rh_ans} - elsif (ref($ans_evaluator) eq 'CODE' ) {return $ans_evaluator} - else {warn "There is a problem using the answer evaluator"} -} - -# -# Make error messages returned by answer checkers prettier -# -sub IndentError { - my $error = trimString(shift); - my $n = shift; $n = 4 unless defined($n); - my $indent = ''; $indent .= ' ' while ($n--); - $error =~ s/ (There is a syntax error)/\n$1/g; - $error =~ s/ Your/\nYour/g; - $error =~ s/\n */\n$indent/g; - return $indent.$error -} - -# -# Return the string or a blank string (if it was not defined) -# -sub StringOrBlank { - my $s = shift; my $default = shift; - $default = '' unless defined($default); - $s = $default unless defined($s); - return $s; -} - -# -# Check for preview mode -# -sub isPreviewMode { - # for WW1 - return ($inputs_ref->{action} =~ m/^Preview/) - if (defined($inputs_ref) && defined($inputs_ref->{action})); - # for WW2 - return defined($inputs_ref) && defined($inputs_ref->{previewAnswers}); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/choiceUtils.pl b/OpenProblemLibrary/macros/Union/choiceUtils.pl deleted file mode 100755 index 7b76e398a3..0000000000 --- a/OpenProblemLibrary/macros/Union/choiceUtils.pl +++ /dev/null @@ -1,76 +0,0 @@ -sub _choiceUtils_init {}; # don't reload this file - -# -# A replacement for std_print_q that uses tables to align the questions, so -# that if a question wraps, it is properly indented. -# - -sub alt_print_q { - my $self = shift; - my @questions = @_; - my $length = $self->{ans_rule_len}; - my $sep = $self->{separation}; $sep = 0 unless defined($sep); - my $valign = $self->{valign}; $valign = "TOP" unless defined($valign); - my $i=1; my $quest; - - my $out = ""; - if ($main::displayMode =~ m/^HTML/) { - $out = "\n

    \n\n"; - foreach $quest (@questions) { - $out .= '\n"; - } - $out .= "
    '. ans_rule($length). - " ".$i++.". $quest
    \n"; - } elsif ($main::displayMode eq 'Latex2HTML') { - $out = "\\par\n\\begin{rawhtml}" . - "\\end{rawhtml}\n"; - foreach $quest (@questions) { - $out .= '\begin{rawhtml}'. - ''. - '\\end{rawhtml}'."\n"; - } - $out .= "\\begin{rawhtml}
    \end{rawhtml}'.ans_rule($length).'\begin{rawhtml} '.$i++.'. \\end{rawhtml} '.$quest. - '\begin{rawhtml}
    \n\\end{rawhtml}"; - } elsif ($main::displayMode eq 'TeX') { - $out = "\n\\par\\begin{enumerate}\n\\advance\\leftskip by 2em"; - foreach $quest (@questions) - {$out .= "\\item[".ans_rule($length).' '.$i++.".] $quest\n"} - $out .= "\\end{enumerate}\n"; - } else { - $out = "Error: alt_print_q: Unknown displayMode: $main::displayMode."; - } - $out; -} - -sub alt_print_a { - my $self = shift; - my (@array) = @_; - my $sep = $self->{separation} || 0; - my $valign = $self->{valign} || "TOP"; - my $i = 0; - - my $out= MODES( - TeX => "\\begin{enumerate}\n", - Latex2HTML => qq{\\begin{rawhtml}\\end{rawhtml}\n}, - HTML => qq{
    }, - ); - my $elem; - foreach $elem (@array) { - my $c = $main::ALPHABET[$i]; - $out .= MODES( - TeX => "\\item[$c.] $elem\n", - Latex2HTML => qq{\\begin{rawhtml}} - . qq{\\end{rawhtml}\n}, - HTML => qq{\n}, - ); - $i++; - } - $out .= MODES( - TeX => "\\end{enumerate}\n", - Latex2HTML => "\\begin{rawhtml}
    $c. \\end{rawhtml}$elem\\begin{rawhtml}
    $c. $elem
    \\end{rawhtml}\n", - HTML => "\n", - ); - $out; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/courseHeaders.pl b/OpenProblemLibrary/macros/Union/courseHeaders.pl deleted file mode 100755 index dbd34ed6e4..0000000000 --- a/OpenProblemLibrary/macros/Union/courseHeaders.pl +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/local/bin/perl - -sub _courseHeaders_init { -# -# This is the hint to display in screen headers. You can change this -# in the specific header .pg file for a problem set, provided you do so -# after the call to loadMacros that loads this file. -# - -$HINT = - "Give 4 or 5 significant digits when entering decimal numbers. ". - "For most problems, you can enter elementary expressions such as ". - "${BTT}2^3${ETT} instead of ${BTT}8${ETT}, ${BTT}sin(3pi/2)${ETT} ". - "instead of ${BTT}-1${ETT}, ${BTT}e^(ln(2))${ETT} instead of ${BTT}2${ETT}, ". - "${BTT}(2+tan(3))*(4-sin(5))^6-7/8${ETT} instead of ${BTT}27620.3413${ETT}, ". - "etc. Consult WeBWorK's ". - htmlLink('http://webwork.math.rochester.edu/webwork_system_html/docs/docs/pglanguage/availablefunctions.html','list of functions'). - " or review the problems in the orientation homework set for more ". - "information on the functions and values that WeBWorK understands."; - -}; - -###################################################################### -# -# This file implements a standard format for the screen and paper -# headers. The same file works for both, so there is no need to -# make duplicate files. See courseHeader() below for details. -# - -loadMacros( - "PG.pl", - "PGbasicmacros.pl", - "PGunion.pl" -); - -# -# Get the course name from the course ID -# -$course = $courseName; -$course =~ s/\d\d(FA|WI|SP|SU)-//; # Union-sepcific -$course =~ s/-.*//; # Union-specific -$course = protect_underbar($course); # just in case - -# -# Set some variables to use in headers -# -$dateTime = $formatedDueDate; -$sectionNumber = protect_underbar($sectionNumber); -$setNumber = protect_underbar($setNumber); - -# -# Evaluate the string as in PG, then trim white space from the ends -# -sub EV_trim { - my $string = EV2(@_); - $string =~ s/(^\s+|\s+$)//g; - return $string; -} - -# -# Get a number sign in all modes -# -$NUMBER = MODES(TeX => '\#', HTML => '#'); - -# -# couseHeader creates the header based on values passed to it. -# It works for both screen and paper headers, so you only need -# one header file. The options you can pass it are: -# -# topic => '...' a short string to be used in -# "this is an assignment on ..." -# -# preposition => 'on' -# what word to use before the topic -# -# bookinfo => '...' the section of the book to refer to for more -# information. -# -# bookprobs => ['...','...',] -# a list of strings giving problems from -# various sections of the book. As many -# strings can be supplied as you want. -# They will be placed on separate lines in -# the screen header, but on one line in the PDF -# file. If left empty, the word "none" will be -# supplied automatically. If set to undef, -# no book problems will be given. -# -# moreprobs => ['...','...',] -# a list of strings giving problems from -# various sections of the book. (See bookprobs -# for details of how this works.) -# -# text => '...' This is additional text to be inserted -# after the book assignment. It can explain -# more about the problems, give hints, etc. -# -# showhint => 0 or 1 Indicates whether the message about typing -# webwork answers should be shown in the screen -# header. It defaults to 1 (yes). -# You can change the variable $HINT to be -# a different hint, if you want. Be sure -# to do this AFTER the loadMacros call. -# -# -sub courseHeader { - my %ops = ( - topic => "", - preposition => "on", - bookinfo => "", - bookprobs => [], - moreprobs => undef, - text => "", - showhint => 1, - - topic_text => 'This is a collection of WeBWorK problems ', - bookinfo_text => 'Relevant information can be found in %s of your text.', - bookprobs_text => 'Book problems to do as part of this assignment:', - moreprobs_text => 'Additional problems to do with this assignment:', - @_ - ); - - if ($displayMode =~ m/^HTML/ || $displayMode eq "Latex2HTML") { -# TEXT($BBLOCKQUOTE); - TEXT(EV_trim($ops{topic_text}.$ops{preposition}.' '.$ops{topic}).".\n") if $ops{topic}; - TEXT(sprintf($ops{bookinfo_text},$ops{bookinfo})) if $ops{bookinfo}; - TEXT($BR,$PAR,"\n\n"); - if (ref($ops{bookprobs}) eq "ARRAY") { - TEXT($ops{bookprobs_text}."\n"); - if (scalar(@{$ops{bookprobs}}) == 0) {TEXT("none.\n\n")} else { - TEXT( - $BBLOCKQUOTE, - EV_trim(join($BR."\n",@{$ops{bookprobs}})), - $EBLOCKQUOTE,"\n" - ); - } - TEXT($PAR,"\n\n"); - } - if (ref($ops{moreprobs}) eq "ARRAY") { - TEXT($ops{moreprobs_text}."\n"); - if (scalar(@{$ops{moreprobs}}) == 0) {TEXT("none.\n\n")} else { - TEXT( - $BBLOCKQUOTE, - EV_trim(join($BR."\n",@{$ops{moreprobs}})), - $EBLOCKQUOTE,"\n" - ); - } - } - TEXT($HR,$PAR,EV_trim($ops{text})) if $ops{text}; - TEXT($HR,$PAR."\n\n",$BSMALL.$HINT.$ESMALL."\n\n",$PAR) - if ($ops{showhint}); -# TEXT($EBLOCKQUOTE); - } elsif ($displayMode eq "TeX") { - TEXT( - $BEGIN_ONE_COLUMN, - '{\parindent=0pt \parskip=4pt',"\n", - '{\large\bf '.$studentName.'\hfill '.$course.' '.$sectionNumber.'}', - '\break\null\hfill '. - "WeBWorK assignment $setNumber due $dateTime".'\par'."\n", - ); - TEXT(EV_trim($ops{topic_text}.$ops{preposition}.' '.$ops{topic}).".\n") if $ops{topic}; - TEXT(sprintf($ops{bookinfo_text},$ops{bookinfo})) if $ops{bookinfo}; - TEXT('\par',"\n\n"); - if (ref($ops{bookprobs}) eq "ARRAY") { - TEXT($ops{bookprobs_text}.' '); - if (scalar(@{$ops{bookprobs}}) == 0) {TEXT("none.\\par\n\n")} else { - TEXT( - EV_trim(join("; ",@{$ops{bookprobs}})). - '.\par'."\n\n" - ); - } - } - if (ref($ops{moreprobs}) eq "ARRAY") { - TEXT($ops{moreprobs_text}.' '); - if (scalar(@{$ops{moreprobs}}) == 0) {TEXT("none.\\par\n\n")} else { - TEXT( - EV_trim(join("; ",@{$ops{moreprobs}})). - '.\par'."\n\n" - ); - } - } - TEXT(EV_trim($ops{text}).'\par'."\n\n") if $ops{text}; - TEXT('\par}',$END_ONE_COLUMN); - } else { - warn "courseHeader: Unknown display mode: $displayMode" - } -} - -1; diff --git a/OpenProblemLibrary/macros/Union/imageChoice.pl b/OpenProblemLibrary/macros/Union/imageChoice.pl deleted file mode 100755 index 178f473e16..0000000000 --- a/OpenProblemLibrary/macros/Union/imageChoice.pl +++ /dev/null @@ -1,91 +0,0 @@ -loadMacros( - 'PGchoicemacros.pl', - 'unionUtils.pl', - 'choiceUtils.pl', -); - -sub _imageChoice_init {}; # don't reload this file - -###################################################################### -# -# Create a match list where the answers are images -# -# Usage: $ml = new_image_match_list(options); -# -# where options are those that can be supplied to image_print_a below. -# The answers should be an image name or reference to to a plot object -# (or a reference to a pair of either of these), and they are passed -# to the Image function for processing. See unionUtils.pl for more -# information on how these are handled. -# -sub new_image_match_list { - my $ml = new Match(random(1,2000,1), \&alt_print_q, \&img_print_a); - $ml->{ImageOptions} = [@_]; - $ml; -} - -###################################################################### -# -# A print routine for image matching. This is designed to display -# four graphs per row. More can be included by setting the ImageOptions -# variable in the match list. For example: -# -# $ml->{ImageOptions} = [size => [100,100]] -# -# You can include the following options: -# -# size => [w,h] the width and height of the images -# width => n the width of the images (obsolete) -# height => n the height of the images (obsolete) -# tex_size => n the size for the TeX version of the image -# separation => n the spacing between the images in a row -# vseparation => n the spacing between the images and captions -# link => 0 or 1 1 to make a link to the original image -# columns => n the number of images in each row (defaults to 4) -# border => n the width of the image border -# - -sub img_print_a { - my $self = shift; - $self->{ImageOptions} = [] unless defined($self->{ImageOptions}); - my %options = ( - width => 150, height => 150, tex_size => 200, columns => 4, - separation => 15, vseparation => 5, link => 0, @{$self->{ImageOptions}}); - my ($w,$h,$m) = ($options{width},$options{height},$options{columns}); - ($w,$h) = @{$options{size}} if defined($options{size}); - my ($sep,$vsep) = ($options{separation},$options{vseparation}); - my ($tsize,$link) = ($options{tex_size},$options{link}); - my $border = $options{border}; $border = ($link?2:1) unless defined($border); - my $HTML; my @images = (); my @labels = (); my $i = 0; - my $out; - - $out = BeginTable(tex_spacing => "5pt", tex_border => "5pt"); - while ($image = shift) { - push(@images,Image( - $image, size => [$w,$h], tex_size => $tsize, - link => $link, border => $border - )); - push(@labels,MODES( - TeX => "\\hfil \\textbf{$main::ALPHABET[$i]}", - Latex2HTML=>$bHTML."

    $main::ALPHABET[$i]
    ".$eHTML, - HTML => "
    $main::ALPHABET[$i]
    " - )); - if ((++$i % $m) == 0) { - $out .= TableSpace(2*$vsep,6) if ($i > $m); - $out .= Row([@images], separation => $sep). - TableSpace($vsep,0). - Row([@labels], separation => $sep); - @images = (); @labels = (); - } - } - if (scalar(@images) > 0) { - $out .= TableSpace(2*$vsep) if ($i > $m && $displayMode ne "TeX"); - $out .= Row([@images], separation => $sep). - TableSpace($vsep,0). - Row([@labels], separation => $sep); - } - $out .= EndTable(tex_border => "5pt"); - $out; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/altPlotMacros.pl b/OpenProblemLibrary/macros/Union/obsolete/altPlotMacros.pl deleted file mode 100755 index ae47655386..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/altPlotMacros.pl +++ /dev/null @@ -1,108 +0,0 @@ -sub _altPlotMacros_int {}; # don't reload this file - -# -# An alternative to plot_functions that allows any perl -# expression as the function (it doesn't get parsed by -# the PG function parser, so you can do fancier -# expressions; in particular, things like f(x,y)) -# -# This is just a hack, as it doesn't really parse the -# expression, so might not translate everything properly. -# It will be obsolete when I finish my new expression -# parser, but I needed it to handle traces of multivariable -# functions easier. --- DPVC -# - -sub alt_plot_functions { - my $graph = shift; - my @function_list = @_; - my $error = ""; - $error .= "The first argument to plot_functions must be a graph object" unless ref($graph) =~/WWPlot/; - my $fn; - my @functions=(); - foreach $fn (@function_list) { - - # model: "2.5-x^2 for x in <-1,0> using color:red and weight:2" - if ($fn =~ /^(.+)for\s*(\w+)\s*in\s*([\(\[\<])\s*([\d\.\-]+)\s*,\s*([\d\.\-]+)\s*([\)\]\>])\s*using\s*(.*)$/ ) { - my ($rule,$var, $left_br, $left_end, $right_end, $right_br, $options) = - ($1, $2, $3, $4, $5, $6, $7); - my %options = split( /\s*and\s*|\s*:\s*|\s*,\s*|\s*=\s*|\s+/,$options); - my ($color, $weight); - if ( defined($options{'color'}) ){ - $color = $options{'color'}; #set pen color - } else { - $color = 'default_color'; - } - if ( defined($options{'weight'}) ) { - $weight = $options{'weight'}; # set pen weight (width in pixels) - } else { - $weight = 2; - } - - my $subRef = alt_string_to_sub($rule,$var); - my $funRef = new Fun($subRef,$graph); - $funRef->color($color); - $funRef->weight($weight); - $funRef->domain($left_end , $right_end); - push(@functions,$funRef); - # place open (1,3) or closed (1,3) circle at the endpoints or do nothing <1,3> - if ($left_br eq '[' ) { - $graph->stamps(closed_circle($left_end,&$subRef($left_end),$color) ); - } elsif ($left_br eq '(' ) { - $graph->stamps(open_circle($left_end, &$subRef($left_end), $color) ); - } - if ($right_br eq ']' ) { - $graph->stamps(closed_circle($right_end,&$subRef($right_end),$color) ); - } elsif ($right_br eq ')' ) { - $graph->stamps(open_circle($right_end, &$subRef($right_end), $color) ); - } - - } else { - $error .= "Error in parsing: $fn"; - } - - } - die ("Error in plot_functions:\n\t $error") if $error; - @functions; # return function references unless there is an error. -} - -# -# A cheap way to convert a string to a perl function -# that returns the value of the expression given in the -# string. Since no special parsing is done, you need -# to make sure your function is essentially in perl -# form. -# -sub alt_string_to_sub { - my $expr = my_math_constants(shift); - my $x = shift; - # - # Give the variable a $ and rename it with an leading underscore - # - $expr =~ s/(\b|\d)$x\b/$1(\$_x)/g; - # - # Fix up the expression - # - $expr =~ s!\\frac\{([^\}]*)\}\s*\{([^\}]*)\}!($1)/($2)!g; # fractions - $expr =~ s/\{([^\}]*)\}/($1)/g; # TeX parameters - $expr =~ s/\\//g; # TeX slashes - $expr =~ s/\^/**/g; # change ^ to ** - # - # Do implied multiplication - # - $expr =~ s/\)\s*\(/\)*\(/g; - $expr =~ s/\)\s*([a-zA-Z0-9.])/\)*$1/g; - $expr =~ s/([\d.])(\s\d)/$1*$2/g; - $expr =~ s/([\d.])\s*([a-zA-Z\(])/$1*$2/g; - # - # Fix +-, -+, ++ and -- - # - $expr =~ s/\+\s*([\+-])/$1/g; - $expr =~ s/-\s*\+/-/g; $expr =~ s/-\s*-/+/g; - # - # Make the function - # - PG_restricted_eval "sub {my \$_x = shift; return $expr}"; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/compositionAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/compositionAnswer.pl deleted file mode 100755 index 1fad973813..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/compositionAnswer.pl +++ /dev/null @@ -1,154 +0,0 @@ -loadMacros('answerUtils.pl'); - -sub _compositionAnswer_init {}; # don't reload this file - -###################################################################### -# -# An answer checker that determines if two functions compose -# to form a given function. (For use in problems where you ask -# a student to break a given function into a composition of two -# simpler functions, neither of which is allowed to be the identity -# function.) -# - -###################################################################### -# -# An answer checked to see if f composed with g matches a given function. -# -# Usage: COMPOSITION_ANS(f,g,options) -# -# where f and g are one possible decomposition of the target function -# (these are used to display the "correct" answer, and the composition -# is computed from them) and options are any of the options allowed -# by composition_ans_list below. -# -# This function actually supplies TWO answer checkers, for the two -# previous answer blanks. So be sure to call it immediately after -# the answer blanks have been supplied. (It may be best to use the -# NAMED_COMPOSITION_ANS checker below, which specifies the answer -# blanks explicitly.) -# -# Example: -# -# BEGIN_TEXT -# \(f\circ g = (1+x)^2\) when -# \(f(x)\) = \{ans_rule(20)\} and \(g(x)\) = \{ans_rule(20)\} -# END_TEXT -# COMPOSITION_ANS("x^2","1+x"); -# -# -sub COMPOSITION_ANS { - my $f = shift; my $g = shift; - my $fID = ANS_NUM_TO_NAME($main::ans_rule_count-1); - my $gID = ANS_NUM_TO_NAME($main::ans_rule_count); - my %ans = composition_ans_list($fID=>$f,$gID=>$g,@_); - ANS(values(%ans)); -} - - -###################################################################### -# -# An answer checked to see if f composed with g matches a given function. -# -# Usage: NAMED_COMPOSITION_ANS(fID=>f,gID->g,options) -# -# where fID and gID are the names of the answer rules for the functions -# f and g, and f and g are the answers for the functions. Options are -# any of the options allowed by composition_ans_list below. -# -# This routine allows you to put the f and g answer blanks at any -# location in the problem, and in any order. -# -# Example: -# -# BEGIN_TEXT -# \(g\circ f = (1+x)^2\) when -# \(f(x)\) = \{NAMED_ANS('f',20)\} and \(g(x)\) = \{NAMED_ANS('g',20)\} -# END_TEXT -# NAMED_COMPOSITION_ANS(f => "x^2", g => "1+x"); - -sub NAMED_COMPOSITION_ANS {NAMED_ANS(composition_ans_list(@_))} - - -###################################################################### -# -# This is an internal routine that returns the named answer checkers -# used by COMPOSITION_ANS and NAMED_COMPOSITION_ANS above. -# -# Usage: composition_ans_list(fID=>f,gID=>g,options) -# -# where fID and gID are the names of the answer rules for the functions -# and f and g are the answers for these functions. Options are from -# among: -# -# var => 'x' the name of the variable to use (both -# functions use the same variable -- this -# should probably be improved). -# -# or any parameters that can be passed to fun_cmp. -# -sub composition_ans_list { - my ($fID,$f,$gID,$g,%params) = @_; - my ($i,$ident,$comp,$eval,$field,$fog,$student_ans); - my @IDs = ($fID,$gID); - my @cmp = (composition_cmp($f),composition_cmp($g)); - my @ans = ($fID => $cmp[0], $gID => $cmp[1]); - my $error = 0; - my $var = "x"; $var = $params{'var'} if (defined($params{'var'})); - - # - # Check that the answers exist (otherwise it's our first time through) - # - foreach $i (@IDs) {return(@ans) if (!defined($inputs_ref->{$i}))} - - $ident = fun_cmp($var,%params); - foreach $i (0,1) { - $eval = evaluateAnswer($ident,$inputs_ref->{$IDs[$i]}); - if ($eval->{ans_message} ne "") {$error = 1} - elsif ($eval->{score} == 1) { - $eval->{ans_message} = "The identity function is not allowed"; - $error = 1; $eval->{score} = 0; - } - foreach $field ('ans_message','error_message','preview_latex_string', - 'preview_text_string','student_ans') { - $cmp[$i]->rh_ans->{$field} = $eval->{$field}; $eval->{$field} = ""; - } - } - - if (!$error) { - $fog = $f; $fog =~ s/$var/($g)/g; - $student_ans = $inputs_ref->{$fID}; - $student_ans =~ s/$var/($inputs_ref->{$gID})/g; - if (isCorrectAnswer(fun_cmp($fog,%params),$student_ans)) { - $cmp[0]->rh_ans->{score} = $cmp[1]->rh_ans->{score} = 1; - } - } - return (@ans); -} - - -###################################################################### -# -# Evaluator that always returns correct or always returns incorrect, -# depending on the parameter passed to it. Used by COMPOSITION_ANS -# to produce "dummy" answer checkers for the two parts of the -# composition. -# -sub composition_cmp { - my $score = shift; - my %params = @_; - $params{debug} = 0 unless defined($params{debug}); - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash(type => "composition", correct_ans => $score); - $answerEvaluator->install_evaluator(\&composition_cmp_check,%params); - return $answerEvaluator; -} - -sub composition_cmp_check { - my $ans = shift; - my %params = @_; - return($ans); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/diffquotientAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/diffquotientAnswer.pl deleted file mode 100755 index 21d246d8dd..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/diffquotientAnswer.pl +++ /dev/null @@ -1,119 +0,0 @@ -loadMacros('answerUtils.pl'); - -sub _diffquotientAnswer_init {}; # don't reload this file - -######################################################################### -## -## An answer checker for handling difference quotients. It requires -## that the student simplify the equation (at least far enough to -## cancel the dx in the bottom). -## - - -######################################################################### -# -# Usage: diff_quotient_cmp(ans,options) -# -# where ans is the correct difference quotient, and options are -# from among: -# -# var => 'x' specifies the variable for the difference quotient -# -# limits => [a,b] gives the lower and upper limits of the domain -# to use for checking the student's response. -# -# or any of the parameters that can be passed to fun_cmp(). -# -# This checker checks that the student answer is the correct difference -# and that it doesn't contain dx in the denominator (i.e., they will -# have had to simplify it at least far enough to cancel the dx). -# -# The checker accepts 'dx' or 'H' as the difference in the x variable. -# (This is a bit of a hack, and there is a chance that the student could -# receive odd error messages because it it.) -# -# Example: -# -# BEGIN_TEXT -# Find the simplified difference quotient for \(f(x)=x^2\): -# \{ans_rule(30)\}. -# END_TEXT -# -# ANS(diff_quotient_cmp("2x+dx")); -# -# - -sub diff_quotient_cmp { - my $answer = shift || ''; - my %params = ( - debug => 0, - var => 'x', - limits => [$functLLimitDefault,$functULimitDefault], - @_ - ); - # - # Get the variable and limits - # - my $x = $params{var}; delete $params{var}; - my ($ll,$ul) = @{$params{limits}}; - $params{vars} = [$x,'H']; - $params{limits} = [[$ll,$ul],[$functLLimitDefault,$functULimitDefault]]; - # - # Convert dx to H - # - my $answer_h = $answer; $answer_h =~ s/d$x/H/g; - # - # The answer checker that tests for division by zero - # - my $zero = fun_cmp($answer_h,%params); - $zero->{rh_ans}{evaluation_points} = [[($ul+$ll)/2,0]]; - - my $ans = new AnswerEvaluator; - $ans->{debug} = $params{debug}; - $ans->ans_hash( - type => "difference quotient", - correct_ans => $answer, - cmp => fun_cmp($answer_h,%params), - zero_cmp => $zero, - var => $x, - ); - $ans->install_evaluator(\&diff_quotient_cmp_check,%params); - return $ans; -} - -# -# The guts of the checker -# -sub diff_quotient_cmp_check { - my $ans = shift; - my $x = $ans->{var}; - my $answer = $ans->{student_ans}; $answer =~ s/d$x/H/g; - - # - # Check the answer, and copy the score and messages - # - my $hash = evaluateAnswer($ans->{cmp},$answer); - foreach my $id ('score','student_ans','ans_message','error_message', - 'error_flags','preview_text_string','preview_latex_string') { - $ans->{$id} = $hash->{$id}; - $ans->{$id} =~ s/H/d$x/g if defined($ans->{$id}); - } - - # - # If the function matches, check that it is simplified - # (no division by zero when dx = 0). Otherwise, give an - # appropriate error message. - # - if ($hash->{score} == 1) { - $hash = evaluateAnswer($ans->{zero_cmp},$answer); - if ($hash->{ans_message} =~ m/division by zero/) { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = - "It looks like you didn't finish simplifying your answer\n"; - } - } - - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/infiniteAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/infiniteAnswer.pl deleted file mode 100755 index 2625b8557e..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/infiniteAnswer.pl +++ /dev/null @@ -1,131 +0,0 @@ -loadMacros('unionUtils.pl','answerUtils.pl'); - -sub _infiniteAnswer_init { - - #$INFINITY_WORD = "INF"; - $INFINITY_WORD = "infinity" unless defined($INFINITY_WORD); - - # - # A message that can be inclued (within BEGIN_TEXT and END_TEXT) - # to tell the student how to enter infinity and minus infinity - # - $INFINITY_MESSAGE = - $BITALIC.$BSMALL. - "Use ${LQ}$INFINITY_WORD${RQ} for $LQ\\(\\infty\\)$RQ ". - "and ${LQ}-$INFINITY_WORD${RQ} for $LQ\\(-\\infty\\)$RQ." . - $ESMALL.$EITALIC; - - $DNE_MESSAGE = - $BITALIC.$BSMALL. - "Use ${LQ}DNE${RQ} for ${LQ}Does not exist${RQ}.". - $ESMALL.$EITALIC; - - $INFINITY_MESSAGE = "" if $displayMode eq 'TeX'; - $DNE_MESSAGE = "" if $displayMode eq 'TeX'; - -}; - -########################################################################## -# -# Check for a number or "-INF", "INF", "DNE" or several synonyms. -# -# WeBWorK's std_num_str_cmp doesn't allow for strings like "-INF", so it -# uses the unsatisfying "MINF" instead. This answer checker provides -# for both MINF and -INF to mean negative infinity, and INF or NaN for -# positive infinity, and similarly INFINITY or -INFINITY. It also -# allows for DNE as an answer. The strings can be entered in upper- -# or lower-case and still be recognized. -# -# Usage: infinite_num_cmp(number, options) -# -# where number is a number or one of the words for infinity, and options -# are chosen from -# -# allowDNE => 0 or 1 whether to accept DNE as a valid entry -# (default is 0) -# -# DNEisINF => 0 or 1 whether to treat DNE as INF or as a -# separate word (default is 0) -# -# -sub infinite_num_cmp { - my $infPattern = '^(INF|INFINITY|\+INF|\+INFINITY|NaN)$'; - my $neginfPattern = '^(-INF|-INFINITY|MINF)$'; - my $num = shift; - my %params = (allowDNE => 0, DNEisINF => 0, - infPattern => $infPattern, - neginfPattern => $neginfPattern, @_); - my %numops = @_; - delete($numops{allowDNE}) if (defined($numops{allowDNE})); - delete($numops{DNEisINF}) if (defined($numops{DNEisINF})); - $params{debug} = 0 unless defined($params{debug}); - $num = "-$INFINITY_WORD" if ($num =~ m/$neginfPattern/i); - $num = $INFINITY_WORD if ($num =~ m/$infPattern/i); - if ($params{allowDNE}) { - $params{strings} = ["INF","-INF","+INF","+INFINITY","INFINITY","-INFINITY","DNE"]; - $params{num_cmp} = num_cmp($num, - strings => ["INF","-INF","+INF","MINF","NaN","INFINITY","-INFINITY","+INFINITY","DNE"], - %numops); - } else { - $params{strings} = ["INF","-INF","+INF","INFINITY","-INFINITY","+INFINITY"]; - $params{num_cmp} = num_cmp($num, - strings => ["INF","-INF","+INF","MINF","NaN","INFINITY","-INFINITY","+INFINITY"], - %numops); - } - $params{num_cmp}->install_post_filter('reset'); # to prevent NUM_CMP from putting - # error messages in student_ans - $params{isString} = 0; - foreach my $string (@{$params{strings}}) { - if (uc($string) eq uc($num)) {$params{isString} = 1; last} - } - - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash(correct_ans => $num, type => "infinite number"); - $answerEvaluator->install_evaluator(\&infinite_num_check,%params); - return $answerEvaluator; -} - -# -# The guts of the checker -# -sub infinite_num_check { - my $ans = shift; - my %params = @_; - $ans->{student_ans} = trimString($ans->{student_ans}); - my $answer = $ans->{student_ans}; - my $realAnswer = $ans->{correct_ans}; - my $isString = 0; - $realAnswer = "DNE" - if (uc($answer) eq "DNE" && $params{DNEisINF} && - $realAnswer =~ m/$params{infPattern}/i); - $answer = "-$INFINITY_WORD" if ($answer =~ m/$params{neginfPattern}/i); - $answer = $INFINITY_WORD if ($answer =~ m/$params{infPattern}/i); - if ($params{isString} && uc($answer) eq uc($realAnswer)) { - $ans->score(1); $isString = 1 - } else { - foreach my $string (@{$params{strings}}) { - if (uc($answer) eq uc($string)) {$ans->score(0); $isString = 1} - } - } - if ($isString) { - $ans->{student_ans} = $answer; - $ans->{preview_text_string} = $answer; - $ans->{preview_latex_string} = $answer; - return $ans; - } - # - # transfer the NUM_CMP error message to the answer message - # so we can report it correctly in other checkers - # - my $hash = evaluateAnswer($params{num_cmp},$ans->{student_ans}); - $hash->{ans_message} = trimString($hash->{error_message}) - if ($hash->{ans_message} eq ''); - $hash->{ans_message} = - "Your answer does not seem to be a number or infinity" - if ($hash->{ans_message} =~ m/recognized answer/); - $hash->clear_error('EVAL'); # in case one was left over from std_num_cmp - return $hash; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/integerAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/integerAnswer.pl deleted file mode 100755 index 8148f01cc5..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/integerAnswer.pl +++ /dev/null @@ -1,52 +0,0 @@ -sub _integerAnswer_init { - $INTEGER_TOLERANCE = 1E-14; -}; - -###################################################################### -# -# Integer answer checker. -# -# integer_cmp(ans,options) -# -# where options are those allowed by num_cmp(). -# -# This provided an answer checker that compares against an integer. -# it is really just a shell around num_cmp() that sets the tolerance to -# an absolute tolerance of .5 (so that only the correct integer -# would be correct). It also adds a post-filter to check that -# the student answer actually IS an integer. -# -# Note, the checker is only good to about 12 digits. -# - -sub integer_cmp { - my $cmp = num_cmp(@_,tol => .5); - $cmp->install_post_filter(\&check_integer_answer); - my $x = $cmp->{rh_ans}{correct_ans}; - warn "Professor's answer is too large for integer comparison" - if (abs($x) > (1/$INTEGER_TOLERANCE)/100); - if (abs($x - int($x+$x*$INTEGER_TOLERANCE)) > $x*$INTEGER_TOLERANCE) { - warn "Professor's answer is not an integer"; - } else { - $cmp->{rh_ans}{original_correct_ans} = - $cmp->{rh_ans}{correct_ans} = sprintf("%.0f",$x); - } - return $cmp; -} - -sub check_integer_answer { - my $ans = shift; - if ($ans->{ans_message} eq "" && $ans->{error_message} eq "") { - my $x = $ans->{student_ans}; - if (abs($x - int($x+$x*$INTEGER_TOLERANCE)) > $x*$INTEGER_TOLERANCE) { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = - "Your answer is not an integer"; - } else { - $ans->{student_ans} = sprintf("%.0f",$x); - } - } - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/intervalAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/intervalAnswer.pl deleted file mode 100755 index 352b02a1a9..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/intervalAnswer.pl +++ /dev/null @@ -1,258 +0,0 @@ -loadMacros('infiniteAnswer.pl'); - -sub _intervalAnswer_init {}; # don't reload this file - -###################################################################### -## -## Interval answer checkers. -## -## num_interval_cmp() intervals with numeric or infinite endpoints -## fun_interval_cmp() intervals with functions as endpoints -## -## interval_cmp() provide your own endpoint checkers -## - -###################################################################### -# -# num_interval_cmp(answer,options) -# -# where options are from among: -# -# cmp_params => [list] parameters to be passed to infinite_num_cmp -# for each coordinate -# -# or any of the options to interval_cmp (see below). -# -# This provided an answer checker that compares against an interval -# with numeric or infinite endpoints. -# -# e.g. num_point_cmp("(1,10)"); -# num_point_cmp("(-INF,10)"); -# - -sub num_interval_cmp { - my $ans = shift; - std_interval_cmp($ans, cmp => \&infinite_num_cmp, @_); -} - - -###################################################################### -# -# num_interval_cmp(answer,options) -# -# where options are from among: -# -# cmp_params => [list] parameters to be passed to infinite_num_cmp -# for each coordinate -# -# vars => [list] the variables for the formulas -# -# or any of the options to interval_cmp (see below). -# -# This provided an answer checker that compares against an interval -# with numeric or infinite endpoints. -# -# e.g. fun_point_cmp("(x,2x)"); -# fun_point_cmp("(-t,t+2)", vars => 't'); -# - -sub fun_interval_cmp { - my $ans = shift; - my %params = (cmp_params => [], @_); - if (defined($params{vars})) { - $params{cmp_params} = [vars => $params{vars}, @{$params{cmp_params}}]; - delete $params{vars}; - } - std_interval_cmp($ans, cmp => \&fun_cmp, %params); -} - - -###################################################################### -# -# std_interval_cmp(ans,options) -# -# where ans is the correct answer as a string (e.g. "(1,INF)"), and -# options are from among the following: -# -# cmp => \&cmp a reference to the answer checker to use for each -# endpoint (e.g., \&std_num_cmp, or ~~&std_num_cmp -# in a .pg file) -# -# cmp_params => [...] is a reference to a list of parameters for the -# answer checker (e.g., [vars => 'x']) -# -# or any of the options allowed by interval_cmp (see below). -# -# This returns an answer checker that compares the interval using the -# given answer checker on each endpoint. -# -# e.g. std_interval_cmp("(1,INF)",cmp => ~~&infinite_num_cmp); -# - -sub std_interval_cmp { - my $ans = shift; - my %params = ( - cmp => \&infinite_num_cmp, - cmp_params => [], - debug => 0, - @_ - ); - my ($cmp,$cmp_params) = ($params{cmp},$params{cmp_params}); - delete $params{cmp}; delete $params{cmp_params}; - die "The correct answer doesn't look like an interval" - if ($ans !~ m/^(\[|\()([^,]*),([^,]*)(\]|\))$/); - my ($left,$a,$b,$right) = ($1,$2,$3,$4); - interval_cmp(&{$cmp}($a,@{$cmp_params}),&{$cmp}($b,@{$cmp_params}), - ends => "$left$right", %params); -} - - -########################################################################## -# -# Check if an answer is an interval -# -# Format: -# -# interval_cmp(left_evaluator,right_evaluator, options) -# -# where "left_evaluator" is an answer checker for the left endpoint -# and "right_evaluator" is an answer checker for the right endpoint, -# -# Options can be taken from: -# -# ends => string indicates the type of interval, where -# "string" is one of '()', '(]', '[)', or '[]'. -# -# showHints => 0 or 1 indicates whether to show hints about -# the correctness of the endpoints -# (default is 1). -# -# showOrderHints => 0 or 1 -# indicates whether to show hints about -# the order of the endpoints -# (default is $showHints). -# -# Example: -# -# interval_cmp(std_num_cmp(0),std_num_cmp(1), ends=>'[)'); -# -sub interval_cmp { - my $lendpnt = shift; - my $rendpnt = shift; - my %params = @_; - set_default_options(\%params, - ends => '()', - showHints => 1, - showOrderHints => undef, - debug => 0 - ); - $params{lendpnt} = $lendpnt; - $params{rendpnt} = $rendpnt; - - my $ends = $params{ends}; - my ($ltype,$rtype) = (substr($ends,0,1),substr($ends,-1)); - my ($lhash,$rhash) = (hashFor($lendpnt),hashFor($rendpnt)); - # - # Needed to get preview to work properly - # - my $type = "interval"; - $type .= " (number)" - if ($lhash->{type} =~ m/number/ || $rhash->{type} =~ m/number/); - - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => $ltype . $lhash->{correct_ans} . "," . - $rhash->{correct_ans} . $rtype, - type => $type - ); - $answerEvaluator->install_evaluator(\&interval_check,%params); - return $answerEvaluator; -} - -# -# The guts of the checker -# -sub interval_check { - my $ans = shift; - my %params = @_; - my ($lendpnt,$rendpnt) = ($params{lendpnt},$params{rendpnt}); - my $ends = $params{ends}; - - my $showHints = defined($params{showHints})? - $params{showHints} : $showPartialCorrectAnswers; - my $showOrderHints = defined($params{showOrderHints})? - $params{showOrderHints} : $showPartialCorrectAnswers; - $showHints = $showOrderHints = 0 if (isPreviewMode()); - - $ans->{student_ans} = trimString($ans->{student_ans}); - my $answer = $ans->{student_ans}; - my ($sl,$sa,$sb,$sr) = ($answer =~ m/^(\[|\()([^,]*),([^,]*)(\]|\))$/); - my @errors = (); my $score = 1; - - if (!defined($sl)) { - push(@errors,"Your answer doesn't look like an interval."); - } else { - my ($lhash,$rhash) = - (evaluateAnswer($lendpnt,$sa),evaluateAnswer($rendpnt,$sb)); - # - # (Second check is a hack since std_num_cmp with strings - # doesn't always set the error message when an error - # occurs. Grrr!) - # - if ($lhash->{ans_message} ne "" || - $lhash->{student_ans} =~ m/ Your answer/) { - push(@errors,"Error evaluating left endpoint: "); - push(@errors,IndentError($lhash->{student_ans})); - push(@errors,IndentError($lhash->{ans_message})); - } else { - $sa = $lhash->{student_ans}; - if ($lhash->{score} != 1) { - push(@errors,"The left endpoint is incorrect") if ($showHints); - $score = 0; - } - } - if ($rhash->{ans_message} ne "" || - $rhash->{student_ans} =~ m/ Your answer/) { - push(@errors,"Error evaluating right endpoint:"); - push(@errors,IndentError($rhash->{student_ans})); - push(@errors,IndentError($rhash->{ans_message})); - } else { - $sb = $rhash->{student_ans}; - if ($rhash->{score} != 1) { - push(@errors,"The right endpoint is incorrect") if ($showHints); - $score = 0; - } - } - $ans->setKeys(student_ans => $sl.$sa.",".$sb.$sr); - my ($la,$ra) = ($lhash->{student_ans},$rhash->{student_ans}); - push(@errors,"Note: The left endpoint is greater than ". - "the right endpoint (empty interval)") - if ($showOrderHints && isNumber($la) && isNumber($ra) && $la > $ra); - if ($sl.$sr ne $ends) { - push(@errors,"The type of interval is incorrect") if ($showHints); - $score = 0; - } - $ans->setKeys( - preview_text_string => - $sl . StringOrBlank($lhash->{preview_text_string},$sa) . "," . - StringOrBlank($rhash->{preview_text_string},$sb) . $sr, - preview_latex_string => - $sl . StringOrBlank($lhash->{preview_latex_string},$sa) . "," . - StringOrBlank($rhash->{preview_latex_string},$sb) . $sr - ); - clearEvaluator($lendpnt); - clearEvaluator($rendpnt); - } - - if (scalar(@errors) == 0) { - $ans->score($score); - $ans->{ans_message} = $ans->{error_message} = ''; - } else { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = join("\n",@errors); - } - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/lineAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/lineAnswer.pl deleted file mode 100755 index a17348e4d0..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/lineAnswer.pl +++ /dev/null @@ -1,73 +0,0 @@ -loadMacros('vectorAnswer.pl'); - -sub _lineAnswer_init {}; # don't reload this file - -###################################################################### -# -# Compare a parametric formula to a given line -# (allows any parallel vector, and any point on the line). -# -# Usage: parametric_line_cmp(ans,options) -# -# where ans is the answer in vector parametric form (e.g., "<1+3t,2-t>") -# or as a Vector object, and options are those that are allowed by -# point_cmp. The variable is assumed to be 't', but that can be changed -# using the cmp_params option. -# - -sub parametric_line_cmp { - my $answer = shift; - my $ans = fun_point_cmp($answer, - post_process => \&checkParametricLine, - showHints => 0, showLengthHints => 1, - cmp_params => [vars => 't'], - @_ - ); - foreach my $cmp (@{$ans->{rh_ans}->{components}}) { - # - # We evaluate the functions at t=0 and t=1 to get - # p = L(0) and v = L(1)-L(0). - # - $cmp->{rh_ans}->{evaluation_points} = [[0],[1]]; - } - return $ans; -} - -# -# The guts of the checker (called as a post-processor by -# the point checker). -# -sub checkParametricLine { - my $ans = shift; - my (@p,@cp,@v,@cv); - # - # Get the P and V for each line - # (we arranged above to evaluate at t=0 and t=1). - # - foreach my $cmp (@{$ans->{components}}) { - push(@cp,$cmp->{rh_ans}->{ra_instructor_values}->[0]); - push(@cv,$cmp->{rh_ans}->{ra_instructor_values}->[1]); - push(@p,$cmp->{rh_ans}->{ra_student_values}->[0]); - push(@v,$cmp->{rh_ans}->{ra_student_values}->[1]); - } - # - # Get the vector (v = L(1)-L(0)). - # - $v = vDiff(Point(@v),Point(@p)); $cv = vDiff(Point(@cv),Point(@cp)); - # - # The vectors must be parallel to be equal. - # - return 0 if (!areParallel($v,$cv)); - # - # If the points are equal, the lines are. - # - my $w = vDiff(Point(@cp),Point(@p)); - return 1 if (isCorrectAnswer(std_num_cmp(0),Norm($w))); - # - # Otherwise, if the vector between the two points is - # parallel to the direction vectors, the lines are equal. - # - return (areParallel($v,$w)); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/listAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/listAnswer.pl deleted file mode 100755 index cbaf9079b1..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/listAnswer.pl +++ /dev/null @@ -1,580 +0,0 @@ -sub _listAnswer_init {}; # don't reload this file - -###################################################################### -# -# This file implements a general list answer checker, which allows -# you to specify how the answer is to be split up, whether -# the entries must appear in the same order as the correct answer, -# whether informational messages are to be produced for incorrect -# entries, and what answer checker to call on each entry. -# -# The list_cmp() and std_list_cmp() functions are very general, and -# could be used to implement a variety of special cases of lists. -# A number of these are given below, including: -# -# num_list_cmp() a list of numeric values -# infinite_num_list_cmp() a list of numeric values or infinities -# fun_list_cmp() a list of formulas -# num_interval_list_cmp() a list of intervals with numeric endpoints -# fun_interval_list_cmp() a list of intervals with endpoint equations -# num_union_list_cmp() a list of unions of numeric intervals -# fun_union_list_cmp() a list of unions of variable intervals -# num_point_list_cmp() a list of numeric points -# fun_point_list_cmp() a list of formulaic points -# num_vector_list_cmp() a list of numeric vectors -# fun_vector_list_cmp() a list of formulaic vectors -# -# Some of these require that you load the .pl file for the -# associated answer checker as well. For example, to use -# num_point_list_cmp(), you must include -# -# loadMacros("pointAnswer.pl"); -# -# in your .pg file. -# -# Partial credit is given for getting some entries correct, unless -# $showParialCorrectAnswers is set to 0. A system level change to -# displayMacros.pl is required to make the system indicate this in -# a meaningful way. You can prevent partial credit by using the -# partialCredit => 0 option to any of the *_cmp() routines. -# - -###################################################################### - - -loadMacros( - "unionUtils.pl", - "answerUtils.pl", -); - -###################################################################### -# -# Usage: num_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "10, -3, 5") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_list_cmp {std_list_cmp(@_)} - - -###################################################################### -# -# Usage: infinite_num_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "10, -INF, 5") -# and options are any of those that can be passed to std_list_cmp. -# - -sub infinite_num_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&infinite_num_cmp, - type => "infinite number", @_); -} - - -###################################################################### -# -# Usage: fun_list_cmp(ans,options) -# -# where ans is the string indicating the correct list and options are -# any of those that can be passed to std_list_cmp. -# -# Options can also include: -# -# vars => [list] the variables for the function checker -# -# or any of the options allowed by list_cmp (see below). -# - -sub fun_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_cmp, - entry_type => 'equation', - fun_var_params(@_)); -} - -# -# Put the function's 'vars' option into the 'cmp_params' option. -# -sub fun_var_params { - my %params = (cmp_defaults => [], @_); - if (defined($params{vars})) { - $params{cmp_defaults} = [vars => $params{vars}, @{$params{cmp_defaults}}]; - delete $params{vars} - } - return %params; -} - - -###################################################################### -# -# Usage: num_interval_list_cmp(ans,options) -# fun_interval_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "[1,3), (-inf,-5)") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_interval_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_interval_cmp, - type => "number interval", - entry_type => 'interval', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_interval_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_interval_cmp, - type => "function interval", - entry_type => 'interval', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0], - @_); -} - - -###################################################################### -# -# Usage: num_union_list_cmp(ans,options) -# fun_union_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., -# "[1,3) U (-inf,-5), [10,11]") and options are any of those that can -# be passed to std_list_cmp. -# - -sub num_union_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_union_cmp, - type => "number union", - entry_type => 'interval or union', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0, showLengthHints => 0], - @_); -} - -sub fun_union_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_union_cmp, - type => "fun union", - entry_type => 'interval or union', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => "(0,1]", - fun_var_params( - cmp_defaults => [showHints => 0, showLengthHints => 0], - @_ - )); -} - - -###################################################################### -# -# Usage: num_point_list_cmp(ans,options) -# fun_point_list_cmp(ans,options) -# num_vector_list_cmp(ans,options) -# fun_vector_list_cmp(ans,options) -# -# where ans is the string indicating the list (e.g., "(1,2,3), (3,-2,4)") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_point_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_point_cmp, - type => "number point", - entry_type => 'point', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => undef, - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_point_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_point_cmp, - type => "function point", - entry_type => 'point', - separator => ' , ', - split => \&paren_split, - split_defaults => [separator => ','], - cmp_ans => undef, - fun_var_params(cmp_defaults => [showHints => 0],@_)); -} - -sub num_vector_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_vector_cmp, - type => "number vector", - entry_type => 'vector', - separator => ' , ', - split => \&paren_split, - split_defaults => [ - separator => ',', - open => '[(<', - close => '])>', - ], - cmp_ans => undef, - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_vector_list_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_vector_cmp, - type => "function vector", - entry_type => 'vector', - separator => ' , ', - split => \&paren_split, - split_defaults => [ - separator => ',', - open => '[(<', - close => '])>', - ], - cmp_ans => undef, - fun_var_params(cmp_defaults => [showHints => 0],@_)); -} - - -###################################################################### -# -# std_list_cmp(ans,options) -# -# where ans is the correct answer as a string (e.g. "10, -5"), and -# options are from among the following: -# -# cmp => \&cmp a reference to the answer checker to use for each -# list element (eg, \&std_num_cmp or \&fun_cmp) -# -# cmp_params => [...] a reference to a list of parameters for the -# answer checker (e.g., cmp_params => [vars => 'x']) -# -# or any of the options allowed by list_cmp (see below). -# -# This returns an answer checker that compares the list using the -# given answer checker on each element in the list. -# -# e.g. std_list_cmp("10, -5",cmp => ~~&std_num_cmp); -# - -sub std_list_cmp { - my $ans = shift; - my %params = ( - cmp => \&std_num_cmp, - cmp_ans => "0", - cmp_params => [], - cmp_defaults => [], - split => \&std_split, - split_params => [], - split_defaults => [separator => ','], - debug => 0, - @_ - ); - my $cmp = $params{cmp}; - my @cmp_params = (@{$params{cmp_defaults}},@{$params{cmp_params}}); - my $split = $params{split}; - my @split_params = (@{$params{split_defaults}},@{$params{split_params}}); - my $ans_ref = &{$split}($ans,@split_params); - warn "Error in professor's answer: $ans_ref" if (ref($ans_ref) ne "ARRAY"); - my @list = (); - foreach my $element (@{$ans_ref}) { - $element = trimString($element); - push(@list,&{$cmp}($element,@cmp_params)); - } - $params{cmp_ans} = hashFor($list[0])->{correct_ans} - if (!defined($params{cmp_ans}) && scalar(@list)); - list_cmp([@list],%params); -} - - -###################################################################### -# -# Usage: list_cmp([list],options) -# -# where list is the array of checkers for the elements in the list -# and options are among: -# -# showHints => 0 or 1 indicates whether to show messages about -# which elements are correct. -# -# showLengthHints => 0 or 1 -# indicates whether to show messages about -# having too many entries in the list -# -# ordered => 0 or 1 specifies if the order of the student's -# answers must match thos of the professor. -# If 0, they can be in any order, if 1, they -# must be in the same order. Defaut: 0. -# -# partialCredit => 0 or 1 indicates if scores other than 0 and 1 are -# to be used (to provide for partial credit -# when some of the answers are correct). -# -# cmp => \&cmp reference to a comparison -# for doing syntax checking on the -# elements given by the student. -# -# cmp_params => [...] parameters to pass to the answer checker -# for syntax checking. -# -# cmp_defaults => [...] default parameters for the syntax answer check -# -# cmp_ans => "..." a string to use for the answer when checking -# for syntax errors in the entries that aren't -# correct. -# -# split => \&split a reference to a routine to split the -# answer string into elements (by default, it -# uses the standard perl split() funciton -# splitting at commas) -# -# split_params => [...] a reference to a list of parameters for the -# split routine (e.g., -# split_params => [separator => ',']) -# -# split_defaults => [...] a reference to a list of defaults for the -# split_params list above. -# -# separator => ', ' the formatted specifier for use when -# creating the string to display (it can -# contain spaces, whereas the separator -# in split_defaults or split_params -# should not). Note that split_defaults -# usually includes the separator used -# for the actual splitting. -# - -sub list_cmp { - my $elements = shift; - $elements = [$elements] unless ref($elements) eq 'ARRAY'; - my %params = @_; - set_default_options(\%params, - cmp => \&std_num_cmp, - cmp_params => [], - cmp_defaults => [], - cmp_ans => "0", - split => \&std_split, - split_params => [], - split_defaults => [separator => ','], - separator => ', ', - debug => 0, - type => "number", - entry_type => "number", - list_type => "list", - showHints => undef, - showLengthHints => undef, - ordered => undef, - ); - my $type = "list ($params{type})"; - my @list = (); - foreach my $cmp (@{$elements}) {push(@list,hashFor($cmp)->{correct_ans})} - - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => join($params{separator},@list), - elements => $elements, - type => $type, - ); - $answerEvaluator->install_evaluator(\&list_check,%params); - return $answerEvaluator; -} - -# -# The guts of the list checker. -# -sub list_check { - my $ans = shift; - my %params = @_; - my $value = $params{entry_type}; my $ltype = $params{list_type}; - my $cmp = $params{cmp}; my $cmp_ans = $params{cmp_ans}; - my @cmp_params = (@{$params{cmp_defaults}},@{$params{cmp_params}}); - my $split = $params{split}; - my @split_params = (@{$params{split_defaults}},@{$params{split_params}}); - my @cmp = @{$ans->{elements}}; - my (@errors,@list,@text,@latex); - my $ordered = $params{ordered}; - my $showHints = defined($params{showHints})? - $params{showHints} : $showPartialCorrectAnswers; - my $showLengthHints = defined($params{showLengthHints})? - $params{showLengthHints} : $showPartialCorrectAnswers; - my $partialCredit = defined($params{partialCredit})? - $params{partialCredit} : $showPartialCorrectAnswers; - $showHints = $showLengthHints = 0 if (isPreviewMode()); - - my $list_ref = &{$split}($ans->{student_ans},@split_params); - if (ref($list_ref) ne "ARRAY") { - $ans->score(0); $ans->{error} = 1; - $ans->{error_message} = $ans->{ans_message} = $list_ref; - return $ans; - } - - my $maxscore = scalar(@cmp); - my $m = scalar(@{$list_ref}); - $maxscore = $m if ($m > $maxscore); - my $i = 0; my $score = 0; my $hash; - my $indent = ($m > 1)? 4: 0; - - ELEMENT: foreach my $element (@{$list_ref}) { - $element = trimString($element); $i++; - if ($element eq '') { - push(@errors,"Your ".NameForNumber($i)." $value is blank"); - push(@list,''); push(@text,''); push(@latex,''); - next ELEMENT; - } - if ($ordered) { - $hash = evaluateAnswer(shift(@cmp),$element); - if ($hash && $hash->{score} == 1) { - push(@list,$hash->{student_ans}); - push(@text,StringOrBlank($hash->{preview_text_string})); - push(@latex,StringOrBlank($hash->{preview_latex_string})); - $score++; next ELEMENT; - } - } else { - foreach my $k (0..$#cmp) { - $hash = evaluateAnswer($cmp[$k],$element); - if ($hash && $hash->{score} == 1) { - splice(@cmp,$k,1); - push(@list,$hash->{student_ans}); - push(@text,StringOrBlank($hash->{preview_text_string})); - push(@latex,StringOrBlank($hash->{preview_latex_string})); - $score++; next ELEMENT; - } - } - } - $hash = evaluateAnswer(&{$cmp}($cmp_ans,@cmp_params),$element) - if (!$ordered || !$hash); - if ($hash->{ans_message} ne '') { - push(@errors,"Error evaluating the ".NameForNumber($i)." $value:") - if ($m > 1); - push(@errors,IndentError($hash->{student_ans},$indent)) - if ($m > 1 || $hash->{student_ans} =~ m/error/i); - push(@errors,IndentError($hash->{ans_message},$indent)); - push(@list,$element); - } else { - push(@list,$hash->{student_ans}); - push(@errors,"Your ".NameForNumber($i)." $value is incorrect") - if $showHints && $m > 1; - } - push(@text,StringOrBlank($hash->{preview_text_string})); - push(@latex,StringOrBlank($hash->{preview_latex_string})); - } - - if ($showLengthHints) { - $value =~ s/ or /s or /; # fix "interval or union" - push(@errors,"There should be more ${value}s in your $ltype") - if ($score == $m && scalar(@cmp) > 0); - push(@errors,"There should be fewer ${value}s in your $ltype") - if ($score < $maxscore && $score == scalar(@{$ans->{elements}})); - } - - $ans->{student_ans} = join($params{separator},@list); - $ans->{preview_text_string} = join($params{separator},@text); - $ans->{preview_latex_string} = join($params{separator},@latex); - - $score = 0 if ($score != $maxscore && !$partialCredit); - $ans->score($score/$maxscore); - $ans->{error_message} = $ans->{ans_message} = join("\n",@errors); - - return $ans; -} - - -###################################################################### -# -# Do a split at a specific character -# (routine should return a reference to the split list of -# answers, or a string that is an error message indicating -# a syntax error in the string). -# - -sub std_split { - my $s = shift; - my %params = (separator => ',', @_); - return [split($params{separator},$s)]; -} - - -###################################################################### -# -# Split at commas between intervals or points. -# (Try to match parentheses, and only split when not within an -# open set of parentheses). -# -# Paremeters can include: -# -# separator => ',' the character to split at (you can use -# a regexp like '[,;]' to allow several -# characters). Default: ',' -# -# open => '...' the characters that count as open -# parentheses. Default: '[(' -# -# close => '...' the characters that count as close -# parentheses. Default: '])' -# -# spaceSeparates => 0 or 1 -# indicates whether a space can be used to -# separate entries in the list (when the regular -# separator is not already there). This will -# only occur at spaces between unnested close -# and open parens (with nothing but spaces in -# between). Default: 1 -# - -sub paren_split { - my $s = shift; - my %params = @_; - set_default_options(\%params, - separator => ',', - open => '[(', - close => '])', - spaceSeparates => 1, - ); - my $separator = $params{separator}; - my ($open,$close) = ($params{open},$params{close}); - $open =~ s/\]/\\\]/g; $close =~ s/\]/\\\]/g; - my $spaceSeparates = $params{spaceSeparates}; - my $parens = 0; my $c; - my @list; my $element; my $space = 0; - - foreach $c (split('',$s)) { - if ($c =~ m/[$open]/) { - if ($spaceSeparates && $space == 2) {push(@list,$element); $element = ''} - $parens++; $space = 0; - } - elsif ($c =~ m/[$close]/) { - $parens-- if $parens; - $space = ($parens == 0); - } - elsif ($c =~ m/$separator/) { - $space = 0; - if ($parens == 0) {push(@list,$element); $element = ''; next} - } - elsif ($c eq ' ' && $space) {$space = 2} - elsif ($c ne ' ') {$space = 0} - $element .= $c; - } - push (@list,$element); - return [@list]; -} - -###################################################################### - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/parallelAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/parallelAnswer.pl deleted file mode 100755 index 704864c253..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/parallelAnswer.pl +++ /dev/null @@ -1,47 +0,0 @@ -loadMacros("vectorAnswer.pl"); - -sub _parallelAnswer_init {}; # don't reload this file - -###################################################################### -# -# Check if the student's vector is parallel to the answer vector. -# -# Usage: parallel_vector_cmp(ans,options) -# -# Where ans is the vector to compare against (e.g., "<1,2,3>" or a -# Vector object), and options are any of the options allowed -# by point_cmp. The vector must be a constant, not a formula. -# One additional option is allowed: -# -# same_direction => 0 or 1 indicates if the two vectors must -# also point in the same (not -# opposite) direction. Default: 0. -# - -sub parallel_vector_cmp { - my $answer = shift; - my %params = (same_direction => 0, @_); - my $sameDirection = $params{same_direction}; - delete $params{same_direction}; - my $cmp = num_point_cmp($answer, - post_process => \&checkParallel, - showHints => 0, - %params - ); - $cmp->{rh_ans}{same_direction} = $sameDirection; - return $cmp; -} - -# -# The parallel check, as a post-processor to point_cmp -# -sub checkParallel { - my $ans = shift; - areParallel( - Point(@{$ans->{correct_point}}), - Point(@{$ans->{answer_point}}), - $ans->{same_direction} - ); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/planeAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/planeAnswer.pl deleted file mode 100755 index 2efeccd883..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/planeAnswer.pl +++ /dev/null @@ -1,147 +0,0 @@ -loadMacros( - 'unionAnswer.pl', - 'unionMacros.pl', - 'unionVectors.pl', - 'vectorUtils.pl', -); - -sub _planeAnswer_init {}; # don't reload this file - -# -# Check that a formula represents a given plane implicitly -# -# Usage: implicit_plane_cmp(N,P); -# -# where N is a normal vector to the plane and P is a point on the -# plane. Ex: plane_cmp(Vector(1,2,3),Point(0,0,0)); or -# plane_cmp([1,2,3],[0,0,0]); -# -# The student enter's an answer in the form "a x + b y + c z = d", -# or anything equivalent to that. This answer checking will properly -# recognize the plane even if it is given as a different multiple of -# the equation, or if it is reorganized, or has computations within it. -# E.g., the student could enter "0 = 20 -(x + 3y + z)". -# -# The answer checker will produce the string used for displaying the -# correct answer for you automatically, properly handling zero coefficients. -# - -sub implicit_plane_cmp { - my $N = Vector(shift); my $P = Point(shift); - my %params = @_; - set_default_options(\%params, - showHints => 1, - debug => 0, - ); - my $answer = Plane($N,$P); - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => $answer, - type => "implicit plane", - N => $N, P => $P, - ); - $answerEvaluator->install_evaluator(\&implicit_plane_check,%params); - return $answerEvaluator; -} - -# -# The guts of the implicit plane checker -# -sub implicit_plane_check { - my $ans = shift; - my %params = @_; - my @errors = (); my $score = 0; - my $showHints = $params{showHints}; - $showHints = 0 if ($showPartialCorrectAnswers == 0 || isPreviewMode()); - my ($text,$latex); - my ($a,$b,$c,$d); - my ($N,$P) = ($ans->{N}->data,$ans->{P}->data); - - my $student_ans = trimString($ans->{student_ans}); - # - # Find the left- and right-hand sides - # - if ($student_ans !~ m/^(.*)=(.*)$/) { - push(@errors,"Your answer is not of the form 'ax + by + cz = d'") - if ($showHints); - } else { - my ($lhs,$rhs) = (trimString($1),trimString($2)); - push(@errors,"The left-hand side is blank") if $lhs eq ''; - push(@errors,"The right-hand side is blank") if $rhs eq ''; - if (scalar(@errors) == 0) { - # - # Do a syntax check on the two sides - # - my $cmp = fun_cmp("0",vars=>['x','y','z']); - my $hash = evaluateAnswer($cmp,$lhs,1); - if ($hash->{ans_message} eq "") { - $lhs = $hash->{student_ans}; - $text = $hash->{preview_text_string}; - $latex = $hash->{preview_latex_string}; - } else { - push(@errors,"Error evaluating left-hand side:"); - push(@errors,IndentError($hash->{student_ans})); - push(@errors,IndentError($hash->{ans_message})); - } - clearEvaluator($cmp); - $hash = evaluateAnswer($cmp,$rhs,1); - if ($hash->{ans_message} eq "") { - $rhs = $hash->{student_ans}; - $text .= " = ".$hash->{preview_text_string}; - $latex .= " = ".$hash->{preview_latex_string}; - } else { - push(@errors,"Error evaluating right-hand side:"); - push(@errors,IndentError($hash->{student_ans})); - push(@errors,IndentError($hash->{ans_message})); - } - clearEvaluator($cmp); - - if (scalar(@errors) == 0) { - # - # Use $cmp to evaluate student function to obtain coefficients - # - $cmp->{rh_ans}{evaluation_points} = [[0,0,0],[1,0,0],[0,1,0],[0,0,1]]; - $hash = evaluateAnswer($cmp,"($lhs)-($rhs)"); - # check for errors here, too? - $d = $hash->{ra_student_values}->[0]; - $a = $hash->{ra_student_values}->[1] - $d; - $b = $hash->{ra_student_values}->[2] - $d; - $c = $hash->{ra_student_values}->[3] - $d; - # - # Check that the student function really IS a plane - # - $cmp = fun_cmp("$a x + $b y + $c z + $d",vars => ['x','y','z']); - if (isCorrectAnswer($cmp,"($lhs)-($rhs)")) { - # - # Check that is is the RIGHT plane - # - $score = (areParallel($N,Point($a,$b,$c)) && - isCorrectAnswer(std_num_cmp(-$d),vDot($P,Point($a,$b,$c)))); - } else { - push(@errors, - "Your answer does not seem to be of the form 'ax + by + cz = d'") - if ($showHints); - } - } - $ans->setKeys( - student_ans => $lhs." = ".$rhs, - preview_text_string => $text, - preview_latex_string => $latex, - ); - } - } - if (scalar(@errors) == 0) { - $ans->score($score); - $ans->{ans_message} = $ans->{error_message} = ''; - $ans->{error} = 0; - } else { - $ans->score(0); - $ans->{error_message} = join("\n",@errors); - $ans->{ans_message} = join("\n",@errors); - $ans->{error} = 1; - } - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/pointAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/pointAnswer.pl deleted file mode 100755 index f441598abd..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/pointAnswer.pl +++ /dev/null @@ -1,300 +0,0 @@ -loadMacros( - "unionMacros.pl", - "unionUtils.pl", - "unionVectors.pl", - "answerUtils.pl", -); - -sub _pointAnswer_init {}; # don't reload this file - -###################################################################### -## -## Point (and Vector) answer checkers -## -## num_point_cmp() check for a point with numeric coordinates -## fun_point_cmp() check for a point with coordinate functions -## -## std_point_cmp() provide your own coordinate checker -## point_cmp() provide array of coordiante checkers - - -###################################################################### -# -# num_point_cmp(answer,options) -# -# where options are from among: -# -# extra_cmp => cmp an answer checker to use for extra coordinates -# supplied by the student (for syntax checking) -# -# cmp_params => [list] parameters to be passed to std_num_cmp -# for each coordinate -# -# format => string an sprintf format string used to format -# the answer (for display purposes only). -# Default: "%.6g" -# -# or any of the options to point_cmp (see below). -# -# This provides an answer checker that compares against a point -# or vector with numeric coordinates. -# -# e.g. num_point_cmp("(1,0,0)") -# - -sub num_point_cmp { - my $ans = shift; - std_point_cmp($ans,cmp => \&std_num_cmp, format => '%.6g', @_); -} - - -###################################################################### -# -# fun_point_cmp(answer,options) -# -# where options are from among: -# -# extra_cmp => cmp an answer checker to use for extra coordinates -# supplied by the student (for syntax checking) -# -# cmp_params => [list] parameters to be passed to fun_cmp -# for each coordinate -# -# vars => [list] the variables used in the functions -# -# or any of the options to point_cmp (see below). -# -# This provides an answer checker that compares against a point -# or vector with functional coordinates. -# -# e.g. fun_point_cmp("(t,t^2,t^3)", vars => 't'); -# - -sub fun_point_cmp { - my $ans = shift; - my %params = (cmp_params => [], @_); - if (defined($params{vars})) { - $params{cmp_params} = [vars => $params{vars}, @{$params{cmp_params}}]; - delete $params{vars}; - } - std_point_cmp($ans, cmp => \&fun_cmp, %params); -} - - -###################################################################### -# -# std_point_cmp(ans,options) -# -# where ans is the correct answer as a string (e.g. "(1,0,0)") and -# options are taken from among the following: -# -# cmp => \&cmp a reference to the answer checker to use for each -# endpoint (e.g., \&std_num_cmp, or ~~&std_num_cmp -# in a .pg file) -# -# cmp_params => [...] is a reference to a list of parameters for the -# answer checker (e.g., [vars => 'x']) -# -# default => ans is the answer to use for any extra coordinates -# supplied by the student (for syntax checking only) -# -# or any of the options allowed by point_cmp (see below) -# -# This returns an answer checker that compares the point using the -# given answer checker on each coordinate. -# -# e.g. std_point_cmp("(1,0,0)",cmp => ~~&strict_num_cmp, default => 0); -# -sub std_point_cmp { - my $ans = shift; $ans = $ans->answer if isVector($ans); - my %params = ( - cmp => \&std_num_cmp, - cmp_params => [], - default => 0, - @_ - ); - my ($cmp,$cmp_params) = ($params{cmp},$params{cmp_params}); - delete $params{cmp}; delete $params{cmp_params}; - my $default = $params{default}; delete $params{default}; - $params{extra_cmp} = &{$cmp}($default,@{$cmp_params}) - unless $params{extra_cmp}; - if ($ans =~ s/^(\[|\(|<)(.*)(>|\]|\))$/$2/) - {$params{left} = $1; $params{right} = $3} - my @answers = (); my $answer; - foreach $answer (split(',',$ans)) - {push(@answers,&{$cmp}($answer,@{$cmp_params}))} - point_cmp([@answers],%params); -} - - -###################################################################### -# -# point_cmp(cmps,options) -# -# where -# -# cmps is a reference to an array of answer checkers, one -# for each component of the answer -# -# and options are taken from: -# -# showHints => 0 or 1 determines if coordinate-by-coordinate -# hints are given (tells whether each -# coordinate is correct or not). -# (default: 1) -# -# showLengthHints => 0 or 1 determines if messages about incorrect -# number of components should be issued -# (default: 1) -# -# extra_cmp => cmp an answer checker to use for any extra -# coordinates supplied by the student -# (for syntax checking purposes) -# -# format => string sprintf format for the coordinates -# -# post_process => code routine to determine the score once -# the point passes the syntax checking -# of the individual components. This -# can be used to change the score -# after the fact (e.g., to check for -# parallel vectors, etc.) -# -sub point_cmp { - my $compref = shift; - my %params = @_; - set_default_options(\%params, - showHints => 1, - showLengthHints => 1, - extra_cmp => undef, - post_process => undef, - format => undef, - debug => 0, - left => '(', - right => ')', - ); - $params{components} = $compref; - $params{pattern} = "\\$params{left}(.*)\\$params{right}" - unless defined($params{pattern}); - - # - # Needed to get preview to work properly - # - my $type = "point"; my $hash; - foreach $hash (@{$compref}) { - if ((hashFor($hash))->{type} =~ m/number/) { - $type .= " (number)"; - last; - } - } - - my @ans = (); my @ans_point = (); - my $format = $params{format}; my $correct_ans; - foreach $hash (@{$compref}) { - $correct_ans = (hashFor($hash))->{correct_ans}; - push(@ans_point,$correct_ans); - $correct_ans = sprintf($format,$correct_ans) if ($format); - push(@ans,$correct_ans); - } - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - correct_ans => $params{left}.' '.join(", ",@ans).' '.$params{right}, - correct_point => [@ans_point], - components => $compref, - type => $type, - ); - $answerEvaluator->install_evaluator(\&point_check,%params); - return $answerEvaluator; -} - -###################################################################### -# -# The guts of the point checker -# -sub point_check { - my $ans = shift; - my %params = @_; - my @errors = (); my $score = 1; - my $showHints = defined($params{showHints})? - $params{showHints} : $showPartialCorrectAnswers; - my $showLengthHints = defined($params{showLengthHints})? - $params{showLengthHints} : $showPartialCorrectAnswers; - $showHints = $showLengthHints = 0 if isPreviewMode(); - - my $extra_cmp = $params{extra_cmp}; - $extra_comp = std_num_cmp(0) unless $extra_cmp; - my ($lp,$rp) = ($params{left},$params{right}); - my ($addparens) = 1; - my $format = $params{format}; - my (@new_ans,@new_ans_point); - - my $student_ans = trimString($ans->{student_ans}); - if ($student_ans !~ s/^$params{pattern}$/$1/) { - my %parens = ('(' => "parentheses", - '<' => "angle brackets", - '[' => "square brackets"); - $parens{$lp} = "the proper characters" unless defined($parens{$lp}); - push(@errors, - showHTML("Your answer is not enclosed in $parens{$lp}: $lp and $rp")); - $score = 0; - } else { - my $i = 0; my @answers = split(',',$student_ans); - my ($answer, $answer_full, $correct_cmp, @text, @latex); - - foreach $answer (@answers) { - $correct_cmp = (@{$ans->{components}})[$i++] || $extra_cmp; - $hash = evaluateAnswer($correct_cmp,$answer); - $answer_full = $answer; - if ($hash->{ans_message} eq "") { - $answer_full = $answer = $hash->{student_ans}; - $answer = sprintf($format,$answer) if ($format); - } else {$hash->{score} = 0} - push(@new_ans,$answer); push(@new_ans_point,$answer_full); - push(@text,StringOrBlank($hash->{preview_text_string},$answer)); - push(@latex,StringOrBlank($hash->{preview_latex_string},$answer)); - if ($hash->{score} != 1 || $correct_cmp == $extra_cmp) { - $score = 0; - if ($hash->{ans_message} ne "") { - push(@errors,"Error evaluating the ".NameForNumber($i). - " coordinate:"); - push(@errors,IndentError($answer)); - push(@errors,IndentError($hash->{ans_message})); - } else { - push(@errors,"The ".NameForNumber($i)." coordinate is incorrect.") - if ($showHints); - } - } - clearEvaluator($correct_cmp); - } - - if (scalar(@answers) != scalar(@{$ans->{components}})) { - push(@errors,"The number of coordinates is not correct.") - if ($showLengthHints); - $score = 0; - } - $ans->setKeys(student_ans => $lp . join(', ',@new_ans) . $rp) - if (scalar(@new_ans) > 0); - $ans->setKeys( - preview_text_string => $lp . join(', ',@text) . $rp, - preview_latex_string => $lp . join(', ',@latex) . $rp, - ); - } - - # make sure vectors aren't interpretted as HTML tags - $ans->setKeys(student_ans => showHTML($ans->{student_ans})); - - if (scalar(@errors) == 0) { - $ans->score($score); - $ans->{ans_message} = $ans->{error_message} = ''; - $ans->{answer_point} = [@new_ans_point]; - if ($params{post_process}) {$ans->score(&{$params{post_process}}($ans))} - } else { - $ans->score(0); - $ans->{ans_message} = $ans->{error_message} = join("\n",@errors); - } - - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/unionAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/unionAnswer.pl deleted file mode 100755 index 3b3f1a6a0e..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/unionAnswer.pl +++ /dev/null @@ -1,56 +0,0 @@ -###################################################################### -# -# Implements answer checkers for unions of intervals -# - -loadMacros( - "intervalAnswer.pl", - "listAnswer.pl" -); - -sub _unionAnswer_init { - $INFINITY_UNION_MESSAGE = - "$BBOLD Note: $EBOLD ". - "If the answer includes more than one interval, write the intervals ". - "separated by the ${LQ}union$RQ symbol, ${BITALIC}U${EITALIC}. If needed, enter ". - "\\(\\infty\\) as ${BITALIC}$INFINITY_WORD${EITALIC} and \\(-\\infty\\) as ". - "${BITALIC}-$INFINITY_WORD${EITALIC}."; - - $INFINITY_UNION_MESSAGE = "" if $displayMode eq 'TeX'; -}; - -###################################################################### -# -# Usage: num_union_cmp(ans,options) -# fun_union_cmp(ans,options) -# -# where ans is the string indicating the union (e.g., "[1,3) U (-inf,-5)") -# and options are any of those that can be passed to std_list_cmp. -# - -sub num_union_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&num_interval_cmp, - type => "number interval union", - entry_type => 'interval', - list_type => 'union', - separator => ' U ', - split_defaults => [separator => 'U'], - cmp_ans => "(0,1]", - cmp_defaults => [showHints => 0], - @_); -} - -sub fun_union_cmp { - my $ans = shift; - std_list_cmp($ans, cmp => \&fun_interval_cmp, - type => "function interval union", - entry_type => 'interval', - list_type => 'union', - separator => ' U ', - split_defaults => [separator => 'U'], - cmp_ans => "(0,1]", - fun_var_params(cmp_defaults => [showHints => 0],@_)); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/unionVectors.pl b/OpenProblemLibrary/macros/Union/obsolete/unionVectors.pl deleted file mode 100755 index 3f3fb89ee6..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/unionVectors.pl +++ /dev/null @@ -1,486 +0,0 @@ -sub _unionVectors_init {}; # don't reload this file - -###################################################################### -# -# A class that implements Vector and Point objects. It allows you -# to create them, perform arithmatic on them, and display them in -# various formats. -# -# This is just a temprary hack until I finish the complete -# multi-variable parser that is in the works. -- DPVC -# -# Usage: -# -# Point(x1,...,xn) creates a point object with n coordinates -# Vector(x1,...,xn) creates a vector object with n coordinates -# -# Norm(x) computes the length of a vector -# Unit(x) computes a unit vector in the same direction as x -# -# areParallel(x,y) returns 1 if the vectors are parallel -# -# vSum(x,y) adds two points or vectors -# vDiff(x,y) computes x - y -# vProd(a,x) computes the scalar product ax for a point -# or vector x and scalar a -# vDiv(x,a) divides point or vector x by a -# vDot(x,y) computes the dot product x.y -# vCross(x,y) comptes the cross product of two vectors -# vNeg(x) computes -x -# vEqual(x,y) true when x and y are equal vectors -# vCmp(x,y) does a comparison of two points or vectors -# in lexicographic order (like <=> for vectors). -# when not of the same length, the shorter one -# is considered to be smaller. -# -# These operations can be performed much easier by using the overload -# package to overload the operators to work with point and vector -# objects. However, to do this in WeBWorK requires making this a -# module and adding it to the PG_module_list.pl file (a system-level -# change). I'm working on a more complete multi-variable package -# that will supersede this, and so wanted to keep it a local change -# for now. -# -# The Point and Vector routines can accept a variety of input -# formats, for example, Point([1,2,3]) also produces the same result -# as Point(1,2,3). So you could do $x = [1,2,3], $y = Point($x), or -# $x = vDiff([1,2,3],[4,5,6]) to obtain the point (-3,-3,-3). -# -# You can also pass Point and Vector objects themselves, which -# provides a means of converting one type to the other. For example: -# -# $x = Point(1,2,3); $y = Point(4,5,6); -# $v = Vector(vDiff($y,$x)); -# -# produces the vector from $x to $y (without the Vector call, the -# result of vDiff would be a Point). -# -# The various vector-based answer checkers all accept Vector or Point -# objects as their inputs (and usually also accept strings like -# "(1,2,3)", or arrays like [1,2,3]). -# -# Once you have a Point or Vector object, $x, you can get a printable -# version of the object using $x->string, $x->TeX or $x->answer. -# Answer and string both produce text strings suitable for display as -# the correct answer, while TeX produces a string for use within -# \( \) in the problem itself. For example: -# -# $P = Point(1,2,3); -# BEGIN_TEXT -# Consider the point \(P = \{$P->TeX\}\). -# END_TEXT -# - -################################################## -# -# Can't seem to get Exporter to work, so doing it by hand -- DPVC -# (If you turn this into a .pm file, you need to make these -# subroutines in PG_priv::, e.g., sub PG_priv::Point ...) -# - -sub Point {unionVectors::Point(@_)} -sub Vector {unionVectors::Vector(@_)} - -sub isVector {unionVectors::isVector(@_)} - -sub Norm {unionVectors::norm(@_)} -sub Unit {unionVectors::unit(@_)} -sub areParallel {unionVectors::areParallel(@_)} - -# -# Can't use 'use overload' from loadMacros, so doing -# explicit calls. (If the file is made a .pm file and added -# to PG_module_list.pl, then things can work much nicer.) -# -sub vSum {unionVectors::add(@_)} -sub vDiff {unionVectors::sub(@_)} -sub vProd {unionVectors::mult($_[1],$_[0])} -sub vDiv {unionVectors::div(@_)} -sub vDot {unionVectors::dot(@_)} -sub vCross {unionVectors::cross(@_)} -sub vNeg {unionVectors::neg(@_)} -sub vCmp {unionVectors::compare(@_)} -sub vEqual {unionVectors::compare(@_) == 0} - -################################################## - -package unionVectors; -my $pkg = 'unionVectors'; - -#use strict; -#use vars qw(%parens); - -#use overload -# '+' => \&add, -# '-' => \&sub, -# '*' => \&mult, -# '/' => \&div, -# '**' => \&power, -# '.' => \&dot, -# 'x' => \&cross, -# '<=>' => \&compare, -# 'cmp' => \&compare, -# 'neg' => sub {$_[0]->neg}, -# 'abs' => sub {$_[0]->abs}, -# '""' => \&string, -# 'nomethod' => \&nomethod; - -# -# Usually we don't want to see the full precision, so -# use this for display purposes (computation still at -# full precision) -# -my $numberFormat = '%.6g'; # could be '%s' for full precision - -# -# What a number looks like -# -my $numPattern = '-?(\d+(\.\d*)?|\.\d+)(E[-+]?\d+)?'; - -# -# What parentheses to use for points and vectors -# -%parens = ( - 'Point' => {open => '(', close => ')'}, - 'Vector' => {open => '<', close => '>'}, -); - -################################################## - -# -# Create an object of the required type -# -sub Point {$pkg->new('Point',@_)} -sub Vector {$pkg->new('Vector',@_)} - -# -# Create a new object of the requested type using the given data -# -# Check that the type is valid -# Get the data into an array if there is more than one coordinate given -# If the input is already a point or vector, use its data otherwise -# Make it an array if it isn't already -# Check that there is at least one coordinate -# Check that each coordinate is a number or string -# Return the new object -# -sub new { - my $self = shift; my $class = ref($self) || $self; - my $type = shift; - die "Unknown multivariable type '$type'" unless defined($parens{$type}); - my $p = shift; $p = [$p,@_] if (scalar(@_) > 0); - if (isVector($p)) {$p = [$p->value]} else { - $p = [$p] if (defined($p) && ref($p) ne 'ARRAY'); - die $type."s must have at least one coordinate" - unless defined($p) && scalar(@{$p}) > 0; - foreach my $x (@{$p}) {die "Can't use ".showType($x)." as a coordinate" - if ref($x)} - } - bless {type => $type, data => $p}, $class; -} - -# -# Create a new object from data that is known to be good -# (faster -- for use within the methods below) -# -sub make { - my $self = shift; my $class = ref($self) || $self; - my $type = $self->type; - bless {type => $type, data => [@_]}, $class; -} - -# -# Get the object's data or type -# -sub value {@{(shift)->{data}}} -sub data {(shift)->{data}} -sub type {(shift)->{type}} - -################################################## - -# -# Check if an item is of the required type -# -sub isNumber {my $n = shift; $n =~ m/^$numPattern$/oi} -sub isVector {ref(shift) eq $pkg} - -# -# Show an item's type for error messages -# -sub showType { - my $value = shift; - return "'".$value."'" unless ref($value); - my $type = ref($value); $type = $value->type if ($type eq $pkg); - return 'an '.$type if substr($type,0,1) =~ m/[aeio]/i; - return 'a '.$type; -} - -# -# Return the object if it is a Point or Vector already, -# otherwise make it into a Point. (This guarantees that -# we have a Point or Vector as a result.) -# -sub promote { - my $x = shift; - $x = [$x,@_] if scalar(@_) > 0 || isNumber($x); - return $pkg->new('Point',$x) if ref($x) eq 'ARRAY'; - return $x if ref($x) eq $pkg; - die "Can't convert ".showType($x)." to a Point"; -} - -# -# Returns the object of highest precedence (where Vectors are -# considered higher precedence than Points). -# -sub highestType { - my ($l,$r) = @_; - return $r if (isVector($r) && $r->type eq 'Vector'); - return $l; -} - -################################################## -# -# Implement the vector operations. These are set up to be used -# with the overload package, and the $flag argument is part of -# that mechanism (it tells if the order of the operands has been -# reversed). -# - -# -# A catch-all for operations that aren't defined -# (when overloading is in effect). -# -sub nomethod { - my ($l,$r,$flag,$op) = @_; - die "Can't use '$op' with ".$l->type."-valued operands" -} - -# -# Add the vectors of they are of the same length. -# -sub add { - my ($l,$r,$flag) = @_; my $self = highestType($l,$r); - ($l,$r) = (promote($l)->data,promote($r)->data); - die $self->type." addition with different number of coordiantes" - unless scalar(@{$l}) == scalar(@{$r}); - my @s = (); - foreach my $i (0..scalar(@{$l})-1) {push(@s,$l->[$i] + $r->[$i])} - return $self->make(@s); -} - -# -# Subtract the vectors, if they are of the same length -# -sub sub { - my ($l,$r,$flag) = @_; my $self = highestType($l,$r); - ($l,$r) = (promote($l)->data,promote($r)->data); - die $self->type." subtraction with different number of coordiantes" - unless scalar(@{$l}) == scalar(@{$r}); - if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; - my @s = (); - foreach my $i (0..scalar(@{$l})-1) {push(@s,$l->[$i] - $r->[$i])} - return $self->make(@s); -} - -# -# Multiply a vector by a scalar. (Adding 0 seems to avoid some funny -# things like -0 as the result. Don't know why.) -# -sub mult { - my ($l,$r,$flag) = @_; my $self = $l; - die $l->type."s can only be multiplied by numbers" unless isNumber($r); - my @coords = (); - foreach my $x ($l->value) {push(@coords,$x*$r+0)} - return $self->make(@coords); -} - -# -# Divide a vector by a non-zer scalar -# -sub div { - my ($l,$r,$flag) = @_; my $self = $l; - die "Can't divide by a ".$self->type if $flag; - die $self->type."s can only be divided by numbers" unless isNumber($r); - die "Division by zero" if $r == 0; - my @coords = (); - foreach my $x ($l->value) {push(@coords,$x/$r+0)} - return $self->make(@coords); -} - -# -# Warn that powers don't make sense for vectors -# (when overloading is in effect) -# -sub power { - my ($l,$r,$flag) = @_; my $self = $l; - die "Can't raise ".$self->type."s to powers" unless $flag; - die "Can't use ".$self->type."s in exponents"; -} - -# -# Dot two vectors together -# -sub dot { - my ($l,$r,$flag) = @_; my $self = $l; - ($l,$r) = (promote($l)->data,promote($r)->data); - die "Dot product with different number of coordiantes" - unless scalar(@{$l}) == scalar(@{$r}); - my $s = 0; - foreach my $i (0..scalar(@{$l})-1) {$s += $l->[$i] * $r->[$i]} - return $s; -} - -# -# Take the cross product of two vectors in 3D -# -sub cross { - my ($l,$r,$flag) = @_; my $self = $l; $self->{type} = "Vector"; - ($l,$r) = (promote($l)->data,promote($r)->data); - die $self->type."s must be in 3-space for cross product" - unless scalar(@{$l}) == 3 && scalar(@{$r}) == 3; - $self->make($l->[1]*$r->[2] - $l->[2]*$r->[1], - -($l->[0]*$r->[2] - $l->[2]*$r->[0]), - $l->[0]*$r->[1] - $l->[1]*$r->[0]); -} - -# -# Do a lexicographic comparison of two vectors -# -sub compare { - my ($l,$r,$flag) = @_; - ($l,$r) = (promote($l)->data,promote($r)->data); - return scalar(@{$l}) <=> scalar(@{$r}) unless scalar(@{$l}) == scalar(@{$r}); - if ($flag) {my $tmp = $l; $l = $r; $r = $tmp}; - my $cmp = 0; - foreach my $i (0..scalar(@{$l})-1) - {$cmp = $l->[$i] <=> $r->[$i]; last if $cmp} - return $cmp; -} - -# -# Negate a vector -# -sub neg { - my $self = shift; $self = promote(@_) unless isVector($self); - my @coords = (); - foreach my $x ($self->value) {push(@coords,-$x)} - return $self->make(@coords); -} - -# -# Get the length of a vector -# -sub abs {(shift)->norm} -sub norm { - my $self = shift; $self = promote($self,@_) unless isVector($self); - my $s = 0; - foreach my $x ($self->value) {$s += $x*$x} - return CORE::sqrt($s); -} - -# -# Produce a unit vector in the same direction as the given -# vector (or return the zero vector if the original is the zero -# vector). -# -sub unit { - my $self = shift; $self = promote($self,@_) unless isVector($self); - my $s = $self->norm; $s = 1/$s unless $s == 0; - return $self->mult($s); -} - -# -# Check if two vectors are parallel -# -sub areParallel { - my $sameDirection = $_[2]; - my @u = (promote($_[0]))->value; - my @v = (promote($_[1]))->value; - if (scalar(@u) != scalar(@v)) {return 0} - my $epsilon = 1E-6; # tolerance for equal to zero - my $k = ''; # scaling factor for u = k v - foreach $i (0..$#u) { - if ($k ne '') { - return 0 if (CORE::abs($v[$i] - $k*$u[$i]) >= $epsilon); - } else { - # - # if one is zero and the other isn't then not parallel - # otherwise use the ratio of the two as k - # - if (CORE::abs($u[$i]) < $epsilon) { - return 0 if (CORE::abs($v[$i]) >= $epsilon); - } else { - return 0 if (CORE::abs($v[$i]) < $epsilon); - $k = $v[$i]/$u[$i]; - return 0 if $k < 0 && $sameDirection; - } - } - } - # - # Note: it will return 1 if both are zero vectors. This is a - # feature, since one is provided by the problem writer, and he - # should only supply the zero vector if he means it. One could - # return ($k ne '') to return 0 if both are zero. - # - return 1; -} - -################################################## -# -# Produce the various output formats -# -# ->string gets a printable string (for output) -# ->answer gets a sting for the answer checkers -# (currently just calls string) -# ->TeX produces TeX commands suitable for use within \( \). -# ->ijk produces TeX version of vector using i,j, and k -# vectors (must include the vectorUtils.pl file -# in order to use this, or define $i, $j and $k -# to be the TeX values you want to use for these). -# - -sub string { - my $self = shift; my $paren = $parens{$self->type}; - my @p = (); - foreach my $x ($self->value) { - if (isNumber($x)) {push(@p,sprintf($numberFormat,$x))} else {push(@p,$x)} - } - $paren->{open}.join(',',@p).$paren->{close}; -} - -sub answer {(shift)->string(@_)} - -sub TeX { - my $self = shift; my $paren = $parens{$self->type}; - my $open = $paren->{TeX_open} || '\left'.$paren->{open}; - my $close = $paren->{TeX_close} || '\right'.$paren->{close}; - my @p = (); - foreach my $x ($self->value) { - if (isNumber($x)) {push(@p,sprintf($numberFormat,$x))} else {push(@p,$x)} - } - $open.join(',',@p).$close -} - -sub ijk { - my $self = shift; my @p = (); - foreach my $x ($self->value) { - if (isNumber($x)) {push(@p,sprintf($numberFormat,$x))} - else {push(@p,'('.$x.')')} - } - "$p[0]$main::i + $p[1]$main::j + $p[2]$main::k"; -} - -################################################## - -# -# Set the number output format to the given sprintf string. -# E.g., '%s' would give the full precision of the number. -# -# Default: '%.6g' -# -sub numberFormat {shift; $numberFormat = shift} - -########################################################################### -########################################################################### - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/variableAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/variableAnswer.pl deleted file mode 100755 index 88f68bd8bc..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/variableAnswer.pl +++ /dev/null @@ -1,94 +0,0 @@ -sub _variableAnswer_init {}; # don't reload this file - -################################################## -# -# A match on a collection of comma- or space-separated -# strings, optionally enclosed in parentheses. -# The match can be case insensitive, and order insensitive. -# -# Usage: variable_cmp(ans,options) -# -# where ans is the correct answer string (or an array of the answers, -# which will be made into a comma-separated string), and options -# are taken from: -# -# ignore_case => 0 or 1 determines wether upper- and -# lower-case are to be distinguished or not -# (Default: 0) -# -# ignore_order => 0 or 1 determines whether the order of the -# answers matters or not -# (Default: 0) -# -# allow_parens => 0 or 1 determines whether parens are stripped -# from around the answer automatically -# (Default: 1) -# -# force_parens => 0 or 1 determines whether parens are -# required in the student's answer -# (Default: 0) - -sub variable_cmp { - my $answer = shift || ''; - $answer = join(',',@{$answer}) if ref($answer) eq 'ARRAY'; - $answer = trimString($answer); - my %params = @_; - set_default_options(\%params, - ignore_case => 0, - ignore_order => 0, - allow_parens => 1, - force_parens => 0, - ); - $answer = '('.$answer.')' if $params{force_parens} && $answer !~ m/^\(.*\)$/; - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug} || 0; - $answerEvaluator->ans_hash(type => "variable", correct_ans => $answer); - $answerEvaluator->install_evaluator(\&variable_cmp_check,%params); - return $answerEvaluator; -} - -# -# The guts of the checker -# -sub variable_cmp_check { - my $ans = shift; - my %option = @_; - my $student_ans = $ans->{student_ans}; - my $correct_ans = $ans->{correct_ans}; - $student_ans = trimString($student_ans); - $ans->setKeys( - student_ans => $student_ans, - original_student_ans => $student_ans, - preview_text_string => $student_ans, - preview_latex_string => $student_ans, - ans_message => '' - ); - if ($option{force_parens}) { - if ($student_ans !~ m/^\(.*\)$/) { - $ans->{ans_message} = "Your answer isn't enclosed in parentheses"; - $ans->{error} = 1; $ans->score(0); return $ans; - } - $option{allow_parens} = 1; - } - if ($option{allow_parens}) { - $student_ans =~ s/^\((.*)\)$/$1/; $correct_ans =~ s/^\((.*)\)$/$1/; - if ($student_ans =~ m/^\(|\)$/) { - $ans->{ans_message} = "Incorrect parentheses"; - $ans->{error} = 1; $ans->score(0); return $ans; - } - } - $student_ans =~ s/,/ /g; $correct_ans =~ s/,/ /g; - $student_ans =~ s/\s+/ /g; $correct_ans =~ s/\s+/ /g; - if ($option{ignore_case}) { - $student_ans = lc($student_ans); - $correct_ans = lc($correct_ans); - } - if ($option{ignore_order}) { - $student_ans = join(' ',lex_sort(split(' ',$student_ans))); - $correct_ans = join(' ',lex_sort(split(' ',$correct_ans))); - } - $ans->score($student_ans eq $correct_ans); - return $ans; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/vectorAnswer.pl b/OpenProblemLibrary/macros/Union/obsolete/vectorAnswer.pl deleted file mode 100755 index f08a9dec80..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/vectorAnswer.pl +++ /dev/null @@ -1,11 +0,0 @@ -loadMacros("pointAnswer.pl","vectorUtils.pl"); - -# -# Just a renaming of the point comparisons. -# -sub num_vector_cmp {num_point_cmp(@_)} -sub fun_vector_cmp {fun_point_cmp(@_)} -sub std_vector_cmp {std_point_cmp(@_)} -sub vector_cmp {point_cmp(@_)} - -1; diff --git a/OpenProblemLibrary/macros/Union/obsolete/vectorUtils.pl b/OpenProblemLibrary/macros/Union/obsolete/vectorUtils.pl deleted file mode 100755 index f537c5bca4..0000000000 --- a/OpenProblemLibrary/macros/Union/obsolete/vectorUtils.pl +++ /dev/null @@ -1,121 +0,0 @@ -##################################################################### -# -# Some utility routines that are useful in vector problems -# - -sub _vectorUtils_init {}; # don't reload this file - -################################################## - -# -# formats a vector name (should be used in math mode) -# -# Vectors will be in bold italics in HTML modes, and -# will be overlined in TeX modes. (Bold italic could also work in -# TeX modes, but the low resolution on screen made it less easy -# to distinguish the difference between bold and regular letters.) -# -sub Overline { - my $v = shift; - my $HTML = ''.$v.''; - MODES( - TeX => "\\overline{$v}", - HTML => $HTML, - HTML_tth => '\begin{rawhtml}'.$HTML.'\end{rawhtml}', - HTML_dpng => "\\overline{$v}", - ); -} - -# -# This gets a bold letter in TeX as well as HTML modes. -# Although \boldsymbol{} works fine on screen in latex2html mode, -# the PDF file produces non-bold letters. I haven't been able to -# track this down, so used \mathbf{} in TeX mode, which produces -# roman bold, not math-italic bold. -# -sub BoldMath { - my $v = shift; - my $HTML = ''.$v.''; - MODES( - TeX => "\\boldsymbol{$v}", # doesn't seem to work in TeX mode -# TeX => "\\mathbf{$v}", # gives non-italic bold in TeX mode - Latex2HTML => "\\boldsymbol{$v}", - HTML => $HTML, - HTML_tth => '\begin{rawhtml}'.$HTML.'\end{rawhtml}', - HTML_dpng => "\\boldsymbol{$v}", - ); -} - -# -# The coordinate unit vectors -# -#if ($main::displayMode eq "TeX") { - $i = BoldMath('i'); - $j = BoldMath('j'); - $k = BoldMath('k'); -#} else { -# $i = Overline(MODES(TeX=>'\imath',HTML=>'i')); -# $j = Overline(MODES(TeX=>'\jmath',HTML=>'j')); -# $k = Overline('k'); -#} - -# -# Grad sumbol -# -$GRAD = '\nabla '; - -# -# Create a non-zero point with the given number of coordinates -# with the given random range (which defaults to (-5,5,1)). -# -# non_zero_point(n,a,b,c) -# -sub non_zero_point { - my $n = shift; my $k = $n; my @v = (); - my $a = shift || -5; my $b = shift || $a + 10; my $c = shift || 1; - while ($k--) {push(@v,random($a,$b,$c))} - if (Norm(@v) == 0) {$v[random(0,$n-1,1)] = non_zero_random($a,$b,$c)} - return Point(@v); -} -sub non_zero_point2D {non_zero_point(2,@_)} -sub non_zero_point3D {non_zero_point(3,@_)} - -# -# Same but for Vectors -# -sub non_zero_vector {Vector(non_zero_point(@_))} -sub non_zero_vector2D {non_zero_vector(2,@_)} -sub non_zero_vector3D {non_zero_vector(3,@_)} - -# -# Form the string for a line given its point and vector -# -# Usage: line(P,V); or line(P,V,'t'); -# -# where P is the point and V the direction vector for the line, and -# t is the variable to use (default is 't'). -# -# Ex: Line([1,-3],[2,1]) produces Vectir("1+2t","-3+t"). -# Ex: Line(Point(1,-3),Vector(2,1)) produces Vector("1+2t","-3+t"). -# -sub Line { - my @p = Point(shift)->value; my @v = Vector(shift)->value; - my $t = shift; $t = 't' unless $t; - my @coords = (); - if ($#p != $#v) {die "Dimensions of point and vector don't match"} - foreach my $i (0..$#p) {push(@coords,FPOLY("$p[$i]+$v[$i]$t",$t))} - return Vector(@coords); -} - -# -# Creates a displayable string for a plane given its -# normal vector and a point on the plane. -# -# Usage: plane(N,P); -# -sub Plane { - my $N = Vector(shift); my $P = Point(shift); my @N = $N->value; - FPOLY("$N[0] x + $N[1] y + $N[2] z = ".vDot($N,$P)); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/piecewiseFunctions.pl b/OpenProblemLibrary/macros/Union/piecewiseFunctions.pl deleted file mode 100755 index 3a903640d0..0000000000 --- a/OpenProblemLibrary/macros/Union/piecewiseFunctions.pl +++ /dev/null @@ -1,97 +0,0 @@ -#! /usr/local/bin/perl - -sub _piecewiseFunctions_init {}; # don't reload this file - -# -# Implements a method of producing piecewise-defined functions that -# works in all modes (TeX, Latex2HTML and HTML). -# - -################################################## -# -# piecewiseFunction(name, [part1, part1-x, ...], options) -# -# Here, "name" is a string like "f(x)" that is the name of the -# function, -# -# "part1" is the function's definition for piece one, e.g. "x+4". -# (It is put in math mode automatically.) -# -# "part1-x" is the function's domain description, e.g., "if \(x $LTS 3\)". -# (It is NOT in math mode automatically.) -# -# You can supply as many partN,partN-x pairs as needed. -# -# The options are: -# -# bracesize => n specifies height of braces for HTML modes -# spacing => n gives row spacing for HTML -# -sub piecewiseFunction { - my $f = shift; - my $arrayref = shift; my @array = @{$arrayref}; - my %options = @_; - set_default_options(\%options, bracesize => 0, spacing => 5); - my ($size,$sep) = ($options{bracesize},$options{spacing}); - my ($output,$fx,$ifx) = ('','',''); - - if ($displayMode eq 'HTML' || $displayMode eq 'HTML_tth') { - # - # A hack to handle piecewise functions in HTML - # - $output = - '
    -
    - ' . - "\n\n". - ' - \n
    ' . "\\($f\\) = " . - '$braceHTML[$size]'."\n"; - while (@array) { - $fx = shift(@array); $ifx = shift(@array); - $output .= ''."\n".'\n"; - $output .= '' if (@array); - } - $output .= "
    ' . DMATH($fx) . - ' ' . $ifx . "
    \n"; - } elsif ($displayMode =~ m/^HTML/ || - $displayMode eq "Latex2HTML" || $displayMode eq "TeX") { - $output = '\[' . $f . '= \begin{cases}'; - while (@array) { - $fx = shift(@array); $ifx = shift(@array); - $ifx =~ s/\\\(/\}/g; $ifx =~ s/\\\)/\\text\{/g; - $output .= $fx . '&\text{' . $ifx . '}\\\\' . "\n"; - } - $output =~ s/\\text\{\}//g; $output =~ s/\\\\$//; - $output .= '\end{cases}\]'; - } else { - warn "piecewiseFunction: Unknown display mode: $displayMode" - } - return($output); -} - -# -# Characters that make up the brace in the symbol font -# -($brt,$brm,$brb,$brc) = ('','','',''); - -# -# Braces of different sizes (an alternative would be to -# add $brc characters to extend the vertical parts). -# If you want additional sizes, you can push more onto the end -# of the array from within the .pg file, or add them here. -# -# -@braceHTML = ( - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    }, - qq{$brt
    $brm
    $brb
    } -); - -1; diff --git a/OpenProblemLibrary/macros/Union/unionImage.pl b/OpenProblemLibrary/macros/Union/unionImage.pl deleted file mode 100755 index 54dbb63c4f..0000000000 --- a/OpenProblemLibrary/macros/Union/unionImage.pl +++ /dev/null @@ -1,88 +0,0 @@ -loadMacros("unionMacros.pl"); - -sub _unionImage_init {}; # don't reload this file - -###################################################################### -# -# A routine to make including images easier to control -# -# Usage: Image(name,options) -# -# where name is the name of an image file or a reference to a -# graphics object (or a reference to a pair of one of these), -# and options are taken from among the following: -# -# size => [w,h] the size of the image in the HTML page -# (default is [150,150]) -# -# tex_size => r the size to use in TeX mode (as a percentage -# of the line width times 10). E.g., 500 is -# half the width, etc. (default is 200.) -# -# link => 0 or 1 whether to include a link to the original -# image (default is 0, unless there are -# two images given) -# -# border => 0 or 1 size of image border in HTML mode -# (defaults to 2 or 1 depending on whether -# there is a link or not) -# -# align => placement vertical alignment for image in HTML mode -# (default is "BOTTOM") -# -# tex_center => 0 or 1 whether to center the image horizontally -# in TeX mode (default is 0) -# -# The image name can be one of a number of different things. It can be -# the name of an image file, or an alias to one produce by the alias() -# command. It can be a graphics object reference created by init_graph(). -# Or it can be a pair of these (in square brackets). The first is the -# image for the HTML file, and the second is the image that it will be -# linked to. -# -# Examples: Image("graph.gif", size => [200,200]); -# Image(["graph.gif","graph-large.gif"]); -# -# The alias() and insertGraph() functions will be called automatically -# when needed. -# -sub Image { - my $image = shift; my $ilink; - return unless defined $image; - my %options = ( - size => [150,150], tex_size => 200, - link => 0, align => "BOTTOM", tex_center => 0, @_); - my ($w,$h) = @{$options{size}}; - my ($ratio,$link) = ($options{tex_size}*(.001),$options{link}); - my ($border,$align) = ($options{border},$options{align}); - my ($tcenter) = $options{tex_center}; - my $HTML; my $TeX; - ($image,$ilink) = @{$image} if (ref($image) eq "ARRAY"); - $ilink = $ilink//''; - $image = alias(insertGraph($image)) if (ref($image) eq "WWPlot"); - $image = alias($image) unless ($image =~ m!^(/|https?:)!i); # see note - if ($ilink) { - $ilink = alias(insertGraph($ilink)) if (ref($ilink) eq "WWPlot"); - $ilink = alias($ilink) unless ($ilink =~ m!^(/|https?:)!i); # see note - } else {$ilink = $image} - # - # Note: These cases were added to handle the examples where the - # $image tag has a full url -- in practice this arises when using lighttpd - # to server images from a different port - # e.g. http://hosted2.webwork.rochester.edu:8000/webwork2_course_files/.... - # A smarter implementation of alias might make this check unnecessary - # - $border = (($link || $ilink ne $image)? 2: 1) unless defined($border); - $HTML = ''; - $HTML = ''.$HTML.'' if $link or $ilink ne $image; - $TeX = '\includegraphics[width='.$ratio.'\linewidth]{'.$image.'}'; - $TeX = '\centerline{'.$TeX.'}' if $tcenter; - MODES( - TeX => $TeX."\n", - Latex2HTML => $bHTML.$HTML.$eHTML, - HTML => $HTML - ); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/unionInclude.pl b/OpenProblemLibrary/macros/Union/unionInclude.pl deleted file mode 100755 index 2ed83f0779..0000000000 --- a/OpenProblemLibrary/macros/Union/unionInclude.pl +++ /dev/null @@ -1,93 +0,0 @@ -###################################################################### -# -# Routines to make it easier to include additional PG files within -# a given one. These files don't have to be in the courseScripts -# directory; rather, they are assumed to be relative to the directory -# containing the calling PG file. -# - -sub _unionInclude_init {}; # don't reload this file - -###################################################################### -# -# Usage: includePGfile(name) -# -# where name is the name of a PG file (relative to the directory -# of the file containing this call). -# -sub includePGfile { - my $name = shift; - my $PGfile = $main::envir{'fileName'}; - $PGfile =~ s![^/]+$!!; $PGfile .= $name; - while ($PGfile =~ s![^/]*/../!!) {} - $PGfile =~ s!^tmpEdit/!!; -# warn("file is $PGfile, directory is $main::templateDirectory"); - my $problem = read_whole_problem_file($main::templateDirectory.$PGfile); - - my ($oldpname,$oldname) = - ($main::envir{'probFileName'},$main::envir{'fileName'}); - $main::envir{'probFileName'} = $PGfile; - $main::envir{'fileName'} = $PGfile; - includePGtext($problem,%main::envir); - ($main::envir{'probFileName'},$main::envir{'fileName'}) = - ($oldpname,$oldname); -} - -###################################################################### -# -# -# Usage: includeRandomProblem(file1,file2,...,fileN); -# -# where the fileNs are the names of the files from which -# to choose (relative to the directory of the file containing -# this call). -# -# To use this, make one PG file that include the call to this -# random routine, and then include it the set definition file -# as many times as you want (up to N times). A different problem -# will be included for each instance in the set definition file. -# -sub includeRandomProblem { - my @flist = @_; - my @shuffle = _NchooseK(scalar(@flist),scalar(@flist)); - my $start = $initialProblemNumber; $start = 1 unless defined $start; - WARN_MESSAGE("There is an error in initializing this random problem. initialProblemNumber $start is greater than the current problem number $probNum") - if $probNum-$start < 0 ; - my $n = (@shuffle)[$probNum-$start]; - includePGfile($flist[$n]); - #$main::problemPostamble->{HTML} = ""; # Hack to prevent ENDDOCUMENT from adding it again - # commenting this out is another hack which prevents nesting on quizzes -- go figure -} - -# -# Legacy code no longer needed. The included file can contain -# BEGIN_INCLUSION(); and END_INCLUSION(); in place of DOCUMENT() -# and ENDDOCUMENT(); calls. -# -sub BEGIN_INCLUSION {} -sub END_INCLUSION {} - -###################################################################### -# -# This is a service routine for includeRandomProblem() above. -# It an array of k numbers chosen from 0 to n-1, but preserves the -# random seed so that the included problem won't be affected by -# this function, and replaces it by the psvn, so that the list -# produced will be the same each time it is called. -# -sub _NchooseK { - my ($n,$k)=@_; - my @array = 0..($n-1); - my @out = (); - my $seed = ($main::psvn || 23)*101 + ($initialProblemNumber || 1); - my $oldseed = $main::PG_random_generator->{seed}; - $main::PG_random_generator->srand($seed); - while (@out<$k) { - push(@out, - splice(@array,$main::PG_random_generator->random(0,$#array,1),1)); - } - $main::PG_random_generator->srand($oldseed); - return @out; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/unionMacros.pl b/OpenProblemLibrary/macros/Union/unionMacros.pl deleted file mode 100755 index 9bdf44a38b..0000000000 --- a/OpenProblemLibrary/macros/Union/unionMacros.pl +++ /dev/null @@ -1,162 +0,0 @@ -###################################################################### -# -# Some macros that add to the ones like $PAR, $BR, etc. -# - -sub _unionMacros_init {}; # don't reload this file - -# -# Shorthand for WeBWorK -# -$WW = "WeBWorK"; - -# -# Shorthands for entering and leaving rawhtml mode in -# LaTeX2HTML (since they are so commonly used). -# -$bHTML = '\begin{rawhtml}'; -$eHTML = '\end{rawhtml}'; - - -# -# HTML(htmlcode) -# HTML(htmlcode,texcode) -# -# Insert $html in HTML mode or \begin{rawhtml}$html\end{rawhtml} in -# Latex2HTML mode. In TeX mode, insert nothing for the first form, and -# $tex for the second form. -# -sub HTML { - my ($html,$tex) = @_; - return('') unless (defined($html) && $html ne ''); - $tex = '' unless (defined($tex)); - MODES(TeX => $tex, Latex2HTML => $bHTML.$html.$eHTML, HTML => $html); -} - - -# -# Begin and end indented text -# - -$BBLOCKQUOTE = HTML('
    ','\par\bgroup\advance\leftskip by 2em '); -$EBLOCKQUOTE = HTML('
    ','\par\egroup '); - -# -# Start and stop centering -# -$BCENTER = HTML('
    ','\begin{center}'); -$ECENTER = HTML('
    ','\end{center}'); - - -# -# Begin and end mode -# -$BTT = HTML('','\texttt{'); -$ETT = HTML('','}'); - -# -# Begin and end mode -# -$BSMALL = HTML('','{\small '); -$ESMALL = HTML('','}'); - -# -# Remove extra space in bold in latex2html mode -# -$BBOLD = HTML('','{\bf '); -$EBOLD = HTML('','}'); - -# -# tth doesn't seem to understand \colon -# -$COLON = MODES(TeX=>'\colon ',HTML=>':', HTML_dpng => '\colon '); - -# -# Alternatives to the standard WW versions of these -# -$LT = $LTS; -$GT = $GTS; - -$LE = $LTE; -$GE = $GTE; - -# -# Common math sets -# -$R = MODES(TeX => '{\bf R}', HTML_tth => '{\bf R}', HTML => 'R'); -$Z = MODES(TeX => '{\bf Z}', HTML_tth => '{\bf Z}', HTML => 'Z'); -$N = MODES(TeX => '{\bf N}', HTML_tth => '{\bf N}', HTML => 'N'); -$Q = MODES(TeX => '{\bf Q}', HTML_tth => '{\bf Q}', HTML => 'Q'); -$C = MODES(TeX => '{\bf C}', HTML_tth => '{\bf C}', HTML => 'C'); - -# -# Superscripts and subscript (mostly for if you want answer -# rules in these positions). -# -$BSUP = HTML('','$^{'); -$ESUP = HTML('','}$'); - -$BSUB = HTML('','$_{'); -$ESUB = HTML('','}$'); - -# -# Browser-only BR -# -$BBR = HTML('
    '); - -# -# Broser-only \displaystyle -# -$DISPLAY = MODES(TeX => '', Latex2HTML => '\displaystyle ', - HTML_tth => '\displaystyle ', HTML => ''); - -# -# Provides a title to the problem -# -sub Title { - my $title = shift; - - TEXT(MODES( - TeX => "\\par\\begin{centering}{\\bf $title}\\par\\end{centering}\\nobreak\n", - Latex2HTML => $bHTML.'

    '.$title.'

    '.$eHTML, - HTML => '

    '.$title.'

    ' - )); -} - - -# -# A warning that we are using javaScript -# -sub JAVASCRIPT_PROBLEM { - TEXT(HTML( - '' - )); -} - - -# -# Modify a polynomial to remove coeficients of 1, -1 and 0 -# The polynomial can be a multivariable one. The parameters -# following the formula itself are the names of the variables -# for the formula. Any number can be provided, and the default -# is x, y, and z. Variable names must be one character long, -# and the formula really should be a polynomial, as the algorithm -# for removing coefficients of 0 relies on that in an important way. -# -sub FPOLY { - my $poly = shift; $poly = FEQ($poly); - @_ = ('x','y','z') unless scalar(@_) > 0; - my $x = '['.join('',@_).']'; - $poly =~ s/(^|[^\d.])1\s*(\*\s*)?($x)/$1$3/g; - $poly =~ s/-1\s*($x)/-$1/g; - $poly =~ s/(^|\+|\-)\s*0(\s*(\*\s*)?$x((\^|\*\*)\d+(\.\d*)?)?)+//g; - $poly =~ s/(^|\+|\-)\s*0+\s*([-+]|$)/$2/; - $poly =~ s/^\s*\+\s*//; - $poly = "0" if ($poly eq ""); - return $poly; -} - -1; diff --git a/OpenProblemLibrary/macros/Union/unionMessages.pl b/OpenProblemLibrary/macros/Union/unionMessages.pl deleted file mode 100755 index c594b3423f..0000000000 --- a/OpenProblemLibrary/macros/Union/unionMessages.pl +++ /dev/null @@ -1,72 +0,0 @@ -sub _unionMessages_init {}; - -loadMacros("unionMacros.pl"); - -###################################################################### -# -# How to say "infinity" -# - -$INFINITY_WORD = $$Value::context->flag('infiniteWord') unless defined($INFINITY_WORD); - -###################################################################### -# -# A message that can be included (within BEGIN_TEXT and END_TEXT) -# to tell the student how to enter infinity and minus infinity. -# -$INFINITY_MESSAGE = - $BITALIC.$BSMALL. - "Use ${LQ}$INFINITY_WORD${RQ} for $LQ\\(\\infty\\)$RQ ". - "and ${LQ}-$INFINITY_WORD${RQ} for $LQ\\(-\\infty\\)$RQ." . - $ESMALL.$EITALIC; - -$INFINITY_MESSAGE = "" if $displayMode eq 'TeX'; - -###################################################################### -# -# A message to tell students how to enter "Does not exist". -# -$DNE_MESSAGE = - $BITALIC.$BSMALL. - "Use ${LQ}DNE${RQ} for ${LQ}Does not exist${RQ}.". - $ESMALL.$EITALIC; - -$DNE_MESSAGE = "" if $displayMode eq 'TeX'; - -###################################################################### -# -# A message to tell students how to enter unions and infinities -# -$INFINITY_UNION_MESSAGE = - "${BSMALL}${BBOLD}Note:${EBOLD} ". - "If the answer includes more than one interval, write the intervals ". - "separated by the ${LQ}union$RQ symbol, ${BITALIC}U${EITALIC}. If needed, enter ". - "\\(\\infty\\) as ${BITALIC}$INFINITY_WORD${EITALIC} and \\(-\\infty\\) as ". - "${BITALIC}-$INFINITY_WORD${EITALIC}.${ESMALL}"; - -$INFINITY_UNION_MESSAGE = "" if $displayMode eq 'TeX'; - -###################################################################### -# -# A message to tell students how to enter lists of intervals -# -$INTERVAL_LIST_MESSAGE = - $BSMALL.$BBOLD."Note:".$EBOLD. - "Your answer should be a list of one or more intervals separated by commas. ". - "Enter ${BITALIC}NONE${EITALIC} if there are no intervals."; - -$INTERVAL_LIST_MESSAGE = "" if $displayMode eq 'TeX'; - -###################################################################### -# -# A message for lists of unions -# -$UNION_LIST_MESSAGE = - $BSMALL.$BBOLD."Note:".$EBOLD. - "Your answer should be a list of one or more intervals or unions of intervals. ". - "Separate the entries in your list by commas. ". - "Enter ${BITALIC}NONE${EITALIC} if there are no intervals or unions."; - -$UNION_LIST_MESSAGE = "" if $displayMode eq 'TeX'; - -1; diff --git a/OpenProblemLibrary/macros/Union/unionProblem.pl b/OpenProblemLibrary/macros/Union/unionProblem.pl deleted file mode 100755 index 08479d3fbd..0000000000 --- a/OpenProblemLibrary/macros/Union/unionProblem.pl +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/local/bin/perl - -sub _unionProblem_init {}; - -################################################## -# -# No longer needed since WW can output the grey -# box automatically by changes in global.conf -# -sub BEGIN_PROBLEM {} -sub END_PROBLEM {} - -sub problem_NoTable {} - - -1; diff --git a/OpenProblemLibrary/macros/Union/unionUtils.pl b/OpenProblemLibrary/macros/Union/unionUtils.pl deleted file mode 100755 index 6a8a93da67..0000000000 --- a/OpenProblemLibrary/macros/Union/unionUtils.pl +++ /dev/null @@ -1,131 +0,0 @@ -###################################################################### -## -## These are some miscellaneous routines that may be useful. -## - -sub _unionUtils_init {}; # don't reload this file - -# -# Remove leading and trailing spaces -# -sub trimString { - my $s = shift; - $s =~ s/^\s+|\s+$//g; - return $s; -} - -# -# Check if a string is a number -# -sub isNumber { - my $n = shift; - $n = ($n =~ m/^[+-]?(\d+(\.\d*)?|\.\d+)([Ee][-+]?\d+)?$/); - return $n; -} - -# -# names for numbers -# -sub NameForNumber { - my $n = shift; - my $name = ('zeroth','first','second','third','fourth','fifth', - 'sixth','seventh','eighth','ninth','tenth')[$n]; - $name = "$n-th" if ($n > 10); - return $name; -} - - -# -# A debugging routine that allows the "warn" function to print a variable that -# contains HTML code properly -# -sub showHTML { - my $string = shift; - $string =~ s/&/\&/g; - $string =~ s//\>/g; - $string; -} - -# -# Make a named subroutine that returns the value of a function. If -# no parameters are passed to the function, the function's string -# is returned. (This will be obsolete when I finish the -# new expression parser -- DPVC.) -# -sub perlFunction { - my $def = shift; my $debug = shift; - if ($def !~ m/^\s*(\w+)\s*\(\s*((\w+\s*,?\s*)+)\s*\)\s*:?=\s*(.*)$/) { - warn "perlFunction: can't parse '$def'"; - return - } - my ($name,$vars,$expr) = ($1,$2,$4); - my $f = $expr; - my @X; my $x; $vars =~ s/\s//g; - foreach $x (split(',',$vars)) { - push(@X,"\$$x"); - $expr =~ s/(\b|\d)$x\b/$1(\$$x)/g; - } - # - # Fix up the expression - # - $expr =~ s/\bpi\b/(4*atan2(1,1))/g; - $expr =~ s/\be\b/(exp(1))/g; - $expr =~ s!\\frac\{([^\}]*)\}\s*\{([^\}]*)\}!($1)/($2)!g; # fractions - $expr =~ s/\{([^\}]*)\}/($1)/g; # TeX parameters - $expr =~ s/\\//g; # TeX slashes - $expr =~ s/\^/**/g; # change ^ to ** - # - # Do implied multiplication - # - $expr =~ s/\)\s*\(/\)*\(/g; - $expr =~ s/\)\s*([a-zA-Z0-9.])/\)*$1/g; - $expr =~ s/([\d.])(\s\d)/$1*$2/g; - $expr =~ s/([\d.])\s*([a-zA-Z\(])/$1*$2/g; - # - # Fix +-, -+, ++ and -- - # - $expr =~ s/\+\s*([\+-])/$1/g; - $expr =~ s/-\s*\+/-/g; $expr =~ s/-\s*-/+/g; - # - # Create the named function - # - PG_restricted_eval("sub $name { return('$f') unless (\@_); - my (".join(',',@X).") = \@_; $expr}"); - warn("sub $name { my (".join(',',@X).") = \@_; $expr}") if $debug; -} - -# -# A hack to pick display mode in HTML modes, but plain math mode -# in TeX modes. This makes fractions appear better in HTML mode, -# without making it worse in TeX modes. This is for use with -# AlignedList objects. -# -sub DMATH { - my $math = shift; - # - # HTML_tth puts an unwanted
    at the beginning, - # and uses a centered table. So here we remove the - #
    and re-align the table to the right. - # - if ($displayMode eq "HTML_tth") { - $math = trimString(EV2('\['.$math.'\]')); - $math =~ s!
    !!; - $math =~ s!table align="center"!table align="right"!; - $math =~ s!(table [^>]*) width="100%"!$1!; - } elsif ($displayMode eq "HTML") { - $math = '\('.$math.'\)' - } elsif ($displayMode =~ m/^HTML/) { - $math = '\(\displaystyle '.$math.'\)' - } - - MODES( - TeX => '\(\displaystyle '.$math.'\)', - Latex2HTML => '\(\displaystyle '.$math.'\)', - HTML => $math, - HTML_tth => $math, - HTML_dpng => $math, - ); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/unorderedAnswer.pl b/OpenProblemLibrary/macros/Union/unorderedAnswer.pl deleted file mode 100755 index d174b8f6fd..0000000000 --- a/OpenProblemLibrary/macros/Union/unorderedAnswer.pl +++ /dev/null @@ -1,163 +0,0 @@ -########################################################################## -########################################################################## -## -## Routines for groups of answer blanks where the user can enter -## answers in any order in the blanks. -## - -loadMacros("answerUtils.pl"); - -sub _unorderedAnswer_init {}; # don't reload this file - -########################################################################## -# -# Collect a group of answer checkers for use with answers that can be given -# in any order. If N answer checkers are given, then the last N answer -# rules will be used. It is beter to use named rules and UNORDERED_NAMED_ANS -# below. Otherwise, be sure to use UNORDERED_ANS right after the answer -# rules for the answers you want to compare. -# -# Format: -# -# UNORDERED_ANS(checker1, checker2, ...); -# -# Example: -# -# BEGIN_TEXT -# The function \(f(x) = \frac{1}{x^2-$a}\) is defined except -# for \(x =\) \{ans_rule(10)\} and \(x =\) \{ans_rule(10)\}. -# END_TEXT -# -# UNORDERED_ANS(std_num_cmp(sqrt($a)), std_num_cmp(-sqrt($a))); -# -# (the student can enter the solutions in either order.) -# -sub UNORDERED_ANS { - my @cmp = @_; my @params = (); my $i; my $n = scalar(@cmp); - # - # The best thing would be to use the size of @PG_ANSWERS in place of - # $main::ans_rule_count, but we don't have access to that - # - my $num_of_answers = (keys %{$PG->{PG_ANSWERS_HASH}} ); - foreach $i (1..$n) - {push(@params,ANS_NUM_TO_NAME($i+$num_of_answers -$n),$cmp[$i-1])} - my @results = unordered_answer_list(@params); - while (scalar(@results) > 0) {shift(@results), ANS(shift(@results))} -} - -########################################################################## -# -# Collect a group of answer checkers for use with named answers that -# can be given in any order. -# -# Format: -# -# UNORDERED_NAMED_ANS(name1 => checker1, name2 => checker2, ...); -# -# Example: -# -# BEGIN_TEXT -# The function \(f(x) = \frac{1}{x^2-$a}\) is defined except -# for \(x =\) \{NAMED_ANS_RULE(A1,10)\} -# and \(x =\) \{NAMED_ANS_RULE(A2,10)\}. -# END_TEXT -# -# UNORDERED_NAMED_ANS( -# A1 => std_num_cmp(sqrt($a)), -# A2 => std_num_cmp(-sqrt($a)) -# ); -# -# (the student can enter the solutions in either blank.) -# -sub UNORDERED_NAMED_ANS { - NAMED_ANS(unordered_answer_list(@_)); -} - -########################################################################## -# -# Low-level routine for handling unordered collections of answer checkers -# -sub unordered_answer_list { - my %params = @_; my @args = @_; - my (@cmp,@ids); - while (scalar(@_) > 0) {push(@ids,shift); push(@cmp,shift)} - # - # Setup - # - my ($i,$j,$k); - my $n = scalar(@cmp); - my @cmpi = (0..$n-1); - my @skipped = (); - # - # Check that the answers exist (otherwise it's our first time through) - # - foreach $i (@ids) {return(@args) if (!defined($inputs_ref->{$i}))} - # - # Check each answer against the available answer checkers. - # Keep track of the ones that match and that don't. - # - ANSWER: foreach $i (0..$n-1) { - $k = 0; - foreach $j (@cmpi) { - if (isCorrectAnswer($cmp[$j],$inputs_ref->{$ids[$i]})) - {$ans[$i] = $j; splice(@cmpi,$k,1); next ANSWER} - $k++; - } - push(@skipped,$i); - } - # - # Check if the unmatched checkers are all blank checkers. - # If so, let them report blanks as correct answers. - # - my $blankOK = 1; - foreach $i (@cmpi) { - if (ref($cmp[$i]) ne "AnswerEvaluator" || - ($cmp[$i]->rh_ans)->{type} ne "blank") {$blankOK = 0; last} - } - if ($blankOK) {foreach $i (@cmpi) {($cmp[$i]->rh_ans)->{blankOK} = 1}} - # - # Assign the unmatching answers to umatched checkers - # - foreach $i (0..scalar(@skipped)-1) {$ans[$skipped[$i]] = $cmpi[$i]} - # - # Make the final list of answer checkers in their proper order - # - my (@list) = (); - foreach $i (0..$n-1) - {clearEvaluator($cmp[$ans[$i]]); push(@list,$ids[$i],$cmp[$ans[$i]])} - return (@list); -} - -################################################## -# -# AnswerChecker that allows a blank answer in a collection of unordered -# answer checkers. It will return "correct" for a blank answer only if -# all the other answers are correct. (The blankOK value is set by -# unordered_answer_list when this is true.) This lets you ask a question -# where the number of answers is not known (by the student) ahead of time. -# -sub blank_cmp { - my %params = @_; - $params{debug} = 0 unless defined($params{debug}); - my $answerEvaluator = new AnswerEvaluator; - $answerEvaluator->{debug} = $params{debug}; - $answerEvaluator->ans_hash( - type => "blank", blankOK => 0, - correct_ans => '', weight => 0 - ); - $answerEvaluator->install_pre_filter('reset'); # remove the blank filter - $answerEvaluator->install_evaluator(\&blank_cmp_check,%params); - $answerEvaluator->install_post_filter('reset'); # remove the blank filter - return $answerEvaluator; -} - -sub blank_cmp_check { - my $ans = shift; - my %params = @_; - $ans->{student_ans} = trimString($ans->{student_ans}); - if ($ans->{student_ans} eq "") {$ans->score($ans->{blankOK})} - else {$ans->score(0)} - return($ans); -} - -1; diff --git a/OpenProblemLibrary/macros/Union/weightedGrader.pl b/OpenProblemLibrary/macros/Union/weightedGrader.pl deleted file mode 100755 index e294444051..0000000000 --- a/OpenProblemLibrary/macros/Union/weightedGrader.pl +++ /dev/null @@ -1,331 +0,0 @@ -loadMacros('unionUtils.pl'); - -sub _weightedGrader_init {}; # don't reload this file - -###################################################################### -# -# A weighted grader that allows you to assign arbitrary percentages -# to the various answers in a problem. It also allows you to indicate -# that answering one part correctly will give you credit for some -# other part(s). This way, if there are several parts leading up to -# a "goal" answer, and the student produces the goal answer by -# some other means, he can be given full credit for the problem anyway. -# -# -# WEIGHTED ANSWERS: -# -# Each problem is assigned a weight (the default is 1). The -# student's score is then the sum of the weights for his correct -# answers divided by the total of the weights for all answers. (To -# assign weights as percentages, use integers that add up to 100, eg, -# use 40 and 60 for the weights for two answers.) -# -# There are two ways to assign weights. The first is to use the -# WEIGHTED_ANS() routine (in place of ANS) to give an answer checker -# plus a weight. -# -# Example: -# -# WEIGHTED_ANS(std_num_cmp($ans1),2); -# -# This assigns a weight of 2 to the corresponding numeric answer. -# -# As with ANS(), WEIGHTED_ANS() can take more than one answer checker and -# weight. -# -# Example: -# -# WEIGHTED_ANS( -# std_num_cmp($ans1), 40, -# std_num_cmp($ans2), 60 -# ); -# -# This assigns 40% to the first answer and 60% to the second answer. -# -# The second way of assigning a weight is through the weight_ans() -# function. This takes a single answer checker and weight and returns -# a new answer checker of the same type that has the desired weight. -# Thus -# -# ANS(weight_ans(std_num_cmp($ans1),2)); -# -# is equivalent to the first example above. -# -# The main purpose for weighted_ans() is so that weights can be used -# with UNORDERED_ANS(), or in other places where you want to use the -# weighted answer checker directly. For example: -# -# UNORDERED_ANS( -# weight_ans(std_num_cmp($ans1),40), -# weight_ans(std_num_cmp($ans2),60), -# ); -# -# produces two answers whose order doesn't matter, but the student -# will get 40% for getting $ans1 and 60% for getting $ans2 (no matter -# what order they appear in). -# -# Note that the blank_cmp() answer checker has a weight of 0 by -# default. You can override this using weight_ans(); for example, -# weight_ans(blank_cmp(),1) makes the blank count the same as all -# the other answers. (If there are two or more non-blank answers, -# then having the blanks with weight 0 will allow the observant -# student to deduce the number of blank answers from the percentage -# for a single correct answer, provided all the non-blank answers are -# equally weighted). -# -# Once you have given weights to the answers, you also need to -# install the weighted grader. Do this using the command -# -# install_weighted_grader(); -# -# -# HAVING ONE ANSWER PROVIDE CREDIT FOR ANOTHER: -# -# You may want to have a correct answer for one problem automatically -# give credit for one or more other parts of the problem. For example -# If several parts are used to lead up to the "real" answer to the -# problem, and the student produces that final answer without doing -# the intermediate parts (perhaps using some other method), then you -# may want to give the student full credit for the problem anyway. -# You can do so in the following way. -# -# First, let us call the final answer the "goal" answer, and the -# answer that it would give automatic credit for the "optional" answer. -# -# The optional answer blank must be a named answer, e.g., -# -# BEGIN_TEXT -# You get credit for this answer: \{NAMED_ANS_RULE('optional',10)\} -# When you answer this one: \{ans_rule(10)\} -# END_TEXT -# NAMED_ANS('optional',std_num_cmp(5)); -# -# Then for the goal answer, in place of ANS, use CREDIT_ANS, listing the -# optional answer as the second argument: -# -# CREDIT_ANS(std_num_cmp(10),'optional'); -# -# You could also use NAMED_WEIGHTED_ANS for the optional answer, and -# supply a third argument for CREDIT_ANS, which is the weight for the -# goal answer. For example: -# -# NAMED_WEIGHTED_ANS('optional',std_num_cmp(5),20); -# CREDIT_ANS(std_num_cmp(10),'optional',80); -# -# This way, if the student gets the optional part right (but not the -# goal), he gets 20%, and if he gets the goal right, he gets 100%. -# -# One can use CREDIT_ANS to give credit for several other (named) -# answers at once by passing a list of names rather than a single one, -# as in: -# -# CREDIT_ANS(std_num_cmp(10),['optional1','optional2'],80); -# -# The weight_ans() routine, described in the section above, also can -# be used to give credit to another answer. In addition to the -# answer-checker and the weight, you can pass an answer name (or -# list of names) that should get credit when this one is right. -# For example -# -# ANS(weight_ans(std_num_cmp(10),80,'optional')); -# -# is equivalent to -# -# CREDIT_ANS(std_num_cmp(10),'optional',80); -# -# One caveat to keep in mind is that credit is given to the optional -# answer ONLY if the answer is left blank (or is actually correct). -# Credit will NOT be given if the answer is incorrect, even if the -# goal answer IS correct. -# -# When credit IS given, the blank answer is still marked as incorrect -# in the grey answer report at the top of the page, but the student -# gets awarded the points for that answer anyway (with no other -# indication). It is possible to cause the blank to be marked as -# correct, but this seemed confusing to the students. -# -# Once you have issued the various ANS calls, you also need to -# install the weighted grader. Do this using the command -# -# install_weighted_grader(); -# - -################################################## -# -# Issue ANS() calls for the weighted checkers -# -sub WEIGHTED_ANS { - my ($checker,$weight); - while (@_) { - $checker = shift; $weight = shift; - ANS(weight_ans($checker,$weight)); - } -} - -################################################## -# -# Issue NAMED_ANS() calls for the weighted checkers -# -sub NAMED_WEIGHTED_ANS { - my ($name,$checker,$weight); - while (@_) { - $name = shift; $checker = shift; $weight = shift; - NAMED_ANS($name,weight_ans($checker,$weight)); - } -} - -################################################## -# -# Issue an ANS() call for the checker, giving -# credit to the given answers. -# -sub CREDIT_ANS { - my $checker = shift; - my $credit = shift; - $credit = [$credit] if defined($credit) && ref($credit) ne "ARRAY"; - my $weight = shift; - ANS(weight_ans($checker,$weight,$credit)); -} - - -################################################## -# -# Either add a weight to an AnswerEvaluator, or return a -# new checker that adds the weight to its result. Also, -# add the "credit" field, if supplied. -# -sub weight_ans { - my $checker = shift; my $weight = shift; - my $credit = shift; - $credit = [$credit] if defined($credit) && ref($credit) ne "ARRAY"; - if (ref($checker) eq "AnswerEvaluator") { - $checker->{rh_ans}->{weight} = $weight; - $checker->{rh_ans}->{credit} = $credit if defined($credit); - return $checker; - } else { - my $newChecker = sub { - my $hash = &{$checker}(@_); - $hash->{weight} = $weight; - $checker->{rh_ans}->{credit} = $credit if defined($credit); - return $hash; - }; - return $newChecker; - } -} - - -################################################## -# -# This is the weighted grader. It uses an extra field added to the -# AnswerHash (named "weight") to tell how much weight to give each -# problem. The grader adds up the total weights for the correct -# answers. For partially correct ones, it uses the score for that -# answer to give a portion of that weight. For example, if the -# weight is 40 and the score for the answer is .5, then 20 is added -# to the total for the problem. (Note that most answer checkers only -# return 1 or 0, but they are allowed to return partial credit as -# well.) -# -# When the student's total is computed, it is divided by the sum of -# all the weights in order to determine the final score. -# -# It also uses a special field called "credit" that determines -# what other (named) answers are given full credit if the given -# answer is correct. This can be used to make "optional" answers, -# where getting the "final" answer right gives credit for the other parts. -# - -sub weighted_grader { - my $rh_evaluated_answers = shift; - my $rh_problem_state = shift; - my %form_options = @_; - my %answers = %{$rh_evaluated_answers}; - my %problem_state = %{$rh_problem_state}; - - my %problem_result = ( - score => 0, - errors => '', - type => 'weighted_grader', - msg => '', - ); - - if (scalar(keys(%answers)) == 0) { - $problem_result{msg} = "This problem did not ask any questions."; - return(\%problem_result,\%problem_state); - } - - return(\%problem_result,\%problem_state) - if (!$form_options{answers_submitted}); - - my ($score,$total) = (0,0); - my ($weight,$ans_name,$credit_name); - my (%credit); - - # - # Get the score for each answer - # (error if can't recognize the answer format). - # - foreach $ans_name (keys %answers) { - if (ref($answers{$ans_name}) =~ m/^(HASH|AnswerHash)$/) { - $credit{$ans_name} = $answers{$ans_name}->{score}; - } else { - die "Error at file ",__FILE__,"line ", __LINE__,": Answer |$ans_name| " . - "is not a hash reference\n" . $answers{$ans_name} . - "\nThis probably means that the answer evaluator for ". - "this answer is not working correctly."; - $problem_result{error} = - "Error: Answer $ans_name is not a hash: $answers{$ans_name}"; - } - } - - # - # Mark any optional answers as correct, if the goal answers - # are right and the optional ones are blank. - # - foreach $ans_name (keys %answers) { - if ($credit{$ans_name} == 1 && - defined($answers{$ans_name}->{credit})) { - foreach $credit_name (@{$answers{$ans_name}->{credit}}) { - $credit{$credit_name} = 1 - if (trimString($answers{$credit_name}->{student_ans}) eq ""); - } - } - } - - # - # Add up the weighted scores - # - foreach $ans_name (keys %answers) { - $weight = $answers{$ans_name}->{weight}; - $weight = 1 unless (defined($weight)); - $total += $weight; - $score += $weight * $credit{$ans_name}; - } - - $problem_result{score} = $score/$total if $total; - - # This gets rid of non-numeric scores - $problem_state{recorded_score} = 0 - unless (defined($problem_state{recorded_score}) && - isNumber($problem_state{recorded_score})); - - $problem_state{recorded_score} = $problem_result{score} - if ($problem_result{score} > $problem_state{recorded_score}); - - $problem_state{num_of_correct_ans}++ if ($score == $total); - $problem_state{num_of_incorrect_ans}++ if ($score < $total); - warn "Error in grading this problem: ". - "the score is larger than the total ($score > $total)" - if $score > $total; - - (\%problem_result, \%problem_state); -} - - -################################################## -# -# Syntactic sugar to avoid ugly ~~& construct in PG. -# -sub install_weighted_grader {install_problem_grader(\&weighted_grader)} - -1; diff --git a/OpenProblemLibrary/macros/WHFreeman/freemanMacros.pl b/OpenProblemLibrary/macros/WHFreeman/freemanMacros.pl deleted file mode 100755 index 9cd1443b1d..0000000000 --- a/OpenProblemLibrary/macros/WHFreeman/freemanMacros.pl +++ /dev/null @@ -1,540 +0,0 @@ -sub textbook_ref { - my ($text, $sec, $ex) = @_; -return ""; - return "$text section $sec, exercise $ex"; -} - -sub textbook_ref_corr { -return ""; - return "Similar to " . textbook_ref(@_) . "."; -} - -sub textbook_ref_exact { -return ""; - return "From " . textbook_ref(@_) . "."; -} - -sub list_random_multi_uniq($@) { - my ($n, @list) = @_; - my @result; - while (@result < $n) { - my $i = random(0,$#list,1); - if ($i==0) { - push @result, shift @list; - } elsif ($i==$#list) { - push @result, pop @list; - } else { - push @result, $list[$i]; - $list[$i] = pop @list; - } - } - return @result; -} - -sub IINT { - return '\int\!\!\int'; -} - -sub IIINT { - return '\int\!\!\int\!\!\int'; -} - -$IINT = IINT(); -$IIINT = IIINT(); - -sub VUSAGE { - return $BBOLD . ' Usage: ' . $EBOLD . 'To enter a vector, for example \(\langle x,y,z\rangle\), type '.$LQ . '\('. $LTS .'\) x, y, z \(' . $GTS . '\)' . $RQ; -} - -$VUSAGE = VUSAGE(); - -sub PUSAGE { - return $BBOLD . ' Usage: ' . $EBOLD . 'To enter a point, for example \( (x,y,z) \), type "(x, y, z)".'; -} - -$PUSAGE = PUSAGE(); - -#sam# copied from bkellMacros.pl -- no reason to maintain two macro files. -#sam# FIXME -- these should probably lose their "bkell_" prefixes. - -# bkellMacros.pl -# Brian Kell -# Last updated 3:24 CDT, 24 Jun 2007 - -############################################################################### -# bkell_linear_simplify($a, $b) -# -# Returns a string representing $a*x+$b in simplified form, where "simplified" -# means something along the lines of the following: -# -# a b output -# --- --- ------ -# 0 0 0 -# 1 1 x+1 -# -1 -1 -x-1 -# -1 5 5-x -# 2 0 2x -# -4 3 3-4x -# -4 -3 -4x-3 -# -sub bkell_linear_simplify -{ - my ($a, $b) = @_; - - if ($a == 0) { - return "$b"; - } elsif ($b == 0) { - if ($a == -1) { - return "-x"; - } elsif ($a == 1) { - return "x"; - } else { - return "${a}x"; - } - } elsif ($a < 0 && $b > 0) { - if ($a == -1) { - return "$b-x"; - } else { - return "$b-".(abs $a)."x"; - } - } elsif ($a == 1) { - return "x".sprintf("%+d", $b); - } elsif ($a == -1) { - return "-x".sprintf("%+d", $b); - } else { - return "${a}x".sprintf("%+d", $b); - } -} - -############################################################################### -# bkell_simplify_fraction($num, $denom) -# -# Simplifies the fraction $num/$denom; returns the list ($num, $denom). -# -sub bkell_simplify_fraction -{ - my ($num, $denom) = @_; - - if ($num == 0) { return (0, 1); } - - my $sign = +1; - if ($num < 0) { $sign *= -1; $num *= -1; } - if ($denom < 0) { $sign *= -1; $denom *= -1; } - - for ($i = 2; $i <= $num && $i <= $denom; ++$i) { - while ($num % $i == 0 && $denom % $i == 0) { - $num /= $i; - $denom /= $i; - } - } - - return ($sign*$num, $denom); -} - -############################################################################### -# bkell_simplify_fraction_string($num, $denom [, $flags]) -# -# Returns a string like "$num/$denom" in simplified form. If the string $flags -# contains "+", then a leading "+" is included if the fraction is non-negative. -# If $flags contains "0", then a fraction with a value of 0 will cause the -# empty string to be returned. If $flags contains "1", then a fraction with a -# value of 1 or -1 will cause only the sign to be returned (or the empty -# string, if the fraction is non-negative and $flags does not contain "+"). If -# $flags contains "(", then parentheses will be placed around "$num/$denom" -# (the sign, if it exists, will be outside the parentheses); if $flags contains -# "[", then parentheses will be used only if a sign is used and the denominator -# is other than 1. A flag of "(" overrides "]". If $flags contains "f", then -# the string returned will be in the form "{$num \over $denom}" instead of the -# normal slashed version. -# -sub bkell_simplify_fraction_string -{ - my ($num, $denom, $flags) = @_; - if (!defined $flags) { $flags = ""; } - - ($num, $denom) = bkell_simplify_fraction($num, $denom); - - my $sign = ($num >= 0 ? ($flags =~ /\+/ ? "+" : "") : "-"); - $num = abs $num; - - my ($pre, $post); - if ($flags =~ /\(/ || ($flags =~ /\[/ && $sign ne "" && $denom != 1)) { - ($pre, $post) = ("(", ")"); - } else { - ($pre, $post) = ("", ""); - } - - if ($num == 0 && $flags =~ /0/) { return ""; } - if ($denom == 1) { - if ($num == 1 && $flags =~ /1/) { return "$sign"; } - return "$sign$pre$num$post"; - } - if ($flags =~ /f/) { return "$sign$pre {$num \\over $denom}$post"; } - return "$sign$pre$num/$denom$post"; -} - -############################################################################### -# bkell_poly_term($coeff_num, $coeff_denom, $var [, "+"]) -# -# Produces and simplifies a term of a polynomial of the general form -# "($coeff_num/$coeff_denom)$var". Handles special cases (such as $num==0, -# $denom==1, etc.). If the fourth argument is "+", then a leading "+" is -# included if the term is positive. -# -sub bkell_poly_term -{ - my ($coeff_num, $coeff_denom, $var, $plus) = @_; - if (!defined $plus) { $plus = ""; } - - if ($coeff_num == 0) { return ""; } - - my $sign = +1; - if ($coeff_num < 0) { $sign *= -1; $coeff_num *= -1; } - if ($coeff_denom < 0) { $sign *= -1; $coeff_denom *= -1; } - $sign = ($sign > 0 ? ($plus eq "+" ? "+" : "") : "-"); - - my ($num, $denom) = bkell_simplify_fraction($coeff_num, $coeff_denom); - - if ($denom == 1) { - if ($num == 1) { - return "$sign$var"; - } else { - return "$sign$num$var"; - } - } - - return "$sign($num/$denom)$var"; -} - -############################################################################### -# bkell_gcd($x, $y) -# -# Returns the greatest common divisor of $x and $y. -# -sub bkell_gcd { - my ($x, $y) = @_; - $x = abs $x; - $y = abs $y; - if ($x > $y) { ($x, $y) = ($y, $x); } - if ($x == 0) { return $y; } - my $r = $y % $x; - if ($r == 0) { return $x; } - return bkell_gcd($x, $r); -} - -############################################################################### -# bkell_poly_eval($x, $a_n, ..., $a_0) -# -# Evaluates the polynomial a_n*x^n + ... + a_1*x + a_0 at the given value of x. -# -sub bkell_poly_eval -{ - my $x = shift; - my $value = shift; - while (@_) { $value *= $x; $value += shift; } - return $value; -} - -############################################################################### -# bkell_real_zeros_finder($a_n, ..., $a_0) -# -# Returns a list of numerical approximations of the zeros of the polynomial -# a_n*x^n + ... + a_1*x + a_0, in order from least to greatest. -# -# The possibility of overflow or underflow is ignored. Overflow is likely to be -# a bigger problem than underflow. -# -# Do not use this code to guide missiles or control nuclear power plants. -# -sub bkell_real_zeros_finder -{ - my @coeffs = @_; - - while (@coeffs && $coeffs[0] == 0) { shift @coeffs; } - my $deg = $#coeffs; - - if ($deg == -1) { - return ("x"); # zero polynomial is zero everywhere - } elsif ($deg == 0) { - return (); # constant nonzero polynomial has no zeros - } elsif ($deg == 1) { - return (-$coeffs[1]/$coeffs[0]); # linear polynomial has one zero - } - - # find critical points - my @derivative = @coeffs; - pop @derivative; - for (my $i = 0; $i < $#derivative; ++$i) { - $derivative[$i] *= @derivative - $i; - } - my @cp = bkell_real_zeros_finder(@derivative); - - # if no critical points, we have a monotone function - if (!@cp) { - my ($lb, $rb) = (-1, 1); - my $y1 = bkell_poly_eval($lb, @coeffs); - my $y2 = bkell_poly_eval($rb, @coeffs); - my $sign = ($y1 < $y2 ? +1 : -1); - while ($sign * $y1 > 0) { - $lb *= 2; - $y1 = bkell_poly_eval($lb, @coeffs); - } - while ($sign * $y2 < 0) { - $rb *= 2; - $y2 = bkell_poly_eval($rb, @coeffs); - } - my $guess_x = ($lb + $rb) / 2; - while ($lb < $guess_x && $guess_x < $rb && - (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) - { - if ( ($y1 < 0 && $guess_y < 0) || ($y1 > 0 && $guess_y > 0) ) { - $lb = $guess_x; - } else { - $rb = $guess_x; - } - $guess_x = ($lb + $rb) / 2; - } - return ($guess_x); - } - - my @zeros = (); - - # search for a zero to the left of the first critical point - { - my $y = bkell_poly_eval($cp[0], @coeffs); - # we catch this case when we check between critical points: - last if $y == 0; - # not really the limit, but only the sign matters: - my $lim = $coeffs[0] * ($deg % 2 ? -1 : +1); - if ( ($y > 0 && $lim < 0) || ($y < 0 && $lim > 0) ) { - my ($lb, $rb) = (undef, $cp[0]); - my $guess_x = $rb - 10; - if ($guess_x >= 0) { $guess_x = -10; } - while ((!defined $lb || $lb < $guess_x) && $guess_x < $rb && - (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) - { - if ( ($y > 0 && $guess_y > 0) || ($y < 0 && $guess_y < 0) ) { - $rb = $guess_x; - if (defined $lb) { - $guess_x = ($lb + $guess_x) / 2; - } else { - $guess_x *= 2; - } - } else { - $lb = $guess_x; - $guess_x = ($guess_x + $rb) / 2; - } - } - push @zeros, $guess_x; - } - } - - # search for zeros between critical points - for (my $i = 0; $i < $#cp; ++$i) { - my $y1 = bkell_poly_eval($cp[$i], @coeffs); - if ($y1 == 0) { - push @zeros, $cp[$i]; - next; - } - my $y2 = bkell_poly_eval($cp[$i+1], @coeffs); - if ($y2 == 0) { - push @zeros, $cp[$i+1]; - ++$i; - next; - } - next if ($y1 > 0 && $y2 > 0) || ($y1 < 0 && $y2 < 0); - my ($lb, $rb) = ($cp[$i], $cp[$i+1]); - my $guess_x = ($lb + $rb) / 2; - while ($lb < $guess_x && $guess_x < $rb && - (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) - { - if ( ($y1 > 0 && $guess_y > 0) || ($y1 < 0 && $guess_y < 0) ) { - $lb = $guess_x; - } else { - $rb = $guess_x; - } - $guess_x = ($lb + $rb) / 2; - } - push @zeros, $guess_x unless @zeros && $zeros[-1] == $guess_x; - } - - # search for a zero to the right of the last critical point - { - my $y = bkell_poly_eval($cp[-1], @coeffs); - if ($y == 0 && $zeros[-1] != $cp[-1]) { - push @zeros, $cp[-1]; - last; - } - if ( ($y > 0 && $coeffs[0] < 0) || ($y < 0 && $coeffs[0] > 0) ) { - my ($lb, $rb) = ($cp[-1], undef); - my $guess_x = $lb + 10; - if ($guess_x <= 0) { $guess_x = 10; } - while ($lb < $guess_x && (!defined $rb || $guess_x < $rb) && - (my $guess_y = bkell_poly_eval($guess_x, @coeffs)) != 0) - { - if ( ($y > 0 && $guess_y > 0) || ($y < 0 && $guess_y < 0) ) { - $lb = $guess_x; - if (defined $rb) { - $guess_x = ($guess_x + $rb) / 2; - } else { - $guess_x *= 2; - } - } else { - $rb = $guess_x; - $guess_x = ($lb + $guess_x) / 2; - } - } - push @zeros, $guess_x unless @zeros && $zeros[-1] == $guess_x; - } - } - - return @zeros; -} - -############################################################################### -# bkell_floor($x) -# -# Returns the floor of $x. Normally this would be done with POSIX::floor, but -# WeBWorK doesn't allow you to use standard modules like POSIX. -# -sub bkell_floor -{ - my $x = shift; - my $floor = int $x; - if ($x < 0 && $x != $floor) { $floor -= 1; } - return $floor; -} - -############################################################################### -# bkell_ceil($x) -# -# Returns the ceiling of $x. -# -sub bkell_ceil -{ - return -bkell_floor(-shift); -} - -############################################################################### -# bkell_sigfigs($x, $n) -# -# Returns a string containing $x rounded to $n significant figures. -# -sub bkell_sigfigs -{ - my ($x, $n) = @_; - - if ($x == 0) { return "0".($n > 1 ? "." : "").("0" x ($n-1)); } - - my $minus = ""; - if ($x < 0) { - $minus = "-"; - $x = -$x; - } - - my $floor_log = bkell_floor(log($x)/log(10)); - - if ($floor_log+1 >= $n) { - my $sf = 10**($floor_log-$n+1); - return $minus.(sprintf("%.0f", $x/$sf)*$sf); - } else { - my $digits = $n-$floor_log-1; - return $minus.sprintf("%.${digits}f", $x); - } -} - -############################################################################### -# bkell_125($x) -# -# Returns the value logarithmically nearest $x in the sequence -# ..., -1000, -500, -200, -100, -50, -20, -10, -5, -2, -1, -0.5, -0.2, -# -0.1, -0.05, -0.02, -0.01, ..., 0, ..., 0.01, 0.02, 0.05, 0.1, -# 0.2, 0.5, 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, ... . -# -sub bkell_125 -{ - my $x = shift; - - if ($x == 0) { return 0; } - - my $sign = +1; - if ($x < 0) { $sign = -1; $x = -$x; } - - my $log = log($x)/log(10); - my $characteristic = bkell_floor($log); - my $mantissa = $log - $characteristic; - - my $log2 = log(2)/log(10); - my $log5 = log(5)/log(10); - my $m; - if ($mantissa < $log2 / 2) { - $m = 0; - } elsif ($mantissa < ($log2 + $log5) / 2) { - $m = $log2; - } elsif ($mantissa < ($log5 + 1) / 2) { - $m = $log5; - } else { - $m = 1; - } - - return $sign * (10 ** ($characteristic + $m)); -} - -############################################################################### -# bkell_list_random_selection($n, @list) -# -# Returns a selection of $n distinct elements of @list. This is like -# list_random_multi_uniq in freemanMacros.pl, except that this function will -# always return the elements in the same order as they appear in @list. -# -sub bkell_list_random_selection -{ - my $n = int abs shift; # so strange $n won't cause infinite loop - my @list = @_; - - my $needed = $n; - my @result = (); - while ($needed && @list) { - if (random(1, scalar @list) <= $needed) { - push @result, shift @list; - --$needed; - } else { - shift @list; - } - } - - return @result; -} - -############################################################################### -# bkell_graph_axis($a, $b) -# -# Returns a list ($min, $max, $step), where $min <= $a, $max >= $b, $step is a -# power of 10, $min and $max are multiples of $step, abs($min) <= 9*$step, and -# abs($max) <= 9*$step. Useful for deciding bounds for the axis of a graph. For -# example, to make a graph axis that can handle values between $a and $b, call -# bkell_graph_axis, and then set the minimum value of the axis to $min and the -# maximum to $max, and put tick marks every $step units. -# -sub bkell_graph_axis -{ - my ($a, $b) = @_; - - if ($a > $b) { ($a, $b) = ($b, $a); } - - my $s1 = 0; - my $s2 = 0; - - if ($a != 0) { $s1 = bkell_floor(log(abs $a)/log(10)); } - if ($b != 0) { $s2 = bkell_floor(log(abs $b)/log(10)); } - - my $s = ($s1 > $s2 ? $s1 : $s2); - - $step = 10**$s; - $min = $step * bkell_floor($a/$step); - $max = $step * bkell_ceil($b/$step); - - return ($min, $max, $step); -} - -####################################################################### EOF ### diff --git a/OpenProblemLibrary/macros/Wiley/littleneck.pl b/OpenProblemLibrary/macros/Wiley/littleneck.pl deleted file mode 100644 index 8eb3cd8e46..0000000000 --- a/OpenProblemLibrary/macros/Wiley/littleneck.pl +++ /dev/null @@ -1,227 +0,0 @@ -#***************************** -# Question mode variables -#***************************** -$PRACTICE_MODE = 0; -loadMacros("problemRandomize.pl"); -#******************************************** -# Set up a "generate new problem" button -#******************************************** -sub rand_button -{ - if ($PRACTICE_MODE == 1) - { - ProblemRandomize(when=>"Always",onlyAfterDue=>0,style=>"Input"); - } - elsif ($PRACTICE_MODE == 2) - { - ProblemRandomize(when=>"Always",onlyAfterDue=>0,label=>"Generate New Problem"); - } -} -#********************************************************** -# Subroutine to reduce fractions -# Input : Num, Denom -# Output: Num, Denom, Wholenum (if != 0, = $num/$denom) -# -# 22Jul09 - Returned Wholenum < 0 if num or denom < 0 -#********************************************************** -sub reduce_fraction -{ - $n = $_[0]; - $d = $_[1]; - $wholenum = 0; -# -# ********************** -# Remove negatives -# ********************** - if ($n < 0) - { - $num = -$n; - } - else - { - $num = $n; - } - if ($d < 0) - { - $denom = -$d; - } - else - { - $denom = $d; - } -# ****************************** -# Check for a whole number -# ****************************** - if ($num/$denom == int($num/$denom)) - { - $wholenum = $n/$d; - } -# *************** -# Find gcf -# *************** - else - { - if ($num > $denom) - { - $gcf = $denom; - } - else - { - $gcf = $num; - } - $found = 0; - while ( ($found == 0) && ($gcf > 1) ) - { - if ( ($num/$gcf == int($num/$gcf)) && ($denom/$gcf == int($denom/$gcf)) ) - { - $found = 1; - } - else - { - $gcf--; - } - } -# ******************************** -# If a GCF was found, reduce -# ******************************** - if ($found == 1) - { - $num = $num/$gcf; - $denom = $denom/$gcf; - } - } -# *********************** -# Restore negatives -# *********************** - if ($n < 0) - { - $num = -1*$num; - } - if ($d < 0) - { - $denom = -1*$denom; - } - return($num, $denom, $wholenum); -} -# -#********************************************************** -# Subroutine to produce reduced display fraction -# Input : Num($n), Denom($d) -# Output: Num($n), Denom($d), Wholenum($w),Display($display) -# calls reduce_fraction to get, -# Output: Num($n), Denom($d), Wholenum($w), -#(if Wholenum > 0,$display = $w -# else $Display=\(\frac{$n}{$d}\) -#********************************************************** -sub display_fraction_long -{ $n = $_[0]; - $d = $_[1]; - $w=0; - $display=0; -#call reduce_fraction -($n,$d,$w)=reduce_fraction($n,$d,$w); -#Decide on display format -if ($w>0){ - $display=$wholenumber; - } -else{ - $display="\\frac{$num}{$denom}"; - } -return($n, $d, $w,$display); -} -# -# -# -# -#********************************************************** -# Subroutine to produce reduced display fraction -# Input : Num($n), Denom($d) -# Output: Display($display) -# calls reduce_fraction to get, -# Output: Num($n), Denom($d), Wholenum($w), -#(if Wholenum > 0,$display = $w -# else $Display=\(\frac{$n}{$d}\) -#********************************************************** -sub display_fraction -{ $n = $_[0]; - $d = $_[1]; - $w=0; - $display=0; -#call reduce_fraction -($n,$d,$w)=reduce_fraction($n,$d,$w); -#Decide on display format -if ($w>0){ - $display=$wholenumber; - } - else{ - $display="\\frac{$num}{$denom}"; - } -return($display); -} -#******************************************************************************* -# sqrt_simplify(Num,Natnum,Radnum,Texstr,Error) -# -# Description: Given a natural number, will find the greatest perfect square -# that is a factor and use this to simplify the expression: -# sqrt(number). -# Input : -# Num = Number whose sqrt is being taken -# -# Output: -# Natnum => Natural number portion of solution (= 1 if no perfect square -# divides evenly) -# Radnum => The value still inside the radical -# Texstr => A LaTex string of the form "Natnum \sqrt{$Radnum}" to be used -# for displaying the solution. -# Error => If == 1, then the number entered was bad (ie, negative, nonint) -# (If necessary, this code can be made specific for the error) -#******************************************************************************* -sub sqrt_simplify -{ - $number = $_[0]; - ($Natnum,$Radnum) = (1,1); - $Error = 0; - - #*********************************** - # Check the number for validity - #*********************************** - if ( ($number == int($number)) && ($number > 0) ) - { - $perfsqr = 1; - #************************************************************************************* - # Check all perfect squares up to the max and store the hightest one that divides - #************************************************************************************* - for ($i = 2; $i*$i <= $number; $i++) - { - $sqr = $i*$i; - if ( int($number/$sqr) == $number/$sqr ) - { - $perfsqr = $i; - } - } - $Natnum = $perfsqr; - $Radnum = $number/($Natnum*$Natnum); - #************************************************************ - # If radnum = 1, the original number is a perfect square - # If natnum = 1, it cannot be simplified - #************************************************************ - if ($Radnum == 1) - { - $Texstr = "$Natnum"; - } - elsif ($Natnum == 1) - { - $Texstr = "\\sqrt{$number}"; - } - else - { - $Texstr = "$Natnum \\sqrt{$Radnum}"; - } - } - else - { - ($Natnum,$Radnum,$Error) = 1; - $Texstr = "1"; - } - return($Num,$Natnum,$Radnum,$Texstr,$Error); -}