File Coverage

lib/Spreadsheet/Engine/Function/MATCH.pm
Criterion Covered Total %
statement 65 65 100.0
branch 17 20 85.0
condition 11 17 64.7
subroutine 17 17 100.0
pod 3 3 100.0
total 113 122 92.6


line stmt bran cond sub pod time code
1             package Spreadsheet::Engine::Function::MATCH;
2              
3 28     28   176 use strict;
  28         56  
  28         904  
4 28     28   152 use warnings;
  28         57  
  28         732  
5              
6 28     28   161 use base 'Spreadsheet::Engine::Fn::base';
  28         66  
  28         2830  
7 28     28   223 use Encode;
  28         65  
  28         4682  
8              
9 27     27 1 88 sub argument_count { -2 => 3 }
10 27     27 1 123 sub signature { '*', 'r', 'n' }
11              
12             use Spreadsheet::Engine::Sheet
13 28     28   185 qw/top_of_stack_value_and_type decode_range_parts/;
  28         60  
  28         2180  
14 28     28   172 use Spreadsheet::Engine::Functions qw/cr_to_coord/;
  28         133  
  28         24375  
15              
16 97     97   337 sub _want_op { (shift->_ops)[0] }
17 244     244   566 sub _range_op { (shift->_ops)[1] }
18 27     27   103 sub _sorted_op { (shift->_ops)[2] }
19              
20             sub result {
21 97     97 1 139 my $self = shift;
22 97         274 my $want = $self->_want_op;
23 97         933 my $sorted = $self->_sorted;
24              
25 97         1050 my ($c, $r, $cincr, $rincr) = $self->_crincs;
26 97         255 my ($rangesheetdata, $rangecol1num, $nrangecols, $rangerow1num, $nrangerows)
27             = $self->_range_data;
28              
29 97         202 my $previousOK = 0;
30 97   100     455 while ($r < $nrangerows && $c < $nrangecols) {
31 749         1937 my $cr = cr_to_coord($rangecol1num + $c, $rangerow1num + $r);
32 749   50     18542 my $got = Spreadsheet::Engine::Value->new(
33             value => $rangesheetdata->{datavalues}->{$cr},
34             type => $rangesheetdata->{valuetypes}->{$cr} || 'b',
35             );
36              
37 749         16516 my $cmp = _cmp_op($want, $got);
38 749 100       28805 next unless defined $cmp;
39 719 100       1459 return $self->_gotit([ $c, $r ]) if $cmp == 0;
40              
41             # If sorted, cache possible result
42 663 100 100     2792 if (($sorted > 0 && $cmp == 1) || ($sorted < 0 && $cmp == -1)) {
      33        
      66        
43 142         290 $previousOK = [ $c, $r ];
44 142         462 next;
45             }
46              
47 521 100       1486 return $self->_gotit($previousOK) if $previousOK;
48             } continue {
49 681         741 $r += $rincr;
50 681         3104 $c += $cincr;
51             }
52              
53             # end of range to check, no exact match
54 29 50       100 return $self->_gotit($previousOK) if $previousOK;
55 29         160 die Spreadsheet::Engine::Error->na;
56             }
57              
58             sub _gotit {
59 18     18   30 my ($self, $cr) = @_;
60 18         25 my ($c, $r) = @{$cr};
  18         35  
61 18         399 return Spreadsheet::Engine::Value->new(
62             type => 'n',
63             value => $c + $r + 1,
64             );
65             }
66              
67             sub _range_data {
68 244     244   354 my $self = shift;
69 244         478 my $range = $self->_range_op->value;
70 244         14770 return decode_range_parts($self->sheetdata, @{$range}{qw/value type/});
  244         2042  
71             }
72              
73             sub _crincs {
74 27     27   51 my $self = shift;
75 27         77 my ($rangesheetdata, $rangecol1num, $nrangecols, $rangerow1num, $nrangerows)
76             = $self->_range_data;
77 27 50 33     166 die Spreadsheet::Engine::Error->na if $nrangerows > 1 and $nrangecols > 1;
78 27         61 my ($cincr, $rincr) = (0, 0);
79 27 50       55 $nrangecols > 1 ? $cincr = 1 : $rincr = 1;
80 27         82 return (0, 0, $cincr, $rincr);
81             }
82              
83             sub _sorted {
84 27     27   44 my $self = shift;
85 27 100       80 my $op = $self->_sorted_op or return 1;
86 10         259 return $op->value;
87             }
88              
89             # TODO promote this to Value with overloading
90             sub _cmp_op {
91 749     749   1400 my ($op1, $op2) = @_;
92 749 100       14585 return unless substr($op1->type, 0, 1) eq substr($op2->type, 0, 1);
93 719         22025 my ($X, $Y) = map $_->value, ($op1, $op2);
94 719 100       8149 return $X <=> $Y if $op1->is_num;
95 591         5352 return lc(decode('utf8', $X)) cmp lc(decode('utf8', $Y));
96             }
97              
98             1;
99              
100             __END__