File Coverage

blib/lib/Types/PDL.pm
Criterion Covered Total %
statement 58 58 100.0
branch 17 18 94.4
condition 4 4 100.0
subroutine 12 12 100.0
pod n/a
total 91 92 98.9


line stmt bran cond sub pod time code
1             package Types::PDL;
2              
3             # ABSTRACT: PDL types using Type::Tiny
4              
5 10     10   1797692 use 5.010;
  10         72  
6              
7 10     10   43 use strict;
  10         16  
  10         153  
8 10     10   38 use warnings;
  10         14  
  10         350  
9              
10             our $VERSION = '0.03';
11              
12 10     10   45 use Carp;
  10         14  
  10         688  
13              
14 10         93 use Type::Library -base,
15             -declare => qw[
16             Piddle
17             Piddle0D
18             Piddle1D
19             Piddle2D
20             Piddle3D
21              
22             PiddleFromAny
23 10     10   3814 ];
  10         190797  
24              
25              
26 10     10   19416 use Types::Standard -types, 'is_Int';
  10         405661  
  10         97  
27 10     10   43314 use Type::Utils;
  10         40245  
  10         84  
28 10     10   16309 use Type::TinyX::Facets;
  10         18652  
  10         94  
29 10     10   6119 use String::Errf 'errf';
  10         221926  
  10         62  
30 10     10   2834 use B qw(perlstring);
  10         18  
  10         7924  
