File Coverage

blib/lib/Math/Symbolic/Operator.pm
Criterion Covered Total %
statement 255 279 91.4
branch 154 194 79.3
condition 67 84 79.7
subroutine 23 23 100.0
pod 12 12 100.0
total 511 592 86.3


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   4337 use 5.006;
  23         91  
52 23     23   130 use strict;
  23         190  
  23         821  
53 23     23   146 use warnings;
  23         63  
  23         1330  
54 23     23   127 no warnings 'recursion';
  23         39  
  23         916  
55              
56 23     23   127 use Carp;
  23         2290  
  23         5789  
57              
58 23     23   134 use Math::Symbolic::ExportConstants qw/:all/;
  23         39  
  23         5347  
59 23     23   13309 use Math::Symbolic::Derivative qw//;
  23         74  
  23         797  
60              
61 23     23   147 use base 'Math::Symbolic::Base';
  23         46  
  23         28396  
62              
63             our $VERSION = '0.613';
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 9251     9251 1 30662 my $proto = shift;
383 9251   66     23938 my $class = ref($proto) || $proto;
384              
385 9251 100 100     29024 if ( @_ and not( ref( $_[0] ) eq 'HASH' ) ) {
386 2991         5516 my $symbol = shift;
387 2991         7193 my $type = $Op_Symbols{$symbol};
388 2991 50       6706 croak "Invalid operator type specified ($symbol)."
389             unless defined $type;
390 2991         13748 my $operands = [ @_[ 0 .. $Op_Types[$type]{arity} - 1 ] ];
391              
392 2991 50       11217 croak "Undefined operands not supported by "
393             . "Math::Symbolic::Operator objects."
394             if grep +( not defined($_) ), @$operands;
395              
396             @$operands =
397             map {
398 2991 100       5590 ref($_) =~ /^Math::Symbolic/
  5627         20993  
399             ? $_
400             : Math::Symbolic::parse_from_string($_)
401             } @$operands;
402              
403 2991         20183 return bless {
404             type => $type,
405             operands => $operands,
406             } => $class;
407             }
408              
409 6260         9405 my %args;
410 6260 100       11936 %args = %{ $_[0] } if @_;
  564         2431  
411             # and ref( $_[0] ) eq 'HASH';
412             # above condition isn't necessary since that'd otherwise have been
413             # the above branch.
414              
415 6260         10139 my $operands = [];
416 6260 100       12417 if ( ref $proto ) {
417 5613         7963 foreach ( @{ $proto->{operands} } ) {
  5613         11820  
418 9161         22137 push @$operands, $_->new();
419             }
420             }
421              
422 6260 100       24610 my $self = {
423             type => undef,
424             ( ref($proto) ? %$proto : () ),
425             operands => $operands,
426             %args,
427             };
428              
429 6260         12557 @{ $self->{operands} } =
430             map {
431 10108 100       30594 ref($_) =~ /^Math::Symbolic/
432             ? $_
433             : Math::Symbolic::parse_from_string($_)
434 6260         10638 } @{ $self->{operands} };
  6260         12661  
435              
436 6260         19668 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 2216     2216 1 3351 my $self = shift;
447 2216         6836 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 21559     21559 1 32662 my $self = shift;
459 21559 50       45554 $self->{type} = shift if @_;
460 21559         52944 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 30243 my $self = shift;
472 526         870 my $string_type = shift;
473 526 100 100     2168 $string_type = 'infix'
474             unless defined $string_type
475             and $string_type eq 'prefix';
476 23     23   223 no warnings 'recursion';
  23         73  
  23         77988  
