File Coverage

blib/lib/Language/FormulaEngine/Namespace/Default.pm
Criterion Covered Total %
statement 219 241 90.8
branch 74 106 69.8
condition 35 53 66.0
subroutine 71 76 93.4
pod 0 52 0.0
total 399 528 75.5


line stmt bran cond sub pod time code
1             package Language::FormulaEngine::Namespace::Default;
2 4     4   4147 use parent 'Language::FormulaEngine::Namespace';
  4         1017  
  4         28  
3 4     4   213 use strict;
  4         11  
  4         104  
4 4     4   20 use warnings FATAL => 'numeric', 'uninitialized';
  4         26  
  4         132  
5 4     4   23 use Try::Tiny;
  4         9  
  4         198  
6 4     4   90 use List::Util ();
  4         11  
  4         100  
7 4     4   1909 use Math::Trig ();
  4         42580  
  4         103  
8 4     4   32 use Scalar::Util ();
  4         9  
  4         88  
9 4     4   22 use POSIX ();
  4         11  
  4         92  
10             # POSIX::round isn't available everywhere
11 4 50   4   312 BEGIN { eval 'use POSIX "round"; 1;' or eval 'sub round { sprintf "%.*f", $_[1]||0, $_[0] }' }
  4     4   24  
  4         11  
  4         38  
12 4     4   28 use Language::FormulaEngine::Error ':all';
  4         12  
  4         493  
13 4     4   3264 use DateTime;
  4         1461568  
  4         205  
14 4     4   3093 use DateTime::Format::Flexible;
  4         557092  
  4         61  
15 4     4   471 use namespace::clean;
  4         9  
  4         45  