31              
32              
33             facet 'empty', sub {
34             my ( $o, $var ) = @_;
35             return unless exists $o->{empty};
36             errf '%{not}s%{var}s->isempty',
37             { var => $var, not => ( !!delete( $o->{empty} ) ? '' : '!' ) };
38             };
39              
40             facet 'null', sub {
41             my ( $o, $var ) = @_;
42             return unless exists $o->{null};
43             errf '%{not}s%{var}s->isnull',
44             { var => $var, not => ( !!delete( $o->{null} ) ? '' : '!' ) };
45             };
46              
47             facet ndims => sub {
48             my ( $o, $var ) = @_;
49              
50             my %o = map { ( $_ => delete $o->{$_} ) }
51             grep { exists $o->{$_} } qw[ ndims ndims_min ndims_max ];
52              
53             return unless keys %o;
54              
55             croak( "'$_' must be an integer\n" )
56             for grep { !is_Int( $o{$_} ) } keys %o;
57              
58              
59             if ( exists $o{ndims_max} and exists $o{ndims_min} ) {
60              
61             if ( $o{ndims_max} < $o{ndims_min} ) {
62             croak( "'ndims_min' must be <= 'ndims_max'\n" );
63             }
64              
65             elsif ( $o{ndims_min} == $o{ndims_max} ) {
66              
67             croak(
68             "cannot mix 'ndims' facet with either 'ndims_min' or 'ndims_max'\n"
69             ) if exists $o{ndims};
70              
71             $o{ndims} = delete $o{ndims_min};
72             delete $o{ndims_max};
73             }
74             }
75              
76             my @code;
77              
78             if ( exists $o{ndims_max} or exists $o{ndims_min} ) {
79              
80             if ( exists $o{ndims_min} ) {
81              
82             push @code, errf '%{var}s->ndims >= %{value}i',
83             {
84             var => $var,
85             value => delete $o{ndims_min} };
86             }
87              
88             if ( exists $o{ndims_max} ) {
89              
90             push @code, errf '%{var}s->ndims <= %{value}i',
91             {
92             var => $var,
93             value => delete $o{ndims_max} };
94             }
95             }
96              
97             elsif ( exists $o{ndims} ) {
98              
99             push @code, errf '%{var}s->ndims == %{value}i',
100             { var => $var, value => delete $o{ndims} };
101             }
102              
103             else {
104             return;
105             }
106              
107             croak( "cannot mix 'ndims' facet with either 'ndims_min' or 'ndims_max'\n" )
108             if keys %o;
109              
110             return join( ' and ', @code );
111             };
112              
113              
114             facet 'type', sub {
115             my ( $o, $var ) = @_;
116             return unless exists $o->{type};
117             my $type = eval { PDL::Type->new( delete $o->{type} )->ioname };
118             croak( "type must be a valid type name or a PDL::Type object: $@\n" )
119             if $@;
120              
121             errf '%{var}s->type->ioname eq q[%{type}s]',
122             { var => $var, type => $type };
123             };
124              
125             facet 'shape', sub {
126             my ( $o, $var ) = @_;
127             return unless exists $o->{shape};
128              
129             my $shape = delete $o->{shape};
130              
131             croak( "shape must be a string or an arrayref of specifications" )
132             unless 'ARRAY' eq ref $shape or ! ref $shape;
133              
134             errf q|join( ',', %{var}s->dims) =~ qr/%{regexp}s/x|,
135             { var => $var, regexp => _mk_shape_regexp( $shape ) };
136             };
137              
138              
139             facetize qw[ empty null ndims type shape ], class_type Piddle, { class => 'PDL' };
140              
141             facetize qw[ null type ], declare Piddle0D, as Piddle [ ndims => 0 ];
142              
143             facetize qw[ empty null type shape ], declare Piddle1D, as Piddle [ ndims => 1 ];
144              
145             facetize qw[ empty null type shape ], declare Piddle2D, as Piddle [ ndims => 2 ];
146              
147             facetize qw[ empty null type shape ], declare Piddle3D, as Piddle [ ndims => 3 ];
148              
149             declare_coercion PiddleFromAny, to_type Piddle, from Any, q[ do { local $@;
150             require PDL::Core;
151             my $new = eval { PDL::Core::topdl( $_ ) };
152             $@ ? $_ : $new
153             }
154             ];
155              
156              
157             sub _mk_shape_regexp {
158              
159 51     51   76376 my $spec = shift;
160              
161             # positive integer
162 51         62 my $int = q/(?:[0123456789]+)/;
163              
164 51         392 my $re = qr/
165             \s*(?:
166             (?:
167             (? $int )
168             |
169             (? X | : ) )
170             (?:
171             (?[*+?])
172             | (?:\{
173             (?$int)
174             (?:(?,) (?$int)? )?
175             \}
176             )
177             )?
178             )
179             \s*
180             /x;
181              
182 51         77 my @spec;
183              
184 51 100       102 if ( !ref $spec ) {
185 10     10   3757 push @spec, { %+ } while $spec =~ /\G$re,?/gc;
  10         3098  
  10         2976  
  35         1075  
186 35 100 100     635 croak( "error in spec starting HERE ==>",
      100        
187             substr( $spec, pos( $spec ) || 0 ), "<\n" )
188             if ( pos( $spec ) || 0 ) != length( $spec );
189             }
190             else {
191              
192             @spec = map {
193 16 50       29 croak( "error parsing spec: >$_<\n" )
  58         330  
194             unless /^$re$/;
195 58         386 +{ %+ }
196             } @$spec;
197             }
198              
199              
200 45         51 my @shape;
201              
202 45         84 for my $spec ( @spec ) {
203              
204 141         145 my $extent;
205              
206 141 100       196 if ( defined $spec->{int} ) {
207 119         135 $extent = $spec->{int};
208 119 100       418 croak( "extent cannot be zero" )
209             if ( $extent += 0 ) == 0;
210             }
211             else {
212 22         27 $extent = $int;
213             }
214              
215 140         199 my $res = "(?:${extent},?)";
216              
217              
218 140 100       234 if ( defined $spec->{quant} ) {
    100          
219 16         26 $res .= $spec->{quant};
220             }
221             elsif ( defined $spec->{min} ) {
222              
223 10         20 $res .= '{' . $spec->{min};
224              
225 10 100       17 $res .= ',' if defined $spec->{comma};
226 10 100       17 $res .= $spec->{max} if defined $spec->{max};
227 10         10 $res .= '}';
228             }
229              
230 140         217 push @shape, $res;
231             }
232              
233             # this must be a string!
234 44         257 return '^' . join( '', @shape ) . '$';
235             }
236              
237              
238              
239             1;
240              
241             #
242             # This file is part of Types-PDL
243             #
244             # This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
245             #
246             # This is free software, licensed under:
247             #
248             # The GNU General Public License, Version 3, June 2007
249             #
250              
251             =pod
252              
253             =head1 NAME
254              
255             Types::PDL - PDL types using Type::Tiny
256              
257             =head1 VERSION
258              
259             version 0.03
260              
261             =head1 SYNOPSIS
262              
263             use Types::PDL -types;
264             use Type::Params qw[ validate ];
265             use PDL;
266              
267             validate( [ pdl ], Piddle );
268              
269             =head1 DESCRIPTION
270              
271             This module provides L compatible types for L.
272              
273             =head2 Types
274              
275             Types which accept parameters (see L) will list them.
276              
277             =head3 C
278              
279             Allows an object blessed into the class C, e.g.
280              
281             validate( [pdl], Piddle );
282              
283             It accepts the following parameters:
284              
285             null
286             empty
287             ndims
288             ndims_min
289             ndims_max
290             shape
291             type
292              
293             =head3 C
294              
295             Allows an object blessed into the class C with C = 0.
296             It accepts the following parameters:
297              
298             null
299             type
300              
301             =head3 C
302              
303             Allows an object blessed into the class C with C = 1.
304             It accepts the following parameters:
305              
306             null
307             empty
308             shape
309             type
310              
311             =head3 C
312              
313             Allows an object blessed into the class C with C = 2.
314             It accepts the following parameters:
315              
316             null
317             empty
318             shape
319             type
320              
321             =head3 C
322              
323             Allows an object blessed into the class C with C = 3.
324             It accepts the following parameters:
325              
326             null
327             empty
328             shape
329             type
330              
331             =head2 Coercions
332              
333             The following coercions are provided, and may be applied via a type
334             object's L or
335             L methods, e.g.
336              
337             Piddle->plus_coercions( PiddleFromAny );
338              
339             =head3 C
340              
341             Uses L to coerce the value into a piddle.
342              
343             =head2 Parameters
344              
345             Some types take optional parameters which add additional constraints
346             on the object. For example, to indicate that only empty piddles are
347             accepted:
348              
349             validate( [pdl], Piddle[ empty => 1 ] );
350              
351             The available parameters are:
352              
353             =head3 C
354              
355             This accepts a boolean value; if true the piddle must be empty
356             (i.e. the C method returns true), if false, it must not be
357             empty.
358              
359             =head3 C
360              
361             This accepts a boolean value; if true the piddle must be a null
362             piddle, if false, it must not be null.
363              
364             =head3 C
365              
366             This specifies a fixed number of dimensions which the piddle must
367             have. Don't mix use this with C or C.
368              
369             =head3 C
370              
371             The minimum number of dimensions the piddle may have. Don't specify
372             this with C.
373              
374             =head3 C
375              
376             The maximum number of dimensions the piddle may have. Don't specify
377             this with C.
378              
379             =head3 C
380              
381             The shape of the piddle.
382              
383             The value is a list of specifications for dimensions, expressed either
384             as elements in a Perl array or as comma-delimited fields in a string.
385              
386             The specifications are reminiscent of regular expressions. A specification
387             is composed of an extent size followed by an optional quantifier indicating
388             the number of dimensions it should match.
389              
390             Extent sizes may be
391              
392             =over
393              
394             =item 1
395              
396             A non-zero positive integer representing the extent of the dimension.
397              
398             =item 2
399              
400             The strings C or C<:> indicating that any extent is accepted for that dimension
401              
402             =back
403              
404             Quantifiers may be
405              
406             * Match 0 or more times
407             + Match 1 or more times
408             ? Match 1 or 0 times
409             {n} Match exactly n times
410             {n,} Match at least n times
411             {n,m} Match at least n but not more than m times
412              
413             Here are some example specifications and the shapes they might match (in the match, C means any extent):
414              
415             2,2 => (2,2)
416             3,3,3 => (3,3,3)
417             3{3} => (3,3,3)
418             3{2,3} => (3,3), (3,3,3)
419             1,X => (1,X)
420             1,X+ => (1,X), (1,X,X), (1,X,X,X), ...
421             1,X{1,} => (1,X), (1,X,X), (1,X,X,X), ...
422             1,X?,3 => (1,X,3), (1,3)
423             1,2,X* => (1,2), (1,2,X), (1,2,X,X), ...
424             1,2,3*,5 => (1,2,5), (1,2,3,5), (1,2,3,3,5), ...
425              
426             =head3 C
427              
428             The type of the piddle. The value may be a L object or a
429             string containing the name of a type (e.g., C). For a complete
430             list of types, run this command:
431              
432             perl -MPDL::Types=mapfld,ppdefs \
433             -E 'say mapfld( $_ => 'ppsym' => 'ioname' ) for ppdefs'
434              
435             =head1 BUGS AND LIMITATIONS
436              
437             You can make new bug reports, and view existing ones, through the
438             web interface at L.
439              
440             =head1 AUTHOR
441              
442             Diab Jerius
443              
444             =head1 COPYRIGHT AND LICENSE
445              
446             This software is Copyright (c) 2017 by Smithsonian Astrophysical Observatory.
447              
448             This is free software, licensed under:
449              
450             The GNU General Public License, Version 3, June 2007
451              
452             =cut
453              
454             __END__