File Coverage

blib/lib/Math/Symbolic/Base.pm
Criterion Covered Total %
statement 169 204 82.8
branch 73 106 68.8
condition 29 47 61.7
subroutine 34 42 80.9
pod 13 14 92.8
total 318 413 77.0


line stmt bran cond sub pod time code
1              
2             =encoding utf8
3              
4             =head1 NAME
5              
6             Math::Symbolic::Base - Base class for symbols in symbolic calculations
7              
8             =head1 SYNOPSIS
9              
10             use Math::Symbolic::Base;
11              
12             =head1 DESCRIPTION
13              
14             This is a base class for all Math::Symbolic::* terms such as
15             Math::Symbolic::Operator, Math::Symbolic::Variable and
16             Math::Symbolic::Constant objects.
17              
18             =head2 EXPORT
19              
20             None by default.
21              
22             =cut
23              
24             package Math::Symbolic::Base;
25              
26 23     23   591 use 5.006;
  23         83  
  23         1286  
27 23     23   136 use strict;
  23         51  
  23         823  
28 23     23   129 use warnings;
  23         44  
  23         2548  
29 23     23   118 no warnings 'recursion';
  23         47  
  23         862  
30              
31 23     23   124 use Carp;
  23         42  
  23         6179  
32              
33             use overload
34             "+" => \&_overload_addition,
35             "-" => \&_overload_subtraction,
36             "*" => \&_overload_multiplication,
37             "/" => \&_overload_division,
38             "**" => \&_overload_exponentiation,
39             "sqrt" => \&_overload_sqrt,
40             "log" => \&_overload_log,
41             "exp" => \&_overload_exp,
42             "sin" => \&_overload_sin,
43             "cos" => \&_overload_cos,
44 65     65   7484 '""' => sub { $_[0]->to_string() },
45 0     0   0 "0+" => sub { $_[0]->value() },
46 23     23   50756 "bool" => sub { $_[0]->value() };
  23     34   36118  
  23         536  
  34         112  
47              
48 23     23   4841 use Math::Symbolic::ExportConstants qw/:all/;
  23         51  
  23         95880  
