The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
# Before `make install' is performed this script should be runnable with
# `make test'. After `make install' it should work as `perl Math-Taylor.t'

#########################
use strict;
use warnings;

use Test::More tests => 27;
BEGIN { use_ok('Math::Taylor') };

#########################

my $taylor;

###################
# Object creation

eval { $taylor = Math::Taylor->new() };
ok($@, 'Cannot create obj without function.');

eval { $taylor = Math::Taylor->new(function => 'x*2') };
ok(
  (not $@ and ref($taylor) eq 'Math::Taylor'),
  'Can create obj with string function.'
);

eval {
     my $obj = Math::Symbolic->parse_from_string('x*2');
     $taylor = Math::Taylor->new(function => $obj);
};
ok(
  (not $@ and ref($taylor) eq 'Math::Taylor'),
  'Can create obj with obj function.'
);

#############
# Accessors

ok(
  ref($taylor->function()) eq 'Math::Symbolic::Operator',
  'Accessor function() get'
);
ok(
  ref($taylor->function('2')) eq 'Math::Symbolic::Constant',
  'Accessor function() set'
);

ok(
  ($taylor->point() == 0),
  'Accessor point() get'
);
ok(
  $taylor->point('3') == 3,
  'Accessor function() set'
);
ok(
  $taylor->point() == 3,
  'Accessor point() get'
);

ok(
  ($taylor->variable()->to_string() eq 'x'),
  'Accessor variable() get'
);

eval { $taylor->variable('x') };
ok(
  $@,
  "Cannot set to variable which isn't contained in the function"
);

$taylor->function('x*2');
ok(
  ref($taylor->variable('x')) eq 'Math::Symbolic::Variable',
  'Accessor variable() set'
);



##################
# Cloning

$taylor = Math::Taylor->new(function => 'x*2', point => '3');
my $clone;
eval { $clone = $taylor->new() };
ok(
  (not $@ and ref($clone) eq 'Math::Taylor' and $clone->point() == 3),
  'Plain cloning.'
);

eval { $clone = $taylor->new(point => '4') };
ok(
  (not $@ and ref($clone) eq 'Math::Taylor' and $clone->point() == 4),
  'Cloning with attributes.'
);


########################
# taylor_poly

$taylor = Math::Taylor->new(function => 'x*2', point => '3');
my $poly;
eval { $poly = $taylor->taylor_poly(0) };
ok(
  !$@,
  "taylor_poly doesn't complain."
);
ok(
  defined($poly) and ref($poly) =~ /^Math::Symbolic/,
  "taylor_poly returns Math::Symbolic tree."
);

ok(
  $poly->value(x => 5) == 6,
  "taylor_poly(0)"
);

eval { $poly = $taylor->taylor_poly(1) };
ok(
  (!$@ and $poly->value(x => 5) == 6+2*5-6),
  "taylor_poly(1)"
);

eval { $poly = $taylor->taylor_poly(2) };
ok(
  (!$@ and $poly->value(x => 5) == 6+2*5-6+0),
  "taylor_poly(2)"
);


#############
# Remainders

$taylor = Math::Taylor->new(function => 'x^2*2', point => '-3');
my $rem;
eval { $rem = $taylor->remainder(0, 'theta1'); };
ok(
  (!$@ and defined($rem) and ref($rem) =~ /^Math::Symbolic/),
  'remainder() returns M::S tree'
);
ok(
  (grep {$_ eq 'theta1'} $rem->explicit_signature()),
  'remainder() contains range var'
);
eval { $rem = $taylor->remainder(); };
ok(
  (!$@),
  'remainder() without args'
);

my $rem2 = $taylor->remainder(1, 'theta');

my $val  = $rem->value(x => 2, theta => 0.3);
my $val2 = $rem2->value(x => 2, theta => 0.3);

use constant EP => 0.00001;
ok(
  (!$@ and $val <= EP()+$val2 and $val >= $val2-EP() ),
  'remainder(1, "theta") equivalent to remainder()'
);

$taylor->remainder_type('cauchy');
eval { $rem = $taylor->remainder(0, 'theta1'); };
ok(
  (!$@ and defined($rem) and ref($rem) =~ /^Math::Symbolic/),
  'remainder() returns M::S tree'
);
ok(
  (grep {$_ eq 'theta1'} $rem->explicit_signature()),
  'remainder() contains range var'
);
eval { $rem = $taylor->remainder(); };
ok(
  (!$@),
  'remainder() without args'
);

$rem2 = $taylor->remainder(1, 'theta');

$val  = $rem->value(x => 2, theta => 0.3);
$val2 = $rem2->value(x => 2, theta => 0.3);

ok(
  (!$@ and $val <= EP()+$val2 and $val >= $val2-EP() ),
  'remainder(1, "theta") equivalent to remainder()'
);