| 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__ |