File Coverage

blib/lib/Sort/strverscmp.pm
Criterion Covered Total %
statement 64 73 87.6
branch 17 20 85.0
condition 9 12 75.0
subroutine 16 19 84.2
pod 0 5 0.0
total 106 129 82.1


line stmt bran cond sub pod time code
1 1     1   29853 use strict;
  1         2  
  1         49  
2 1     1   6 use warnings;
  1         2  
  1         49  
3              
4             package StringIterator;
5              
6 1     1   6 use Carp qw(croak);
  1         6  
  1         1085  
7              
8             sub new {
9 30     30   42 my $class = shift;
10 30         37 my $string = shift;
11              
12 30 50       58 unless ($string) {
13 0         0 croak 'invalid string';
14             }
15              
16 30         51 my $o = {};
17 30         93 $o->{pos} = 0;
18 30         54 $o->{string} = $string;
19 30         46 $o->{len} = length($string);
20              
21 30         81 return bless $o, $class;
22             }
23              
24             sub pos {
25 675     675   683 my $self = shift;
26 675         2018 return $self->{pos};
27             }
28              
29             sub string {
30 349     349   510 my $self = shift;
31 349         884 return $self->{string};
32             }
33              
34             sub len {
35 326     326   357 my $self = shift;
36 326         653 return $self->{len};
37             }
38              
39             sub head {
40 326     326   383 my $self = shift;
41 326 100       471 if ($self->pos >= $self->len) {
42 3         23 return;
43             } else {
44 323         498 return substr($self->string, $self->pos, 1);
45             }
46             }
47              
48             sub tail {
49 26     26   29 my $self = shift;
50 26         36 return substr($self->string, $self->pos + 1);
51             }
52              
53             sub tail_len {
54 0     0   0 my $self = shift;
55 0         0 return ($self->len - $self->pos);
56             }
57              
58             sub advance {
59 104     104   114 my $self = shift;
60 104         219 $self->{pos}++;
61             }
62              
63             sub next {
64 0     0   0 my $self = shift;
65 0         0 my $head = $self->head();
66 0         0 $self->advance();
67 0         0 return $head;
68             }
69              
70             package Sort::strverscmp;
71              
72 1     1   9 use Exporter 'import';
  1         2  
  1         84  
73             our @EXPORT = qw(strverscmp);
74             our @EXPORT_OK = qw(strverssort);
75              
76 1     1   13 use feature ':5.10';
  1         1  
  1         2260  
77              
78             sub isdigit {
79 79     79 0 100 my $c = shift;
80 79   66     555 return (defined($c) && $c =~ /^\d+$/);
81             }
82              
83             sub fcmp {
84 7     7 0 11 my ($l, $r) = @_;
85              
86 7         9 my ($lz, $ln, $rz, $rn);
87 7         14 ($lz, $ln) = decompose_fractional($l);
88 7         15 ($rz, $rn) = decompose_fractional($r);
89              
90 7 100       18 if (length($lz) == length($rz)) {
91 1         10 return $ln <=> $rn;
92             } else {
93 6 100       50 return (length($lz) > length($rz) ? -1 : 1);
94             }
95             }
96              
97             sub decompose_fractional {
98 14     14 0 53 my ($zeroes, $number) = shift =~ /^(0*)(\d+)$/;
99 14         40 return ($zeroes, $number);
100             }
101              
102             # strnum_cmp from bam_sort.c
103             sub strverscmp {
104 15     15 0 105 my ($a, $b) = @_;
105              
106 15         49 my $ai = StringIterator->new($a);
107 15         38 my $bi = StringIterator->new($b);
108              
109 15   66     22 do {
110 66 100 66     117 if (isdigit($ai->head) && isdigit($bi->head)) {
111 13         26 my $an = (($ai->head . $ai->tail) =~ /^(\d*)/)[0];
112 13         38 my $bn = (($bi->head . $bi->tail) =~ /^(\d*)/)[0];
113 13 100 100     78 if ($an =~ /^0\d/ || $bn =~ /^0\d/) {
114 7         18 return fcmp($an, $bn);
115             } else {
116 6 100       24 if ($an <=> $bn) {
117 2         17 return ($an <=> $bn);
118             }
119             }
120             } else {
121 53 100       93 if ($ai->head cmp $bi->head) {
122 5         587 return ($ai->head cmp $bi->head);
123             }
124             }
125 52         146 $ai->advance();
126 52         82 $bi->advance();
127             } while (defined($ai->head) && defined($bi->head));
128              
129 1 50       3 return $ai->head ? 1 : $bi->head ? -1 : 0;
    50          
130             }
131              
132             sub strverssort {
133 0     0 0   return sort { strverscmp($a, $b) } @_;
  0            
134             }
135              
136             1;
137              
138             __END__