Math-Algebra-Symbols-1.21/0040777000175500010010000000000010063422766013734 5ustar philNoneMath-Algebra-Symbols-1.21/CHANGES0100777000175500010010000000561310063422762014730 0ustar philNoneChanges 2004/06/14 1.21 Minor documentation corrections. 2004/06/10 1.20 Capitalized module file names correctly. 2004/06/09 1.19 Lib, Pod cleanup 2004/06/08 Observed transit of Venus in room converted to Camera Obscura. 2004/06/04 1.18 Equation simplification tied to += operator 2004/06/02 1.17 Following further excellent suggestions from Mike Schilli slightly improved notation for equation solving: eq is a synonym for - > is a synonym for solve() Thus: $x = symbols('x'); ($a, $b) = @{ ($x**2 eq 5*$x+6) > $x }; print "$a,$b\n"; # 2,3 2004/06/01 1.16 Following excellent suggestions from Mike Schilli detect constants with decimal places and convert them to rational fractions, thus 1.2 becomes 12/10. The rationale: using fractions avoids loss of precision, if you only need approximate numerical results, there is no need for symbolic manipulations. 2004/05/28 1.15 Minor changes to documentation. 2004/05/27 1.14 Admiring new CPAN Layout: diff+grep. Updated META.yml requires/recommends to match MANIFEST. Removed use of Hash::Util as this seems to be causing problems with ActiveState. 2004/05/11 1.13 Memory Leak and exp() problems solved: 1. Solved a memory leak by using Scalar::Util::weaken(). This has reduced memory consumption when running the test cases from 130M to 37M: intermediate expressions are now being freed. I believe that this has been the source of the erratic failures on various platforms, other than my development system, as reported (most helpfully) by CPAN testers and a Mac OSX user: these reported errors appear to have been related to running out of memory. 2. Corrected exp(-i)*exp(-i) which was being correctly reduced to exp(0), but should have been reduced further to 1, which was causing the multiply out of exp(i...) in cot(x) == 1/tan(x) to fail. 3. Added the != operator, as this simplifies testing with the test harness. 2004/04/30 1.12 Included PREREQ_PM in Makefile as a result of notes from automated CPAN testing. 2004/04/24 1.08 Included test cases in manifest and renamed to .t in line with observed practice. 2004/03/28 Split into three classes. Considerable performance improvements. Added standard Perl Test harness. Unfixed power() so we are back to constant powers. 2004/03/14 Fixed power() so that it recognizes constant and variable powers. Added TODO List. Finished testSM(): new requirements in TODO. 2004/03/13 Added change log on esteemed advice of Steffen Müller. Made yet another attempt to stop polynomialDivide() from producing an infinite series as a representation of a single term. Most of mathematics seems to erupt from the division of one polynomial by another. Math-Algebra-Symbols-1.21/lib/0040777000175500010010000000000010063422766014502 5ustar philNoneMath-Algebra-Symbols-1.21/lib/Math/0040777000175500010010000000000010063422766015373 5ustar philNoneMath-Algebra-Symbols-1.21/lib/Math/Algebra/0040777000175500010010000000000010063422766016730 5ustar philNoneMath-Algebra-Symbols-1.21/lib/Math/Algebra/Symbols/0040777000175500010010000000000010063422766020360 5ustar philNoneMath-Algebra-Symbols-1.21/lib/Math/Algebra/Symbols/Sum.pm0100777000175500010010000021772610063422762021475 0ustar philNone#!perl -w =head1 Sums Symbolic Algebra using Pure Perl: sums. See user manual L. Operations on sums of terms. PhilipRBrenan@yahoo.com, 2004, Perl License. =cut package Math::Algebra::Symbols::Sum; $VERSION=1.21; use Math::Algebra::Symbols::Term; use IO::Handle; use Carp; #HashUtil use Hash::Util qw(lock_hash); use Scalar::Util qw(weaken); =head2 Constructors =head3 new Constructor =cut sub new {bless {t=>{}}; } =head3 newFromString New from String =cut sub newFromString($) {my ($a) = @_; return $zero unless $a; $a .='+'; my @a = $a =~ /(.+?)[\+\-]/g; my @t = map {term($_)} @a; sigma(@t); } =head3 n New from Strings =cut sub n(@) {return $zero unless @_; my @a = map {newFromString($_)} @_; return @a if wantarray; $a[0]; } =head3 sigma Create a sum from a list of terms. =cut sub sigma(@) {return $zero unless scalar(@_); my $z = new(); for my $t(@_) {my $s = $t->signature; if (exists($z->{t}{$s})) {my $a = $z->{t}{$s}->add($t); if ($a->c == 0) {delete $z->{t}{$s}; } else {$z->{t}{$s} = $a; } } else {$z->{t}{$s} = $t } } $z->z; } =head3 makeInt Construct an integer =cut sub makeInt($) {sigma(term()->one->clone->c(shift())->z) } =head2 Methods =head3 isSum Confirm type =cut sub isSum($) {1}; =head3 t Get list of terms from existing sum =cut sub t($) {my ($a) = @_; (map {$a->{t}{$_}} sort(keys(%{$a->{t}}))); } =head3 count Count terms in sum =cut sub count($) {my ($a) = @_; scalar(keys(%{$a->{t}})); } =head3 st Get the single term from a sum containing just one term =cut sub st($) {my ($a) = @_; return (values(%{$a->{t}}))[0] if scalar(keys(%{$a->{t}})) == 1; undef; } =head3 negate Multiply each term in a sum by -1 =cut sub negate($) {my ($s) = @_; my @t; for my $t($s->t) {push @t, $t->clone->timesInt(-1)->z; } sigma(@t); } =head3 add Add two sums together to make a new sum =cut sub add($$) {my ($a, $b) = @_; sigma($a->t, $b->t); } =head3 subtract Subtract one sum from another =cut sub subtract($$) {my ($a, $b) = @_; return $b->negate if $a->{id} == $zero->{id}; $a->add($b->negate); } =head3 Conditional Multiply Multiply two sums if both sums are defined, otherwise return the defined sum. Assumes that at least one sum is defined. =cut sub multiplyC($$) {my ($a, $b) = @_; return $a unless defined($b); return $b unless defined($a); $a->multiply($b); } =head3 multiply Multiply two sums together =cut my %M; # Memoize multiplication sub multiply($$) {my ($A, $B) = @_; my $m = $M{$A->{id}}{$B->{id}}; return $m if defined($m); return $A if $A->{id} == $zero->{id} or $B->{id} == $one->{id}; return $B if $B->{id} == $zero->{id} or $A->{id} == $one->{id}; my @t; # Check for divides that match multiplier my @a = $A->t; for my $a(@a) {my $d = $a->Divide; next unless $d; if ($d->{id} == $B->{id}) {push @t, $a->removeDivide; $a = undef; } } my @b = $B->t; for my $b(@b) {my $d = $b->Divide; next unless $d; if ($d->{id} == $A->{id}) {push @t, $b->removeDivide; $b = undef; } } # Simple multiply for my $aa(@a) {next unless $aa; for my $bb(@b) {next unless $bb; my $m = $aa->multiply($bb); push (@t, $m), next if $m; # Complicated multiply my %a = $aa->split; my %b = $bb->split; my $a = $a{t}; my $b = $b{t}; # Sqrt my $s = 0; $s = $a{s} if $a{s} and $b{s} and $a{s}->{id} == $b{s}->{id}; # Equal sqrts $a->Sqrt(multiplyC($a{s}, $b{s})) unless $s; # Divide $a->Divide(multiplyC($a{d}, $b{d})) if $a{d} or $b{d}; # Exp $a->Exp($a{e} ? $a{e} : $b{e}) if $a{e} xor $b{e}; my $e; if ($a{e} and $b{e}) {my $s = $a{e}->add($b{e}); $e = $s->st; # Check for single term $e = $e->exp2 if defined($e); # Simplify single term if possible $a->Exp($s) unless defined($e); # Reinstate Exp as sum of terms if no simplification possible } # Log $a->Log($a{l} ? $a{l} : $b{l}) if $a{l} xor $b{l}; die "Cannot multiply logs yet" if $a{l} and $b{l}; # Combine results $a = $a->z; $b = $b->z; $a = $a->multiply($b); $a = $a->multiply($e) if defined($e); $a or die "Bad multiply"; push @t, $a unless $s; push @t, sigma($a)->multiply($s)->t if $s; } } # Result my $C = sigma(@t); $M{$A->{id}}{$B->{id}} = $C; $C; } =head3 divide Divide one sum by another =cut sub divide($$) {my ($A, $B) = @_; # Obvious cases $B->{id} == $zero->{id} and croak "Cannot divide by zero"; return $zero if $A->{id} == $zero->{id}; return $A if $B->{id} == $one->{id}; return $A->negate if $B->{id} == $mOne->{id}; # Divide term by term my $a = $A->st; my $b = $B->st; if (defined($a) and defined($b)) {my $c = $a->divide2($b); return sigma($c) if $c; } # Divide sum by term elsif ($b) {ST: for(1..1) {my @t; for my $t($A->t) {my $c = $t->divide2($b); last ST unless $c; push @t, $c; } return sigma(@t); } } # Divide sum by sum my @t; for my $aa($A->t) {my $a = $aa->clone; my $d = $a->Divide; $a->Divide($d->multiply($B)) if $d; $a->Divide($B) unless $d; push @t, $a->z; } # Result sigma(@t); } =head3 sub Substitute a sum for a variable. =cut sub sub($@) {my $E = shift(); my @R = @_; # Each replacement for(;@R > 0;) {my $s = shift @R; # Replace this variable my $w = shift @R; # With this expression my $Z = $zero; $s =~ /^[a-z]+$/i or croak "Can only substitute an expression for a variable, not $s"; $w = newFromString($w) unless ref($w); $w->isSum; # Each term of the sum comprising the replacement expression. for my $t($E->t) {my $n = $t->vp($s); my %t = $t->split; my $S = sigma($t{t}->vp($s, 0)->z); # Remove substitution variable $S = $S->multiply(($t{s}->sub(@_))->Sqrt) if defined($t{s}); $S = $S->divide ($t{d}->sub(@_)) if defined($t{d}); $S = $S->multiply(($t{e}->sub(@_))->Exp) if defined($t{e}); $S = $S->multiply(($t{l}->sub(@_))->Log) if defined($t{l}); $S = $S->multiply($w->power(makeInt($n))) if $n; $Z = $Z->add($S); } $E = $Z; } # Result $E; } =head3 isEqual Check whether one sum is equal to another after multiplying out all divides and divisors. =cut sub isEqual($) {my ($C) = @_; # Until there are no more divides for(;;) {my (%c, $D, $N); $N = 0; # Most frequent divisor for my $t($C->t) {my $d = $t->Divide; next unless $d; my $s = $d->getSignature; if (++$c{$s} > $N) {$N = $c{$s}; $D = $d; } } last unless $N; $C = $C->multiply($D); } # Until there are no more negative powers for(;;) {my %v; for my $t($C->t) {for my $v($t->v) {my $p = $t->vp($v); next unless $p < 0; $p = -$p; $v{$v} = $p if !defined($v{$v}) or $v{$v} < $p; } } last unless scalar(keys(%v)); my $m = term()->one->clone; $m->vp($_, $v{$_}) for keys(%v); my $M = sigma($m->z); $C = $C->multiply($M); } # Result $C; } =head3 normalizeSqrts Normalize sqrts in a sum. This routine needs fixing. It should simplify square roots. =cut sub normalizeSqrts($) {my ($s) = @_; return $s; my (@t, @s); # Find terms with single simple sqrts that can be normalized. for my $t($s->t) {push @t, $t; my $S = $t->Sqrt; next unless $S; # Check for sqrt my $St = $S->st; next unless $St; # Check for single term sqrt my %T = $St->split; # Split single term sqrt next if $T{s} or $T{d} or $T{e} or $T{l}; pop @t; push @s, {t=>$t, s=>$T{t}->z}; # Sqrt with simple single term } # Already normalized unless there are several such terms return $s unless scalar(@s) > 1; # Remove divisor for each normalized term for my $r(@s) {my $d = $r->{t}->d; next unless $d > 1; for my $s(@s) {$s->{t} = $s->{t}->clone->divideInt($d) ->z; $s->{s} = $s->{s}->clone->timesInt ($d*$d)->z; } } # Eliminate duplicate squared factors for my $s(@s) {my $F = factorize($s->{s}->c); my $p = 1; for my $f(keys(%$F)) {$p *= $f**(int($F->{$f}/2)) if $F->{$f} > 1; } $s->{t} = $s->{t}->clone->timesInt ($p) ->z; $s->{s} = $s->{s}->clone->divideInt($p*$p)->z; $DB::single = 1; if ($s->{s}->isOne) { push @t, $s->{t}->removeSqrt; } else { push @t, $s->{t}->clone->Sqrt($s->{$s})->z; } } # Result sigma(@t); } =head3 isEqualSqrt Check whether one sum is equal to another after multiplying out sqrts. =cut sub isEqualSqrt($) {my ($C) = @_; #_______________________________________________________________________ # Each sqrt #_______________________________________________________________________ for(1..99) {$C = $C->normalizeSqrts; my @s = grep { defined($_->Sqrt)} $C->t; my @n = grep {!defined($_->Sqrt)} $C->t; last unless scalar(@s) > 0; #_______________________________________________________________________ # Partition by square roots. #_______________________________________________________________________ my %S = (); for my $t(@s) {my $s = $t->Sqrt; my $S = $s->signature; push @{$S{$S}}, $t; } #_______________________________________________________________________ # Square each partitions, as required by the formulae below. #_______________________________________________________________________ my @t; push @t, sigma(@n)->power($two) if scalar(@n); # Non sqrt partition for my $s(keys(%S)) {push @t, sigma(@{$S{$s}})->power($two); # Sqrt partition } #_______________________________________________________________________ # I can multiply out upto 4 square roots using the formulae below. # There are formula to multiply out more than 4 sqrts, but they are big. # These formulae are obtained by squaring out and rearranging: # sqrt(a)+sqrt(b)+sqrt(c)+sqrt(d) == 0 until no sqrts remain, and # then matching terms to produce optimal execution. # This remarkable result was obtained with the help of this package: # demonstrating its utility in optimizing complex calculations written # in Perl: which in of itself cannot optimize broadly. #_______________________________________________________________________ my $ns = scalar(@t); $ns < 5 or die "There are $ns square roots present. I can handle less than 5"; my ($a, $b, $c, $d) = @t; if ($ns == 1) {$C = $a; } elsif ($ns == 2) {$C = $a-$b; } elsif ($ns == 3) {$C = -$a**2+2*$a*$b-$b**2+2*$c*$a+2*$c*$b-$c**2; } elsif ($ns == 4) {my $a2 = $a * $a; my $a3 = $a2 * $a; my $a4 = $a3 * $a; my $b2 = $b * $b; my $b3 = $b2 * $b; my $b4 = $b3 * $b; my $c2 = $c * $c; my $c3 = $c2 * $c; my $c4 = $c3 * $c; my $d2 = $d * $d; my $d3 = $d2 * $d; my $d4 = $d3 * $d; my $bpd = $b + $d; my $bpc = $b + $c; my $cpd = $c + $d; $C = - ($a4 + $b4 + $c4 + $d4) + 4*( +$a3*($b+$cpd)+$b3*($a+$cpd)+$c3*($a+$bpd)+$d3*($a+$bpc) -$a2*($b *($cpd)+ $c*$d) -$a *($b2*($cpd)+$d2*($bpc)) ) - 6*($a2*$b2+($a2+$b2)*($c2+$d2)+$c2*$d2) - 4*$c*($b2*$d+$b*$d2) - 4*$c2*($a*($bpd)+$b*$d) +40*$c*$a*$b*$d ; } } #________________________________________________________________________ # Test result #________________________________________________________________________ # $C->isEqual($zero); $C; } =head3 isZero Transform a sum assuming that it is equal to zero =cut sub isZero($) {my ($C) = @_; $C->isEqualSqrt->isEqual; } =head3 powerOfTwo Check that a number is a power of two =cut sub powerof2($) {my ($N) = @_; my $n = 0; return undef unless $N > 0; for (;;) {return $n if $N == 1; return undef unless $N % 2 == 0; ++$n; $N /= 2; } } =head3 solve Solve an equation known to be equal to zero for a specified variable. =cut sub solve($$) {my ($A, @x) = @_; croak 'Need variable to solve for' unless scalar(@x) > 0; @x = @{$x[0]} if scalar(@x) == 1 and ref($x[0]) eq 'ARRAY'; # Array of variables supplied my %x; for my $x(@x) {if (!ref $x) {$x =~ /^[a-z]+$/i or croak "Cannot solve for: $x, not a variable name"; } elsif (ref $x eq __PACKAGE__) {my $t = $x->st; $t or die "Cannot solve for multiple terms"; my @b = $t->v; scalar(@b) == 1 or die "Can only solve for one variable"; my $p = $t->vp($b[0]); $p == 1 or die "Can only solve by variable to power 1"; $x = $b[0]; } else {die "$x is not a variable name"; } $x{$x} = 1; } my $x = $x[0]; $B = $A->isZero; # Eliminate sqrts and negative powers # Strike all terms with free variables other than x: i.e. not x and not one of the named constants my @t = (); for my $t($B->t) {my @v = $t->v; push @t, $t; for my $v($t->v) {next if exists($x{$v}); pop @t; last; } } my $C = sigma(@t); # Find highest and lowest power of x my $n = 0; my $N; for my $t($C->t) {my $p = $t->vp($x); $n = $p if $p > $n; $N = $p if !defined($N) or $p < $N; } my $D = $C; $D = $D->multiply(sigma(term()->one->clone->vp($x, -$N)->z)) if $N; $n -= $N if $N; # Find number of terms in x my $c = 0; for my $t($D->t) {++$c if $t->vp($x) > 0; } $n == 0 and croak "Equation not dependant on $x, so cannot solve for $x"; $n > 4 and $c > 1 and croak "Unable to solve polynomial or power $n > 4 in $x (Galois)"; ($n > 2 and $c > 1) and die "Need solver for polynomial of degree $n in $x"; # Solve linear equation if ($n == 1 or $c == 1) {my (@c, @v); for my $t($D->t) {push(@c, $t), next if $t->vp($x) == 0; # Constants push @v, $t; # Powers of x } my $d = sigma(@v)->multiply(sigma(term()->one->clone->vp($x, -$n)->negate->z)); $D = sigma(@c)->divide($d); return $D if $n == 1; my $p = powerof2($n); $p or croak "Fractional power 1/$n of $x unconstructable by sqrt"; $D = $D->Sqrt for(1..$p); return $D; } # Solve quadratic equation if ($n == 2) {my @c = ($one, $one, $one); $c[$_->vp($x)] = $_ for $D->t; $_ = sigma($_->clone->vp($x, 0)->z) for (@c); my ($c, $b, $a) = @c; return [ (-$b->add (($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a)), (-$b->subtract(($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a)) ] } # Check that it works # my $yy = $e->sub($x=>$xx); # $yy == 0 or die "Proposed solution \$$x=$xx does not zero equation $e"; # $xx; } =head3 power Raise a sum to an integer power or an integer/2 power. =cut sub power($$) {my ($a, $b) = @_; return $one if $b->{id} == $zero->{id}; return $a->multiply($a) if $b->{id} == $two->{id}; return $a if $b->{id} == $one->{id}; return $one->divide($a) if $b->{id} == $mOne->{id}; return $a->sqrt if $b->{id} == $half->{id}; return $one->divide($a->sqrt) if $b->{id} == $mHalf->{id}; my $T = $b->st; $T or croak "Power by expression too complicated"; my %t = $T->split; croak "Power by term too complicated" if $t{s} or $t{d} or $t{e} or $t{l}; my $t = $t{t}; $t->i == 0 or croak "Complex power not allowed yet"; my ($p, $d) = ($t->c, $t->d); $d == 1 or $d == 2 or croak "Fractional power other than /2 not allowed yet"; $a = $a->sqrt if $d == 2; return $one->divide($a)->power(sigma(term()->c($p)->z)) if $p < 0; $p = abs($p); my $r = $a; $r = $r->multiply($a) for (2..$p); $r; } =head3 d Differentiate. =cut sub d($;$); sub d($;$) {my $c = $_[0]; # Differentiate this sum my $b = $_[1]; # With this variable #_______________________________________________________________________ # Get differentrix. Assume 'x', 'y', 'z' or 't' if appropriate. #_______________________________________________________________________ if (defined($b)) {if (!ref $b) {$b =~ /^[a-z]+$/i or croak "Cannot differentiate by $b"; } elsif (ref $b eq __PACKAGE__) {my $t = $b->st; $t or die "Cannot differentiate by multiple terms"; my @b = $t->v; scalar(@b) == 1 or die "Can only differentiate by one variable"; my $p = $t->vp($b[0]); $p == 1 or die "Can only differentiate by variable to power 1"; $b = $b[0]; } else {die "Cannot differentiate by $b"; } } else {my %b; for my $t($c->t) {my %b; $b{$_}++ for ($t->v); } my $i = 0; my $n = scalar(keys(%b)); ++$i, $b = 'x' if $n == 0; # Constant expression anyway ++$i, $b = (%b)[0] if $n == 1; for my $v(qw(t x y z)) {++$i, $b = 't' if $n > 1 and exists($b{$v}); } $i == 1 or croak "Please specify a single variable to differentiate by"; } #_______________________________________________________________________ # Each term #_______________________________________________________________________ my @t = (); for my $t($c->t) {my %V = $t->split; my $T = $V{t}->z->clone->z; my ($S, $D, $E, $L) = @V{qw(s d e l)}; my $s = $S->d($b) if $S; my $d = $D->d($b) if $D; my $e = $E->d($b) if $E; my $l = $L->d($b) if $L; #_______________________________________________________________________ # Differentiate Variables: A*v**n->d == A*n*v**(n-1) #_______________________________________________________________________ {my $v = $T->clone; my $p = $v->vp($b); if ($p != 0) {$v->timesInt($p)->vp($b, $p-1); $v->Sqrt ($S) if $S; $v->Divide($D) if $D; $v->Exp ($E) if $E; $v->Log ($L) if $L; push @t, $v->z; } } #_______________________________________________________________________ # Differentiate Sqrt: A*sqrt(F(x))->d == 1/2*A*f(x)/sqrt(F(x)) #_______________________________________________________________________ if ($S) {my $v = $T->clone->divideInt(2); $v->Divide($D) if $D; $v->Exp ($E) if $E; $v->Log ($L) if $L; push @t, sigma($v->z)->multiply($s)->divide($S->Sqrt)->t; } #_______________________________________________________________________ # Differentiate Divide: A/F(x)->d == -A*f(x)/F(x)**2 #_______________________________________________________________________ if ($D) {my $v = $T->clone->negate; $v->Sqrt($S) if $S; $v->Exp ($E) if $E; $v->Log ($L) if $L; push @t, sigma($v->z)->multiply($d)->divide($D->multiply($D))->t; } #_______________________________________________________________________ # Differentiate Exp: A*exp(F(x))->d == A*f(x)*exp(F(x)) #_______________________________________________________________________ if ($E) {my $v = $T->clone; $v->Sqrt ($S) if $S; $v->Divide($D) if $D; $v->Exp ($E); $v->Log ($L) if $L; push @t, sigma($v->z)->multiply($e)->t; } #_______________________________________________________________________ # Differentiate Log: A*log(F(x))->d == A*f(x)/F(x) #_______________________________________________________________________ if ($L) {my $v = $T->clone; $v->Sqrt ($S) if $S; $v->Divide($D) if $D; $v->Exp ($E) if $E; push @t, sigma($v->z)->multiply($l)->divide($L)->t; } } #_______________________________________________________________________ # Result #_______________________________________________________________________ sigma(@t); } =head3 simplify Simplify just before assignment. There is no general simplification algorithm. So try various methods and see if any simplifications occur. This is cheating really, because the examples will represent these specific transformations as general features which they are not. On the other hand, Mathematics is full of specifics so I suppose its not entirely unacceptable. Simplification cannot be done after every operation as it is inefficient, doing it as part of += ameliorates this inefficiency. Note: += only works as a synonym for simplify() if the left hand side is currently undefined. This can be enforced by using my() as in: my $z += ($x**2+5x+6)/($x+2); =cut sub simplify($) {my ($x) = @_; $x = polynomialDivision($x); $x = eigenValue($x); } #_______________________________________________________________________ # Common factor: find the largest factor in one or more expressions #_______________________________________________________________________ sub commonFactor(@) {return undef unless scalar(@_); return undef unless scalar(keys(%{$_[0]->{t}})); my $p = (values(%{$_[0]->{t}}))[0]; my %v = %{$p->{v}}; # Variables my %s = $p->split; my ($s, $d, $e, $l) = @s{qw(s d e l)}; # Sub expressions my ($C, $D, $I) = ($p->c, $p->d, $p->i); my @t; for my $a(@_) {for my $b($a->t) {push @t, $b; } } for my $t(@t) {my %V = %v; %v = (); for my $v($t->v) {next unless $V{$v}; my $p = $t->vp($v); $v{$v} = ($V{$v} < $p ? $V{$v} : $p); } my %S = $t->split; my ($S, $D, $E, $L) = @S{qw(s d e l)}; # Sub expressions $s = undef unless defined($s) and defined($S) and $S->id eq $s->id; $d = undef unless defined($d) and defined($D) and $D->id eq $d->id; $e = undef unless defined($e) and defined($E) and $E->id eq $e->id; $l = undef unless defined($l) and defined($L) and $L->id eq $l->id; $C = undef unless defined($C) and $C == $t->c; $D = undef unless defined($D) and $D == $t->d; $I = undef unless defined($I) and $I == $t->i; } my $r = term()->one->clone; $r->c($C) if defined($C); $r->d($D) if defined($D); $r->i($I) if defined($I); $r->vp($_, $v{$_}) for(keys(%v)); $r->Sqrt ($s) if defined($s); $r->Divide($d) if defined($d); $r->Exp ($e) if defined($e); $r->Log ($l) if defined($l); sigma($r->z); } #_______________________________________________________________________ # Find term of polynomial of highest degree. #_______________________________________________________________________ sub polynomialTermOfHighestDegree($$) {my ($p, $v) = @_; # Polynomial, variable my $n = 0; # Current highest degree my $t; # Term with this degree for my $T($p->t) {my $N = $T->vp($v); if ($N > $n) {$n = $N; $t = $T; } } ($n, $t); } =head3 polynomialDivide Polynomial divide - divide one polynomial (a) by another (b) in variable v =cut sub polynomialDivide($$$) {my ($p, $q, $v) = @_; my $r = zero()->clone()->z; for(;;) {my ($np, $mp) = $p->polynomialTermOfHighestDegree($v); my ($nq, $mq) = $q->polynomialTermOfHighestDegree($v); last unless $np >= $nq; my $pq = sigma($mp->divide2($mq)); $r = $r->add($pq); $p = $p->subtract($q->multiply($pq)); } return $r if $p->isZero()->{id} == $zero->{id}; undef; } =head3 eigenValue Eigenvalue check =cut sub eigenValue($) {my ($p) = @_; # Find divisors my %d; for my $t($p->t) {my $d = $t->Divide; next unless defined($d); $d{$d->id} = $d; } # Consolidate numerator and denominator my $P = $p ->clone()->z; $P = $P->multiply($d{$_}) for(keys(%d)); my $Q = one()->clone()->z; $Q = $Q->multiply($d{$_}) for(keys(%d)); # Check for P=nQ i.e. for eigenvalue my $cP = $P->commonFactor; my $dP = $P->divide($cP); my $cQ = $Q->commonFactor; my $dQ = $Q->divide($cQ); return $cP->divide($cQ) if $dP->id == $dQ->id; $p; } =head3 polynomialDivision Polynomial division. =cut sub polynomialDivision($) {my ($p) = @_; # Find a plausible indeterminate my %v; # Possible indeterminates my $v; # Polynomial indeterminate my %D; # Divisors for each term # Each term for my $t($p->t) {my @v = $t->v; $v{$_}{$t->vp($_)} = 1 for(@v); my %V = $t->split; my ($S, $D, $E, $L) = @V{qw(s d e l)}; return $p if defined($S) or defined($E) or defined($L); # Each divisor term if (defined($D)) {for my $T($D->t) {my @v = $T->v; $v{$_}{$T->vp($_)} = 1 for(@v); my %V = $T->split; my ($S, $D, $E, $L) = @V{qw(s d e l)}; return $p if defined($S) or defined($D) or defined($E) or defined($L); } $D{$D->id} = $D; } } # Consolidate numerator and denominator my $P = $p ->clone()->z; $P = $P->multiply($D{$_}) for(keys(%D)); my $Q = one()->clone()->z; $Q = $Q->multiply($D{$_}) for(keys(%D)); # Pick a possible indeterminate for(keys(%v)) {delete $v{$_} if scalar(keys(%{$v{$_}})) == 1; } return $p unless scalar(keys(%v)); $v = (keys(%v))[0]; # Divide P by Q my $r; $r = $P->polynomialDivide($Q, $v); return $r if defined($r); $r = $Q->polynomialDivide($P, $v); return one()->divide($r) if defined($r); $p; } =head3 Sqrt Square root of a sum =cut sub Sqrt($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->sqrt2; return sigma($r) if defined($r); } sigma(term()->c(1)->Sqrt($x)->z); } =head3 Exp Exponential (B raised to the power) of a sum =cut sub Exp($) {my ($x) = @_; my $p = term()->one; my @r; for my $t($x->t) {my $r = $t->exp2; $p = $p->multiply($r) if $r; push @r, $t unless $r; } return sigma($p) if scalar(@r) == 0; return sigma($p->clone->Exp(sigma(@r))->z); } =head3 Log Log to base B of a sum =cut sub Log($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->log2; return sigma($r) if defined($r); } sigma(term()->c(1)->Log($x)->z); } =head3 Sin Sine of a sum =cut sub Sin($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->sin2; return sigma($r) if defined($r); } my $a = $i->multiply($x); $i->multiply($half)->multiply($a->negate->Exp->subtract($a->Exp)); } =head3 Cos Cosine of a sum =cut sub Cos($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->cos2; return sigma($r) if defined($r); } my $a = $i->multiply($x); $half->multiply($a->negate->Exp->add($a->Exp)); } =head3 tan, Ssc, csc, cot Tan, sec, csc, cot of a sum =cut sub tan($) {my ($x) = @_; $x->Sin()->divide($x->Cos())} sub sec($) {my ($x) = @_; $one ->divide($x->Cos())} sub csc($) {my ($x) = @_; $one ->divide($x->Sin())} sub cot($) {my ($x) = @_; $x->Cos()->divide($x->Sin())} =head3 sinh Hyperbolic sine of a sum =cut sub sinh($) {my ($x) = @_; return $zero if $x->{id} == $zero->{id}; my $n = $x->negate; sigma (term()->c( 1)->divideInt(2)->Exp($x)->z, term()->c(-1)->divideInt(2)->Exp($n)->z ) } =head3 cosh Hyperbolic cosine of a sum =cut sub cosh($) {my ($x) = @_; return $one if $x->{id} == $zero->{id}; my $n = $x->negate; sigma (term()->c(1)->divideInt(2)->Exp($x)->z, term()->c(1)->divideInt(2)->Exp($n)->z ) } =head3 Tanh, Sech, Csch, Coth Tanh, Sech, Csch, Coth of a sum =cut sub tanh($) {my ($x) = @_; $x->sinh()->divide($x->cosh())} sub sech($) {my ($x) = @_; $one ->divide($x->cosh())} sub csch($) {my ($x) = @_; $one ->divide($x->sinh())} sub coth($) {my ($x) = @_; $x->cosh()->divide($x->sinh())} =head3 dot Dot - complex dot product of two complex sums =cut sub dot($$) {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->re->multiply($b->re)->add($a->im->multiply($b->im)); } =head3 cross The area of the parallelogram formed by two complex sums =cut sub cross($$) {my ($a, $b) = @_; $a->dot($a)->multiply($b->dot($b))->subtract($a->dot($b)->power($two))->Sqrt; } =head3 unit Intersection of a complex sum with the unit circle. =cut sub unit($) {my ($a) = @_; my $b = $a->modulus; my $c = $a->divide($b); $a->divide($a->modulus); } =head3 re Real part of a complex sum =cut sub re($) {my ($A) = @_; $A = newFromString("$A") unless ref($A) eq __PACKAGE__; my @r; for my $a($A->t) {next if $a->i == 1; push @r, $a; } sigma(@r); } =head3 im Imaginary part of a complex sum =cut sub im($) {my ($A) = @_; $A = newFromString("$A") unless ref($A) eq __PACKAGE__; my @r; for my $a($A->t) {next if $a->i == 0; push @r, $a; } $mI->multiply(sigma(@r)); } =head3 modulus Modulus of a complex sum =cut sub modulus($) {my ($a) = @_; $a->re->power($two)->add($a->im->power($two))->Sqrt; } =head3 conjugate Conjugate of a complexs sum =cut sub conjugate($) {my ($a) = @_; $a->re->subtract($a->im->multiply($i)); } =head3 clone Clone =cut sub clone($) {my ($t) = @_; $t->{z} or die "Attempt to clone unfinalized sum"; my $c = bless {%$t}; $c->{t} = {%{$t->{t}}}; delete $c->{z}; delete $c->{s}; delete $c->{id}; $c; } =head3 signature Signature of a sum: used to optimize add(). # Fix the problem of adding different logs =cut sub signature($) {my ($t) = @_; my $s = ''; for my $a($t->t) {$s .= '+'. $a->print; } $s; } =head3 getSignature Get the signature (see L) of a sum =cut sub getSignature($) {my ($t) = @_; exists $t->{z} ? $t->{z} : die "Attempt to get signature of unfinalized sum"; } =head3 id Get Id of sum: each sum has a unique identifying number. =cut sub id($) {my ($t) = @_; $t->{id} or die "Sum $t not yet finalized"; $t->{id}; } =head3 zz Check sum finalized. See: L. =cut sub zz($) {my ($t) = @_; $t->{z} or die "Sum $t not yet finalized"; print $t->{z}, "\n"; $t; } =head3 z Finalize creation of the sum: Once a sum has been finalized it becomes read only. =cut my $lock = 0; # Hash locking my $z = 0; # Term counter my %z; # Terms finalized sub z($) {my ($t) = @_; !exists($t->{z}) or die "Already finalized this term"; my $p = $t->print; return $z{$p} if defined($z{$p}); $z{$p} = $t; weaken($z{$p}); # Reduces memory usage. $t->{s} = $p; $t->{z} = $t->signature; $t->{id} = ++$z; #HashUtil lock_hash(%{$t->{v}}) if $lock; #HashUtil lock_hash %$t if $lock; $t; } #sub DESTROY($) # {my ($t) = @_; # delete $z{$t->{s}} if defined($t) and exists $t->{s}; # } sub lockHashes() {my ($l) = @_; #HashUtil for my $t(values %z) #HashUtil {lock_hash(%{$t->{v}}); #HashUtil lock_hash %$t; #HashUtil } $lock = 1; } =head3 print Print sum =cut sub print($) {my ($t) = @_; return $t->{s} if defined($t->{s}); my $s = ''; for my $a($t->t) {$s .= $a->print .'+'; } chop($s) if $s; $s =~ s/^\+//; $s =~ s/\+\-/\-/g; $s =~ s/\+1\*/\+/g; # change: +1* to + $s =~ s/\*1\*/\*/g; # remove: *1* to * $s =~ s/^1\*//g; # remove: 1* at start of expression $s =~ s/^\-1\*/\-/g; # change: -1* at start of expression to - $s =~ s/^0\+//g; # change: 0+ at start of expression to $s =~ s/\+0$//; # remove: +0 at end of expression $s =~ s#\(\+0\+#\(#g; # change: (+0+ to ( $s =~ s/\(\+/\(/g; # change: (+ to ( $s =~ s/\(1\*/\(/g; # change: (1* to ( $s =~ s/\(\-1\*/\(\-/g; # change: (-1* to (- $s =~ s/([a-zA-Z0-9)])\-1\*/$1\-/g; # change: term-1* to term- $s =~ s/\*(\$[a-zA-Z]+)\*\*\-1(?!\d)/\/$1/g; # change: *$y**-1 to /$y $s =~ s/\*(\$[a-zA-Z]+)\*\*\-(\d+)/\/$1**$2/g; # change: *$y**-n to /$y**n $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-1(?!\d)/1\/$1/g; # change: +-$y**-1 to +-1/$y $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-(\d+)/${1}1\/$2**$3/g; # change: +-$y**-n to +-1/$y**n $s = 0 if $s eq ''; $s; } =head3 constants Useful constants =cut $zero = sigma(term('0')); sub zero() {$zero} $one = sigma(term('1')); sub one() {$one} $two = sigma(term('2')); sub two() {$two} $four = sigma(term('4')); sub four() {$four} $mOne = sigma(term('-1')); sub mOne() {$mOne} $i = sigma(term('i')); sub i() {$i} $mI = sigma(term('-i')); sub mI() {$mI} $half = sigma(term('1/2')); sub half() {$half} $mHalf = sigma(term('-1/2')); sub mHalf() {$mHalf} $pi = sigma(term('pi')); sub pi() {$pi} =head3 factorize Factorize a number. =cut @primes = qw( 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997); sub factorize($) {my ($n) = @_; my $f; for my $p(@primes) {for(;$n % $p == 0;) {$f->{$p}++; $n /= $p; } last unless $n > $p; } $f; }; =head2 import Export L with either the default name B, or a name supplied by the caller of this package. =cut sub import {my %P = (program=>@_); my %p; $p{lc()} = $P{$_} for(keys(%P)); #_______________________________________________________________________ # New sum constructor - export to calling package. #_______________________________________________________________________ my $s = "package XXXX;\n". <<'END'; no warnings 'redefine'; sub NNNN {return SSSSn(@_); } use warnings 'redefine'; END #_______________________________________________________________________ # Export to calling package. #_______________________________________________________________________ my $name = 'sum'; $name = $p{sum} if exists($p{sum}); my ($main) = caller(); my $pack = __PACKAGE__ . '::'; $s=~ s/XXXX/$main/g; $s=~ s/NNNN/$name/g; $s=~ s/SSSS/$pack/g; eval($s); #_______________________________________________________________________ # Check options supplied by user #_______________________________________________________________________ delete @p{qw(program sum)}; croak "Unknown option(s): ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p); Valid options are: sum =>'name' Create a routine with this name in the callers namespace to create new symbols. The default is 'sum'. END } =head2 Operators =head3 Operator Overloads Overload Perl operators. Beware the low priority of B<^>. =cut use overload '+' =>\&add3, '-' =>\&negate3, '*' =>\&multiply3, '/' =>\÷3, '**' =>\&power3, '==' =>\&equals3, '!=' =>\&nequal3, 'eq' =>\&negate3, '>' =>\&solve3, '<=>' =>\&tequals3, 'sqrt' =>\&sqrt3, 'exp' =>\&exp3, 'log' =>\&log3, 'tan' =>\&tan3, 'sin' =>\&sin3, 'cos' =>\&cos3, '""' =>\&print3, '^' =>\&dot3, # Beware the low priority of this operator '~' =>\&conjugate3, 'x' =>\&cross3, 'abs' =>\&modulus3, '!' =>\&unit3, fallback=>1; =head3 add3 Add operator. =cut sub add3 {my ($a, $b) = @_; return simplify($a) unless defined($b); # += : simplify() $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Add using unfinalized sums"; $a->add($b); } =head3 negate3 Negate operator. Used in combination with the L operator to perform subtraction. =cut sub negate3 {my ($a, $b, $c) = @_; if (defined($b)) {$b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Negate using unfinalized sums"; return $b->subtract($a) if $c; return $a->subtract($b) unless $c; } else {$a->{z} or die "Negate single unfinalized terms"; return $a->negate; } } =head3 multiply3 Multiply operator. =cut sub multiply3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Multiply using unfinalized sums"; $a->multiply($b); } =head3 divide3 Divide operator. =cut sub divide3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Divide using unfinalized sums"; return $b->divide($a) if $c; return $a->divide($b) unless $c; } =head3 power3 Power operator. =cut sub power3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Power using unfinalized sums"; $a->power($b); } =head3 equals3 Equals operator. =cut sub equals3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Equals using unfinalized sums"; return 1 if $a->{id} == $b->{id}; # Fast equals my $c = $a->subtract($b); return 1 if $c->isZero()->{id} == $zero->{id}; return 0; } =head3 nequal3 Not equal operator. =cut sub nequal3 {my ($a, $b) = @_; !equals3($a, $b); } =head3 tequals Evaluate the expression on the left hand side, stringify it, then compare it for string equality with the string on the right hand side. This operator is useful for making examples written with Test::Simple more readable. =cut sub tequals3 {my ($a, $b) = @_; return 1 if "$a" eq $b; my $z = simplify($a); "$z" eq "$b"; } =head3 solve3 Solve operator. =cut sub solve3 {my ($a, $b) = @_; $a->{z} or die "Solve using unfinalized sum"; # $b =~ /^[a-z]+$/i or croak "Bad variable $b to solve for"; solve($a, $b); } =head3 print3 Print operator. =cut sub print3 {my ($a) = @_; $a->{z} or die "Print of unfinalized sum"; $a->print(); } =head3 sqrt3 Sqrt operator. =cut sub sqrt3 {my ($a) = @_; $a->{z} or die "Sqrt of unfinalized sum"; $a->Sqrt(); } =head3 exp3 Exp operator. =cut sub exp3 {my ($a) = @_; $a->{z} or die "Exp of unfinalized sum"; $a->Exp(); } =head3 sin3 Sine operator. =cut sub sin3 {my ($a) = @_; $a->{z} or die "Sin of unfinalized sum"; $a->Sin(); } =head3 cos3 Cosine operator. =cut sub cos3 {my ($a) = @_; $a->{z} or die "Cos of unfinalized sum"; $a->Cos(); } =head3 tan3 Tan operator. =cut sub tan3 {my ($a) = @_; $a->{z} or die "Tan of unfinalized sum"; $a->tan(); } =head3 log3 Log operator. =cut sub log3 {my ($a) = @_; $a->{z} or die "Log of unfinalized sum"; $a->Log(); } =head3 dot3 Dot Product operator. =cut sub dot3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Dot of unfinalized sum"; dot($a, $b); } =head3 cross3 Cross operator. =cut sub cross3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Cross of unfinalized sum"; cross($a, $b); } =head3 unit3 Unit operator. =cut sub unit3 {my ($a, $b, $c) = @_; $a->{z} or die "Unit of unfinalized sum"; unit($a); } =head3 modulus3 Modulus operator. =cut sub modulus3 {my ($a, $b, $c) = @_; $a->{z} or die "Modulus of unfinalized sum"; modulus($a); } =head3 conjugate3 Conjugate. =cut sub conjugate3 {my ($a, $b, $c) = @_; $a->{z} or die "Conjugate of unfinalized sum"; conjugate($a); } #________________________________________________________________________ # Package installed successfully #________________________________________________________________________ 1; __DATA__ #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =cut =head2 Credits =head3 Author philiprbrenan@yahoo.com =head3 Copyright philiprbrenan@yahoo.com, 2004 =head3 License Perl License. =cut Math-Algebra-Symbols-1.21/lib/Math/Algebra/Symbols/Symbols.pm0100777000175500010010000010653010063422762022347 0ustar philNone =head1 Symbols Symbolic Algebra in Pure Perl. See user manual L. PhilipRBrenan@yahoo.com, 2004, Perl License. =head2 Synopsis This package delivers the public components of package B. =cut package Math::Algebra::Symbols; $VERSION=1.21; use Math::Algebra::Symbols::Sum; use Carp; =head2 import Export components as requested by caller. use Math::Algebra::Symbols symbols=>'s' trig=>1 hyper=>1 complex=>0; Valid options are: =over =item symbols=>'s' Create a function with name B in the callers namespace to create new symbols. The default is B. item trig=>0 The default, no trigonometric functions are exported. item trig=>1 Export trigonometric functions: tan, sec, csc, cot. sin, cos are created by default by overloading the existing Perl sin and cos operators. =item hyper=>0 The default, no hyperbolic functions =item hyper=>1 Export hyperbolic functions: sinh, cosh, tanh, sech, csch, coth. =item complex=>0 The default, no complex functions =item complex=>1 Export complex functions: conjugate, cross, dot, im, modulus, re, unit. =back Trigonometric can be used instead of trig. Hyperbolic can be used instead of hyper. =cut sub import {my %P = (program=>@_); my %p; $p{lc()} = $P{$_} for(keys(%P)); #_ Symbols _____________________________________________________________ # New symbols term constructor - export to calling package. #_______________________________________________________________________ my $s = "package XXXX;\n". <<'END'; no warnings 'redefine'; sub NNNN {return SSSSsum(@_); } END #_ Symbols _____________________________________________________________ # Complex functions: re, im, dot, cross, conjugate, modulus #_______________________________________________________________________ if (exists($p{complex})) {$s .= <<'END'; sub conjugate($) {$_[0]->conjugate()} sub cross ($$) {$_[0]->cross ($_[1])} sub dot ($$) {$_[0]->dot ($_[1])} sub im ($) {$_[0]->im ()} sub modulus ($) {$_[0]->modulus ()} sub re ($) {$_[0]->re ()} sub unit ($) {$_[0]->unit ()} END } #_ Symbols _____________________________________________________________ # Trigonometric functions: tan, sec, csc, cot #_______________________________________________________________________ if (exists($p{trig}) or exists($p{trigonometric})) {$s .= <<'END'; sub tan($) {$_[0]->tan()} sub sec($) {$_[0]->sec()} sub csc($) {$_[0]->csc()} sub cot($) {$_[0]->cot()} END } if (exists($p{trig}) and exists($p{trigonometric})) {croak 'Please use specify just one of trig or trigonometric'; } #_ Symbols _____________________________________________________________ # Hyperbolic functions: sinh, cosh, tanh, sech, csch, coth #_______________________________________________________________________ if (exists($p{hyper}) or exists($p{hyperbolic})) {$s .= <<'END'; sub sinh($) {$_[0]->sinh()} sub cosh($) {$_[0]->cosh()} sub tanh($) {$_[0]->tanh()} sub sech($) {$_[0]->sech()} sub csch($) {$_[0]->csch()} sub coth($) {$_[0]->coth()} END } if (exists($p{hyper}) and exists($p{hyperbolic})) {croak 'Please specify just one of hyper or hyperbolic'; } #_ Symbols _____________________________________________________________ # Export to calling package. #_______________________________________________________________________ $s .= <<'END'; use warnings 'redefine'; END my $name = 'symbols'; $name = $p{symbols} if exists($p{symbols}); my ($main) = caller(); my $pack = __PACKAGE__. '::'; $s=~ s/XXXX/$main/g; $s=~ s/NNNN/$name/g; $s=~ s/SSSS/$pack/g; eval($s); #_ Symbols _____________________________________________________________ # Check options supplied by user #_______________________________________________________________________ delete @p{qw( symbols program trig trigonometric hyper hyperbolic complex )}; croak "Unknown option(s): ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p); Valid options are: symbols=>'symbols' Create a routine with this name in the callers namespace to create new symbols. The default is 'symbols'. trig =>0 The default, no trigonometric functions trig =>1 Export trigonometric functions: tan, sec, csc, cot. sin, cos are created by default by overloading the existing Perl sin and cos operators. trigonometric can be used instead of trig. hyper =>0 The default, no hyperbolic functions hyper =>1 Export hyperbolic functions: sinh, cosh, tanh, sech, csch, coth. hyperbolic can be used instead of hyper. complex=>0 The default, no complex functions complex=>1 Export complex functions: conjugate, cross, dot, im, modulus, re, unit END } #_ Symbols _____________________________________________________________ # Package installed successfully #_______________________________________________________________________ 1; __DATA__ #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =cut =head2 Credits =head3 Author philiprbrenan@yahoo.com =head3 Copyright philiprbrenan@yahoo.com, 2004 =head3 License Perl License. =cut Math-Algebra-Symbols-1.21/lib/Math/Algebra/Symbols/Term.pm0100777000175500010010000016617010063422762021634 0ustar philNone =head1 Terms Symbolic Algebra in Pure Perl: terms. See user manual L. A term represents a product of: variables, coefficents, divisors, square roots, exponentials, and logs. PhilipRBrenan@yahoo.com, 2004, Perl License. =cut package Math::Algebra::Symbols::Term; $VERSION=1.21; use Carp; use Math::BigInt; #HashUtil use Hash::Util qw(lock_hash); use Scalar::Util qw(weaken); =head2 Constructors =head3 new Constructor =cut sub new {bless {c=>1, d=>1, i=>0, v=>{}, sqrt=>undef, divide=>undef, exp=>undef, log=>undef}; } =head3 newFromString New from String =cut sub newFromString($) {my ($a) = @_; return $zero unless $a; my $A = $a; for(;$A =~ /(\d+)\.(\d+)/;) {my $i = $1; my $j = $2; my $l = '0' x length($j); # carp "Replacing $i.$j with $i$j\/1$l in $A"; $A =~ s/$i\.$j/$i$j\/1$l/; } if ($A =~ /^\s*([+-])?(\d+)?(?:\/(\d+))?(i)?(?:\*)?(.*)$/) {my $c = ''; $c = '-'.$c if $1 and $1 eq '-'; $c .= $2 if $2; $c = '1' if $c eq ''; $c = '-1' if $c eq '-'; my $d = ''; $d = $3 if $3; $d = 1 if $d eq ''; my $i = 0; $i = 1 if $4; my $z = new()->c($c)->d($d)->i($i); my $b = $5; for (;$b =~ /^([a-z]+)(?:\*\*)?(\d+)?(?:\*)?(.*)$/i;) {$b = $3; $z->{v}{$1} = $2 if defined($2); $z->{v}{$1} = 1 unless defined($2); } croak "Cannot parse: $a" if $A eq $b; croak "Cannot parse: $b in $a" if $b; return $z->z; } croak "Unable to parse $a"; } =head3 n Short name for L =cut sub n($) {newFromString($_[0]); } =head3 newFromStrings New from Strings =cut sub newFromStrings(@) {return $zero->clone() unless scalar(@_); map {newFromString($_)} @_; } =head3 gcd Greatest Common Divisor. =cut sub gcd($$) {my $x = abs($_[0]); my $y = abs($_[1]); return 1 if $x == 1 or $y == 1; my ($a, $b) = ($x, $y); $a = $y, $b = $x if $y < $a; for(my $r;;) {$r = $b % $a; return $a if $r == 0; ($a, $b) = ($r, $a); } } =head3 lcm Least common multiple. =cut sub lcm($$) {my $x = abs($_[0]); my $y = abs($_[1]); return $x*$y if $x == 1 or $y == 1; $x*$y / gcd($x, $y); } =head3 isTerm Confirm type =cut sub isTerm($) {1}; =head3 intCheck Integer check =cut sub intCheck($$) {my ($i, $m) = @_; return $i if $i == 1; $i =~ /^[\+\-]?\d+/ or die "Integer required for $m not $i"; return Math::BigInt->new($i) if $i > 10_000_000; $i; } =head3 c Coefficient =cut sub c($;$) {my ($t) = @_; return $t->{c} unless @_ > 1; $t->{c} = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'c')); $t; } =head3 d Divisor =cut sub d($;$) {my ($t) = @_; return $t->{d} unless @_ > 1; $t->{d} = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'd')); $t; } =head3 timesInt Multiply term by integer =cut sub timesInt($$) {my ($t) = @_; my $m = ($_[1] ? $_[1] : intCheck($_[1], 'times')); $t->{c} *= $m; if ($t->{d} > 1) {my $g = gcd($t->{c}, $t->{d}); if ($g > 1) {$t->{d} /= $g; $t->{c} /= $g; } } $t; } =head3 divideInt Divide term by integer =cut sub divideInt($$) {my ($t) = @_; my $d = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'divide')); $d != 0 or die "Cannot divide by zero"; $t->{d} *= abs($d); my $g = gcd($t->{d}, $t->{c}); if ($g > 1) {$t->{d} /= $g; $t->{c} /= $g; } $t->{c} = - $t->{c} if $d < 0; $t; } =head3 negate Negate term =cut sub negate($) {my ($t) = @_; $t->{c} = -$t->{c}; $t; } =head3 isZero Zero? =cut sub isZero($) {my ($t) = @_; exists $t->{z} or die "Testing unfinalized term"; $t->{id} == $zero->{id}; } =head3 notZero Not Zero? =cut sub notZero($) {return !isZero($_[0])} =head3 isOne One? =cut sub isOne($) {my ($t) = @_; exists $t->{z} or die "Testing unfinalized term"; $t->{id} == $one->{id}; } =head3 notOne Not One? =cut sub notOne($) {return !isOne($_[0])} =head3 isMinusOne Minus One? =cut sub isMinusOne($) {my ($t) = @_; exists $t->{z} or die "Testing unfinalized term"; $t->{id} == $mOne->{id}; } =head3 notMinusOne Not Minus One? =cut sub notMinusOne($) {return !isMinusOne($_[0])} =head3 i Get/Set i - sqrt(-1) =cut sub i($;$) {my ($t) = @_; return $t->{i} unless(@_) > 1; my $i = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'i')); my $i4 = $i % 4; $t->{i} = $i % 2; $t->{c} = -$t->{c} if $i4 == 2 or $i4 == 3; $t; } =head3 iby i by power: multiply a term by a power of i =cut sub iby($$) {my ($t, $p) = @_; $t->i($p+$t->{i}); $t; } =head3 Divide Get/Set divide by. =cut sub Divide($;$) {my ($t, $d) = @_; return $t->{divide} unless @_ > 1; $t->{divide} = $d; $t; } =head3 removeDivide Remove divide =cut sub removeDivide($) {my ($t) = @_; my $z = $t->clone; delete $z->{divide}; $z->z; } =head3 Sqrt Get/Set square root. =cut sub Sqrt($;$) {my ($t, $s) = @_; return $t->{sqrt} unless @_ > 1; $t->{sqrt} = $s; $t; } =head3 removeSqrt Remove square root. =cut sub removeSqrt($) {my ($t) = @_; my $z = $t->clone; delete $z->{sqrt}; $z->z; } =head3 Exp Get/Set exp =cut sub Exp($;$) {my ($t, $e) = @_; return $t->{exp} unless @_ > 1; $t->{exp} = $e; $t; } =head3 Log # Get/Set log =cut sub Log($$) {my ($t, $l) = @_; return $t->{log} unless @_ > 1; $t->{log} = $l; $t; } =head3 vp Get/Set variable power. On get: returns the power of a variable, or zero if the variable is not present in the term. On set: Sets the power of a variable. If the power is zero, removes the variable from the term. =cut =cut sub vp($$;$) {my ($t, $v) = @_; # $v =~ /^[a-z]+$/i or die "Bad variable name $v"; return exists($t->{v}{$v}) ? $t->{v}{$v} : 0 if @_ == 2; my $p = ($_[2] == 1 ? $_[2] : intCheck($_[2], 'vp')); $t->{v}{$v} = $p if $p; delete $t->{v}{$v} unless $p; $t; } =head3 v Get all variables mentioned in the term. Variables to power zero should have been removed by L. =cut sub v($) {my ($t) = @_; return keys %{$t->{v}}; } =head3 clone Clone a term. The existing term must be finalized, see L: the new term will not be finalized, allowing modifications to be made to it. =cut sub clone($) {my ($t) = @_; $t->{z} or die "Attempt to clone unfinalized term"; my $c = bless {%$t}; $c->{v} = {%{$t->{v}}}; delete @$c{qw(id s z)}; $c; } =head3 split Split a term into its components =cut sub split($) {my ($t) = @_; my $c = $t->clone; my @c = @$c{qw(sqrt divide exp log)}; @$c{qw(sqrt divide exp log)} = ((undef()) x 4); (t=>$c, s=>$c[0], d=>$c[1], e=>$c[2], l=>$c[3]); } =head3 signature Sign the term. Used to optimize addition. Fix the problem of adding different logs =cut sub signature($) {my ($t) = @_; my $s = ''; $s .= sprintf("%010d", $t->{v}{$_}) . $_ for keys %{$t->{v}}; $s .= '(divide'. $t->{divide} .')' if defined($t->{divide}); $s .= '(sqrt'. $t->{sqrt} .')' if defined($t->{sqrt}); $s .= '(exp'. $t->{exp} .')' if defined($t->{exp}); $s .= '(log'. $t->{log} .')' if defined($t->{log}); $s .= 'i' if $t->{i} == 1; $s = '1' if $s eq ''; $s; } =head3 getSignature Get the signature of a term =cut sub getSignature($) {my ($t) = @_; exists $t->{z} ? $t->{z} : die "Attempt to get signature of unfinalized term"; } =head3 add Add two finalized terms, return result in new term or undef. =cut sub add($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to add unfinalized terms"; return undef unless $a->{z} eq $b->{z}; return $a->clone->timesInt(2)->z if $a == $b; my $z = $a->clone; my $c = $a->{c} * $b->{d} + $b->{c} * $a->{d}; my $d = $a->{d} * $b->{d}; return $zero if $c == 0; $z->c($c)->d(1)->divideInt($d)->z; } =head3 subtract Subtract two finalized terms, return result in new term or undef. =cut sub subtract($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to subtract unfinalized terms"; return $zero if $a == $b; return $a if $b == $zero; return $b->clone->negate->z if $a == $zero; return undef unless $a->{z} eq $b->{z}; my $z = $a->clone; my $c = $a->{c} * $b->{d} - $b->{c} * $a->{d}; my $d = $a->{d} * $b->{d}; $z->c($c)->d(1)->divideInt($d)->z; } =head3 multiply Multiply two finalized terms, return the result in a new term or undef =cut sub multiply($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to multiply unfinalized terms"; # Check return undef if (defined($a->{divide}) and defined($b->{divide})) or (defined($a->{sqrt} ) and defined($b->{sqrt})) or (defined($a->{exp} ) and defined($b->{exp})) or (defined($a->{log} ) and defined($b->{log})); # cdi my $c = $a->{c} * $b->{c}; my $d = $a->{d} * $b->{d}; my $i = $a->{i} + $b->{i}; $c = -$c, $i = 0 if $i == 2; my $z = $a->clone->c($c)->d(1)->divideInt($d)->i($i); # v # for my $v($b->v) # {$z->vp($v, $z->vp($v)+$b->vp($v)); # } for my $v(keys(%{$b->{v}})) {$z->vp($v, (exists($z->{v}{$v}) ? $z->{v}{$v} : 0)+$b->{v}{$v}); } # Divide, sqrt, exp, log $z->{divide} = $b->{divide} unless defined($a->{divide}); $z->{sqrt} = $b->{sqrt} unless defined($a->{sqrt}); $z->{exp} = $b->{exp} unless defined($a->{exp}); $z->{log} = $b->{log} unless defined($a->{log}); # Result $z->z; } =head3 divide2 Divide two finalized terms, return the result in a new term or undef =cut sub divide2($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to divide unfinalized terms"; # Check return undef if (defined($b->{divide}) and (!defined($a->{divide}) or $a->{divide}->id != $b->{divide}->id)); return undef if (defined($b->{sqrt} ) and (!defined($a->{sqrt} ) or $a->{sqrt} ->id != $b->{sqrt} ->id)); return undef if (defined($b->{exp} ) and (!defined($a->{exp} ) or $a->{exp} ->id != $b->{exp} ->id)); return undef if (defined($b->{log} ) and (!defined($a->{log} ) or $a->{log} ->id != $b->{log} ->id)); # cdi my $c = $a->{c} * $b->{d}; my $d = $a->{d} * $b->{c}; my $i = $a->{i} - $b->{i}; $c = -$c, $i = 1 if $i == -1; my $g = gcd($c, $d); $c /= $g; $d /= $g; my $z = $a->clone->c($c)->d(1)->divideInt($d)->i($i); # v for my $v($b->v) {$z->vp($v, $z->vp($v)-$b->vp($v)); } # Sqrt, divide, exp, log delete $z->{divide} if defined($a->{divide}) and defined($b->{divide}); delete $z->{sqrt } if defined($a->{sqrt }) and defined($b->{sqrt }); delete $z->{exp } if defined($a->{exp }) and defined($b->{exp }); delete $z->{log } if defined($a->{log }) and defined($b->{log }); # Result $z->z; } =head3 invert Invert a term =cut sub invert($) {my ($t) = @_; $t->{z} or die "Attempt to invert unfinalized term"; # Check return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; # cdi my ($c, $d, $i) = ($t->{c}, $t->{d}, $t->{i}); $c = -$c if $i; my $z = clone($t)->c($d)->d(1)->divideInt($c)->i($i); # v for my $v($z->v) {$z->vp($v, $z->vp($v)); } # Result $z->z; } =head3 power Take power of term =cut sub power($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to take power of unfinalized term"; # Check return $one if $a == $one or $b == $zero; return undef if $a->{divide} or $a->{sqrt} or $a->{exp} or $a->{log}; return undef if $b->{d} != 1 or $b->{i} == 1 or $b->{divide} or $b->{sqrt} or $b->{exp} or $b->{log}; # cdi my ($c, $d, $i) = ($a->{c}, $a->{d}, $a->{i}); my $p = $b->{c}; if ($p < 0) {$a = invert($a); return undef unless $a; $p = -$p; return $a if $p == 1; } my $z = $a->clone->z; $z = $z->multiply($a) for (2..$p); $i *= $p; $z = $z->clone->i($i); # v # for my $v($z->v) # {$z->vp($v, $p*$z->vp($v)); # } # Result $z->z; } =head3 sqrt2 Square root of a term =cut sub sqrt2($) {my ($t) = @_; $t->{z} or die "Attempt to sqrt unfinalized term"; # Check return undef if $t->{i} or $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; # cd my ($c, $d, $i) = ($t->{c}, $t->{d}, 0); $c = -$c, $i = 1 if $c < 0; my $c2 = sqrt($c); return undef unless $c2*$c2 == $c; my $d2 = sqrt($d); return undef unless $d2*$d2 == $d; my $z = clone($t)->c($c2)->d($d2)->i($i); # v for my $v($t->v) {my $p = $z->vp($v); return undef unless $p % 2 == 0; $z->vp($v, $p/2); } # Result $z->z; } =head3 exp2 Exponential of a term =cut sub exp2($) {my ($t) = @_; $t->{z} or die "Attempt to use unfinalized term in exp"; return $one if $t == $zero; return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; return undef unless $t->{i} == 1; return undef unless $t->{d} == 1 or $t->{d} == 2 or $t->{d} == 4; return undef unless scalar(keys(%{$t->{v}})) == 1 and exists($t->{v}{pi}) and $t->{v}{pi} == 1; my $c = $t->{c}; my $d = $t->{d}; $c *= 2 if $d == 1; $c %= 4; return $one if $c == 0; return $i if $c == 1; return $mOne if $c == 2; return $mI if $c == 3; } =head3 sin2 Sine of a term =cut sub sin2($) {my ($t) = @_; $t->{z} or die "Attempt to use unfinalized term in sin"; return $zero if $t == $zero; return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; return undef unless $t->{i} == 0; return undef unless scalar(keys(%{$t->{v}})) == 1; return undef unless exists($t->{v}{pi}); return undef unless $t->{v}{pi} == 1; my $c = $t->{c}; my $d = $t->{d}; return undef unless $d== 1 or $d == 2 or $d == 3 or $d == 6; $c *= 6 if $d == 1; $c *= 3 if $d == 2; $c *= 2 if $d == 3; $c = $c % 12; return $zero if $c == 0; return $half if $c == 1; return undef if $c == 2; return $one if $c == 3; return undef if $c == 4; return $half if $c == 5; return $zero if $c == 6; return $mHalf if $c == 7; return $undef if $c == 8; return $mOne if $c == 9; return $undef if $c == 10; return $mHalf if $c == 11; return $zero if $c == 12; } =head3 cos2 Cosine of a term =cut sub cos2($) {my ($t) = @_; $t->{z} or die "Attempt to use unfinalized term in cos"; return $one if $t == $zero; return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; return undef unless $t->{i} == 0; return undef unless scalar(keys(%{$t->{v}})) == 1; return undef unless exists($t->{v}{pi}); return undef unless $t->{v}{pi} == 1; my $c = $t->{c}; my $d = $t->{d}; return undef unless $d== 1 or $d == 2 or $d == 3 or $d == 6; $c *= 6 if $d == 1; $c *= 3 if $d == 2; $c *= 2 if $d == 3; $c = $c % 12; return $half if $c == 10; return $undef if $c == 11; return $one if $c == 12; return $one if $c == 0; return undef if $c == 1; return $half if $c == 2; return $zero if $c == 3; return $mHalf if $c == 4; return $undef if $c == 5; return $mOne if $c == 6; return $undef if $c == 7; return $mHalf if $c == 8; return $zero if $c == 9; } =head3 log2 Log of a term =cut sub log2($) {my ($a) = @_; $a->{z} or die "Attempt to use unfinalized term in log"; return $zero if $a == $one; return undef; } =head3 id Get Id of a term =cut sub id($) {my ($t) = @_; $t->{id} or die "Term $t not yet finalized"; $t->{id}; } =head3 zz # Check term finalized =cut sub zz($) {my ($t) = @_; $t->{z} or die "Term $t not yet finalized"; $t; } =head3 z Finalize creation of the term. Once a term has been finalized, it becomes readonly, which allows optimization to be performed. =cut =cut my $lock = 0; # Hash locking my $z = 0; # Term counter my %z; # Terms finalized sub z($) {my ($t) = @_; !exists($t->{z}) or die "Already finalized this term"; my $p = $t->print; return $z{$p} if defined($z{$p}); $z{$p} = $t; weaken($z{$p}); # Greatly reduces memory usage $t->{s} = $p; $t->{z} = $t->signature; $t->{id} = ++$z; #HashUtil lock_hash(%{$t->{v}}) if $lock; #HashUtil lock_hash %$t if $lock; $t; } #sub DESTROY($) # {my ($t) = @_; # delete $z{$t->{s}} if defined($t) and exists $t->{s}; # } sub lockHashes() {my ($l) = @_; #HashUtil for my $t(values %z) #HashUtil {lock_hash(%{$t->{v}}); #HashUtil lock_hash %$t; #HashUtil } $lock = 1; } =head3 print Print =cut sub print($) {my ($t) = @_; return $t->{s} if defined($t->{s}); my @k = keys %{$t->{v}}; my $v = $t->{v}; my $s = ''; $s .= $t->{c}; $s .= '/'.$t->{d} if $t->{d} != 1; $s .= '*i' if $t->{i} == 1; $s .= '*$'.$_ for grep {$v->{$_} == 1} @k; $s .= '/$'.$_ for grep {$v->{$_} == -1} @k; $s .= '*$'.$_.'**'. $v->{$_} for grep {$v->{$_} > 1} @k; $s .= '/$'.$_.'**'.-$v->{$_} for grep {$v->{$_} < -1} @k; $s .= '/('. $t->{divide} .')' if $t->{divide}; $s .= '*sqrt('. $t->{sqrt} .')' if $t->{sqrt}; $s .= '*exp('. $t->{exp} .')' if $t->{exp}; $s .= '*log('. $t->{log} .')' if $t->{log}; $s; } =head3 constants Useful constants =cut $zero = new()->c(0)->z; sub zero () {$zero} $one = new()->z; sub one () {$one} $two = new()->c(2)->z; sub two () {$two} $mOne = new()->c(-1)->z; sub mOne () {$mOne} $i = new()->i(1)->z; sub pI () {$pI} $mI = new()->c(-1)->i(1)->z; sub mI () {$mI} $half = new()->c( 1)->d(2)->z; sub half () {$half} $mHalf = new()->c(-1)->d(2)->z; sub mHalf() {$mHalf} $pi = new()->vp('pi', 1)->z; sub pi () {$pi} =head2 import Export L to calling package with a name specifed by the caller, or as B by default. =cut =cut sub import {my %P = (program=>@_); my %p; $p{lc()} = $P{$_} for(keys(%P)); #_______________________________________________________________________ # New symbols term constructor - export to calling package. #_______________________________________________________________________ my $s = "pack"."age XXXX;\n". <<'END'; no warnings 'redefine'; sub NNNN {return SSSSnewFromStrings(@_); } use warnings 'redefine'; END #_______________________________________________________________________ # Export to calling package. #_______________________________________________________________________ my $name = 'term'; $name = $p{term} if exists($p{term}); my ($main) = caller(); my $pack = __PACKAGE__.'::'; $s=~ s/XXXX/$main/g; $s=~ s/NNNN/$name/g; $s=~ s/SSSS/$pack/g; eval($s); #_______________________________________________________________________ # Check options supplied by user #_______________________________________________________________________ delete @p{qw(program terms)}; croak "Unknown option(s) for ". __PACKAGE__ .": ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p); Valid options are: terms=>'name' Desired name of the constructor routine for creating new terms. The default is 'term'. END } =head2 Operators =head3 Operator Overloads Operator Overloads =cut use overload '+' =>\&add3, '-' =>\&negate3, '*' =>\&multiply3, '/' =>\÷3, '**' =>\&power3, '==' =>\&equals3, 'sqrt' =>\&sqrt3, 'exp' =>\&exp3, 'log' =>\&log3, 'sin' =>\&sin3, 'cos' =>\&cos3, '""' =>\&print3, fallback=>1; =head3 add3 Add operator. =cut sub add3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Add using unfinalized terms"; $a->add($b); } =head3 negate3 Negate operator. =cut sub negate3 {my ($a, $b, $c) = @_; if (defined($b)) {$b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Negate using unfinalized terms"; return $b->subtract($a) if $c; return $a->subtract($b) unless $c; } else {$a->{z} or die "Negate single unfinalized terms"; return $a->negate; } } =head3 multiply3 Multiply operator. =cut sub multiply3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Multiply using unfinalized terms"; $a->multiply($b); } =head3 divide3 Divide operator. =cut sub divide3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Divide using unfinalized terms"; return $b->divide2($a) if $c; return $a->divide2($b) unless $c; } =head3 power3 Power operator. =cut sub power3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Power using unfinalized terms"; $a->power($b); } =head3 equals3 Equals operator. =cut sub equals3 {my ($a, $b) = @_; if (ref($b) eq __PACKAGE__) {$a->{z} and $b->{z} or die "Equals using unfinalized terms"; return $a->{id} == $b->{id}; } else {$a->{z} or die "Equals using unfinalized terms"; return $a->print eq "$b"; } } =head3 print3 Print operator. =cut sub print3 {my ($a) = @_; $a->{z} or die "Print of unfinalized term"; $a->print(); } =head3 sqrt3 Square root operator. =cut sub sqrt3 {my ($a) = @_; $a->{z} or die "Sqrt of unfinalized term"; $a->sqrt2(); } =head3 exp3 Exponential operator. =cut sub exp3 {my ($a) = @_; $a->{z} or die "Exp of unfinalized term"; $a->exp2(); } =head3 sin3 Sine operator. =cut sub sin3 {my ($a) = @_; $a->{z} or die "Sin of unfinalized term"; $a->sin2(); } =head3 cos3 Cosine operator. =cut sub cos3 {my ($a) = @_; $a->{z} or die "Cos of unfinalized term"; $a->cos2(); } =head3 log3 Log operator. =cut sub log3 {my ($a) = @_; $a->{z} or die "Log of unfinalized term"; $a->log2(); } =head2 test Tests =cut sub test() {my ($a, $b, $c); # lockHashes(); $a = n(0); $a == $zero or die "100"; $a = n(1); $a == $one or die "101"; $a = n(2); $a == $two or die "102"; $b = n(3); $b == 3 or die "103"; $c = $a+$a; $c == 4 or die "104"; $c = $a+$b; $c == 5 or die "105"; $c = $a+$b+$a+$b; $c == 10 or die "106"; $c = $a+1; $c == 3 or die "107"; $c = $a+2; $c == 4 or die "108"; $c = $b-1; $c == 2 or die "109"; $c = $b-2; $c == 1 or die "110"; $c = $b-9; $c == -6 or die "111"; $c = $a/2; $c == $one or die "112"; $c = $a/4; $c == '1/2' or die "113"; $c = $a*2/2; $c == $two or die "114"; $c = $a*2/4; $c == $one or die "115"; $c = $a**2; $c == 4 or die "116"; $c = $a**10; $c == 1024 or die "117"; $c = sqrt($a**2); $c == $a or die "118"; $d = n(-1); $d == -1 or die "119"; $c = sqrt($d); $c == '1*i' or die "120"; $d = n(4); $d == 4 or die "121"; $c = sqrt($d); $c == 2 or die "122"; $c = n('x*y2')/n('a*b2'); $c == '1*$x/$a*$y**2/$b**2' or die "122"; $a = n('x'); $a == '1*$x' or die "21"; $b = n('2*x**2'); $b == '2*$x**2' or die "22"; $c = $a+$a; $c == '2*$x' or die "23"; $c = $a+$a+$a; $c == '3*$x' or die "24"; $c = $a-$a; $c == $zero or die "25"; $c = $a-$a-$a; $c == '-1*$x' or die "26"; $c = $a*$b; $c == '2*$x**3' or die "27"; $c = $a*$b*$a*$b; $c == '4*$x**6' or die "28"; $c = $b/$a; $c == '2*$x' or die "29"; $c = $a**2/$b; $c == '1/2' or die "29"; $c = sqrt($a**4/($b/2)); $c == $a or die "29"; $a = sin($zero); $a == -0 or die "301"; $a = sin($pi/6); $a == $half or die "302"; $a = sin($pi/2); $a == 1 or die "303"; $a = sin(5*$pi/6); $a == $half or die "304"; $a = sin(120*$pi/120); $a == $zero or die "305"; $a = sin(7*$pi/6); $a == -$half or die "306"; $a = sin(3*$pi/2); $a == -1 or die "307"; $a = sin(110*$pi/ 60); $a == '-1/2' or die "308"; $a = sin(2*$pi); $a == $zero or die "309"; $a = sin(-$zero); $a == $zero or die "311"; $a = sin(-$pi/6); $a == -$half or die "312"; $a = sin(-$pi/2); $a == -$one or die "313"; $a = sin(-5*$pi/6); $a == -$half or die "314"; $a = sin(-120*$pi/120); $a == -$zero or die "315"; $a = sin(-7*$pi/6); $a == $half or die "316"; $a = sin(-3*$pi/2); $a == $one or die "317"; $a = sin(-110*$pi/ 60); $a == $half or die "318"; $a = sin(-2*$pi); $a == $zero or die "319"; $a = cos($zero); $a == $one or die "321"; $a = cos($pi/3); $a == $half or die "322"; $a = cos($pi/2); $a == $zero or die "323"; $a = cos(4*$pi/6); $a == -$half or die "324"; $a = cos(120*$pi/120); $a == -$one or die "325"; $a = cos(8*$pi/6); $a == -$half or die "326"; $a = cos(3*$pi/2); $a == $zero or die "327"; $a = cos(100*$pi/ 60); $a == $half or die "328"; $a = cos(2*$pi); $a == $one or die "329"; $a = cos(-$zero); $a == $one or die "331"; $a = cos(-$pi/3); $a == +$half or die "332"; $a = cos(-$pi/2); $a == $zero or die "333"; $a = cos(-4*$pi/6); $a == -$half or die "334"; $a = cos(-120*$pi/120); $a == -$one or die "335"; $a = cos(-8*$pi/6); $a == -$half or die "336"; $a = cos(-3*$pi/2); $a == $zero or die "337"; $a = cos(-100*$pi/ 60); $a == $half or die "338"; $a = cos(-2*$pi); $a == $one or die "339"; $a = exp($zero); $a == $one or die "340"; $a = exp($i*$pi/2); $a == $i or die "341"; $a = exp($i*$pi); $a == -$one or die "342"; $a = exp(3*$i*$pi/2); $a == -$i or die "343"; $a = exp(4*$i*$pi/2); $a == $one or die "344"; } test unless caller; #_______________________________________________________________________ # Package installed successfully #_______________________________________________________________________ 1; __DATA__ #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =cut =head2 Credits =head3 Author philiprbrenan@yahoo.com =head3 Copyright philiprbrenan@yahoo.com, 2004 =head3 License Perl License. =cut Math-Algebra-Symbols-1.21/lib/Math/Algebra/Symbols.pm0100777000175500010010000010653010063422764020721 0ustar philNone =head1 Symbols Symbolic Algebra in Pure Perl. See user manual L. PhilipRBrenan@yahoo.com, 2004, Perl License. =head2 Synopsis This package delivers the public components of package B. =cut package Math::Algebra::Symbols; $VERSION=1.21; use Math::Algebra::Symbols::Sum; use Carp; =head2 import Export components as requested by caller. use Math::Algebra::Symbols symbols=>'s' trig=>1 hyper=>1 complex=>0; Valid options are: =over =item symbols=>'s' Create a function with name B in the callers namespace to create new symbols. The default is B. item trig=>0 The default, no trigonometric functions are exported. item trig=>1 Export trigonometric functions: tan, sec, csc, cot. sin, cos are created by default by overloading the existing Perl sin and cos operators. =item hyper=>0 The default, no hyperbolic functions =item hyper=>1 Export hyperbolic functions: sinh, cosh, tanh, sech, csch, coth. =item complex=>0 The default, no complex functions =item complex=>1 Export complex functions: conjugate, cross, dot, im, modulus, re, unit. =back Trigonometric can be used instead of trig. Hyperbolic can be used instead of hyper. =cut sub import {my %P = (program=>@_); my %p; $p{lc()} = $P{$_} for(keys(%P)); #_ Symbols _____________________________________________________________ # New symbols term constructor - export to calling package. #_______________________________________________________________________ my $s = "package XXXX;\n". <<'END'; no warnings 'redefine'; sub NNNN {return SSSSsum(@_); } END #_ Symbols _____________________________________________________________ # Complex functions: re, im, dot, cross, conjugate, modulus #_______________________________________________________________________ if (exists($p{complex})) {$s .= <<'END'; sub conjugate($) {$_[0]->conjugate()} sub cross ($$) {$_[0]->cross ($_[1])} sub dot ($$) {$_[0]->dot ($_[1])} sub im ($) {$_[0]->im ()} sub modulus ($) {$_[0]->modulus ()} sub re ($) {$_[0]->re ()} sub unit ($) {$_[0]->unit ()} END } #_ Symbols _____________________________________________________________ # Trigonometric functions: tan, sec, csc, cot #_______________________________________________________________________ if (exists($p{trig}) or exists($p{trigonometric})) {$s .= <<'END'; sub tan($) {$_[0]->tan()} sub sec($) {$_[0]->sec()} sub csc($) {$_[0]->csc()} sub cot($) {$_[0]->cot()} END } if (exists($p{trig}) and exists($p{trigonometric})) {croak 'Please use specify just one of trig or trigonometric'; } #_ Symbols _____________________________________________________________ # Hyperbolic functions: sinh, cosh, tanh, sech, csch, coth #_______________________________________________________________________ if (exists($p{hyper}) or exists($p{hyperbolic})) {$s .= <<'END'; sub sinh($) {$_[0]->sinh()} sub cosh($) {$_[0]->cosh()} sub tanh($) {$_[0]->tanh()} sub sech($) {$_[0]->sech()} sub csch($) {$_[0]->csch()} sub coth($) {$_[0]->coth()} END } if (exists($p{hyper}) and exists($p{hyperbolic})) {croak 'Please specify just one of hyper or hyperbolic'; } #_ Symbols _____________________________________________________________ # Export to calling package. #_______________________________________________________________________ $s .= <<'END'; use warnings 'redefine'; END my $name = 'symbols'; $name = $p{symbols} if exists($p{symbols}); my ($main) = caller(); my $pack = __PACKAGE__. '::'; $s=~ s/XXXX/$main/g; $s=~ s/NNNN/$name/g; $s=~ s/SSSS/$pack/g; eval($s); #_ Symbols _____________________________________________________________ # Check options supplied by user #_______________________________________________________________________ delete @p{qw( symbols program trig trigonometric hyper hyperbolic complex )}; croak "Unknown option(s): ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p); Valid options are: symbols=>'symbols' Create a routine with this name in the callers namespace to create new symbols. The default is 'symbols'. trig =>0 The default, no trigonometric functions trig =>1 Export trigonometric functions: tan, sec, csc, cot. sin, cos are created by default by overloading the existing Perl sin and cos operators. trigonometric can be used instead of trig. hyper =>0 The default, no hyperbolic functions hyper =>1 Export hyperbolic functions: sinh, cosh, tanh, sech, csch, coth. hyperbolic can be used instead of hyper. complex=>0 The default, no complex functions complex=>1 Export complex functions: conjugate, cross, dot, im, modulus, re, unit END } #_ Symbols _____________________________________________________________ # Package installed successfully #_______________________________________________________________________ 1; __DATA__ #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =cut =head2 Credits =head3 Author philiprbrenan@yahoo.com =head3 Copyright philiprbrenan@yahoo.com, 2004 =head3 License Perl License. =cut Math-Algebra-Symbols-1.21/Makefile.PL0100777000175500010010000000051110063422761015676 0ustar philNoneuse ExtUtils::MakeMaker; WriteMakefile (NAME => 'Math::Algebra::Symbols', VERSION => '1.21', ABSTRACT=> 'Symbolic Algebra in Pure Perl', AUTHOR => 'philiprbrenan@yahoo.com', PREREQ_PM => {'Carp', => '0', 'Scalar::Util' => '0', 'IO::Handle' => '0', 'Math::BigInt' => '0' } ); Math-Algebra-Symbols-1.21/MANIFEST0100777000175500010010000000155210063422764015066 0ustar philNoneMakefile.PL README CHANGES userGuide pod/CHANGES.pod pod/README.pod pod/userGuide.pod MANIFEST META.yml symbols.pl lib/Math/Algebra/Symbols.pm lib/Math/Algebra/Symbols/Sum.pm lib/Math/Algebra/Symbols/Symbols.pm lib/Math/Algebra/Symbols/Term.pm t/abs.t t/bigInt.t t/bug_2004_6_1.t t/bug_2004_6_2.t t/bug_2004_6_5.t t/complex.t t/conjugate.t t/conjugate2.t t/constants.t t/cos.t t/cross.t t/differentiation.t t/dot.t t/dotCross.t t/ellipse.t t/eq.t t/exp.t t/expd.t t/hyperbola.t t/hyperbolic.t t/ipi.t t/ipi2.t t/ix.t t/logExp.t t/parabola.t t/pentagon.t t/polynomial.t t/quadratic.t t/reIm.t t/simple.t t/simplify.t t/simplify2.t t/sin.t t/sinCos.t t/sinCos2.t t/solve.t t/solve1.t t/solve2.t t/sqrt.t t/sub.t t/symbols.t t/tanh.t t/trigonometric.t t/unit.t t/x2y2.t pod/sum.pod pod/symbols.pod pod/term.podMath-Algebra-Symbols-1.21/META.yml0100777000175500010010000000031710063422764015204 0ustar philNone--- #YAML:1.0 name: Math-Algebra-Symbols version: 1.21 abstract: Symbolic Algebra in Pure Perl license: perl distribution_type: module requires: recommends: build_requires: dynamic_config: 0 Math-Algebra-Symbols-1.21/pod/0040777000175500010010000000000010063422766014516 5ustar philNoneMath-Algebra-Symbols-1.21/pod/CHANGES.pod0100777000175500010010000000553010063422761016270 0ustar philNone=head1 Changes 2004/06/14 1.21 Minor documentation corrections. 2004/06/10 1.20 Capitalized module file names correctly. 2004/06/09 1.19 Lib, Pod cleanup 2004/06/08 Observed transit of Venus in room converted to Camera Obscura. 2004/06/04 1.18 Equation simplification tied to += operator 2004/06/02 1.17 Following further excellent suggestions from Mike Schilli slightly improved notation for equation solving: B is a synonym for B<-> B> is a synonym for B Thus: $x = symbols('x'); ($a, $b) = @{ ($x**2 eq 5*$x+6) > $x }; print "$a,$b\n"; # 2,3 2004/06/01 1.16 Following excellent suggestions from Mike Schilli detect constants with decimal places and convert them to rational fractions, thus 1.2 becomes 12/10. The rationale: using fractions avoids loss of precision, if you only need approximate numerical results, there is no need for symbolic manipulations. 2004/05/28 1.15 Minor changes to documentation. 2004/05/27 1.14 Admiring new CPAN Layout: diff+grep. Updated META.yml requires/recommends to match MANIFEST. Removed use of Hash::Util as this seems to be causing problems with ActiveState. 2004/05/11 1.13 Memory Leak and B problems solved: 1. Solved a memory leak by using Scalar::Util::weaken(). This has reduced memory consumption when running the test cases from 130M to 37M: intermediate expressions are now being freed. I believe that this has been the source of the erratic failures on various platforms, other than my development system, as reported (most helpfully) by CPAN testers and a Mac OSX user: these reported errors appear to have been related to running out of memory. 2. Corrected B which was being correctly reduced to B, but should have been reduced further to B<1>, which was causing the multiply out of B in B to fail. 3. Added the != operator, as this simplifies testing with the test harness. 2004/04/30 1.12 Included PREREQ_PM in Makefile as a result of notes from automated CPAN testing. 2004/04/24 1.08 Included test cases in manifest and renamed to .t in line with observed practice. 2004/03/28 Split into three classes. Considerable performance improvements. Added standard Perl Test harness. Unfixed power() so we are back to constant powers. 2004/03/14 Fixed power() so that it recognizes constant and variable powers. Added TODO List. Finished testSM(): new requirements in TODO. 2004/03/13 Added change log on esteemed advice of Steffen Müller. Made yet another attempt to stop polynomialDivide() from producing an infinite series as a representation of a single term. Most of mathematics seems to erupt from the division of one polynomial by another. =cut Math-Algebra-Symbols-1.21/pod/README.pod0100777000175500010010000000421210063422761016151 0ustar philNone =head1 README: Math::Algebra::Symbols Symbolic Algebra in Pure Perl This package supplies a set of functions and operators to manipulate Perl expressions algebraically: Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 INSTALL This is alpha code. It is written in pure Perl. It uses the standard Perl install mechanism. Download from CPAN, untar and: perl Makefile.PL make make test make install If you are on Windows, use nmake, available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe Following the excellent work done by Steffen Müller (CPAN Author: SMUELLER), I believe that we should try to capture all known Mathematics symbolically in Perl. Indeed, can you say that you know any Mathematics at all if you cannot explain it in Pure Perl? For bug reports or suggestions please send email to: philiprbrenan@yahoo.com =head1 TO DO Help with this project would be appreciated: Allow substitution and solution for simple expressions like miles/gallon. Recognize sin, cos, sinh, cosh etc. in expressions involving exp. Sigma,Product Taylor series Integrals Fourier Laplace Groups Sets Graphing using Tk. normalizeSqrts(), see t/polynomial.t =cut Math-Algebra-Symbols-1.21/pod/sum.pod0100777000175500010010000022376410063422763016041 0ustar philNone#!perl -w =head1 Sums Symbolic Algebra using Pure Perl: sums. See user manual L. Operations on sums of terms. PhilipRBrenan@yahoo.com, 2004, Perl License. package Math::Algebra::Symbols::Sum; $VERSION=1.21; use Math::Algebra::Symbols::Term; use IO::Handle; use Carp; #HashUtil use Hash::Util qw(lock_hash); use Scalar::Util qw(weaken); =head2 Constructors =head3 new Constructor sub new {bless {t=>{}}; } =head3 newFromString New from String sub newFromString($) {my ($a) = @_; return $zero unless $a; $a .='+'; my @a = $a =~ /(.+?)[\+\-]/g; my @t = map {term($_)} @a; sigma(@t); } =head3 n New from Strings sub n(@) {return $zero unless @_; my @a = map {newFromString($_)} @_; return @a if wantarray; $a[0]; } =head3 sigma Create a sum from a list of terms. sub sigma(@) {return $zero unless scalar(@_); my $z = new(); for my $t(@_) {my $s = $t->signature; if (exists($z->{t}{$s})) {my $a = $z->{t}{$s}->add($t); if ($a->c == 0) {delete $z->{t}{$s}; } else {$z->{t}{$s} = $a; } } else {$z->{t}{$s} = $t } } $z->z; } =head3 makeInt Construct an integer sub makeInt($) {sigma(term()->one->clone->c(shift())->z) } =head2 Methods =head3 isSum Confirm type sub isSum($) {1}; =head3 t Get list of terms from existing sum sub t($) {my ($a) = @_; (map {$a->{t}{$_}} sort(keys(%{$a->{t}}))); } =head3 count Count terms in sum sub count($) {my ($a) = @_; scalar(keys(%{$a->{t}})); } =head3 st Get the single term from a sum containing just one term sub st($) {my ($a) = @_; return (values(%{$a->{t}}))[0] if scalar(keys(%{$a->{t}})) == 1; undef; } =head3 negate Multiply each term in a sum by -1 sub negate($) {my ($s) = @_; my @t; for my $t($s->t) {push @t, $t->clone->timesInt(-1)->z; } sigma(@t); } =head3 add Add two sums together to make a new sum sub add($$) {my ($a, $b) = @_; sigma($a->t, $b->t); } =head3 subtract Subtract one sum from another sub subtract($$) {my ($a, $b) = @_; return $b->negate if $a->{id} == $zero->{id}; $a->add($b->negate); } =head3 Conditional Multiply Multiply two sums if both sums are defined, otherwise return the defined sum. Assumes that at least one sum is defined. sub multiplyC($$) {my ($a, $b) = @_; return $a unless defined($b); return $b unless defined($a); $a->multiply($b); } =head3 multiply Multiply two sums together my %M; # Memoize multiplication sub multiply($$) {my ($A, $B) = @_; my $m = $M{$A->{id}}{$B->{id}}; return $m if defined($m); return $A if $A->{id} == $zero->{id} or $B->{id} == $one->{id}; return $B if $B->{id} == $zero->{id} or $A->{id} == $one->{id}; my @t; # Check for divides that match multiplier my @a = $A->t; for my $a(@a) {my $d = $a->Divide; next unless $d; if ($d->{id} == $B->{id}) {push @t, $a->removeDivide; $a = undef; } } my @b = $B->t; for my $b(@b) {my $d = $b->Divide; next unless $d; if ($d->{id} == $A->{id}) {push @t, $b->removeDivide; $b = undef; } } # Simple multiply for my $aa(@a) {next unless $aa; for my $bb(@b) {next unless $bb; my $m = $aa->multiply($bb); push (@t, $m), next if $m; # Complicated multiply my %a = $aa->split; my %b = $bb->split; my $a = $a{t}; my $b = $b{t}; # Sqrt my $s = 0; $s = $a{s} if $a{s} and $b{s} and $a{s}->{id} == $b{s}->{id}; # Equal sqrts $a->Sqrt(multiplyC($a{s}, $b{s})) unless $s; # Divide $a->Divide(multiplyC($a{d}, $b{d})) if $a{d} or $b{d}; # Exp $a->Exp($a{e} ? $a{e} : $b{e}) if $a{e} xor $b{e}; my $e; if ($a{e} and $b{e}) {my $s = $a{e}->add($b{e}); $e = $s->st; # Check for single term $e = $e->exp2 if defined($e); # Simplify single term if possible $a->Exp($s) unless defined($e); # Reinstate Exp as sum of terms if no simplification possible } # Log $a->Log($a{l} ? $a{l} : $b{l}) if $a{l} xor $b{l}; die "Cannot multiply logs yet" if $a{l} and $b{l}; # Combine results $a = $a->z; $b = $b->z; $a = $a->multiply($b); $a = $a->multiply($e) if defined($e); $a or die "Bad multiply"; push @t, $a unless $s; push @t, sigma($a)->multiply($s)->t if $s; } } # Result my $C = sigma(@t); $M{$A->{id}}{$B->{id}} = $C; $C; } =head3 divide Divide one sum by another sub divide($$) {my ($A, $B) = @_; # Obvious cases $B->{id} == $zero->{id} and croak "Cannot divide by zero"; return $zero if $A->{id} == $zero->{id}; return $A if $B->{id} == $one->{id}; return $A->negate if $B->{id} == $mOne->{id}; # Divide term by term my $a = $A->st; my $b = $B->st; if (defined($a) and defined($b)) {my $c = $a->divide2($b); return sigma($c) if $c; } # Divide sum by term elsif ($b) {ST: for(1..1) {my @t; for my $t($A->t) {my $c = $t->divide2($b); last ST unless $c; push @t, $c; } return sigma(@t); } } # Divide sum by sum my @t; for my $aa($A->t) {my $a = $aa->clone; my $d = $a->Divide; $a->Divide($d->multiply($B)) if $d; $a->Divide($B) unless $d; push @t, $a->z; } # Result sigma(@t); } =head3 sub Substitute a sum for a variable. sub sub($@) {my $E = shift(); my @R = @_; # Each replacement for(;@R > 0;) {my $s = shift @R; # Replace this variable my $w = shift @R; # With this expression my $Z = $zero; $s =~ /^[a-z]+$/i or croak "Can only substitute an expression for a variable, not $s"; $w = newFromString($w) unless ref($w); $w->isSum; # Each term of the sum comprising the replacement expression. for my $t($E->t) {my $n = $t->vp($s); my %t = $t->split; my $S = sigma($t{t}->vp($s, 0)->z); # Remove substitution variable $S = $S->multiply(($t{s}->sub(@_))->Sqrt) if defined($t{s}); $S = $S->divide ($t{d}->sub(@_)) if defined($t{d}); $S = $S->multiply(($t{e}->sub(@_))->Exp) if defined($t{e}); $S = $S->multiply(($t{l}->sub(@_))->Log) if defined($t{l}); $S = $S->multiply($w->power(makeInt($n))) if $n; $Z = $Z->add($S); } $E = $Z; } # Result $E; } =head3 isEqual Check whether one sum is equal to another after multiplying out all divides and divisors. sub isEqual($) {my ($C) = @_; # Until there are no more divides for(;;) {my (%c, $D, $N); $N = 0; # Most frequent divisor for my $t($C->t) {my $d = $t->Divide; next unless $d; my $s = $d->getSignature; if (++$c{$s} > $N) {$N = $c{$s}; $D = $d; } } last unless $N; $C = $C->multiply($D); } # Until there are no more negative powers for(;;) {my %v; for my $t($C->t) {for my $v($t->v) {my $p = $t->vp($v); next unless $p < 0; $p = -$p; $v{$v} = $p if !defined($v{$v}) or $v{$v} < $p; } } last unless scalar(keys(%v)); my $m = term()->one->clone; $m->vp($_, $v{$_}) for keys(%v); my $M = sigma($m->z); $C = $C->multiply($M); } # Result $C; } =head3 normalizeSqrts Normalize sqrts in a sum. This routine needs fixing. It should simplify square roots. sub normalizeSqrts($) {my ($s) = @_; return $s; my (@t, @s); # Find terms with single simple sqrts that can be normalized. for my $t($s->t) {push @t, $t; my $S = $t->Sqrt; next unless $S; # Check for sqrt my $St = $S->st; next unless $St; # Check for single term sqrt my %T = $St->split; # Split single term sqrt next if $T{s} or $T{d} or $T{e} or $T{l}; pop @t; push @s, {t=>$t, s=>$T{t}->z}; # Sqrt with simple single term } # Already normalized unless there are several such terms return $s unless scalar(@s) > 1; # Remove divisor for each normalized term for my $r(@s) {my $d = $r->{t}->d; next unless $d > 1; for my $s(@s) {$s->{t} = $s->{t}->clone->divideInt($d) ->z; $s->{s} = $s->{s}->clone->timesInt ($d*$d)->z; } } # Eliminate duplicate squared factors for my $s(@s) {my $F = factorize($s->{s}->c); my $p = 1; for my $f(keys(%$F)) {$p *= $f**(int($F->{$f}/2)) if $F->{$f} > 1; } $s->{t} = $s->{t}->clone->timesInt ($p) ->z; $s->{s} = $s->{s}->clone->divideInt($p*$p)->z; $DB::single = 1; if ($s->{s}->isOne) { push @t, $s->{t}->removeSqrt; } else { push @t, $s->{t}->clone->Sqrt($s->{$s})->z; } } # Result sigma(@t); } =head3 isEqualSqrt Check whether one sum is equal to another after multiplying out sqrts. sub isEqualSqrt($) {my ($C) = @_; #_______________________________________________________________________ # Each sqrt #_______________________________________________________________________ for(1..99) {$C = $C->normalizeSqrts; my @s = grep { defined($_->Sqrt)} $C->t; my @n = grep {!defined($_->Sqrt)} $C->t; last unless scalar(@s) > 0; #_______________________________________________________________________ # Partition by square roots. #_______________________________________________________________________ my %S = (); for my $t(@s) {my $s = $t->Sqrt; my $S = $s->signature; push @{$S{$S}}, $t; } #_______________________________________________________________________ # Square each partitions, as required by the formulae below. #_______________________________________________________________________ my @t; push @t, sigma(@n)->power($two) if scalar(@n); # Non sqrt partition for my $s(keys(%S)) {push @t, sigma(@{$S{$s}})->power($two); # Sqrt partition } #_______________________________________________________________________ # I can multiply out upto 4 square roots using the formulae below. # There are formula to multiply out more than 4 sqrts, but they are big. # These formulae are obtained by squaring out and rearranging: # sqrt(a)+sqrt(b)+sqrt(c)+sqrt(d) == 0 until no sqrts remain, and # then matching terms to produce optimal execution. # This remarkable result was obtained with the help of this package: # demonstrating its utility in optimizing complex calculations written # in Perl: which in of itself cannot optimize broadly. #_______________________________________________________________________ my $ns = scalar(@t); $ns < 5 or die "There are $ns square roots present. I can handle less than 5"; my ($a, $b, $c, $d) = @t; if ($ns == 1) {$C = $a; } elsif ($ns == 2) {$C = $a-$b; } elsif ($ns == 3) {$C = -$a**2+2*$a*$b-$b**2+2*$c*$a+2*$c*$b-$c**2; } elsif ($ns == 4) {my $a2 = $a * $a; my $a3 = $a2 * $a; my $a4 = $a3 * $a; my $b2 = $b * $b; my $b3 = $b2 * $b; my $b4 = $b3 * $b; my $c2 = $c * $c; my $c3 = $c2 * $c; my $c4 = $c3 * $c; my $d2 = $d * $d; my $d3 = $d2 * $d; my $d4 = $d3 * $d; my $bpd = $b + $d; my $bpc = $b + $c; my $cpd = $c + $d; $C = - ($a4 + $b4 + $c4 + $d4) + 4*( +$a3*($b+$cpd)+$b3*($a+$cpd)+$c3*($a+$bpd)+$d3*($a+$bpc) -$a2*($b *($cpd)+ $c*$d) -$a *($b2*($cpd)+$d2*($bpc)) ) - 6*($a2*$b2+($a2+$b2)*($c2+$d2)+$c2*$d2) - 4*$c*($b2*$d+$b*$d2) - 4*$c2*($a*($bpd)+$b*$d) +40*$c*$a*$b*$d ; } } #________________________________________________________________________ # Test result #________________________________________________________________________ # $C->isEqual($zero); $C; } =head3 isZero Transform a sum assuming that it is equal to zero sub isZero($) {my ($C) = @_; $C->isEqualSqrt->isEqual; } =head3 powerOfTwo Check that a number is a power of two sub powerof2($) {my ($N) = @_; my $n = 0; return undef unless $N > 0; for (;;) {return $n if $N == 1; return undef unless $N % 2 == 0; ++$n; $N /= 2; } } =head3 solve Solve an equation known to be equal to zero for a specified variable. sub solve($$) {my ($A, @x) = @_; croak 'Need variable to solve for' unless scalar(@x) > 0; @x = @{$x[0]} if scalar(@x) == 1 and ref($x[0]) eq 'ARRAY'; # Array of variables supplied my %x; for my $x(@x) {if (!ref $x) {$x =~ /^[a-z]+$/i or croak "Cannot solve for: $x, not a variable name"; } elsif (ref $x eq __PACKAGE__) {my $t = $x->st; $t or die "Cannot solve for multiple terms"; my @b = $t->v; scalar(@b) == 1 or die "Can only solve for one variable"; my $p = $t->vp($b[0]); $p == 1 or die "Can only solve by variable to power 1"; $x = $b[0]; } else {die "$x is not a variable name"; } $x{$x} = 1; } my $x = $x[0]; $B = $A->isZero; # Eliminate sqrts and negative powers # Strike all terms with free variables other than x: i.e. not x and not one of the named constants my @t = (); for my $t($B->t) {my @v = $t->v; push @t, $t; for my $v($t->v) {next if exists($x{$v}); pop @t; last; } } my $C = sigma(@t); # Find highest and lowest power of x my $n = 0; my $N; for my $t($C->t) {my $p = $t->vp($x); $n = $p if $p > $n; $N = $p if !defined($N) or $p < $N; } my $D = $C; $D = $D->multiply(sigma(term()->one->clone->vp($x, -$N)->z)) if $N; $n -= $N if $N; # Find number of terms in x my $c = 0; for my $t($D->t) {++$c if $t->vp($x) > 0; } $n == 0 and croak "Equation not dependant on $x, so cannot solve for $x"; $n > 4 and $c > 1 and croak "Unable to solve polynomial or power $n > 4 in $x (Galois)"; ($n > 2 and $c > 1) and die "Need solver for polynomial of degree $n in $x"; # Solve linear equation if ($n == 1 or $c == 1) {my (@c, @v); for my $t($D->t) {push(@c, $t), next if $t->vp($x) == 0; # Constants push @v, $t; # Powers of x } my $d = sigma(@v)->multiply(sigma(term()->one->clone->vp($x, -$n)->negate->z)); $D = sigma(@c)->divide($d); return $D if $n == 1; my $p = powerof2($n); $p or croak "Fractional power 1/$n of $x unconstructable by sqrt"; $D = $D->Sqrt for(1..$p); return $D; } # Solve quadratic equation if ($n == 2) {my @c = ($one, $one, $one); $c[$_->vp($x)] = $_ for $D->t; $_ = sigma($_->clone->vp($x, 0)->z) for (@c); my ($c, $b, $a) = @c; return [ (-$b->add (($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a)), (-$b->subtract(($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a)) ] } # Check that it works # my $yy = $e->sub($x=>$xx); # $yy == 0 or die "Proposed solution \$$x=$xx does not zero equation $e"; # $xx; } =head3 power Raise a sum to an integer power or an integer/2 power. sub power($$) {my ($a, $b) = @_; return $one if $b->{id} == $zero->{id}; return $a->multiply($a) if $b->{id} == $two->{id}; return $a if $b->{id} == $one->{id}; return $one->divide($a) if $b->{id} == $mOne->{id}; return $a->sqrt if $b->{id} == $half->{id}; return $one->divide($a->sqrt) if $b->{id} == $mHalf->{id}; my $T = $b->st; $T or croak "Power by expression too complicated"; my %t = $T->split; croak "Power by term too complicated" if $t{s} or $t{d} or $t{e} or $t{l}; my $t = $t{t}; $t->i == 0 or croak "Complex power not allowed yet"; my ($p, $d) = ($t->c, $t->d); $d == 1 or $d == 2 or croak "Fractional power other than /2 not allowed yet"; $a = $a->sqrt if $d == 2; return $one->divide($a)->power(sigma(term()->c($p)->z)) if $p < 0; $p = abs($p); my $r = $a; $r = $r->multiply($a) for (2..$p); $r; } =head3 d Differentiate. sub d($;$); sub d($;$) {my $c = $_[0]; # Differentiate this sum my $b = $_[1]; # With this variable #_______________________________________________________________________ # Get differentrix. Assume 'x', 'y', 'z' or 't' if appropriate. #_______________________________________________________________________ if (defined($b)) {if (!ref $b) {$b =~ /^[a-z]+$/i or croak "Cannot differentiate by $b"; } elsif (ref $b eq __PACKAGE__) {my $t = $b->st; $t or die "Cannot differentiate by multiple terms"; my @b = $t->v; scalar(@b) == 1 or die "Can only differentiate by one variable"; my $p = $t->vp($b[0]); $p == 1 or die "Can only differentiate by variable to power 1"; $b = $b[0]; } else {die "Cannot differentiate by $b"; } } else {my %b; for my $t($c->t) {my %b; $b{$_}++ for ($t->v); } my $i = 0; my $n = scalar(keys(%b)); ++$i, $b = 'x' if $n == 0; # Constant expression anyway ++$i, $b = (%b)[0] if $n == 1; for my $v(qw(t x y z)) {++$i, $b = 't' if $n > 1 and exists($b{$v}); } $i == 1 or croak "Please specify a single variable to differentiate by"; } #_______________________________________________________________________ # Each term #_______________________________________________________________________ my @t = (); for my $t($c->t) {my %V = $t->split; my $T = $V{t}->z->clone->z; my ($S, $D, $E, $L) = @V{qw(s d e l)}; my $s = $S->d($b) if $S; my $d = $D->d($b) if $D; my $e = $E->d($b) if $E; my $l = $L->d($b) if $L; #_______________________________________________________________________ # Differentiate Variables: A*v**n->d == A*n*v**(n-1) #_______________________________________________________________________ {my $v = $T->clone; my $p = $v->vp($b); if ($p != 0) {$v->timesInt($p)->vp($b, $p-1); $v->Sqrt ($S) if $S; $v->Divide($D) if $D; $v->Exp ($E) if $E; $v->Log ($L) if $L; push @t, $v->z; } } #_______________________________________________________________________ # Differentiate Sqrt: A*sqrt(F(x))->d == 1/2*A*f(x)/sqrt(F(x)) #_______________________________________________________________________ if ($S) {my $v = $T->clone->divideInt(2); $v->Divide($D) if $D; $v->Exp ($E) if $E; $v->Log ($L) if $L; push @t, sigma($v->z)->multiply($s)->divide($S->Sqrt)->t; } #_______________________________________________________________________ # Differentiate Divide: A/F(x)->d == -A*f(x)/F(x)**2 #_______________________________________________________________________ if ($D) {my $v = $T->clone->negate; $v->Sqrt($S) if $S; $v->Exp ($E) if $E; $v->Log ($L) if $L; push @t, sigma($v->z)->multiply($d)->divide($D->multiply($D))->t; } #_______________________________________________________________________ # Differentiate Exp: A*exp(F(x))->d == A*f(x)*exp(F(x)) #_______________________________________________________________________ if ($E) {my $v = $T->clone; $v->Sqrt ($S) if $S; $v->Divide($D) if $D; $v->Exp ($E); $v->Log ($L) if $L; push @t, sigma($v->z)->multiply($e)->t; } #_______________________________________________________________________ # Differentiate Log: A*log(F(x))->d == A*f(x)/F(x) #_______________________________________________________________________ if ($L) {my $v = $T->clone; $v->Sqrt ($S) if $S; $v->Divide($D) if $D; $v->Exp ($E) if $E; push @t, sigma($v->z)->multiply($l)->divide($L)->t; } } #_______________________________________________________________________ # Result #_______________________________________________________________________ sigma(@t); } =head3 simplify Simplify just before assignment. There is no general simplification algorithm. So try various methods and see if any simplifications occur. This is cheating really, because the examples will represent these specific transformations as general features which they are not. On the other hand, Mathematics is full of specifics so I suppose its not entirely unacceptable. Simplification cannot be done after every operation as it is inefficient, doing it as part of += ameliorates this inefficiency. Note: += only works as a synonym for simplify() if the left hand side is currently undefined. This can be enforced by using my() as in: my $z += ($x**2+5x+6)/($x+2); sub simplify($) {my ($x) = @_; $x = polynomialDivision($x); $x = eigenValue($x); } #_______________________________________________________________________ # Common factor: find the largest factor in one or more expressions #_______________________________________________________________________ sub commonFactor(@) {return undef unless scalar(@_); return undef unless scalar(keys(%{$_[0]->{t}})); my $p = (values(%{$_[0]->{t}}))[0]; my %v = %{$p->{v}}; # Variables my %s = $p->split; my ($s, $d, $e, $l) = @s{qw(s d e l)}; # Sub expressions my ($C, $D, $I) = ($p->c, $p->d, $p->i); my @t; for my $a(@_) {for my $b($a->t) {push @t, $b; } } for my $t(@t) {my %V = %v; %v = (); for my $v($t->v) {next unless $V{$v}; my $p = $t->vp($v); $v{$v} = ($V{$v} < $p ? $V{$v} : $p); } my %S = $t->split; my ($S, $D, $E, $L) = @S{qw(s d e l)}; # Sub expressions $s = undef unless defined($s) and defined($S) and $S->id eq $s->id; $d = undef unless defined($d) and defined($D) and $D->id eq $d->id; $e = undef unless defined($e) and defined($E) and $E->id eq $e->id; $l = undef unless defined($l) and defined($L) and $L->id eq $l->id; $C = undef unless defined($C) and $C == $t->c; $D = undef unless defined($D) and $D == $t->d; $I = undef unless defined($I) and $I == $t->i; } my $r = term()->one->clone; $r->c($C) if defined($C); $r->d($D) if defined($D); $r->i($I) if defined($I); $r->vp($_, $v{$_}) for(keys(%v)); $r->Sqrt ($s) if defined($s); $r->Divide($d) if defined($d); $r->Exp ($e) if defined($e); $r->Log ($l) if defined($l); sigma($r->z); } #_______________________________________________________________________ # Find term of polynomial of highest degree. #_______________________________________________________________________ sub polynomialTermOfHighestDegree($$) {my ($p, $v) = @_; # Polynomial, variable my $n = 0; # Current highest degree my $t; # Term with this degree for my $T($p->t) {my $N = $T->vp($v); if ($N > $n) {$n = $N; $t = $T; } } ($n, $t); } =head3 polynomialDivide Polynomial divide - divide one polynomial (a) by another (b) in variable v sub polynomialDivide($$$) {my ($p, $q, $v) = @_; my $r = zero()->clone()->z; for(;;) {my ($np, $mp) = $p->polynomialTermOfHighestDegree($v); my ($nq, $mq) = $q->polynomialTermOfHighestDegree($v); last unless $np >= $nq; my $pq = sigma($mp->divide2($mq)); $r = $r->add($pq); $p = $p->subtract($q->multiply($pq)); } return $r if $p->isZero()->{id} == $zero->{id}; undef; } =head3 eigenValue Eigenvalue check sub eigenValue($) {my ($p) = @_; # Find divisors my %d; for my $t($p->t) {my $d = $t->Divide; next unless defined($d); $d{$d->id} = $d; } # Consolidate numerator and denominator my $P = $p ->clone()->z; $P = $P->multiply($d{$_}) for(keys(%d)); my $Q = one()->clone()->z; $Q = $Q->multiply($d{$_}) for(keys(%d)); # Check for P=nQ i.e. for eigenvalue my $cP = $P->commonFactor; my $dP = $P->divide($cP); my $cQ = $Q->commonFactor; my $dQ = $Q->divide($cQ); return $cP->divide($cQ) if $dP->id == $dQ->id; $p; } =head3 polynomialDivision Polynomial division. sub polynomialDivision($) {my ($p) = @_; # Find a plausible indeterminate my %v; # Possible indeterminates my $v; # Polynomial indeterminate my %D; # Divisors for each term # Each term for my $t($p->t) {my @v = $t->v; $v{$_}{$t->vp($_)} = 1 for(@v); my %V = $t->split; my ($S, $D, $E, $L) = @V{qw(s d e l)}; return $p if defined($S) or defined($E) or defined($L); # Each divisor term if (defined($D)) {for my $T($D->t) {my @v = $T->v; $v{$_}{$T->vp($_)} = 1 for(@v); my %V = $T->split; my ($S, $D, $E, $L) = @V{qw(s d e l)}; return $p if defined($S) or defined($D) or defined($E) or defined($L); } $D{$D->id} = $D; } } # Consolidate numerator and denominator my $P = $p ->clone()->z; $P = $P->multiply($D{$_}) for(keys(%D)); my $Q = one()->clone()->z; $Q = $Q->multiply($D{$_}) for(keys(%D)); # Pick a possible indeterminate for(keys(%v)) {delete $v{$_} if scalar(keys(%{$v{$_}})) == 1; } return $p unless scalar(keys(%v)); $v = (keys(%v))[0]; # Divide P by Q my $r; $r = $P->polynomialDivide($Q, $v); return $r if defined($r); $r = $Q->polynomialDivide($P, $v); return one()->divide($r) if defined($r); $p; } =head3 Sqrt Square root of a sum sub Sqrt($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->sqrt2; return sigma($r) if defined($r); } sigma(term()->c(1)->Sqrt($x)->z); } =head3 Exp Exponential (B raised to the power) of a sum sub Exp($) {my ($x) = @_; my $p = term()->one; my @r; for my $t($x->t) {my $r = $t->exp2; $p = $p->multiply($r) if $r; push @r, $t unless $r; } return sigma($p) if scalar(@r) == 0; return sigma($p->clone->Exp(sigma(@r))->z); } =head3 Log Log to base B of a sum sub Log($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->log2; return sigma($r) if defined($r); } sigma(term()->c(1)->Log($x)->z); } =head3 Sin Sine of a sum sub Sin($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->sin2; return sigma($r) if defined($r); } my $a = $i->multiply($x); $i->multiply($half)->multiply($a->negate->Exp->subtract($a->Exp)); } =head3 Cos Cosine of a sum sub Cos($) {my ($x) = @_; my $s = $x->st; if (defined($s)) {my $r = $s->cos2; return sigma($r) if defined($r); } my $a = $i->multiply($x); $half->multiply($a->negate->Exp->add($a->Exp)); } =head3 tan, Ssc, csc, cot Tan, sec, csc, cot of a sum sub tan($) {my ($x) = @_; $x->Sin()->divide($x->Cos())} sub sec($) {my ($x) = @_; $one ->divide($x->Cos())} sub csc($) {my ($x) = @_; $one ->divide($x->Sin())} sub cot($) {my ($x) = @_; $x->Cos()->divide($x->Sin())} =head3 sinh Hyperbolic sine of a sum sub sinh($) {my ($x) = @_; return $zero if $x->{id} == $zero->{id}; my $n = $x->negate; sigma (term()->c( 1)->divideInt(2)->Exp($x)->z, term()->c(-1)->divideInt(2)->Exp($n)->z ) } =head3 cosh Hyperbolic cosine of a sum sub cosh($) {my ($x) = @_; return $one if $x->{id} == $zero->{id}; my $n = $x->negate; sigma (term()->c(1)->divideInt(2)->Exp($x)->z, term()->c(1)->divideInt(2)->Exp($n)->z ) } =head3 Tanh, Sech, Csch, Coth Tanh, Sech, Csch, Coth of a sum sub tanh($) {my ($x) = @_; $x->sinh()->divide($x->cosh())} sub sech($) {my ($x) = @_; $one ->divide($x->cosh())} sub csch($) {my ($x) = @_; $one ->divide($x->sinh())} sub coth($) {my ($x) = @_; $x->cosh()->divide($x->sinh())} =head3 dot Dot - complex dot product of two complex sums sub dot($$) {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->re->multiply($b->re)->add($a->im->multiply($b->im)); } =head3 cross The area of the parallelogram formed by two complex sums sub cross($$) {my ($a, $b) = @_; $a->dot($a)->multiply($b->dot($b))->subtract($a->dot($b)->power($two))->Sqrt; } =head3 unit Intersection of a complex sum with the unit circle. sub unit($) {my ($a) = @_; my $b = $a->modulus; my $c = $a->divide($b); $a->divide($a->modulus); } =head3 re Real part of a complex sum sub re($) {my ($A) = @_; $A = newFromString("$A") unless ref($A) eq __PACKAGE__; my @r; for my $a($A->t) {next if $a->i == 1; push @r, $a; } sigma(@r); } =head3 im Imaginary part of a complex sum sub im($) {my ($A) = @_; $A = newFromString("$A") unless ref($A) eq __PACKAGE__; my @r; for my $a($A->t) {next if $a->i == 0; push @r, $a; } $mI->multiply(sigma(@r)); } =head3 modulus Modulus of a complex sum sub modulus($) {my ($a) = @_; $a->re->power($two)->add($a->im->power($two))->Sqrt; } =head3 conjugate Conjugate of a complexs sum sub conjugate($) {my ($a) = @_; $a->re->subtract($a->im->multiply($i)); } =head3 clone Clone sub clone($) {my ($t) = @_; $t->{z} or die "Attempt to clone unfinalized sum"; my $c = bless {%$t}; $c->{t} = {%{$t->{t}}}; delete $c->{z}; delete $c->{s}; delete $c->{id}; $c; } =head3 signature Signature of a sum: used to optimize add(). # Fix the problem of adding different logs sub signature($) {my ($t) = @_; my $s = ''; for my $a($t->t) {$s .= '+'. $a->print; } $s; } =head3 getSignature Get the signature (see L) of a sum sub getSignature($) {my ($t) = @_; exists $t->{z} ? $t->{z} : die "Attempt to get signature of unfinalized sum"; } =head3 id Get Id of sum: each sum has a unique identifying number. sub id($) {my ($t) = @_; $t->{id} or die "Sum $t not yet finalized"; $t->{id}; } =head3 zz Check sum finalized. See: L. sub zz($) {my ($t) = @_; $t->{z} or die "Sum $t not yet finalized"; print $t->{z}, "\n"; $t; } =head3 z Finalize creation of the sum: Once a sum has been finalized it becomes read only. my $lock = 0; # Hash locking my $z = 0; # Term counter my %z; # Terms finalized sub z($) {my ($t) = @_; !exists($t->{z}) or die "Already finalized this term"; my $p = $t->print; return $z{$p} if defined($z{$p}); $z{$p} = $t; weaken($z{$p}); # Reduces memory usage. $t->{s} = $p; $t->{z} = $t->signature; $t->{id} = ++$z; #HashUtil lock_hash(%{$t->{v}}) if $lock; #HashUtil lock_hash %$t if $lock; $t; } #sub DESTROY($) # {my ($t) = @_; # delete $z{$t->{s}} if defined($t) and exists $t->{s}; # } sub lockHashes() {my ($l) = @_; #HashUtil for my $t(values %z) #HashUtil {lock_hash(%{$t->{v}}); #HashUtil lock_hash %$t; #HashUtil } $lock = 1; } =head3 print Print sum sub print($) {my ($t) = @_; return $t->{s} if defined($t->{s}); my $s = ''; for my $a($t->t) {$s .= $a->print .'+'; } chop($s) if $s; $s =~ s/^\+//; $s =~ s/\+\-/\-/g; $s =~ s/\+1\*/\+/g; # change: +1* to + $s =~ s/\*1\*/\*/g; # remove: *1* to * $s =~ s/^1\*//g; # remove: 1* at start of expression $s =~ s/^\-1\*/\-/g; # change: -1* at start of expression to - $s =~ s/^0\+//g; # change: 0+ at start of expression to $s =~ s/\+0$//; # remove: +0 at end of expression $s =~ s#\(\+0\+#\(#g; # change: (+0+ to ( $s =~ s/\(\+/\(/g; # change: (+ to ( $s =~ s/\(1\*/\(/g; # change: (1* to ( $s =~ s/\(\-1\*/\(\-/g; # change: (-1* to (- $s =~ s/([a-zA-Z0-9)])\-1\*/$1\-/g; # change: term-1* to term- $s =~ s/\*(\$[a-zA-Z]+)\*\*\-1(?!\d)/\/$1/g; # change: *$y**-1 to /$y $s =~ s/\*(\$[a-zA-Z]+)\*\*\-(\d+)/\/$1**$2/g; # change: *$y**-n to /$y**n $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-1(?!\d)/1\/$1/g; # change: +-$y**-1 to +-1/$y $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-(\d+)/${1}1\/$2**$3/g; # change: +-$y**-n to +-1/$y**n $s = 0 if $s eq ''; $s; } =head3 constants Useful constants $zero = sigma(term('0')); sub zero() {$zero} $one = sigma(term('1')); sub one() {$one} $two = sigma(term('2')); sub two() {$two} $four = sigma(term('4')); sub four() {$four} $mOne = sigma(term('-1')); sub mOne() {$mOne} $i = sigma(term('i')); sub i() {$i} $mI = sigma(term('-i')); sub mI() {$mI} $half = sigma(term('1/2')); sub half() {$half} $mHalf = sigma(term('-1/2')); sub mHalf() {$mHalf} $pi = sigma(term('pi')); sub pi() {$pi} =head3 factorize Factorize a number. @primes = qw( 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997); sub factorize($) {my ($n) = @_; my $f; for my $p(@primes) {for(;$n % $p == 0;) {$f->{$p}++; $n /= $p; } last unless $n > $p; } $f; }; =head2 import Export L with either the default name B, or a name supplied by the caller of this package. sub import {my %P = (program=>@_); my %p; $p{lc()} = $P{$_} for(keys(%P)); #_______________________________________________________________________ # New sum constructor - export to calling package. #_______________________________________________________________________ my $s = "package XXXX;\n". <<'END'; no warnings 'redefine'; sub NNNN {return SSSSn(@_); } use warnings 'redefine'; END #_______________________________________________________________________ # Export to calling package. #_______________________________________________________________________ my $name = 'sum'; $name = $p{sum} if exists($p{sum}); my ($main) = caller(); my $pack = __PACKAGE__ . '::'; $s=~ s/XXXX/$main/g; $s=~ s/NNNN/$name/g; $s=~ s/SSSS/$pack/g; eval($s); #_______________________________________________________________________ # Check options supplied by user #_______________________________________________________________________ delete @p{qw(program sum)}; croak "Unknown option(s): ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p); Valid options are: sum =>'name' Create a routine with this name in the callers namespace to create new symbols. The default is 'sum'. END } =head2 Operators =head3 Operator Overloads Overload Perl operators. Beware the low priority of B<^>. use overload '+' =>\&add3, '-' =>\&negate3, '*' =>\&multiply3, '/' =>\÷3, '**' =>\&power3, '==' =>\&equals3, '!=' =>\&nequal3, 'eq' =>\&negate3, '>' =>\&solve3, '<=>' =>\&tequals3, 'sqrt' =>\&sqrt3, 'exp' =>\&exp3, 'log' =>\&log3, 'tan' =>\&tan3, 'sin' =>\&sin3, 'cos' =>\&cos3, '""' =>\&print3, '^' =>\&dot3, # Beware the low priority of this operator '~' =>\&conjugate3, 'x' =>\&cross3, 'abs' =>\&modulus3, '!' =>\&unit3, fallback=>1; =head3 add3 Add operator. sub add3 {my ($a, $b) = @_; return simplify($a) unless defined($b); # += : simplify() $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Add using unfinalized sums"; $a->add($b); } =head3 negate3 Negate operator. Used in combination with the L operator to perform subtraction. sub negate3 {my ($a, $b, $c) = @_; if (defined($b)) {$b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Negate using unfinalized sums"; return $b->subtract($a) if $c; return $a->subtract($b) unless $c; } else {$a->{z} or die "Negate single unfinalized terms"; return $a->negate; } } =head3 multiply3 Multiply operator. sub multiply3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Multiply using unfinalized sums"; $a->multiply($b); } =head3 divide3 Divide operator. sub divide3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Divide using unfinalized sums"; return $b->divide($a) if $c; return $a->divide($b) unless $c; } =head3 power3 Power operator. sub power3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Power using unfinalized sums"; $a->power($b); } =head3 equals3 Equals operator. sub equals3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Equals using unfinalized sums"; return 1 if $a->{id} == $b->{id}; # Fast equals my $c = $a->subtract($b); return 1 if $c->isZero()->{id} == $zero->{id}; return 0; } =head3 nequal3 Not equal operator. sub nequal3 {my ($a, $b) = @_; !equals3($a, $b); } =head3 tequals Evaluate the expression on the left hand side, stringify it, then compare it for string equality with the string on the right hand side. This operator is useful for making examples written with Test::Simple more readable. sub tequals3 {my ($a, $b) = @_; return 1 if "$a" eq $b; my $z = simplify($a); "$z" eq "$b"; } =head3 solve3 Solve operator. sub solve3 {my ($a, $b) = @_; $a->{z} or die "Solve using unfinalized sum"; # $b =~ /^[a-z]+$/i or croak "Bad variable $b to solve for"; solve($a, $b); } =head3 print3 Print operator. sub print3 {my ($a) = @_; $a->{z} or die "Print of unfinalized sum"; $a->print(); } =head3 sqrt3 Sqrt operator. sub sqrt3 {my ($a) = @_; $a->{z} or die "Sqrt of unfinalized sum"; $a->Sqrt(); } =head3 exp3 Exp operator. sub exp3 {my ($a) = @_; $a->{z} or die "Exp of unfinalized sum"; $a->Exp(); } =head3 sin3 Sine operator. sub sin3 {my ($a) = @_; $a->{z} or die "Sin of unfinalized sum"; $a->Sin(); } =head3 cos3 Cosine operator. sub cos3 {my ($a) = @_; $a->{z} or die "Cos of unfinalized sum"; $a->Cos(); } =head3 tan3 Tan operator. sub tan3 {my ($a) = @_; $a->{z} or die "Tan of unfinalized sum"; $a->tan(); } =head3 log3 Log operator. sub log3 {my ($a) = @_; $a->{z} or die "Log of unfinalized sum"; $a->Log(); } =head3 dot3 Dot Product operator. sub dot3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Dot of unfinalized sum"; dot($a, $b); } =head3 cross3 Cross operator. sub cross3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Cross of unfinalized sum"; cross($a, $b); } =head3 unit3 Unit operator. sub unit3 {my ($a, $b, $c) = @_; $a->{z} or die "Unit of unfinalized sum"; unit($a); } =head3 modulus3 Modulus operator. sub modulus3 {my ($a, $b, $c) = @_; $a->{z} or die "Modulus of unfinalized sum"; modulus($a); } =head3 conjugate3 Conjugate. sub conjugate3 {my ($a, $b, $c) = @_; $a->{z} or die "Conjugate of unfinalized sum"; conjugate($a); } #________________________________________________________________________ # Package installed successfully #________________________________________________________________________ 1; __DATA__ #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =head2 Credits =head3 Author philiprbrenan@yahoo.com =head3 Copyright philiprbrenan@yahoo.com, 2004 =head3 License Perl License. Math-Algebra-Symbols-1.21/pod/symbols.pod0100777000175500010010000010744010063422763016715 0ustar philNone =head1 Symbols Symbolic Algebra in Pure Perl. See user manual L. PhilipRBrenan@yahoo.com, 2004, Perl License. =head2 Synopsis This package delivers the public components of package B. package Math::Algebra::Symbols; $VERSION=1.21; use Math::Algebra::Symbols::Sum; use Carp; =head2 import Export components as requested by caller. use Math::Algebra::Symbols symbols=>'s' trig=>1 hyper=>1 complex=>0; Valid options are: =over =item symbols=>'s' Create a function with name B in the callers namespace to create new symbols. The default is B. item trig=>0 The default, no trigonometric functions are exported. item trig=>1 Export trigonometric functions: tan, sec, csc, cot. sin, cos are created by default by overloading the existing Perl sin and cos operators. =item hyper=>0 The default, no hyperbolic functions =item hyper=>1 Export hyperbolic functions: sinh, cosh, tanh, sech, csch, coth. =item complex=>0 The default, no complex functions =item complex=>1 Export complex functions: conjugate, cross, dot, im, modulus, re, unit. =back Trigonometric can be used instead of trig. Hyperbolic can be used instead of hyper. sub import {my %P = (program=>@_); my %p; $p{lc()} = $P{$_} for(keys(%P)); #_ Symbols _____________________________________________________________ # New symbols term constructor - export to calling package. #_______________________________________________________________________ my $s = "package XXXX;\n". <<'END'; no warnings 'redefine'; sub NNNN {return SSSSsum(@_); } END #_ Symbols _____________________________________________________________ # Complex functions: re, im, dot, cross, conjugate, modulus #_______________________________________________________________________ if (exists($p{complex})) {$s .= <<'END'; sub conjugate($) {$_[0]->conjugate()} sub cross ($$) {$_[0]->cross ($_[1])} sub dot ($$) {$_[0]->dot ($_[1])} sub im ($) {$_[0]->im ()} sub modulus ($) {$_[0]->modulus ()} sub re ($) {$_[0]->re ()} sub unit ($) {$_[0]->unit ()} END } #_ Symbols _____________________________________________________________ # Trigonometric functions: tan, sec, csc, cot #_______________________________________________________________________ if (exists($p{trig}) or exists($p{trigonometric})) {$s .= <<'END'; sub tan($) {$_[0]->tan()} sub sec($) {$_[0]->sec()} sub csc($) {$_[0]->csc()} sub cot($) {$_[0]->cot()} END } if (exists($p{trig}) and exists($p{trigonometric})) {croak 'Please use specify just one of trig or trigonometric'; } #_ Symbols _____________________________________________________________ # Hyperbolic functions: sinh, cosh, tanh, sech, csch, coth #_______________________________________________________________________ if (exists($p{hyper}) or exists($p{hyperbolic})) {$s .= <<'END'; sub sinh($) {$_[0]->sinh()} sub cosh($) {$_[0]->cosh()} sub tanh($) {$_[0]->tanh()} sub sech($) {$_[0]->sech()} sub csch($) {$_[0]->csch()} sub coth($) {$_[0]->coth()} END } if (exists($p{hyper}) and exists($p{hyperbolic})) {croak 'Please specify just one of hyper or hyperbolic'; } #_ Symbols _____________________________________________________________ # Export to calling package. #_______________________________________________________________________ $s .= <<'END'; use warnings 'redefine'; END my $name = 'symbols'; $name = $p{symbols} if exists($p{symbols}); my ($main) = caller(); my $pack = __PACKAGE__. '::'; $s=~ s/XXXX/$main/g; $s=~ s/NNNN/$name/g; $s=~ s/SSSS/$pack/g; eval($s); #_ Symbols _____________________________________________________________ # Check options supplied by user #_______________________________________________________________________ delete @p{qw( symbols program trig trigonometric hyper hyperbolic complex )}; croak "Unknown option(s): ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p); Valid options are: symbols=>'symbols' Create a routine with this name in the callers namespace to create new symbols. The default is 'symbols'. trig =>0 The default, no trigonometric functions trig =>1 Export trigonometric functions: tan, sec, csc, cot. sin, cos are created by default by overloading the existing Perl sin and cos operators. trigonometric can be used instead of trig. hyper =>0 The default, no hyperbolic functions hyper =>1 Export hyperbolic functions: sinh, cosh, tanh, sech, csch, coth. hyperbolic can be used instead of hyper. complex=>0 The default, no complex functions complex=>1 Export complex functions: conjugate, cross, dot, im, modulus, re, unit END } #_ Symbols _____________________________________________________________ # Package installed successfully #_______________________________________________________________________ 1; __DATA__ #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =head2 Credits =head3 Author philiprbrenan@yahoo.com =head3 Copyright philiprbrenan@yahoo.com, 2004 =head3 License Perl License. Math-Algebra-Symbols-1.21/pod/term.pod0100777000175500010010000017112210063422764016173 0ustar philNone =head1 Terms Symbolic Algebra in Pure Perl: terms. See user manual L. A term represents a product of: variables, coefficents, divisors, square roots, exponentials, and logs. PhilipRBrenan@yahoo.com, 2004, Perl License. package Math::Algebra::Symbols::Term; $VERSION=1.21; use Carp; use Math::BigInt; #HashUtil use Hash::Util qw(lock_hash); use Scalar::Util qw(weaken); =head2 Constructors =head3 new Constructor sub new {bless {c=>1, d=>1, i=>0, v=>{}, sqrt=>undef, divide=>undef, exp=>undef, log=>undef}; } =head3 newFromString New from String sub newFromString($) {my ($a) = @_; return $zero unless $a; my $A = $a; for(;$A =~ /(\d+)\.(\d+)/;) {my $i = $1; my $j = $2; my $l = '0' x length($j); # carp "Replacing $i.$j with $i$j\/1$l in $A"; $A =~ s/$i\.$j/$i$j\/1$l/; } if ($A =~ /^\s*([+-])?(\d+)?(?:\/(\d+))?(i)?(?:\*)?(.*)$/) {my $c = ''; $c = '-'.$c if $1 and $1 eq '-'; $c .= $2 if $2; $c = '1' if $c eq ''; $c = '-1' if $c eq '-'; my $d = ''; $d = $3 if $3; $d = 1 if $d eq ''; my $i = 0; $i = 1 if $4; my $z = new()->c($c)->d($d)->i($i); my $b = $5; for (;$b =~ /^([a-z]+)(?:\*\*)?(\d+)?(?:\*)?(.*)$/i;) {$b = $3; $z->{v}{$1} = $2 if defined($2); $z->{v}{$1} = 1 unless defined($2); } croak "Cannot parse: $a" if $A eq $b; croak "Cannot parse: $b in $a" if $b; return $z->z; } croak "Unable to parse $a"; } =head3 n Short name for L sub n($) {newFromString($_[0]); } =head3 newFromStrings New from Strings sub newFromStrings(@) {return $zero->clone() unless scalar(@_); map {newFromString($_)} @_; } =head3 gcd Greatest Common Divisor. sub gcd($$) {my $x = abs($_[0]); my $y = abs($_[1]); return 1 if $x == 1 or $y == 1; my ($a, $b) = ($x, $y); $a = $y, $b = $x if $y < $a; for(my $r;;) {$r = $b % $a; return $a if $r == 0; ($a, $b) = ($r, $a); } } =head3 lcm Least common multiple. sub lcm($$) {my $x = abs($_[0]); my $y = abs($_[1]); return $x*$y if $x == 1 or $y == 1; $x*$y / gcd($x, $y); } =head3 isTerm Confirm type sub isTerm($) {1}; =head3 intCheck Integer check sub intCheck($$) {my ($i, $m) = @_; return $i if $i == 1; $i =~ /^[\+\-]?\d+/ or die "Integer required for $m not $i"; return Math::BigInt->new($i) if $i > 10_000_000; $i; } =head3 c Coefficient sub c($;$) {my ($t) = @_; return $t->{c} unless @_ > 1; $t->{c} = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'c')); $t; } =head3 d Divisor sub d($;$) {my ($t) = @_; return $t->{d} unless @_ > 1; $t->{d} = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'd')); $t; } =head3 timesInt Multiply term by integer sub timesInt($$) {my ($t) = @_; my $m = ($_[1] ? $_[1] : intCheck($_[1], 'times')); $t->{c} *= $m; if ($t->{d} > 1) {my $g = gcd($t->{c}, $t->{d}); if ($g > 1) {$t->{d} /= $g; $t->{c} /= $g; } } $t; } =head3 divideInt Divide term by integer sub divideInt($$) {my ($t) = @_; my $d = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'divide')); $d != 0 or die "Cannot divide by zero"; $t->{d} *= abs($d); my $g = gcd($t->{d}, $t->{c}); if ($g > 1) {$t->{d} /= $g; $t->{c} /= $g; } $t->{c} = - $t->{c} if $d < 0; $t; } =head3 negate Negate term sub negate($) {my ($t) = @_; $t->{c} = -$t->{c}; $t; } =head3 isZero Zero? sub isZero($) {my ($t) = @_; exists $t->{z} or die "Testing unfinalized term"; $t->{id} == $zero->{id}; } =head3 notZero Not Zero? sub notZero($) {return !isZero($_[0])} =head3 isOne One? sub isOne($) {my ($t) = @_; exists $t->{z} or die "Testing unfinalized term"; $t->{id} == $one->{id}; } =head3 notOne Not One? sub notOne($) {return !isOne($_[0])} =head3 isMinusOne Minus One? sub isMinusOne($) {my ($t) = @_; exists $t->{z} or die "Testing unfinalized term"; $t->{id} == $mOne->{id}; } =head3 notMinusOne Not Minus One? sub notMinusOne($) {return !isMinusOne($_[0])} =head3 i Get/Set i - sqrt(-1) sub i($;$) {my ($t) = @_; return $t->{i} unless(@_) > 1; my $i = ($_[1] == 1 ? $_[1] : intCheck($_[1], 'i')); my $i4 = $i % 4; $t->{i} = $i % 2; $t->{c} = -$t->{c} if $i4 == 2 or $i4 == 3; $t; } =head3 iby i by power: multiply a term by a power of i sub iby($$) {my ($t, $p) = @_; $t->i($p+$t->{i}); $t; } =head3 Divide Get/Set divide by. sub Divide($;$) {my ($t, $d) = @_; return $t->{divide} unless @_ > 1; $t->{divide} = $d; $t; } =head3 removeDivide Remove divide sub removeDivide($) {my ($t) = @_; my $z = $t->clone; delete $z->{divide}; $z->z; } =head3 Sqrt Get/Set square root. sub Sqrt($;$) {my ($t, $s) = @_; return $t->{sqrt} unless @_ > 1; $t->{sqrt} = $s; $t; } =head3 removeSqrt Remove square root. sub removeSqrt($) {my ($t) = @_; my $z = $t->clone; delete $z->{sqrt}; $z->z; } =head3 Exp Get/Set exp sub Exp($;$) {my ($t, $e) = @_; return $t->{exp} unless @_ > 1; $t->{exp} = $e; $t; } =head3 Log # Get/Set log sub Log($$) {my ($t, $l) = @_; return $t->{log} unless @_ > 1; $t->{log} = $l; $t; } =head3 vp Get/Set variable power. On get: returns the power of a variable, or zero if the variable is not present in the term. On set: Sets the power of a variable. If the power is zero, removes the variable from the term. =cut sub vp($$;$) {my ($t, $v) = @_; # $v =~ /^[a-z]+$/i or die "Bad variable name $v"; return exists($t->{v}{$v}) ? $t->{v}{$v} : 0 if @_ == 2; my $p = ($_[2] == 1 ? $_[2] : intCheck($_[2], 'vp')); $t->{v}{$v} = $p if $p; delete $t->{v}{$v} unless $p; $t; } =head3 v Get all variables mentioned in the term. Variables to power zero should have been removed by L. sub v($) {my ($t) = @_; return keys %{$t->{v}}; } =head3 clone Clone a term. The existing term must be finalized, see L: the new term will not be finalized, allowing modifications to be made to it. sub clone($) {my ($t) = @_; $t->{z} or die "Attempt to clone unfinalized term"; my $c = bless {%$t}; $c->{v} = {%{$t->{v}}}; delete @$c{qw(id s z)}; $c; } =head3 split Split a term into its components sub split($) {my ($t) = @_; my $c = $t->clone; my @c = @$c{qw(sqrt divide exp log)}; @$c{qw(sqrt divide exp log)} = ((undef()) x 4); (t=>$c, s=>$c[0], d=>$c[1], e=>$c[2], l=>$c[3]); } =head3 signature Sign the term. Used to optimize addition. Fix the problem of adding different logs sub signature($) {my ($t) = @_; my $s = ''; $s .= sprintf("%010d", $t->{v}{$_}) . $_ for keys %{$t->{v}}; $s .= '(divide'. $t->{divide} .')' if defined($t->{divide}); $s .= '(sqrt'. $t->{sqrt} .')' if defined($t->{sqrt}); $s .= '(exp'. $t->{exp} .')' if defined($t->{exp}); $s .= '(log'. $t->{log} .')' if defined($t->{log}); $s .= 'i' if $t->{i} == 1; $s = '1' if $s eq ''; $s; } =head3 getSignature Get the signature of a term sub getSignature($) {my ($t) = @_; exists $t->{z} ? $t->{z} : die "Attempt to get signature of unfinalized term"; } =head3 add Add two finalized terms, return result in new term or undef. sub add($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to add unfinalized terms"; return undef unless $a->{z} eq $b->{z}; return $a->clone->timesInt(2)->z if $a == $b; my $z = $a->clone; my $c = $a->{c} * $b->{d} + $b->{c} * $a->{d}; my $d = $a->{d} * $b->{d}; return $zero if $c == 0; $z->c($c)->d(1)->divideInt($d)->z; } =head3 subtract Subtract two finalized terms, return result in new term or undef. sub subtract($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to subtract unfinalized terms"; return $zero if $a == $b; return $a if $b == $zero; return $b->clone->negate->z if $a == $zero; return undef unless $a->{z} eq $b->{z}; my $z = $a->clone; my $c = $a->{c} * $b->{d} - $b->{c} * $a->{d}; my $d = $a->{d} * $b->{d}; $z->c($c)->d(1)->divideInt($d)->z; } =head3 multiply Multiply two finalized terms, return the result in a new term or undef sub multiply($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to multiply unfinalized terms"; # Check return undef if (defined($a->{divide}) and defined($b->{divide})) or (defined($a->{sqrt} ) and defined($b->{sqrt})) or (defined($a->{exp} ) and defined($b->{exp})) or (defined($a->{log} ) and defined($b->{log})); # cdi my $c = $a->{c} * $b->{c}; my $d = $a->{d} * $b->{d}; my $i = $a->{i} + $b->{i}; $c = -$c, $i = 0 if $i == 2; my $z = $a->clone->c($c)->d(1)->divideInt($d)->i($i); # v # for my $v($b->v) # {$z->vp($v, $z->vp($v)+$b->vp($v)); # } for my $v(keys(%{$b->{v}})) {$z->vp($v, (exists($z->{v}{$v}) ? $z->{v}{$v} : 0)+$b->{v}{$v}); } # Divide, sqrt, exp, log $z->{divide} = $b->{divide} unless defined($a->{divide}); $z->{sqrt} = $b->{sqrt} unless defined($a->{sqrt}); $z->{exp} = $b->{exp} unless defined($a->{exp}); $z->{log} = $b->{log} unless defined($a->{log}); # Result $z->z; } =head3 divide2 Divide two finalized terms, return the result in a new term or undef sub divide2($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to divide unfinalized terms"; # Check return undef if (defined($b->{divide}) and (!defined($a->{divide}) or $a->{divide}->id != $b->{divide}->id)); return undef if (defined($b->{sqrt} ) and (!defined($a->{sqrt} ) or $a->{sqrt} ->id != $b->{sqrt} ->id)); return undef if (defined($b->{exp} ) and (!defined($a->{exp} ) or $a->{exp} ->id != $b->{exp} ->id)); return undef if (defined($b->{log} ) and (!defined($a->{log} ) or $a->{log} ->id != $b->{log} ->id)); # cdi my $c = $a->{c} * $b->{d}; my $d = $a->{d} * $b->{c}; my $i = $a->{i} - $b->{i}; $c = -$c, $i = 1 if $i == -1; my $g = gcd($c, $d); $c /= $g; $d /= $g; my $z = $a->clone->c($c)->d(1)->divideInt($d)->i($i); # v for my $v($b->v) {$z->vp($v, $z->vp($v)-$b->vp($v)); } # Sqrt, divide, exp, log delete $z->{divide} if defined($a->{divide}) and defined($b->{divide}); delete $z->{sqrt } if defined($a->{sqrt }) and defined($b->{sqrt }); delete $z->{exp } if defined($a->{exp }) and defined($b->{exp }); delete $z->{log } if defined($a->{log }) and defined($b->{log }); # Result $z->z; } =head3 invert Invert a term sub invert($) {my ($t) = @_; $t->{z} or die "Attempt to invert unfinalized term"; # Check return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; # cdi my ($c, $d, $i) = ($t->{c}, $t->{d}, $t->{i}); $c = -$c if $i; my $z = clone($t)->c($d)->d(1)->divideInt($c)->i($i); # v for my $v($z->v) {$z->vp($v, $z->vp($v)); } # Result $z->z; } =head3 power Take power of term sub power($$) {my ($a, $b) = @_; $a->{z} and $b->{z} or die "Attempt to take power of unfinalized term"; # Check return $one if $a == $one or $b == $zero; return undef if $a->{divide} or $a->{sqrt} or $a->{exp} or $a->{log}; return undef if $b->{d} != 1 or $b->{i} == 1 or $b->{divide} or $b->{sqrt} or $b->{exp} or $b->{log}; # cdi my ($c, $d, $i) = ($a->{c}, $a->{d}, $a->{i}); my $p = $b->{c}; if ($p < 0) {$a = invert($a); return undef unless $a; $p = -$p; return $a if $p == 1; } my $z = $a->clone->z; $z = $z->multiply($a) for (2..$p); $i *= $p; $z = $z->clone->i($i); # v # for my $v($z->v) # {$z->vp($v, $p*$z->vp($v)); # } # Result $z->z; } =head3 sqrt2 Square root of a term sub sqrt2($) {my ($t) = @_; $t->{z} or die "Attempt to sqrt unfinalized term"; # Check return undef if $t->{i} or $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; # cd my ($c, $d, $i) = ($t->{c}, $t->{d}, 0); $c = -$c, $i = 1 if $c < 0; my $c2 = sqrt($c); return undef unless $c2*$c2 == $c; my $d2 = sqrt($d); return undef unless $d2*$d2 == $d; my $z = clone($t)->c($c2)->d($d2)->i($i); # v for my $v($t->v) {my $p = $z->vp($v); return undef unless $p % 2 == 0; $z->vp($v, $p/2); } # Result $z->z; } =head3 exp2 Exponential of a term sub exp2($) {my ($t) = @_; $t->{z} or die "Attempt to use unfinalized term in exp"; return $one if $t == $zero; return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; return undef unless $t->{i} == 1; return undef unless $t->{d} == 1 or $t->{d} == 2 or $t->{d} == 4; return undef unless scalar(keys(%{$t->{v}})) == 1 and exists($t->{v}{pi}) and $t->{v}{pi} == 1; my $c = $t->{c}; my $d = $t->{d}; $c *= 2 if $d == 1; $c %= 4; return $one if $c == 0; return $i if $c == 1; return $mOne if $c == 2; return $mI if $c == 3; } =head3 sin2 Sine of a term sub sin2($) {my ($t) = @_; $t->{z} or die "Attempt to use unfinalized term in sin"; return $zero if $t == $zero; return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; return undef unless $t->{i} == 0; return undef unless scalar(keys(%{$t->{v}})) == 1; return undef unless exists($t->{v}{pi}); return undef unless $t->{v}{pi} == 1; my $c = $t->{c}; my $d = $t->{d}; return undef unless $d== 1 or $d == 2 or $d == 3 or $d == 6; $c *= 6 if $d == 1; $c *= 3 if $d == 2; $c *= 2 if $d == 3; $c = $c % 12; return $zero if $c == 0; return $half if $c == 1; return undef if $c == 2; return $one if $c == 3; return undef if $c == 4; return $half if $c == 5; return $zero if $c == 6; return $mHalf if $c == 7; return $undef if $c == 8; return $mOne if $c == 9; return $undef if $c == 10; return $mHalf if $c == 11; return $zero if $c == 12; } =head3 cos2 Cosine of a term sub cos2($) {my ($t) = @_; $t->{z} or die "Attempt to use unfinalized term in cos"; return $one if $t == $zero; return undef if $t->{divide} or $t->{sqrt} or $t->{exp} or $t->{log}; return undef unless $t->{i} == 0; return undef unless scalar(keys(%{$t->{v}})) == 1; return undef unless exists($t->{v}{pi}); return undef unless $t->{v}{pi} == 1; my $c = $t->{c}; my $d = $t->{d}; return undef unless $d== 1 or $d == 2 or $d == 3 or $d == 6; $c *= 6 if $d == 1; $c *= 3 if $d == 2; $c *= 2 if $d == 3; $c = $c % 12; return $half if $c == 10; return $undef if $c == 11; return $one if $c == 12; return $one if $c == 0; return undef if $c == 1; return $half if $c == 2; return $zero if $c == 3; return $mHalf if $c == 4; return $undef if $c == 5; return $mOne if $c == 6; return $undef if $c == 7; return $mHalf if $c == 8; return $zero if $c == 9; } =head3 log2 Log of a term sub log2($) {my ($a) = @_; $a->{z} or die "Attempt to use unfinalized term in log"; return $zero if $a == $one; return undef; } =head3 id Get Id of a term sub id($) {my ($t) = @_; $t->{id} or die "Term $t not yet finalized"; $t->{id}; } =head3 zz # Check term finalized sub zz($) {my ($t) = @_; $t->{z} or die "Term $t not yet finalized"; $t; } =head3 z Finalize creation of the term. Once a term has been finalized, it becomes readonly, which allows optimization to be performed. =cut my $lock = 0; # Hash locking my $z = 0; # Term counter my %z; # Terms finalized sub z($) {my ($t) = @_; !exists($t->{z}) or die "Already finalized this term"; my $p = $t->print; return $z{$p} if defined($z{$p}); $z{$p} = $t; weaken($z{$p}); # Greatly reduces memory usage $t->{s} = $p; $t->{z} = $t->signature; $t->{id} = ++$z; #HashUtil lock_hash(%{$t->{v}}) if $lock; #HashUtil lock_hash %$t if $lock; $t; } #sub DESTROY($) # {my ($t) = @_; # delete $z{$t->{s}} if defined($t) and exists $t->{s}; # } sub lockHashes() {my ($l) = @_; #HashUtil for my $t(values %z) #HashUtil {lock_hash(%{$t->{v}}); #HashUtil lock_hash %$t; #HashUtil } $lock = 1; } =head3 print Print sub print($) {my ($t) = @_; return $t->{s} if defined($t->{s}); my @k = keys %{$t->{v}}; my $v = $t->{v}; my $s = ''; $s .= $t->{c}; $s .= '/'.$t->{d} if $t->{d} != 1; $s .= '*i' if $t->{i} == 1; $s .= '*$'.$_ for grep {$v->{$_} == 1} @k; $s .= '/$'.$_ for grep {$v->{$_} == -1} @k; $s .= '*$'.$_.'**'. $v->{$_} for grep {$v->{$_} > 1} @k; $s .= '/$'.$_.'**'.-$v->{$_} for grep {$v->{$_} < -1} @k; $s .= '/('. $t->{divide} .')' if $t->{divide}; $s .= '*sqrt('. $t->{sqrt} .')' if $t->{sqrt}; $s .= '*exp('. $t->{exp} .')' if $t->{exp}; $s .= '*log('. $t->{log} .')' if $t->{log}; $s; } =head3 constants Useful constants $zero = new()->c(0)->z; sub zero () {$zero} $one = new()->z; sub one () {$one} $two = new()->c(2)->z; sub two () {$two} $mOne = new()->c(-1)->z; sub mOne () {$mOne} $i = new()->i(1)->z; sub pI () {$pI} $mI = new()->c(-1)->i(1)->z; sub mI () {$mI} $half = new()->c( 1)->d(2)->z; sub half () {$half} $mHalf = new()->c(-1)->d(2)->z; sub mHalf() {$mHalf} $pi = new()->vp('pi', 1)->z; sub pi () {$pi} =head2 import Export L to calling package with a name specifed by the caller, or as B by default. =cut sub import {my %P = (program=>@_); my %p; $p{lc()} = $P{$_} for(keys(%P)); #_______________________________________________________________________ # New symbols term constructor - export to calling package. #_______________________________________________________________________ my $s = "pack"."age XXXX;\n". <<'END'; no warnings 'redefine'; sub NNNN {return SSSSnewFromStrings(@_); } use warnings 'redefine'; END #_______________________________________________________________________ # Export to calling package. #_______________________________________________________________________ my $name = 'term'; $name = $p{term} if exists($p{term}); my ($main) = caller(); my $pack = __PACKAGE__.'::'; $s=~ s/XXXX/$main/g; $s=~ s/NNNN/$name/g; $s=~ s/SSSS/$pack/g; eval($s); #_______________________________________________________________________ # Check options supplied by user #_______________________________________________________________________ delete @p{qw(program terms)}; croak "Unknown option(s) for ". __PACKAGE__ .": ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p); Valid options are: terms=>'name' Desired name of the constructor routine for creating new terms. The default is 'term'. END } =head2 Operators =head3 Operator Overloads Operator Overloads use overload '+' =>\&add3, '-' =>\&negate3, '*' =>\&multiply3, '/' =>\÷3, '**' =>\&power3, '==' =>\&equals3, 'sqrt' =>\&sqrt3, 'exp' =>\&exp3, 'log' =>\&log3, 'sin' =>\&sin3, 'cos' =>\&cos3, '""' =>\&print3, fallback=>1; =head3 add3 Add operator. sub add3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Add using unfinalized terms"; $a->add($b); } =head3 negate3 Negate operator. sub negate3 {my ($a, $b, $c) = @_; if (defined($b)) {$b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Negate using unfinalized terms"; return $b->subtract($a) if $c; return $a->subtract($b) unless $c; } else {$a->{z} or die "Negate single unfinalized terms"; return $a->negate; } } =head3 multiply3 Multiply operator. sub multiply3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Multiply using unfinalized terms"; $a->multiply($b); } =head3 divide3 Divide operator. sub divide3 {my ($a, $b, $c) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Divide using unfinalized terms"; return $b->divide2($a) if $c; return $a->divide2($b) unless $c; } =head3 power3 Power operator. sub power3 {my ($a, $b) = @_; $b = newFromString("$b") unless ref($b) eq __PACKAGE__; $a->{z} and $b->{z} or die "Power using unfinalized terms"; $a->power($b); } =head3 equals3 Equals operator. sub equals3 {my ($a, $b) = @_; if (ref($b) eq __PACKAGE__) {$a->{z} and $b->{z} or die "Equals using unfinalized terms"; return $a->{id} == $b->{id}; } else {$a->{z} or die "Equals using unfinalized terms"; return $a->print eq "$b"; } } =head3 print3 Print operator. sub print3 {my ($a) = @_; $a->{z} or die "Print of unfinalized term"; $a->print(); } =head3 sqrt3 Square root operator. sub sqrt3 {my ($a) = @_; $a->{z} or die "Sqrt of unfinalized term"; $a->sqrt2(); } =head3 exp3 Exponential operator. sub exp3 {my ($a) = @_; $a->{z} or die "Exp of unfinalized term"; $a->exp2(); } =head3 sin3 Sine operator. sub sin3 {my ($a) = @_; $a->{z} or die "Sin of unfinalized term"; $a->sin2(); } =head3 cos3 Cosine operator. sub cos3 {my ($a) = @_; $a->{z} or die "Cos of unfinalized term"; $a->cos2(); } =head3 log3 Log operator. sub log3 {my ($a) = @_; $a->{z} or die "Log of unfinalized term"; $a->log2(); } =head2 test Tests sub test() {my ($a, $b, $c); # lockHashes(); $a = n(0); $a == $zero or die "100"; $a = n(1); $a == $one or die "101"; $a = n(2); $a == $two or die "102"; $b = n(3); $b == 3 or die "103"; $c = $a+$a; $c == 4 or die "104"; $c = $a+$b; $c == 5 or die "105"; $c = $a+$b+$a+$b; $c == 10 or die "106"; $c = $a+1; $c == 3 or die "107"; $c = $a+2; $c == 4 or die "108"; $c = $b-1; $c == 2 or die "109"; $c = $b-2; $c == 1 or die "110"; $c = $b-9; $c == -6 or die "111"; $c = $a/2; $c == $one or die "112"; $c = $a/4; $c == '1/2' or die "113"; $c = $a*2/2; $c == $two or die "114"; $c = $a*2/4; $c == $one or die "115"; $c = $a**2; $c == 4 or die "116"; $c = $a**10; $c == 1024 or die "117"; $c = sqrt($a**2); $c == $a or die "118"; $d = n(-1); $d == -1 or die "119"; $c = sqrt($d); $c == '1*i' or die "120"; $d = n(4); $d == 4 or die "121"; $c = sqrt($d); $c == 2 or die "122"; $c = n('x*y2')/n('a*b2'); $c == '1*$x/$a*$y**2/$b**2' or die "122"; $a = n('x'); $a == '1*$x' or die "21"; $b = n('2*x**2'); $b == '2*$x**2' or die "22"; $c = $a+$a; $c == '2*$x' or die "23"; $c = $a+$a+$a; $c == '3*$x' or die "24"; $c = $a-$a; $c == $zero or die "25"; $c = $a-$a-$a; $c == '-1*$x' or die "26"; $c = $a*$b; $c == '2*$x**3' or die "27"; $c = $a*$b*$a*$b; $c == '4*$x**6' or die "28"; $c = $b/$a; $c == '2*$x' or die "29"; $c = $a**2/$b; $c == '1/2' or die "29"; $c = sqrt($a**4/($b/2)); $c == $a or die "29"; $a = sin($zero); $a == -0 or die "301"; $a = sin($pi/6); $a == $half or die "302"; $a = sin($pi/2); $a == 1 or die "303"; $a = sin(5*$pi/6); $a == $half or die "304"; $a = sin(120*$pi/120); $a == $zero or die "305"; $a = sin(7*$pi/6); $a == -$half or die "306"; $a = sin(3*$pi/2); $a == -1 or die "307"; $a = sin(110*$pi/ 60); $a == '-1/2' or die "308"; $a = sin(2*$pi); $a == $zero or die "309"; $a = sin(-$zero); $a == $zero or die "311"; $a = sin(-$pi/6); $a == -$half or die "312"; $a = sin(-$pi/2); $a == -$one or die "313"; $a = sin(-5*$pi/6); $a == -$half or die "314"; $a = sin(-120*$pi/120); $a == -$zero or die "315"; $a = sin(-7*$pi/6); $a == $half or die "316"; $a = sin(-3*$pi/2); $a == $one or die "317"; $a = sin(-110*$pi/ 60); $a == $half or die "318"; $a = sin(-2*$pi); $a == $zero or die "319"; $a = cos($zero); $a == $one or die "321"; $a = cos($pi/3); $a == $half or die "322"; $a = cos($pi/2); $a == $zero or die "323"; $a = cos(4*$pi/6); $a == -$half or die "324"; $a = cos(120*$pi/120); $a == -$one or die "325"; $a = cos(8*$pi/6); $a == -$half or die "326"; $a = cos(3*$pi/2); $a == $zero or die "327"; $a = cos(100*$pi/ 60); $a == $half or die "328"; $a = cos(2*$pi); $a == $one or die "329"; $a = cos(-$zero); $a == $one or die "331"; $a = cos(-$pi/3); $a == +$half or die "332"; $a = cos(-$pi/2); $a == $zero or die "333"; $a = cos(-4*$pi/6); $a == -$half or die "334"; $a = cos(-120*$pi/120); $a == -$one or die "335"; $a = cos(-8*$pi/6); $a == -$half or die "336"; $a = cos(-3*$pi/2); $a == $zero or die "337"; $a = cos(-100*$pi/ 60); $a == $half or die "338"; $a = cos(-2*$pi); $a == $one or die "339"; $a = exp($zero); $a == $one or die "340"; $a = exp($i*$pi/2); $a == $i or die "341"; $a = exp($i*$pi); $a == -$one or die "342"; $a = exp(3*$i*$pi/2); $a == -$i or die "343"; $a = exp(4*$i*$pi/2); $a == $one or die "344"; } test unless caller; #_______________________________________________________________________ # Package installed successfully #_______________________________________________________________________ 1; __DATA__ #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =head2 Credits =head3 Author philiprbrenan@yahoo.com =head3 Copyright philiprbrenan@yahoo.com, 2004 =head3 License Perl License. Math-Algebra-Symbols-1.21/pod/userGuide.pod0100777000175500010010000007365110063422761017165 0ustar philNone #______________________________________________________________________ # User guide. #______________________________________________________________________ =head1 NAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. =head1 SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); =head1 DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from L, L, and L, and processed via L. For examples, see: L. =head2 Symbols Symbols are created with the exported B constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The B routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol B is recognized as the square root of B<-1>. The special symbol B is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Constructor Routine Name If you wish to use a different name for the constructor routine, say B: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); =head3 Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); =head2 Operators L can be combined with L to create symbolic expressions: =head3 Arithmetic operators =head4 Arithmetic Operators: B<+> B<-> B<*> B B<**> Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: B<+=> B<-=> B<*=> B are overloaded to work symbolically rather than numerically. If you need numeric results, you can always B the resulting symbolic expression. =head4 Square root Operator: B Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol B, which allows complex expressions to be processed by Math::Complex. =head4 Exponential Operator: B Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. =head4 Logarithm Operator: B Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base B. Note: the above result is only true for x > 0. B does not include domain and range specifications of the functions it uses. =head4 Sine and Cosine Operators: B and B Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into B as it is in commercial products. Instead: an expression for B is constructed using the complex exponential: L, said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of B to verify such statements from first principles. =head3 Relational operators =head4 Relational operators: B<==>, B Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator B<==> compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. B produces the opposite result. =head4 Relational operator: B Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator B is a synonym for the minus B<-> operator, with the expectation that later on the L function will be used to simplify and rearrange the equation. You may prefer to use B instead of B<-> to enhance readability, there is no functional difference. =head3 Complex operators =head4 Complex operators: the B operator: B<^> Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The B<^> operator has low priority. The B<^> operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. =head4 Complex operators: the B operator: B Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The B operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The B operator returns the area of this parallelogram. Note the space before the B, otherwise Perl is unable to disambiguate the expression correctly. =head4 Complex operators: the B operator: B<~> Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The B<~> operator returns the complex conjugate of its right hand side. =head4 Complex operators: the B operator: B Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The B operator returns the modulus (length) of its right hand side. =head4 Complex operators: the B operator: B Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The B operator returns a complex number of unit length pointing in the same direction as its right hand side. =head3 Equation Manipulation Operators =head4 Equation Manipulation Operators: B operator: B<+=> Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator B<+=> is a synonym for the L method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the L, and the desired pre-condition can always achieved by using B. =head4 Equation Manipulation Operators: B operator: B> Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator B> is a synonym for the L method. The priority of B> is higher than that of B, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the B> operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), B> returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. =head2 Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the L construction routine, or they may be exported into the user's namespace as described in L. =head3 Trigonometric and Hyperbolic functions =head4 Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head4 Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions B, B, B, B, B, B are available, either as exports to the caller's name space, or as methods. =head3 Complex functions =head4 Complex functions: B and B use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The B and B functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. =head4 Complex functions: B and B Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The B and B operators are available as functions, either as exports to the caller's name space, or as methods. =head4 Complex functions: B, B and B Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The B, B and B operators are available as functions: B, B and B, either as exports to the caller's name space, or as methods. The confusion over the naming of: the B operator being the same as the B complex function; arises over the limited set of Perl operator names available for overloading. =head2 Methods =head3 Methods for manipulating Equations =head4 Simplifying equations: B Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); B attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The B<+=> operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of B<=> in this manner. =head4 Substituting into equations: B Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The B function example on line B<#1> demonstrates replacing variables with expressions. The replacement specified for B has no effect as B is not present in this equation. Line B<#2> demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z =head4 Solving equations: B Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); B assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via B if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), B returns an array of symbolic expressions containing the solutions. =head3 Methods for performing Calculus =head4 Differentiation: B Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); B differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of B, B, B, B is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. =head2 Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve B, and incidentally, demonstrating that this curve is a hyperbola. =head1 EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; =over =item trig=>0 The default, do not export trigonometric functions. =item trig=>1 Export trigonometric functions: B, B, B, B to the caller's namespace. B, B are created by default by overloading the existing Perl B and B operators. =item B Alias of B =item hyperbolic=>0 The default, do not export hyperbolic functions. =item hyper=>1 Export hyperbolic functions: B, B, B, B, B, B to the caller's namespace. =item B Alias of B =item complex=>0 The default, do not export complex functions =item complex=>1 Export complex functions: B, B, B, B, B, B, B to the caller's namespace. =back =head1 PACKAGES The B packages manipulate a sum of products representation of an algebraic equation. The B package is the user interface to the functionality supplied by the B and B packages. =head2 Math::Algebra::Symbols::Term B represents a product term. A product term consists of the number B<1>, optionally multiplied by: =over =item Variables any number of variables raised to integer powers, =item Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. =item Sqrt The sqrt of of any symbolic expression representable by the B package, including minus one: represented as B. =item Reciprocal The multiplicative inverse of any symbolic expression representable by the B package: i.e. a B may be divided by any symbolic expression representable by the B package. =item Exp The number B raised to the power of any symbolic expression representable by the B package. =item Log The logarithm to base B of any symbolic expression representable by the B package. =back Thus B can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package B is required. =head2 Math::Algebra::Symbols::Sum B represents a sum of product terms supplied by B and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming B and B as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own B method, with Perl method lookup selecting the appropriate one as required. =head2 Math::Algebra::Symbols Packaging the user functionality alone and separately in package B allows the internal functions to be conveniently hidden from user scripts. =head1 AUTHOR Philip R Brenan at B =cut Math-Algebra-Symbols-1.21/README0100777000175500010010000000447710063422762014624 0ustar philNoneREADME: Math::Algebra::Symbols Symbolic Algebra in Pure Perl This package supplies a set of functions and operators to manipulate Perl expressions algebraically: Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. INSTALL This is alpha code. It is written in pure Perl. It uses the standard Perl install mechanism. Download from CPAN, untar and: perl Makefile.PL make make test make install If you are on Windows, use nmake, available at: http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15 .exe Following the excellent work done by Steffen Müller (CPAN Author: SMUELLER), I believe that we should try to capture all known Mathematics symbolically in Perl. Indeed, can you say that you know any Mathematics at all if you cannot explain it in Pure Perl? For bug reports or suggestions please send email to: philiprbrenan@yahoo.com TO DO Help with this project would be appreciated: Allow substitution and solution for simple expressions like miles/gallon. Recognize sin, cos, sinh, cosh etc. in expressions involving exp. Sigma,Product Taylor series Integrals Fourier Laplace Groups Sets Graphing using Tk. normalizeSqrts(), see t/polynomial.t Math-Algebra-Symbols-1.21/symbols.pl0100777000175500010010000000142510063422761015756 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); Math-Algebra-Symbols-1.21/t/0040777000175500010010000000000010063422766014177 5ustar philNoneMath-Algebra-Symbols-1.21/t/abs.t0100777000175500010010000000101210063422761015116 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); Math-Algebra-Symbols-1.21/t/bigInt.t0100777000175500010010000000061010063422761015570 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); Math-Algebra-Symbols-1.21/t/bug_2004_6_1.t0100777000175500010010000000452510063422761016254 0ustar philNone#!perl -w #______________________________________________________________________ # Convert miles per gallon to liters per 100 kilometers symbolically. # Mike Schilli, m@perlmeister.com, 2004 #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 3; #($gallons, $liters, $miles, $kilometers, $mileage, $consumption) = # symbols(qw(gallons liters miles kilometers mileage consumption)); #$liters = $gallons / 3.7854; # 1: * not / #$kilometers = 1.609 * $miles; #$mileage = $miles/$gallons; #$consumption = $liters / (100 * $kilometers); # 2: Brackets, order give wrong precedence #$mileage = 40; # 3: Overwrites $miles/$gallon #print $consumption, "\n"; #______________________________________________________________________ # Convert miles per gallon to liters per 100 kilometers symbolically. # PhilipRBrenan@yahoo.com, 2004 #______________________________________________________________________ ($gallons, $miles) = symbols(qw(gallons miles)); $liters = $gallons * 3.8; # 4: Have to use fraction, not decimal, improvement needed. $kilometers = $miles * 1.6; # 5: Have to use fraction, not decimal, improvement needed. $consumption = $liters / $kilometers * 100; # 6: Correct precedence print "Liters per 100 kilometers = $consumption\n"; # 7: As a general formula. $miles = 40; # 8: This expresses that the mileage is 40. Mike points out that $gallons = 1; # it would be more natural to express this as $miles/$gallons == 40, print "$miles miles per $gallons gallon = ", eval "$consumption", # 9: Evaluate for a specific example " liters per 100 kilometers\n"; # Liters per 100 kilometers = 475/2*$gallons/$miles # 10: The general formula # 40 miles per 1 gallon = 5.9375 liters per 100 kilometers # 11: A specific example ok("$consumption" eq '475/2*$gallons/$miles'); ok(eval "$consumption" == 5.9375); my ($gallons, $miles) = symbols(qw(gallons miles)); ok($gallons/$miles == 1/($miles/$gallons)); # 12: Check inverse of $miles/$gallon Math-Algebra-Symbols-1.21/t/bug_2004_6_2.t0100777000175500010010000000631310063422761016252 0ustar philNone#!perl -w #______________________________________________________________________ # Solve quadratic equation. # Mike Schilli, m@perlmeister.com, 2004 #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 12; {my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit - $fox)->solve("t")}; print "$a\n$b\n"; ok("$a" eq "1/14*sqrt(305)+5/14"); ok("$b" eq "-1/14*sqrt(305)+5/14"); } #______________________________________________________________________ # Solve quadratic equation. # PhilipRBrenan@yahoo.com, 2004 #______________________________________________________________________ $c = << 'END'; #______________________________________________________________________ # As per Mike, but with **2 and no final eval to show symbolic results. # ($rabbit eq $fox)->solve("t") #______________________________________________________________________ END {my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t ** 2; my ($a, $b) = @{($rabbit eq $fox)->solve("t")}; print "\n$c\n$a\n$b\n"; ok("$a" eq "1/14*sqrt(305)+5/14"); ok("$b" eq "-1/14*sqrt(305)+5/14"); $c = << 'END'; #______________________________________________________________________ # With $a->solve($b) as a synonym for $a->solve("b") # ($rabbit eq $fox)->solve($t) #______________________________________________________________________ END ($a, $b) = @{($rabbit eq $fox)->solve("t")}; print "\n$c\n$a\n$b\n"; ok("$a" eq "1/14*sqrt(305)+5/14"); ok("$b" eq "-1/14*sqrt(305)+5/14"); $c = << 'END'; #______________________________________________________________________ # With $a > "b" as a synonym for $a->solve("b") # $rabbit eq $fox > "t" #______________________________________________________________________ END ($a, $b) = @{($rabbit eq $fox) > "t"}; print "\n$c\n$a\n$b\n"; ok("$a" eq "1/14*sqrt(305)+5/14"); ok("$b" eq "-1/14*sqrt(305)+5/14"); $c = << 'END'; #______________________________________________________________________ # With $a > $b as a synonym for $a->solve($t) # $rabbit eq $fox > $t # Requires version 1.17 #______________________________________________________________________ END ($a, $b) = @{($rabbit eq $fox) > $t}; print "\n$c\n$a\n$b\n"; ok("$a" eq "1/14*sqrt(305)+5/14"); ok("$b" eq "-1/14*sqrt(305)+5/14"); } $c = << 'END'; #______________________________________________________________________ # In terms of variables: # rabbit = rd + rv * t; # fox = fa * t ** 2; # # (rabbit - fox)->solve(qw(t in terms of rd rv fa)); # The resulting equation can then be reused many times. #______________________________________________________________________ END {my ($t, $rd, $rv, $fa) = symbols(qw(t rd rv fa)); my $rabbit = $rd + $rv * $t; my $fox = $fa * $t ** 2; my ($a, $b) = @{($rabbit eq $fox) > [qw(t in terms of rd rv fa)]}; print "\n$c\n$a\n$b\n"; ok("$a" eq '1/2/$fa*sqrt(4*$fa*$rd+$rv**2)+1/2*$rv/$fa'); ok("$b" eq '-1/2/$fa*sqrt(4*$fa*$rd+$rv**2)+1/2*$rv/$fa'); } Math-Algebra-Symbols-1.21/t/bug_2004_6_5.t0100777000175500010010000000156410063422761016260 0ustar philNone#!perl -w -I. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; use strict; use warnings; # As per Mike's example # my ($x) = symbols(x); # my $y = ($x*2 + 5*$x + 6) / ($x + 3); my ($x) = symbols(qw(x)); # Quote the x my $y += ($x**2 + 5*$x + 6) / ($x + 3); # ** not *, note += print "$y\n"; # Print result my $z += ($x**8 - 1)/($x-1); # Additional example, again note += print "$z\n"; # Print result $y = sin($x); print $y, "\n"; use Math::Complex; # Need complex arithmetic $x = 2; my $sx = eval $y; ok($sx>0.90 and $sx<0.91); Math-Algebra-Symbols-1.21/t/complex.t0100777000175500010010000000222710063422761016031 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 21; ($a, $b, $x, $y, $i, $one) = symbols(qw(a b x y i 1)); ok( ($i ^ 1) == 0); ok( ($i ^ $i) == 1); ok( $i x 1 == 1); ok( $i x $i == 0); ok( $one x 1 == 0); ok( !$i == $i); ok( abs $i == 1); ok( re $i == 0); ok( im $i == 1); ok( re $one == 1); ok( im $one == 0); ok( ($i+1) x ($i-1) == 2); ok( (1+$i ^ -1+$i) == 0); ok( ~($x+$y) == ~$x + ~$y); ok( ~($x*$y) == ~$x * ~$y); ok( ~($x**2) == (~$x)** 2); ok( abs($x+$y*$i) == sqrt($x**2+$y**2)); ok( !($x+$y*$i) == ($x+$y*$i) / sqrt($x**2+$y**2)); ok( abs(!($x+$y*$i)) == sqrt($x**2/($x**2+$y**2)+$y**2/($x**2+$y**2))); ok( abs(($a+$i*sqrt(1-$a*$a))*($b+$i*sqrt(1-$b*$b))) == 1); ok( abs($a+$i*$b)*abs($x+$i*$y) == abs(($a+$i*$b)*($x+$i*$y))); Math-Algebra-Symbols-1.21/t/conjugate.t0100777000175500010010000000101210063422761016330 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); Math-Algebra-Symbols-1.21/t/conjugate2.t0100777000175500010010000000077210063422761016426 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); Math-Algebra-Symbols-1.21/t/constants.t0100777000175500010010000000063710063422761016401 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); Math-Algebra-Symbols-1.21/t/cos.t0100777000175500010010000000226610063422761015151 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 22; ($zero, $half, $one, $pi, $x) = symbols(qw(0 1/2 1 pi x)); ok( cos($zero) == $one); ok( cos($pi/3) == $half); ok( cos($pi/2) == $zero); ok( cos(4*$pi/6) == -$half); ok( cos(120*$pi/120) == -$one); ok( cos(8*$pi/6) == -$half); ok( cos(3*$pi/2) == $zero); ok( cos(100*$pi/ 60) == $half); ok( cos(2*$pi) == $one); ok( cos(-$zero) == $one); ok( cos(-$pi/3) == +$half); ok( cos(-$pi/2) == $zero); ok( cos(-4*$pi/6) == -$half); ok( cos(-120*$pi/120) == -$one); ok( cos(-8*$pi/6) == -$half); ok( cos(-3*$pi/2) == $zero); ok( cos(-100*$pi/ 60) == $half); ok( cos(-2*$pi) == $one); ok( cos($x)->d == -sin($x)); ok( cos($x)->d->d == -cos($x)); ok( cos($x)->d->d->d == sin($x)); ok( cos($x)->d->d->d->d == cos($x)); Math-Algebra-Symbols-1.21/t/cross.t0100777000175500010010000000067610063422761015521 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); Math-Algebra-Symbols-1.21/t/differentiation.t0100777000175500010010000000103510063422761017530 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); Math-Algebra-Symbols-1.21/t/dot.t0100777000175500010010000000105010063422761015141 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Math-Algebra-Symbols-1.21/t/dotCross.t0100777000175500010010000000063110063422761016157 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); Math-Algebra-Symbols-1.21/t/ellipse.t0100777000175500010010000001240310063422761016014 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: Invariants of the ellipse. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; #______________________________________________________________________ # Focus trip == 2R. #______________________________________________________________________ {my ($i, $R, $f, $x) = symbols(qw(i R f x)); my $y = sqrt($R*$R-$f*$f - $x*$x +$f*$f*$x*$x / ($R*$R)); # Ellipse: rr=RR-ff my $a = $x+$i*$y - $f; # Vector from Focus to locus my $b = $x+$i*$y + $f; # Vector from other Focus to locus ok(abs($a) + abs($b) == 2*$R, 'Focus trip is constant 2R'); } #______________________________________________________________________ # Angle of incidence equals angle of reflection via dot product with # normal to tangent vector. #______________________________________________________________________ {my ($i, $R, $f, $x) = symbols(qw(i R f x)); my $r = sqrt($R*$R - $f*$f); # Minor radius my $y = sqrt($r*$r - $x*$x +$f*$f*$x*$x / ($R*$R)); # Ellipse my $p = $x + $i * $y; # x,y point on locus of ellipse my $s = $x*$r*$r + $i*$y*$R*$R; # Normal to tangent at locus my $a = $p - $f; # Vector from Focus to locus my $b = $p + $f; # Vector from other Focus to locus my $c = $a * abs($b); # Make each focus vector the same length my $d = $b * abs($a); # so that dot or cross will measure angle my $A = $c^$s; # Angle of Reflection vs my $B = $d^$s; # Angle of Incidence ok($A == $B, "Angle of incidence equals angle of reflection via dot product with normal to tangent"); } #______________________________________________________________________ # Angle of incidence equals angle of reflection via dot product with # tangent vector using optimized substitutions. # NB: -B due to anti-symmetry of cos(x) at x==pi/2 #______________________________________________________________________ {my ($i, $R, $f, $x) = symbols(qw(i R f x)); my $r = sqrt($R*$R - $f*$f); # Minor radius my $y = sqrt($r*$r - $x*$x +$f*$f*$x*$x / ($R*$R)); # Ellipse my $p = $x + $i * $y; # x,y point on locus of ellipse my $s = $i*$x*$r*$r - $y*$R*$R; # Tangent at locus my $a = $p - $f; # Vector from Focus to locus my $b = $p + $f; # Vector from other Focus to locus my $c = $a * abs($b); # Make each focus vector the same length my $d = $b * abs($a); # so that dot or cross will measure angle my $A = $c ^ $s; # Angle of Reflection vs my $B = $d ^ $s; # Angle of Incidence ok($A == -$B, "Angle of incidence equals angle of reflection via dot product with tangent"); } #______________________________________________________________________ # Angle of incidence equals angle of reflection via cross product with # normal to tangent vector. #______________________________________________________________________ {my ($i, $R, $f, $x) = symbols(qw(i R f x)); my $r = sqrt($R*$R - $f*$f); # Minor radius my $y = sqrt($r*$r - $x*$x +$f*$f*$x*$x / ($R*$R)); # Ellipse my $p = $x + $i * $y; # x,y point on locus of ellipse my $s = $x*$r*$r + $y*$R*$R*$i; # Normal to tangent at locus my $a = $p - $f; # Vector from Focus to locus my $b = $p + $f; # Vector from other Focus to locus my $c = $a * abs($b); # Make each focus vector the same length my $d = $b * abs($a); # so that dot or cross will measure angle my $A = $c x $s; # Angle of Reflection vs my $B = $d x $s; # Angle of Incidence ok($A == $B, "Angle of incidence equals angle of reflection via cross product with normal to tangent"); } #______________________________________________________________________ # Angle of incidence equals angle of reflection via cross product with # tangent vector. #______________________________________________________________________ {my ($i, $R, $f, $x) = symbols(qw(i R f x)); my $r = sqrt($R*$R - $f*$f); # Focus my $y = sqrt($r*$r - $x*$x +$f*$f*$x*$x / ($R*$R)); # Ellipse my $p = $x + $i * $y; # x,y point on locus of ellipse my $s = $i*($x*$r*$r + $y*$R*$R*$i); # Normal to tangent at locus my $a = $p - $f; # Vector from Focus to locus my $b = $p + $f; # Vector from other Focus to locus my $c = $a * abs($b); # Make each focus vector the same length my $d = $b * abs($a); # so that dot or cross will measure angle my $A = $c x $s; # Angle of Reflection vs my $B = $d x $s; # Angle of Incidence ok($A == $B, "Angle of incidence equals angle of reflection via cross product with tangent"); } Math-Algebra-Symbols-1.21/t/eq.t0100777000175500010010000000105010063422761014760 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); Math-Algebra-Symbols-1.21/t/exp.t0100777000175500010010000000147710063422761015164 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 9; ($x, $zero, $one, $i, $pi) = symbols(qw(x 0 1 i pi)); ok( exp($zero) == $one); ok( exp($i*$pi/2) == $i); ok( exp($i*$pi) == -$one); ok( exp(3*$i*$pi/2) == -$i); ok( exp(4*$i*$pi/2) == $one); ok( exp($i*$pi) == -1); ok( $i*exp(3*$i*$pi/2) == 1); ok( exp($x)*exp($i*$x)*exp($x)*exp(-$i*$x)-exp(2*$x) == 0); ok( 1+$one+'1/2'*$one**2+'1/6'*$one**3+'1/24'*$one**4+'1/120'*$one**5+ '1/720'*$one**6+'1/5040'*$one**7+'1/40320'*$one**8 == '109601/40320'); Math-Algebra-Symbols-1.21/t/expd.t0100777000175500010010000000064510063422761015324 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); Math-Algebra-Symbols-1.21/t/hyperbola.t0100777000175500010010000000172710063422761016353 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: Hyperbola. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 1; no warnings qw(void); ($x, $y, $i) = symbols(qw(x y i)); # Find focii of hyperbola y=1/x # Assume by symmetry the focii are on ' the line y=x: ', $f1 = $x + $i * $x, ' and equidistant from the origin: ', $f2 = -$f1, ' Choose a convenient point on y=1/x: ', $a = 1+$i, ' and another point: ', $b = $y+$i/$y, ' Distances from focii ', ' From first point: ', $A = abs($a - $f2) - abs($a - $f1), ' From second point: ', $B = abs($b - $f2) + abs($b - $f1), ' Solve for difference in distances ', ok( (($A-$B) > $x) == sqrt(symbols('2'))); Math-Algebra-Symbols-1.21/t/hyperbolic.t0100777000175500010010000000224410063422761016521 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1, trig=>1; use Test::More tests => 20; ($x, $y, $i) = symbols(qw(x y i)); ok( cosh($x)->d == sinh($x)); ok( sinh($x)->d == cosh($x)); ok( cosh($x)**2-sinh($x)**2 == 1); ok( cosh($x+$y) == cosh($x)*cosh($y)+sinh($x)*sinh($y)); ok( sinh($x+$y) == sinh($x)*cosh($y)+cosh($x)*sinh($y)); # Reciprocal ok( sinh($x) == 1/csch($x)); ok( cosh($x) == 1/sech($x)); ok( tanh($x) == 1/coth($x)); ok( csch($x) == 1/sinh($x)); ok( sech($x) == 1/cosh($x)); ok( coth($x) == 1/tanh($x)); # Pythagoras ok( cosh($x)**2 - sinh($x)**2 == 1); ok( tanh($x)**2 + sech($x)**2 == 1); ok( coth($x)**2 - csch($x)**2 == 1); # Relations to Trigonometric Function ok( sinh($x) == -$i*sin($i*$x)); ok( csch($x) == $i*csc($i*$x)); ok( cosh($x) == cos($i*$x)); ok( sech($x) == sec($i*$x)); ok( tanh($x) == -$i*tan($i*$x)); ok( coth($x) == $i*cot($i*$x)); Math-Algebra-Symbols-1.21/t/ipi.t0100777000175500010010000000063110063422761015140 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); Math-Algebra-Symbols-1.21/t/ipi2.t0100777000175500010010000000063610063422761015227 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); Math-Algebra-Symbols-1.21/t/ix.t0100777000175500010010000000063610063422761015004 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); Math-Algebra-Symbols-1.21/t/logExp.t0100777000175500010010000000060510063422761015616 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Math-Algebra-Symbols-1.21/t/parabola.t0100777000175500010010000000177010063422761016145 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: Parabola. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 2; no warnings qw(void); ($x, $i) = symbols(qw(x i)); # Parabola: Focussing to infinity ' From focus to locus: ', $a = $x + $i * $x**2 - $i/4, ' Vertical of same length:', $b = $i * abs($a), ' Tangent vector to locus:', $d = 1 + 2*$x*$i, ' Compare angles via dot: ', ok( ($a ^ $d) == ($b ^ $d), 'Focusses to infinity'); # Distance from focus to locus to directrix at y = 1/4 ' From focus to locus: ', $a = $x + $i * $x**2 - $i/4, ' From focus to locus squared: ', $A = $a^$a, ' From locus to directrix squared:', $B = ($x**2 + '1/4')**2, ' Equal lengths', ok ($A == $B, 'Distance from focus to locus equals distance from locus to directrix'); Math-Algebra-Symbols-1.21/t/pentagon.t0100777000175500010010000000100010063422761016161 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 1; ($i, $x, $f, $one, $zero) = symbols(qw(i x 5 1 0)); $x = ($one+sqrt($f)) / 4; $a = ($x+$i*sqrt(1-$x*$x))**3; $b = ($x+$i*sqrt(1-$x*$x))**2; $c = $a-$b; $d = $c->im; ok( $d == $zero); Math-Algebra-Symbols-1.21/t/polynomial.t0100777000175500010010000000331610063422761016545 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: polynomial tests. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 19; my ($a, $b, $x, $y, $i, $c2, $c3) = symbols(qw(a b x y i 2 3)); ok( sin($x)**2 + cos($x)**2 == 1, 'Pythagoras'); ok( ($x**8 - 1) / ($x**4+1) == $x**4-1, 'Polynomial division'); ok( ($x**2 - 1) == ($x-1) * ($x+1),'Polynomial multiplication'); ok( abs(!($x+$y*$i)*!($a+$b*$i)) == 1, 'Length of product of units'); ok( ($x+$x*$x)*$y/($x*$y) == 1+$x); ok( (2*$x*$y**20) / (4*$y**19+4*$y**19) == ($x*$y)/4); ok( (4*$b+4*$a*$b)/(4*$b+4*$a*$b) == 1/($a+1)*$a+1/($a+1)); #ok( (sqrt($c2)+sqrt($c3))**4 == 10*(sqrt($c2)+sqrt($c3))**2 - 1); ok( ($x**16-1)/($x**8-1) == $x**8+1); ok( ($x+1)**11 / (1+$x)**12 == 1/($x+1)); ok( ($x**2 + $y**2)/($x**2 + $y**2) == 1); ok( ($x**2 + 2*$x*$y +$y**2) / ($x+$y) == $x+$y); ok( (($x**2-1)/(($x+1)*($x-1))) == 1); $A += ($x**2 + 5 * $x + 6) / ($x + 2); $B += ($x**8 - 1) / ($x-1); $C += ($x-1) / ($x**2 - 1); $D += ($x**2 - $y**2) / ($x + $y); $E += ($x**4 - $y**4) / ($x**2 + $y**2); $F += ($x**6 - $y**6) / ($x**3 + $y**3); $G += ($x**4 + $y**4) / ($x**8 - $y**8); ok("$A" eq "$x+3"); ok("$B" eq "$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1"); ok("$C" eq "1/($x+1)"); ok("$D" eq "$x-$y"); ok("$E" eq "$x**2-$y**2"); ok("$F" eq "$x**3-$y**3"); ok("$G" eq "1/($x**4-$y**4)"); Math-Algebra-Symbols-1.21/t/quadratic.t0100777000175500010010000000157410063422761016343 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Test::More tests => 1; use Math::Algebra::Symbols; ($a, $b, $c, $x, $y) = symbols(qw(a b c x y)); $p = $a*$x**2 + $b*$x + $c; # The polynomial in question $q = sqrt($y)/sqrt($a) - $b/2/$a; # Proposed Substitution $y = $p->sub(x=>$q); # Perform substitution $z = $y->solve(qw(y a b c)); # Solve for y - assumes substitution reduced the complexity of the polynomail by eliminating a term $x = $q->sub(y=>$z); # Substitute back to get result in terms of x print "x=$x\n"; # Proposed solution ok( $x == (-$b+sqrt($b*$b-4*$a*$c))/(2*$a), 'Quadratic solution'); Math-Algebra-Symbols-1.21/t/reIm.t0100777000175500010010000000062710063422761015260 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); Math-Algebra-Symbols-1.21/t/simple.t0100777000175500010010000000314710063422761015655 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: simple tests. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 34; ($a, $b, $x, $i, $zero, $one, $two) = symbols(qw(2 3 x i 0 1 2)); ok( symbols(0) == $zero); ok( symbols(0) != $one); ok( symbols(1) == $one); ok( symbols(1) != $zero); ok( $a == $two); ok( $b == 3); ok( $a+$a == 4); ok( $a+$b == 5); ok( $a+$b+$a+$b == 10); ok( $a+1 == 3); ok( $a+2 == 4); ok( $b-1 == 2); ok( $b-2 == 1); ok( $b-9 == -6); ok( $a/2 == $one); ok( $a/4 == '1/2'); ok( $a*2/2 == $two); ok( $a*2/4 == $one); ok( $a**2 == 4); ok( $a**10 == 1024); ok( sqrt($a**2) == $a); ok( sqrt(symbols(-1)) == 'i'); ok( sqrt(symbols(4)) == 2); ok( symbols('1/2') + '1/3' + '1/4' - '1/12' == 1); ok( sqrt(symbols('-1')) == $i); ok( symbols('x') == $x); ok( symbols('2*x**2') == 2*$x**2); ok( $a+$a == 2*$a); ok( $a+$a+$a == 3*$a); ok( $a-$a == $zero); ok( $a-$a-$a == -$a); ok( $a*$b*$a*$b == $a**2*$b**2); ok( ($b/$a)**2 == $b**2/$a**2); ok( $a**128 == '340282366920938463463374607431768211456'); Math-Algebra-Symbols-1.21/t/simplify.t0100777000175500010010000000076510063422761016223 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); Math-Algebra-Symbols-1.21/t/simplify2.t0100777000175500010010000000111310063422761016271 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); Math-Algebra-Symbols-1.21/t/sin.t0100777000175500010010000000226010063422761015150 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 22; ($zero, $half, $one, $pi, $x) = symbols(qw(0 1/2 1 pi x)); ok( sin($zero) == -0); ok( sin($pi/6) == $half); ok( sin($pi/2) == 1); ok( sin(5*$pi/6) == $half); ok( sin(120*$pi/120) == $zero); ok( sin(7*$pi/6) == -$half); ok( sin(3*$pi/2) == -1); ok( sin(110*$pi/ 60) == '-1/2'); ok( sin(2*$pi) == $zero); ok( sin(-$zero) == $zero); ok( sin(-$pi/6) == -$half); ok( sin(-$pi/2) == -$one); ok( sin(-5*$pi/6) == -$half); ok( sin(-120*$pi/120) == -$zero); ok( sin(-7*$pi/6) == $half); ok( sin(-3*$pi/2) == $one); ok( sin(-110*$pi/ 60) == $half); ok( sin(-2*$pi) == $zero); ok( sin($x)->d == cos($x)); ok( sin($x)->d->d == -sin($x)); ok( sin($x)->d->d->d == -cos($x)); ok( sin($x)->d->d->d->d == sin($x)); Math-Algebra-Symbols-1.21/t/sinCos.t0100777000175500010010000000072510063422761015621 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); Math-Algebra-Symbols-1.21/t/sinCos2.t0100777000175500010010000000057710063422761015710 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); Math-Algebra-Symbols-1.21/t/solve.t0100777000175500010010000000100310063422761015501 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); Math-Algebra-Symbols-1.21/t/solve1.t0100777000175500010010000000107510063422761015573 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); Math-Algebra-Symbols-1.21/t/solve2.t0100777000175500010010000000102310063422761015565 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); Math-Algebra-Symbols-1.21/t/sqrt.t0100777000175500010010000000223610063422761015353 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 11; ($x, $y) = symbols(qw(x y)); ok( sqrt(($x+$y)**2)+$x-$y == 2*$x); ok( sqrt(($x+$y)**2)+sqrt(($x-$y)**2) == 2*$x); ok( sqrt(($x+$y)**2)+sqrt(($x-$y)**2)+sqrt((-$x+$y)**2)+sqrt((-$x-$y)**2) == 4*$x); ok( ($x*sqrt($x))->d == 3*sqrt($x)/2); ok( sqrt($x**3)->d == symbols('3/2')*sqrt($x)); ok(((1+$x)/sqrt(1+$x))->d == sqrt(1+$x)->d); ok( sqrt($x+1) / sqrt(1+$x) == 1); ok( 2*$y**2*sqrt($x+1) / (4*$y*sqrt(1+$x)) == $y/2); ok( 1/sqrt(1+$x) == 1/sqrt(1+$x)); ok( 1/sqrt(1+$x)**3 == 1/(sqrt(1+$x)+sqrt(1+$x)*$x)); ok( sqrt($x+1)**3 / sqrt(1+$x)**3 == 1); Math-Algebra-Symbols-1.21/t/sub.t0100777000175500010010000000110210063422761015142 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); Math-Algebra-Symbols-1.21/t/symbols.t0100777000175500010010000000142210063422761016046 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); Math-Algebra-Symbols-1.21/t/tanh.t0100777000175500010010000000063610063422761015316 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); Math-Algebra-Symbols-1.21/t/trigonometric.t0100777000175500010010000000663010063422761017251 0ustar philNone#!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols trig=>1; use Test::More tests => 64; ($x, $y, $pi) = symbols(qw(x y pi)); # Reciprocals ok( sin($x) == 1/csc($x)); ok( cos($x) == 1/sec($x)); ok( tan($x) == 1/cot($x)); ok( csc($x) == 1/sin($x)); ok( sec($x) == 1/cos($x)); ok( cot($x) == 1/tan($x)); # Pythagoras ok( sin($x)**2 + cos($x)**2 == 1); ok( sec($x)**2 - tan($x)**2 == 1); ok( csc($x)**2 - cot($x)**2 == 1); # Quotient ok( tan($x) == sin($x)/cos($x)); ok( cot($x) == cos($x)/sin($x)); # Co-Function Identities ok( sin($x) == cos($pi/2-$x)); ok( cos($x) == sin($pi/2-$x)); ok( cot($x) == tan($pi/2-$x)); ok( sec($x) == csc($pi/2-$x)); ok( csc($x) == sec($pi/2-$x)); ok( tan($x) == cot($pi/2-$x)); # Even-Odd Identities ok( cos($x) == cos(-$x)); ok( sin($x) == -sin(-$x)); ok( tan($x) == -tan(-$x)); ok( cot($x) == -cot(-$x)); ok( csc($x) == -csc(-$x)); ok( sec($x) == sec(-$x)); # Values of sin, cos at well known points ok( cos(symbols(0))== 1); ok( cos($pi/2) == 0); ok( cos($pi) == -1); ok( cos(3*$pi/2) == 0); ok( cos(4*$pi/2) == 1); ok( sin(symbols(0))== 0); ok( sin($pi/2) == 1); ok( sin($pi) == 0); ok( sin(3*$pi/2) == -1); ok( sin(4*$pi/2) == 0); # Sums and Differences ok( sin($x+$y) == sin($x)*cos($y)+cos($x)*sin($y)); ok( sin($x-$y) == sin($x)*cos($y)-cos($x)*sin($y)); ok( cos($x+$y) == cos($x)*cos($y)-sin($x)*sin($y)); ok( cos($x-$y) == cos($x)*cos($y)+sin($x)*sin($y)); ok( tan($x+$y) == (tan($x)+tan($y))/(1-tan($x)*tan($y))); ok( tan($x-$y) == (tan($x)-tan($y))/(1+tan($x)*tan($y))); # Double angles ok( sin(2*$x) == 2*sin($x)*cos($x)); ok( cos(2*$x) == cos($x)**2-sin($x)**2); ok( cos(2*$x) == 2*cos($x)**2-1); ok( cos(2*$x) == 1-2*sin($x)**2); ok( tan(2*$x) == 2*tan($x)/(1-tan($x)**2)); # Power-Reducing/Half Angle Formulas ok( sin($x)**2 == (1-cos(2*$x))/2); ok( cos($x)**2 == (1+cos(2*$x))/2); ok( tan($x)**2 == (1-cos(2*$x))/(1+cos(2*$x))); # Sum-to-Product Formulas ok( sin($x)+sin($y) == 2*sin(($x+$y)/2)*cos(($x-$y)/2)); ok( sin($x)-sin($y) == 2*cos(($x+$y)/2)*sin(($x-$y)/2)); ok( cos($x)+cos($y) == 2*cos(($x+$y)/2)*cos(($x-$y)/2)); ok( cos($x)-cos($y) == -2*sin(($x+$y)/2)*sin(($x-$y)/2)); # Product-to-Sum Formulas ok( sin($x)*sin($y) == cos($x-$y)/2-cos($x+$y)/2); ok( cos($x)*cos($y) == cos($x-$y)/2+cos($x+$y)/2); ok( sin($x)*cos($y) == sin($x+$y)/2+sin($x-$y)/2); ok( cos($x)*sin($y) == sin($x+$y)/2-sin($x-$y)/2); # Differentials. ok( cos($x) == -cos($x)->d->d); ok( sin($x) == -sin($x)->d->d); ok( sin($x)->d == cos($x)); ok( cos($x)->d == -sin($x)); ok( tan($x)->d == tan($x)**2 + 1); ok( tan($x)->d == sec($x)**2); ok( cot($x)->d == -csc($x)**2); ok( sec($x)->d == sec($x)*tan($x)); ok( csc($x)->d == -csc($x)*cot($x)); Math-Algebra-Symbols-1.21/t/unit.t0100777000175500010010000000102710063422761015336 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); Math-Algebra-Symbols-1.21/t/x2y2.t0100777000175500010010000000074110063422761015165 0ustar philNone#!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); Math-Algebra-Symbols-1.21/userGuide0100777000175500010010000007531710063422763015625 0ustar philNoneNAME Math::Algebra::Symbols - Symbolic Algebra in Pure Perl. User guide. SYNOPSIS Example symbols.pl #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # Perl License. # PhilipRBrenan@yahoo.com, 2004. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>5; ($n, $x, $y) = symbols(qw(n x y)); $a += ($x**8 - 1)/($x-1); $b += sin($x)**2 + cos($x)**2; $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x)); $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y)); ($e,$f) = @{($x**2 eq 5*$x-6) > $x}; print "$a\n$b\n$c\n$d\n$e,$f\n"; ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1'); ok("$b" eq '1'); ok("$c" eq '$n**4'); ok("$d" eq '1'); ok("$e,$f" eq '2,3'); DESCRIPTION This package supplies a set of functions and operators to manipulate operator expressions algebraically using the familiar Perl syntax. These expressions are constructed from "Symbols", "Operators", and "Functions", and processed via "Methods". For examples, see: "Examples". Symbols Symbols are created with the exported symbols() constructor routine: Example t/constants.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi)); ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' ); The symbols() routine constructs references to symbolic variables and symbolic constants from a list of names and integer constants. The special symbol i is recognized as the square root of -1. The special symbol pi is recognized as the smallest positive real that satisfies: Example t/ipi.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($i, $pi) = symbols(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); Constructor Routine Name If you wish to use a different name for the constructor routine, say S: Example t/ipi2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: constants. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols symbols=>'S'; use Test::Simple tests=>2; my ($i, $pi) = S(qw(i pi)); ok( exp($i*$pi) == -1 ); ok( exp($i*$pi) <=> '-1' ); Big Integers Symbols automatically uses big integers if needed. Example t/bigInt.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: bigInt. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my $z = symbols('1234567890987654321/1234567890987654321'); ok( eval $z eq '1'); Operators "Symbols" can be combined with "Operators" to create symbolic expressions: Arithmetic operators Arithmetic Operators: + - * / ** Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The operators: += -= *= /= are overloaded to work symbolically rather than numerically. If you need numeric results, you can always eval() the resulting symbolic expression. Square root Operator: sqrt Example t/ix.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: sqrt(-1). # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( sqrt(-$x**2) == $i*$x ); ok( sqrt(-$x**2) <=> 'i*$x' ); The square root is represented by the symbol i, which allows complex expressions to be processed by Math::Complex. Exponential Operator: exp Example t/expd.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: exp. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( exp($x)->d($x) == exp($x) ); ok( exp($x)->d($x) <=> 'exp($x)' ); The exponential operator. Logarithm Operator: log Example t/logExp.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: log: need better example. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x) = symbols(qw(x)); ok( log($x) <=> 'log($x)' ); Logarithm to base e. Note: the above result is only true for x > 0. Symbols does not include domain and range specifications of the functions it uses. Sine and Cosine Operators: sin and cos Example t/sinCos.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x) = symbols(qw(x)); ok( sin($x)**2 + cos($x)**2 == 1 ); ok( sin($x)**2 + cos($x)**2 != 0 ); ok( sin($x)**2 + cos($x)**2 <=> '1' ); This famous trigonometric identity is not preprogrammed into Symbols as it is in commercial products. Instead: an expression for sin() is constructed using the complex exponential: "exp", said expression is algebraically multiplied out to prove the identity. The proof steps involve large intermediate expressions in each step, as yet I have not provided a means to neatly lay out these intermediate steps and thus provide a more compelling demonstration of the ability of Symbols to verify such statements from first principles. Relational operators Relational operators: ==, != Example t/x2y2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplification. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y) = symbols(qw(x y)); ok( ($x**2-$y**2)/($x-$y) == $x+$y ); ok( ($x**2-$y**2)/($x-$y) != $x-$y ); ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' ); The relational equality operator == compares two symbolic expressions and returns TRUE(1) or FALSE(0) accordingly. != produces the opposite result. Relational operator: eq Example t/eq.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: solving. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v+$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); The relational operator eq is a synonym for the minus - operator, with the expectation that later on the solve() function will be used to simplify and rearrange the equation. You may prefer to use eq instead of - to enhance readability, there is no functional difference. Complex operators Complex operators: the dot operator: ^ Example t/dot.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($a, $b, $i) = symbols(qw(a b i)); ok( (($a+$i*$b)^($a-$i*$b)) == $a**2-$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) != $a**2+$b**2 ); ok( (($a+$i*$b)^($a-$i*$b)) <=> '$a**2-$b**2' ); Note the use of brackets: The ^ operator has low priority. The ^ operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors to which the vector dot product is applied. Complex operators: the cross operator: x Example t/cross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: cross operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( $i*$x x $x == $x**2 ); ok( $i*$x x $x != $x**3 ); ok( $i*$x x $x <=> '$x**2' ); The x operator treats its left hand and right hand arguments as complex numbers, which in turn are regarded as two dimensional vectors defining the sides of a parallelogram. The x operator returns the area of this parallelogram. Note the space before the x, otherwise Perl is unable to disambiguate the expression correctly. Complex operators: the conjugate operator: ~ Example t/conjugate.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $y, $i) = symbols(qw(x y i)); ok( ~($x+$i*$y) == $x-$i*$y ); ok( ~($x-$i*$y) == $x+$i*$y ); ok( (($x+$i*$y)^($x-$i*$y)) <=> '$x**2-$y**2' ); The ~ operator returns the complex conjugate of its right hand side. Complex operators: the modulus operator: abs Example t/abs.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: dot operator. Note the low priority # of the ^ operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $i) = symbols(qw(x i)); ok( abs($x+$i*$x) == sqrt(2*$x**2) ); ok( abs($x+$i*$x) != sqrt(2*$x**3) ); ok( abs($x+$i*$x) <=> 'sqrt(2*$x**2)' ); The abs operator returns the modulus (length) of its right hand side. Complex operators: the unit operator: ! Example t/unit.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: unit operator. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>4; my ($i) = symbols(qw(i)); ok( !$i == $i ); ok( !$i <=> 'i' ); ok( !($i+1) <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( !($i-1) <=> '-1/(sqrt(2))+i/(sqrt(2))' ); The ! operator returns a complex number of unit length pointing in the same direction as its right hand side. Equation Manipulation Operators Equation Manipulation Operators: Simplify operator: += Example t/simplify.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); ok( ($x**8 - 1)/($x-1) == $x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1 ); ok( ($x**8 - 1)/($x-1) <=> '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); The simplify operator += is a synonym for the simplify() method, if and only if, the target on the left hand side initially has a value of undef. Admittedly this is very strange behavior: it arises due to the shortage of over-rideable operators in Perl: in particular it arises due to the shortage of over-rideable unary operators in Perl. Never-the-less: this operator is useful as can be seen in the Synopsis, and the desired pre-condition can always achieved by using my. Equation Manipulation Operators: Solve operator: > Example t/solve2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($t) = symbols(qw(t)); my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; ok( "$a" eq '1/14*sqrt(305)+5/14' ); ok( "$b" eq '-1/14*sqrt(305)+5/14' ); The solve operator > is a synonym for the solve() method. The priority of > is higher than that of eq, so the brackets around the equation to be solved are necessary until Perl provides a mechanism for adjusting operator priority (cf. Algol 68). If the equation is in a single variable, the single variable may be named after the > operator without the use of [...]: use Math::Algebra::Symbols; my $rabbit = 10 + 5 * $t; my $fox = 7 * $t * $t; my ($a, $b) = @{($rabbit eq $fox) > $t}; print "$a\n"; # 1/14*sqrt(305)+5/14 If there are multiple solutions, (as in the case of polynomials), > returns an array of symbolic expressions containing the solutions. This example was provided by Mike Schilli m@perlmeister.com. Functions Perl operator overloading is very useful for producing compact representations of algebraic expressions. Unfortunately there are only a small number of operators that Perl allows to be overloaded. The following functions are used to provide capabilities not easily expressed via Perl operator overloading. These functions may either be called as methods from symbols constructed by the "Symbols" construction routine, or they may be exported into the user's namespace as described in "EXPORT". Trigonometric and Hyperbolic functions Trigonometric functions Example t/sinCos2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( (sin($x)**2 == (1-cos(2*$x))/2) ); The trigonometric functions cos, sin, tan, sec, csc, cot are available, either as exports to the caller's name space, or as methods. Hyperbolic functions Example t/tanh.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols hyper=>1; use Test::Simple tests=>1; my ($x, $y) = symbols(qw(x y)); ok( tanh($x+$y)==(tanh($x)+tanh($y))/(1+tanh($x)*tanh($y))); The hyperbolic functions cosh, sinh, tanh, sech, csch, coth are available, either as exports to the caller's name space, or as methods. Complex functions Complex functions: re and im use Math::Algebra::Symbols complex=>1; Example t/reIm.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $i) = symbols(qw(x i)); ok( ($i*$x)->re <=> 0 ); ok( ($i*$x)->im <=> '$x' ); The re and im functions return an expression which represents the real and imaginary parts of the expression, assuming that symbolic variables represent real numbers. Complex functions: dot and cross Example t/dotCross.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my $i = symbols(qw(i)); ok( ($i+1)->cross($i-1) <=> 2 ); ok( ($i+1)->dot ($i-1) <=> 0 ); The dot and cross operators are available as functions, either as exports to the caller's name space, or as methods. Complex functions: conjugate, modulus and unit Example t/conjugate2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: methods. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my $i = symbols(qw(i)); ok( ($i+1)->unit <=> '1/(sqrt(2))+i/(sqrt(2))' ); ok( ($i+1)->modulus <=> 'sqrt(2)' ); ok( ($i+1)->conjugate <=> '1-i' ); The conjugate, abs and unit operators are available as functions: conjugate, modulus and unit, either as exports to the caller's name space, or as methods. The confusion over the naming of: the abs operator being the same as the modulus complex function; arises over the limited set of Perl operator names available for overloading. Methods Methods for manipulating Equations Simplifying equations: simplify() Example t/simplify2.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x) = symbols(qw(x)); my $y = (($x**8 - 1)/($x-1))->simplify(); # Simplify method my $z += ($x**8 - 1)/($x-1); # Simplify via += ok( "$y" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); ok( "$z" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1' ); Simplify() attempts to simplify an expression. There is no general simplification algorithm: consequently simplifications are carried out on ad hoc basis. You may not even agree that the proposed simplification for a given expressions is indeed any simpler than the original. It is for these reasons that simplification has to be explicitly requested rather than being performed automagically. At the moment, simplifications consist of polynomial division: when the expression consists, in essence, of one polynomial divided by another, an attempt is made to perform polynomial division, the result is returned if there is no remainder. The += operator may be used to simplify and assign an expression to a Perl variable. Perl operator overloading precludes the use of = in this manner. Substituting into equations: sub() Example t/sub.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: expression substitution for a variable. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>2; my ($x, $y) = symbols(qw(x y)); my $e = 1+$x+$x**2/2+$x**3/6+$x**4/24+$x**5/120; ok( $e->sub(x=>$y**2, z=>2) <=> '$y**2+1/2*$y**4+1/6*$y**6+1/24*$y**8+1/120*$y**10+1' ); ok( $e->sub(x=>1) <=> '163/60'); The sub() function example on line #1 demonstrates replacing variables with expressions. The replacement specified for z has no effect as z is not present in this equation. Line #2 demonstrates the resulting rational fraction that arises when all the variables have been replaced by constants. This package does not convert fractions to decimal expressions in case there is a loss of accuracy, however: my $e2 = $e->sub(x=>1); $result = eval "$e2"; or similar will produce approximate results. At the moment only variables can be replaced by expressions. Mike Schilli, m@perlmeister.com, has proposed that substitutions for expressions should also be allowed, as in: $x/$y => $z Solving equations: solve() Example t/solve1.t #!perl -w #______________________________________________________________________ # Symbolic algebra: examples: simplify. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests=>3; my ($x, $v, $t) = symbols(qw(x v t)); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) == $v*$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) != $v/$t ); ok( ($v eq $x / $t)->solve(qw(x in terms of v t)) <=> '$v*$t' ); solve() assumes that the equation on the left hand side is equal to zero, applies various simplifications, then attempts to rearrange the equation to obtain an equation for the first variable in the parameter list assuming that the other terms mentioned in the parameter list are known constants. There may of course be other unknown free variables in the equation to be solved: the proposed solution is automatically tested against the original equation to check that the proposed solution removes these variables, an error is reported via die() if it does not. Example t/solve.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra: quadratic equation. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::Simple tests => 2; my ($x) = symbols(qw(x)); my $p = $x**2-5*$x+6; # Quadratic polynomial my ($a, $b) = @{($p > $x )}; # Solve for x print "x=$a,$b\n"; # Roots ok($a == 2); ok($b == 3); If there are multiple solutions, (as in the case of polynomials), solve() returns an array of symbolic expressions containing the solutions. Methods for performing Calculus Differentiation: d() Example t/differentiation.t #!perl -w -I.. #______________________________________________________________________ # Symbolic algebra. # PhilipRBrenan@yahoo.com, 2004, Perl License. #______________________________________________________________________ use Math::Algebra::Symbols; use Test::More tests => 5; $x = symbols(qw(x)); ok( sin($x) == sin($x)->d->d->d->d); ok( cos($x) == cos($x)->d->d->d->d); ok( exp($x) == exp($x)->d($x)->d('x')->d->d); ok( (1/$x)->d == -1/$x**2); ok( exp($x)->d->d->d->d <=> 'exp($x)' ); d() differentiates the equation on the left hand side by the named variable. The variable to be differentiated by may be explicitly specified, either as a string or as single symbol; or it may be heuristically guessed as follows: If the equation to be differentiated refers to only one symbol, then that symbol is used. If several symbols are present in the equation, but only one of t, x, y, z is present, then that variable is used in honor of Newton, Leibnitz, Cauchy. Example of Equation Solving: the focii of a hyperbola: use Math::Algebra::Symbols; my ($a, $b, $x, $y, $i, $o) = symbols(qw(a b x y i 1)); print "Hyperbola: Constant difference between distances from focii to locus of y=1/x", "\n Assume by symmetry the focii are on ", "\n the line y=x: ", $f1 = $x + $i * $x, "\n and equidistant from the origin: ", $f2 = -$f1, "\n Choose a convenient point on y=1/x: ", $a = $o+$i, "\n and a general point on y=1/x: ", $b = $y+$i/$y, "\n Difference in distances from focii", "\n From convenient point: ", $A = abs($a - $f2) - abs($a - $f1), "\n From general point: ", $B = abs($b - $f2) + abs($b - $f1), "\n\n Solving for x we get: x=", ($A - $B) > $x, "\n (should be: sqrt(2))", "\n Which is indeed constant, as was to be demonstrated\n"; This example demonstrates the power of symbolic processing by finding the focii of the curve y=1/x, and incidentally, demonstrating that this curve is a hyperbola. EXPORTS use Math::Algebra::Symbols symbols=>'S', trig => 1, hyper => 1, complex=> 1; trig=>0 The default, do not export trigonometric functions. trig=>1 Export trigonometric functions: tan, sec, csc, cot to the caller's namespace. sin, cos are created by default by overloading the existing Perl sin and cos operators. trigonometric Alias of trig hyperbolic=>0 The default, do not export hyperbolic functions. hyper=>1 Export hyperbolic functions: sinh, cosh, tanh, sech, csch, coth to the caller's namespace. hyperbolic Alias of hyper complex=>0 The default, do not export complex functions complex=>1 Export complex functions: conjugate, cross, dot, im, modulus, re, unit to the caller's namespace. PACKAGES The Symbols packages manipulate a sum of products representation of an algebraic equation. The Symbols package is the user interface to the functionality supplied by the Symbols::Sum and Symbols::Term packages. Math::Algebra::Symbols::Term Symbols::Term represents a product term. A product term consists of the number 1, optionally multiplied by: Variables any number of variables raised to integer powers, Coefficient An integer coefficient optionally divided by a positive integer divisor, both represented as BigInts if necessary. Sqrt The sqrt of of any symbolic expression representable by the Symbols package, including minus one: represented as i. Reciprocal The multiplicative inverse of any symbolic expression representable by the Symbols package: i.e. a SymbolsTerm may be divided by any symbolic expression representable by the Symbols package. Exp The number e raised to the power of any symbolic expression representable by the Symbols package. Log The logarithm to base e of any symbolic expression representable by the Symbols package. Thus SymbolsTerm can represent expressions like: 2/3*$x**2*$y**-3*exp($i*$pi)*sqrt($z**3) / $x but not: $x + $y for which package Symbols::Sum is required. Math::Algebra::Symbols::Sum Symbols::Sum represents a sum of product terms supplied by Symbols::Term and thus behaves as a polynomial. Operations such as equation solving and differentiation are applied at this level. The main benefit of programming Symbols::Term and Symbols::Sum as two separate but related packages is Object Oriented Polymorphism. I.e. both packages need to multiply items together: each package has its own multiply method, with Perl method lookup selecting the appropriate one as required. Math::Algebra::Symbols Packaging the user functionality alone and separately in package Symbols allows the internal functions to be conveniently hidden from user scripts. AUTHOR Philip R Brenan at philiprbrenan@yahoo.com