The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!perl
use strict;
use warnings;

use Test::More tests => 34;

BEGIN {
	use_ok('Math::Symbolic');
}

if ($ENV{TEST_YAPP_PARSER}) {
	require Math::Symbolic::Parser::Yapp;
	$Math::Symbolic::Parser = Math::Symbolic::Parser::Yapp->new();
}

use Math::Symbolic::ExportConstants qw/:all/;

my $var = Math::Symbolic::Variable->new();
my $a   = $var->new( 'x' => 10 );

print "Vars: x=" . $a->value() . " (Value is optional)\n\n";

print "Expression: x * 2 + 1, x / 2 - 1, x * (2+1)\n\n";

my ( $first, $second, $third );

$@ = undef;
eval <<'HERE';
$first  = $a * 2 + 1;    # x*2 + 1
HERE
ok( !$@, 'overloaded multiplication and addition' );

my $str = $first->to_string();
$str =~ s/\s+//g;
ok(
    $str eq '(x*2)+1' || $str eq '1+(2*x)',
    'Correct result of overloaded *,+',
);

ok( $first->value() == 21, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$second  = $a / 2 - 1;    # x*2 + 1
HERE
ok( !$@, 'overloaded division and subtraction' );

$str = $second->to_string();
$str =~ s/\s+//g;
ok( $str eq '(x/2)-1', 'Correct result of overloaded /,-', );

ok( $second->value() == 4, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$third = $a * "2 + 1";  # x*3
HERE
ok( !$@, 'overloaded multiplication involving auto-parsing' );

$str = $third->to_string();
$str =~ s/\s+//g;
ok(
    $str      eq 'x*(1+2)'
      || $str eq 'x*(2+1)'
      || $str eq '(2+1)*x'
      || $str eq '(1+2)*x',
    'Correct result of overloaded * involving auto-parsing',
);

ok( $third->value() == 30, 'Result evaluates to the correct number' );

my $fourth;
$@ = undef;
eval <<'HERE';
$fourth = 2 ** ($third/$a);
HERE
ok( !$@, 'overloaded ** w/ constant recognition and M::S::Operators' );

ok( $fourth->value() == 2**3, 'Result evaluates to the correct number' );

my $fifth;
$@ = undef;
eval <<'HERE';
$fifth = $fourth ** $fourth;
HERE
ok( !$@, 'overloaded ** w/ two M::S::Operators' );

ok( $fifth->value() == 8**8, 'Result evaluates to the correct number' );

my $sixth;
$@ = undef;
eval <<'HERE';
$sixth = sqrt($third*$third);
HERE
ok( !$@, 'overloaded sqrt, * w/ M::S::Operators' );
ok( $sixth->value() == 30, 'Result evaluates to the correct number' );

my $seventh;
$@ = undef;
eval <<'HERE';
$seventh = -exp(Math::Symbolic::Constant->zero());
HERE
ok( !$@, 'overloaded unary minus, exp w/ M::S::Constant' );

ok( $seventh->value() == -1, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$seventh = log(Math::Symbolic::Constant->one());
HERE
ok( !$@, 'overloaded log w/ M::S::Constant' );

ok( $seventh->value() == 0, 'Result evaluates to the correct number' );

ok( ( $seventh ? 0 : 1 ), 'automatic boolean conversion (Test1)' );

ok( ( $second ? 1 : 0 ), 'automatic boolean conversion (Test2)' );

$@ = undef;
eval <<'HERE';
$seventh = cos(sin(Math::Symbolic::Constant->zero()));
HERE
ok( !$@, 'overloaded sin, cos w/ M::S::Constant' );

ok( $seventh->value() == 1, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$seventh += 2;
HERE
ok( !$@, 'overloaded += w/ M::S::Constant' );

ok( $seventh->value() == 3, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$seventh -= 2;
HERE
ok( !$@, 'overloaded -= w/ M::S::Constant' );

ok( $seventh->value() == 1, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$seventh *= 2;
HERE
ok( !$@, 'overloaded *= w/ M::S::Constant' );

ok( $seventh->value() == 2, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$seventh /= 2;
HERE
ok( !$@, 'overloaded /= w/ M::S::Constant' );

ok( $seventh->value() == 1, 'Result evaluates to the correct number' );

$@ = undef;
eval <<'HERE';
$seventh += 2;
$seventh **= 2;
HERE
ok( !$@, 'overloaded **= w/ M::S::Constant' );

ok( $seventh->value() == 9, 'Result evaluates to the correct number' );

print "prefix notation and evaluation:\n";
print $first->to_string('prefix') . " = " . $first->value() . "\n\n";
print $second->to_string('prefix') . " = " . $second->value() . "\n\n";

print "Now, we derive this partially to x: (prefix again)\n";

my $n_tree = Math::Symbolic::Operator->new(
    {
        type     => U_P_DERIVATIVE,
        operands => [ $first, $a ],
    }
);
my $n_tree2 = Math::Symbolic::Operator->new(
    {
        type     => U_P_DERIVATIVE,
        operands => [ $second, $a ],
    }
);
my $n_tree3 = Math::Symbolic::Operator->new(
    {
        type     => U_P_DERIVATIVE,
        operands => [ $third, $a ],
    }
);

print $n_tree->to_string('prefix') . " = " . $n_tree->value() . "\n\n";
print $n_tree2->to_string('prefix') . " = " . $n_tree2->value() . "\n\n";
print $n_tree3->to_string('prefix') . " = " . $n_tree3->value() . "\n\n";