File Coverage

blib/lib/List/Insertion.pm
Criterion Covered Total %
statement 99 180 55.0
branch 18 62 29.0
condition 17 23 73.9
subroutine 12 13 92.3
pod 1 1 100.0
total 147 279 52.6


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