49              
50             our $VERSION = '0.612';
51             our $AUTOLOAD;
52              
53             =head1 METHODS
54              
55             =cut
56              
57             =head2 Method to_string
58              
59             Default method for stringification just returns the object's value.
60              
61             =cut
62              
63             sub to_string {
64 0     0 1 0 my $self = shift;
65 0         0 return $self->value();
66             }
67              
68             =head2 Method value
69              
70             value() evaluates the Math::Symbolic tree to its numeric representation.
71              
72             value() without arguments requires that every variable in the tree contains
73             a defined value attribute. Please note that this refers to every variable
74             I, not just every named variable.
75              
76             value() with one argument sets the object's value (in case of a variable or
77             constant).
78              
79             value() with named arguments (key/value pairs) associates variables in the tree
80             with the value-arguments if the corresponging key matches the variable name.
81             (Can one say this any more complicated?) Since version 0.132, an alternative
82             syntax is to pass a single hash reference.
83              
84             Example: $tree->value(x => 1, y => 2, z => 3, t => 0) assigns the value 1 to
85             any occurrances of variables of the name "x", aso.
86              
87             If a variable in the tree has no value set (and no argument of value sets
88             it temporarily), the call to value() returns undef.
89              
90             =cut
91              
92             sub value {
93 0     0 1 0 croak "This is a method stub from Math::Symbolic::Base. Implement me.";
94             }
95              
96             =head2 Method signature
97              
98             signature() returns a tree's signature.
99              
100             In the context of Math::Symbolic, signatures are the list of variables
101             any given tree depends on. That means the tree "v*t+x" depends on the
102             variables v, t, and x. Thus, applying signature() on the tree that would
103             be parsed from above example yields the sorted list ('t', 'v', 'x').
104              
105             Constants do not depend on any variables and therefore return the empty list.
106             Obviously, operators' dependencies vary.
107              
108             Math::Symbolic::Variable objects, however, may have a slightly more
109             involved signature. By convention, Math::Symbolic variables depend on
110             themselves. That means their signature contains their own name. But they
111             can also depend on various other variables because variables themselves
112             can be viewed as placeholders for more compicated terms. For example
113             in mechanics, the acceleration of a particle depends on its mass and
114             the sum of all forces acting on it. So the variable 'acceleration' would
115             have the signature ('acceleration', 'force1', 'force2',..., 'mass', 'time').
116              
117             If you're just looking for a list of the names of all variables in the tree,
118             you should use the explicit_signature() method instead.
119              
120             =cut
121              
122             sub signature {
123 0     0 1 0 croak "signature() implemented in the inheriting classes.";
124             }
125              
126             =head2 Method explicit_signature
127              
128             explicit_signature() returns a lexicographically sorted list of
129             variable names in the tree.
130              
131             See also: signature().
132              
133             =cut
134              
135             sub explicit_signature {
136 0     0 1 0 croak "explicit_signature() implemented in the inheriting classes.";
137             }
138              
139             =head2 Method set_signature
140              
141             set_signature expects any number of variable identifiers as arguments.
142             It sets a variable's signature to this list of identifiers.
143              
144             =cut
145              
146             sub set_signature {
147 0     0 1 0 croak "Cannot set signature of non-Variable Math::Symbolic tree element.";
148             }
149              
150             =head2 Method implement
151              
152             implement() works in-place!
153              
154             Takes key/value pairs as arguments. The keys are to be variable names
155             and the values must be valid Math::Symbolic trees. All occurrances
156             of the variables will be replaced with their implementation.
157              
158             =cut
159              
160             sub implement {
161 28     28 1 45 my $self = shift;
162 28         93 my %args = @_;
163              
164             return $self->descend(
165             in_place => 1,
166             after => sub {
167 810     810   925 my $tree = shift;
168 810         1945 my $ttype = $tree->term_type();
169 810 100 66     2817 if ( $ttype == T_VARIABLE ) {
    50          
170 191         452 my $name = $tree->name();
171 191 100 66     930 if ( exists $args{$name}
172             and defined $args{$name} )
173             {
174 187 50       431 $args{$name} =
175             Math::Symbolic::parse_from_string( $args{$name} )
176             unless ref( $args{$name} );
177 187         415 $tree->replace( $args{$name} );
178             }
179             }
180             elsif ( $ttype == T_OPERATOR or $ttype == T_CONSTANT ) {
181             }
182             else {
183 0         0 croak "'implement' called on invalid term " . "type.";
184             }
185             },
186             operand_finder => sub {
187 499     499   1055 return $_[0]->descending_operands('all_vars');
188             },
189 28         256 );
190             }
191              
192             =head2 Method replace
193              
194             First argument must be a valid Math::Symbolic tree.
195              
196             replace() modifies the object it is called on in-place in that it
197             replaces it with its first argument. Doing that, it retains the original
198             object reference. This destroys the object it is called on.
199              
200             However, this also means that you can create recursive trees of objects if
201             the new tree is to contain the old tree. So make sure you clone the old tree
202             using the new() method before using it in the replacement tree or you will
203             end up with a program that eats your memory fast.
204              
205             =cut
206              
207             sub replace {
208 12814     12814 1 15684 my $tree = shift;
209 12814         13925 my $new = shift;
210 12814         60187 %$tree = %$new;
211 12814         34805 bless $tree => ref $new;
212 12814         32682 return $tree;
213             }
214              
215             =head2 fill_in_vars
216              
217             This method returns a modified copy of the tree it was called on.
218              
219             It walks the tree and replaces all variables whose value attribute is
220             defined (either done at the time of object creation or using set_value())
221             with the corresponding constant objects. Variables whose value is
222             not defined are unaffected. Take, for example, the following code:
223              
224             $tree = parse_from_string('a*b+a*c');
225             $tree->set_value(a => 4, c => 10); # value of b still not defined.
226             print $tree->fill_in_vars();
227             # prints "(4 * b) + (4 * 10)"
228              
229             =cut
230              
231             sub fill_in_vars {
232 1     1 1 3 my $self = shift;
233             return $self->descend(
234             in_place => 0,
235             before => sub {
236 3     3   4 my $term = shift;
237 3 100 100     10 if ( $term->term_type() == T_VARIABLE and defined $term->{value} )
238             {
239 1         6 $term->replace(
240             Math::Symbolic::Constant->new( $term->{value} ) );
241             }
242 3         7 return ();
243             },
244 1         7 );
245             }
246              
247             =head2 Method simplify
248              
249             Minimum method for term simpilification just clones.
250              
251             =cut
252              
253             sub simplify {
254 1132     1132 1 1497 my $self = shift;
255 1132         8901 return $self->new();
256             }
257              
258             =head2 Method descending_operands
259              
260             When called on an operator, descending_operands tries hard to determine
261             which operands to descend into. (Which usually means all operands.)
262             A list of these is returned.
263              
264             When called on a constant or a variable, it returns the empty list.
265              
266             Of course, some routines may have to descend into different branches of the
267             Math::Symbolic tree, but this routine returns the default operands.
268              
269             The first argument to this method may control its behaviour. If it is any of
270             the following key-words, behaviour is modified accordingly:
271              
272             default -- obvious. Use default heuristics.
273            
274             These are all supersets of 'default':
275             all -- returns ALL operands. Use with caution.
276             all_vars -- returns all operands that may contain vars.
277              
278             =cut
279              
280             sub descending_operands {
281 8925     8925 1 10989 my $tree = shift;
282 8925         21494 my $ttype = $tree->term_type();
283              
284 8925 50 33     46327 if ( $ttype == T_CONSTANT or $ttype == T_VARIABLE ) {
    50          
285 0         0 return ();
286             }
287             elsif ( $ttype == T_OPERATOR ) {
288 8925   100     27652 my $action = shift || 'default';
289 8925         27416 my $type = $tree->type();
290              
291 8925 50       21713 if ( $action eq 'all' ) {
    100          
292 0         0 return @{ $tree->{operands} };
  0         0  
293             }
294             elsif ( $action eq 'all_vars' ) {
295 1535         1582 return @{ $tree->{operands} };
  1535         6038  
296             }
297             else { # default
298 7390 100 66     28268 if ( $type == U_P_DERIVATIVE
299             or $type == U_T_DERIVATIVE )
300             {
301 11         30 return $tree->{operands}[0];
302             }
303             else {
304 7379         7575 return @{ $tree->{operands} };
  7379         27992  
305             }
306             }
307             }
308             else {
309 0         0 croak "'descending_operands' called on invalid term type.";
310             }
311 0         0 die "Sanity check in 'descending_operands'. Should not be reached.";
312             }
313              
314             =head2 Method descend
315              
316             The method takes named arguments (key/value pairs).
317             descend() descends (Who would have guessed?) into the Math::Symbolic tree
318             recursively and for each node, it calls code references with a copy of
319             the current node as argument. The copy may be modified and will be used for
320             construction of the returned tree. The automatic copying behaviour may be
321             turned off.
322              
323             Returns a (modified) copy of the original tree. If in-place modification is
324             turned on, the returned tree will not be a copy.
325              
326             Available parameters are:
327              
328             =over 2
329              
330             =item before
331              
332             A code reference to be used as a callback that will be invoked before descent.
333             Depending on whether or not the "in_place" option is set, the callback will
334             be passed a copy of the current node (default) or the original node itself.
335              
336             The callback may modify the tree node and the modified node will be used to
337             construct descend()'s return value.
338              
339             The return value of this callback describes the way descend() handles the
340             descent into the current node's operands.
341              
342             If it returns the empty list, the (possibly modified) copy of the current
343             that was passed to the callback is used as the return value of descend(),
344             but the recursive descent is continued for all of the current node's operands
345             which may or may not be modified by the callback. The "after" callback will
346             be called on the node after descent into the operands. (This is the
347             normal behavior.)
348              
349             If the callback returns undef, the descent is stopped for the current branch
350             and an exact copy of the current branch's children will be used for
351             descend()'s return value. The "after" callback will be called immediately.
352              
353             If the callback returns a list of integers, these numbers are assumed to
354             be the indexes of the current node's operands that are to be descended into.
355             That means if the callback returns (1), descend will be called for the
356             second operand and only the second. All other children/operands will be cloned.
357             As usual, the "after" callback will be called after descent.
358              
359             Any other return lists will lead to hard-to-debug errors. Tough luck.
360              
361             Returning a hash reference from the callback allows for complete control
362             over the descend() routine. The hash may contain the following elements:
363              
364             =over 2
365              
366             =item operands
367              
368             This is a referenced array that will be put in place of the previous
369             operands. It is the callback's job to make sure the number of operands stays
370             correct. The "operands" entry is evaluated I the "descend_into"
371             entry.
372              
373             =item descend_into
374              
375             This is a referenced array of integers and references. The integers are
376             assumed to be indices of the array of operands. Returning (1) results in
377             descent into the second operand and only the second.
378              
379             References are assumed to be operands to descend into. descend() will be
380             directly called on them.
381              
382             If the array is empty, descend() will act just as if
383             an empty list had been returned.
384              
385             =item in_place
386              
387             Boolean indicating whether or not to modify the operands in-place or not.
388             If this is true, descend() will be called with the "in_place => 1" parameter.
389             If false, it will be called with "in_place => 0" instead.
390             Defaults to false. (Cloning)
391              
392             This does not affect the call to the "after" callback but only the descent
393             into operands.
394              
395             =item skip_after
396              
397             If this option exists and is set to true, the "after" callback will not be
398             invoked. This only applies to the current node, not to its children/operands.
399              
400             =back
401              
402             The list of options may grow in future versions.
403              
404             =item after
405              
406             This is a code reference which will be invoked as a callback after the descent
407             into the operands.
408              
409             =item in_place
410              
411             Controls whether or not to modify the current tree node in-place. Defaults to
412             false - cloning.
413              
414             =item operand_finder
415              
416             This option controls how the descend routine chooses which operands to
417             recurse into by default. That means it controls which operands descend()
418             recurses into if the 'before' routine returned the empty list or if
419             no 'before' routine was specified.
420              
421             The option may either be a code reference or a string. If it is a code
422             reference, this code reference will be called with the current node as
423             argument. If it is a string, the method with that name will be called
424             on the current node object.
425              
426             By default, descend() calls the 'descending_operands()' method on the current
427             node to determine the operands to descend into.
428              
429             =back
430              
431             =cut
432              
433             sub descend {
434 13783     13783 1 32345 my ( $tree, %args ) = @_;
435 13783 100 66     73130 $tree = $tree->new()
436             unless exists $args{in_place}
437             and $args{in_place};
438              
439 13783         14700 my @opt;
440              
441             # Will be used at several locations inside this routine.
442             my $operand_finder = sub {
443 7889 100   7889   14499 if ( exists $args{operand_finder} ) {
444 499         583 my $op_f = $args{operand_finder};
445 499 50       1114 return $tree->$op_f() if not ref $op_f;
446 499 50       1087 croak "Invalid 'operand_finder' option passed to "
447             . "descend() routine."
448             if not ref($op_f) eq 'CODE';
449 499         814 return $op_f->($tree);
450             }
451             else {
452 7390         15719 return $tree->descending_operands();
453             }
454 13783         50693 };
455              
456 13783 100       34735 if ( exists $args{before} ) {
457 12963 50       34173 croak "'before' parameter to descend() must be code reference."
458             unless ref( $args{before} ) eq 'CODE';
459 12963         39112 @opt = $args{before}->($tree);
460             }
461 13783 50 66     39379 if ( exists $args{after} and ref( $args{after} ) ne 'CODE' ) {
462 0         0 croak "'after' parameter to descend() must be code reference.";
463             }
464              
465 13783 100 100     43930 my $has_control = ( @opt == 1 && ref( $opt[0] ) eq 'HASH' ? 1 : 0 );
466              
467 13783         37804 my $ttype = $tree->term_type();
468              
469             # Do nothing!
470 13783 100 33     37499 if ( $ttype != T_OPERATOR ) { }
    100 0        
    100          
    50          
    0          
471              
472             # Fine control!
473             elsif ($has_control) {
474 35         48 my $opt = $opt[0];
475 35         104 my %new_args = %args;
476 35 100       99 $new_args{in_place} = $opt->{in_place}
477             if exists $opt->{in_place};
478              
479 35 50       86 if ( exists $opt->{operands} ) {
480 0 0       0 croak "'operands' return value of 'begin' callback\n"
481             . "in descend() must be array reference."
482             unless ref( $opt->{operands} ) eq 'ARRAY';
483              
484 0         0 $tree->{operands} = $opt->{operands};
485             }
486              
487 35 50       80 if ( exists $opt->{descend_into} ) {
488 35 50       156 croak "'descend_into' return value of 'begin'\n"
489             . "callback in descend() must be array reference."
490             unless ref( $opt->{descend_into} ) eq 'ARRAY';
491              
492 35         99 $opt->{descend_into} = [ $operand_finder->() ]
493 35 100       41 if @{ $opt->{descend_into} } == 0;
494              
495 35         42 foreach ( @{ $opt->{descend_into} } ) {
  35         83  
496 68 50       142 if ( ref $_ ) {
497 68         198 $_->replace( $_->descend(%new_args) );
498             }
499             else {
500 0         0 $tree->{operands}[$_] =
501             $tree->{operands}[$_]->descend(%new_args);
502             }
503             }
504             }
505             }
506              
507             # descend into all operands.
508             elsif ( @opt == 0 ) {
509 7886         13254 foreach ( $operand_finder->() ) {
510 12484         37384 $_->replace( $_->descend(%args) );
511             }
512             }
513              
514             # Do nothing.
515 0         0 elsif ( @opt == 1 and not defined( $opt[0] ) ) {
516             }
517              
518             # Descend into indexed operands
519             elsif ( @opt >= 1 and not grep { $_ !~ /^[+-]?\d+$/ } @opt ) {
520 0         0 foreach (@opt) {
521 0         0 $tree->{operands}[$_] = $tree->{operands}[$_]->descend(%args);
522             }
523             }
524              
525             # Error!
526             else {
527 0         0 croak "Invalid return list from descend() 'before' callback.";
528             }
529              
530             # skip the after callback?
531 13783 100 33     39584 if (
      66        
532             exists $args{after}
533             and not($has_control
534             and exists $opt[0]{skip_after}
535             and $opt[0]{skip_after} )
536             )
537             {
538 820         1466 $args{after}->($tree);
539             }
540              
541 13783         82865 return $tree;
542             }
543              
544             =head2 Method term_type
545              
546             Returns the type of the term. This is a stub to be overridden.
547              
548             =cut
549              
550             sub term_type {
551 0     0 1 0 croak "term_type not defined for " . __PACKAGE__;
552             }
553              
554             =head2 Method set_value
555              
556             set_value() returns the tree it modifies, but acts in-place on the
557             Math::Symbolic tree it was called on.
558              
559             set_value() requires named arguments (key/value pairs) that associate
560             variable names of variables in the tree with the value-arguments if the
561             corresponging key matches the variable name.
562             (Can one say this any more complicated?) Since version 0.132, an alternative
563             syntax is to pass a single hash reference to the method.
564              
565             Example: $tree->set_value(x => 1, y => 2, z => 3, t => 0) assigns the value 1
566             to any occurrances of variables of the name "x", aso.
567              
568             As opposed to value(), set_value() assigns to the variables I
569             and does not evaluate the tree.
570              
571             When called on constants, set_value() sets their value to its first
572             argument, but only if there is only one argument.
573              
574             =cut
575              
576             sub set_value {
577 2     2 1 514 my ( $self, %args );
578 2 50       13 if ( @_ == 1 ) {
    100          
579 0         0 return();
580             }
581             elsif ( @_ == 2 ) {
582 1         6 $self = shift;
583 1 50       6 croak "Invalid arguments to method set_value()"
584             unless ref $_[0] eq 'HASH';
585 1         3 %args = %{ $_[0] };
  1         7  
586             }
587             else {
588 1         5 ( $self, %args ) = @_;
589             }
590              
591 2         11 my $ttype = $self->term_type();
592 2 50       8 if ( $ttype == T_CONSTANT ) {
593 0 0       0 return $self unless @_ == 2;
594 0         0 my $value = $_[1];
595 0 0       0 $self->{value} = $value if defined $value;
596 0         0 return $self;
597             }
598              
599             $self->descend(
600             in_place => 1,
601             after => sub {
602 10     10   13 my $tree = shift;
603 10         26 my $ttype = $tree->term_type();
604 10 100 100     62 if ( $ttype == T_OPERATOR or $ttype == T_CONSTANT ) {
    50          
605             }
606             elsif ( $ttype == T_VARIABLE ) {
607 3 50       18 $tree->{value} = $args{ $tree->{name} }
608             if exists $args{ $tree->{name} };
609             }
610             else {
611 0         0 croak "'set_value' called on invalid term " . "type.";
612             }
613             },
614 2         18 );
615              
616 2         17 return $self;
617             }
618              
619             =begin comment
620              
621             Since version 0.102, there are several overloaded operators. The overloaded
622             interface is documented below. For more info, please have a look at the
623             Math::Symbolic man page.
624              
625             =end comment
626              
627             =cut
628              
629             sub _overload_make_object {
630 397     397   551 my $operand = shift;
631 397 100       2433 unless ( ref($operand) =~ /^Math::Symbolic/ ) {
632 20 100       129 if ( not defined $operand ) {
    100          
633 1         2 return $operand;
634             }
635             elsif ( $operand !~ /^\s*\d+\s*$/ ) {
636 5         35 $operand = Math::Symbolic::parse_from_string($operand);
637             }
638             else {
639 14         66 $operand = Math::Symbolic::Constant->new($operand);
640             }
641             }
642 396         885 return $operand;
643             }
644              
645             sub _overload_addition {
646 85     85   947 my ( $obj, $operand, $reverse ) = @_;
647 85         240 $operand = _overload_make_object($operand);
648 85 100 66     282 return $obj if not defined $operand and $reverse;
649 84 50       196 ( $obj, $operand ) = ( $operand, $obj ) if $reverse;
650 84         371 my $n_obj = Math::Symbolic::Operator->new( '+', $obj, $operand );
651 84         473 return $n_obj;
652             }
653              
654             sub _overload_subtraction {
655 38     38   80 my ( $obj, $operand, $reverse ) = @_;
656 38         79 $operand = _overload_make_object($operand);
657 38 50 33     113 return Math::Symbolic::Operator->new( 'neg', $obj )
658             if not defined $operand
659             and $reverse;
660 38 100       92 ( $obj, $operand ) = ( $operand, $obj ) if $reverse;
661 38         158 my $n_obj = Math::Symbolic::Operator->new( '-', $obj, $operand );
662 38         204 return $n_obj;
663             }
664              
665             sub _overload_multiplication {
666 258     258   822 my ( $obj, $operand, $reverse ) = @_;
667 258         580 $operand = _overload_make_object($operand);
668 258 50       651 ( $obj, $operand ) = ( $operand, $obj ) if $reverse;
669 258         916 my $n_obj = Math::Symbolic::Operator->new( '*', $obj, $operand );
670 258         983 return $n_obj;
671             }
672              
673             sub _overload_division {
674 12     12   41 my ( $obj, $operand, $reverse ) = @_;
675 12         37 $operand = _overload_make_object($operand);
676 12 50       35 ( $obj, $operand ) = ( $operand, $obj ) if $reverse;
677 12         55 my $n_obj = Math::Symbolic::Operator->new( '/', $obj, $operand );
678 12         68 return $n_obj;
679             }
680              
681             sub _overload_exponentiation {
682 4     4   17 my ( $obj, $operand, $reverse ) = @_;
683 4         13 $operand = _overload_make_object($operand);
684 4 100       14 ( $obj, $operand ) = ( $operand, $obj ) if $reverse;
685 4         16 my $n_obj = Math::Symbolic::Operator->new( '^', $obj, $operand );
686 4         26 return $n_obj;
687             }
688              
689             sub _overload_sqrt {
690 1     1   4 my ( $obj, undef, $reverse ) = @_;
691 1         6 my $n_obj =
692             Math::Symbolic::Operator->new( '^', $obj,
693             Math::Symbolic::Constant->new(0.5) );
694 1         8 return $n_obj;
695             }
696              
697             sub _overload_exp {
698 1     1   3 my ( $obj, undef, $reverse ) = @_;
699 1         6 my $n_obj =
700             Math::Symbolic::Operator->new( '^', Math::Symbolic::Constant->euler(),
701             $obj, );
702 1         5 return $n_obj;
703             }
704              
705             sub _overload_log {
706 1     1   4 my ( $obj, undef, $reverse ) = @_;
707 1         6 my $n_obj =
708             Math::Symbolic::Operator->new( 'log', Math::Symbolic::Constant->euler(),
709             $obj, );
710 1         8 return $n_obj;
711             }
712              
713             sub _overload_sin {
714 2     2   29 my ( $obj, undef, $reverse ) = @_;
715 2         15 my $n_obj = Math::Symbolic::Operator->new( 'sin', $obj );
716 2         11 return $n_obj;
717             }
718              
719             sub _overload_cos {
720 1     1   4 my ( $obj, undef, $reverse ) = @_;
721 1         4 my $n_obj = Math::Symbolic::Operator->new( 'cos', $obj );
722 1         7 return $n_obj;
723             }
724              
725             =begin comment
726              
727             The following AUTOLOAD mechanism delegates all method calls that aren't found
728             in the normal Math::Symbolic inheritance tree and that start with
729             'is_', 'test_', 'contains_', 'apply_', 'mod_', or 'to_' to the
730             Math::Symbolic::Custom class.
731              
732             The 'is_' and 'test_' "namespaces" are intended for methods that test a
733             tree on whether or not it has certain characteristics that define a group.
734             Eg.: 'is_polynomial'
735              
736             The 'contains_' prefix is intended for tests as well.
737              
738             The 'apply_' and 'mod_' prefixes are intended for modifications to the tree
739             itself. Eg.: 'apply_derivatives'
740              
741             The 'to_' prefix is intended for output / conversion related routines.
742              
743             =end comment
744              
745             =cut
746              
747             sub AUTOLOAD {
748 2493     2493   37041 my $call = $AUTOLOAD;
749 2493         16174 $call =~ s/.*\:\:(\w+)$/$1/;
750 2493 50       10758 if ( $call =~ /^((?:apply|mod|is|test|contains|to)_\w+)/ ) {
751 2493         4824 my $method = $1;
752 2493         14100 my $ref = Math::Symbolic::Custom->can($method);
753 2493 50       5532 if ( defined $ref ) {
754 2493         10541 goto &$ref;
755             }
756             else {
757 0         0 my $obj = $_[0];
758 0         0 my $class = ref $obj;
759 0         0 croak "Invalid method '$call' called on Math::Symbolic "
760             ."tree. Tree was of type '$class'";
761             }
762             }
763             else {
764 0         0 my $obj = $_[0];
765 0         0 my $class = ref $obj;
766 0         0 croak "Invalid method '$call' called on Math::Symbolic "
767             ."tree. Tree was of type '$class'";
768             }
769             }
770              
771             =begin comment
772              
773             We override the UNIVERSAL::can routine to reflect method delegations.
774              
775             =end comment
776              
777             =cut
778              
779             sub can {
780 9     9 0 31 my $obj = shift;
781 9         16 my $method = shift;
782              
783 9         103 my $sub = $obj->SUPER::can($method);
784 9 100       34 return $sub if defined $sub;
785              
786 6         96 return Math::Symbolic::Custom->can($method);
787             }
788              
789             # to make AUTOLOAD happy: (because it would otherwise try to delegate DESTROY)
790 0     0     sub DESTROY { }
791              
792             1;
793             __END__