use
5.006;
no
warnings
'recursion'
;
our
$VERSION
=
'0.613'
;
BEGIN {
*import
= \
&Math::Symbolic::Custom::Base::aggregate_import
}
our
$Aggregate_Export
= [
qw/
apply_derivatives
apply_constant_fold
mod_add_constant
mod_multiply_constant
/
];
sub
apply_derivatives {
my
$tree
=
shift
;
my
$n
=
shift
|| -1;
return
$tree
->descend(
in_place
=> 0,
before
=>
sub
{
my
$tree
=
shift
;
my
$ttype
=
$tree
->term_type();
if
(
$ttype
== T_CONSTANT ||
$ttype
== T_VARIABLE ) {
return
undef
;
}
elsif
(
$ttype
== T_OPERATOR ) {
my
$max_derivatives
=
$n
;
my
$type
=
$tree
->type();
while
(
$n
&& (
$type
== U_P_DERIVATIVE
or
$type
== U_T_DERIVATIVE )
)
{
my
$op
=
$Math::Symbolic::Operator::Op_Types
[
$type
];
my
$operands
=
$tree
->{operands};
my
$application
=
$op
->{application};
if
(
$type
== U_T_DERIVATIVE
and
$operands
->[0]->term_type() == T_VARIABLE )
{
my
@sig
=
$operands
->[0]->signature();
my
$name
=
$operands
->[1]->name();
if
(
(
grep
{
$_
eq
$name
}
@sig
) > 0
and not(
@sig
== 1
and
$sig
[0] eq
$name
)
)
{
return
undef
;
}
}
$tree
->replace(
$application
->(
@$operands
) );
return
undef
unless
$tree
->term_type() == T_OPERATOR;
$type
=
$tree
->type();
$n
--;
}
return
();
}
else
{
croak
"apply_derivatives called on invalid "
.
"tree type."
;
}
die
"Sanity check in apply_derivatives() should not "
.
"be reached."
;
},
);
}
sub
apply_constant_fold {
my
$tree
=
shift
;
my
$in_place
=
shift
;
return
$tree
->descend(
in_place
=>
$in_place
,
before
=>
sub
{
my
$tree
=
shift
;
if
(
$tree
->is_simple_constant() ) {
$tree
->replace(
$tree
->apply() )
unless
$tree
->term_type() == T_CONSTANT;
return
undef
;
}
return
undef
if
$tree
->term_type() == T_VARIABLE;
return
{
in_place
=> 1,
descend_into
=> [] };
}
);
return
$tree
;
}
sub
mod_add_constant {
my
$tree
=
shift
;
my
$constant
=
shift
;
return
$tree
if
not
$constant
;
$constant
=
$constant
->value()
if
ref
(
$constant
);
my
$tt
=
$tree
->term_type();
if
(
$tt
== T_CONSTANT) {
return
Math::Symbolic::Constant->new(
$tree
->{value}+
$constant
);
}
elsif
(
$tt
== T_OPERATOR) {
my
$type
=
$tree
->type();
if
(
$type
== B_SUM ||
$type
== B_DIFFERENCE) {
my
$ops
=
$tree
->{operands};
my
$const_op
;
if
(
$ops
->[0]->is_simple_constant()) {
$const_op
= 0;
}
elsif
(
$ops
->[1]->is_simple_constant()) {
$const_op
= 1;
}
if
(
defined
$const_op
) {
my
$value
=
$ops
->[
$const_op
]->value();
my
$other
=
$ops
->[(
$const_op
+1)%2];
if
(
$const_op
== 0) {
$value
+=
$constant
;
}
else
{
$value
=
$type
==B_SUM ?
$value
+
$constant
:
$value
-
$constant
;
}
if
(
$value
== 0) {
return
$other
if
$const_op
== 1 or
$type
== B_SUM;
return
Math::Symbolic::Constant->new(-
$other
->{value});
}
return
Math::Symbolic::Operator->new(
(
$type
== B_DIFFERENCE ?
'-'
:
'+'
),
$const_op
== 0
?(
$value
,
$other
)
:(
$other
,
$value
)
);
}
if
(
$ops
->[1]->term_type() == T_OPERATOR) {
my
$otype
=
$ops
->[1]->type();
if
(
$otype
== B_SUM ||
$otype
== B_DIFFERENCE) {
return
Math::Symbolic::Operator->new(
(
$type
== B_SUM ?
'+'
:
'-'
),
$ops
->[0],
$ops
->[1]->mod_add_constant(
$constant
)
);
}
}
else
{
return
Math::Symbolic::Operator->new(
(
$type
== B_SUM ?
'+'
:
'-'
),
$ops
->[0]->mod_add_constant(
$constant
),
$ops
->[1],
);
}
}
}
return
Math::Symbolic::Operator->new(
'+'
, Math::Symbolic::Constant->new(
$constant
),
$tree
);
}
sub
mod_multiply_constant {
my
$tree
=
shift
;
my
$constant
=
shift
;
return
$tree
if
not
defined
$constant
;
$constant
=
$constant
->value()
if
ref
(
$constant
);
return
$tree
if
$constant
== 1;
return
Math::Symbolic::Constant->zero()
if
$constant
== 0;
my
$tt
=
$tree
->term_type();
if
(
$tt
== T_CONSTANT) {
return
Math::Symbolic::Constant->new(
$tree
->{value}
*$constant
);
}
elsif
(
$tt
== T_OPERATOR) {
my
$type
=
$tree
->type();
if
(
$type
== B_PRODUCT ||
$type
== B_DIVISION) {
my
$ops
=
$tree
->{operands};
my
$const_op
;
if
(
$ops
->[0]->is_simple_constant()) {
$const_op
= 0;
}
elsif
(
$ops
->[1]->is_simple_constant()) {
$const_op
= 1;
}
if
(
defined
$const_op
) {
my
$value
=
$ops
->[
$const_op
]->value();
my
$other
=
$ops
->[(
$const_op
+1)%2];
if
(
$const_op
== 0) {
$value
*=
$constant
;
}
else
{
$value
=
$type
==B_PRODUCT ?
$value
*
$constant
:
$value
/
$constant
;
}
if
(
$value
== 1) {
return
$other
if
$const_op
== 1 or
$type
== B_PRODUCT;
return
Math::Symbolic::Constant->new(1/
$other
->{value});
}
return
Math::Symbolic::Operator->new(
(
$type
== B_DIVISION ?
'/'
:
'*'
),
$const_op
== 0
?(
$value
,
$other
)
:(
$other
,
$value
)
);
}
if
(
$ops
->[1]->term_type() == T_OPERATOR) {
my
$otype
=
$ops
->[1]->type();
if
(
$otype
== B_PRODUCT ||
$otype
== B_DIVISION) {
return
Math::Symbolic::Operator->new(
(
$type
== B_PRODUCT ?
'*'
:
'/'
),
$ops
->[0],
$ops
->[1]->mod_multiply_constant(
$constant
)
);
}
}
else
{
return
Math::Symbolic::Operator->new(
(
$type
== B_PRODUCT ?
'*'
:
'('
),
$ops
->[0]->mod_multiply_constant(
$constant
),
$ops
->[1],
);
}
}
}
return
Math::Symbolic::Operator->new(
'*'
, Math::Symbolic::Constant->new(
$constant
),
$tree
);
}
1;