File Coverage

blib/lib/List/Insertion.pm
Criterion Covered Total %
statement 135 207 65.2
branch 20 64 31.2
condition 20 29 68.9
subroutine 24 25 96.0
pod 1 1 100.0
total 200 326 61.3


line stmt bran cond sub pod time code
1             package List::Insertion;
2              
3              
4 1     1   127568 use strict;
  1         2  
  1         35  
5 1     1   5 use warnings;
  1         1  
  1         41  
6              
7 1     1   487 use Template::Plex;
  1         35133  
  1         41  
8 1     1   583 use Data::Combination;
  1         922  
  1         133  
9              
10              
11             our $VERSION = 'v0.2.1';
12              
13             sub make_search;
14              
15             sub import {
16              
17 8     8   846 shift;
18 8         22 my @import=@_;
19              
20             # Generate subs based on import options
21 8         30 my ($package)=caller;
22              
23             # Import make search if requested
24             #
25 8 100 100     71 if(@import==1 and grep /make_search/, @import){
26 1     1   11 no strict 'refs';
  1         2  
  1         98  
27 1         3 *{$package."::make_search"}=\&make_search;
  1         6  
28 1         33 return;
29             }
30            
31             # Otherwise assume we have a list of specifications
32 7         13 my @spec;
33 7         29 push @spec, (Data::Combination::combinations $_)->@* for @import;
34            
35 1     1   8 no strict 'refs';
  1         2  
  1         914  
36 7         811 for my $spec(@spec){
37 13   100     57 $spec->{prefix}//="search";
38 13   50     32 $spec->{type}//="string";
39 13   100     38 $spec->{duplicate}//="left";
40 13   33     65 $spec->{package}//=$package;
41              
42 13         58 my ($sub,$code)=make_search $spec;
43 13 50       50 *{$package."::".$spec->{name}}=$sub if $sub;
  13         3195  
44             #say STDERR $code;
45             }
46             }
47              
48              
49              
50             my $template_base=
51             '
52             my \$middle;
53             my \$lower;
54             my \$upper;
55              
56             sub {
57             package $package;
58             my (\$key, \$array)=\@_;
59             \$lower = 0;
60             \$upper = \@\$array;
61             return 0 unless \$upper;
62              
63             use integer;
64             # TODO: Run in eval for accessor fall back
65             #
66             # local \$_;
67             while(\$lower<\$upper){
68             \$middle=(\$upper+\$lower)>>1;
69             (\$key $condition->{$fields{type}}{$fields{duplicate}} \$array->[\$middle]$accessor)
70             $update->{$fields{duplicate}}
71             }
72             \$lower;
73             }
74             ';
75              
76             my %condition=(
77             string=>{
78             left=>'le',
79             right=>'ge',
80             },
81             numeric=>{
82             left=>'<=',
83             right=>'>='
84             },
85              
86             );
87              
88              
89             my %update=(
90             left=>
91             '
92             ? ($upper=$middle)
93             : ($lower=$middle+1)
94             ',
95              
96             right=>
97              
98             '
99             ? ($lower=$middle+1)
100             : ($upper=$middle)
101             '
102             );
103            
104              
105              
106             # Make a binary search optimised for types and avoid sub routine callbacks
107             #
108             sub make_search {
109 16     16 1 881 my ($options)=@_;
110              
111             # Ensure at least a default value for the required fields
112             #
113 16   50     43 $options->{duplicate}//="left";
114 16   50     38 $options->{type}//="string";
115 16   100     61 $options->{accessor}//="";
116 16   100     82 $options->{prefix}//="search";
117 16   66     1978 $options->{package}//=caller;
118              
119             # Attempt to normalise values
120             #
121 16         37 $options->{duplicate}=~s/lesser/left/;
122 16         28 $options->{duplicate}=~s/greater/right/;
123              
124 16         48 $options->{type}=~s/pv/string/i;
125 16         43 $options->{type}=~s/nv/numeric/i;
126 16         35 $options->{type}=~s/int/numeric/i;
127              
128 16   33     120 $options->{name}//="$options->{prefix}_$options->{type}_$options->{duplicate}";
129              
130              
131             #Check fields values are supported
132              
133            
134             die "Unsupported value for duplicate field: $options->{duplicate }. Must be left or right"
135 16 50       1502 unless $options->{duplicate }=~/^(left|right)$/;
136             die "Unsupported value for type field: $options->{type}. Must be string, pv, nv or int"
137 16 50       95 unless $options->{type}=~/^(string|numeric)$/;
138             die "Unsupported value for type field: $options->{accessor}. Must be post dereference/method call ->..."
139 16 50 66     63 unless $options->{accessor} eq "" or $options->{accessor}=~/^->/;
140              
141 16         167 my $template=Template::Plex->load( [$template_base], {condition=>\%condition, update=>\%update, accessor=>$options->{accessor}, package=>$options->{package}}, inject=>['use feature "signatures";']);
142 16         45916 my $code_str=$template->render({duplicate =>$options->{duplicate}, type=>$options->{type}});
143 16         1970 $template->cleanup;
144              
145             #use feature "say";
146             #use Error::Show;
147 1 0   1   702 my $sub=eval($code_str);
  1 0   1   50  
  1 0   1   6  
  1 0   1   8  
  1 0   1   2  
  1 0   1   4  
  1 0   1   9  
  1 0   1   2  
  1 0   1   5  
  1 0   1   8  
  1 50   1   2  
  1 100   1   4  
  1 50   1   15  
  1 100   1   2  
  1 0   1   4  
  1 0   1   16  
  1 0   0   3  
  1 0       5  
  1 50       8  
  1 100       2  
  1 50       5  
  1 100       8  
  1 0       2  
  1 0       5  
  1 0       9  
  1 0       2  
  1         4  
  1         10  
  1         3  
  1         4  
  1         9  
  1         2  
  1         4  
  1         9  
  1         2  
  1         5  
  1         10  
  1         3  
  1         5  
  1         28  
  1         3  
  1         8  
  1         11  
  1         3  
  1         8  
  1         10  
  1         3  
  1         6  
  16         9797  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         256025  
  1         3  
  1         3  
  1         5  
  1         6  
  2         6  
  2         11  
  1         3  
  3         897  
  3         8  
  3         6  
  3         12  
  3         14  
  9         21  
  9         26  
  3         10  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  0         0  
  1         470  
  1         3  
  1         4  
  1         5  
  1         4  
  2         5  
  2         11  
  1         3  
  3         1554  
  3         7  
  3         6  
  3         12  
  3         11  
  9         15  
  9         31  
  3         32  
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
  0            
148             #say STDERR Error::Show::context error=>$@, program=>$code_str if($@ or !$sub);
149             #say STDERR $code_str;
150 16 100       105 wantarray?($sub,$code_str):$sub;
151             }
152              
153             1;