fix pagesize < 1
[perl/list-index.git] / t / 10-ranges.t
1 #!/usr/bin/env perl
2 use strict;
3 use warnings;
4
5 use Test::More tests => 9;
6 use Test::NoWarnings;
7 use Data::Dump 'pp';
8
9 BEGIN { use_ok('List::Index'); }
10 ok(eval { List::Index->VERSION(1) }, 'version 1.00 compatibility');
11
12 subtest 'single-char alphabet' => sub {
13         plan tests => 5;
14         my @uniform = 'a'..'z';
15         my $index = List::Index->new(\@uniform) or return;
16         is_deeply(\@uniform, ['a'..'z'], 'original data unaltered');
17         is_deeply($index->ranges, ['-'], 'single page');
18         is_deeply($index->ranges({pages => 3}), [qw(-i j-q r-)], 'given pages');
19         is_deeply($index->ranges({pagesize => @uniform / 2.1}), [qw(
20                 -i j-q r-
21         )], 'equivalent pagesize');
22         is_deeply($index->ranges({ pages => 500 }), ['-a', 'b'..'y', 'z-'], 'max pages');
23 };
24
25 subtest 'uniform alphanumeric' => sub {
26         plan tests => 2;
27         my $index = List::Index->new(['aa'..'zz', 1..202]) or return;
28         is_deeply($index->ranges, [qw(
29                 -.
30                 .-bp bq-dm dn-fi fj-hf hg-i j-k l-m n-os ot-qp qq-sm sn-uj uk-wf wg-x y-
31
32         )], 'default ranges');
33         is_deeply($index->ranges({pagesize => 300}), [qw(-c d-n o-)], 'large pagesize');
34 };
35
36 subtest 'context' => sub {
37         plan tests => 9;
38         my $index = List::Index->new([qw(
39                 kkeg kl km kmlu knsy    koxb kpeo kuaa kuab kuac
40                 kuapa kuq kur kux kzb   lc lg lgu lgua lguc
41                 lguq lgur lgws lgx lka  lkq lks lln llq llx
42         )]) or return;
43         is_deeply(
44                 $index->ranges({ pagesize=>10, context=>0, length=>5 }),
45                 # ranges should match offsets exactly
46                 [qw(-kuap. kuapa-lgup lguq-)],
47                 'no context'
48         );
49         is_deeply(
50                 $index->ranges({ pagesize=>10, context=>0 }),
51                 # default length limits to 4 chars
52                 [qw(-kuao kuap-lgup lguq-)],
53                 'default length'
54         );
55         is_deeply(
56                 $index->ranges({ pagesize=>10, context=>1 }),
57                 # lookbehinds aren't shorter (kuac<kuap, lguc<lguq)
58                 # 'kuap' can advance to 'kuq'
59                 [qw(-kup kuq-lgup lguq-)],
60                 'lookahead'
61         );
62 TODO: {
63         local $TODO = 'backtrack';
64         is_deeply(
65                 $index->ranges({ pagesize=>10, context=>2 }),
66                 # allowed to advance to 'kur', but provides no benefits over 'kuq'
67                 [qw(-kup kuq-lgup lguq-)],
68                 'minimal lookahead'
69         );
70 }
71         is_deeply(
72                 $index->ranges({ pagesize=>10, context=>3 }),
73                 # shorten 'kuap' to 'ku' because lookbehind is 'kp...'
74                 # 'lguq' matches 'lg', but may only backtrack to 'lgu'
75                 [qw(-kt ku-lgt lgu-)],
76                 'lookbehind'
77         );
78         is_deeply(
79                 $index->ranges({ pagesize=>10, context=>4 }),
80                 [qw(-kt ku-lf lg-)],
81                 'maximal lookahead'
82         );
83         is_deeply(
84                 $index->ranges({ pagesize=>10, context=>5 }),
85                 # after forwarding 'kuap' to 'lc'
86                 # disallow backtracking of 'lguq' to 'lc' to prevent qw[-k l-]
87                 # so only lookahead (to 'lkq') remains
88                 [qw(-k l-lj lk-)],
89                 'lookbehind forbidden'
90         );
91         is_deeply(
92                 $index->ranges({ pagesize=>10, context=>9 }),
93                 # allow a single (10-9) entry (l-lf = lc) to remain
94                 [qw(-k l-lf lg-)],
95                 'lookbehind penalty'
96         );
97         is_deeply(
98                 $index->ranges({ pagesize=>10, context=>10 }),
99                 # allow the last page to go back upto 'lc', replacing the 2nd page
100                 [qw(-k l-)],
101                 'full overlap'
102         );
103 };
104
105 subtest 'distribution' => sub {
106         plan tests => 2;
107         my $index = List::Index->new([qw(
108                 gnihka gniub go gsearnrqns gtdvcxyt gw gwoufolwcvmtueyg gysgphci h habkdgifjfxoh
109                 hbbvjf hbqleexnqts hccg hd hdoeqwdmgqwaoya hfbegicieuxz hfm hj hkoysmws hmylu
110                 hnvtvpievbdlkrmb hs hvdvcqn hvn hyrybeur iaiaab ib ibavqyar idfniqvxpohbk idh
111         )]) or return;
112         is_deeply(
113                 $index->ranges({ pagesize=>10, context=>8 }),
114                 [qw(-g h i-)],
115                 'large context'
116         );
117         is_deeply(
118                 $index->ranges({ pagesize=>10, context=>7 }),
119                 # after 2nd page is enlarged by lookbehind to 'h', limit subsequent lookahead
120                 # to prevent the page from getting too large (17 entries if forwarded to 'i')
121                 [qw(-g h-hm hn-)],
122                 'lookahead penalty'
123         );
124         # page #14 [gn-g] (8): gnihka gniub go gsearnrqns gtdvcxyt gwawkvmueovdjtfj gwoufolwcvmtueyg gysgphci
125         # page #15 [h] (17): h habkdgifjfxoh hbbvjf hbqleexnqts hccgszftbaymfu hdaqzkow hdoeqwdmgqwaoya hfbegicieu hfmlpzzioqjbthz hj hkoysmws hmylu hnvtvpievbdlkrmb hsodfpkatk hvdvcqn hvn hyrybeurqtevjfmi
126         # page #16 [i-ie] (5): i iaab ibiavqyar idfniqvxpohbk idh
127 };
128
129 subtest 'modulo' => sub {
130         plan tests => 2;
131         my $index = List::Index->new([qw(
132                 a b ccb   ccd  cce gf gg   gh  i j
133         )]) or return;
134         # 10 entries at 4 per page requires 3 pages
135         # so actual target page sizes should be 3,4,3 (not 4,4,2)
136
137         is_deeply(
138                 $index->ranges({ pagesize=>4, context=>0 }),
139                 [qw(-ccc ccd-gg gh-)],
140                 'uniform page sizes'
141         );
142 { local $TODO = 'early lookbehind causing [c-gg]';
143         is_deeply(
144                 $index->ranges({ pagesize=>4, context=>1 }),
145                 [qw(-b c-h i-)],
146                 'context at new intervals'
147         );
148 }
149 };
150
151 subtest 'context' => sub {
152         plan tests => 4;
153         my $index = List::Index->new([qw(
154                 baa1 baa2  baa3 baaa  bbc cbc  daaa ea  eaaa zed
155         )]) or return;
156         is_deeply($index->ranges({pagesize => 2, context => 0}), [
157                 qw(-baa. baa.-bbb bbc-daa. daaa-eaa. eaaa-)
158         ], 'no context');
159         is_deeply($index->ranges({pagesize => 2}), [
160                 qw(-a b c d e-)
161         ], 'default context');  # context should be 1
162         is_deeply($index->ranges({pagesize => 2, context => 2}), [
163                 qw(-a b-c d e-)
164         ], 'overlap');  # first item equals second due to large context
165         is_deeply($index->ranges({pagesize => 2, context => 0, length => 1}), [
166                 qw(-a b-c d e-)
167         ], 'single char');
168
169         #pp($index->ranges({pagesize => 2, context => 2, length => 1}));
170 };
171