File Coverage

blib/lib/Math/Symbolic/Operator.pm
Criterion Covered Total %
statement 253 278 91.0
branch 152 196 77.5
condition 64 87 73.5
subroutine 23 23 100.0
pod 12 12 100.0
total 504 596 84.5


line stmt bran cond sub pod time code
1              
2             =encoding utf8
3              
4             =head1 NAME
5              
6             Math::Symbolic::Operator - Operators in symbolic calculations
7              
8             =head1 SYNOPSIS
9              
10             use Math::Symbolic::Operator;
11            
12             my $sum = Math::Symbolic::Operator->new('+', $term1, $term2);
13            
14             # or:
15             my $division =
16             Math::Symbolic::Operator->new(
17             {
18             type => B_DIVISON,
19             operands => [$term1, $term2],
20             }
21             );
22            
23             my $derivative =
24             Math::Symbolic::Operator->new(
25             {
26             type => U_P_DERIVATIVE,
27             operands => [$term],
28             }
29             );
30              
31             =head1 DESCRIPTION
32              
33             This module implements all Math::Symbolic::Operator objects.
34             These objects are overloaded in stringification-context to call the
35             to_string() method on the object. In numeric and boolean context, they
36             evaluate to their numerical representation.
37              
38             For a list of supported operators, please refer to the list found below, in the
39             documentation for the new() constructor.
40              
41             Math::Symbolic::Operator inherits from Math::Symbolic::Base.
42              
43             =head2 EXPORT
44              
45             None.
46              
47             =cut
48              
49             package Math::Symbolic::Operator;
50              
51 23     23   864 use 5.006;
  23         86  
  23         1105  
52 23     23   141 use strict;
  23         53  
  23         868  
53 23     23   144 use warnings;
  23         52  
  23         7373  
54 23     23   197 no warnings 'recursion';
  23         49  
  23         913  
55              
56 23     23   147 use Carp;
  23         51  
  23         2034  
57              
58 23     23   164 use Math::Symbolic::ExportConstants qw/:all/;
  23         56  
  23         5810  
59 23     23   16128 use Math::Symbolic::Derivative qw//;
  23         80  
  23         675  
60              
61 23     23   948 use base 'Math::Symbolic::Base';
  23         52  
  23         33897  
