PageRenderTime 58ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/pod/sum.pod

https://github.com/gitpan/Math-Algebra-Symbols
Perl | 3479 lines | 1929 code | 1314 blank | 236 comment | 301 complexity | ff1aad1e807c569e7f25865d754e2860 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. #!perl -w
  2. =head1 Sums
  3. Symbolic Algebra using Pure Perl: sums.
  4. See user manual L</NAME>.
  5. Operations on sums of terms.
  6. PhilipRBrenan@yahoo.com, 2004, Perl License.
  7. package Math::Algebra::Symbols::Sum;
  8. $VERSION=1.21;
  9. use Math::Algebra::Symbols::Term;
  10. use IO::Handle;
  11. use Carp;
  12. #HashUtil use Hash::Util qw(lock_hash);
  13. use Scalar::Util qw(weaken);
  14. =head2 Constructors
  15. =head3 new
  16. Constructor
  17. sub new
  18. {bless {t=>{}};
  19. }
  20. =head3 newFromString
  21. New from String
  22. sub newFromString($)
  23. {my ($a) = @_;
  24. return $zero unless $a;
  25. $a .='+';
  26. my @a = $a =~ /(.+?)[\+\-]/g;
  27. my @t = map {term($_)} @a;
  28. sigma(@t);
  29. }
  30. =head3 n
  31. New from Strings
  32. sub n(@)
  33. {return $zero unless @_;
  34. my @a = map {newFromString($_)} @_;
  35. return @a if wantarray;
  36. $a[0];
  37. }
  38. =head3 sigma
  39. Create a sum from a list of terms.
  40. sub sigma(@)
  41. {return $zero unless scalar(@_);
  42. my $z = new();
  43. for my $t(@_)
  44. {my $s = $t->signature;
  45. if (exists($z->{t}{$s}))
  46. {my $a = $z->{t}{$s}->add($t);
  47. if ($a->c == 0)
  48. {delete $z->{t}{$s};
  49. }
  50. else
  51. {$z->{t}{$s} = $a;
  52. }
  53. }
  54. else
  55. {$z->{t}{$s} = $t
  56. }
  57. }
  58. $z->z;
  59. }
  60. =head3 makeInt
  61. Construct an integer
  62. sub makeInt($)
  63. {sigma(term()->one->clone->c(shift())->z)
  64. }
  65. =head2 Methods
  66. =head3 isSum
  67. Confirm type
  68. sub isSum($) {1};
  69. =head3 t
  70. Get list of terms from existing sum
  71. sub t($)
  72. {my ($a) = @_;
  73. (map {$a->{t}{$_}} sort(keys(%{$a->{t}})));
  74. }
  75. =head3 count
  76. Count terms in sum
  77. sub count($)
  78. {my ($a) = @_;
  79. scalar(keys(%{$a->{t}}));
  80. }
  81. =head3 st
  82. Get the single term from a sum containing just one term
  83. sub st($)
  84. {my ($a) = @_;
  85. return (values(%{$a->{t}}))[0] if scalar(keys(%{$a->{t}})) == 1;
  86. undef;
  87. }
  88. =head3 negate
  89. Multiply each term in a sum by -1
  90. sub negate($)
  91. {my ($s) = @_;
  92. my @t;
  93. for my $t($s->t)
  94. {push @t, $t->clone->timesInt(-1)->z;
  95. }
  96. sigma(@t);
  97. }
  98. =head3 add
  99. Add two sums together to make a new sum
  100. sub add($$)
  101. {my ($a, $b) = @_;
  102. sigma($a->t, $b->t);
  103. }
  104. =head3 subtract
  105. Subtract one sum from another
  106. sub subtract($$)
  107. {my ($a, $b) = @_;
  108. return $b->negate if $a->{id} == $zero->{id};
  109. $a->add($b->negate);
  110. }
  111. =head3 Conditional Multiply
  112. Multiply two sums if both sums are defined, otherwise return
  113. the defined sum. Assumes that at least one sum is defined.
  114. sub multiplyC($$)
  115. {my ($a, $b) = @_;
  116. return $a unless defined($b);
  117. return $b unless defined($a);
  118. $a->multiply($b);
  119. }
  120. =head3 multiply
  121. Multiply two sums together
  122. my %M; # Memoize multiplication
  123. sub multiply($$)
  124. {my ($A, $B) = @_;
  125. my $m = $M{$A->{id}}{$B->{id}}; return $m if defined($m);
  126. return $A if $A->{id} == $zero->{id} or $B->{id} == $one->{id};
  127. return $B if $B->{id} == $zero->{id} or $A->{id} == $one->{id};
  128. my @t;
  129. # Check for divides that match multiplier
  130. my @a = $A->t;
  131. for my $a(@a)
  132. {my $d = $a->Divide;
  133. next unless $d;
  134. if ($d->{id} == $B->{id})
  135. {push @t, $a->removeDivide;
  136. $a = undef;
  137. }
  138. }
  139. my @b = $B->t;
  140. for my $b(@b)
  141. {my $d = $b->Divide;
  142. next unless $d;
  143. if ($d->{id} == $A->{id})
  144. {push @t, $b->removeDivide;
  145. $b = undef;
  146. }
  147. }
  148. # Simple multiply
  149. for my $aa(@a)
  150. {next unless $aa;
  151. for my $bb(@b)
  152. {next unless $bb;
  153. my $m = $aa->multiply($bb);
  154. push (@t, $m), next if $m;
  155. # Complicated multiply
  156. my %a = $aa->split; my %b = $bb->split;
  157. my $a = $a{t}; my $b = $b{t};
  158. # Sqrt
  159. my $s = 0;
  160. $s = $a{s} if $a{s} and $b{s} and $a{s}->{id} == $b{s}->{id}; # Equal sqrts
  161. $a->Sqrt(multiplyC($a{s}, $b{s})) unless $s;
  162. # Divide
  163. $a->Divide(multiplyC($a{d}, $b{d})) if $a{d} or $b{d};
  164. # Exp
  165. $a->Exp($a{e} ? $a{e} : $b{e}) if $a{e} xor $b{e};
  166. my $e;
  167. if ($a{e} and $b{e})
  168. {my $s = $a{e}->add($b{e});
  169. $e = $s->st; # Check for single term
  170. $e = $e->exp2 if defined($e); # Simplify single term if possible
  171. $a->Exp($s) unless defined($e); # Reinstate Exp as sum of terms if no simplification possible
  172. }
  173. # Log
  174. $a->Log($a{l} ? $a{l} : $b{l}) if $a{l} xor $b{l};
  175. die "Cannot multiply logs yet" if $a{l} and $b{l};
  176. # Combine results
  177. $a = $a->z;
  178. $b = $b->z;
  179. $a = $a->multiply($b);
  180. $a = $a->multiply($e) if defined($e);
  181. $a or die "Bad multiply";
  182. push @t, $a unless $s;
  183. push @t, sigma($a)->multiply($s)->t if $s;
  184. }
  185. }
  186. # Result
  187. my $C = sigma(@t);
  188. $M{$A->{id}}{$B->{id}} = $C;
  189. $C;
  190. }
  191. =head3 divide
  192. Divide one sum by another
  193. sub divide($$)
  194. {my ($A, $B) = @_;
  195. # Obvious cases
  196. $B->{id} == $zero->{id} and croak "Cannot divide by zero";
  197. return $zero if $A->{id} == $zero->{id};
  198. return $A if $B->{id} == $one->{id};
  199. return $A->negate if $B->{id} == $mOne->{id};
  200. # Divide term by term
  201. my $a = $A->st; my $b = $B->st;
  202. if (defined($a) and defined($b))
  203. {my $c = $a->divide2($b);
  204. return sigma($c) if $c;
  205. }
  206. # Divide sum by term
  207. elsif ($b)
  208. {ST: for(1..1)
  209. {my @t;
  210. for my $t($A->t)
  211. {my $c = $t->divide2($b);
  212. last ST unless $c;
  213. push @t, $c;
  214. }
  215. return sigma(@t);
  216. }
  217. }
  218. # Divide sum by sum
  219. my @t;
  220. for my $aa($A->t)
  221. {my $a = $aa->clone;
  222. my $d = $a->Divide;
  223. $a->Divide($d->multiply($B)) if $d;
  224. $a->Divide($B) unless $d;
  225. push @t, $a->z;
  226. }
  227. # Result
  228. sigma(@t);
  229. }
  230. =head3 sub
  231. Substitute a sum for a variable.
  232. sub sub($@)
  233. {my $E = shift();
  234. my @R = @_;
  235. # Each replacement
  236. for(;@R > 0;)
  237. {my $s = shift @R; # Replace this variable
  238. my $w = shift @R; # With this expression
  239. my $Z = $zero;
  240. $s =~ /^[a-z]+$/i or croak "Can only substitute an expression for a variable, not $s";
  241. $w = newFromString($w) unless ref($w);
  242. $w->isSum;
  243. # Each term of the sum comprising the replacement expression.
  244. for my $t($E->t)
  245. {my $n = $t->vp($s);
  246. my %t = $t->split;
  247. my $S = sigma($t{t}->vp($s, 0)->z); # Remove substitution variable
  248. $S = $S->multiply(($t{s}->sub(@_))->Sqrt) if defined($t{s});
  249. $S = $S->divide ($t{d}->sub(@_)) if defined($t{d});
  250. $S = $S->multiply(($t{e}->sub(@_))->Exp) if defined($t{e});
  251. $S = $S->multiply(($t{l}->sub(@_))->Log) if defined($t{l});
  252. $S = $S->multiply($w->power(makeInt($n))) if $n;
  253. $Z = $Z->add($S);
  254. }
  255. $E = $Z;
  256. }
  257. # Result
  258. $E;
  259. }
  260. =head3 isEqual
  261. Check whether one sum is equal to another after multiplying out all
  262. divides and divisors.
  263. sub isEqual($)
  264. {my ($C) = @_;
  265. # Until there are no more divides
  266. for(;;)
  267. {my (%c, $D, $N); $N = 0;
  268. # Most frequent divisor
  269. for my $t($C->t)
  270. {my $d = $t->Divide;
  271. next unless $d;
  272. my $s = $d->getSignature;
  273. if (++$c{$s} > $N)
  274. {$N = $c{$s};
  275. $D = $d;
  276. }
  277. }
  278. last unless $N;
  279. $C = $C->multiply($D);
  280. }
  281. # Until there are no more negative powers
  282. for(;;)
  283. {my %v;
  284. for my $t($C->t)
  285. {for my $v($t->v)
  286. {my $p = $t->vp($v);
  287. next unless $p < 0;
  288. $p = -$p;
  289. $v{$v} = $p if !defined($v{$v}) or $v{$v} < $p;
  290. }
  291. }
  292. last unless scalar(keys(%v));
  293. my $m = term()->one->clone;
  294. $m->vp($_, $v{$_}) for keys(%v);
  295. my $M = sigma($m->z);
  296. $C = $C->multiply($M);
  297. }
  298. # Result
  299. $C;
  300. }
  301. =head3 normalizeSqrts
  302. Normalize sqrts in a sum.
  303. This routine needs fixing.
  304. It should simplify square roots.
  305. sub normalizeSqrts($)
  306. {my ($s) = @_;
  307. return $s;
  308. my (@t, @s);
  309. # Find terms with single simple sqrts that can be normalized.
  310. for my $t($s->t)
  311. {push @t, $t;
  312. my $S = $t->Sqrt; next unless $S; # Check for sqrt
  313. my $St = $S->st; next unless $St; # Check for single term sqrt
  314. my %T = $St->split; # Split single term sqrt
  315. next if $T{s} or $T{d} or $T{e} or $T{l};
  316. pop @t;
  317. push @s, {t=>$t, s=>$T{t}->z}; # Sqrt with simple single term
  318. }
  319. # Already normalized unless there are several such terms
  320. return $s unless scalar(@s) > 1;
  321. # Remove divisor for each normalized term
  322. for my $r(@s)
  323. {my $d = $r->{t}->d; next unless $d > 1;
  324. for my $s(@s)
  325. {$s->{t} = $s->{t}->clone->divideInt($d) ->z;
  326. $s->{s} = $s->{s}->clone->timesInt ($d*$d)->z;
  327. }
  328. }
  329. # Eliminate duplicate squared factors
  330. for my $s(@s)
  331. {my $F = factorize($s->{s}->c);
  332. my $p = 1;
  333. for my $f(keys(%$F))
  334. {$p *= $f**(int($F->{$f}/2)) if $F->{$f} > 1;
  335. }
  336. $s->{t} = $s->{t}->clone->timesInt ($p) ->z;
  337. $s->{s} = $s->{s}->clone->divideInt($p*$p)->z;
  338. $DB::single = 1;
  339. if ($s->{s}->isOne)
  340. {
  341. push @t, $s->{t}->removeSqrt;
  342. }
  343. else
  344. {
  345. push @t, $s->{t}->clone->Sqrt($s->{$s})->z;
  346. }
  347. }
  348. # Result
  349. sigma(@t);
  350. }
  351. =head3 isEqualSqrt
  352. Check whether one sum is equal to another after multiplying out sqrts.
  353. sub isEqualSqrt($)
  354. {my ($C) = @_;
  355. #_______________________________________________________________________
  356. # Each sqrt
  357. #_______________________________________________________________________
  358. for(1..99)
  359. {$C = $C->normalizeSqrts;
  360. my @s = grep { defined($_->Sqrt)} $C->t;
  361. my @n = grep {!defined($_->Sqrt)} $C->t;
  362. last unless scalar(@s) > 0;
  363. #_______________________________________________________________________
  364. # Partition by square roots.
  365. #_______________________________________________________________________
  366. my %S = ();
  367. for my $t(@s)
  368. {my $s = $t->Sqrt;
  369. my $S = $s->signature;
  370. push @{$S{$S}}, $t;
  371. }
  372. #_______________________________________________________________________
  373. # Square each partitions, as required by the formulae below.
  374. #_______________________________________________________________________
  375. my @t;
  376. push @t, sigma(@n)->power($two) if scalar(@n); # Non sqrt partition
  377. for my $s(keys(%S))
  378. {push @t, sigma(@{$S{$s}})->power($two); # Sqrt partition
  379. }
  380. #_______________________________________________________________________
  381. # I can multiply out upto 4 square roots using the formulae below.
  382. # There are formula to multiply out more than 4 sqrts, but they are big.
  383. # These formulae are obtained by squaring out and rearranging:
  384. # sqrt(a)+sqrt(b)+sqrt(c)+sqrt(d) == 0 until no sqrts remain, and
  385. # then matching terms to produce optimal execution.
  386. # This remarkable result was obtained with the help of this package:
  387. # demonstrating its utility in optimizing complex calculations written
  388. # in Perl: which in of itself cannot optimize broadly.
  389. #_______________________________________________________________________
  390. my $ns = scalar(@t);
  391. $ns < 5 or die "There are $ns square roots present. I can handle less than 5";
  392. my ($a, $b, $c, $d) = @t;
  393. if ($ns == 1)
  394. {$C = $a;
  395. }
  396. elsif ($ns == 2)
  397. {$C = $a-$b;
  398. }
  399. elsif ($ns == 3)
  400. {$C = -$a**2+2*$a*$b-$b**2+2*$c*$a+2*$c*$b-$c**2;
  401. }
  402. elsif ($ns == 4)
  403. {my $a2 = $a * $a;
  404. my $a3 = $a2 * $a;
  405. my $a4 = $a3 * $a;
  406. my $b2 = $b * $b;
  407. my $b3 = $b2 * $b;
  408. my $b4 = $b3 * $b;
  409. my $c2 = $c * $c;
  410. my $c3 = $c2 * $c;
  411. my $c4 = $c3 * $c;
  412. my $d2 = $d * $d;
  413. my $d3 = $d2 * $d;
  414. my $d4 = $d3 * $d;
  415. my $bpd = $b + $d;
  416. my $bpc = $b + $c;
  417. my $cpd = $c + $d;
  418. $C =
  419. - ($a4 + $b4 + $c4 + $d4)
  420. + 4*(
  421. +$a3*($b+$cpd)+$b3*($a+$cpd)+$c3*($a+$bpd)+$d3*($a+$bpc)
  422. -$a2*($b *($cpd)+ $c*$d)
  423. -$a *($b2*($cpd)+$d2*($bpc))
  424. )
  425. - 6*($a2*$b2+($a2+$b2)*($c2+$d2)+$c2*$d2)
  426. - 4*$c*($b2*$d+$b*$d2)
  427. - 4*$c2*($a*($bpd)+$b*$d)
  428. +40*$c*$a*$b*$d
  429. ;
  430. }
  431. }
  432. #________________________________________________________________________
  433. # Test result
  434. #________________________________________________________________________
  435. # $C->isEqual($zero);
  436. $C;
  437. }
  438. =head3 isZero
  439. Transform a sum assuming that it is equal to zero
  440. sub isZero($)
  441. {my ($C) = @_;
  442. $C->isEqualSqrt->isEqual;
  443. }
  444. =head3 powerOfTwo
  445. Check that a number is a power of two
  446. sub powerof2($)
  447. {my ($N) = @_;
  448. my $n = 0;
  449. return undef unless $N > 0;
  450. for (;;)
  451. {return $n if $N == 1;
  452. return undef unless $N % 2 == 0;
  453. ++$n; $N /= 2;
  454. }
  455. }
  456. =head3 solve
  457. Solve an equation known to be equal to zero for a specified variable.
  458. sub solve($$)
  459. {my ($A, @x) = @_;
  460. croak 'Need variable to solve for' unless scalar(@x) > 0;
  461. @x = @{$x[0]} if scalar(@x) == 1 and ref($x[0]) eq 'ARRAY'; # Array of variables supplied
  462. my %x;
  463. for my $x(@x)
  464. {if (!ref $x)
  465. {$x =~ /^[a-z]+$/i or croak "Cannot solve for: $x, not a variable name";
  466. }
  467. elsif (ref $x eq __PACKAGE__)
  468. {my $t = $x->st; $t or die "Cannot solve for multiple terms";
  469. my @b = $t->v; scalar(@b) == 1 or die "Can only solve for one variable";
  470. my $p = $t->vp($b[0]); $p == 1 or die "Can only solve by variable to power 1";
  471. $x = $b[0];
  472. }
  473. else
  474. {die "$x is not a variable name";
  475. }
  476. $x{$x} = 1;
  477. }
  478. my $x = $x[0];
  479. $B = $A->isZero; # Eliminate sqrts and negative powers
  480. # Strike all terms with free variables other than x: i.e. not x and not one of the named constants
  481. my @t = ();
  482. for my $t($B->t)
  483. {my @v = $t->v;
  484. push @t, $t;
  485. for my $v($t->v)
  486. {next if exists($x{$v});
  487. pop @t;
  488. last;
  489. }
  490. }
  491. my $C = sigma(@t);
  492. # Find highest and lowest power of x
  493. my $n = 0; my $N;
  494. for my $t($C->t)
  495. {my $p = $t->vp($x);
  496. $n = $p if $p > $n;
  497. $N = $p if !defined($N) or $p < $N;
  498. }
  499. my $D = $C;
  500. $D = $D->multiply(sigma(term()->one->clone->vp($x, -$N)->z)) if $N;
  501. $n -= $N if $N;
  502. # Find number of terms in x
  503. my $c = 0;
  504. for my $t($D->t)
  505. {++$c if $t->vp($x) > 0;
  506. }
  507. $n == 0 and croak "Equation not dependant on $x, so cannot solve for $x";
  508. $n > 4 and $c > 1 and croak "Unable to solve polynomial or power $n > 4 in $x (Galois)";
  509. ($n > 2 and $c > 1) and die "Need solver for polynomial of degree $n in $x";
  510. # Solve linear equation
  511. if ($n == 1 or $c == 1)
  512. {my (@c, @v);
  513. for my $t($D->t)
  514. {push(@c, $t), next if $t->vp($x) == 0; # Constants
  515. push @v, $t; # Powers of x
  516. }
  517. my $d = sigma(@v)->multiply(sigma(term()->one->clone->vp($x, -$n)->negate->z));
  518. $D = sigma(@c)->divide($d);
  519. return $D if $n == 1;
  520. my $p = powerof2($n);
  521. $p or croak "Fractional power 1/$n of $x unconstructable by sqrt";
  522. $D = $D->Sqrt for(1..$p);
  523. return $D;
  524. }
  525. # Solve quadratic equation
  526. if ($n == 2)
  527. {my @c = ($one, $one, $one);
  528. $c[$_->vp($x)] = $_ for $D->t;
  529. $_ = sigma($_->clone->vp($x, 0)->z) for (@c);
  530. my ($c, $b, $a) = @c;
  531. return
  532. [ (-$b->add (($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a)),
  533. (-$b->subtract(($b->power($two)->subtract($four->multiply($a)->multiply($c)))->Sqrt))->divide($two->multiply($a))
  534. ]
  535. }
  536. # Check that it works
  537. # my $yy = $e->sub($x=>$xx);
  538. # $yy == 0 or die "Proposed solution \$$x=$xx does not zero equation $e";
  539. # $xx;
  540. }
  541. =head3 power
  542. Raise a sum to an integer power or an integer/2 power.
  543. sub power($$)
  544. {my ($a, $b) = @_;
  545. return $one if $b->{id} == $zero->{id};
  546. return $a->multiply($a) if $b->{id} == $two->{id};
  547. return $a if $b->{id} == $one->{id};
  548. return $one->divide($a) if $b->{id} == $mOne->{id};
  549. return $a->sqrt if $b->{id} == $half->{id};
  550. return $one->divide($a->sqrt) if $b->{id} == $mHalf->{id};
  551. my $T = $b->st;
  552. $T or croak "Power by expression too complicated";
  553. my %t = $T->split;
  554. croak "Power by term too complicated" if $t{s} or $t{d} or $t{e} or $t{l};
  555. my $t = $t{t};
  556. $t->i == 0 or croak "Complex power not allowed yet";
  557. my ($p, $d) = ($t->c, $t->d);
  558. $d == 1 or $d == 2 or croak "Fractional power other than /2 not allowed yet";
  559. $a = $a->sqrt if $d == 2;
  560. return $one->divide($a)->power(sigma(term()->c($p)->z)) if $p < 0;
  561. $p = abs($p);
  562. my $r = $a; $r = $r->multiply($a) for (2..$p);
  563. $r;
  564. }
  565. =head3 d
  566. Differentiate.
  567. sub d($;$);
  568. sub d($;$)
  569. {my $c = $_[0]; # Differentiate this sum
  570. my $b = $_[1]; # With this variable
  571. #_______________________________________________________________________
  572. # Get differentrix. Assume 'x', 'y', 'z' or 't' if appropriate.
  573. #_______________________________________________________________________
  574. if (defined($b))
  575. {if (!ref $b)
  576. {$b =~ /^[a-z]+$/i or croak "Cannot differentiate by $b";
  577. }
  578. elsif (ref $b eq __PACKAGE__)
  579. {my $t = $b->st; $t or die "Cannot differentiate by multiple terms";
  580. my @b = $t->v; scalar(@b) == 1 or die "Can only differentiate by one variable";
  581. my $p = $t->vp($b[0]); $p == 1 or die "Can only differentiate by variable to power 1";
  582. $b = $b[0];
  583. }
  584. else
  585. {die "Cannot differentiate by $b";
  586. }
  587. }
  588. else
  589. {my %b;
  590. for my $t($c->t)
  591. {my %b; $b{$_}++ for ($t->v);
  592. }
  593. my $i = 0; my $n = scalar(keys(%b));
  594. ++$i, $b = 'x' if $n == 0; # Constant expression anyway
  595. ++$i, $b = (%b)[0] if $n == 1;
  596. for my $v(qw(t x y z))
  597. {++$i, $b = 't' if $n > 1 and exists($b{$v});
  598. }
  599. $i == 1 or croak "Please specify a single variable to differentiate by";
  600. }
  601. #_______________________________________________________________________
  602. # Each term
  603. #_______________________________________________________________________
  604. my @t = ();
  605. for my $t($c->t)
  606. {my %V = $t->split;
  607. my $T = $V{t}->z->clone->z;
  608. my ($S, $D, $E, $L) = @V{qw(s d e l)};
  609. my $s = $S->d($b) if $S;
  610. my $d = $D->d($b) if $D;
  611. my $e = $E->d($b) if $E;
  612. my $l = $L->d($b) if $L;
  613. #_______________________________________________________________________
  614. # Differentiate Variables: A*v**n->d == A*n*v**(n-1)
  615. #_______________________________________________________________________
  616. {my $v = $T->clone;
  617. my $p = $v->vp($b);
  618. if ($p != 0)
  619. {$v->timesInt($p)->vp($b, $p-1);
  620. $v->Sqrt ($S) if $S;
  621. $v->Divide($D) if $D;
  622. $v->Exp ($E) if $E;
  623. $v->Log ($L) if $L;
  624. push @t, $v->z;
  625. }
  626. }
  627. #_______________________________________________________________________
  628. # Differentiate Sqrt: A*sqrt(F(x))->d == 1/2*A*f(x)/sqrt(F(x))
  629. #_______________________________________________________________________
  630. if ($S)
  631. {my $v = $T->clone->divideInt(2);
  632. $v->Divide($D) if $D;
  633. $v->Exp ($E) if $E;
  634. $v->Log ($L) if $L;
  635. push @t, sigma($v->z)->multiply($s)->divide($S->Sqrt)->t;
  636. }
  637. #_______________________________________________________________________
  638. # Differentiate Divide: A/F(x)->d == -A*f(x)/F(x)**2
  639. #_______________________________________________________________________
  640. if ($D)
  641. {my $v = $T->clone->negate;
  642. $v->Sqrt($S) if $S;
  643. $v->Exp ($E) if $E;
  644. $v->Log ($L) if $L;
  645. push @t, sigma($v->z)->multiply($d)->divide($D->multiply($D))->t;
  646. }
  647. #_______________________________________________________________________
  648. # Differentiate Exp: A*exp(F(x))->d == A*f(x)*exp(F(x))
  649. #_______________________________________________________________________
  650. if ($E)
  651. {my $v = $T->clone;
  652. $v->Sqrt ($S) if $S;
  653. $v->Divide($D) if $D;
  654. $v->Exp ($E);
  655. $v->Log ($L) if $L;
  656. push @t, sigma($v->z)->multiply($e)->t;
  657. }
  658. #_______________________________________________________________________
  659. # Differentiate Log: A*log(F(x))->d == A*f(x)/F(x)
  660. #_______________________________________________________________________
  661. if ($L)
  662. {my $v = $T->clone;
  663. $v->Sqrt ($S) if $S;
  664. $v->Divide($D) if $D;
  665. $v->Exp ($E) if $E;
  666. push @t, sigma($v->z)->multiply($l)->divide($L)->t;
  667. }
  668. }
  669. #_______________________________________________________________________
  670. # Result
  671. #_______________________________________________________________________
  672. sigma(@t);
  673. }
  674. =head3 simplify
  675. Simplify just before assignment.
  676. There is no general simplification algorithm. So try various methods and
  677. see if any simplifications occur. This is cheating really, because the
  678. examples will represent these specific transformations as general
  679. features which they are not. On the other hand, Mathematics is full of
  680. specifics so I suppose its not entirely unacceptable.
  681. Simplification cannot be done after every operation as it is
  682. inefficient, doing it as part of += ameliorates this inefficiency.
  683. Note: += only works as a synonym for simplify() if the left hand side is
  684. currently undefined. This can be enforced by using my() as in: my $z +=
  685. ($x**2+5x+6)/($x+2);
  686. sub simplify($)
  687. {my ($x) = @_;
  688. $x = polynomialDivision($x);
  689. $x = eigenValue($x);
  690. }
  691. #_______________________________________________________________________
  692. # Common factor: find the largest factor in one or more expressions
  693. #_______________________________________________________________________
  694. sub commonFactor(@)
  695. {return undef unless scalar(@_);
  696. return undef unless scalar(keys(%{$_[0]->{t}}));
  697. my $p = (values(%{$_[0]->{t}}))[0];
  698. my %v = %{$p->{v}}; # Variables
  699. my %s = $p->split;
  700. my ($s, $d, $e, $l) = @s{qw(s d e l)}; # Sub expressions
  701. my ($C, $D, $I) = ($p->c, $p->d, $p->i);
  702. my @t;
  703. for my $a(@_)
  704. {for my $b($a->t)
  705. {push @t, $b;
  706. }
  707. }
  708. for my $t(@t)
  709. {my %V = %v;
  710. %v = ();
  711. for my $v($t->v)
  712. {next unless $V{$v};
  713. my $p = $t->vp($v);
  714. $v{$v} = ($V{$v} < $p ? $V{$v} : $p);
  715. }
  716. my %S = $t->split;
  717. my ($S, $D, $E, $L) = @S{qw(s d e l)}; # Sub expressions
  718. $s = undef unless defined($s) and defined($S) and $S->id eq $s->id;
  719. $d = undef unless defined($d) and defined($D) and $D->id eq $d->id;
  720. $e = undef unless defined($e) and defined($E) and $E->id eq $e->id;
  721. $l = undef unless defined($l) and defined($L) and $L->id eq $l->id;
  722. $C = undef unless defined($C) and $C == $t->c;
  723. $D = undef unless defined($D) and $D == $t->d;
  724. $I = undef unless defined($I) and $I == $t->i;
  725. }
  726. my $r = term()->one->clone;
  727. $r->c($C) if defined($C);
  728. $r->d($D) if defined($D);
  729. $r->i($I) if defined($I);
  730. $r->vp($_, $v{$_}) for(keys(%v));
  731. $r->Sqrt ($s) if defined($s);
  732. $r->Divide($d) if defined($d);
  733. $r->Exp ($e) if defined($e);
  734. $r->Log ($l) if defined($l);
  735. sigma($r->z);
  736. }
  737. #_______________________________________________________________________
  738. # Find term of polynomial of highest degree.
  739. #_______________________________________________________________________
  740. sub polynomialTermOfHighestDegree($$)
  741. {my ($p, $v) = @_; # Polynomial, variable
  742. my $n = 0; # Current highest degree
  743. my $t; # Term with this degree
  744. for my $T($p->t)
  745. {my $N = $T->vp($v);
  746. if ($N > $n)
  747. {$n = $N;
  748. $t = $T;
  749. }
  750. }
  751. ($n, $t);
  752. }
  753. =head3 polynomialDivide
  754. Polynomial divide - divide one polynomial (a) by another (b) in variable v
  755. sub polynomialDivide($$$)
  756. {my ($p, $q, $v) = @_;
  757. my $r = zero()->clone()->z;
  758. for(;;)
  759. {my ($np, $mp) = $p->polynomialTermOfHighestDegree($v);
  760. my ($nq, $mq) = $q->polynomialTermOfHighestDegree($v);
  761. last unless $np >= $nq;
  762. my $pq = sigma($mp->divide2($mq));
  763. $r = $r->add($pq);
  764. $p = $p->subtract($q->multiply($pq));
  765. }
  766. return $r if $p->isZero()->{id} == $zero->{id};
  767. undef;
  768. }
  769. =head3 eigenValue
  770. Eigenvalue check
  771. sub eigenValue($)
  772. {my ($p) = @_;
  773. # Find divisors
  774. my %d;
  775. for my $t($p->t)
  776. {my $d = $t->Divide;
  777. next unless defined($d);
  778. $d{$d->id} = $d;
  779. }
  780. # Consolidate numerator and denominator
  781. my $P = $p ->clone()->z; $P = $P->multiply($d{$_}) for(keys(%d));
  782. my $Q = one()->clone()->z; $Q = $Q->multiply($d{$_}) for(keys(%d));
  783. # Check for P=nQ i.e. for eigenvalue
  784. my $cP = $P->commonFactor; my $dP = $P->divide($cP);
  785. my $cQ = $Q->commonFactor; my $dQ = $Q->divide($cQ);
  786. return $cP->divide($cQ) if $dP->id == $dQ->id;
  787. $p;
  788. }
  789. =head3 polynomialDivision
  790. Polynomial division.
  791. sub polynomialDivision($)
  792. {my ($p) = @_;
  793. # Find a plausible indeterminate
  794. my %v; # Possible indeterminates
  795. my $v; # Polynomial indeterminate
  796. my %D; # Divisors for each term
  797. # Each term
  798. for my $t($p->t)
  799. {my @v = $t->v;
  800. $v{$_}{$t->vp($_)} = 1 for(@v);
  801. my %V = $t->split;
  802. my ($S, $D, $E, $L) = @V{qw(s d e l)};
  803. return $p if defined($S) or defined($E) or defined($L);
  804. # Each divisor term
  805. if (defined($D))
  806. {for my $T($D->t)
  807. {my @v = $T->v;
  808. $v{$_}{$T->vp($_)} = 1 for(@v);
  809. my %V = $T->split;
  810. my ($S, $D, $E, $L) = @V{qw(s d e l)};
  811. return $p if defined($S) or defined($D) or defined($E) or defined($L);
  812. }
  813. $D{$D->id} = $D;
  814. }
  815. }
  816. # Consolidate numerator and denominator
  817. my $P = $p ->clone()->z; $P = $P->multiply($D{$_}) for(keys(%D));
  818. my $Q = one()->clone()->z; $Q = $Q->multiply($D{$_}) for(keys(%D));
  819. # Pick a possible indeterminate
  820. for(keys(%v))
  821. {delete $v{$_} if scalar(keys(%{$v{$_}})) == 1;
  822. }
  823. return $p unless scalar(keys(%v));
  824. $v = (keys(%v))[0];
  825. # Divide P by Q
  826. my $r;
  827. $r = $P->polynomialDivide($Q, $v); return $r if defined($r);
  828. $r = $Q->polynomialDivide($P, $v); return one()->divide($r) if defined($r);
  829. $p;
  830. }
  831. =head3 Sqrt
  832. Square root of a sum
  833. sub Sqrt($)
  834. {my ($x) = @_;
  835. my $s = $x->st;
  836. if (defined($s))
  837. {my $r = $s->sqrt2;
  838. return sigma($r) if defined($r);
  839. }
  840. sigma(term()->c(1)->Sqrt($x)->z);
  841. }
  842. =head3 Exp
  843. Exponential (B<e> raised to the power) of a sum
  844. sub Exp($)
  845. {my ($x) = @_;
  846. my $p = term()->one;
  847. my @r;
  848. for my $t($x->t)
  849. {my $r = $t->exp2;
  850. $p = $p->multiply($r) if $r;
  851. push @r, $t unless $r;
  852. }
  853. return sigma($p) if scalar(@r) == 0;
  854. return sigma($p->clone->Exp(sigma(@r))->z);
  855. }
  856. =head3 Log
  857. Log to base B<e> of a sum
  858. sub Log($)
  859. {my ($x) = @_;
  860. my $s = $x->st;
  861. if (defined($s))
  862. {my $r = $s->log2;
  863. return sigma($r) if defined($r);
  864. }
  865. sigma(term()->c(1)->Log($x)->z);
  866. }
  867. =head3 Sin
  868. Sine of a sum
  869. sub Sin($)
  870. {my ($x) = @_;
  871. my $s = $x->st;
  872. if (defined($s))
  873. {my $r = $s->sin2;
  874. return sigma($r) if defined($r);
  875. }
  876. my $a = $i->multiply($x);
  877. $i->multiply($half)->multiply($a->negate->Exp->subtract($a->Exp));
  878. }
  879. =head3 Cos
  880. Cosine of a sum
  881. sub Cos($)
  882. {my ($x) = @_;
  883. my $s = $x->st;
  884. if (defined($s))
  885. {my $r = $s->cos2;
  886. return sigma($r) if defined($r);
  887. }
  888. my $a = $i->multiply($x);
  889. $half->multiply($a->negate->Exp->add($a->Exp));
  890. }
  891. =head3 tan, Ssc, csc, cot
  892. Tan, sec, csc, cot of a sum
  893. sub tan($) {my ($x) = @_; $x->Sin()->divide($x->Cos())}
  894. sub sec($) {my ($x) = @_; $one ->divide($x->Cos())}
  895. sub csc($) {my ($x) = @_; $one ->divide($x->Sin())}
  896. sub cot($) {my ($x) = @_; $x->Cos()->divide($x->Sin())}
  897. =head3 sinh
  898. Hyperbolic sine of a sum
  899. sub sinh($)
  900. {my ($x) = @_;
  901. return $zero if $x->{id} == $zero->{id};
  902. my $n = $x->negate;
  903. sigma
  904. (term()->c( 1)->divideInt(2)->Exp($x)->z,
  905. term()->c(-1)->divideInt(2)->Exp($n)->z
  906. )
  907. }
  908. =head3 cosh
  909. Hyperbolic cosine of a sum
  910. sub cosh($)
  911. {my ($x) = @_;
  912. return $one if $x->{id} == $zero->{id};
  913. my $n = $x->negate;
  914. sigma
  915. (term()->c(1)->divideInt(2)->Exp($x)->z,
  916. term()->c(1)->divideInt(2)->Exp($n)->z
  917. )
  918. }
  919. =head3 Tanh, Sech, Csch, Coth
  920. Tanh, Sech, Csch, Coth of a sum
  921. sub tanh($) {my ($x) = @_; $x->sinh()->divide($x->cosh())}
  922. sub sech($) {my ($x) = @_; $one ->divide($x->cosh())}
  923. sub csch($) {my ($x) = @_; $one ->divide($x->sinh())}
  924. sub coth($) {my ($x) = @_; $x->cosh()->divide($x->sinh())}
  925. =head3 dot
  926. Dot - complex dot product of two complex sums
  927. sub dot($$)
  928. {my ($a, $b) = @_;
  929. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  930. $a->re->multiply($b->re)->add($a->im->multiply($b->im));
  931. }
  932. =head3 cross
  933. The area of the parallelogram formed by two complex sums
  934. sub cross($$)
  935. {my ($a, $b) = @_;
  936. $a->dot($a)->multiply($b->dot($b))->subtract($a->dot($b)->power($two))->Sqrt;
  937. }
  938. =head3 unit
  939. Intersection of a complex sum with the unit circle.
  940. sub unit($)
  941. {my ($a) = @_;
  942. my $b = $a->modulus;
  943. my $c = $a->divide($b);
  944. $a->divide($a->modulus);
  945. }
  946. =head3 re
  947. Real part of a complex sum
  948. sub re($)
  949. {my ($A) = @_;
  950. $A = newFromString("$A") unless ref($A) eq __PACKAGE__;
  951. my @r;
  952. for my $a($A->t)
  953. {next if $a->i == 1;
  954. push @r, $a;
  955. }
  956. sigma(@r);
  957. }
  958. =head3 im
  959. Imaginary part of a complex sum
  960. sub im($)
  961. {my ($A) = @_;
  962. $A = newFromString("$A") unless ref($A) eq __PACKAGE__;
  963. my @r;
  964. for my $a($A->t)
  965. {next if $a->i == 0;
  966. push @r, $a;
  967. }
  968. $mI->multiply(sigma(@r));
  969. }
  970. =head3 modulus
  971. Modulus of a complex sum
  972. sub modulus($)
  973. {my ($a) = @_;
  974. $a->re->power($two)->add($a->im->power($two))->Sqrt;
  975. }
  976. =head3 conjugate
  977. Conjugate of a complexs sum
  978. sub conjugate($)
  979. {my ($a) = @_;
  980. $a->re->subtract($a->im->multiply($i));
  981. }
  982. =head3 clone
  983. Clone
  984. sub clone($)
  985. {my ($t) = @_;
  986. $t->{z} or die "Attempt to clone unfinalized sum";
  987. my $c = bless {%$t};
  988. $c->{t} = {%{$t->{t}}};
  989. delete $c->{z};
  990. delete $c->{s};
  991. delete $c->{id};
  992. $c;
  993. }
  994. =head3 signature
  995. Signature of a sum: used to optimize add().
  996. # Fix the problem of adding different logs
  997. sub signature($)
  998. {my ($t) = @_;
  999. my $s = '';
  1000. for my $a($t->t)
  1001. {$s .= '+'. $a->print;
  1002. }
  1003. $s;
  1004. }
  1005. =head3 getSignature
  1006. Get the signature (see L</signature>) of a sum
  1007. sub getSignature($)
  1008. {my ($t) = @_;
  1009. exists $t->{z} ? $t->{z} : die "Attempt to get signature of unfinalized sum";
  1010. }
  1011. =head3 id
  1012. Get Id of sum: each sum has a unique identifying number.
  1013. sub id($)
  1014. {my ($t) = @_;
  1015. $t->{id} or die "Sum $t not yet finalized";
  1016. $t->{id};
  1017. }
  1018. =head3 zz
  1019. Check sum finalized. See: L</z>.
  1020. sub zz($)
  1021. {my ($t) = @_;
  1022. $t->{z} or die "Sum $t not yet finalized";
  1023. print $t->{z}, "\n";
  1024. $t;
  1025. }
  1026. =head3 z
  1027. Finalize creation of the sum: Once a sum has been finalized it becomes
  1028. read only.
  1029. my $lock = 0; # Hash locking
  1030. my $z = 0; # Term counter
  1031. my %z; # Terms finalized
  1032. sub z($)
  1033. {my ($t) = @_;
  1034. !exists($t->{z}) or die "Already finalized this term";
  1035. my $p = $t->print;
  1036. return $z{$p} if defined($z{$p});
  1037. $z{$p} = $t;
  1038. weaken($z{$p}); # Reduces memory usage.
  1039. $t->{s} = $p;
  1040. $t->{z} = $t->signature;
  1041. $t->{id} = ++$z;
  1042. #HashUtil lock_hash(%{$t->{v}}) if $lock;
  1043. #HashUtil lock_hash %$t if $lock;
  1044. $t;
  1045. }
  1046. #sub DESTROY($)
  1047. # {my ($t) = @_;
  1048. # delete $z{$t->{s}} if defined($t) and exists $t->{s};
  1049. # }
  1050. sub lockHashes()
  1051. {my ($l) = @_;
  1052. #HashUtil for my $t(values %z)
  1053. #HashUtil {lock_hash(%{$t->{v}});
  1054. #HashUtil lock_hash %$t;
  1055. #HashUtil }
  1056. $lock = 1;
  1057. }
  1058. =head3 print
  1059. Print sum
  1060. sub print($)
  1061. {my ($t) = @_;
  1062. return $t->{s} if defined($t->{s});
  1063. my $s = '';
  1064. for my $a($t->t)
  1065. {$s .= $a->print .'+';
  1066. }
  1067. chop($s) if $s;
  1068. $s =~ s/^\+//;
  1069. $s =~ s/\+\-/\-/g;
  1070. $s =~ s/\+1\*/\+/g; # change: +1* to +
  1071. $s =~ s/\*1\*/\*/g; # remove: *1* to *
  1072. $s =~ s/^1\*//g; # remove: 1* at start of expression
  1073. $s =~ s/^\-1\*/\-/g; # change: -1* at start of expression to -
  1074. $s =~ s/^0\+//g; # change: 0+ at start of expression to
  1075. $s =~ s/\+0$//; # remove: +0 at end of expression
  1076. $s =~ s#\(\+0\+#\(#g; # change: (+0+ to (
  1077. $s =~ s/\(\+/\(/g; # change: (+ to (
  1078. $s =~ s/\(1\*/\(/g; # change: (1* to (
  1079. $s =~ s/\(\-1\*/\(\-/g; # change: (-1* to (-
  1080. $s =~ s/([a-zA-Z0-9)])\-1\*/$1\-/g; # change: term-1* to term-
  1081. $s =~ s/\*(\$[a-zA-Z]+)\*\*\-1(?!\d)/\/$1/g; # change: *$y**-1 to /$y
  1082. $s =~ s/\*(\$[a-zA-Z]+)\*\*\-(\d+)/\/$1**$2/g; # change: *$y**-n to /$y**n
  1083. $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-1(?!\d)/1\/$1/g; # change: +-$y**-1 to +-1/$y
  1084. $s =~ s/([\+\-])(\$[a-zA-Z]+)\*\*\-(\d+)/${1}1\/$2**$3/g; # change: +-$y**-n to +-1/$y**n
  1085. $s = 0 if $s eq '';
  1086. $s;
  1087. }
  1088. =head3 constants
  1089. Useful constants
  1090. $zero = sigma(term('0')); sub zero() {$zero}
  1091. $one = sigma(term('1')); sub one() {$one}
  1092. $two = sigma(term('2')); sub two() {$two}
  1093. $four = sigma(term('4')); sub four() {$four}
  1094. $mOne = sigma(term('-1')); sub mOne() {$mOne}
  1095. $i = sigma(term('i')); sub i() {$i}
  1096. $mI = sigma(term('-i')); sub mI() {$mI}
  1097. $half = sigma(term('1/2')); sub half() {$half}
  1098. $mHalf = sigma(term('-1/2')); sub mHalf() {$mHalf}
  1099. $pi = sigma(term('pi')); sub pi() {$pi}
  1100. =head3 factorize
  1101. Factorize a number.
  1102. @primes = qw(
  1103. 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61
  1104. 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151
  1105. 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251
  1106. 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359
  1107. 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463
  1108. 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593
  1109. 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701
  1110. 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827
  1111. 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953
  1112. 967 971 977 983 991 997);
  1113. sub factorize($)
  1114. {my ($n) = @_;
  1115. my $f;
  1116. for my $p(@primes)
  1117. {for(;$n % $p == 0;)
  1118. {$f->{$p}++;
  1119. $n /= $p;
  1120. }
  1121. last unless $n > $p;
  1122. }
  1123. $f;
  1124. };
  1125. =head2 import
  1126. Export L</n> with either the default name B<sums>, or a name supplied by
  1127. the caller of this package.
  1128. sub import
  1129. {my %P = (program=>@_);
  1130. my %p; $p{lc()} = $P{$_} for(keys(%P));
  1131. #_______________________________________________________________________
  1132. # New sum constructor - export to calling package.
  1133. #_______________________________________________________________________
  1134. my $s = "package XXXX;\n". <<'END';
  1135. no warnings 'redefine';
  1136. sub NNNN
  1137. {return SSSSn(@_);
  1138. }
  1139. use warnings 'redefine';
  1140. END
  1141. #_______________________________________________________________________
  1142. # Export to calling package.
  1143. #_______________________________________________________________________
  1144. my $name = 'sum';
  1145. $name = $p{sum} if exists($p{sum});
  1146. my ($main) = caller();
  1147. my $pack = __PACKAGE__ . '::';
  1148. $s=~ s/XXXX/$main/g;
  1149. $s=~ s/NNNN/$name/g;
  1150. $s=~ s/SSSS/$pack/g;
  1151. eval($s);
  1152. #_______________________________________________________________________
  1153. # Check options supplied by user
  1154. #_______________________________________________________________________
  1155. delete @p{qw(program sum)};
  1156. croak "Unknown option(s): ". join(' ', keys(%p))."\n\n". <<'END' if keys(%p);
  1157. Valid options are:
  1158. sum =>'name' Create a routine with this name in the callers
  1159. namespace to create new symbols. The default is
  1160. 'sum'.
  1161. END
  1162. }
  1163. =head2 Operators
  1164. =head3 Operator Overloads
  1165. Overload Perl operators. Beware the low priority of B<^>.
  1166. use overload
  1167. '+' =>\&add3,
  1168. '-' =>\&negate3,
  1169. '*' =>\&multiply3,
  1170. '/' =>\&divide3,
  1171. '**' =>\&power3,
  1172. '==' =>\&equals3,
  1173. '!=' =>\&nequal3,
  1174. 'eq' =>\&negate3,
  1175. '>' =>\&solve3,
  1176. '<=>' =>\&tequals3,
  1177. 'sqrt' =>\&sqrt3,
  1178. 'exp' =>\&exp3,
  1179. 'log' =>\&log3,
  1180. 'tan' =>\&tan3,
  1181. 'sin' =>\&sin3,
  1182. 'cos' =>\&cos3,
  1183. '""' =>\&print3,
  1184. '^' =>\&dot3, # Beware the low priority of this operator
  1185. '~' =>\&conjugate3,
  1186. 'x' =>\&cross3,
  1187. 'abs' =>\&modulus3,
  1188. '!' =>\&unit3,
  1189. fallback=>1;
  1190. =head3 add3
  1191. Add operator.
  1192. sub add3
  1193. {my ($a, $b) = @_;
  1194. return simplify($a) unless defined($b); # += : simplify()
  1195. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1196. $a->{z} and $b->{z} or die "Add using unfinalized sums";
  1197. $a->add($b);
  1198. }
  1199. =head3 negate3
  1200. Negate operator. Used in combination with the L</add3> operator to
  1201. perform subtraction.
  1202. sub negate3
  1203. {my ($a, $b, $c) = @_;
  1204. if (defined($b))
  1205. {$b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1206. $a->{z} and $b->{z} or die "Negate using unfinalized sums";
  1207. return $b->subtract($a) if $c;
  1208. return $a->subtract($b) unless $c;
  1209. }
  1210. else
  1211. {$a->{z} or die "Negate single unfinalized terms";
  1212. return $a->negate;
  1213. }
  1214. }
  1215. =head3 multiply3
  1216. Multiply operator.
  1217. sub multiply3
  1218. {my ($a, $b) = @_;
  1219. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1220. $a->{z} and $b->{z} or die "Multiply using unfinalized sums";
  1221. $a->multiply($b);
  1222. }
  1223. =head3 divide3
  1224. Divide operator.
  1225. sub divide3
  1226. {my ($a, $b, $c) = @_;
  1227. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1228. $a->{z} and $b->{z} or die "Divide using unfinalized sums";
  1229. return $b->divide($a) if $c;
  1230. return $a->divide($b) unless $c;
  1231. }
  1232. =head3 power3
  1233. Power operator.
  1234. sub power3
  1235. {my ($a, $b) = @_;
  1236. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1237. $a->{z} and $b->{z} or die "Power using unfinalized sums";
  1238. $a->power($b);
  1239. }
  1240. =head3 equals3
  1241. Equals operator.
  1242. sub equals3
  1243. {my ($a, $b) = @_;
  1244. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1245. $a->{z} and $b->{z} or die "Equals using unfinalized sums";
  1246. return 1 if $a->{id} == $b->{id}; # Fast equals
  1247. my $c = $a->subtract($b);
  1248. return 1 if $c->isZero()->{id} == $zero->{id};
  1249. return 0;
  1250. }
  1251. =head3 nequal3
  1252. Not equal operator.
  1253. sub nequal3
  1254. {my ($a, $b) = @_;
  1255. !equals3($a, $b);
  1256. }
  1257. =head3 tequals
  1258. Evaluate the expression on the left hand side, stringify it, then
  1259. compare it for string equality with the string on the right hand side.
  1260. This operator is useful for making examples written with Test::Simple
  1261. more readable.
  1262. sub tequals3
  1263. {my ($a, $b) = @_;
  1264. return 1 if "$a" eq $b;
  1265. my $z = simplify($a);
  1266. "$z" eq "$b";
  1267. }
  1268. =head3 solve3
  1269. Solve operator.
  1270. sub solve3
  1271. {my ($a, $b) = @_;
  1272. $a->{z} or die "Solve using unfinalized sum";
  1273. # $b =~ /^[a-z]+$/i or croak "Bad variable $b to solve for";
  1274. solve($a, $b);
  1275. }
  1276. =head3 print3
  1277. Print operator.
  1278. sub print3
  1279. {my ($a) = @_;
  1280. $a->{z} or die "Print of unfinalized sum";
  1281. $a->print();
  1282. }
  1283. =head3 sqrt3
  1284. Sqrt operator.
  1285. sub sqrt3
  1286. {my ($a) = @_;
  1287. $a->{z} or die "Sqrt of unfinalized sum";
  1288. $a->Sqrt();
  1289. }
  1290. =head3 exp3
  1291. Exp operator.
  1292. sub exp3
  1293. {my ($a) = @_;
  1294. $a->{z} or die "Exp of unfinalized sum";
  1295. $a->Exp();
  1296. }
  1297. =head3 sin3
  1298. Sine operator.
  1299. sub sin3
  1300. {my ($a) = @_;
  1301. $a->{z} or die "Sin of unfinalized sum";
  1302. $a->Sin();
  1303. }
  1304. =head3 cos3
  1305. Cosine operator.
  1306. sub cos3
  1307. {my ($a) = @_;
  1308. $a->{z} or die "Cos of unfinalized sum";
  1309. $a->Cos();
  1310. }
  1311. =head3 tan3
  1312. Tan operator.
  1313. sub tan3
  1314. {my ($a) = @_;
  1315. $a->{z} or die "Tan of unfinalized sum";
  1316. $a->tan();
  1317. }
  1318. =head3 log3
  1319. Log operator.
  1320. sub log3
  1321. {my ($a) = @_;
  1322. $a->{z} or die "Log of unfinalized sum";
  1323. $a->Log();
  1324. }
  1325. =head3 dot3
  1326. Dot Product operator.
  1327. sub dot3
  1328. {my ($a, $b, $c) = @_;
  1329. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1330. $a->{z} and $b->{z} or die "Dot of unfinalized sum";
  1331. dot($a, $b);
  1332. }
  1333. =head3 cross3
  1334. Cross operator.
  1335. sub cross3
  1336. {my ($a, $b, $c) = @_;
  1337. $b = newFromString("$b") unless ref($b) eq __PACKAGE__;
  1338. $a->{z} and $b->{z} or die "Cross of unfinalized sum";
  1339. cross($a, $b);
  1340. }
  1341. =head3 unit3
  1342. Unit operator.
  1343. sub unit3
  1344. {my ($a, $b, $c) = @_;
  1345. $a->{z} or die "Unit of unfinalized sum";
  1346. unit($a);
  1347. }
  1348. =head3 modulus3
  1349. Modulus operator.
  1350. sub modulus3
  1351. {my ($a, $b, $c) = @_;
  1352. $a->{z} or die "Modulus of unfinalized sum";
  1353. modulus($a);
  1354. }
  1355. =head3 conjugate3
  1356. Conjugate.
  1357. sub conjugate3
  1358. {my ($a, $b, $c) = @_;
  1359. $a->{z} or die "Conjugate of unfinalized sum";
  1360. conjugate($a);
  1361. }
  1362. #________________________________________________________________________
  1363. # Package installed successfully
  1364. #________________________________________________________________________
  1365. 1;
  1366. __DATA__
  1367. #______________________________________________________________________
  1368. # User guide.
  1369. #______________________________________________________________________
  1370. =head1 NAME
  1371. Math::Algebra::Symbols - Symbolic Algebra in Pure Perl.
  1372. User guide.
  1373. =head1 SYNOPSIS
  1374. Example symbols.pl
  1375. #!perl -w -I..
  1376. #______________________________________________________________________
  1377. # Symbolic algebra.
  1378. # Perl License.
  1379. # PhilipRBrenan@yahoo.com, 2004.
  1380. #______________________________________________________________________
  1381. use Math::Algebra::Symbols hyper=>1;
  1382. use Test::Simple tests=>5;
  1383. ($n, $x, $y) = symbols(qw(n x y));
  1384. $a += ($x**8 - 1)/($x-1);
  1385. $b += sin($x)**2 + cos($x)**2;
  1386. $c += (sin($n*$x) + cos($n*$x))->d->d->d->d / (sin($n*$x)+cos($n*$x));
  1387. $d = tanh($x+$y) == (tanh($x)+tanh($y))/(1+tanh($x)*tanh($y));
  1388. ($e,$f) = @{($x**2 eq 5*$x-6) > $x};
  1389. print "$a\n$b\n$c\n$d\n$e,$f\n";
  1390. ok("$a" eq '$x+$x**2+$x**3+$x**4+$x**5+$x**6+$x**7+1');
  1391. ok("$b" eq '1');
  1392. ok("$c" eq '$n**4');
  1393. ok("$d" eq '1');
  1394. ok("$e,$f" eq '2,3');
  1395. =head1 DESCRIPTION
  1396. This package supplies a set of functions and operators to manipulate
  1397. operator expressions algebraically using the familiar Perl syntax.
  1398. These expressions are constructed
  1399. from L</Symbols>, L</Operators>, and L</Functions>, and processed via
  1400. L</Methods>. For examples, see: L</Examples>.
  1401. =head2 Symbols
  1402. Symbols are created with the exported B<symbols()> constructor routine:
  1403. Example t/constants.t
  1404. #!perl -w
  1405. #______________________________________________________________________
  1406. # Symbolic algebra: examples: constants.
  1407. # PhilipRBrenan@yahoo.com, 2004, Perl License.
  1408. #______________________________________________________________________
  1409. use Math::Algebra::Symbols;
  1410. use Test::Simple tests=>1;
  1411. my ($x, $y, $i, $o, $pi) = symbols(qw(x y i 1 pi));
  1412. ok( "$x $y $i $o $pi" eq '$x $y i 1 $pi' );
  1413. The B<symbols()> routine constructs references to symbolic variables and
  1414. symbolic constants from a list of names and integer constants.
  1415. The special symbol B<i> is recognized as the square root of B<-1>.
  1416. The special symbol B<pi> is recognized as the smallest positive real
  1417. that satisfies:
  1418. Example t/ipi.t
  1419. #!perl -w
  1420. #______________________________________________________________________
  1421. # Symbolic algebra: examples: constants.
  1422. # PhilipRBrenan@yahoo.com, 2004, Perl License.
  1423. #______________________________________________________________________
  1424. use Math::Algebra::Symbols;
  1425. use Test::Simple tests=>2;
  1426. my ($i, $pi) = symbols(qw(i pi));
  1427. ok( exp($i*$pi) == -1 );
  1428. ok( exp($i*$pi) <=> '-1' );
  1429. =head3 Constructor Routine Name
  1430. If you wish to use a different name for the constructor routine, say
  1431. B<S>:
  1432. Example t/ipi2.t
  1433. #!perl -w
  1434. #______________________________________________________________________
  1435. # Symbolic algebra: examples: constants.
  1436. # PhilipRBrenan@yahoo.com, 2004, Perl License.
  1437. #______________________________________________________________________
  1438. use Math::Algebra::Symbols symbols=>'S';
  1439. use Test::Simple tests=>2;
  1440. my ($i, $pi) = S(qw(i pi));
  1441. ok( exp($i*$pi) == -1 );
  1442. ok( exp($i*$pi) <=> '-1' );
  1443. =head3 Big Integers
  1444. Symbols automatically uses big integers if needed.
  1445. Example t/bigInt.t
  1446. #!perl -w
  1447. #______________________________________________________________________
  1448. # Symbolic algebra: examples: bigInt.
  1449. # PhilipRBrenan@yahoo.com, 2004, Perl License.
  1450. #______________________________________________________________________
  1451. use Math::Algebra::Symbols;
  1452. use Test::Simple tests=>1;
  1453. my $z = symbols('1234567890987654321/1234567890987654321');
  1454. ok( eval $z eq '1');
  1455. =head2 Operators
  1456. L</Symbols> can be combined with L</Operators> to create symbolic expressions:
  1457. =head3 Arithmetic operators
  1458. =head4 Arithmetic Operators: B<+> B<-> B<*> B</> B<**>
  1459. Example t/x2y2.t
  1460. #!perl -w
  1461. #______________________________________________________________________
  1462. # Symbolic algebra: examples: simplification.
  1463. # PhilipRBrenan@yahoo.com, 2004, Perl License.
  1464. #______________________________________________________________________
  1465. use Math::Algebra::Symbols;
  1466. use Test::Simple tests=>3;
  1467. my ($x, $y) = symbols(qw(x y));
  1468. ok( ($x**2-$y**2)/($x-$y) == $x+$y );
  1469. ok( ($x**2-$y**2)/($x-$y) != $x-$y );
  1470. ok( ($x**2-$y**2)/($x-$y) <=> '$x+$y' );
  1471. The operators: B<+=> B<-=> B<*=> B</=> are overloaded to work
  1472. symbolically rather than numerically. If you need numeric results, you
  1473. can always B<eval()> the resulting symbolic expression.
  1474. =head4 Square root Operator: B<sqrt>
  1475. Example t/ix.t
  1476. #!perl -w
  1477. #______________________________________________________________________
  1478. # Symbolic algebra: examples: sqrt(-1).
  1479. # PhilipRBrenan@yahoo.com, 2004, Perl License.
  1480. #______________________________________________________________________
  1481. use Math::Algebra::Symbols;
  1482. use Test::Simple tests=>2;
  1483. my ($x, $i) = symbols(qw(x i));
  1484. ok( sqrt(-$x**2) == $i*$x );
  1485. ok( sqrt(-$x**2) <=> 'i*$x' );
  1486. The square root is represented by the symbol B<i>

Large files files are truncated, but you can click here to view the full file