16              
17             # ABSTRACT: Default spreadsheet-like set of functions and behavior
18             our $VERSION = '0.06'; # VERSION
19              
20             # No official versioned namespace yet, but this code is for when I publish one.
21              
22             #sub _fake_require {
23             # my $version= shift;
24             # $INC{'Language/FormulaEngine/Namespace/Default/V'.$version.'.pm'}=
25             # $INC{'Language/FormulaEngine/Namespace/Default.pm'};
26             #}
27             #
28             #sub _declare_versioned_namespace {
29             # my ($from_ver, $to_ver, @list)= @_;
30             # no strict 'refs';
31             # my $from_stash= \%{ __PACKAGE__ . '::V' . $from_ver . '::' };
32             # my $to_stash= \%{ __PACKAGE__ . '::V' . $to_ver . '::' };
33             # if (@list) {
34             # for (@list) {
35             # defined $from_stash->{$_} or die "Version $from_ver does not contain method $_";
36             # $to_stash->{$_}= $from_stash->{$_};
37             # }
38             # } else {
39             # for (keys %$from_stash) {
40             # $to_stash->{$_}= $from_stash->{$_}
41             # if defined $from_stash->{$_}{CODE} && !defined $to_stash->{$_};
42             # }
43             # }
44             # @{ __PACKAGE__ . '::V' . $to_ver . '::ISA' }= ( 'Language::FormulaEngine::Namespace' );
45             #}
46             #
47             #_fake_require 0;
48             #@Language::FormulaEngine::Namespace::Default::V0::ISA= ( __PACKAGE__ );
49              
50              
51             *fn_sum= *List::Util::sum0;
52             sub perlgen_sum {
53 12     12 0 103 my ($self, $compiler, $node)= @_;
54 12         23 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  12         47  
55 12         114 return '( '.join(' + ', @arg_code).' )';
56             }
57              
58             sub fn_negative {
59 2 50   2 0 8 @_ == 1 or die "Can only negate a single value, not a list\n";
60 2         10 return -$_[0];
61             }
62             sub perlgen_negative {
63 2     2 0 15 my ($self, $compiler, $node)= @_;
64 2         4 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  2         6  
65 2 50       9 @arg_code == 1 or die "Can only negate a single value, not a list\n";
66 2         10 return '(-('.$arg_code[0].'))';
67             }
68              
69             *fn_mul= *List::Util::product;
70             sub perlgen_mul {
71 14     14 0 121 my ($self, $compiler, $node)= @_;
72 14         29 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  14         39  
73 14         127 return '( '.join(' * ', @arg_code).' )';
74             }
75              
76             sub fn_div {
77 1 50   1 0 5 @_ == 2 or die "div() takes exactly two arguments\n";
78 1 50       13 $_[1] or die "division by zero\n";
79 1         15 return $_[0] / $_[1];
80             }
81             sub perlgen_div {
82 4     4 0 36 my ($self, $compiler, $node)= @_;
83 4         9 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  4         12  
84 4         26 return '( '.join(' / ', @arg_code).' )';
85             }
86              
87             sub nodeval_and { # customize nodeval_ to provide lazy evaluation of arguments
88 3     3 0 8 my ($self, $node)= @_;
89             $_->evaluate($self) or return 0
90 3   100     5 for @{ $node->parameters };
  3         10  
91 2         52 return 1;
92             }
93             sub perlgen_and {
94 4     4 0 34 my ($self, $compiler, $node)= @_;
95 4         8 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  4         15  
96 4         67 return '( ('.join(' and ', @arg_code).')? 1 : 0)';
97             }
98              
99             sub nodeval_or {
100 0     0 0 0 my ($self, $node)= @_;
101             $_->evaluate($self) and return 1
102 0   0     0 for @{ $node->parameters };
  0         0  
103 0         0 return 0;
104             }
105             sub perlgen_or {
106 0     0 0 0 my ($self, $compiler, $node)= @_;
107 0         0 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  0         0  
108 0         0 return '( ('.join(' or ', @arg_code).')? 1 : 0)';
109             }
110              
111             sub fn_not {
112 2 50   2 0 10 @_ == 1 or die "Too many arguments to 'not'\n";
113 2 100       16 return $_[0]? 0 : 1;
114             }
115             sub perlgen_not {
116 3     3 0 29 my ($self, $compiler, $node)= @_;
117 3         8 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  3         12  
118 3 50       14 @arg_code == 1 or die "Too many arguments to 'not'\n";
119 3         18 return '('.$arg_code[0].'? 0 : 1)';
120             }
121              
122             sub fn_compare {
123 20     20 0 78 my $left= shift;
124 20         54 while (@_) {
125 26         54 my $op= shift;
126 26         36 my $right= shift;
127 26   66     136 my $numeric= Scalar::Util::looks_like_number($left) && Scalar::Util::looks_like_number($right);
128 26 100 66     203 if ($op eq '==' or $op eq '!=') {
    100 100        
    50 66        
129 2 50       12 return 0 unless ($numeric? ($left == $right) : ($left eq $right)) == ($op eq '==');
    50          
130             }
131             elsif ($op eq '>=' or $op eq '<') {
132 6 100       20 return 0 unless ($numeric? ($left >= $right) : ($left ge $right)) == ($op eq '>=');
    50          
133             }
134             elsif ($op eq '<=' or $op eq '>') {
135 18 50       80 return 0 unless ($numeric? ($left <= $right) : ($left le $right)) == ($op eq '<=');
    100          
136             }
137             else {
138 0         0 die "Unhandled operator '$op' in compare()\n";
139             }
140 20         52 $left= $right;
141             }
142 14         84 return 1;
143             }
144              
145              
146             sub fn_choose {
147 2 50 33 2 0 17 $_[0] > 0 and $_[0] < @_ or die "CHOSE() selector out of bounds ($_[0])";
148 2         12 return $_[$_[0]];
149             }
150              
151             sub nodeval_if { # customize nodeval_ to provide lazy evaluation of arguments
152 2     2 0 7 my ($self, $node)= @_;
153 2 50       4 @{$node->parameters} == 3 or die "IF(test, when_true, when_false) requires all 3 parameters\n";
  2         8  
154 2         7 my $bool= $node->parameters->[0]->evaluate($self);
155 2 100       8 return $node->parameters->[$bool? 1 : 2]->evaluate($self);
156             }
157             sub perlgen_if {
158 4     4 0 35 my ($self, $compiler, $node)= @_;
159 4         11 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  4         11  
160 4 50       20 @arg_code == 3 or die "IF(test, when_true, when_false) requires all 3 parameters\n";
161 4         34 return '( '.$arg_code[0].'? '.$arg_code[1].' : '.$arg_code[2].' )';
162             }
163              
164             sub nodeval_iferror {
165 6     6 0 13 my ($self, $node)= @_;
166 6         12 my $ret;
167             try {
168 6     6   261 $ret= $node->parameters->[0]->evaluate($self);
169             } catch {
170 5     5   88 my $err= $node->parameters->[1];
171 5 50       17 $ret= defined $err? $err->evaluate($self) : '';
172 6         51 };
173 5         64 return $ret;
174             }
175             sub perlgen_iferror {
176 6     6 0 128 my ($self, $compiler, $node)= @_;
177 6         9 my @arg_code= map $compiler->perlgen($_), @{$node->parameters};
  6         21  
178 6         40 return '(do { local $@; my $x; eval { $x=('.$arg_code[0].'); 1 }? $x : ('.$arg_code[1].') })';
179             }
180              
181             sub nodeval_ifs {
182 3     3 0 9 my ($self, $node)= @_;
183 3 50       5 (my @todo= @{$node->parameters}) & 1
  3         10  
184             and die "IFS(cond, val, ...) requires an even number of parameters\n";
185 3         12 while (@todo) {
186 6         17 my ($cond, $val)= splice @todo, 0, 2;
187 6 100       20 return $val->evaluate($self) if $cond->evaluate($self);
188             }
189 1         9 die "IFS() had no true conditions\n";
190             }
191             sub perlgen_ifs {
192 3     3 0 24 my ($self, $compiler, $node)= @_;
193 3 50       8 (my @arg_code= map $compiler->perlgen($_), @{$node->parameters}) & 1
  3         10  
194             and die "IFS(cond, val, ...) requires an even number of parameters\n";
195 3         9 my $expr= '(';
196 3         10 while (@arg_code) {
197 6         16 my ($cond, $val)= splice @arg_code, 0, 2;
198 6         24 $expr .= "($cond)? ($val) : ";
199             }
200 3         8 $expr .= 'die "IFS() had no true conditions\n")';
201 3         16 return $expr;
202             }
203              
204             sub fn_na {
205 2     2 0 15 die ErrNA("NA");
206             }
207              
208              
209             *fn_abs= *CORE::abs;
210             *fn_acos= *Math::Trig::acos;
211             *fn_acot= *Math::Trig::acot;
212             *fn_asin= *Math::Trig::asin;
213             *fn_atan= *Math::Trig::atan;
214              
215             sub fn_atan2 {
216             # Perl differs in argument order from popular spreadsheet programs
217 2     2 0 18 atan2($_[1], $_[0])
218             }
219              
220             sub fn_average {
221 2     2 0 23 List::Util::sum0(@_) / @_;
222             }
223              
224             sub fn_base {
225 4     4 0 17 my ($num, $radix, $min_length)= @_;
226 4         9 my $digits= '';
227 4         12 while ($num > 0) {
228 4     4   8829 use integer;
  4         44  
  4         40  
229 16         33 ($num, my $digit)= ($num / $radix, $num % $radix);
230 16 50       56 $digits= chr(($digit < 10? 48 : 65) + $digit) . $digits;
231             }
232 4   100     16 my $pad= ($min_length||0) - length $digits;
233 4 100       30 return $pad > 0? '0'x$pad . $digits : $digits;
234             }
235              
236             *fn_cos= *CORE::cos;
237             *fn_degrees= *Math::Trig::rad2deg;
238             *fn_exp= *CORE::exp;
239             sub fn_fact {
240 6     6 0 23 my $n= int($_[0]);
241 6 100       27 return 1 unless $n;
242 4 50       12 $n > 0 or die ErrNUM("Can't compute factorial of negative number '$n'");
243 4         34 List::Util::product(1 .. $n);
244             }
245              
246             *fn_min= *List::Util::min;
247             *fn_max= *List::Util::max;
248             sub fn_mod {
249 8     8 0 32 my ($num, $modulo)= @_;
250 8 50       23 $modulo+0 or die ErrNUM("MOD($num, $modulo): can't claculate modulus-0");
251 8         45 $num % $modulo;
252             }
253              
254             *fn_pi= *Math::Trig::pi;
255              
256             sub fn_round {
257 6     6 0 46 my ($num, $digits)= @_;
258 6   100     43 my $scale= 0.1 ** ($_[1] || 0);
259 6         70 return round($num / $scale) * $scale;
260             }
261              
262             our $epsilon= 5e-14; # fudge factor for avoiding floating point rounding errors
263             sub fn_ceiling {
264 18     18 0 65 my ($num, $step)= @_;
265 18 100       55 $step= 1 unless defined $step;
266 18         162 return POSIX::ceil($num / $step - $epsilon) * $step;
267             }
268             sub fn_floor {
269 18     18 0 61 my ($num, $step)= @_;
270 18 100       52 $step= 1 unless defined $step;
271 18         150 return POSIX::floor($num / $step + $epsilon) * $step;
272             }
273             sub fn_roundup {
274 8   100 8 0 70 fn_ceiling($_[0], 0.1 ** ($_[1] || 0));
275             }
276             sub fn_rounddown {
277 8   100 8 0 68 fn_floor($_[0], 0.1 ** ($_[1] || 0));
278             }
279              
280             sub fn_power {
281 2 50   2 0 12 @_ == 2 or die ErrInval("POWER() takes 2 arguments");
282 2         15 return $_[0] ** $_[1];
283             }
284              
285             *fn_rand= *CORE::rand;
286             *fn_sin= *CORE::sin;
287             *fn_sqrt= *CORE::sqrt;
288             *fn_tan= *Math::Trig::tan;
289              
290              
291             *fn_char= *CORE::chr;
292             sub fn_clean {
293 14     14 0 53 my $str= shift;
294 14         99 $str =~ s/[[:^print:]]+//g;
295 14         84 $str;
296             }
297             *fn_code= *CORE::ord;
298             *fn_upper= *CORE::uc;
299             *fn_lower= *CORE::lc;
300              
301             sub fn_replace {
302 0 0   0 0 0 @_ == 4 or die ErrInval("REPLACE() takes 4 arguments");
303 0         0 my ($text, $ofs, $n, $newtext)= @_;
304 0         0 substr($text, $ofs, $n)= $newtext;
305 0         0 return $text;
306             }
307              
308             *fn_substr= *CORE::substr;
309             *fn_len= *CORE::length;
310              
311             sub fn_concatenate {
312 0     0 0 0 join '', @_;
313             }
314             *fn_concat= *fn_concatenate;
315             *fn_join= *CORE::join;
316              
317             sub fn_find {
318 0     0 0 0 my ($needle, $haystack, $ofs)= @_;
319 0 0 0     0 $ofs= 1 unless $ofs && $ofs > 0;
320 0         0 return index($haystack, $needle, $ofs) + 1;
321             }
322              
323             sub fn_fixed {
324 10     10 0 41 my ($number, $places, $comma)= @_;
325 10 100       31 $places= 2 unless defined $places;
326 10 100 66     43 $comma= ',' unless defined $comma && (!$comma or $comma eq '.');
      66        
327 10 100       86 $number= $places > 0? sprintf("%.*f", $places, $number) : fn_round($number, $places);
328 10 100       27 if ($comma) {
329 8 100       30 $number =~ s/\./,/ if $comma eq '.';
330 8 100       35 my $tmp= reverse substr($number, 0, $places > 0? -($places+1) : length $number);
331 8         39 $tmp =~ s/(\d\d\d)(?=\d)/$1$comma/g;
332 8 100       31 substr($number, 0, $places > 0? -($places+1) : length $number)= reverse $tmp;
333             }
334 10         64 return $number;
335             }
336              
337             sub fn_trim {
338 12     12 0 44 my $str= shift;
339 12         61 $str =~ s/\p{Space}+/ /g;
340 12         37 $str =~ s/^ //;
341 12         34 $str =~ s/ $//;
342 12         69 $str;
343             }
344              
345              
346             sub fn_datevalue {
347 82     82 0 4480 my $date= shift;
348 82 100 66     588 return $date if ref $date && ref($date)->isa('DateTime');
349 34     34   1558 try { DateTime::Format::Flexible->parse_datetime($date) }
350 34     2   261 catch { die ErrInval("Not a date: '$date'") };
  2         20533  
351             }
352 4     4   12758 BEGIN { *_date= *fn_datevalue; } # for convenience
353             sub fn_date {
354 4     4 0 17 my ($y, $m, $d)= @_;
355 4     4   198 try { DateTime->new(year => $y, month => $m, day => $d) }
356 4 50 33 2   35 catch { die ErrInval(ref $_ && $_->can("message")? $_->message : "$_") };
  2         2448  
357             }
358              
359             sub fn_datedif {
360 2     2 0 10 my ($start, $end, $unit)= @_;
361 2   50     9 $unit= uc($unit || '');
362 2 50       7 if ($unit eq 'Y') { return _date($end)->delta_md(_date($start))->in_units('years') }
  0         0  
363 2 50       7 if ($unit eq 'M') { return _date($end)->delta_md(_date($start))->in_units('months') }
  2         9  
364 0 0       0 if ($unit eq 'D') { return _date($end)->delta_days(_date($start))->in_units('days') }
  0         0  
365 0         0 die ErrInval "Unsupported datedif unit '$unit'";
366             }
367             sub fn_day {
368 8     8 0 6088 _date($_[0])->day
369             }
370             sub fn_days {
371 4     4 0 21 my ($end, $start)= ( _date($_[0]), _date($_[1]) );
372 4         2315 my $n= $end->delta_days($start)->in_units('days');
373 4 100       704 return $end > $start? $n : -$n;
374             }
375             sub fn_eomonth {
376 6     6 0 30 my ($start, $m_ofs)= @_;
377 6 100       21 $m_ofs= 0 unless @_ > 1;
378 6         19 _date($start)->clone->add(months => $m_ofs+1)->truncate(to => 'month')->subtract(days => 1);
379             }
380             sub fn_hour {
381 4     4 0 21 _date($_[0])->hour
382             }
383             sub fn_minute {
384 2     2 0 12 _date($_[0])->minute
385             }
386             sub fn_month {
387 2     2 0 11 _date($_[0])->month
388             }
389             sub fn_now {
390 2     2 0 13 DateTime->now;
391             }
392             sub fn_second {
393 2     2 0 13 _date($_[0])->second
394             }
395             sub fn_today {
396 2     2 0 15 DateTime->now->truncate(to => 'day');
397             }
398             sub fn_weekday {
399 44     44 0 180 my ($date, $standard)= @_;
400 44         115 my $day_mon1= _date($date)->day_of_week;
401 44 100 100     330 return $day_mon1 % 7 + 1 if !$standard or $standard == 1;
402 36 100 100     159 return $day_mon1 if $standard == 2 or $standard == 11;
403 28 100       86 return $day_mon1-1 if $standard == 3;
404 24 50 33     189 return ($day_mon1 - ($standard - 10)) % 7 + 1 if $standard >= 12 && $standard <= 17;
405 0         0 die ErrInval("No known weekday standard '$standard'");
406             }
407             sub fn_year {
408 2     2 0 12 _date($_[0])->year
409             }
410              
411             # Perl older than 5.16 can't actually reference the functions in CORE:: namespace.
412             # For example, perl -e 'my $sub= sub { CORE::ord(shift) }; print $sub->("A")' works but
413             # perl -e 'my $sub= sub { &CORE::ord }; print $sub->("A")' does not. Neither does
414             # perl -e 'CORE->can("ord")->("A")', nor does *fn_foo= *CORE::foo used above.
415             # I could of course just wrap each core function with a function defined in this
416             # package, but it would be a needless performance hit for modern perl, and clutter
417             # the code above.
418             unless (CORE->can("abs")) {
419             require Sub::Util;
420             my $stash= \%Language::FormulaEngine::Namespace::Default::;
421             for my $fn (grep /^fn_/, keys %$stash) {
422             my $symname= "$stash->{$fn}";
423             next unless $symname =~ s/^\*CORE/CORE/;
424             #print "# Stash $fn is $symname\n";
425             # prototypes make this annoying
426             my $code= $symname eq 'CORE::substr'? "sub { substr(shift, \@_) }"
427             : $symname eq 'CORE::join'? "sub { join(shift, \@_) }"
428             : "sub { $symname(shift) }";
429             my $sub= eval $code or die "$@";
430 4     4   38 no strict 'refs'; no warnings 'redefine';
  4     4   11  
  4         209  
  4         29  
  4         9  
  4         365  
431             # The name of the sub needs to remain as CORE::foo else test cases will fail
432             *$fn= Sub::Util::set_subname($symname, $sub);
433             }
434             }
435              
436              
437             1;
438              
439             __END__
440              
441             =pod
442              
443             =encoding UTF-8
444              
445             =head1 NAME
446              
447             Language::FormulaEngine::Namespace::Default - Default spreadsheet-like set of functions and behavior
448              
449             =head1 VERSION
450              
451             version 0.06
452              
453             =head1 DESCRIPTION
454              
455             This is a L<namespace|Language::FormulaEngine::Namespace> containing many spreadsheet-like
456             functions. It aims for spreadsheet similarity rather than compatibility; the goal to give
457             users of the FormulaEngine a familiar environmet rather than to try duplicating all features
458             and misfeatures Excel.
459              
460             =head2 Core Grammar Functionality
461              
462             These are the methods that implement the infix operators.
463              
464             =over
465              
466             =item C<< sum( num1, num2 ... ) >>
467              
468             =item C<< negative( num1 ) >>
469              
470             =item C<< mul( num1, num2, ... ) >>
471              
472             =item C<< div( numerator, denominator ) >>
473              
474             =item C<< and( bool1, bool2, ... ) >>
475              
476             This applies perl-ish boolean semantics to each argument, and returns a numeric 0 or 1.
477             No arguments are evaluated after the first false value.
478              
479             =item C<< or( bool1, bool2, ... ) >>
480              
481             This applies perl-ish boolean semantics to each argument, and returns a numeric 0 or 1.
482             No arguments are evaluated after the first true value.
483              
484             =item C<< not( bool1 ) >>
485              
486             This applies perl-ish boolean semantics to the argument and returns numeric 1 or 0.
487              
488             =item C<< compare( val1, op, val2, ...op, val ) >>
489              
490             This compares two or more values against the 6 canonical operators
491             C<< "<", "<=", ">", ">=", "==", "!=" >> and returns 0 or 1.
492              
493             It uses numeric comparison if both sides of an operator C<looks_like_number>, and uses string
494             comparison otherwise.
495              
496             =back
497              
498             =head2 Utility Functions
499              
500             =over
501              
502             =item C<< choose( offset, val1, val2, val3, ... ) >>
503              
504             Given a 1-based offset, return the value of the Nth parameter.
505              
506             =item C<< if( condition, val_if_true, val_if_false ) >>
507              
508             If C<condition> is "true" (Perl interpretation) return C<val_if_true>, else C<val_if_false>.
509              
510             =item C<< iferror( value_maybe_error, alternate_value ) >>
511              
512             If C<value_maybe_error> does not throw an exception, return it, else return the
513             C<alternate_value>.
514              
515             =item C<< ifs( condition1, value1, condition2, value2, ... ) >>
516              
517             A simplified sequence of IF functions. If C<condition1> is true, it returns C<value1>, else if
518             C<condition2> is true it returns C<value2>, and so on. If no condition is true it dies. (use
519             a final true condition and value to provide a default)
520              
521             =item C<< na() >>
522              
523             Throw an NA exception.
524              
525             =back
526              
527             =head2 Math Functions
528              
529             =over
530              
531             =item C<< abs( number ) >>
532              
533             Return absolute value of number
534              
535             =item C<< acos( ratio ) >>
536              
537             Return angle in radians of the ratio adjacent/hypotenuse.
538              
539             =item C<< acot( ratio ) >>
540              
541             Return angle in radians of the ratio adjacent/opposite.
542              
543             =item C<< asin( ratio ) >>
544              
545             Return angle in radians of the ratio opposite/hypotenuse.
546              
547             =item C<< atan( ratio ) >>
548              
549             Return angle in radians of the ratio opposite/adjacent.
550              
551             =item C<< atan2( x, y ) >>
552              
553             Same as atan, but without division, so x=0 returns PI/2 instead of division error.
554              
555             =item C<< average( num1, ... ) >>
556              
557             Return sum of numbers divided by number of arguments
558              
559             =item C<< base( num1, radix, min_length=0 ) >>
560              
561             Return number converted to different base, with optional leading zeroes to reach min_length.
562              
563             =item C<< ceiling( number, step=1 ) >>
564              
565             Round a number up to the next multiple of C<step>. If step is negative, this rounds away from
566             zero in the negative direction.
567              
568             =item C<< cos( angle ) >>
569              
570             Cosine of C<angle> in radians
571              
572             =item C<< cot( ratio ) >>
573              
574             Return the angle for the triangle ratio adjacent/opposite.
575              
576             =item C<< degrees( angle_in_radians ) >>
577              
578             Convert radians to degrees
579              
580             =item C<< exp( power ) >>
581              
582             Return base of the natural log raised to the specified power.
583              
584             =item C<< fact( n ) >>
585              
586             Compute factorial of C<n>. (C<< 1 * 2 * 3 * ... n >>)
587              
588             =item C<< floor( number, step=1 ) >>
589              
590             Round a number down to the previous multiple of C<step>. If step is negative, this rounds
591             toward zero in the positive direction.
592              
593             =item C<< max( number, ... ) >>
594              
595             Return largest value in list
596              
597             =item C<< min( number, ... ) >>
598              
599             Return smallest value in list
600              
601             =item C<< mod( number, modulo ) >>
602              
603             Returns true modulous of a number. This uses Perl's (and math's) definition. For the Excel-
604             compatible MOD function, see C<remainder>.
605              
606             =item C<< pi() >>
607              
608             Value of π
609              
610             =item C<< radians( angle_in_degrees ) >>
611              
612             Convert degrees to radians.
613              
614             =item C<< rand( range=1 ) >>
615              
616             Returns pseudo-random value greater or equal to 0 and less than C<range>. This uses perl's
617             (C's) built-in C<< rand() >> function which is likely not as good as the generators used by
618             spreadsheet programs, but I didn't want to add a hefty dependency.
619              
620             =item C<< remainder( number, divisor ) >>
621              
622             Return the number after subtracting the biggest multiple of divisor that can be removed from it.
623             The remainder's sign will be the same as the sign of C<divisor> (unless remainder is zero).
624              
625             =item C<< round( number, digits=0 ) >>
626              
627             Round NUMBER to DIGITS decimal places of precision. Uses the IEEE
628             5-round-to-even algorithm that C gives us. DIGITS defaults to 0,
629             making it round to the nearest integer.
630              
631             Dies if you attempt to round something that isn't a number.
632              
633             =item C<< roundup( number, digits=0 ) >>
634              
635             Like L</round>, but always round up. See also L</ceiling>.
636              
637             =item C<< rounddown( number, digits=0 ) >>
638              
639             Like L</round>, but always round down. See also L</floor>.
640              
641             =item C<< sign( value ) >>
642              
643             Return 1, 0, or -1 depending on the sign of C<value>.
644              
645             =item C<< sin( angle ) >>
646              
647             Returns ratio of opposite/adjacent for a given angle in radians.
648              
649             =item C<< sqrt( number ) >>
650              
651             Return square root of a number.
652              
653             =item C<< tan( angle ) >>
654              
655             Return ratio of opposite/adjacent for an angle.
656              
657             =back
658              
659             =head2 String Functions
660              
661             =over
662              
663             =item C<< char( codepoint_value ) >>
664              
665             Return a unicode character.
666              
667             =item C<< clean( string ) >>
668              
669             Returns C<string> after removing all non-printable characters (defined as C<< [:^print:] >> )
670              
671             =item C<< code( string ) >>
672              
673             Opposite of L</char>, known as C<ord()> in other languages. Returns the unicode codepoint
674             number of the first character of the string.
675              
676             =item C<< concat, concatenate( string, ... ) >>
677              
678             Returns all arguments concatenated as a string
679              
680             =item C<< find( needle, haystack, from_offset=1 ) >>
681              
682             Return the character offset of C<needle> from start of C<haystack>, beginning the search at
683             from_offset. All offsets are 1-based.
684              
685             =item C<< fixed( number, decimals=2, no_commas=false ) >>
686              
687             Return the number formatted with a fixed number of decimal places. By default, it gets commas
688             added in the USA notation, but this can be disabled.
689              
690             =item C<< len( string ) >>
691              
692             Return number of unicode characters in STRING.
693              
694             =item C<< lower( string ) >>
695              
696             Return lowercase version of STRING.
697              
698             =item C<< replace( string, offset, length, new_text ) >>
699              
700             Replace text in C<string> with C<new_text>, overwriting C<length> characters from C<offset>.
701              
702             =item C<< substr( string, offset, length=max ) >>
703              
704             Same as perl's builtin.
705              
706             =item C<< trim( string ) >>
707              
708             Remove all leading and trailing whitespace and replace runs of whitespace with a single space
709             character.
710              
711             =item C<< upper( string ) >>
712              
713             Return uppercase version of STRING.
714              
715             =item C<< textjoin, join( separator, string, ... ) >>
716              
717             Same as perl's builtin.
718              
719             =back
720              
721             =head2 DateTime Functions
722              
723             Date math is implemented using the L<DateTime> module. Strings are coerced into dates using
724             the L<DateTime::Format::Flexible> module for any parameter where a spreadsheet function would
725             normally expect a date value. "Since 1900" date serial numbers are not used at all.
726              
727             =over
728              
729             =item C<< date( year, month, day ) >>
730              
731             Convert a (year,month,day) triplet into a date.
732              
733             =item C<< datedif( start_date, end_date, unit ) >>
734              
735             Calculate difference bwteen two dates. Unit can be one of: C<"Y"> (whole years), C<"M"> (whole
736             months), C<"D"> (whole days). Dates can be parsed from any string resembling a date.
737              
738             =item C<< datevalue( text ) >>
739              
740             Parse a date, or die trying.
741              
742             =item C<< day( date ) >>
743              
744             Returns the day number of a date
745              
746             =item C<< days( end_date, start_date ) >>
747              
748             Returns number of days difference between start and end date.
749              
750             =item C<< eomonth( start_date, months ) >>
751              
752             Calculate the date of End-Of-Month at some offset from the start date.
753              
754             =item C<< hour( date ) >>
755              
756             Return the hour field of a date.
757              
758             =item C<< minute( date ) >>
759              
760             Return minute field of a date.
761              
762             =item C<< month( date ) >>
763              
764             Return month field of a date.
765              
766             =item C<< year( date ) >>
767              
768             Return the year field of a date.
769              
770             =back
771              
772             =head1 AUTHOR
773              
774             Michael Conrad <mconrad@intellitree.com>
775              
776             =head1 COPYRIGHT AND LICENSE
777              
778             This software is copyright (c) 2021 by Michael Conrad, IntelliTree Solutions llc.
779              
780             This is free software; you can redistribute it and/or modify it under
781             the same terms as the Perl 5 programming language system itself.
782              
783             =cut