62              
63             our $VERSION = '0.612';
64              
65             =head1 CLASS DATA
66              
67             Math::Symbolic::Operator contains several class data structures. Usually, you
68             should not worry about dealing with any of them because they are mostly an
69             implementation detail, but for the sake of completeness, here's the gist, but
70             feel free to skip this section of the docs:
71              
72             One of these is the %Op_Symbols hash that associates operator (and function)
73             symbols with the corresponding constant as exported by Math::Symbolic or
74             Math::Symbolic::ExportConstants. (For example, '+' => B_SUM which in turn is
75             0, if I recall correctly. But I didn't tell you that. Because you're supposed
76             to use the supplied (inlined and hence fast) constants so I can change their
77             internal order if I deem it necessary.)
78              
79             =cut
80              
81             our %Op_Symbols = (
82             '+' => B_SUM,
83             '-' => B_DIFFERENCE,
84             '*' => B_PRODUCT,
85             '/' => B_DIVISION,
86             'log' => B_LOG,
87             '^' => B_EXP,
88             'neg' => U_MINUS,
89             'partial_derivative' => U_P_DERIVATIVE,
90             'total_derivative' => U_T_DERIVATIVE,
91             'sin' => U_SINE,
92             'cos' => U_COSINE,
93             'tan' => U_TANGENT,
94             'cot' => U_COTANGENT,
95             'asin' => U_ARCSINE,
96             'acos' => U_ARCCOSINE,
97             'atan' => U_ARCTANGENT,
98             'acot' => U_ARCCOTANGENT,
99             'sinh' => U_SINE_H,
100             'cosh' => U_COSINE_H,
101             'asinh' => U_AREASINE_H,
102             'acosh' => U_AREACOSINE_H,
103             'atan2' => B_ARCTANGENT_TWO,
104             );
105              
106             =pod
107              
108             The array @Op_Types associates operator indices (recall those nifty constants?)
109             with anonymous hash datastructures that contain some info on the operator such
110             as its arity, the rule used to derive it, its infix string, its prefix string,
111             and information on how to actually apply it to numbers.
112              
113             =cut
114              
115             our @Op_Types = (
116              
117             # B_SUM
118             {
119             arity => 2,
120             derive => 'each operand',
121             infix_string => '+',
122             prefix_string => 'add',
123             application => '$_[0] + $_[1]',
124             commutative => 1,
125             },
126              
127             # B_DIFFERENCE
128             {
129             arity => 2,
130             derive => 'each operand',
131             infix_string => '-',
132             prefix_string => 'subtract',
133             application => '$_[0] - $_[1]',
134             #commutative => 0,
135             },
136              
137             # B_PRODUCT
138             {
139             arity => 2,
140             derive => 'product rule',
141             infix_string => '*',
142             prefix_string => 'multiply',
143             application => '$_[0] * $_[1]',
144             commutative => 1,
145             },
146              
147             # B_DIVISION
148             {
149             derive => 'quotient rule',
150             arity => 2,
151             infix_string => '/',
152             prefix_string => 'divide',
153             application => '$_[0] / $_[1]',
154             #commutative => 0,
155             },
156              
157             # U_MINUS
158             {
159             arity => 1,
160             derive => 'each operand',
161             infix_string => '-',
162             prefix_string => 'negate',
163             application => '-$_[0]',
164             },
165              
166             # U_P_DERIVATIVE
167             {
168             arity => 2,
169             derive => 'derivative commutation',
170             infix_string => undef,
171             prefix_string => 'partial_derivative',
172             application => \&Math::Symbolic::Derivative::partial_derivative,
173             },
174              
175             # U_T_DERIVATIVE
176             {
177             arity => 2,
178             derive => 'derivative commutation',
179             infix_string => undef,
180             prefix_string => 'total_derivative',
181             application => \&Math::Symbolic::Derivative::total_derivative,
182             },
183              
184             # B_EXP
185             {
186             arity => 2,
187             derive => 'logarithmic chain rule after ln',
188             infix_string => '^',
189             prefix_string => 'exponentiate',
190             application => '$_[0] ** $_[1]',
191             #commutative => 0,
192             },
193              
194             # B_LOG
195             {
196             arity => 2,
197             derive => 'logarithmic chain rule',
198             infix_string => undef,
199             prefix_string => 'log',
200             application => 'log($_[1]) / log($_[0])',
201             #commutative => 0,
202             },
203              
204             # U_SINE
205             {
206             arity => 1,
207             derive => 'trigonometric derivatives',
208             infix_string => undef,
209             prefix_string => 'sin',
210             application => 'sin($_[0])',
211             },
212              
213             # U_COSINE
214             {
215             arity => 1,
216             derive => 'trigonometric derivatives',
217             infix_string => undef,
218             prefix_string => 'cos',
219             application => 'cos($_[0])',
220             },
221              
222             # U_TANGENT
223             {
224             arity => 1,
225             derive => 'trigonometric derivatives',
226             infix_string => undef,
227             prefix_string => 'tan',
228             application => 'sin($_[0])/cos($_[0])',
229             },
230              
231             # U_COTANGENT
232             {
233             arity => 1,
234             derive => 'trigonometric derivatives',
235             infix_string => undef,
236             prefix_string => 'cot',
237             application => 'cos($_[0])/sin($_[0])',
238             },
239              
240             # U_ARCSINE
241             {
242             arity => 1,
243             derive => 'inverse trigonometric derivatives',
244             infix_string => undef,
245             prefix_string => 'asin',
246             #application => 'Math::Symbolic::AuxFunctions::asin($_[0])',
247             application => 'atan2( $_[0], sqrt( 1 - $_[0] * $_[0] ) )',
248             },
249              
250             # U_ARCCOSINE
251             {
252             arity => 1,
253             derive => 'inverse trigonometric derivatives',
254             infix_string => undef,
255             prefix_string => 'acos',
256             application => 'atan2( sqrt( 1 - $_[0] * $_[0] ), $_[0] ) ',
257             #application => 'Math::Symbolic::AuxFunctions::acos($_[0])',
258             },
259              
260             # U_ARCTANGENT
261             {
262             arity => 1,
263             derive => 'inverse trigonometric derivatives',
264             infix_string => undef,
265             prefix_string => 'atan',
266             application => 'atan2($_[0], 1)',
267             #application => 'Math::Symbolic::AuxFunctions::atan($_[0])',
268             },
269              
270             # U_ARCCOTANGENT
271             {
272             arity => 1,
273             derive => 'inverse trigonometric derivatives',
274             infix_string => undef,
275             prefix_string => 'acot',
276             application => 'atan2(1 / $_[0], 1)',
277             #application => 'Math::Symbolic::AuxFunctions::acot($_[0])',
278             },
279              
280             # U_SINE_H
281             {
282             arity => 1,
283             derive => 'trigonometric derivatives',
284             infix_string => undef,
285             prefix_string => 'sinh',
286             #application => '0.5*(EULER**$_[0] - EULER**(-$_[0]))',
287             application => '0.5*('.EULER.'**$_[0] - '.EULER.'**(-$_[0]))',
288             },
289              
290             # U_COSINE_H
291             {
292             arity => 1,
293             derive => 'trigonometric derivatives',
294             infix_string => undef,
295             prefix_string => 'cosh',
296             application => '0.5*('.EULER.'**$_[0] + '.EULER.'**(-$_[0]))',
297             #application => '0.5*(EULER**$_[0] + EULER**(-$_[0]))',
298             },
299              
300             # U_AREASINE_H
301             {
302             arity => 1,
303             derive => 'inverse trigonometric derivatives',
304             infix_string => undef,
305             prefix_string => 'asinh',
306             application => 'log( $_[0] + sqrt( $_[0] * $_[0] + 1 ) ) ',
307             #application => 'Math::Symbolic::AuxFunctions::asinh($_[0])',
308             },
309              
310             # U_AREACOSINE_H
311             {
312             arity => 1,
313             derive => 'inverse trigonometric derivatives',
314             infix_string => undef,
315             prefix_string => 'acosh',
316             application => 'log( $_[0] + sqrt( $_[0] * $_[0] - 1 ) ) ',
317             #application => 'Math::Symbolic::AuxFunctions::acosh($_[0])',
318             },
319              
320             # B_ARCTANGENT_TWO
321             {
322             arity => 2,
323             derive => 'inverse atan2',
324             infix_string => undef,
325             prefix_string => 'atan2',
326             application => 'atan2($_[0], $_[1])',
327             #application => 'Math::Symbolic::AuxFunctions::atan($_[0])',
328             #commutative => 0,
329             },
330              
331             );
332              
333             =head1 METHODS
334              
335             =head2 Constructor new
336              
337             Expects a hash reference as first argument. That hash's contents
338             will be treated as key-value pairs of object attributes.
339             Important attributes are 'type' => OPERATORTYPE (use constants as
340             exported by Math::Symbolic::ExportConstants!) and 'operands=>[op1,op2,...]'.
341             Where the operands themselves may either be valid Math::Symbolic::* objects
342             or strings that will be parsed as such.
343              
344             Special case: if no hash reference was found, first
345             argument is assumed to be the operator's symbol and the operator
346             is assumed to be binary. The following 2 arguments will be treated as
347             operands. This special case will ignore attempts to clone objects but if
348             the operands are no valid Math::Symbolic::* objects, they will be sent
349             through a Math::Symbolic::Parser to construct Math::Symbolic trees.
350              
351             Returns a Math::Symbolic::Operator.
352              
353             Supported operator symbols: (number of operands and their
354             function in parens)
355              
356             + => sum (2)
357             - => difference (2)
358             * => product (2)
359             / => division (2)
360             log => logarithm (2: base, function)
361             ^ => exponentiation (2: base, exponent)
362             neg => unary minus (1)
363             partial_derivative => partial derivative (2: function, var)
364             total_derivative => total derivative (2: function, var)
365             sin => sine (1)
366             cos => cosine (1)
367             tan => tangent (1)
368             cot => cotangent (1)
369             asin => arc sine (1)
370             acos => arc cosine (1)
371             atan => arc tangent (1)
372             atan2 => arc tangent of y/x (2: y, x)
373             acot => arc cotangent (1)
374             sinh => hyperbolic sine (1)
375             cosh => hyperbolic cosine (1)
376             asinh => hyperbolic area sine (1)
377             acosh => hyperbolic area cosine (1)
378              
379             =cut
380              
381             sub new {
382 9234     9234 1 32629 my $proto = shift;
383 9234   66     25710 my $class = ref($proto) || $proto;
384              
385 9234 100 100     30081 if ( @_ and not( ref( $_[0] ) eq 'HASH' ) ) {
386 2979         8795 my $symbol = shift;
387 2979         6688 my $type = $Op_Symbols{$symbol};
388 2979 50       6818 croak "Invalid operator type specified ($symbol)."
389             unless defined $type;
390 2979         13945 my $operands = [ @_[ 0 .. $Op_Types[$type]{arity} - 1 ] ];
391              
392 2979 50       12288 croak "Undefined operands not supported by "
393             . "Math::Symbolic::Operator objects."
394             if grep +( not defined($_) ), @$operands;
395              
396 5603 100       30720 @$operands =
397             map {
398 2979         4769 ref($_) =~ /^Math::Symbolic/
399             ? $_
400             : Math::Symbolic::parse_from_string($_)
401             } @$operands;
402              
403 2979         25907 return bless {
404             type => $type,
405             operands => $operands,
406             } => $class;
407             }
408              
409 6255         7094 my %args;
410 6255 100       12102 %args = %{ $_[0] } if @_;
  564         3323  
411             # and ref( $_[0] ) eq 'HASH';
412             # above condition isn't necessary since that'd otherwise have been
413             # the above branch.
414              
415 6255         9501 my $operands = [];
416 6255 100       13947 if ( ref $proto ) {
417 5608         6140 foreach ( @{ $proto->{operands} } ) {
  5608         13421  
418 9151         24639 push @$operands, $_->new();
419             }
420             }
421              
422 6255 100       31906 my $self = {
423             type => undef,
424             ( ref($proto) ? %$proto : () ),
425             operands => $operands,
426             %args,
427             };
428              
429 6255 100       17095 @{ $self->{operands} } =
  10098         35579  
430             map {
431 6255         13782 ref($_) =~ /^Math::Symbolic/
432             ? $_
433             : Math::Symbolic::parse_from_string($_)
434 6255         8801 } @{ $self->{operands} };
435              
436 6255         27627 bless $self => $class;
437             }
438              
439             =head2 Method arity
440              
441             Returns the operator's arity as an integer.
442              
443             =cut
444              
445             sub arity {
446 2209     2209 1 2858 my $self = shift;
447 2209         7913 return $Op_Types[ $self->{type} ]{arity};
448             }
449              
450             =head2 Method type
451              
452             Optional integer argument that sets the operator's type.
453             Returns the operator's type as an integer.
454              
455             =cut
456              
457             sub type {
458 21334     21334 1 25714 my $self = shift;
459 21334 50       45095 $self->{type} = shift if @_;
460 21334         54780 return $self->{type};
461             }
462              
463             =head2 Method to_string
464              
465             Returns a string representation of the operator and its operands.
466             Optional argument: 'prefix' or 'infix'. Defaults to 'infix'.
467              
468             =cut
469              
470             sub to_string {
471 526     526 1 24852 my $self = shift;
472 526         709 my $string_type = shift;
473 526 100 100     2497 $string_type = 'infix'
474             unless defined $string_type
475             and $string_type eq 'prefix';
476 23     23   190 no warnings 'recursion';
  23         48  
  23         98072  
477              
478 526         749 my $string = '';
479 526 100       1058 if ( $string_type eq 'prefix' ) {
480 169         483 $string .= $self->_to_string_prefix();
481             }
482             else {
483 357         1040 $string .= $self->_to_string_infix();
484             }
485 526         16473 return $string;
486             }
487              
488             sub _to_string_infix {
489 357     357   479 my $self = shift;
490 357         951 my $op = $Op_Types[ $self->{type} ];
491              
492 357         719 my $op_str = $op->{infix_string};
493 357         386 my $string;
494 357 100       891 if ( $op->{arity} == 2 ) {
    50          
495 323         1238 my $op1 = $self->{operands}[0]->term_type() == T_OPERATOR;
496 323         949 my $op2 = $self->{operands}[1]->term_type() == T_OPERATOR;
497              
498 323 100       636 if ( not defined $op_str ) {
499 11         22 $op_str = $op->{prefix_string};
500 11         23 $string = "$op_str(";
501 22         65 $string .= join( ', ',
502 11         24 map { $_->to_string('infix') } @{ $self->{operands} } );
  11         41  
503 11         33 $string .= ')';
504             }
505             else {
506 312 100       1204 $string =
    100          
    100          
    100          
507             ( $op1 ? '(' : '' )
508             . $self->{operands}[0]->to_string('infix')
509             . ( $op1 ? ')' : '' )
510             . " $op_str "
511             . ( $op2 ? '(' : '' )
512             . $self->{operands}[1]->to_string('infix')
513             . ( $op2 ? ')' : '' );
514             }
515             }
516             elsif ( $op->{arity} == 1 ) {
517 34         145 my $is_op1 = $self->{operands}[0]->term_type() == T_OPERATOR;
518 34 100       95 if ( not defined $op_str ) {
519 23         44 $op_str = $op->{prefix_string};
520 23         90 $string =
521             "$op_str(" . $self->{operands}[0]->to_string('infix') . ")";
522             }
523             else {
524 11 100       75 $string = "$op_str"
    100          
525             . ( $is_op1 ? '(' : '' )
526             . $self->{operands}[0]->to_string('infix')
527             . ( $is_op1 ? ')' : '' );
528             }
529             }
530             else {
531 0         0 $string = $self->_to_string_prefix();
532             }
533 357         918 return $string;
534             }
535              
536             sub _to_string_prefix {
537 169     169   216 my $self = shift;
538 169         944 my $op = $Op_Types[ $self->{type} ];
539              
540 169         331 my $op_str = $op->{prefix_string};
541 169         324 my $string = "$op_str(";
542 303         944 $string .=
543 169         215 join( ', ', map { $_->to_string('prefix') } @{ $self->{operands} } );
  169         363  
544 169         284 $string .= ')';
545 169         437 return $string;
546             }
547              
548             =head2 Method term_type
549              
550             Returns the type of the term. ( T_OPERATOR )
551              
552             =cut
553              
554 30819     30819 1 64665 sub term_type {T_OPERATOR}
555              
556             =head2 Method simplify
557              
558             Term simpilification.
559             First argument: Boolean indicating that the tree does not
560             need to be cloned, but can be restructured instead.
561             While this is faster, you might not be able to use the old
562             tree any more.
563              
564             Example:
565              
566             my $othertree = $tree->simplify();
567             # can use $othertree and $tree now.
568              
569             my $yetanothertree = $tree->simplify(1);
570             # must not use $tree any more because its internal
571             # representation might have been destroyed.
572              
573             If you want to optimize a routine and you're sure that you
574             won't need the unsimplified tree any more, go ahead and use
575             the first parameter. In all other cases, you should go the
576             safe route.
577              
578             =cut
579              
580             sub simplify {
581 1586     1586 1 3982 my $self = shift;
582 1586         1763 my $dont_clone = shift;
583 1586 100       3066 $self = $self->new() unless $dont_clone;
584              
585 1586         2762 my $operands = $self->{operands};
586 1586         3144 my $op = $Op_Types[ $self->type() ];
587              
588             # simplify operands without cloning.
589 1586         2778 @$operands = map { $_->simplify(1) } @$operands;
  2549         8529  
590              
591 1586 100       10226 if ( $self->arity() == 2 ) {
    50          
592 963         1615 my $o1 = $operands->[0];
593 963         1313 my $o2 = $operands->[1];
594 963         2217 my $tt1 = $o1->term_type();
595 963         2461 my $tt2 = $o2->term_type();
596 963         2115 my $type = $self->type();
597              
598 963 100       6033 if ( $self->is_simple_constant() ) {
599 70         320 return $self->apply();
600             }
601              
602 893 100       6185 if ( $o1->is_identical($o2) ) {
603 17 100       63 if ( $type == B_PRODUCT ) {
    50          
    0          
    0          
604 12         69 my $two = Math::Symbolic::Constant->new(2);
605 12         65 return $self->new( '^', $o1, $two )->simplify(1);
606             }
607             elsif ( $type == B_SUM ) {
608 5         79 my $two = Math::Symbolic::Constant->new(2);
609 5         24 return $self->new( '*', $two, $o1 )->simplify(1);
610             }
611             elsif ( $type == B_DIVISION ) {
612 0 0 0     0 croak "Symbolic division by zero."
      0        
613             if $o2->term_type() == T_CONSTANT
614             and ($o2->value() == 0
615             or $o2->special() eq 'zero' );
616 0         0 return Math::Symbolic::Constant->one();
617             }
618             elsif ( $type == B_DIFFERENCE ) {
619 0         0 return Math::Symbolic::Constant->zero();
620             }
621             }
622              
623             # exp(0) = 1
624 876 50 100     4211 if ( $tt2 == T_CONSTANT
      100        
      66        
625             and $tt1 == T_OPERATOR
626             and $type == B_EXP
627             and $o2->value() == 0 )
628             {
629 0         0 return Math::Symbolic::Constant->one();
630             }
631            
632             # a^1 = a
633 876 100 100     3092 if ( $tt2 == T_CONSTANT
      66        
      66        
634             and $type == B_EXP
635             and ( $o2->value() == 1 or $o2->special() eq 'one' ) )
636             {
637 6         35 return $o1;
638             }
639              
640             # (a^b)^const = a^(const*b)
641 870 100 100     3459 if ( $tt2 == T_CONSTANT
      100        
      100        
642             and $tt1 == T_OPERATOR
643             and $type == B_EXP
644             and $o1->type() == B_EXP )
645             {
646 11         52 return $self->new( '^', $o1->op1(),
647             $self->new( '*', $o2, $o1->op2() ) )->simplify(1);
648             }
649              
650             # redundant
651             # if ( $tt1 == T_VARIABLE
652             # and $tt2 == T_VARIABLE
653             # and $o1->name() eq $o2->name() )
654             # {
655             # if ( $type == B_SUM ) {
656             # my $two = Math::Symbolic::Constant->new(2);
657             # return $self->new( '*', $two, $o1 );
658             # }
659             # elsif ( $type == B_DIFFERENCE ) {
660             # return Math::Symbolic::Constant->zero();
661             # }
662             # elsif ( $type == B_PRODUCT ) {
663             # my $two = Math::Symbolic::Constant->new(2);
664             # return $self->new( '^', $o1, $two );
665             # }
666             # elsif ( $type == B_DIVISION ) {
667             # return Math::Symbolic::Constant->one();
668             # }
669             # }
670              
671 859 100 100     3914 if ( $tt1 == T_CONSTANT or $tt2 == T_CONSTANT ) {
    100          
672 508 100       1369 my $const = ( $tt1 == T_CONSTANT ? $o1 : $o2 );
673 508 100       919 my $not_c = ( $tt1 == T_CONSTANT ? $o2 : $o1 );
674 508         876 my $constant_first = $tt1 == T_CONSTANT;
675              
676 508 100       1357 if ( $type == B_SUM ) {
677 19 100       70 return $not_c if $const->value() == 0;
678 15         132 return $not_c->mod_add_constant($const);
679             }
680            
681 489 100       1187 if ( $type == B_DIFFERENCE ) {
682 4 100       45 if (!$constant_first) {
683 2         19 my $value = $const->value();
684 2 50       8 return $not_c if $value == 0;
685 2         19 return $not_c->mod_add_constant(-$value);
686             }
687 2 50 33     14 if ( $constant_first and $const->value == 0 ) {
688 0         0 return Math::Symbolic::Operator->new(
689             {
690             type => U_MINUS,
691             operands => [$not_c],
692             }
693             );
694             }
695             }
696            
697 487 100       1327 if ( $type == B_PRODUCT ) {
    100          
698 248 100       864 return $not_c if $const->value() == 1;
699 247 100       787 return Math::Symbolic::Constant->zero()
700             if $const->value == 0;
701              
702 227 100 100     612 if ( $not_c->term_type() == T_OPERATOR
    100 100        
      66        
      100        
      100        
703             and $not_c->type() == B_PRODUCT
704             and $not_c->op1()->term_type() == T_CONSTANT
705             || $not_c->op2()->term_type() == T_CONSTANT )
706             {
707 20 100       70 my ( $c, $nc ) = (
708             $not_c->op1()->term_type() == T_CONSTANT
709             ? ( $not_c->op1, $not_c->op2 )
710             : ( $not_c->op2, $not_c->op1 )
711             );
712 20         81 my $c_product = $not_c->new( '*', $const, $c )->apply();
713 20         104 return $not_c->new( '*', $c_product, $nc );
714             }
715             elsif ( $not_c->term_type() == T_OPERATOR
716             and $not_c->type() == B_DIVISION
717             and $not_c->op1()->term_type() == T_CONSTANT )
718             {
719 7         31 return Math::Symbolic::Operator->new(
720             '/',
721             Math::Symbolic::Constant->new(
722             $const->value() * $not_c->op1()->value()
723             ),
724             $not_c->op2()
725             );
726             }
727             }
728             elsif ( $type == B_DIVISION ) {
729 31 50 33     118 return $not_c
730             if !$constant_first
731             and $const->value == 1;
732 31 50 33     109 return Math::Symbolic::Constant->new('#Inf')
733             if !$constant_first
734             and $const->value == 0;
735 31 50       127 return Math::Symbolic::Constant->zero()
736             if $const->value == 0;
737              
738             }
739             }
740             elsif ( $type == B_PRODUCT ) {
741 188 50 100     1330 if ( $tt2 == T_CONSTANT ) {
    50          
    100          
742 0         0 return $o1->mod_multiply_constant($o2);
743             }
744             elsif ( $tt1 == T_CONSTANT ) {
745 0         0 return $o2->mod_multiply_constant($o1);
746             }
747             elsif ( $tt1 == T_OPERATOR and $tt2 == T_VARIABLE ) {
748 7         18 return $self->new( '*', $o2, $o1 );
749             }
750             }
751              
752 783 100       2044 if ( $type == B_SUM ) {
753 30         82 my @ops;
754             my @const;
755 30         86 my @todo = ( $o1, $o2 );
756 30         51 my %vars;
757 30         121 while (@todo) {
758 94         146 my $this = shift @todo;
759              
760 94 100       211 if ( $this->term_type() == T_OPERATOR ) {
    50          
761 89         187 my $t = $this->type();
762 89 100       431 if ( $t == B_SUM ) {
    50          
    50          
    100          
763 17         23 push @todo, @{ $this->{operands} };
  17         70  
764             }
765             elsif ( $t == B_DIFFERENCE ) {
766 0         0 push @todo, $this->op1(),
767             Math::Symbolic::Operator->new( 'neg',
768             $this->op2() );
769             }
770             elsif ( $t == U_MINUS ) {
771 0         0 my $op = $this->op1();
772 0         0 my $tt = $op->term_type();
773 0 0       0 if ( $tt == T_VARIABLE ) {
    0          
774 0         0 $vars{$op->name}--;
775             }
776             elsif ( $tt == T_CONSTANT ) {
777 0         0 push @const, $todo[0]->value();
778             }
779             else {
780 0         0 my $ti = $op->type();
781 0 0       0 if ( $ti == U_MINUS ) {
    0          
    0          
782 0         0 push @todo, $op->op1();
783             }
784             elsif ( $ti == B_SUM ) {
785 0         0 push @todo,
786             Math::Symbolic::Operator->new(
787             'neg', $op->op1()
788             ),
789             Math::Symbolic::Operator->new( 'neg',
790             $op->op2() );
791             }
792             elsif ( $ti == B_DIFFERENCE ) {
793 0         0 push @todo, $op->op2(),
794             Math::Symbolic::Operator->new( 'neg',
795             $op->op1() );
796             }
797             else {
798 0         0 push @ops, $this;
799             }
800             }
801             }
802             elsif ( $t == B_PRODUCT ) {
803 62         95 my ($o1, $o2) = @{$this->{operands}};
  62         144  
804 62         164 my $tl = $o1->term_type();
805 62         153 my $tr = $o2->term_type();
806            
807 62 50 33     336 if ($tl == T_VARIABLE and $tr == T_CONSTANT) {
    100 66        
808 0         0 $vars{$o1->name}+= $o2->value();
809             }
810             elsif ($tr == T_VARIABLE and $tl == T_CONSTANT) {
811 1         4 $vars{$o2->name}+= $o1->value();
812             }
813             else {
814 61         234 push @ops, $this;
815             }
816             }
817             else {
818 10         32 push @ops, $this;
819             }
820             }
821             elsif ( $this->term_type() == T_VARIABLE ) {
822 5         19 $vars{$this->name}++;
823             }
824             else {
825 0         0 push @const, $this->value();
826             }
827             }
828              
829 30         71 my @vars = ();
830 30         114 foreach (keys %vars) {
831 6         12 my $num = $vars{$_};
832 6 50       15 if (!$num) { next; }
  0         0  
833            
834 6 100       20 if ($num == 1) {
835 5         20 push @vars, Math::Symbolic::Variable->new($_);
836 5         15 next;
837             }
838 1         15 my $mul = Math::Symbolic::Operator->new(
839             '*',
840             Math::Symbolic::Constant->new(abs($num)),
841             Math::Symbolic::Variable->new($_)
842             );
843 1 50       5 push @ops, $num < 0
844             ? Math::Symbolic::Operator->new('neg', $mul)
845             : $mul;
846             }
847            
848 30         63 my $const;
849 30 50 33     133 $const = Math::Symbolic::Constant->new($const) if defined $const and $const != 0;
850              
851 30 50       109 $const = shift @vars if not defined $const;
852 30         78 foreach ( @vars ) {
853 1         4 $const = Math::Symbolic::Operator->new('+', $const, $_);
854             }
855            
856 30         74 @ops = map {$_->simplify(1)} @ops;
  72         307  
857 30         71 my @newops;
858 30 100       118 push @newops, $const if defined $const;
859 30         136 foreach my $out ( 0 .. $#ops ) {
860 72 100       197 next if not defined $ops[$out];
861 70         101 my $identical = 0;
862 70         168 foreach my $in ( 0 .. $#ops ) {
863 214 100 100     903 next if $in == $out or not defined $ops[$in];
864 141 100       782 if ( $ops[$out]->is_identical( $ops[$in] ) ) {
865 2         5 $identical++;
866 2         7 $ops[$in] = undef;
867             }
868             }
869 70 100       207 if ( not $identical ) {
870 68         181 push @newops, $ops[$out];
871             }
872             else {
873 2         19 push @newops,
874             Math::Symbolic::Operator->new( '*', $identical + 1,
875             $ops[$out] );
876             }
877             }
878            
879 30         59 my $sumops;
880 30 50       132 if (@newops) {
  0         0  
881 30         57 $sumops = shift @newops;
882 30         211 $sumops += $_ foreach @newops;
883             }
884             else {return Math::Symbolic::Constant->zero()}
885              
886 30         937 return $sumops;
887             }
888             }
889             elsif ( $self->arity() == 1 ) {
890 623         934 my $o = $operands->[0];
891 623         1561 my $tt = $o->term_type();
892 623         1233 my $type = $self->type();
893 623 100       1470 if ( $type == U_MINUS ) {
894 181 100       667 if ( $tt == T_CONSTANT ) {
    100          
895 2         9 return Math::Symbolic::Constant->new( -$o->value(), );
896             }
897             elsif ( $tt == T_OPERATOR ) {
898 172         402 my $inner_type = $o->type();
899 172 100       668 if ( $inner_type == U_MINUS ) {
    100          
900 3         45 return $o->{operands}[0];
901             }
902             elsif ( $inner_type == B_DIFFERENCE ) {
903 7         11 return $o->new( '-', @{$o->{operands}}[1,0] );
  7         19  
904             }
905             }
906             }
907             }
908              
909 1364         5418 return $self;
910             }
911              
912             =head2 Methods op1 and op2
913              
914             Returns first/second operand of the operator if it exists or undef.
915              
916             =cut
917              
918             sub op1 {
919 156 50   156 1 265 return $_[0]{operands}[0] if @{ $_[0]{operands} } >= 1;
  156         1140  
920 0         0 return undef;
921             }
922              
923             sub op2 {
924 70 50   70 1 121 return $_[0]{operands}[1] if @{ $_[0]{operands} } >= 2;
  70         501  
925             }
926              
927             =head2 Method apply
928              
929             Applies the operation to its operands' value() and returns the result
930             as a constant (-object).
931              
932             Without arguments, all variables in the tree are required to have a value.
933             If any don't, the call to apply() returns undef.
934              
935             To (temorarily, for this single method call) assign values to
936             variables in the tree, you may provide key/value pairs of variable names
937             and values. Instead of passing a list of key/value pairs, you may also pass
938             a single hash reference containing the variable mappings.
939              
940             You usually want to call the value() instead of this.
941              
942             =cut
943              
944             sub apply {
945 4764     4764 1 5523 my $self = shift;
946 4764 100       8671 my $args = ( @_ == 1 ? $_[0] : +{ @_ } );
947 4764         8694 my $op_type = $self->type();
948 4764         8176 my $op = $Op_Types[$op_type];
949 4764         7331 my $operands = $self->{operands};
950 4764         6901 my $application = $op->{application};
951              
952 4764 100       8782 if ( ref($application) ne 'CODE' ) {
953 4757         6727 local @_;
954 4757         5895 local $@;
955 4757         6960 eval {
956 8705         28267 @_ = map {
957 4757         7305 my $v = $_->value($args);
958             (
959 8705 100       32461 defined $v
960             ? $v
961             : croak
962             "Undefined operand in Math::Symbolic::Operator->apply()"
963             )
964             } @$operands;
965             };
966 4757 100       11085 return undef if $@;
967 4755 50 66     10805 return undef if $op_type == B_DIVISION and $_[1] == 0;
968 4755         294948 my $result = eval $application;
969 4755 50       16033 die "Invalid operator application: $@" if $@;
970 4755 50       9581 die "Undefined result from operator application."
971             if not defined $result;
972              
973 4755         16161 return Math::Symbolic::Constant->new($result);
974             }
975             else {
976 7         34 return $application->(@$operands);
977             }
978             }
979              
980             =head2 Method value
981              
982             value() evaluates the Math::Symbolic tree to its numeric representation.
983              
984             value() without arguments requires that every variable in the tree contains
985             a defined value attribute. Please note that this refers to every variable
986             I, not just every named variable.
987              
988             value() with one argument sets the object's value if you're dealing with
989             Variables or Constants. In case of operators, a call with one argument will
990             assume that the argument is a hash reference. (see next paragraph)
991              
992             value() with named arguments (key/value pairs) associates variables in the tree
993             with the value-arguments if the corresponging key matches the variable name.
994             (Can one say this any more complicated?) Since version 0.132, an
995             equivalent and valid syntax is to pass a single hash reference instead of a
996             list.
997              
998             Example: $tree->value(x => 1, y => 2, z => 3, t => 0) assigns the value 1 to
999             any occurrances of variables of the name "x", aso.
1000              
1001             If a variable in the tree has no value set (and no argument of value sets
1002             it temporarily), the call to value() returns undef.
1003              
1004             =cut
1005              
1006             sub value {
1007 4669     4669 1 16047 my $self = shift;
1008 4669 100       11504 my $args = ( @_ == 1 ? $_[0] : +{@_} );
1009              
1010 4669         9403 my $applied = $self->apply($args);
1011 4669 100       18485 return undef unless defined $applied;
1012 4668         18589 return $applied->value($args);
1013             }
1014              
1015             =head2 Method signature
1016              
1017             signature() returns a tree's signature.
1018              
1019             In the context of Math::Symbolic, signatures are the list of variables
1020             any given tree depends on. That means the tree "v*t+x" depends on the
1021             variables v, t, and x. Thus, applying signature() on the tree that would
1022             be parsed from above example yields the sorted list ('t', 'v', 'x').
1023              
1024             Constants do not depend on any variables and therefore return the empty list.
1025             Obviously, operators' dependencies vary.
1026              
1027             Math::Symbolic::Variable objects, however, may have a slightly more
1028             involved signature. By convention, Math::Symbolic variables depend on
1029             themselves. That means their signature contains their own name. But they
1030             can also depend on various other variables because variables themselves
1031             can be viewed as placeholders for more compicated terms. For example
1032             in mechanics, the acceleration of a particle depends on its mass and
1033             the sum of all forces acting on it. So the variable 'acceleration' would
1034             have the signature ('acceleration', 'force1', 'force2',..., 'mass', 'time').
1035              
1036             If you're just looking for a list of the names of all variables in the tree,
1037             you should use the explicit_signature() method instead.
1038              
1039             =cut
1040              
1041             sub signature {
1042 864     864 1 1183 my $self = shift;
1043 864         865 my %sig;
1044 864         2618 foreach my $o ( $self->descending_operands('all_vars') ) {
1045 1350         3967 $sig{$_} = undef for $o->signature();
1046             }
1047 864         4214 return sort keys %sig;
1048             }
1049              
1050             =head2 Method explicit_signature
1051              
1052             explicit_signature() returns a lexicographically sorted list of
1053             variable names in the tree.
1054              
1055             See also: signature().
1056              
1057             =cut
1058              
1059             sub explicit_signature {
1060 172     172 1 481 my $self = shift;
1061 172         189 my %sig;
1062 172         453 foreach my $o ( $self->descending_operands('all_vars') ) {
1063 330         1242 $sig{$_} = undef for $o->explicit_signature();
1064             }
1065 172         1452 return sort keys %sig;
1066             }
1067              
1068             1;
1069             __END__