File Coverage

blib/lib/Math/Algebra/Symbols/Sum.pm
Criterion Covered Total %
statement 702 805 87.2
branch 329 484 67.9
condition 73 146 50.0
subroutine 87 101 86.1
pod 71 93 76.3
total 1262 1629 77.4


line stmt bran cond sub pod time code
1             #!perl -w
2            
3             =head1 Sums
4            
5             Symbolic Algebra using Pure Perl: sums.
6            
7             See user manual L.
8            
9             Operations on sums of terms.
10            
11             PhilipRBrenan@yahoo.com, 2004, Perl License.
12            
13             =cut
14            
15            
16             package Math::Algebra::Symbols::Sum;
17             $VERSION=1.21;
18 45     45   62061 use Math::Algebra::Symbols::Term;
  45         131  
  45         403  
19 45     45   67548 use IO::Handle;
  45         473246  
  45         2973  
20 45     45   483 use Carp;
  45         99  
  45         14381  
21             #HashUtil use Hash::Util qw(lock_hash);
22 45     45   277 use Scalar::Util qw(weaken);
  45         127  
  45         652379  
23            
24            
25             =head2 Constructors
26            
27            
28             =head3 new
29            
30             Constructor
31            
32             =cut
33            
34            
35             sub new
36 9781     9781 1 33633 {bless {t=>{}};
37             }
38            
39            
40             =head3 newFromString
41            
42             New from String
43            
44             =cut
45            
46            
47 697     697 1 1446 sub newFromString($)
48             {my ($a) = @_;
49 697 100       1877 return $zero unless $a;
50 607         1009 $a .='+';
51 607         3258 my @a = $a =~ /(.+?)[\+\-]/g;
52 607         1248 my @t = map {term($_)} @a;
  607         26173  
53 607         1556 sigma(@t);
54             }
55            
56            
57             =head3 n
58            
59             New from Strings
60            
61             =cut
62            
63            
64 60 50   60 1 301 sub n(@)
65             {return $zero unless @_;
66 60         188 my @a = map {newFromString($_)} @_;
  138         426  
67 60 100       421 return @a if wantarray;
68 16         71 $a[0];
69             }
70            
71            
72             =head3 sigma
73            
74             Create a sum from a list of terms.
75            
76             =cut
77            
78            
79 9840 100   9840 1 23510 sub sigma(@)
80             {return $zero unless scalar(@_);
81 9781         18332 my $z = new();
82 9781         19700 for my $t(@_)
  26440         79848  
83             {my $s = $t->signature;
84 26440 100       77959 if (exists($z->{t}{$s}))
  8525         29662  
85 17915         70892 {my $a = $z->{t}{$s}->add($t);
86 8525 100       47638 if ($a->c == 0)
  1499         6150  
87 7021         71026 {delete $z->{t}{$s};
88             }
89             else
90             {$z->{t}{$s} = $a;
91             }
92             }
93             else
94             {$z->{t}{$s} = $t
95             }
96             }
97 9776         25646 $z->z;
98             }
99            
100            
101             =head3 makeInt
102            
103             Construct an integer
104            
105             =cut
106            
107            
108 13     13 1 557 sub makeInt($)
109             {sigma(term()->one->clone->c(shift())->z)
110             }
111            
112            
113             =head2 Methods
114            
115            
116             =head3 isSum
117            
118             Confirm type
119            
120             =cut
121            
122            
123 8     8 1 10 sub isSum($) {1};
124            
125            
126             =head3 t
127            
128             Get list of terms from existing sum
129            
130             =cut
131            
132            
133 27727     27727 1 46328 sub t($)
134             {my ($a) = @_;
135 27727         40818 (map {$a->{t}{$_}} sort(keys(%{$a->{t}})));
  49637         159575  
  27727         109891  
136             }
137            
138            
139             =head3 count
140            
141             Count terms in sum
142            
143             =cut
144            
145            
146 0     0 1 0 sub count($)
147             {my ($a) = @_;
148 0         0 scalar(keys(%{$a->{t}}));
  0         0  
149             }
150            
151            
152             =head3 st
153            
154             Get the single term from a sum containing just one term
155            
156             =cut
157            
158            
159 4760     4760 1 7069 sub st($)
160             {my ($a) = @_;
161 4760 100       5928 return (values(%{$a->{t}}))[0] if scalar(keys(%{$a->{t}})) == 1;
  1622         4523  
  4760         15593  
162 3138         5753 undef;
163             }
164            
165            
166             =head3 negate
167            
168             Multiply each term in a sum by -1
169            
170             =cut
171            
172            
173 774     774 1 1798 sub negate($)
174             {my ($s) = @_;
175 774         897 my @t;
176 774         1616 for my $t($s->t)
  1005         3315  
177             {push @t, $t->clone->timesInt(-1)->z;
178             }
179 774         2262 sigma(@t);
180             }
181            
182            
183             =head3 add
184            
185             Add two sums together to make a new sum
186            
187             =cut
188            
189            
190 4654     4654 1 7235 sub add($$)
191             {my ($a, $b) = @_;
192 4654         9756 sigma($a->t, $b->t);
193             }
194            
195            
196             =head3 subtract
197            
198             Subtract one sum from another
199            
200             =cut
201            
202            
203 475     475 1 831 sub subtract($$)
204             {my ($a, $b) = @_;
205 475 100       1807 return $b->negate if $a->{id} == $zero->{id};
206 405         9570 $a->add($b->negate);
207             }
208            
209            
210             =head3 Conditional Multiply
211            
212             Multiply two sums if both sums are defined, otherwise return
213             the defined sum. Assumes that at least one sum is defined.
214            
215             =cut
216            
217            
218 6026     6026 0 8303 sub multiplyC($$)
219             {my ($a, $b) = @_;
220 6026 100       24976 return $a unless defined($b);
221 735 100       2755 return $b unless defined($a);
222 251         711 $a->multiply($b);
223             }
224            
225            
226             =head3 multiply
227            
228             Multiply two sums together
229            
230             =cut
231            
232            
233             my %M; # Memoize multiplication
234            
235 2354     2354 1 4514 sub multiply($$)
236             {my ($A, $B) = @_;
237            
238 2354 100       8437 my $m = $M{$A->{id}}{$B->{id}}; return $m if defined($m);
  2354         7999  
239            
240 1336 100 100     9184 return $A if $A->{id} == $zero->{id} or $B->{id} == $one->{id};
241 1177 100 100     16667 return $B if $B->{id} == $zero->{id} or $A->{id} == $one->{id};
242            
243 1095         1726 my @t;
244            
245             # Check for divides that match multiplier
246 1095         2849 my @a = $A->t;
247 1095         2648 for my $a(@a)
  2914         8844  
248             {my $d = $a->Divide;
249 2914 100       11789 next unless $d;
250 1184 100       3423 if ($d->{id} == $B->{id})
  756         2286  
251             {push @t, $a->removeDivide;
252 756         1539 $a = undef;
253             }
254             }
255            
256 1095         2771 my @b = $B->t;
257 1095         3861 for my $b(@b)
  1701         4906  
258             {my $d = $b->Divide;
259 1701 100       5674 next unless $d;
260 134 100       485 if ($d->{id} == $A->{id})
  1         6  
261             {push @t, $b->removeDivide;
262 1         5 $b = undef;
263             }
264             }
265            
266             # Simple multiply
267 1095 100       2136 for my $aa(@a)
  2914         8909  
268             {next unless $aa;
269 2158 100       5172 for my $bb(@b)
  5836         16544  
270             {next unless $bb;
271 5835         18728 my $m = $aa->multiply($bb);
272 5835 100       17172 push (@t, $m), next if $m;
273            
274             # Complicated multiply
275 3859         11008 my %a = $aa->split; my %b = $bb->split;
  3859         13261  
276 3859         8110 my $a = $a{t}; my $b = $b{t};
  3859         5040  
277            
278             # Sqrt
279 3859         4568 my $s = 0;
280 3859 100 66     9324 $s = $a{s} if $a{s} and $b{s} and $a{s}->{id} == $b{s}->{id}; # Equal sqrts
      100        
281 3859 100       15393 $a->Sqrt(multiplyC($a{s}, $b{s})) unless $s;
282            
283             # Divide
284 3859 100 100     14097 $a->Divide(multiplyC($a{d}, $b{d})) if $a{d} or $b{d};
285            
286             # Exp
287 3859 50 75     11975 $a->Exp($a{e} ? $a{e} : $b{e}) if $a{e} xor $b{e};
    100          
288 3859         7790 my $e;
289 3859 100 66     8026 if ($a{e} and $b{e})
  3783         10182  
290             {my $s = $a{e}->add($b{e});
291 3783         11658 $e = $s->st; # Check for single term
292 3783 100       9859 $e = $e->exp2 if defined($e); # Simplify single term if possible
293 3783 100       14948 $a->Exp($s) unless defined($e); # Reinstate Exp as sum of terms if no simplification possible
294             }
295             # Log
296 3859 0 25     20817 $a->Log($a{l} ? $a{l} : $b{l}) if $a{l} xor $b{l};
    50          
297 3859 50 33     9793 die "Cannot multiply logs yet" if $a{l} and $b{l};
298            
299             # Combine results
300 3859         10437 $a = $a->z;
301 3859         11321 $b = $b->z;
302 3859         11819 $a = $a->multiply($b);
303 3859 100       10067 $a = $a->multiply($e) if defined($e);
304 3859 50       10709 $a or die "Bad multiply";
305            
306 3859 100       11225 push @t, $a unless $s;
307 3859 100       39424 push @t, sigma($a)->multiply($s)->t if $s;
308             }
309             }
310            
311             # Result
312 1095         3954 my $C = sigma(@t);
313 1093         4708 $M{$A->{id}}{$B->{id}} = $C;
314 1093         16778 $C;
315             }
316            
317            
318             =head3 divide
319            
320             Divide one sum by another
321            
322             =cut
323            
324            
325 314     314 1 573 sub divide($$)
326             {my ($A, $B) = @_;
327            
328             # Obvious cases
329 314 50       1131 $B->{id} == $zero->{id} and croak "Cannot divide by zero";
330 314 50       982 return $zero if $A->{id} == $zero->{id};
331 314 100       998 return $A if $B->{id} == $one->{id};
332 279 50       886 return $A->negate if $B->{id} == $mOne->{id};
333            
334             # Divide term by term
335 279         844 my $a = $A->st; my $b = $B->st;
  279         672  
336 279 100 100     1580 if (defined($a) and defined($b))
  87 100       427  
337 54         102 {my $c = $a->divide2($b);
338 87 100       445 return sigma($c) if $c;
339             }
340            
341             # Divide sum by term
342             elsif ($b)
343 54         140 {ST: for(1..1)
344             {my @t;
345 54         134 for my $t($A->t)
  100         351  
346             {my $c = $t->divide2($b);
347 100 100       315 last ST unless $c;
348 85         284 push @t, $c;
349             }
350 39         125 return sigma(@t);
351             }
352             }
353            
354             # Divide sum by sum
355 159         276 my @t;
356 159         390 for my $aa($A->t)
  292         948  
357             {my $a = $aa->clone;
358 292         961 my $d = $a->Divide;
359 292 100       645 $a->Divide($d->multiply($B)) if $d;
360 292 100       1350 $a->Divide($B) unless $d;
361 292         1089 push @t, $a->z;
362             }
363            
364             # Result
365 159         714 sigma(@t);
366             }
367            
368            
369             =head3 sub
370            
371             Substitute a sum for a variable.
372            
373             =cut
374            
375            
376 7     7 1 43 sub sub($@)
377             {my $E = shift();
378 7         211 my @R = @_;
379            
380             # Each replacement
381 7         39 for(;@R > 0;)
  8         18  
382             {my $s = shift @R; # Replace this variable
383 8         27 my $w = shift @R; # With this expression
384 8         85 my $Z = $zero;
385            
386 8 50       65 $s =~ /^[a-z]+$/i or croak "Can only substitute an expression for a variable, not $s";
387 8 100       31 $w = newFromString($w) unless ref($w);
388 8         34 $w->isSum;
389            
390             # Each term of the sum comprising the replacement expression.
391 8         19 for my $t($E->t)
  26         196  
392             {my $n = $t->vp($s);
393 26         79 my %t = $t->split;
394 26         102 my $S = sigma($t{t}->vp($s, 0)->z); # Remove substitution variable
395 26 100       87 $S = $S->multiply(($t{s}->sub(@_))->Sqrt) if defined($t{s});
396 26 100       147 $S = $S->divide ($t{d}->sub(@_)) if defined($t{d});
397 26 50       57 $S = $S->multiply(($t{e}->sub(@_))->Exp) if defined($t{e});
398 26 50       55 $S = $S->multiply(($t{l}->sub(@_))->Log) if defined($t{l});
399 26 100       72 $S = $S->multiply($w->power(makeInt($n))) if $n;
400 26         127 $Z = $Z->add($S);
401             }
402 8         115 $E = $Z;
403             }
404            
405             # Result
406 7         36 $E;
407             }
408            
409            
410             =head3 isEqual
411            
412             Check whether one sum is equal to another after multiplying out all
413             divides and divisors.
414            
415             =cut
416            
417            
418 76     76 1 225 sub isEqual($)
419             {my ($C) = @_;
420            
421             # Until there are no more divides
422 76         128 for(;;)
  158         359  
423 158         241 {my (%c, $D, $N); $N = 0;
424            
425             # Most frequent divisor
426 158         470 for my $t($C->t)
  1727         4420  
427             {my $d = $t->Divide;
428 1727 100       3872 next unless $d;
429 1094         2334 my $s = $d->getSignature;
430 1094 100       3565 if (++$c{$s} > $N)
  713         1236  
431             {$N = $c{$s};
432 713         1172 $D = $d;
433             }
434             }
435 158 100       802 last unless $N;
436 82         251 $C = $C->multiply($D);
437             }
438            
439             # Until there are no more negative powers
440 76         139 for(;;)
  85         136  
441             {my %v;
442 85         743 for my $t($C->t)
  156         641  
443 150         537 {for my $v($t->v)
444             {my $p = $t->vp($v);
445 156 100       747 next unless $p < 0;
446 10         22 $p = -$p;
447 10 100 66     72 $v{$v} = $p if !defined($v{$v}) or $v{$v} < $p;
448             }
449             }
450 85 100       407 last unless scalar(keys(%v));
451 9         460 my $m = term()->one->clone;
452 9         78 $m->vp($_, $v{$_}) for keys(%v);
453 9         39 my $M = sigma($m->z);
454 9         42 $C = $C->multiply($M);
455             }
456            
457             # Result
458 76         867 $C;
459             }
460            
461            
462             =head3 normalizeSqrts
463            
464             Normalize sqrts in a sum.
465            
466             This routine needs fixing.
467            
468             It should simplify square roots.
469            
470             =cut
471            
472            
473 86     86 1 153 sub normalizeSqrts($)
474             {my ($s) = @_;
475 86         199 return $s;
476 0         0 my (@t, @s);
477            
478             # Find terms with single simple sqrts that can be normalized.
479 0         0 for my $t($s->t)
  0         0  
480             {push @t, $t;
481 0 0       0 my $S = $t->Sqrt; next unless $S; # Check for sqrt
  0         0  
482 0 0       0 my $St = $S->st; next unless $St; # Check for single term sqrt
  0         0  
483            
484 0         0 my %T = $St->split; # Split single term sqrt
485 0 0 0     0 next if $T{s} or $T{d} or $T{e} or $T{l};
      0        
      0        
486 0         0 pop @t;
487 0         0 push @s, {t=>$t, s=>$T{t}->z}; # Sqrt with simple single term
488             }
489            
490             # Already normalized unless there are several such terms
491 0 0       0 return $s unless scalar(@s) > 1;
492            
493             # Remove divisor for each normalized term
494 0         0 for my $r(@s)
  0         0  
495 0 0       0 {my $d = $r->{t}->d; next unless $d > 1;
496 0         0 for my $s(@s)
  0         0  
497             {$s->{t} = $s->{t}->clone->divideInt($d) ->z;
498 0         0 $s->{s} = $s->{s}->clone->timesInt ($d*$d)->z;
499             }
500             }
501            
502             # Eliminate duplicate squared factors
503 0         0 for my $s(@s)
  0         0  
504             {my $F = factorize($s->{s}->c);
505 0         0 my $p = 1;
506 0 0       0 for my $f(keys(%$F))
  0         0  
507             {$p *= $f**(int($F->{$f}/2)) if $F->{$f} > 1;
508             }
509 0         0 $s->{t} = $s->{t}->clone->timesInt ($p) ->z;
510 0         0 $s->{s} = $s->{s}->clone->divideInt($p*$p)->z;
511            
512 0         0 $DB::single = 1;
513 0 0       0 if ($s->{s}->isOne)
514             {
515            
516 0         0 push @t, $s->{t}->removeSqrt;
517             }
518             else
519             {
520            
521 0         0 push @t, $s->{t}->clone->Sqrt($s->{$s})->z;
522             }
523             }
524            
525             # Result
526 0         0 sigma(@t);
527             }
528            
529            
530             =head3 isEqualSqrt
531            
532             Check whether one sum is equal to another after multiplying out sqrts.
533            
534             =cut
535            
536            
537 79     79 1 177 sub isEqualSqrt($)
538             {my ($C) = @_;
539            
540             #_______________________________________________________________________
541             # Each sqrt
542             #_______________________________________________________________________
543            
544 79         229 for(1..99)
  86         458  
545             {$C = $C->normalizeSqrts;
546 86         377 my @s = grep { defined($_->Sqrt)} $C->t;
  278         1055  
547 86         289 my @n = grep {!defined($_->Sqrt)} $C->t;
  278         749  
548 86 100       439 last unless scalar(@s) > 0;
549            
550             #_______________________________________________________________________
551             # Partition by square roots.
552             #_______________________________________________________________________
553            
554 10         31 my %S = ();
555 10         37 for my $t(@s)
  19         81  
556             {my $s = $t->Sqrt;
557 19         53 my $S = $s->signature;
558 19         36 push @{$S{$S}}, $t;
  19         95  
559             }
560            
561             #_______________________________________________________________________
562             # Square each partitions, as required by the formulae below.
563             #_______________________________________________________________________
564            
565 10         22 my @t;
566 10 100       65 push @t, sigma(@n)->power($two) if scalar(@n); # Non sqrt partition
567 10         47 for my $s(keys(%S))
  19         62  
568 19         35 {push @t, sigma(@{$S{$s}})->power($two); # Sqrt partition
569             }
570            
571             #_______________________________________________________________________
572             # I can multiply out upto 4 square roots using the formulae below.
573             # There are formula to multiply out more than 4 sqrts, but they are big.
574             # These formulae are obtained by squaring out and rearranging:
575             # sqrt(a)+sqrt(b)+sqrt(c)+sqrt(d) == 0 until no sqrts remain, and
576             # then matching terms to produce optimal execution.
577             # This remarkable result was obtained with the help of this package:
578             # demonstrating its utility in optimizing complex calculations written
579             # in Perl: which in of itself cannot optimize broadly.
580             #_______________________________________________________________________
581            
582 10         37 my $ns = scalar(@t);
583 10 50       76 $ns < 5 or die "There are $ns square roots present. I can handle less than 5";
584            
585 10         30 my ($a, $b, $c, $d) = @t;
586            
587 10 50       93 if ($ns == 1)
  0 100       0  
    100          
    50          
588 6         31 {$C = $a;
589             }
590             elsif ($ns == 2)
591 3         23 {$C = $a-$b;
592             }
593             elsif ($ns == 3)
594 1         4 {$C = -$a**2+2*$a*$b-$b**2+2*$c*$a+2*$c*$b-$c**2;
595             }
596             elsif ($ns == 4)
597             {my $a2 = $a * $a;
598 1         6 my $a3 = $a2 * $a;
599 0         0 my $a4 = $a3 * $a;
600 0         0 my $b2 = $b * $b;
601 0         0 my $b3 = $b2 * $b;
602 0         0 my $b4 = $b3 * $b;
603 0         0 my $c2 = $c * $c;
604 0         0 my $c3 = $c2 * $c;
605 0         0 my $c4 = $c3 * $c;
606 0         0 my $d2 = $d * $d;
607 0         0 my $d3 = $d2 * $d;
608 0         0 my $d4 = $d3 * $d;
609 0         0 my $bpd = $b + $d;
610 0         0 my $bpc = $b + $c;
611 0         0 my $cpd = $c + $d;
612 0         0 $C =
613             - ($a4 + $b4 + $c4 + $d4)
614             + 4*(
615             +$a3*($b+$cpd)+$b3*($a+$cpd)+$c3*($a+$bpd)+$d3*($a+$bpc)
616             -$a2*($b *($cpd)+ $c*$d)
617             -$a *($b2*($cpd)+$d2*($bpc))
618             )
619            
620             - 6*($a2*$b2+($a2+$b2)*($c2+$d2)+$c2*$d2)
621            
622             - 4*$c*($b2*$d+$b*$d2)
623             - 4*$c2*($a*($bpd)+$b*$d)
624             +40*$c*$a*$b*$d
625             ;
626             }
627             }
628            
629             #________________________________________________________________________
630             # Test result
631             #________________________________________________________________________
632            
633             # $C->isEqual($zero);
634 76         313 $C;
635             }
636            
637            
638             =head3 isZero
639            
640             Transform a sum assuming that it is equal to zero
641            
642             =cut
643            
644            
645 79     79 1 147 sub isZero($)
646             {my ($C) = @_;
647 79         436 $C->isEqualSqrt->isEqual;
648             }
649            
650            
651             =head3 powerOfTwo
652            
653             Check that a number is a power of two
654            
655             =cut
656            
657            
658 0     0 0 0 sub powerof2($)
659             {my ($N) = @_;
660 0         0 my $n = 0;
661 0 0       0 return undef unless $N > 0;
662 0 0       0 for (;;)
  0         0  
663             {return $n if $N == 1;
664 0 0       0 return undef unless $N % 2 == 0;
665 0         0 ++$n; $N /= 2;
  0         0  
666             }
667             }
668            
669            
670             =head3 solve
671            
672             Solve an equation known to be equal to zero for a specified variable.
673            
674             =cut
675            
676            
677 17     17 1 63 sub solve($$)
678             {my ($A, @x) = @_;
679 17 50       67 croak 'Need variable to solve for' unless scalar(@x) > 0;
680            
681 17 100 100     132 @x = @{$x[0]} if scalar(@x) == 1 and ref($x[0]) eq 'ARRAY'; # Array of variables supplied
  1         4  
682 17         37 my %x;
683 17 50       46 for my $x(@x)
  51         206  
684 56 100       134 {if (!ref $x)
    50          
685 5         21 {$x =~ /^[a-z]+$/i or croak "Cannot solve for: $x, not a variable name";
686             }
687             elsif (ref $x eq __PACKAGE__)
688 5 50       29 {my $t = $x->st; $t or die "Cannot solve for multiple terms";
  0         0  
689 5 50       24 my @b = $t->v; scalar(@b) == 1 or die "Can only solve for one variable";
  5         32  
690 5 50       26 my $p = $t->vp($b[0]); $p == 1 or die "Can only solve by variable to power 1";
  5         21  
691 5         16 $x = $b[0];
692             }
693             else
694             {die "$x is not a variable name";
695             }
696 56         235 $x{$x} = 1;
697             }
698 17         37 my $x = $x[0];
699            
700 17         126 $B = $A->isZero; # Eliminate sqrts and negative powers
701            
702             # Strike all terms with free variables other than x: i.e. not x and not one of the named constants
703 16         45 my @t = ();
704 16         47 for my $t($B->t)
  42         114  
705             {my @v = $t->v;
706 42         76 push @t, $t;
707 42 50       114 for my $v($t->v)
  44         177  
708             {next if exists($x{$v});
709 0         0 pop @t;
710 0         0 last;
711             }
712             }
713 16         54 my $C = sigma(@t);
714            
715             # Find highest and lowest power of x
716 16         30 my $n = 0; my $N;
  16         28  
717 16         51 for my $t($C->t)
  42         303  
718             {my $p = $t->vp($x);
719 42 100       105 $n = $p if $p > $n;
720 42 100 100     327 $N = $p if !defined($N) or $p < $N;
721             }
722 16         40 my $D = $C;
723 16 50       47 $D = $D->multiply(sigma(term()->one->clone->vp($x, -$N)->z)) if $N;
724 16 50       43 $n -= $N if $N;
725            
726             # Find number of terms in x
727 16         33 my $c = 0;
728 16 100       46 for my $t($D->t)
  42         122  
729             {++$c if $t->vp($x) > 0;
730             }
731            
732 16 50       60 $n == 0 and croak "Equation not dependant on $x, so cannot solve for $x";
733 16 50 33     70 $n > 4 and $c > 1 and croak "Unable to solve polynomial or power $n > 4 in $x (Galois)";
734 16 50 33     227 ($n > 2 and $c > 1) and die "Need solver for polynomial of degree $n in $x";
735            
736             # Solve linear equation
737 16 100 66     80 if ($n == 1 or $c == 1)
  7         11  
738             {my (@c, @v);
739 7 100       20 for my $t($D->t)
  15         46  
740             {push(@c, $t), next if $t->vp($x) == 0; # Constants
741 7         21 push @v, $t; # Powers of x
742             }
743 7         25 my $d = sigma(@v)->multiply(sigma(term()->one->clone->vp($x, -$n)->negate->z));
744 7         76 $D = sigma(@c)->divide($d);
745            
746 7 50       87 return $D if $n == 1;
747            
748 0         0 my $p = powerof2($n);
749 0 0       0 $p or croak "Fractional power 1/$n of $x unconstructable by sqrt";
750 0         0 $D = $D->Sqrt for(1..$p);
751 0         0 return $D;
752             }
753            
754             # Solve quadratic equation
755 9 50       33 if ($n == 2)
  9         23  
756             {my @c = ($one, $one, $one);
757 9         22 $c[$_->vp($x)] = $_ for $D->t;
758 9         50 $_ = sigma($_->clone->vp($x, 0)->z) for (@c);
759 9         27 my ($c, $b, $a) = @c;
760             return
761 9         42 [ (-$b->add (($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a)),
762             (-$b->subtract(($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a))
763             ]
764             }
765            
766             # Check that it works
767            
768             # my $yy = $e->sub($x=>$xx);
769             # $yy == 0 or die "Proposed solution \$$x=$xx does not zero equation $e";
770             # $xx;
771             }
772            
773            
774             =head3 power
775            
776             Raise a sum to an integer power or an integer/2 power.
777            
778             =cut
779            
780            
781 287     287 1 516 sub power($$)
782             {my ($a, $b) = @_;
783            
784 287 50       1011 return $one if $b->{id} == $zero->{id};
785 287 100       1227 return $a->multiply($a) if $b->{id} == $two->{id};
786 47 100       166 return $a if $b->{id} == $one->{id};
787 43 50       169 return $one->divide($a) if $b->{id} == $mOne->{id};
788 43 50       148 return $a->sqrt if $b->{id} == $half->{id};
789 43 50       172 return $one->divide($a->sqrt) if $b->{id} == $mHalf->{id};
790            
791 43         129 my $T = $b->st;
792 43 50       196 $T or croak "Power by expression too complicated";
793            
794 43         205 my %t = $T->split;
795 43 50 33     589 croak "Power by term too complicated" if $t{s} or $t{d} or $t{e} or $t{l};
      33        
      33        
796            
797 43         78 my $t = $t{t};
798 43 50       147 $t->i == 0 or croak "Complex power not allowed yet";
799            
800 43         157 my ($p, $d) = ($t->c, $t->d);
801 43 50 33     1285 $d == 1 or $d == 2 or croak "Fractional power other than /2 not allowed yet";
802            
803 43 50       130 $a = $a->sqrt if $d == 2;
804            
805 43 50       108 return $one->divide($a)->power(sigma(term()->c($p)->z)) if $p < 0;
806            
807 43         74 $p = abs($p);
808 43         1387 my $r = $a; $r = $r->multiply($a) for (2..$p);
  43         348  
809 43         538 $r;
810             }
811            
812            
813             =head3 d
814            
815             Differentiate.
816            
817             =cut
818            
819            
820             sub d($;$);
821 184     184 1 300 sub d($;$)
822             {my $c = $_[0]; # Differentiate this sum
823 184         309 my $b = $_[1]; # With this variable
824            
825             #_______________________________________________________________________
826             # Get differentrix. Assume 'x', 'y', 'z' or 't' if appropriate.
827             #_______________________________________________________________________
828            
829 184 50       389 if (defined($b))
  129 100       1327  
830 132 100       309 {if (!ref $b)
  52 50       79  
831 3         10 {$b =~ /^[a-z]+$/i or croak "Cannot differentiate by $b";
832             }
833             elsif (ref $b eq __PACKAGE__)
834 3 50       13 {my $t = $b->st; $t or die "Cannot differentiate by multiple terms";
  0         0  
835 3 50       13 my @b = $t->v; scalar(@b) == 1 or die "Can only differentiate by one variable";
  3         39  
836 3 50       14 my $p = $t->vp($b[0]); $p == 1 or die "Can only differentiate by variable to power 1";
  3         16  
837 3         7 $b = $b[0];
838             }
839             else
840             {die "Cannot differentiate by $b";
841             }
842             }
843             else
844             {my %b;
845 52         161 for my $t($c->t)
  103         148  
846 103         330 {my %b; $b{$_}++ for ($t->v);
847             }
848 52         205 my $i = 0; my $n = scalar(keys(%b));
  52         103  
849 52 50       347 ++$i, $b = 'x' if $n == 0; # Constant expression anyway
850 52 50       132 ++$i, $b = (%b)[0] if $n == 1;
851 52 50 33     100 for my $v(qw(t x y z))
  208         634  
852             {++$i, $b = 't' if $n > 1 and exists($b{$v});
853             }
854 52 50       334 $i == 1 or croak "Please specify a single variable to differentiate by";
855             }
856            
857             #_______________________________________________________________________
858             # Each term
859             #_______________________________________________________________________
860            
861 184         610 my @t = ();
862 184         415 for my $t($c->t)
  243         865  
863             {my %V = $t->split;
864 243         922 my $T = $V{t}->z->clone->z;
865 243         1355 my ($S, $D, $E, $L) = @V{qw(s d e l)};
866 243 50       515 my $s = $S->d($b) if $S;
867 243 100       455 my $d = $D->d($b) if $D;
868 243 100       484 my $e = $E->d($b) if $E;
869 243 50       540 my $l = $L->d($b) if $L;
870            
871             #_______________________________________________________________________
872             # Differentiate Variables: A*v**n->d == A*n*v**(n-1)
873             #_______________________________________________________________________
874            
875 243         271 {my $v = $T->clone;
  243         752  
876 243         770 my $p = $v->vp($b);
877 243 100       829 if ($p != 0)
  121         373  
878             {$v->timesInt($p)->vp($b, $p-1);
879 121 50       307 $v->Sqrt ($S) if $S;
880 121 50       257 $v->Divide($D) if $D;
881 121 50       240 $v->Exp ($E) if $E;
882 121 50       271 $v->Log ($L) if $L;
883 121         364 push @t, $v->z;
884             }
885             }
886            
887             #_______________________________________________________________________
888             # Differentiate Sqrt: A*sqrt(F(x))->d == 1/2*A*f(x)/sqrt(F(x))
889             #_______________________________________________________________________
890            
891 243 50       517 if ($S)
  0         0  
892             {my $v = $T->clone->divideInt(2);
893 0 0       0 $v->Divide($D) if $D;
894 0 0       0 $v->Exp ($E) if $E;
895 0 0       0 $v->Log ($L) if $L;
896 0         0 push @t, sigma($v->z)->multiply($s)->divide($S->Sqrt)->t;
897             }
898            
899             #_______________________________________________________________________
900             # Differentiate Divide: A/F(x)->d == -A*f(x)/F(x)**2
901             #_______________________________________________________________________
902            
903 243 100       560 if ($D)
  8         30  
904             {my $v = $T->clone->negate;
905 8 50       27 $v->Sqrt($S) if $S;
906 8 100       20 $v->Exp ($E) if $E;
907 8 50       29 $v->Log ($L) if $L;
908 8         28 push @t, sigma($v->z)->multiply($d)->divide($D->multiply($D))->t;
909             }
910            
911             #_______________________________________________________________________
912             # Differentiate Exp: A*exp(F(x))->d == A*f(x)*exp(F(x))
913             #_______________________________________________________________________
914            
915 243 100       592 if ($E)
  120         598  
916             {my $v = $T->clone;
917 120 50       738 $v->Sqrt ($S) if $S;
918 120 100       243 $v->Divide($D) if $D;
919 120         525 $v->Exp ($E);
920 120 50       638 $v->Log ($L) if $L;
921 120         632 push @t, sigma($v->z)->multiply($e)->t;
922             }
923            
924             #_______________________________________________________________________
925             # Differentiate Log: A*log(F(x))->d == A*f(x)/F(x)
926             #_______________________________________________________________________
927            
928 243 50       1638 if ($L)
  0         0  
929             {my $v = $T->clone;
930 0 0       0 $v->Sqrt ($S) if $S;
931 0 0       0 $v->Divide($D) if $D;
932 0 0       0 $v->Exp ($E) if $E;
933 0         0 push @t, sigma($v->z)->multiply($l)->divide($L)->t;
934             }
935             }
936            
937             #_______________________________________________________________________
938             # Result
939             #_______________________________________________________________________
940            
941 184         635 sigma(@t);
942             }
943            
944            
945             =head3 simplify
946            
947             Simplify just before assignment.
948            
949             There is no general simplification algorithm. So try various methods and
950             see if any simplifications occur. This is cheating really, because the
951             examples will represent these specific transformations as general
952             features which they are not. On the other hand, Mathematics is full of
953             specifics so I suppose its not entirely unacceptable.
954            
955             Simplification cannot be done after every operation as it is
956             inefficient, doing it as part of += ameliorates this inefficiency.
957            
958             Note: += only works as a synonym for simplify() if the left hand side is
959             currently undefined. This can be enforced by using my() as in: my $z +=
960             ($x**2+5x+6)/($x+2);
961            
962             =cut
963            
964            
965 14     14 1 27 sub simplify($)
966             {my ($x) = @_;
967 14         57 $x = polynomialDivision($x);
968 12         47 $x = eigenValue($x);
969             }
970            
971             #_______________________________________________________________________
972             # Common factor: find the largest factor in one or more expressions
973             #_______________________________________________________________________
974            
975 24 50   24 0 75 sub commonFactor(@)
976             {return undef unless scalar(@_);
977 24 50       33 return undef unless scalar(keys(%{$_[0]->{t}}));
  24         90  
978 24         48 my $p = (values(%{$_[0]->{t}}))[0];
  24         70  
979            
980 24         36 my %v = %{$p->{v}}; # Variables
  24         71  
981 24         95 my %s = $p->split;
982 24         84 my ($s, $d, $e, $l) = @s{qw(s d e l)}; # Sub expressions
983 24         94 my ($C, $D, $I) = ($p->c, $p->d, $p->i);
984            
985 24         40 my @t;
986 24         59 for my $a(@_)
  75         167  
987 24         60 {for my $b($a->t)
988             {push @t, $b;
989             }
990             }
991            
992 24         45 for my $t(@t)
  75         163  
993             {my %V = %v;
994 75         124 %v = ();
995 75 100       227 for my $v($t->v)
  51         171  
996             {next unless $V{$v};
997 27         107 my $p = $t->vp($v);
998 27 100       126 $v{$v} = ($V{$v} < $p ? $V{$v} : $p);
999             }
1000 75         246 my %S = $t->split;
1001 75         210 my ($S, $D, $E, $L) = @S{qw(s d e l)}; # Sub expressions
1002 75 0 33     217 $s = undef unless defined($s) and defined($S) and $S->id eq $s->id;
      33        
1003 75 0 33     1367 $d = undef unless defined($d) and defined($D) and $D->id eq $d->id;
      33        
1004 75 50 66     223 $e = undef unless defined($e) and defined($E) and $E->id eq $e->id;
      66        
1005 75 0 33     193 $l = undef unless defined($l) and defined($L) and $L->id eq $l->id;
      33        
1006 75 100 100     375 $C = undef unless defined($C) and $C == $t->c;
1007 75 50 33     259 $D = undef unless defined($D) and $D == $t->d;
1008 75 100 100     421 $I = undef unless defined($I) and $I == $t->i;
1009             }
1010 24         1176 my $r = term()->one->clone;
1011 24 100       173 $r->c($C) if defined($C);
1012 24 50       102 $r->d($D) if defined($D);
1013 24 100       106 $r->i($I) if defined($I);
1014 24         86 $r->vp($_, $v{$_}) for(keys(%v));
1015 24 50       109 $r->Sqrt ($s) if defined($s);
1016 24 50       67 $r->Divide($d) if defined($d);
1017 24 50       62 $r->Exp ($e) if defined($e);
1018 24 50       67 $r->Log ($l) if defined($l);
1019 24         84 sigma($r->z);
1020             }
1021            
1022             #_______________________________________________________________________
1023             # Find term of polynomial of highest degree.
1024             #_______________________________________________________________________
1025            
1026 148     148 0 206 sub polynomialTermOfHighestDegree($$)
1027             {my ($p, $v) = @_; # Polynomial, variable
1028 148         173 my $n = 0; # Current highest degree
1029 148         168 my $t; # Term with this degree
1030 148         353 for my $T($p->t)
  293         768  
1031             {my $N = $T->vp($v);
1032 293 100       802 if ($N > $n)
  141         189  
1033             {$n = $N;
1034 141         396 $t = $T;
1035             }
1036             }
1037 148         446 ($n, $t);
1038             }
1039            
1040            
1041             =head3 polynomialDivide
1042            
1043             Polynomial divide - divide one polynomial (a) by another (b) in variable v
1044            
1045             =cut
1046            
1047            
1048 12     12 1 23 sub polynomialDivide($$$)
1049             {my ($p, $q, $v) = @_;
1050            
1051 12         35 my $r = zero()->clone()->z;
1052 12         36 for(;;)
  74         311  
1053             {my ($np, $mp) = $p->polynomialTermOfHighestDegree($v);
1054 74         246 my ($nq, $mq) = $q->polynomialTermOfHighestDegree($v);
1055 74 100       206 last unless $np >= $nq;
1056 64         198 my $pq = sigma($mp->divide2($mq));
1057 64         218 $r = $r->add($pq);
1058 64         305 $p = $p->subtract($q->multiply($pq));
1059             }
1060 10 100       41 return $r if $p->isZero()->{id} == $zero->{id};
1061 1         3 undef;
1062             }
1063            
1064            
1065             =head3 eigenValue
1066            
1067             Eigenvalue check
1068            
1069             =cut
1070            
1071            
1072 12     12 1 23 sub eigenValue($)
1073             {my ($p) = @_;
1074            
1075             # Find divisors
1076 12         20 my %d;
1077 12         38 for my $t($p->t)
  59         175  
1078             {my $d = $t->Divide;
1079 59 100       177 next unless defined($d);
1080 5         14 $d{$d->id} = $d;
1081             }
1082            
1083             # Consolidate numerator and denominator
1084 12         90 my $P = $p ->clone()->z; $P = $P->multiply($d{$_}) for(keys(%d));
  12         74  
1085 12         45 my $Q = one()->clone()->z; $Q = $Q->multiply($d{$_}) for(keys(%d));
  12         65  
1086            
1087             # Check for P=nQ i.e. for eigenvalue
1088 12         52 my $cP = $P->commonFactor; my $dP = $P->divide($cP);
  12         55  
1089 12         48 my $cQ = $Q->commonFactor; my $dQ = $Q->divide($cQ);
  12         46  
1090            
1091 12 100       56 return $cP->divide($cQ) if $dP->id == $dQ->id;
1092 9         60 $p;
1093             }
1094            
1095            
1096             =head3 polynomialDivision
1097            
1098             Polynomial division.
1099            
1100             =cut
1101            
1102            
1103 14     14 1 129 sub polynomialDivision($)
1104             {my ($p) = @_;
1105            
1106             # Find a plausible indeterminate
1107 14         24 my %v; # Possible indeterminates
1108             my $v; # Polynomial indeterminate
1109 0         0 my %D; # Divisors for each term
1110            
1111             # Each term
1112 14         45 for my $t($p->t)
  27         100  
1113             {my @v = $t->v;
1114 27         105 $v{$_}{$t->vp($_)} = 1 for(@v);
1115 27         86 my %V = $t->split;
1116 27         228 my ($S, $D, $E, $L) = @V{qw(s d e l)};
1117 27 100 66     214 return $p if defined($S) or defined($E) or defined($L);
      66        
1118            
1119             # Each divisor term
1120 26 100       75 if (defined($D))
  48         170  
1121 24         57 {for my $T($D->t)
1122             {my @v = $T->v;
1123 48         159 $v{$_}{$T->vp($_)} = 1 for(@v);
1124 48         145 my %V = $T->split;
1125 48         124 my ($S, $D, $E, $L) = @V{qw(s d e l)};
1126 48 50 33     609 return $p if defined($S) or defined($D) or defined($E) or defined($L);
      33        
      33        
1127             }
1128 24         82 $D{$D->id} = $D;
1129             }
1130             }
1131            
1132             # Consolidate numerator and denominator
1133 13         62 my $P = $p ->clone()->z; $P = $P->multiply($D{$_}) for(keys(%D));
  13         85  
1134 13         121 my $Q = one()->clone()->z; $Q = $Q->multiply($D{$_}) for(keys(%D));
  13         81  
1135            
1136             # Pick a possible indeterminate
1137 13         45 for(keys(%v))
  15         81  
1138 15 100       22 {delete $v{$_} if scalar(keys(%{$v{$_}})) == 1;
1139             }
1140 13 100       50 return $p unless scalar(keys(%v));
1141 11         30 $v = (keys(%v))[0];
1142            
1143             # Divide P by Q
1144 11         18 my $r;
1145 11 100       53 $r = $P->polynomialDivide($Q, $v); return $r if defined($r);
  9         70  
1146 1 50       4 $r = $Q->polynomialDivide($P, $v); return one()->divide($r) if defined($r);
  1         7  
1147 0         0 $p;
1148             }
1149            
1150            
1151             =head3 Sqrt
1152            
1153             Square root of a sum
1154            
1155             =cut
1156            
1157            
1158 90     90 1 189 sub Sqrt($)
1159             {my ($x) = @_;
1160 90         463 my $s = $x->st;
1161 90 100       307 if (defined($s))
  54         211  
1162             {my $r = $s->sqrt2;
1163 54 100       294 return sigma($r) if defined($r);
1164             }
1165            
1166 64         3230 sigma(term()->c(1)->Sqrt($x)->z);
1167             }
1168            
1169            
1170             =head3 Exp
1171            
1172             Exponential (B raised to the power) of a sum
1173            
1174             =cut
1175            
1176            
1177 484     484 1 671 sub Exp($)
1178             {my ($x) = @_;
1179 484         16674 my $p = term()->one;
1180 484         11883 my @r;
1181 484         1039 for my $t($x->t)
  548         1583  
1182             {my $r = $t->exp2;
1183 548 100       1191 $p = $p->multiply($r) if $r;
1184 548 100       1834 push @r, $t unless $r;
1185             }
1186 484 100       1385 return sigma($p) if scalar(@r) == 0;
1187 473         1347 return sigma($p->clone->Exp(sigma(@r))->z);
1188             }
1189            
1190            
1191             =head3 Log
1192            
1193             Log to base B of a sum
1194            
1195             =cut
1196            
1197            
1198 1     1 1 2 sub Log($)
1199             {my ($x) = @_;
1200 1         5 my $s = $x->st;
1201 1 50       4 if (defined($s))
  1         6  
1202             {my $r = $s->log2;
1203 1 50       5 return sigma($r) if defined($r);
1204             }
1205            
1206 1         39 sigma(term()->c(1)->Log($x)->z);
1207             }
1208            
1209            
1210             =head3 Sin
1211            
1212             Sine of a sum
1213            
1214             =cut
1215            
1216            
1217 136     136 1 213 sub Sin($)
1218             {my ($x) = @_;
1219 136         342 my $s = $x->st;
1220 136 100       389 if (defined($s))
  120         560  
1221             {my $r = $s->sin2;
1222 120 100       346 return sigma($r) if defined($r);
1223             }
1224            
1225 113         350 my $a = $i->multiply($x);
1226 113         301 $i->multiply($half)->multiply($a->negate->Exp->subtract($a->Exp));
1227             }
1228            
1229            
1230             =head3 Cos
1231            
1232             Cosine of a sum
1233            
1234             =cut
1235            
1236            
1237 141     141 1 215 sub Cos($)
1238             {my ($x) = @_;
1239 141         533 my $s = $x->st;
1240 141 100       431 if (defined($s))
  125         626  
1241             {my $r = $s->cos2;
1242 125 100       365 return sigma($r) if defined($r);
1243             }
1244            
1245 118         319 my $a = $i->multiply($x);
1246 118         378 $half->multiply($a->negate->Exp->add($a->Exp));
1247             }
1248            
1249            
1250             =head3 tan, Ssc, csc, cot
1251            
1252             Tan, sec, csc, cot of a sum
1253            
1254             =cut
1255            
1256            
1257 27     27 1 59 sub tan($) {my ($x) = @_; $x->Sin()->divide($x->Cos())}
  27         89  
1258 11     11 0 28 sub sec($) {my ($x) = @_; $one ->divide($x->Cos())}
  11         37  
1259 11     11 1 18 sub csc($) {my ($x) = @_; $one ->divide($x->Sin())}
  11         52  
1260 11     11 1 22 sub cot($) {my ($x) = @_; $x->Cos()->divide($x->Sin())}
  11         45  
1261            
1262            
1263             =head3 sinh
1264            
1265             Hyperbolic sine of a sum
1266            
1267             =cut
1268            
1269            
1270 34     34 1 95 sub sinh($)
1271             {my ($x) = @_;
1272            
1273 34 50       154 return $zero if $x->{id} == $zero->{id};
1274            
1275 34         103 my $n = $x->negate;
1276 34         1188 sigma
1277             (term()->c( 1)->divideInt(2)->Exp($x)->z,
1278             term()->c(-1)->divideInt(2)->Exp($n)->z
1279             )
1280             }
1281            
1282            
1283             =head3 cosh
1284            
1285             Hyperbolic cosine of a sum
1286            
1287             =cut
1288            
1289            
1290 34     34 1 62 sub cosh($)
1291             {my ($x) = @_;
1292            
1293 34 50       151 return $one if $x->{id} == $zero->{id};
1294            
1295 34         105 my $n = $x->negate;
1296 34         1269 sigma
1297             (term()->c(1)->divideInt(2)->Exp($x)->z,
1298             term()->c(1)->divideInt(2)->Exp($n)->z
1299             )
1300             }
1301            
1302            
1303             =head3 Tanh, Sech, Csch, Coth
1304            
1305             Tanh, Sech, Csch, Coth of a sum
1306            
1307             =cut
1308            
1309            
1310 14     14 0 34 sub tanh($) {my ($x) = @_; $x->sinh()->divide($x->cosh())}
  14         49  
1311 4     4 0 9 sub sech($) {my ($x) = @_; $one ->divide($x->cosh())}
  4         15  
1312 4     4 0 9 sub csch($) {my ($x) = @_; $one ->divide($x->sinh())}
  4         19  
1313 4     4 0 10 sub coth($) {my ($x) = @_; $x->cosh()->divide($x->sinh())}
  4         18  
1314            
1315            
1316             =head3 dot
1317            
1318             Dot - complex dot product of two complex sums
1319            
1320             =cut
1321            
1322            
1323 35     35 1 52 sub dot($$)
1324             {my ($a, $b) = @_;
1325 35 50       126 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1326 35         100 $a->re->multiply($b->re)->add($a->im->multiply($b->im));
1327             }
1328            
1329            
1330             =head3 cross
1331            
1332             The area of the parallelogram formed by two complex sums
1333            
1334             =cut
1335            
1336            
1337 8     8 1 16 sub cross($$)
1338             {my ($a, $b) = @_;
1339 8         33 $a->dot($a)->multiply($b->dot($b))->subtract($a->dot($b)->power($two))->Sqrt;
1340             }
1341            
1342            
1343             =head3 unit
1344            
1345             Intersection of a complex sum with the unit circle.
1346            
1347             =cut
1348            
1349            
1350 10     10 1 32 sub unit($)
1351             {my ($a) = @_;
1352 10         37 my $b = $a->modulus;
1353 10         52 my $c = $a->divide($b);
1354 10         38 $a->divide($a->modulus);
1355             }
1356            
1357            
1358             =head3 re
1359            
1360             Real part of a complex sum
1361            
1362             =cut
1363            
1364            
1365 123     123 1 183 sub re($)
1366             {my ($A) = @_;
1367 123 50       1039 $A = newFromString("$A") unless ref($A) eq __PACKAGE__;
1368 123         156 my @r;
1369 123 100       313 for my $a($A->t)
  208         601  
1370             {next if $a->i == 1;
1371 102         236 push @r, $a;
1372             }
1373 123         564 sigma(@r);
1374             }
1375            
1376            
1377             =head3 im
1378            
1379             Imaginary part of a complex sum
1380            
1381             =cut
1382            
1383            
1384 124     124 1 208 sub im($)
1385             {my ($A) = @_;
1386 124 50       381 $A = newFromString("$A") unless ref($A) eq __PACKAGE__;
1387 124         155 my @r;
1388 124 100       284 for my $a($A->t)
  213         588  
1389             {next if $a->i == 0;
1390 108         345 push @r, $a;
1391             }
1392 124         352 $mI->multiply(sigma(@r));
1393             }
1394            
1395            
1396             =head3 modulus
1397            
1398             Modulus of a complex sum
1399            
1400             =cut
1401            
1402            
1403 39     39 1 141 sub modulus($)
1404             {my ($a) = @_;
1405 39         140 $a->re->power($two)->add($a->im->power($two))->Sqrt;
1406             }
1407            
1408            
1409             =head3 conjugate
1410            
1411             Conjugate of a complexs sum
1412            
1413             =cut
1414            
1415            
1416 11     11 1 16 sub conjugate($)
1417             {my ($a) = @_;
1418 11         29 $a->re->subtract($a->im->multiply($i));
1419             }
1420            
1421            
1422             =head3 clone
1423            
1424             Clone
1425            
1426             =cut
1427            
1428            
1429 62     62 1 101 sub clone($)
1430             {my ($t) = @_;
1431 62 50       180 $t->{z} or die "Attempt to clone unfinalized sum";
1432 62         287 my $c = bless {%$t};
1433 62         105 $c->{t} = {%{$t->{t}}};
  62         223  
1434 62         150 delete $c->{z};
1435 62         179 delete $c->{s};
1436 62         103 delete $c->{id};
1437 62         209 $c;
1438             }
1439            
1440            
1441             =head3 signature
1442            
1443             Signature of a sum: used to optimize add().
1444             # Fix the problem of adding different logs
1445            
1446             =cut
1447            
1448            
1449 3565     3565 1 6558 sub signature($)
1450             {my ($t) = @_;
1451 3565         4675 my $s = '';
1452 3565         8554 for my $a($t->t)
  7262         26621  
1453             {$s .= '+'. $a->print;
1454             }
1455 3565         13052 $s;
1456             }
1457            
1458            
1459             =head3 getSignature
1460            
1461             Get the signature (see L) of a sum
1462            
1463             =cut
1464            
1465            
1466 1094     1094 1 1189 sub getSignature($)
1467             {my ($t) = @_;
1468 1094 50       3045 exists $t->{z} ? $t->{z} : die "Attempt to get signature of unfinalized sum";
1469             }
1470            
1471            
1472             =head3 id
1473            
1474             Get Id of sum: each sum has a unique identifying number.
1475            
1476             =cut
1477            
1478            
1479 61     61 1 98 sub id($)
1480             {my ($t) = @_;
1481 61 50       160 $t->{id} or die "Sum $t not yet finalized";
1482 61         310 $t->{id};
1483             }
1484            
1485            
1486             =head3 zz
1487            
1488             Check sum finalized. See: L.
1489            
1490             =cut
1491            
1492            
1493 0     0 1 0 sub zz($)
1494             {my ($t) = @_;
1495 0 0       0 $t->{z} or die "Sum $t not yet finalized";
1496 0         0 print $t->{z}, "\n";
1497 0         0 $t;
1498             }
1499            
1500            
1501             =head3 z
1502            
1503             Finalize creation of the sum: Once a sum has been finalized it becomes
1504             read only.
1505            
1506             =cut
1507            
1508            
1509             my $lock = 0; # Hash locking
1510             my $z = 0; # Term counter
1511             my %z; # Terms finalized
1512            
1513 9838     9838 1 18686 sub z($)
1514             {my ($t) = @_;
1515 9838 50       28055 !exists($t->{z}) or die "Already finalized this term";
1516            
1517 9838         18642 my $p = $t->print;
1518 9838 100       62711 return $z{$p} if defined($z{$p});
1519 3546         9488 $z{$p} = $t;
1520 3546         16062 weaken($z{$p}); # Reduces memory usage.
1521            
1522 3546         7380 $t->{s} = $p;
1523 3546         8700 $t->{z} = $t->signature;
1524 3546         7247 $t->{id} = ++$z;
1525            
1526             #HashUtil lock_hash(%{$t->{v}}) if $lock;
1527             #HashUtil lock_hash %$t if $lock;
1528 3546         16452 $t;
1529             }
1530            
1531             #sub DESTROY($)
1532             # {my ($t) = @_;
1533             # delete $z{$t->{s}} if defined($t) and exists $t->{s};
1534             # }
1535            
1536 0     0 0 0 sub lockHashes()
1537             {my ($l) = @_;
1538             #HashUtil for my $t(values %z)
1539             #HashUtil {lock_hash(%{$t->{v}});
1540             #HashUtil lock_hash %$t;
1541             #HashUtil }
1542 0         0 $lock = 1;
1543             }
1544            
1545            
1546             =head3 print
1547            
1548             Print sum
1549            
1550             =cut
1551            
1552            
1553 130634     130634 1 168115 sub print($)
1554             {my ($t) = @_;
1555 130634 100       666826 return $t->{s} if defined($t->{s});
1556 9838         14031 my $s = '';
1557 9838         21173 for my $a($t->t)
  16504         45952  
1558             {$s .= $a->print .'+';
1559             }
1560 9838 100       30527 chop($s) if $s;
1561            
1562 9838         19751 $s =~ s/^\+//;
1563 9838         18671 $s =~ s/\+\-/\-/g;
1564 9838         20917 $s =~ s/\+1\*/\+/g; # change: +1* to +
1565 9838         13409 $s =~ s/\*1\*/\*/g; # remove: *1* to *
1566 9838         19297 $s =~ s/^1\*//g; # remove: 1* at start of expression
1567 9838         16379 $s =~ s/^\-1\*/\-/g; # change: -1* at start of expression to -
1568 9838         12163 $s =~ s/^0\+//g; # change: 0+ at start of expression to
1569 9838         12064 $s =~ s/\+0$//; # remove: +0 at end of expression
1570 9838         14958 $s =~ s#\(\+0\+#\(#g; # change: (+0+ to (
1571 9838         12731 $s =~ s/\(\+/\(/g; # change: (+ to (
1572 9838         14332 $s =~ s/\(1\*/\(/g; # change: (1* to (
1573 9838         11552 $s =~ s/\(\-1\*/\(\-/g; # change: (-1* to (-
1574 9838         17934 $s =~ s/([a-zA-Z0-9)])\-1\*/$1\-/g; # change: term-1* to term-
1575 9838         12564 $s =~ s/\*(\$[a-zA-Z]+)\*\*\-1(?!\d)/\/$1/g; # change: *$y**-1 to /$y
1576 9838         12485 $s =~ s/\*(\$[a-zA-Z]+)\*\*\-(\d+)/\/$1**$2/g; # change: *$y**-n to /$y**n
1577 9838         13373 $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-1(?!\d)/1\/$1/g; # change: +-$y**-1 to +-1/$y
1578 9838         12965 $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-(\d+)/${1}1\/$2**$3/g; # change: +-$y**-n to +-1/$y**n
1579 9838 100       22022 $s = 0 if $s eq '';
1580 9838         36670 $s;
1581             }
1582            
1583            
1584             =head3 constants
1585            
1586             Useful constants
1587            
1588             =cut
1589            
1590            
1591 12     12 0 34 $zero = sigma(term('0')); sub zero() {$zero}
1592 26     26 0 102 $one = sigma(term('1')); sub one() {$one}
1593 0     0 0 0 $two = sigma(term('2')); sub two() {$two}
1594 0     0 0 0 $four = sigma(term('4')); sub four() {$four}
1595 0     0 0 0 $mOne = sigma(term('-1')); sub mOne() {$mOne}
1596 0     0 0 0 $i = sigma(term('i')); sub i() {$i}
1597 0     0 0 0 $mI = sigma(term('-i')); sub mI() {$mI}
1598 0     0 0 0 $half = sigma(term('1/2')); sub half() {$half}
1599 0     0 0 0 $mHalf = sigma(term('-1/2')); sub mHalf() {$mHalf}
1600 0     0 0 0 $pi = sigma(term('pi')); sub pi() {$pi}
1601            
1602            
1603             =head3 factorize
1604            
1605             Factorize a number.
1606            
1607             =cut
1608            
1609            
1610             @primes = qw(
1611             2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61
1612             67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151
1613             157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251
1614             257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359
1615             367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463
1616             467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593
1617             599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701
1618             709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827
1619             829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953
1620             967 971 977 983 991 997);
1621            
1622 0     0 1 0 sub factorize($)
1623             {my ($n) = @_;
1624 0         0 my $f;
1625            
1626 0         0 for my $p(@primes)
  0         0  
1627 0         0 {for(;$n % $p == 0;)
1628             {$f->{$p}++;
1629 0         0 $n /= $p;
1630             }
1631 0 0       0 last unless $n > $p;
1632             }
1633 0         0 $f;
1634             };
1635            
1636            
1637             =head2 import
1638            
1639             Export L with either the default name B, or a name supplied by
1640             the caller of this package.
1641            
1642             =cut
1643            
1644            
1645             sub import
1646 45     45   200 {my %P = (program=>@_);
1647 45         92 my %p; $p{lc()} = $P{$_} for(keys(%P));
  45         285  
1648            
1649             #_______________________________________________________________________
1650             # New sum constructor - export to calling package.
1651             #_______________________________________________________________________
1652            
1653 45         147 my $s = "package XXXX;\n". <<'END';
1654             no warnings 'redefine';
1655             sub NNNN
1656             {return SSSSn(@_);
1657             }
1658             use warnings 'redefine';
1659             END
1660            
1661             #_______________________________________________________________________
1662             # Export to calling package.
1663             #_______________________________________________________________________
1664            
1665 45         96 my $name = 'sum';
1666 45 50       351 $name = $p{sum} if exists($p{sum});
1667 45         190 my ($main) = caller();
1668 45         110 my $pack = __PACKAGE__ . '::';
1669            
1670 45         212 $s=~ s/XXXX/$main/g;
1671 45         183 $s=~ s/NNNN/$name/g;
1672 45         268 $s=~ s/SSSS/$pack/g;
1673 45     45 0 495 eval($s);
  45     45   97  
  45     60   4559  
  45         266  
  45         141  
  45         1551  
  45         5121  
  60         524  
1674            
1675             #_______________________________________________________________________
1676             # Check options supplied by user
1677             #_______________________________________________________________________
1678            
1679 45         191 delete @p{qw(program sum)};
1680            
1681 45 50       1185 croak "Unknown option(s): ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p);
1682            
1683             Valid options are:
1684            
1685             sum =>'name' Create a routine with this name in the callers
1686             namespace to create new symbols. The default is
1687             'sum'.
1688             END
1689             }
1690            
1691            
1692             =head2 Operators
1693            
1694            
1695             =head3 Operator Overloads
1696            
1697             Overload Perl operators. Beware the low priority of B<^>.
1698            
1699             =cut
1700            
1701            
1702             use overload
1703 45         2522 '+' =>\&add3,
1704             '-' =>\&negate3,
1705             '*' =>\&multiply3,
1706             '/' =>\÷3,
1707             '**' =>\&power3,
1708             '==' =>\&equals3,
1709             '!=' =>\&nequal3,
1710             'eq' =>\&negate3,
1711             '>' =>\&solve3,
1712             '<=>' =>\&tequals3,
1713             'sqrt' =>\&sqrt3,
1714             'exp' =>\&exp3,
1715             'log' =>\&log3,
1716             'tan' =>\&tan3,
1717             'sin' =>\&sin3,
1718             'cos' =>\&cos3,
1719             '""' =>\&print3,
1720             '^' =>\&dot3, # Beware the low priority of this operator
1721             '~' =>\&conjugate3,
1722             'x' =>\&cross3,
1723             'abs' =>\&modulus3,
1724             '!' =>\&unit3,
1725 45     45   690 fallback=>1;
  45         108  
1726            
1727            
1728             =head3 add3
1729            
1730             Add operator.
1731            
1732             =cut
1733            
1734            
1735             sub add3
1736 185     185 1 592 {my ($a, $b) = @_;
1737 185 100       662 return simplify($a) unless defined($b); # += : simplify()
1738 175 100       753 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1739 175 50 33     1404 $a->{z} and $b->{z} or die "Add using unfinalized sums";
1740 175         554 $a->add($b);
1741             }
1742            
1743            
1744             =head3 negate3
1745            
1746             Negate operator. Used in combination with the L operator to
1747             perform subtraction.
1748            
1749             =cut
1750            
1751            
1752             sub negate3
1753 200     200 1 807 {my ($a, $b, $c) = @_;
1754            
1755 200 100       645 if (defined($b))
  200 50       1021  
1756 0 0       0 {$b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1757 200 50 33     1393 $a->{z} and $b->{z} or die "Negate using unfinalized sums";
1758 200 100       745 return $b->subtract($a) if $c;
1759 123 50       690 return $a->subtract($b) unless $c;
1760             }
1761             else
1762             {$a->{z} or die "Negate single unfinalized terms";
1763 0         0 return $a->negate;
1764             }
1765             }
1766            
1767            
1768             =head3 multiply3
1769            
1770             Multiply operator.
1771            
1772             =cut
1773            
1774            
1775             sub multiply3
1776 246     246 1 711 {my ($a, $b) = @_;
1777 246 100       1344 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1778 246 50 33     1641 $a->{z} and $b->{z} or die "Multiply using unfinalized sums";
1779 246         741 $a->multiply($b);
1780             }
1781            
1782            
1783             =head3 divide3
1784            
1785             Divide operator.
1786            
1787             =cut
1788            
1789            
1790             sub divide3
1791 146     146 1 407 {my ($a, $b, $c) = @_;
1792 146 100       857 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1793 146 50 33     1042 $a->{z} and $b->{z} or die "Divide using unfinalized sums";
1794 146 100       415 return $b->divide($a) if $c;
1795 128 50       628 return $a->divide($b) unless $c;
1796             }
1797            
1798            
1799             =head3 power3
1800            
1801             Power operator.
1802            
1803             =cut
1804            
1805            
1806             sub power3
1807 145     145 1 1216 {my ($a, $b) = @_;
1808 145 50       3219 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1809 145 50 33     1473 $a->{z} and $b->{z} or die "Power using unfinalized sums";
1810 145         516 $a->power($b);
1811             }
1812            
1813            
1814             =head3 equals3
1815            
1816             Equals operator.
1817            
1818             =cut
1819            
1820            
1821             sub equals3
1822 243     243 1 550 {my ($a, $b) = @_;
1823 243 100       1042 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1824 243 50 33     1804 $a->{z} and $b->{z} or die "Equals using unfinalized sums";
1825            
1826 243 100       2098 return 1 if $a->{id} == $b->{id}; # Fast equals
1827            
1828 52         188 my $c = $a->subtract($b);
1829            
1830 52 100       447 return 1 if $c->isZero()->{id} == $zero->{id};
1831 15         241 return 0;
1832             }
1833            
1834            
1835             =head3 nequal3
1836            
1837             Not equal operator.
1838            
1839             =cut
1840            
1841            
1842             sub nequal3
1843 9     9 1 24 {my ($a, $b) = @_;
1844 9         39 !equals3($a, $b);
1845             }
1846            
1847            
1848             =head3 tequals
1849            
1850             Evaluate the expression on the left hand side, stringify it, then
1851             compare it for string equality with the string on the right hand side.
1852             This operator is useful for making examples written with Test::Simple
1853             more readable.
1854            
1855             =cut
1856            
1857            
1858             sub tequals3
1859 27     27 0 74 {my ($a, $b) = @_;
1860            
1861 27 100       100 return 1 if "$a" eq $b;
1862            
1863 3         13 my $z = simplify($a);
1864            
1865 2         8 "$z" eq "$b";
1866             }
1867            
1868            
1869             =head3 solve3
1870            
1871             Solve operator.
1872            
1873             =cut
1874            
1875            
1876             sub solve3
1877 7     7 1 24 {my ($a, $b) = @_;
1878 7 50       31 $a->{z} or die "Solve using unfinalized sum";
1879             # $b =~ /^[a-z]+$/i or croak "Bad variable $b to solve for";
1880 7         29 solve($a, $b);
1881             }
1882            
1883            
1884             =head3 print3
1885            
1886             Print operator.
1887            
1888             =cut
1889            
1890            
1891             sub print3
1892 120796     120796 1 154068 {my ($a) = @_;
1893 120796 50       276855 $a->{z} or die "Print of unfinalized sum";
1894 120796         233576 $a->print();
1895             }
1896            
1897            
1898             =head3 sqrt3
1899            
1900             Sqrt operator.
1901            
1902             =cut
1903            
1904            
1905             sub sqrt3
1906 23     23 1 64 {my ($a) = @_;
1907 23 50       92 $a->{z} or die "Sqrt of unfinalized sum";
1908 23         90 $a->Sqrt();
1909             }
1910            
1911            
1912             =head3 exp3
1913            
1914             Exp operator.
1915            
1916             =cut
1917            
1918            
1919             sub exp3
1920 22     22 1 57 {my ($a) = @_;
1921 22 50       68 $a->{z} or die "Exp of unfinalized sum";
1922 22         71 $a->Exp();
1923             }
1924            
1925            
1926             =head3 sin3
1927            
1928             Sine operator.
1929            
1930             =cut
1931            
1932            
1933             sub sin3
1934 87     87 1 261 {my ($a) = @_;
1935 87 50       277 $a->{z} or die "Sin of unfinalized sum";
1936 87         281 $a->Sin();
1937             }
1938            
1939            
1940             =head3 cos3
1941            
1942             Cosine operator.
1943            
1944             =cut
1945            
1946            
1947             sub cos3
1948 92     92 1 182 {my ($a) = @_;
1949 92 50       286 $a->{z} or die "Cos of unfinalized sum";
1950 92         256 $a->Cos();
1951             }
1952            
1953            
1954             =head3 tan3
1955            
1956             Tan operator.
1957            
1958             =cut
1959            
1960            
1961             sub tan3
1962 0     0 1 0 {my ($a) = @_;
1963 0 0       0 $a->{z} or die "Tan of unfinalized sum";
1964 0         0 $a->tan();
1965             }
1966            
1967            
1968             =head3 log3
1969            
1970             Log operator.
1971            
1972             =cut
1973            
1974            
1975             sub log3
1976 1     1 1 9 {my ($a) = @_;
1977 1 50       6 $a->{z} or die "Log of unfinalized sum";
1978 1         5 $a->Log();
1979             }
1980            
1981            
1982             =head3 dot3
1983            
1984             Dot Product operator.
1985            
1986             =cut
1987            
1988            
1989             sub dot3
1990 10     10 1 34 {my ($a, $b, $c) = @_;
1991 10 100       39 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
1992 10 50 33     74 $a->{z} and $b->{z} or die "Dot of unfinalized sum";
1993 10         37 dot($a, $b);
1994             }
1995            
1996            
1997             =head3 cross3
1998            
1999             Cross operator.
2000            
2001             =cut
2002            
2003            
2004             sub cross3
2005 7     7 1 17 {my ($a, $b, $c) = @_;
2006 7 100       85 $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
2007 7 50 33     46 $a->{z} and $b->{z} or die "Cross of unfinalized sum";
2008 7         21 cross($a, $b);
2009             }
2010            
2011            
2012             =head3 unit3
2013            
2014             Unit operator.
2015            
2016             =cut
2017            
2018            
2019             sub unit3
2020 9     9 1 30 {my ($a, $b, $c) = @_;
2021 9 50       34 $a->{z} or die "Unit of unfinalized sum";
2022 9         28 unit($a);
2023             }
2024            
2025            
2026             =head3 modulus3
2027            
2028             Modulus operator.
2029            
2030             =cut
2031            
2032            
2033             sub modulus3
2034 18     18 1 63 {my ($a, $b, $c) = @_;
2035 18 50       84 $a->{z} or die "Modulus of unfinalized sum";
2036 18         67 modulus($a);
2037             }
2038            
2039            
2040             =head3 conjugate3
2041            
2042             Conjugate.
2043            
2044             =cut
2045            
2046            
2047             sub conjugate3
2048 10     10 1 27 {my ($a, $b, $c) = @_;
2049 10 50       28 $a->{z} or die "Conjugate of unfinalized sum";
2050 10         35 conjugate($a);
2051             }
2052            
2053             #________________________________________________________________________
2054             # Package installed successfully
2055             #________________________________________________________________________
2056            
2057             1;
2058            
2059             __DATA__