1# -*-perl-*-
2#
3# test some PDL core routines
4#
5
6use strict;
7use Test::More;
8
9BEGIN {
10    # if we've got this far in the tests then
11    # we can probably assume PDL::LiteF works!
12    #
13    eval {
14        require PDL::LiteF;
15    } or BAIL_OUT("PDL::LiteF failed: $@");
16    plan tests => 75;
17    PDL::LiteF->import;
18}
19$| = 1;
20
21sub tapprox ($$) {
22    my ( $a, $b ) = @_;
23    my $d = abs( $a - $b );
24    print "diff = [$d]\n";
25    return $d <= 0.0001;
26}
27
28my $a_long = sequence long, 10;
29my $a_dbl  = sequence 10;
30
31my $b_long = $a_long->slice('5');
32my $b_dbl  = $a_dbl->slice('5');
33
34my $c_long = $a_long->slice('4:7');
35my $c_dbl  = $a_dbl->slice('4:7');
36
37# test 'sclr' method
38#
39is $b_long->sclr, 5, "sclr test of 1-elem pdl (long)";
40is $c_long->sclr, 4, "sclr test of 3-elem pdl (long)";
41
42ok tapprox( $b_dbl->sclr, 5 ), "sclr test of 1-elem pdl (dbl)";
43ok tapprox( $c_dbl->sclr, 4 ), "sclr test of 3-elem pdl (dbl)";
44
45# switch multielement check on
46is( PDL->sclr({Check=>'barf'}), 2, "changed error mode of sclr" );
47
48eval '$c_long->sclr';
49like $@, qr/multielement piddle in 'sclr' call/, "sclr failed on multi-element piddle (long)";
50
51eval '$c_dbl->sclr';
52like $@, qr/multielement piddle in 'sclr' call/, "sclr failed on multi-element piddle (dbl)";
53
54# test reshape barfing with negative args
55#
56eval 'my $d_long = $a_long->reshape(0,-3);';
57like $@, qr/invalid dim size/, "reshape() failed with negative args (long)";
58
59eval 'my $d_dbl = $a_dbl->reshape(0,-3);';
60like $@, qr/invalid dim size/, "reshape() failed with negative args (dbl)";
61
62# test reshape with no args
63my ( $a, $b, $c );
64
65$a = ones 3,1,4;
66$b = $a->reshape;
67ok eq_array( [ $b->dims ], [3,4] ), "reshape()";
68
69# test reshape(-1) and squeeze
70$a = ones 3,1,4;
71$b = $a->reshape(-1);
72$c = $a->squeeze;
73ok eq_array( [ $b->dims ], [3,4] ), "reshape(-1)";
74ok all( $b == $c ), "squeeze";
75
76$c++; # check dataflow in reshaped PDL
77ok all( $b == $c ), "dataflow"; # should flow back to b
78ok all( $a == 2 ), "dataflow";
79
80our $d = pdl(5); # zero dim piddle and reshape/squeeze
81ok $d->reshape(-1)->ndims==0, "reshape(-1) on 0-dim PDL gives 0-dim PDL";
82ok $d->reshape(1)->ndims==1, "reshape(1) on 0-dim PDL gives 1-dim PDL";
83ok $d->reshape(1)->reshape(-1)->ndims==0, "reshape(-1) on 1-dim, 1-element PDL gives 0-dim PDL";
84
85# reshape test related to bug SF#398 "$pdl->hdr items are lost after $pdl->reshape"
86$c = ones(25);
87$c->hdr->{demo} = "yes";
88is($c->hdr->{demo}, "yes", "hdr before reshape");
89$c->reshape(5,5);
90is($c->hdr->{demo}, "yes", "hdr after reshape");
91
92
93
94# test topdl
95
96isa_ok( PDL->topdl(1),       "PDL", "topdl(1) returns a piddle" );
97isa_ok( PDL->topdl([1,2,3]), "PDL", "topdl([1,2,3]) returns a piddle" );
98isa_ok( PDL->topdl(1,2,3),   "PDL", "topdl(1,2,3) returns a piddle" );
99$a=PDL->topdl(1,2,3);
100ok (($a->nelem == 3  and  all($a == pdl(1,2,3))), "topdl(1,2,3) returns a 3-piddle containing (1,2,3)");
101
102
103# test $PDL::undefval support in pdl (bug #886263)
104#
105is $PDL::undefval, 0, "default value of $PDL::undefval is 0";
106
107$a = [ [ 2, undef ], [3, 4 ] ];
108$b = pdl( $a );
109$c = pdl( [ 2, 0, 3, 4 ] )->reshape(2,2);
110ok all( $b == $c ), "undef converted to 0 (dbl)";
111ok eq_array( $a, [[2,undef],[3,4]] ), "pdl() has not changed input array";
112
113$b = pdl( long, $a );
114$c = pdl( long, [ 2, 0, 3, 4 ] )->reshape(2,2);
115ok all( $b == $c ), "undef converted to 0 (long)";
116
117do {
118    local($PDL::undefval) = -999;
119    $a = [ [ 2, undef ], [3, 4 ] ];
120    $b = pdl( $a );
121    $c = pdl( [ 2, -999, 3, 4 ] )->reshape(2,2);
122    ok all( $b == $c ), "undef converted to -999 (dbl)";
123
124    $b = pdl( long, $a );
125    $c = pdl( long, [ 2, -999, 3, 4 ] )->reshape(2,2);
126    ok all( $b == $c ), "undef converted to -999 (long)";
127} while(0);
128
129##############
130# Funky constructor cases
131
132# pdl of a pdl
133$a = pdl(pdl(5));
134ok all( $a== pdl(5)), "pdl() can piddlify a piddle";
135
136TODO: {
137   local $TODO = 'Known_problems bug sf.net #3011879' if ($PDL::Config{SKIP_KNOWN_PROBLEMS} or exists $ENV{SKIP_KNOWN_PROBLEMS});
138
139   # pdl of mixed-dim pdls: pad within a dimension
140   $a = pdl( zeroes(5), ones(3) );
141   ok all($a == pdl([0,0,0,0,0],[1,1,1,0,0])),"Piddlifying two piddles concatenates them and pads to length" or diag("a=$a\n");
142}
143
144# pdl of mixed-dim pdls: pad a whole dimension
145$a = pdl( [[9,9],[8,8]], xvals(3)+1 );
146ok all($a == pdl([[[9,9],[8,8],[0,0]] , [[1,0],[2,0],[3,0]] ])),"can concatenate mixed-dim piddles" or diag("a=$a\n");
147
148# pdl of mixed-dim pdls: a hairier case
149$c = pdl [1], pdl[2,3,4], pdl[5];
150ok all($c == pdl([[[1,0,0],[0,0,0]],[[2,3,4],[5,0,0]]])),"Can concatenate mixed-dim piddles: hairy case" or diag("c=$c\n");
151
152# same thing, with undefval set differently
153do {
154    local($PDL::undefval) = 99;
155    $c = pdl [1], pdl[2,3,4], pdl[5];
156    ok all($c == pdl([[[1,99,99],[99,99,99]],[[2,3,4],[5,99,99]]])), "undefval works for padding" or diag("c=$c\n");;
157} while(0);
158
159# empty pdl cases
160eval {$a = zeroes(2,0,1);};
161ok(!$@,"zeroes accepts empty PDL specification");
162
163eval { $b = pdl($a,sequence(2,0,1)); };
164ok((!$@ and all(pdl($b->dims) == pdl(2,0,1,2))), "concatenating two empties gives an empty");
165
166eval { $b = pdl($a,sequence(2,1,1)); };
167ok((!$@ and all(pdl($b->dims) == pdl(2,1,1,2))), "concatenating an empty and a nonempty treats the empty as a filler");
168
169eval { $b = pdl($a,5) };
170ok((!$@ and all(pdl($b->dims)==pdl(2,1,1,2))), "concatenating an empty and a scalar on the right works");
171ok( all($b==pdl([[[0,0]]],[[[5,0]]])), "concatenating an empty and a scalar on the right gives the right answer");
172
173eval { $b = pdl(5,$a) };
174ok((!$@ and all(pdl($b->dims)==pdl(2,1,1,2))), "concatenating an empty and a scalar on the left works");
175ok( all($b==pdl([[[5,0]]],[[[0,0]]])), "concatenating an empty and a scalar on the left gives the right answer");
176
177# end
178
179# cat problems
180eval {cat(1, pdl(1,2,3), {}, 6)};
181ok ($@ ne '', 'cat barfs on non-piddle arguments');
182like ($@, qr/Arguments 0, 2 and 3 are not piddles/, 'cat correctly identifies non-piddle arguments');
183$@ = '';
184eval {cat(1, pdl(1,2,3))};
185like($@, qr/Argument 0 is not a piddle/, 'cat uses good grammar when discussing non-piddles');
186$@ = '';
187
188my $two_dim_array = cat(pdl(1,2), pdl(1,2));
189eval {cat(pdl(1,2,3,4,5), $two_dim_array, pdl(1,2,3,4,5), pdl(1,2,3))};
190ok ($@ ne '', 'cat barfs on mismatched piddles');
191like($@, qr/The dimensions of arguments 1 and 3 do not match/
192	, 'cat identifies all piddles with differing dimensions');
193like ($@, qr/\(argument 0\)/, 'cat identifies the first actual piddle in the arg list');
194$@ = '';
195eval {cat(pdl(1,2,3), pdl(1,2))};
196like($@, qr/The dimensions of argument 1 do not match/
197	, 'cat uses good grammar when discussing piddle dimension mismatches');
198$@ = '';
199eval {cat(1, pdl(1,2,3), $two_dim_array, 4, {}, pdl(4,5,6), pdl(7))};
200ok ($@ ne '', 'cat barfs combined screw-ups');
201like($@, qr/Arguments 0, 3 and 4 are not piddles/
202	, 'cat properly identifies non-piddles in combined screw-ups');
203like($@, qr/arguments 2 and 6 do not match/
204	, 'cat properly identifies piddles with mismatched dimensions in combined screw-ups');
205like($@, qr/\(argument 1\)/,
206	'cat properly identifies the first actual piddle in combined screw-ups');
207$@ = '';
208
209eval {$a = cat(pdl(1),pdl(2,3));};
210ok(!$@, 'cat(pdl(1),pdl(2,3)) succeeds');
211ok( ($a->ndims==2 and $a->dim(0)==2 and $a->dim(1)==2), 'weird cat case has the right shape');
212ok( all( $a == pdl([1,1],[2,3]) ), "cat does the right thing with catting a 0-pdl and 2-pdl together");
213$@='';
214
215my $by=xvals(byte,5)+253;
216my $so=xvals(short,5)+32766;
217my $lo=xvals(long,5)+32766;
218my $fl=float(xvals(5)+0.2);
219my @list = ($lo,$so,$fl,$by);
220my $c2 = cat(@list);
221is($c2->type,'float','concatentating different datatypes returns the highest type');
222my $i=0;
223map{ ok(all($_==$list[$i]),"cat/dog symmetry for values ($i)"); $i++; }$c2->dog;
224
225# new_or_inplace
226$a = sequence(byte,5);
227
228
229$b = $a->new_or_inplace;
230ok( all($b==$a) && ($b->get_datatype ==  $a->get_datatype), "new_or_inplace with no pref returns something like the orig.");
231
232$b++;
233ok(all($b!=$a),"new_or_inplace with no inplace flag returns something disconnected from the orig.");
234
235$b = $a->new_or_inplace("float,long");
236ok($b->type eq 'float',"new_or_inplace returns the first type in case of no match");
237
238$b = $a->inplace->new_or_inplace;
239$b++;
240ok(all($b==$a),"new_or_inplace returns the original thing if inplace is set");
241ok(!($b->is_inplace),"new_or_inplace clears the inplace flag");
242
243# check reshape and dims.  While we're at it, check null & empty creation too.
244my $null = null;
245my $empty = zeroes(0);
246ok($empty->nelem==0,"you can make an empty PDL with zeroes(0)");
247ok("$empty" =~ m/Empty/, "an empty PDL prints 'Empty'");
248
249ok($null->info =~ /^PDL->null$/, "null piddle's info is 'PDL->null'");
250my $mt_info = $empty->info;
251$mt_info =~m/\[([\d,]+)\]/;
252my $mt_info_dims = pdl("$1");
253ok(any($mt_info_dims==0), "empty piddle's info contains a 0 dimension");
254ok($null->isnull && $null->isempty, "a null piddle is both null and empty");
255ok(!$empty->isnull && $empty->isempty, "an empty piddle is empty but not null");
256
257$a = short pdl(3,4,5,6);
258eval { $a->reshape(2,2);};
259ok(!$@,"reshape succeeded in the normal case");
260ok( ( $a->ndims==2 and $a->dim(0)==2 and $a->dim(1)==2 ), "reshape did the right thing");
261ok(all($a == short pdl([[3,4],[5,6]])), "reshape moved the elements to the right place");
262
263$b = $a->slice(":,:");
264eval { $b->reshape(4); };
265ok( $@ !~ m/Can\'t/, "reshape doesn't fail on a PDL with a parent" );
266
267