477              
478 526         1036 my $string = '';
479 526 100       1024 if ( $string_type eq 'prefix' ) {
480 169         442 $string .= $self->_to_string_prefix();
481             }
482             else {
483 357         1179 $string .= $self->_to_string_infix();
484             }
485 526         3091 return $string;
486             }
487              
488             sub _to_string_infix {
489 357     357   600 my $self = shift;
490 357         947 my $op = $Op_Types[ $self->{type} ];
491              
492 357         716 my $op_str = $op->{infix_string};
493 357         534 my $string;
494 357 100       904 if ( $op->{arity} == 2 ) {
    50          
495 323         961 my $op1 = $self->{operands}[0]->term_type() == T_OPERATOR;
496 323         815 my $op2 = $self->{operands}[1]->term_type() == T_OPERATOR;
497              
498 323 100       677 if ( not defined $op_str ) {
499 11         29 $op_str = $op->{prefix_string};
500 11         27 $string = "$op_str(";
501             $string .= join( ', ',
502 11         51 map { $_->to_string('infix') } @{ $self->{operands} } );
  22         63  
  11         41  
503 11         36 $string .= ')';
504             }
505             else {
506             $string =
507             ( $op1 ? '(' : '' )
508             . $self->{operands}[0]->to_string('infix')
509             . ( $op1 ? ')' : '' )
510             . " $op_str "
511             . ( $op2 ? '(' : '' )
512 312 100       1034 . $self->{operands}[1]->to_string('infix')
    100          
    100          
    100          
513             . ( $op2 ? ')' : '' );
514             }
515             }
516             elsif ( $op->{arity} == 1 ) {
517 34         116 my $is_op1 = $self->{operands}[0]->term_type() == T_OPERATOR;
518 34 100       113 if ( not defined $op_str ) {
519 23         52 $op_str = $op->{prefix_string};
520             $string =
521 23         84 "$op_str(" . $self->{operands}[0]->to_string('infix') . ")";
522             }
523             else {
524             $string = "$op_str"
525             . ( $is_op1 ? '(' : '' )
526 11 100       86 . $self->{operands}[0]->to_string('infix')
    100          
527             . ( $is_op1 ? ')' : '' );
528             }
529             }
530             else {
531 0         0 $string = $self->_to_string_prefix();
532             }
533 357         1043 return $string;
534             }
535              
536             sub _to_string_prefix {
537 169     169   260 my $self = shift;
538 169         550 my $op = $Op_Types[ $self->{type} ];
539              
540 169         316 my $op_str = $op->{prefix_string};
541 169         381 my $string = "$op_str(";
542             $string .=
543 169         236 join( ', ', map { $_->to_string('prefix') } @{ $self->{operands} } );
  303         790  
  169         356  
544 169         531 $string .= ')';
545 169         394 return $string;
546             }
547              
548             =head2 Method term_type
549              
550             Returns the type of the term. ( T_OPERATOR )
551              
552             =cut
553              
554 30875     30875 1 58763 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 1593     1593 1 3528 my $self = shift;
582 1593         2506 my $dont_clone = shift;
583 1593 100       3134 $self = $self->new() unless $dont_clone;
584              
585 1593         2730 my $operands = $self->{operands};
586 1593         3086 my $op = $Op_Types[ $self->type() ];
587              
588             # simplify operands without cloning.
589 1593         3132 @$operands = map { $_->simplify(1) } @$operands;
  2563         7716  
