line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Spreadsheet::Engine::Functions; |
2
|
|
|
|
|
|
|
## no critic |
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
=head1 NAME |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
Spreadsheet::Engine::Functions - Spreadsheet functions (SUM, MAX, etc) |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 SYNOPSIS |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
my $ok = calculate_function($fname, \@operand, \$errortext, \%typelookup, \%sheetdata); |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 DESCRIPTION |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
This provides all the spreadsheet functions (SUM, MAX, IRR, ISNULL, |
15
|
|
|
|
|
|
|
etc). |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=cut |
18
|
|
|
|
|
|
|
|
19
|
28
|
|
|
28
|
|
155
|
use strict; |
|
28
|
|
|
|
|
49
|
|
|
28
|
|
|
|
|
1215
|
|
20
|
|
|
|
|
|
|
|
21
|
28
|
|
|
28
|
|
152
|
use Spreadsheet::Engine::Sheet; # bah! |
|
28
|
|
|
|
|
52
|
|
|
28
|
|
|
|
|
13400
|
|
22
|
28
|
|
|
28
|
|
26058
|
use Time::Local; # For timegm in NOW and TODAY |
|
28
|
|
|
|
|
55116
|
|
|
28
|
|
|
|
|
2072
|
|
23
|
28
|
|
|
28
|
|
28610
|
use Encode; |
|
28
|
|
|
|
|
416861
|
|
|
28
|
|
|
|
|
3714
|
|
24
|
|
|
|
|
|
|
|
25
|
28
|
|
|
28
|
|
271
|
use base 'Exporter'; |
|
28
|
|
|
|
|
64
|
|
|
28
|
|
|
|
|
120463
|
|
26
|
|
|
|
|
|
|
our @EXPORT = qw(calculate_function); |
27
|
|
|
|
|
|
|
our @EXPORT_OK = qw(cr_to_coord); |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
#0 = no arguments |
30
|
|
|
|
|
|
|
#>0 = exactly that many arguments |
31
|
|
|
|
|
|
|
#<0 = that many arguments (abs value) or more |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
our %function_list = ( |
34
|
|
|
|
|
|
|
COLUMNS => [ \&columns_rows_function, 1 ], |
35
|
|
|
|
|
|
|
COUNTIF => [ \&countif_sumif_functions, 2 ], |
36
|
|
|
|
|
|
|
DAVERAGE => [ \&dseries_functions, 3 ], |
37
|
|
|
|
|
|
|
DCOUNT => [ \&dseries_functions, 3 ], |
38
|
|
|
|
|
|
|
DCOUNTA => [ \&dseries_functions, 3 ], |
39
|
|
|
|
|
|
|
DGET => [ \&dseries_functions, 3 ], |
40
|
|
|
|
|
|
|
DMAX => [ \&dseries_functions, 3 ], |
41
|
|
|
|
|
|
|
DMIN => [ \&dseries_functions, 3 ], |
42
|
|
|
|
|
|
|
DPRODUCT => [ \&dseries_functions, 3 ], |
43
|
|
|
|
|
|
|
DSTDEV => [ \&dseries_functions, 3 ], |
44
|
|
|
|
|
|
|
DSTDEVP => [ \&dseries_functions, 3 ], |
45
|
|
|
|
|
|
|
DSUM => [ \&dseries_functions, 3 ], |
46
|
|
|
|
|
|
|
DVAR => [ \&dseries_functions, 3 ], |
47
|
|
|
|
|
|
|
DVARP => [ \&dseries_functions, 3 ], |
48
|
|
|
|
|
|
|
INDEX => [ \&index_function, -1 ], |
49
|
|
|
|
|
|
|
ROWS => [ \&columns_rows_function, 1 ], |
50
|
|
|
|
|
|
|
SUMIF => [ \&countif_sumif_functions, -2 ], |
51
|
|
|
|
|
|
|
HTML => [ \&html_function, -1 ], |
52
|
|
|
|
|
|
|
PLAINTEXT => [ \&text_function, -1 ], |
53
|
|
|
|
|
|
|
); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head1 EXTENDING |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head2 register |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Spreadsheet::Engine->register(SUM => 'Spreadsheet::Engine::Function::SUM'); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
If you wish to make a new function available you should register it |
62
|
|
|
|
|
|
|
here. A series of base classes are provided that do all the argument |
63
|
|
|
|
|
|
|
checking etc., allowing you to concentrate on the calculations. Have a |
64
|
|
|
|
|
|
|
look at how the existing functions are implemented for details (it |
65
|
|
|
|
|
|
|
should hopefully be mostly self-explanatory!) |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
information on how many arguments should be passed: |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=cut |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
my $_reg = {}; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub register { |
74
|
28
|
|
|
28
|
1
|
2356
|
my ($class, %to_reg) = @_; |
75
|
28
|
|
|
|
|
363
|
while (my ($name, $where) = each %to_reg) { |
76
|
28
|
|
|
28
|
|
15940
|
eval "use $where"; |
|
28
|
|
|
28
|
|
104
|
|
|
28
|
|
|
28
|
|
686
|
|
|
28
|
|
|
28
|
|
15164
|
|
|
28
|
|
|
28
|
|
82
|
|
|
28
|
|
|
28
|
|
583
|
|
|
28
|
|
|
28
|
|
12950
|
|
|
28
|
|
|
28
|
|
90
|
|
|
28
|
|
|
28
|
|
577
|
|
|
28
|
|
|
28
|
|
13433
|
|
|
28
|
|
|
28
|
|
85
|
|
|
28
|
|
|
28
|
|
707
|
|
|
28
|
|
|
28
|
|
14485
|
|
|
28
|
|
|
28
|
|
76
|
|
|
28
|
|
|
28
|
|
502
|
|
|
28
|
|
|
28
|
|
19587
|
|
|
28
|
|
|
28
|
|
85
|
|
|
28
|
|
|
28
|
|
550
|
|
|
28
|
|
|
28
|
|
18963
|
|
|
28
|
|
|
28
|
|
83
|
|
|
28
|
|
|
28
|
|
540
|
|
|
28
|
|
|
28
|
|
14278
|
|
|
28
|
|
|
28
|
|
93
|
|
|
28
|
|
|
28
|
|
516
|
|
|
28
|
|
|
28
|
|
18655
|
|
|
28
|
|
|
28
|
|
85
|
|
|
28
|
|
|
28
|
|
579
|
|
|
28
|
|
|
28
|
|
13034
|
|
|
28
|
|
|
28
|
|
85
|
|
|
28
|
|
|
28
|
|
550
|
|
|
28
|
|
|
28
|
|
12174
|
|
|
28
|
|
|
28
|
|
84
|
|
|
28
|
|
|
28
|
|
569
|
|
|
28
|
|
|
28
|
|
15069
|
|
|
28
|
|
|
28
|
|
79
|
|
|
28
|
|
|
28
|
|
494
|
|
|
28
|
|
|
28
|
|
12633
|
|
|
28
|
|
|
28
|
|
83
|
|
|
28
|
|
|
28
|
|
524
|
|
|
28
|
|
|
28
|
|
12602
|
|
|
28
|
|
|
28
|
|
79
|
|
|
28
|
|
|
28
|
|
582
|
|
|
28
|
|
|
28
|
|
13391
|
|
|
28
|
|
|
28
|
|
86
|
|
|
28
|
|
|
28
|
|
517
|
|
|
28
|
|
|
28
|
|
12049
|
|
|
28
|
|
|
28
|
|
266
|
|
|
28
|
|
|
28
|
|
511
|
|
|
28
|
|
|
28
|
|
12116
|
|
|
28
|
|
|
28
|
|
74
|
|
|
28
|
|
|
28
|
|
538
|
|
|
28
|
|
|
28
|
|
13094
|
|
|
28
|
|
|
28
|
|
81
|
|
|
28
|
|
|
28
|
|
871
|
|
|
28
|
|
|
28
|
|
12092
|
|
|
28
|
|
|
28
|
|
270
|
|
|
28
|
|
|
28
|
|
489
|
|
|
28
|
|
|
28
|
|
12195
|
|
|
28
|
|
|
28
|
|
166
|
|
|
28
|
|
|
28
|
|
501
|
|
|
28
|
|
|
28
|
|
12603
|
|
|
28
|
|
|
28
|
|
87
|
|
|
28
|
|
|
28
|
|
514
|
|
|
28
|
|
|
28
|
|
12764
|
|
|
28
|
|
|
28
|
|
73
|
|
|
28
|
|
|
28
|
|
473
|
|
|
28
|
|
|
28
|
|
15016
|
|
|
28
|
|
|
28
|
|
77
|
|
|
28
|
|
|
28
|
|
484
|
|
|
28
|
|
|
28
|
|
11199
|
|
|
28
|
|
|
28
|
|
82
|
|
|
28
|
|
|
28
|
|
709
|
|
|
28
|
|
|
28
|
|
13626
|
|
|
28
|
|
|
28
|
|
95
|
|
|
28
|
|
|
28
|
|
509
|
|
|
28
|
|
|
28
|
|
11449
|
|
|
28
|
|
|
28
|
|
74
|
|
|
28
|
|
|
28
|
|
500
|
|
|
28
|
|
|
28
|
|
12861
|
|
|
28
|
|
|
28
|
|
86
|
|
|
28
|
|
|
28
|
|
719
|
|
|
28
|
|
|
28
|
|
12202
|
|
|
28
|
|
|
28
|
|
528
|
|
|
28
|
|
|
28
|
|
585
|
|
|
28
|
|
|
28
|
|
12932
|
|
|
28
|
|
|
28
|
|
272
|
|
|
28
|
|
|
28
|
|
486
|
|
|
28
|
|
|
28
|
|
12396
|
|
|
28
|
|
|
28
|
|
82
|
|
|
28
|
|
|
28
|
|
483
|
|
|
28
|
|
|
28
|
|
12783
|
|
|
28
|
|
|
28
|
|
79
|
|
|
28
|
|
|
28
|
|
489
|
|
|
28
|
|
|
|
|
12680
|
|
|
28
|
|
|
|
|
76
|
|
|
28
|
|
|
|
|
483
|
|
|
28
|
|
|
|
|
25626
|
|
|
28
|
|
|
|
|
90
|
|
|
28
|
|
|
|
|
551
|
|
|
28
|
|
|
|
|
11972
|
|
|
28
|
|
|
|
|
84
|
|
|
28
|
|
|
|
|
617
|
|
|
28
|
|
|
|
|
12013
|
|
|
28
|
|
|
|
|
83
|
|
|
28
|
|
|
|
|
511
|
|
|
28
|
|
|
|
|
12728
|
|
|
28
|
|
|
|
|
80
|
|
|
28
|
|
|
|
|
519
|
|
|
28
|
|
|
|
|
12257
|
|
|
28
|
|
|
|
|
82
|
|
|
28
|
|
|
|
|
798
|
|
|
28
|
|
|
|
|
15609
|
|
|
28
|
|
|
|
|
82
|
|
|
28
|
|
|
|
|
478
|
|
|
28
|
|
|
|
|
12267
|
|
|
28
|
|
|
|
|
81
|
|
|
28
|
|
|
|
|
551
|
|
|
28
|
|
|
|
|
12135
|
|
|
28
|
|
|
|
|
84
|
|
|
28
|
|
|
|
|
494
|
|
|
28
|
|
|
|
|
13162
|
|
|
28
|
|
|
|
|
85
|
|
|
28
|
|
|
|
|
569
|
|
|
28
|
|
|
|
|
11748
|
|
|
28
|
|
|
|
|
84
|
|
|
28
|
|
|
|
|
509
|
|
|
28
|
|
|
|
|
11575
|
|
|
28
|
|
|
|
|
83
|
|
|
28
|
|
|
|
|
503
|
|
|
28
|
|
|
|
|
12252
|
|
|
28
|
|
|
|
|
85
|
|
|
28
|
|
|
|
|
506
|
|
|
28
|
|
|
|
|
10754
|
|
|
28
|
|
|
|
|
85
|
|
|
28
|
|
|
|
|
492
|
|
|
28
|
|
|
|
|
12103
|
|
|
28
|
|
|
|
|
85
|
|
|
28
|
|
|
|
|
495
|
|
|
28
|
|
|
|
|
11244
|
|
|
28
|
|
|
|
|
79
|
|
|
28
|
|
|
|
|
487
|
|
|
28
|
|
|
|
|
11542
|
|
|
28
|
|
|
|
|
79
|
|
|
28
|
|
|
|
|
527
|
|
|
28
|
|
|
|
|
11616
|
|
|
28
|
|
|
|
|
77
|
|
|
28
|
|
|
|
|
487
|
|
|
28
|
|
|
|
|
11659
|
|
|
28
|
|
|
|
|
93
|
|
|
28
|
|
|
|
|
477
|
|
|
28
|
|
|
|
|
12028
|
|
|
28
|
|
|
|
|
84
|
|
|
28
|
|
|
|
|
519
|
|
|
28
|
|
|
|
|
11568
|
|
|
28
|
|
|
|
|
77
|
|
|
28
|
|
|
|
|
493
|
|
|
28
|
|
|
|
|
11618
|
|
|
28
|
|
|
|
|
81
|
|
|
28
|
|
|
|
|
477
|
|
|
28
|
|
|
|
|
11394
|
|
|
28
|
|
|
|
|
117
|
|
|
28
|
|
|
|
|
490
|
|
|
28
|
|
|
|
|
12145
|
|
|
28
|
|
|
|
|
75
|
|
|
28
|
|
|
|
|
505
|
|
|
28
|
|
|
|
|
12000
|
|
|
28
|
|
|
|
|
76
|
|
|
28
|
|
|
|
|
506
|
|
|
28
|
|
|
|
|
11732
|
|
|
28
|
|
|
|
|
79
|
|
|
28
|
|
|
|
|
487
|
|
|
28
|
|
|
|
|
11209
|
|
|
28
|
|
|
|
|
84
|
|
|
28
|
|
|
|
|
511
|
|
|
28
|
|
|
|
|
11913
|
|
|
28
|
|
|
|
|
85
|
|
|
28
|
|
|
|
|
479
|
|
|
28
|
|
|
|
|
13941
|
|
|
28
|
|
|
|
|
78
|
|
|
28
|
|
|
|
|
470
|
|
|
28
|
|
|
|
|
11838
|
|
|
28
|
|
|
|
|
110
|
|
|
28
|
|
|
|
|
513
|
|
|
28
|
|
|
|
|
12161
|
|
|
28
|
|
|
|
|
113
|
|
|
28
|
|
|
|
|
465
|
|
|
28
|
|
|
|
|
11545
|
|
|
28
|
|
|
|
|
79
|
|
|
28
|
|
|
|
|
482
|
|
|
28
|
|
|
|
|
11843
|
|
|
28
|
|
|
|
|
77
|
|
|
28
|
|
|
|
|
491
|
|
|
28
|
|
|
|
|
11566
|
|
|
28
|
|
|
|
|
88
|
|
|
28
|
|
|
|
|
499
|
|
|
28
|
|
|
|
|
12261
|
|
|
28
|
|
|
|
|
92
|
|
|
28
|
|
|
|
|
531
|
|
|
28
|
|
|
|
|
12315
|
|
|
28
|
|
|
|
|
83
|
|
|
28
|
|
|
|
|
472
|
|
|
28
|
|
|
|
|
11721
|
|
|
28
|
|
|
|
|
81
|
|
|
28
|
|
|
|
|
543
|
|
|
28
|
|
|
|
|
11875
|
|
|
28
|
|
|
|
|
88
|
|
|
28
|
|
|
|
|
481
|
|
|
28
|
|
|
|
|
11639
|
|
|
28
|
|
|
|
|
87
|
|
|
28
|
|
|
|
|
1471
|
|
|
28
|
|
|
|
|
12507
|
|
|
28
|
|
|
|
|
95
|
|
|
28
|
|
|
|
|
534
|
|
|
28
|
|
|
|
|
11909
|
|
|
28
|
|
|
|
|
1069
|
|
|
28
|
|
|
|
|
490
|
|
|
28
|
|
|
|
|
13963
|
|
|
28
|
|
|
|
|
78
|
|
|
28
|
|
|
|
|
1893
|
|
|
28
|
|
|
|
|
13820
|
|
|
28
|
|
|
|
|
74
|
|
|
28
|
|
|
|
|
1621
|
|
|
28
|
|
|
|
|
12584
|
|
|
28
|
|
|
|
|
76
|
|
|
28
|
|
|
|
|
1523
|
|
|
28
|
|
|
|
|
11109
|
|
|
28
|
|
|
|
|
80
|
|
|
28
|
|
|
|
|
471
|
|
|
28
|
|
|
|
|
11351
|
|
|
28
|
|
|
|
|
83
|
|
|
28
|
|
|
|
|
508
|
|
|
28
|
|
|
|
|
11425
|
|
|
28
|
|
|
|
|
76
|
|
|
28
|
|
|
|
|
467
|
|
|
28
|
|
|
|
|
11117
|
|
|
28
|
|
|
|
|
79
|
|
|
28
|
|
|
|
|
488
|
|
|
28
|
|
|
|
|
11288
|
|
|
28
|
|
|
|
|
86
|
|
|
28
|
|
|
|
|
478
|
|
|
28
|
|
|
|
|
11402
|
|
|
28
|
|
|
|
|
78
|
|
|
28
|
|
|
|
|
479
|
|
|
28
|
|
|
|
|
11554
|
|
|
28
|
|
|
|
|
77
|
|
|
28
|
|
|
|
|
565
|
|
|
28
|
|
|
|
|
11911
|
|
|
28
|
|
|
|
|
97
|
|
|
28
|
|
|
|
|
473
|
|
|
28
|
|
|
|
|
17347
|
|
|
28
|
|
|
|
|
84
|
|
|
28
|
|
|
|
|
477
|
|
|
28
|
|
|
|
|
10864
|
|
|
28
|
|
|
|
|
78
|
|
|
28
|
|
|
|
|
474
|
|
|
28
|
|
|
|
|
11243
|
|
|
28
|
|
|
|
|
102
|
|
|
28
|
|
|
|
|
537
|
|
|
28
|
|
|
|
|
11366
|
|
|
28
|
|
|
|
|
90
|
|
|
28
|
|
|
|
|
506
|
|
|
28
|
|
|
|
|
12252
|
|
|
28
|
|
|
|
|
83
|
|
|
28
|
|
|
|
|
576
|
|
|
28
|
|
|
|
|
17999
|
|
|
28
|
|
|
|
|
84
|
|
|
28
|
|
|
|
|
478
|
|
|
28
|
|
|
|
|
11387
|
|
|
28
|
|
|
|
|
85
|
|
|
28
|
|
|
|
|
488
|
|
|
28
|
|
|
|
|
12133
|
|
|
28
|
|
|
|
|
85
|
|
|
28
|
|
|
|
|
491
|
|
|
28
|
|
|
|
|
10693
|
|
|
28
|
|
|
|
|
471
|
|
|
28
|
|
|
|
|
473
|
|
|
28
|
|
|
|
|
16629
|
|
|
28
|
|
|
|
|
1147
|
|
|
28
|
|
|
|
|
831
|
|
|
2604
|
|
|
|
|
137865
|
|
77
|
2604
|
50
|
|
|
|
9427
|
die $@ if $@; |
78
|
2604
|
|
|
|
|
16458
|
$_reg->{$name} = $where; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
__PACKAGE__->register( |
83
|
|
|
|
|
|
|
map +($_ => "Spreadsheet::Engine::Function::$_"), |
84
|
|
|
|
|
|
|
qw/ ABS ACOS AND ASIN ATAN ATAN2 AVERAGE CHOOSE COS COUNT COUNTA |
85
|
|
|
|
|
|
|
COUNTBLANK DATE DAY DDB DEGREES ERRCELL EVEN EXACT EXP FACT FALSE FIND |
86
|
|
|
|
|
|
|
FV HLOOKUP HOUR IF INT IRR ISBLANK ISERR ISERROR ISLOGICAL ISNA |
87
|
|
|
|
|
|
|
ISNONTEXT ISNUMBER ISTEXT LEFT LEN LN LOG LOG10 LOWER MATCH MAX MID |
88
|
|
|
|
|
|
|
MIN MINUTE MOD MONTH N NA NOT NOW NPER NPV ODD OR PI PMT POWER PRODUCT |
89
|
|
|
|
|
|
|
PROPER PV RADIANS RATE REPLACE REPT RIGHT ROUND SECOND SIN SLN SQRT |
90
|
|
|
|
|
|
|
STDEV STDEVP SUBSTITUTE SUM SYD T TAN TIME TODAY TRIM TRUE TRUNC UPPER |
91
|
|
|
|
|
|
|
VALUE VAR VARP VLOOKUP WEEKDAY YEAR / |
92
|
|
|
|
|
|
|
); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
=head1 EXPORTS |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head2 calculate_function |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
my $ok = calculate_function($fname, \@operand, \$errortext, \%typelookup, \%sheetdata); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=cut |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
sub calculate_function { |
103
|
|
|
|
|
|
|
|
104
|
18769
|
|
|
18769
|
1
|
34590
|
my ($fname, $operand, $errortext, $typelookup, $sheetdata) = @_; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# has the function been registered? (new style) |
107
|
18769
|
100
|
|
|
|
68372
|
if (my $fclass = $_reg->{$fname}) { |
108
|
18252
|
|
|
|
|
531103
|
my $fn = $fclass->new( |
109
|
|
|
|
|
|
|
fname => $fname, |
110
|
|
|
|
|
|
|
operand => $operand, |
111
|
|
|
|
|
|
|
errortext => $errortext, |
112
|
|
|
|
|
|
|
typelookup => $typelookup, |
113
|
|
|
|
|
|
|
sheetdata => $sheetdata, |
114
|
|
|
|
|
|
|
); |
115
|
18252
|
|
|
|
|
9242520
|
$fn->execute; |
116
|
18252
|
|
|
|
|
136727
|
return 1; |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# Otherwise is it in our function_list (old style) |
120
|
517
|
|
|
|
|
900
|
my ($function_sub, $want_args) = @{ $function_list{$fname} }[ 0, 1 ]; |
|
517
|
|
|
|
|
1757
|
|
121
|
|
|
|
|
|
|
|
122
|
517
|
100
|
|
|
|
1258
|
if ($function_sub) { |
123
|
244
|
|
|
|
|
1095
|
copy_function_args($operand, \my @foperand); |
124
|
|
|
|
|
|
|
|
125
|
244
|
|
|
|
|
456
|
my $have_args = scalar @foperand; |
126
|
|
|
|
|
|
|
|
127
|
244
|
50
|
66
|
|
|
1807
|
if ( ($want_args < 0 and $have_args < -$want_args) |
|
|
|
66
|
|
|
|
|
|
|
|
33
|
|
|
|
|
128
|
|
|
|
|
|
|
or ($want_args >= 0 and $have_args != $want_args)) { |
129
|
0
|
|
|
|
|
0
|
function_args_error($fname, $operand, $errortext); |
130
|
0
|
|
|
|
|
0
|
return 0; |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
244
|
|
|
|
|
768
|
$function_sub->( |
134
|
|
|
|
|
|
|
$fname, $operand, \@foperand, $errortext, $typelookup, $sheetdata |
135
|
|
|
|
|
|
|
); |
136
|
|
|
|
|
|
|
} else { |
137
|
273
|
|
|
|
|
503
|
my $ttext = $fname; |
138
|
273
|
50
|
33
|
|
|
1608
|
if (@$operand && $operand->[ @$operand - 1 ]->{type} eq "start") |
139
|
|
|
|
|
|
|
{ # no arguments - name or zero arg function |
140
|
273
|
|
|
|
|
353
|
pop @$operand; |
141
|
273
|
|
|
|
|
1412
|
push @$operand, { type => "name", value => $ttext }; |
142
|
|
|
|
|
|
|
} else { |
143
|
0
|
|
|
|
|
0
|
$$errortext = "Unknown function $ttext. "; |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
} |
146
|
517
|
|
|
|
|
2667
|
return 1; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
=head1 FUNCTION providers |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
=head2 dseries_functions |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=over |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=item DAVERAGE(databaserange, fieldname, criteriarange) |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=item DCOUNT(databaserange, fieldname, criteriarange) |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item DCOUNTA(databaserange, fieldname, criteriarange) |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=item DGET(databaserange, fieldname, criteriarange) |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=item DMAX(databaserange, fieldname, criteriarange) |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
=item DMIN(databaserange, fieldname, criteriarange) |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=item DPRODUCT(databaserange, fieldname, criteriarange) |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
=item DSTDEV(databaserange, fieldname, criteriarange) |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
=item DSTDEVP(databaserange, fieldname, criteriarange) |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=item DSUM(databaserange, fieldname, criteriarange) |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
=item DVAR(databaserange, fieldname, criteriarange) |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=item DVARP(databaserange, fieldname, criteriarange) |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=back |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
=cut |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# Calculate all of these and then return the desired one (overhead is in accessing not calculating) |
184
|
|
|
|
|
|
|
# If this routine is changed, check the series_functions, too. |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub dseries_functions { |
187
|
|
|
|
|
|
|
|
188
|
182
|
|
|
182
|
1
|
348
|
my ($fname, $operand, $foperand, $errortext, $typelookup, $sheetdata) = @_; |
189
|
|
|
|
|
|
|
|
190
|
182
|
|
|
|
|
214
|
my ($value1, $tostype, $cr); |
191
|
|
|
|
|
|
|
|
192
|
182
|
|
|
|
|
258
|
my $sum = 0; |
193
|
182
|
|
|
|
|
255
|
my $resulttypesum = ""; |
194
|
182
|
|
|
|
|
294
|
my $count = 0; |
195
|
182
|
|
|
|
|
266
|
my $counta = 0; |
196
|
182
|
|
|
|
|
211
|
my $countblank = 0; |
197
|
182
|
|
|
|
|
207
|
my $product = 1; |
198
|
182
|
|
|
|
|
255
|
my $maxval; |
199
|
|
|
|
|
|
|
my $minval; |
200
|
0
|
|
|
|
|
0
|
my ($mk, $sk, $mk1, $sk1); # For variance, etc.: M sub k, k-1, and S sub k-1 |
201
|
|
|
|
|
|
|
# as per Knuth "The Art of Computer Programming" Vol. 2 3rd edition, page 232 |
202
|
|
|
|
|
|
|
|
203
|
182
|
|
|
|
|
662
|
my ($dbrange, $dbrangetype) = |
204
|
|
|
|
|
|
|
top_of_stack_value_and_type($sheetdata, $foperand, $errortext); |
205
|
182
|
|
|
|
|
324
|
my $fieldtype; |
206
|
182
|
|
|
|
|
663
|
my $fieldname = |
207
|
|
|
|
|
|
|
operand_value_and_type($sheetdata, $foperand, $errortext, \$fieldtype); |
208
|
182
|
|
|
|
|
576
|
my ($criteriarange, $criteriarangetype) = |
209
|
|
|
|
|
|
|
top_of_stack_value_and_type($sheetdata, $foperand, $errortext); |
210
|
|
|
|
|
|
|
|
211
|
182
|
50
|
33
|
|
|
936
|
if ($dbrangetype ne "range" || $criteriarangetype ne "range") { |
212
|
0
|
|
|
|
|
0
|
function_args_error($fname, $operand, $errortext); |
213
|
0
|
|
|
|
|
0
|
return 0; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
182
|
|
|
|
|
671
|
my ($dbsheetdata, $dbcol1num, $ndbcols, $dbrow1num, $ndbrows) = |
217
|
|
|
|
|
|
|
decode_range_parts($sheetdata, $dbrange, $dbrangetype); |
218
|
|
|
|
|
|
|
my ( |
219
|
182
|
|
|
|
|
585
|
$criteriasheetdata, $criteriacol1num, $ncriteriacols, |
220
|
|
|
|
|
|
|
$criteriarow1num, $ncriteriarows |
221
|
|
|
|
|
|
|
) |
222
|
|
|
|
|
|
|
= decode_range_parts($sheetdata, $criteriarange, $criteriarangetype); |
223
|
|
|
|
|
|
|
|
224
|
182
|
|
|
|
|
664
|
my $fieldasnum = |
225
|
|
|
|
|
|
|
field_to_colnum($dbsheetdata, $dbcol1num, $ndbcols, $dbrow1num, |
226
|
|
|
|
|
|
|
$fieldname, $fieldtype); |
227
|
182
|
|
|
|
|
245
|
$fieldasnum = int($fieldasnum); |
228
|
182
|
50
|
|
|
|
415
|
if ($fieldasnum <= 0) { |
229
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#VALUE!", value => 0 }; |
230
|
0
|
|
|
|
|
0
|
return; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
182
|
|
|
|
|
300
|
my $targetcol = $dbcol1num + $fieldasnum - 1; |
234
|
|
|
|
|
|
|
|
235
|
182
|
|
|
|
|
284
|
my (@criteriafieldnums, $criteriafieldname, $criteriafieldtype, |
236
|
|
|
|
|
|
|
$criterianum); |
237
|
|
|
|
|
|
|
|
238
|
182
|
|
|
|
|
440
|
for (my $i = 0 ; $i < $ncriteriacols ; $i++) { # get criteria field colnums |
239
|
218
|
|
|
|
|
604
|
my $criteriacr = cr_to_coord($criteriacol1num + $i, $criteriarow1num); |
240
|
218
|
|
|
|
|
562
|
$criteriafieldname = $criteriasheetdata->{datavalues}->{$criteriacr}; |
241
|
218
|
|
|
|
|
438
|
$criteriafieldtype = $criteriasheetdata->{valuetypes}->{$criteriacr}; |
242
|
218
|
|
|
|
|
525
|
$criterianum = |
243
|
|
|
|
|
|
|
field_to_colnum($dbsheetdata, $dbcol1num, $ndbcols, $dbrow1num, |
244
|
|
|
|
|
|
|
$criteriafieldname, $criteriafieldtype); |
245
|
218
|
|
|
|
|
356
|
$criterianum = int($criterianum); |
246
|
218
|
50
|
|
|
|
568
|
if ($criterianum <= 0) { |
247
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#VALUE!", value => 0 }; |
248
|
0
|
|
|
|
|
0
|
return; |
249
|
|
|
|
|
|
|
} |
250
|
218
|
|
|
|
|
635
|
push @criteriafieldnums, $dbcol1num + $criterianum - 1; |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
182
|
|
|
|
|
220
|
my ($testok, $criteria, $testcol, $testcr); |
254
|
|
|
|
|
|
|
|
255
|
182
|
|
|
|
|
490
|
for (my $i = 1 ; $i < $ndbrows ; $i++) |
256
|
|
|
|
|
|
|
{ # go through each row of the database |
257
|
2366
|
|
|
|
|
2602
|
$testok = 0; |
258
|
|
|
|
|
|
|
CRITERIAROW: |
259
|
2366
|
|
|
|
|
4501
|
for (my $j = 1 ; $j < $ncriteriarows ; $j++) |
260
|
|
|
|
|
|
|
{ # go through each criteria row |
261
|
2709
|
|
|
|
|
4882
|
for (my $k = 0 ; $k < $ncriteriacols ; $k++) { # look at each column |
262
|
2840
|
|
|
|
|
7174
|
my $criteriacr = |
263
|
|
|
|
|
|
|
cr_to_coord($criteriacol1num + $k, $criteriarow1num + $j) |
264
|
|
|
|
|
|
|
; # where criteria is |
265
|
2840
|
|
|
|
|
6029
|
$criteria = $criteriasheetdata->{datavalues}->{$criteriacr}; |
266
|
2840
|
50
|
|
|
|
4720
|
next unless $criteria; # blank items are OK |
267
|
2840
|
|
|
|
|
10579
|
$testcol = |
268
|
|
|
|
|
|
|
$criteriasheetdata->{datavalues} |
269
|
|
|
|
|
|
|
->{ cr_to_coord($criteriacol1num + $k, $criteriarow1num) }; |
270
|
2840
|
|
|
|
|
5163
|
$testcol = $criteriafieldnums[$k]; |
271
|
2840
|
|
|
|
|
8953
|
$testcr = cr_to_coord($testcol, $dbrow1num + $i); # cell to check |
272
|
|
|
|
|
|
|
next CRITERIAROW |
273
|
2840
|
100
|
50
|
|
|
13367
|
unless test_criteria($criteriasheetdata->{datavalues}->{$testcr}, |
274
|
|
|
|
|
|
|
($criteriasheetdata->{valuetypes}->{$testcr} || "b"), $criteria); |
275
|
|
|
|
|
|
|
} |
276
|
500
|
|
|
|
|
578
|
$testok = 1; |
277
|
500
|
|
|
|
|
693
|
last CRITERIAROW; |
278
|
|
|
|
|
|
|
} |
279
|
2366
|
100
|
|
|
|
6958
|
next unless $testok; |
280
|
|
|
|
|
|
|
|
281
|
500
|
|
|
|
|
1306
|
$cr = |
282
|
|
|
|
|
|
|
cr_to_coord($targetcol, $dbrow1num + $i) |
283
|
|
|
|
|
|
|
; # get cell of this row to do the function on |
284
|
500
|
|
|
|
|
1350
|
$value1 = $dbsheetdata->{datavalues}->{$cr}; |
285
|
500
|
|
|
|
|
958
|
$tostype = $dbsheetdata->{valuetypes}->{$cr}; |
286
|
500
|
|
50
|
|
|
984
|
$tostype ||= "b"; |
287
|
500
|
50
|
|
|
|
988
|
if ($tostype eq "b") { # blank |
288
|
0
|
|
|
|
|
0
|
$value1 = 0; |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
500
|
50
|
|
|
|
1104
|
$count += 1 if substr($tostype, 0, 1) eq "n"; |
292
|
500
|
50
|
|
|
|
1086
|
$counta += 1 if substr($tostype, 0, 1) ne "b"; |
293
|
500
|
50
|
|
|
|
957
|
$countblank += 1 if substr($tostype, 0, 1) eq "b"; |
294
|
|
|
|
|
|
|
|
295
|
500
|
50
|
0
|
|
|
957
|
if (substr($tostype, 0, 1) eq "n") { |
|
|
0
|
|
|
|
|
|
296
|
500
|
|
|
|
|
587
|
$sum += $value1; |
297
|
500
|
|
|
|
|
542
|
$product *= $value1; |
298
|
500
|
100
|
|
|
|
1113
|
$maxval = |
|
|
100
|
|
|
|
|
|
299
|
|
|
|
|
|
|
(defined $maxval) ? ($value1 > $maxval ? $value1 : $maxval) : $value1; |
300
|
500
|
50
|
|
|
|
975
|
$minval = |
|
|
100
|
|
|
|
|
|
301
|
|
|
|
|
|
|
(defined $minval) ? ($value1 < $minval ? $value1 : $minval) : $value1; |
302
|
500
|
100
|
|
|
|
810
|
if ($count eq 1) |
303
|
|
|
|
|
|
|
{ # initialize with with first values for variance used in STDEV, VAR, etc. |
304
|
174
|
|
|
|
|
341
|
$mk1 = $value1; |
305
|
174
|
|
|
|
|
232
|
$sk1 = 0; |
306
|
|
|
|
|
|
|
} else { # Accumulate S sub 1 through n as per Knuth noted above |
307
|
326
|
|
|
|
|
523
|
$mk = $mk1 + ($value1 - $mk1) / $count; |
308
|
326
|
|
|
|
|
439
|
$sk = $sk1 + ($value1 - $mk1) * ($value1 - $mk); |
309
|
326
|
|
|
|
|
329
|
$sk1 = $sk; |
310
|
326
|
|
|
|
|
351
|
$mk1 = $mk; |
311
|
|
|
|
|
|
|
} |
312
|
500
|
|
66
|
|
|
2086
|
$resulttypesum = |
313
|
|
|
|
|
|
|
lookup_result_type($tostype, $resulttypesum || $tostype, |
314
|
|
|
|
|
|
|
$typelookup->{plus}); |
315
|
|
|
|
|
|
|
} elsif (substr($tostype, 0, 1) eq "e" |
316
|
|
|
|
|
|
|
&& substr($resulttypesum, 0, 1) ne "e") { |
317
|
0
|
|
|
|
|
0
|
$resulttypesum = $tostype; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
} |
320
|
|
|
|
|
|
|
|
321
|
182
|
|
100
|
|
|
423
|
$resulttypesum ||= "n"; |
322
|
|
|
|
|
|
|
|
323
|
182
|
100
|
|
|
|
940
|
if ($fname eq "DSUM") { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
324
|
94
|
|
|
|
|
659
|
push @$operand, { type => $resulttypesum, value => $sum }; |
325
|
|
|
|
|
|
|
} elsif ($fname eq "DPRODUCT") |
326
|
|
|
|
|
|
|
{ # may handle cases with text differently than some other spreadsheets |
327
|
6
|
|
|
|
|
33
|
push @$operand, { type => $resulttypesum, value => $product }; |
328
|
|
|
|
|
|
|
} elsif ($fname eq "DMIN") { |
329
|
7
|
|
50
|
|
|
59
|
push @$operand, { type => $resulttypesum, value => ($minval || 0) }; |
330
|
|
|
|
|
|
|
} elsif ($fname eq "DMAX") { |
331
|
8
|
|
50
|
|
|
81
|
push @$operand, { type => $resulttypesum, value => ($maxval || 0) }; |
332
|
|
|
|
|
|
|
} elsif ($fname eq "DCOUNT") { |
333
|
12
|
|
|
|
|
74
|
push @$operand, { type => "n", value => $count }; |
334
|
|
|
|
|
|
|
} elsif ($fname eq "DCOUNTA") { |
335
|
11
|
|
|
|
|
90
|
push @$operand, { type => "n", value => $counta }; |
336
|
|
|
|
|
|
|
} elsif ($fname eq "DAVERAGE") { |
337
|
13
|
50
|
|
|
|
24
|
if ($count > 0) { |
338
|
13
|
|
|
|
|
61
|
push @$operand, { type => $resulttypesum, value => ($sum / $count) }; |
339
|
|
|
|
|
|
|
} else { |
340
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#DIV/0!", value => 0 }; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
} elsif ($fname eq "DSTDEV") { |
343
|
5
|
50
|
|
|
|
19
|
if ($count > 1) { |
344
|
5
|
|
|
|
|
36
|
push @$operand, |
345
|
|
|
|
|
|
|
{ type => $resulttypesum, value => (sqrt($sk / ($count - 1))) }; |
346
|
|
|
|
|
|
|
} else { |
347
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#DIV/0!", value => 0 }; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
} elsif ($fname eq "DSTDEVP") { |
350
|
4
|
50
|
|
|
|
13
|
if ($count > 1) { |
351
|
4
|
|
|
|
|
28
|
push @$operand, |
352
|
|
|
|
|
|
|
{ type => $resulttypesum, value => (sqrt($sk / $count)) }; |
353
|
|
|
|
|
|
|
} else { |
354
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#DIV/0!", value => 0 }; |
355
|
|
|
|
|
|
|
} |
356
|
|
|
|
|
|
|
} elsif ($fname eq "DVAR") { |
357
|
2
|
50
|
|
|
|
10
|
if ($count > 1) { |
358
|
2
|
|
|
|
|
13
|
push @$operand, |
359
|
|
|
|
|
|
|
{ type => $resulttypesum, value => ($sk / ($count - 1)) }; |
360
|
|
|
|
|
|
|
} else { |
361
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#DIV/0!", value => 0 }; |
362
|
|
|
|
|
|
|
} |
363
|
|
|
|
|
|
|
} elsif ($fname eq "DVARP") { |
364
|
1
|
50
|
|
|
|
7
|
if ($count > 1) { |
365
|
1
|
|
|
|
|
9
|
push @$operand, { type => $resulttypesum, value => ($sk / $count) }; |
366
|
|
|
|
|
|
|
} else { |
367
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#DIV/0!", value => 0 }; |
368
|
|
|
|
|
|
|
} |
369
|
|
|
|
|
|
|
} elsif ($fname eq "DGET") { |
370
|
19
|
100
|
|
|
|
55
|
if ($count == 1) { |
|
|
50
|
|
|
|
|
|
371
|
10
|
|
|
|
|
47
|
push @$operand, { type => $resulttypesum, value => $sum }; |
372
|
|
|
|
|
|
|
} elsif ($count == 0) { |
373
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#VALUE!", value => 0 }; |
374
|
|
|
|
|
|
|
} else { |
375
|
9
|
|
|
|
|
35
|
push @$operand, { type => "e#NUM!", value => 0 }; |
376
|
|
|
|
|
|
|
} |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
|
379
|
182
|
|
|
|
|
904
|
return; |
380
|
|
|
|
|
|
|
} |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=head2 index_function |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=over |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
=item INDEX(range, rownum, colnum) |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
=back |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
=cut |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
sub index_function { |
393
|
|
|
|
|
|
|
|
394
|
12
|
|
|
12
|
1
|
33
|
my ($fname, $operand, $foperand, $errortext, $typelookup, $sheetdata) = @_; |
395
|
|
|
|
|
|
|
|
396
|
12
|
|
|
|
|
57
|
my ($range, $rangetype) = |
397
|
|
|
|
|
|
|
top_of_stack_value_and_type($sheetdata, $foperand, $errortext) |
398
|
|
|
|
|
|
|
; # get range |
399
|
12
|
50
|
|
|
|
47
|
if ($rangetype ne "range") { |
400
|
0
|
|
|
|
|
0
|
function_args_error($fname, $operand, $errortext); |
401
|
0
|
|
|
|
|
0
|
return 0; |
402
|
|
|
|
|
|
|
} |
403
|
12
|
|
|
|
|
50
|
my ($indexsheetdata, $col1num, $ncols, $row1num, $nrows) = |
404
|
|
|
|
|
|
|
decode_range_parts($sheetdata, $range, $rangetype); |
405
|
|
|
|
|
|
|
|
406
|
12
|
|
|
|
|
26
|
my $rowindex = 0; |
407
|
12
|
|
|
|
|
12
|
my $colindex = 0; |
408
|
12
|
|
|
|
|
14
|
my $tostype; |
409
|
|
|
|
|
|
|
|
410
|
12
|
50
|
|
|
|
31
|
if (scalar @$foperand) { # look for row number |
411
|
12
|
|
|
|
|
39
|
$rowindex = |
412
|
|
|
|
|
|
|
operand_as_number($sheetdata, $foperand, $errortext, \$tostype); |
413
|
12
|
50
|
33
|
|
|
74
|
if (substr($tostype, 0, 1) ne "n" || $rowindex < 0) { |
414
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#VALUE!", value => 0 }; |
415
|
0
|
|
|
|
|
0
|
return; |
416
|
|
|
|
|
|
|
} |
417
|
12
|
50
|
|
|
|
28
|
if (scalar @$foperand) { # look for col number |
418
|
12
|
|
|
|
|
48
|
$colindex = |
419
|
|
|
|
|
|
|
operand_as_number($sheetdata, $foperand, $errortext, \$tostype); |
420
|
12
|
50
|
33
|
|
|
66
|
if (substr($tostype, 0, 1) ne "n" || $colindex < 0) { |
421
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#VALUE!", value => 0 }; |
422
|
0
|
|
|
|
|
0
|
return; |
423
|
|
|
|
|
|
|
} |
424
|
12
|
50
|
|
|
|
38
|
if (scalar @$foperand) { |
425
|
0
|
|
|
|
|
0
|
function_args_error($fname, $operand, $errortext); |
426
|
0
|
|
|
|
|
0
|
return 0; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
} else { # col number missing |
429
|
0
|
0
|
|
|
|
0
|
if ($nrows == 1) { # if only one row, then rowindex is really colindex |
430
|
0
|
|
|
|
|
0
|
$colindex = $rowindex; |
431
|
0
|
|
|
|
|
0
|
$rowindex = 0; |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
12
|
50
|
33
|
|
|
64
|
if ($rowindex > $nrows || $colindex > $ncols) { |
437
|
0
|
|
|
|
|
0
|
push @$operand, { type => "e#REF!", value => 0 }; |
438
|
0
|
|
|
|
|
0
|
return; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
12
|
|
|
|
|
16
|
my ($result, $resulttype); |
442
|
|
|
|
|
|
|
|
443
|
12
|
50
|
|
|
|
30
|
if ($rowindex == 0) { |
444
|
0
|
0
|
|
|
|
0
|
if ($colindex == 0) { |
445
|
0
|
0
|
0
|
|
|
0
|
if ($nrows == 1 && $ncols == 1) { |
446
|
0
|
|
|
|
|
0
|
$result = cr_to_coord($col1num, $row1num); |
447
|
0
|
|
|
|
|
0
|
$resulttype = "coord"; |
448
|
|
|
|
|
|
|
} else { |
449
|
0
|
|
|
|
|
0
|
$result = |
450
|
|
|
|
|
|
|
cr_to_coord($col1num, $row1num) . "|" |
451
|
|
|
|
|
|
|
. cr_to_coord($col1num + $ncols - 1, $row1num + $nrows - 1) . "|"; |
452
|
0
|
|
|
|
|
0
|
$resulttype = "range"; |
453
|
|
|
|
|
|
|
} |
454
|
|
|
|
|
|
|
} else { |
455
|
0
|
0
|
|
|
|
0
|
if ($nrows == 1) { |
456
|
0
|
|
|
|
|
0
|
$result = cr_to_coord($col1num + $colindex - 1, $row1num); |
457
|
0
|
|
|
|
|
0
|
$resulttype = "coord"; |
458
|
|
|
|
|
|
|
} else { |
459
|
0
|
|
|
|
|
0
|
$result = |
460
|
|
|
|
|
|
|
cr_to_coord($col1num + $colindex - 1, $row1num) . "|" |
461
|
|
|
|
|
|
|
. cr_to_coord($col1num + $colindex - 1, $row1num + $nrows - 1) |
462
|
|
|
|
|
|
|
. "|"; |
463
|
0
|
|
|
|
|
0
|
$resulttype = "range"; |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
} |
466
|
|
|
|
|
|
|
} else { |
467
|
12
|
50
|
|
|
|
35
|
if ($colindex == 0) { |
468
|
0
|
0
|
|
|
|
0
|
if ($ncols == 1) { |
469
|
0
|
|
|
|
|
0
|
$result = cr_to_coord($col1num, $row1num + $rowindex - 1); |
470
|
0
|
|
|
|
|
0
|
$resulttype = "coord"; |
471
|
|
|
|
|
|
|
} else { |
472
|
0
|
|
|
|
|
0
|
$result = |
473
|
|
|
|
|
|
|
cr_to_coord($col1num, $row1num + $rowindex - 1) . "|" |
474
|
|
|
|
|
|
|
. cr_to_coord($col1num + $ncols - 1, $row1num + $rowindex - 1) |
475
|
|
|
|
|
|
|
. "|"; |
476
|
0
|
|
|
|
|
0
|
$resulttype = "range"; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
} else { |
479
|
12
|
|
|
|
|
49
|
$result = |
480
|
|
|
|
|
|
|
cr_to_coord($col1num + $colindex - 1, $row1num + $rowindex - 1); |
481
|
12
|
|
|
|
|
32
|
$resulttype = "coord"; |
482
|
|
|
|
|
|
|
} |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
|
485
|
12
|
|
|
|
|
42
|
push @$operand, { type => $resulttype, value => $result }; |
486
|
12
|
|
|
|
|
34
|
return; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head2 countif_sumif_functions |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
=over |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
=item COUNTIF(c1:c2,"criteria") |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=item SUMIF(c1:c2,"criteria") |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=back |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=cut |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
sub countif_sumif_functions { |
503
|
|
|
|
|
|
|
|
504
|
29
|
|
|
29
|
1
|
64
|
my ($fname, $operand, $foperand, $errortext, $typelookup, $sheetdata) = @_; |
505
|
|
|
|
|
|
|
|
506
|
29
|
|
|
|
|
57
|
my ($tostype, $tostype2, $sumrangevalue, $sumrangetype); |
507
|
|
|
|
|
|
|
|
508
|
29
|
|
|
|
|
114
|
my ($rangevalue, $rangetype) = |
509
|
|
|
|
|
|
|
top_of_stack_value_and_type($sheetdata, $foperand, $errortext) |
510
|
|
|
|
|
|
|
; # get range or coord |
511
|
29
|
|
|
|
|
144
|
my ($criteriavalue, $criteriatype) = |
512
|
|
|
|
|
|
|
operand_as_text($sheetdata, $foperand, $errortext, \$tostype) |
513
|
|
|
|
|
|
|
; # get criteria |
514
|
29
|
50
|
|
|
|
95
|
if ($fname eq "SUMIF") { |
515
|
29
|
100
|
|
|
|
118
|
if ((scalar @$foperand) == 1) { # three arg form of SUMIF |
|
|
50
|
|
|
|
|
|
516
|
5
|
|
|
|
|
19
|
($sumrangevalue, $sumrangetype) = |
517
|
|
|
|
|
|
|
top_of_stack_value_and_type($sheetdata, $foperand, $errortext); |
518
|
|
|
|
|
|
|
} elsif ((scalar @$foperand) == 0) { # two arg form |
519
|
24
|
|
|
|
|
408
|
$sumrangevalue = $rangevalue; |
520
|
24
|
|
|
|
|
47
|
$sumrangetype = $rangetype; |
521
|
|
|
|
|
|
|
} else { |
522
|
0
|
|
|
|
|
0
|
function_args_error($fname, $operand, $errortext); |
523
|
0
|
|
|
|
|
0
|
return 0; |
524
|
|
|
|
|
|
|
} |
525
|
|
|
|
|
|
|
} else { |
526
|
0
|
|
|
|
|
0
|
$sumrangevalue = $rangevalue; |
527
|
0
|
|
|
|
|
0
|
$sumrangetype = $rangetype; |
528
|
|
|
|
|
|
|
} |
529
|
|
|
|
|
|
|
|
530
|
29
|
|
50
|
|
|
224
|
my $ct = substr($criteriatype || '', 0, 1) || ''; |
531
|
29
|
50
|
|
|
|
124
|
if ($ct eq "n") { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
532
|
0
|
|
|
|
|
0
|
$criteriavalue = "$criteriavalue"; |
533
|
|
|
|
|
|
|
} elsif ($ct eq "e") { # error |
534
|
0
|
|
|
|
|
0
|
undef $criteriavalue; |
535
|
|
|
|
|
|
|
} elsif ($ct eq "b") { # blank here is undefined |
536
|
0
|
|
|
|
|
0
|
undef $criteriavalue; |
537
|
|
|
|
|
|
|
} |
538
|
|
|
|
|
|
|
|
539
|
29
|
100
|
66
|
|
|
154
|
if ($rangetype ne "coord" && $rangetype ne "range") { |
540
|
5
|
|
|
|
|
26
|
function_args_error($fname, $operand, $errortext); |
541
|
5
|
|
|
|
|
15
|
return 0; |
542
|
|
|
|
|
|
|
} |
543
|
|
|
|
|
|
|
|
544
|
24
|
50
|
33
|
|
|
196
|
if ( $fname eq "SUMIF" |
|
|
|
33
|
|
|
|
|
545
|
|
|
|
|
|
|
&& $sumrangetype ne "coord" |
546
|
|
|
|
|
|
|
&& $sumrangetype ne "range") { |
547
|
0
|
|
|
|
|
0
|
function_args_error($fname, $operand, $errortext); |
548
|
0
|
|
|
|
|
0
|
return 0; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
24
|
|
|
|
|
100
|
push @$foperand, { type => $rangetype, value => $rangevalue }; |
552
|
24
|
|
|
|
|
38
|
my @f2operand; # to allow for 3 arg form |
553
|
24
|
|
|
|
|
106
|
push @f2operand, { type => $sumrangetype, value => $sumrangevalue }; |
554
|
|
|
|
|
|
|
|
555
|
24
|
|
|
|
|
50
|
my $sum = 0; |
556
|
24
|
|
|
|
|
38
|
my $resulttypesum = ""; |
557
|
24
|
|
|
|
|
35
|
my $count = 0; |
558
|
|
|
|
|
|
|
|
559
|
24
|
|
|
|
|
64
|
while (@$foperand) { |
560
|
68
|
|
|
|
|
200
|
my $value1 = |
561
|
|
|
|
|
|
|
operand_value_and_type($sheetdata, $foperand, $errortext, \$tostype); |
562
|
68
|
|
|
|
|
403
|
my $value2 = |
563
|
|
|
|
|
|
|
operand_value_and_type($sheetdata, \@f2operand, $errortext, \$tostype2); |
564
|
|
|
|
|
|
|
|
565
|
68
|
100
|
|
|
|
242
|
next unless test_criteria($value1, $tostype, $criteriavalue); |
566
|
|
|
|
|
|
|
|
567
|
26
|
|
|
|
|
42
|
$count += 1; |
568
|
|
|
|
|
|
|
|
569
|
26
|
100
|
33
|
|
|
122
|
if (substr($tostype2, 0, 1) eq "n") { |
|
|
50
|
|
|
|
|
|
570
|
22
|
|
|
|
|
32
|
$sum += $value2; |
571
|
22
|
|
66
|
|
|
153
|
$resulttypesum = |
572
|
|
|
|
|
|
|
lookup_result_type($tostype2, $resulttypesum || $tostype2, |
573
|
|
|
|
|
|
|
$typelookup->{plus}); |
574
|
|
|
|
|
|
|
} elsif (substr($tostype2, 0, 1) eq "e" |
575
|
|
|
|
|
|
|
&& substr($resulttypesum, 0, 1) ne "e") { |
576
|
0
|
|
|
|
|
0
|
$resulttypesum = $tostype2; |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
} |
579
|
|
|
|
|
|
|
|
580
|
24
|
|
100
|
|
|
78
|
$resulttypesum ||= "n"; |
581
|
|
|
|
|
|
|
|
582
|
24
|
50
|
|
|
|
88
|
if ($fname eq "SUMIF") { |
|
|
0
|
|
|
|
|
|
583
|
24
|
|
|
|
|
128
|
push @$operand, { type => $resulttypesum, value => $sum }; |
584
|
|
|
|
|
|
|
} elsif ($fname eq "COUNTIF") { |
585
|
0
|
|
|
|
|
0
|
push @$operand, { type => "n", value => $count }; |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
24
|
|
|
|
|
102
|
return; |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
} |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=head2 columns_rows_function |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
=over |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
=item COLUMNS(c1:c2) |
597
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
=item ROWS(c1:c2) |
599
|
|
|
|
|
|
|
|
600
|
|
|
|
|
|
|
=back |
601
|
|
|
|
|
|
|
|
602
|
|
|
|
|
|
|
=cut |
603
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
sub columns_rows_function { |
605
|
|
|
|
|
|
|
|
606
|
21
|
|
|
21
|
1
|
46
|
my ($fname, $operand, $foperand, $errortext, $typelookup, $sheetdata) = @_; |
607
|
|
|
|
|
|
|
|
608
|
21
|
|
|
|
|
26
|
my ($value1, $tostype, $resultvalue, $resulttype); |
609
|
|
|
|
|
|
|
|
610
|
21
|
|
|
|
|
73
|
($value1, $tostype) = |
611
|
|
|
|
|
|
|
top_of_stack_value_and_type($sheetdata, $foperand, $errortext); |
612
|
|
|
|
|
|
|
|
613
|
21
|
100
|
|
|
|
95
|
if ($tostype eq "coord") { |
|
|
50
|
|
|
|
|
|
614
|
9
|
|
|
|
|
14
|
$resultvalue = 1; |
615
|
9
|
|
|
|
|
16
|
$resulttype = "n"; |
616
|
|
|
|
|
|
|
} elsif ($tostype eq "range") { |
617
|
12
|
|
|
|
|
61
|
my ($v1, $v2, $sequence) = split (/\|/, $value1); |
618
|
12
|
|
|
|
|
19
|
my ($sheet1, $sheet2); |
619
|
12
|
|
|
|
|
41
|
($v1, $sheet1) = split (/!/, $v1); |
620
|
12
|
|
|
|
|
41
|
($v2, $sheet2) = split (/!/, $v2); |
621
|
12
|
|
|
|
|
45
|
my ($c1, $r1) = coord_to_cr($v1); |
622
|
12
|
|
|
|
|
36
|
my ($c2, $r2) = coord_to_cr($v2); |
623
|
12
|
50
|
|
|
|
31
|
($c2, $c1) = ($c1, $c2) if ($c1 > $c2); |
624
|
12
|
50
|
|
|
|
38
|
($r2, $r1) = ($r1, $r2) if ($r1 > $r2); |
625
|
|
|
|
|
|
|
|
626
|
12
|
100
|
|
|
|
39
|
if ($fname eq "COLUMNS") { |
|
|
50
|
|
|
|
|
|
627
|
9
|
|
|
|
|
16
|
$resultvalue = $c2 - $c1 + 1; |
628
|
|
|
|
|
|
|
} elsif ($fname eq "ROWS") { |
629
|
3
|
|
|
|
|
10
|
$resultvalue = $r2 - $r1 + 1; |
630
|
|
|
|
|
|
|
} |
631
|
12
|
|
|
|
|
27
|
$resulttype = "n"; |
632
|
|
|
|
|
|
|
} else { |
633
|
0
|
|
|
|
|
0
|
$resultvalue = 0; |
634
|
0
|
|
|
|
|
0
|
$resulttype = "e#VALUE!"; |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
|
637
|
21
|
|
|
|
|
88
|
push @$operand, { type => $resulttype, value => $resultvalue }; |
638
|
|
|
|
|
|
|
|
639
|
21
|
|
|
|
|
61
|
return; |
640
|
|
|
|
|
|
|
|
641
|
|
|
|
|
|
|
} |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
=head2 text_function |
644
|
|
|
|
|
|
|
|
645
|
|
|
|
|
|
|
=over |
646
|
|
|
|
|
|
|
|
647
|
|
|
|
|
|
|
=item PLAINTEXT |
648
|
|
|
|
|
|
|
|
649
|
|
|
|
|
|
|
=back |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=cut |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
sub text_function { |
654
|
|
|
|
|
|
|
|
655
|
0
|
|
|
0
|
1
|
0
|
my ($fname, $operand, $foperand, $errortext, $typelookup, $sheetdata) = @_; |
656
|
|
|
|
|
|
|
|
657
|
0
|
|
|
|
|
0
|
my ($value1, $tostype, $resulttype); |
658
|
|
|
|
|
|
|
|
659
|
0
|
|
|
|
|
0
|
my $textstr = ""; |
660
|
0
|
|
|
|
|
0
|
$resulttype = ""; |
661
|
0
|
|
|
|
|
0
|
while (@$foperand) { |
662
|
0
|
|
|
|
|
0
|
$value1 = operand_as_text($sheetdata, $foperand, $errortext, \$tostype); |
663
|
0
|
0
|
0
|
|
|
0
|
if (substr($tostype, 0, 1) eq "t") { |
|
|
0
|
|
|
|
|
|
664
|
0
|
|
|
|
|
0
|
$textstr .= $value1; |
665
|
0
|
|
0
|
|
|
0
|
$resulttype = lookup_result_type($tostype, $resulttype || $tostype, |
666
|
|
|
|
|
|
|
$typelookup->{concat}); |
667
|
|
|
|
|
|
|
} elsif (substr($tostype, 0, 1) eq "e" |
668
|
|
|
|
|
|
|
&& substr($resulttype, 0, 1) ne "e") { |
669
|
0
|
|
|
|
|
0
|
$resulttype = $tostype; |
670
|
|
|
|
|
|
|
} |
671
|
|
|
|
|
|
|
} |
672
|
0
|
0
|
|
|
|
0
|
$resulttype = substr($resulttype, 0, 1) eq "t" ? "t" : $resulttype; |
673
|
0
|
|
|
|
|
0
|
push @$operand, { type => $resulttype, value => $textstr }; |
674
|
|
|
|
|
|
|
|
675
|
0
|
|
|
|
|
0
|
return; |
676
|
|
|
|
|
|
|
|
677
|
|
|
|
|
|
|
} |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
=head2 html_function |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
=over |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=item HTML |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
=back |
686
|
|
|
|
|
|
|
|
687
|
|
|
|
|
|
|
=cut |
688
|
|
|
|
|
|
|
|
689
|
|
|
|
|
|
|
sub html_function { |
690
|
|
|
|
|
|
|
|
691
|
0
|
|
|
0
|
1
|
0
|
my ($fname, $operand, $foperand, $errortext, $typelookup, $sheetdata) = @_; |
692
|
|
|
|
|
|
|
|
693
|
0
|
|
|
|
|
0
|
my ($value1, $tostype, $resulttype); |
694
|
|
|
|
|
|
|
|
695
|
0
|
|
|
|
|
0
|
my $textstr = ""; |
696
|
0
|
|
|
|
|
0
|
$resulttype = ""; |
697
|
0
|
|
|
|
|
0
|
while (@$foperand) { |
698
|
0
|
|
|
|
|
0
|
$value1 = operand_as_text($sheetdata, $foperand, $errortext, \$tostype); |
699
|
0
|
0
|
0
|
|
|
0
|
if (substr($tostype, 0, 1) eq "t") { |
|
|
0
|
|
|
|
|
|
700
|
0
|
|
|
|
|
0
|
$textstr .= $value1; |
701
|
0
|
|
0
|
|
|
0
|
$resulttype = lookup_result_type($tostype, $resulttype || $tostype, |
702
|
|
|
|
|
|
|
$typelookup->{concat}); |
703
|
|
|
|
|
|
|
} elsif (substr($tostype, 0, 1) eq "e" |
704
|
|
|
|
|
|
|
&& substr($resulttype, 0, 1) ne "e") { |
705
|
0
|
|
|
|
|
0
|
$resulttype = $tostype; |
706
|
|
|
|
|
|
|
} |
707
|
|
|
|
|
|
|
} |
708
|
0
|
0
|
|
|
|
0
|
$resulttype = substr($resulttype, 0, 1) eq "t" ? "th" : $resulttype; |
709
|
0
|
|
|
|
|
0
|
push @$operand, { type => $resulttype, value => $textstr }; |
710
|
|
|
|
|
|
|
|
711
|
0
|
|
|
|
|
0
|
return; |
712
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
=head1 HELPERS |
716
|
|
|
|
|
|
|
|
717
|
|
|
|
|
|
|
=head2 field_to_colnum |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
$colnum = field_to_colnum(\@sheetdata, $col1num, $ncols, $row1num, $fieldname, $fieldtype) |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
If fieldname is a number, uses it, otherwise looks up string in cells in row to find field number |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
If not found, returns 0. |
724
|
|
|
|
|
|
|
|
725
|
|
|
|
|
|
|
=cut |
726
|
|
|
|
|
|
|
|
727
|
|
|
|
|
|
|
sub field_to_colnum { |
728
|
|
|
|
|
|
|
|
729
|
400
|
|
|
400
|
1
|
696
|
my ($sheetdata, $col1num, $ncols, $row1num, $fieldname, $fieldtype) = @_; |
730
|
|
|
|
|
|
|
|
731
|
400
|
50
|
|
|
|
975
|
if (substr($fieldtype, 0, 1) eq "n") { # number - return it if legal |
732
|
0
|
0
|
0
|
|
|
0
|
if ($fieldname <= 0 || $fieldname > $ncols) { |
733
|
0
|
|
|
|
|
0
|
return 0; |
734
|
|
|
|
|
|
|
} |
735
|
0
|
|
|
|
|
0
|
return int($fieldname); |
736
|
|
|
|
|
|
|
} |
737
|
|
|
|
|
|
|
|
738
|
400
|
50
|
|
|
|
923
|
if (substr($fieldtype, 0, 1) ne "t") { # must be text otherwise |
739
|
0
|
|
|
|
|
0
|
return 0; |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
|
742
|
400
|
|
|
|
|
1302
|
$fieldname = decode('utf8', $fieldname); # change UTF-8 bytes to chars |
743
|
400
|
|
|
|
|
12655
|
$fieldname = lc $fieldname; |
744
|
|
|
|
|
|
|
|
745
|
400
|
|
|
|
|
561
|
my ($cr, $value); |
746
|
|
|
|
|
|
|
|
747
|
400
|
|
|
|
|
1029
|
for (my $i = 0 ; $i < $ncols ; $i++) |
748
|
|
|
|
|
|
|
{ # look through column headers for a match |
749
|
951
|
|
|
|
|
2338
|
$cr = cr_to_coord($col1num + $i, $row1num); |
750
|
951
|
|
|
|
|
2386
|
$value = $sheetdata->{datavalues}->{$cr}; |
751
|
951
|
|
|
|
|
2126
|
$value = decode('utf8', $value); |
752
|
951
|
|
|
|
|
20746
|
$value = lc $value; #ignore case |
753
|
951
|
100
|
|
|
|
2692
|
next if $value ne $fieldname; # no match |
754
|
400
|
|
|
|
|
939
|
return $i + 1; # match |
755
|
|
|
|
|
|
|
} |
756
|
0
|
|
|
|
|
|
return 0; # looked at all and no match |
757
|
|
|
|
|
|
|
} |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
1; |
760
|
|
|
|
|
|
|
|
761
|
|
|
|
|
|
|
__END__ |