diff --git a/OpenProblemLibrary/macros/univ/Alfredmacros.pl b/OpenProblemLibrary/macros/univ/Alfredmacros.pl
new file mode 100755
index 0000000000..de0871b50a
--- /dev/null
+++ b/OpenProblemLibrary/macros/univ/Alfredmacros.pl
@@ -0,0 +1,369 @@
+# 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/univ/BrockPhysicsMacros.pl b/OpenProblemLibrary/macros/univ/BrockPhysicsMacros.pl
new file mode 100644
index 0000000000..67405cbe6f
--- /dev/null
+++ b/OpenProblemLibrary/macros/univ/BrockPhysicsMacros.pl
@@ -0,0 +1 @@
+# this file defines all Brock-Physics-specific macros.
\ No newline at end of file
diff --git a/OpenProblemLibrary/macros/univ/CofIdaho_macros.pl b/OpenProblemLibrary/macros/univ/CofIdaho_macros.pl
new file mode 100755
index 0000000000..262ef22dc4
--- /dev/null
+++ b/OpenProblemLibrary/macros/univ/CofIdaho_macros.pl
@@ -0,0 +1,758 @@
+=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/univ/Dartmouthmacros.pl b/OpenProblemLibrary/macros/univ/Dartmouthmacros.pl
new file mode 100755
index 0000000000..a1a6ce63d2
--- /dev/null
+++ b/OpenProblemLibrary/macros/univ/Dartmouthmacros.pl
@@ -0,0 +1,313 @@
+#!/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/univ/MUHelp.pl b/OpenProblemLibrary/macros/univ/MUHelp.pl
new file mode 100644
index 0000000000..3f9e185e2a
--- /dev/null
+++ b/OpenProblemLibrary/macros/univ/MUHelp.pl
@@ -0,0 +1,45 @@
+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/univ/littleneck.pl b/OpenProblemLibrary/macros/univ/littleneck.pl
new file mode 100644
index 0000000000..8eb3cd8e46
--- /dev/null
+++ b/OpenProblemLibrary/macros/univ/littleneck.pl
@@ -0,0 +1,227 @@
+#*****************************
+# 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);
+}