590              
591 1593 100       3850 if ( $self->arity() == 2 ) {
    50          
592 970         1685 my $o1 = $operands->[0];
593 970         1555 my $o2 = $operands->[1];
594 970         2245 my $tt1 = $o1->term_type();
595 970         1973 my $tt2 = $o2->term_type();
596 970         1860 my $type = $self->type();
597              
598 970 100       5649 if ( $self->is_simple_constant() ) {
599 71         243 return $self->apply();
600             }
601              
602 899 100       5515 if ( $o1->is_identical($o2) ) {
603 17 100       63 if ( $type == B_PRODUCT ) {
    50          
    0          
    0          
604 12         90 my $two = Math::Symbolic::Constant->new(2);
605 12         50 return $self->new( '^', $o1, $two )->simplify(1);
606             }
607             elsif ( $type == B_SUM ) {
608 5         29 my $two = Math::Symbolic::Constant->new(2);
609 5         23 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 882 50 100     3540 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 882 100 100     2833 if ( $tt2 == T_CONSTANT
      66        
      100        
634             and $type == B_EXP
635             and ( $o2->value() == 1 or $o2->special() eq 'one' ) )
636             {
637 6         27 return $o1;
638             }
639              
640             # (a^b)^const = a^(const*b)
641 876 100 100     2926 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         55 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 865 100 100     3202 if ( $tt1 == T_CONSTANT or $tt2 == T_CONSTANT ) {
    100          
672 511 100       1213 my $const = ( $tt1 == T_CONSTANT ? $o1 : $o2 );
673 511 100       915 my $not_c = ( $tt1 == T_CONSTANT ? $o2 : $o1 );
674 511         846 my $constant_first = $tt1 == T_CONSTANT;
675              
676 511 100       8372 if ( $type == B_SUM ) {
677 20 100       65 return $not_c if $const->value() == 0;
678 16         94 return $not_c->mod_add_constant($const);
679             }
680            
681 491 100       1082 if ( $type == B_DIFFERENCE ) {
682 4 100       13 if (!$constant_first) {
683 2         18 my $value = $const->value();
684 2 50       11 return $not_c if $value == 0;
685 2         18 return $not_c->mod_add_constant(-$value);
686             }
687 2 50 33     7 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 489 100       1172 if ( $type == B_PRODUCT ) {
    100          
698 250 100       833 return $not_c if $const->value() == 1;
699 249 100       560 return Math::Symbolic::Constant->zero()
700             if $const->value == 0;
701              
702 229 100 100     480 if ( $not_c->term_type() == T_OPERATOR
    100 100        
      100        
      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       107 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         71 my $c_product = $not_c->new( '*', $const, $c )->apply();
713 20         105 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         17 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     109 return $not_c
730             if !$constant_first
731             and $const->value == 1;
732 31 50 33     96 return Math::Symbolic::Constant->new('#Inf')
733             if !$constant_first
734             and $const->value == 0;
735 31 50       101 return Math::Symbolic::Constant->zero()
736             if $const->value == 0;
737              
738             }
739             }
740             elsif ( $type == B_PRODUCT ) {
741 190 50 100     1053 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         23 return $self->new( '*', $o2, $o1 );
749             }
750             }
751              
752 788 100       1917 if ( $type == B_SUM ) {
753 31         81 my @ops;
754             my @const;
755 31         119 my @todo = ( $o1, $o2 );
756 31         80 my %vars;
757 31         112 while (@todo) {
758 98         160 my $this = shift @todo;
759              
760 98 100       267 if ( $this->term_type() == T_OPERATOR ) {
    100          
761 92         207 my $t = $this->type();
762 92 100       351 if ( $t == B_SUM ) {
    50          
    50          
    100          
763 18         40 push @todo, @{ $this->{operands} };
  18         63  
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 64         93 my ($o1, $o2) = @{$this->{operands}};
  64         171  
804 64         161 my $tl = $o1->term_type();
805 64         150 my $tr = $o2->term_type();
806            
807 64 50 66     320 if ($tl == T_VARIABLE and $tr == T_CONSTANT) {
    100 100        
808 0         0 $vars{$o1->name}+= $o2->value();
809             }
810             elsif ($tr == T_VARIABLE and $tl == T_CONSTANT) {
811 2         8 $vars{$o2->name}+= $o1->value();
812             }
813             else {
814 62         203 push @ops, $this;
815             }
816             }
817             else {
818 10         35 push @ops, $this;
819             }
820             }
821             elsif ( $this->term_type() == T_VARIABLE ) {
822 5         16 $vars{$this->name}++;
823             }
824             else {
825 1         3 push @const, $this->value();
826             }
827             }
828              
829 31         74 my @vars = ();
830 31         113 foreach (keys %vars) {
831 7         12 my $num = $vars{$_};
832 7 50       24 if (!$num) { next; }
  0         0  
833            
834 7 100       53 if ($num == 1) {
835 5         17 push @vars, Math::Symbolic::Variable->new($_);
836 5         13 next;
837             }
838 2         57 my $mul = Math::Symbolic::Operator->new(
839             '*',
840             Math::Symbolic::Constant->new(abs($num)),
841             Math::Symbolic::Variable->new($_)
842             );
843 2 100       8 push @ops, $num < 0
844             ? Math::Symbolic::Operator->new('neg', $mul)
845             : $mul;
846             }
847              
848 31         86 my $const = 0;
849 31         86 $const += $_ foreach @const;
850 31 100       84 if ( $const == 0 ) {
851 30         84 $const = shift @vars;
852             }
853             else {
854 1         3 $const = Math::Symbolic::Constant->new($const);
855             }
856              
857 31         76 foreach ( @vars ) {
858 1         3 $const = Math::Symbolic::Operator->new('+', $const, $_);
859             }
860            
861 31         96 @ops = map {$_->simplify(1)} @ops;
  74         250  
862 31         69 my @newops;
863 31 100       129 push @newops, $const if defined $const;
864 31         146 foreach my $out ( 0 .. $#ops ) {
865 74 100       216 next if not defined $ops[$out];
866 72         143 my $identical = 0;
867 72         195 foreach my $in ( 0 .. $#ops ) {
868 218 100 100     947 next if $in == $out or not defined $ops[$in];
869 143 100       1916 if ( $ops[$out]->is_identical( $ops[$in] ) ) {
870 2         4 $identical++;
871 2         5 $ops[$in] = undef;
872             }
873             }
874 72 100       219 if ( not $identical ) {
875 70         190 push @newops, $ops[$out];
876             }
877             else {
878 2         9 push @newops,
879             Math::Symbolic::Operator->new( '*', $identical + 1,
880             $ops[$out] );
881             }
882             }
883            
884 31         67 my $sumops;
885 31 50       104 if (@newops) {
886 31         63 $sumops = shift @newops;
887 31         223 $sumops += $_ foreach @newops;
888             }
889 0         0 else {return Math::Symbolic::Constant->zero()}
890              
891 31         973 return $sumops;
892             }
893             }
894             elsif ( $self->arity() == 1 ) {
895 623         1123 my $o = $operands->[0];
896 623         7712 my $tt = $o->term_type();
897 623         1114 my $type = $self->type();
898 623 100       1472 if ( $type == U_MINUS ) {
899 181 100       800 if ( $tt == T_CONSTANT ) {
    100          
900 2         11 return Math::Symbolic::Constant->new( -$o->value(), );
901             }
902             elsif ( $tt == T_OPERATOR ) {
903 172         450 my $inner_type = $o->type();
904 172 100       550 if ( $inner_type == U_MINUS ) {
    100          
905 3         49 return $o->{operands}[0];
906             }
907             elsif ( $inner_type == B_DIFFERENCE ) {
908 7         28 return $o->new( '-', @{$o->{operands}}[1,0] );
  7         21  
909             }
910             }
911             }
912             }
913              
914 1368         5338 return $self;
915             }
916              
917             =head2 Methods op1 and op2
918              
919             Returns first/second operand of the operator if it exists or undef.
920              
921             =cut
922              
923             sub op1 {
924 156 50   156 1 349 return $_[0]{operands}[0] if @{ $_[0]{operands} } >= 1;
  156         936  
925 0         0 return undef;
926             }
927              
928             sub op2 {
929 70 50   70 1 130 return $_[0]{operands}[1] if @{ $_[0]{operands} } >= 2;
  70         323  
930             }
931              
932             =head2 Method apply
933              
934             Applies the operation to its operands' value() and returns the result
935             as a constant (-object).
936              
937             Without arguments, all variables in the tree are required to have a value.
938             If any don't, the call to apply() returns undef.
939              
940             To (temorarily, for this single method call) assign values to
941             variables in the tree, you may provide key/value pairs of variable names
942             and values. Instead of passing a list of key/value pairs, you may also pass
943             a single hash reference containing the variable mappings.
944              
945             You usually want to call the value() instead of this.
946              
947             =cut
948              
949             sub apply {
950 4945     4945 1 7703 my $self = shift;
951 4945 100       10132 my $args = ( @_ == 1 ? $_[0] : +{ @_ } );
952 4945         11505 my $op_type = $self->type();
953 4945         9905 my $op = $Op_Types[$op_type];
954 4945         8555 my $operands = $self->{operands};
955 4945         9914 my $application = $op->{application};
956              
957 4945 100       10228 if ( ref($application) ne 'CODE' ) {
958 4938         9524 local @_;
959 4938         7450 local $@;
960 4938         9690 eval {
961             @_ = map {
962 4938         10021 my $v = $_->value($args);
  9067         31682  
963             (
964 9067 100       31737 defined $v
965             ? $v
966             : croak
967             "Undefined operand in Math::Symbolic::Operator->apply()"
968             )
969             } @$operands;
970             };
971 4938 100       11788 return undef if $@;
972 4936 50 66     19933 return undef if $op_type == B_DIVISION and $_[1] == 0;
973 4936         407551 my $result = eval $application;
974 4936 50       27551 die "Invalid operator application: $@" if $@;
975 4936 50       15748 die "Undefined result from operator application."
976             if not defined $result;
977              
978 4936         23581 return Math::Symbolic::Constant->new($result);
979             }
980             else {
981 7         35 return $application->(@$operands);
982             }
983             }
984              
985             =head2 Method value
986              
987             value() evaluates the Math::Symbolic tree to its numeric representation.
988              
989             value() without arguments requires that every variable in the tree contains
990             a defined value attribute. Please note that this refers to every variable
991             I, not just every named variable.
992              
993             value() with one argument sets the object's value if you're dealing with
994             Variables or Constants. In case of operators, a call with one argument will
995             assume that the argument is a hash reference. (see next paragraph)
996              
997             value() with named arguments (key/value pairs) associates variables in the tree
998             with the value-arguments if the corresponging key matches the variable name.
999             (Can one say this any more complicated?) Since version 0.132, an
1000             equivalent and valid syntax is to pass a single hash reference instead of a
1001             list.
1002              
1003             Example: $tree->value(x => 1, y => 2, z => 3, t => 0) assigns the value 1 to
1004             any occurrances of variables of the name "x", aso.
1005              
1006             If a variable in the tree has no value set (and no argument of value sets
1007             it temporarily), the call to value() returns undef.
1008              
1009             =cut
1010              
1011             sub value {
1012 4849     4849 1 20321 my $self = shift;
1013 4849 100       12012 my $args = ( @_ == 1 ? $_[0] : +{@_} );
1014              
1015 4849         12841 my $applied = $self->apply($args);
1016 4849 100       13020 return undef unless defined $applied;
1017 4848         14197 return $applied->value($args);
1018             }
1019              
1020             =head2 Method signature
1021              
1022             signature() returns a tree's signature.
1023              
1024             In the context of Math::Symbolic, signatures are the list of variables
1025             any given tree depends on. That means the tree "v*t+x" depends on the
1026             variables v, t, and x. Thus, applying signature() on the tree that would
1027             be parsed from above example yields the sorted list ('t', 'v', 'x').
1028              
1029             Constants do not depend on any variables and therefore return the empty list.
1030             Obviously, operators' dependencies vary.
1031              
1032             Math::Symbolic::Variable objects, however, may have a slightly more
1033             involved signature. By convention, Math::Symbolic variables depend on
1034             themselves. That means their signature contains their own name. But they
1035             can also depend on various other variables because variables themselves
1036             can be viewed as placeholders for more compicated terms. For example
1037             in mechanics, the acceleration of a particle depends on its mass and
1038             the sum of all forces acting on it. So the variable 'acceleration' would
1039             have the signature ('acceleration', 'force1', 'force2',..., 'mass', 'time').
1040              
1041             If you're just looking for a list of the names of all variables in the tree,
1042             you should use the explicit_signature() method instead.
1043              
1044             =cut
1045              
1046             sub signature {
1047 873     873 1 1419 my $self = shift;
1048 873         1405 my %sig;
1049 873         2282 foreach my $o ( $self->descending_operands('all_vars') ) {
1050 1368         3768 $sig{$_} = undef for $o->signature();
1051             }
1052 873         3390 return sort keys %sig;
1053             }
1054              
1055             =head2 Method explicit_signature
1056              
1057             explicit_signature() returns a lexicographically sorted list of
1058             variable names in the tree.
1059              
1060             See also: signature().
1061              
1062             =cut
1063              
1064             sub explicit_signature {
1065 172     172 1 306 my $self = shift;
1066 172         259 my %sig;
1067 172         427 foreach my $o ( $self->descending_operands('all_vars') ) {
1068 330         966 $sig{$_} = undef for $o->explicit_signature();
1069             }
1070 172         841 return sort keys %sig;
1071             }
1072              
1073             1;
1074             __END__