File Coverage

blib/lib/CommonsLang.pm
Criterion Covered Total %
statement 395 453 87.2
branch 148 206 71.8
condition 33 65 50.7
subroutine 62 72 86.1
pod 0 50 0.0
total 638 846 75.4


line stmt bran cond sub pod time code
1             package CommonsLang;
2              
3             =head1 NAME
4              
5             CommonsLang - Commonly used functions for Perl language
6              
7             =head1 SYNOPSIS
8             use CommonsLang;
9              
10             print s_pad("a", 5, "0") . "\n";
11             # > "a0000"
12              
13             print s_left("abc", 1) . "\n";
14             # > "a"
15              
16             print s_right("abc", 1) . "\n";
17             # > "c"
18              
19             print s_starts_with("abc", "ab") . "\n";
20             # > 1
21              
22             print s_ends_with("abc", "bc") . "\n";
23             # > 1
24              
25             =head1 DESCRIPTION
26              
27             * v_type_of - returns a string indicating the type of the variable.
28             * v_cmp - compare function, usually it is used for sort.
29             * v_max - returns the largest of the element given as input parameters, or undef if there are no parameters.
30             * v_min - returns the smallest of the numbers given as input parameters, or undef if there are no parameters.
31             * s_match_glob - check if a string matches a glob pattern.
32             * s_left - returns a string containing a specified number of characters from the left side of a string.
33             * s_right - returns a string containing a specified number of characters from the right side of a string.
34             * s_starts_with - check whether the string begins with the characters of a specified string, returning 1 or 0 as appropriate.
35             * s_ends_with - check whether the string ends with the characters of a specified string, returning 1 or 0 as appropriate.
36             * s_pad - string padding.
37             * s_trim - a new string representing str stripped of whitespace from both its beginning and end. Whitespace is defined as /\s/.
38             * s_ellipsis - truncate the string to the specified length and add ellipsis "..." at the end of the string to indicate that the string has been truncated.
39             * s_split - takes a pattern and divides this string into an ordered list of substrings by searching for the pattern, puts these substrings into an array, and returns the array.
40             * a_splice - changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.
41             * a_slice - returns a shallow copy of a portion of an array into a new array
42             * a_left - returns an array containing a specified number of elements from the left side of an array.
43             * a_right - returns an array containing a specified number of elements from the right side of an array.
44             * a_push - adds the specified elements to the end of an array and returns the new length of the array.
45             * a_pop - removes the last element from an array and returns that element. This method changes the length of the array.
46             * a_shift - removes the first element from an array and returns that removed element. This method changes the length of the array.
47             * a_unshift - adds the specified elements to the beginning of an array and returns the new length of the array.
48             * a_filter - creates a shallow copy of a portion of a given array, filtered down to just the elements from the given array that pass the test implemented by the provided function.
49             * a_sort - returns a sorted array by callbackFn. The original array will not be modified.
50             * a_concat - merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
51             * a_find_index - returns the index of the first element in an array that satisfies the provided testing function. If no elements satisfy the testing function, -1 is returned.
52             * a_find_last_index - iterates the array in reverse order and returns the index of the first element that satisfies the provided testing function. If no elements satisfy the testing function, -1 is returned.
53             * a_find - returns the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undef is returned.
54             * a_find_last - iterates the array in reverse order and returns the index of the first element that satisfies the provided testing function. If no elements satisfy the testing function, -1 is returned.
55             * a_index_of - returns the first index at which a given element can be found in the array, or -1 if it is not present.
56             * a_last_index_of - returns the first index at which a given element can be found in the array, or -1 if it is not present.
57             * a_every - tests whether all elements in the array pass the test implemented by the provided function. It returns 1 or 0. It doesn't modify the array.
58             * a_some - tests whether at least one element in the array passes the test implemented by the provided function. It returns 1 if, in the array, it finds an element for which the provided function returns 1; otherwise it returns 0. It doesn't modify the array.
59             * a_map - creates a new array populated with the results of calling a provided function on every element in the calling array.
60             * a_reduce - executes a user-supplied "reducer" callback function on each element of the array, in order, passing in the return value from the calculation on the preceding element. The final result of running the reducer across all elements of the array is a single value.
61             * a_join - creates and returns a new string by concatenating all of the elements in this array, separated by commas or a specified separator string.
62             * h_keys - returns an array of a given hash's own enumerable names.
63             * h_values - returns an array of a given hash's own enumerable values.
64             * h_find - returns the k-v tuple in the provided hash that satisfies the provided testing function. If no values satisfy the testing function, undef is returned.
65             * h_group_by - groups the elements of a given iterable according to the string values returned by a provided callback function.
66             * h_assign - Copies all key/value from one or more source objects to a target object.
67             * x_now_ts - get formated timestamp(YYYY-mm-ddTHH:MM:SS.SSS).
68             * x_now_ms - get current timestamp.
69             * x_log - print with the line number and subroutine name of caller to STDOUT.
70             * x_debug - print with the line number and subroutine name of caller to STDOUT for debug.
71             * x_error - print with the line number and subroutine name of caller to STDERR.
72             * x_stack - print the call stack to STDERR.
73             * x_fatal - print the error, and exit the perl process with code 1.
74              
75             =head1 AUTHOR
76              
77             YUPEN 12/23/24 - new
78              
79             =cut
80              
81             our $VERSION = 0.02;
82              
83 30     30   288440 use Exporter;
  30         64  
  30         5282  
84             @ISA = qw(Exporter);
85             @EXPORT_OK = (
86             v_type_of, v_cmp,
87             v_max, v_min,
88             s_match_glob, s_left, s_right,
89             s_starts_with, s_ends_with,
90             s_pad, s_trim,
91             s_ellipsis, s_split,
92             a_splice, a_slice,
93             a_left, a_right,
94             a_push, a_pop,
95             a_shift, a_unshift,
96             a_filter, a_sort, a_concat,
97             a_find_index, a_find_last_index,
98             a_find, a_find_last,
99             a_index_of, a_last_index_of,
100             a_every, a_some,
101             a_map, a_reduce, a_join,
102             h_keys, h_values, h_find, h_group_by, h_assign,
103             x_now_ts, x_now_ms,
104             x_log, x_debug, x_error, x_stack, x_fatal
105             );
106             @EXPORT = @EXPORT_OK;
107              
108 30     30   328 use strict;
  30         94  
  30         1271  
109 30     30   19367 use Data::Dumper;
  30         300003  
  30         2836  
110 30     30   240 use Carp 'croak';
  30         82  
  30         2235  
111 30     30   15848 use POSIX qw(floor ceil);
  30         237118  
  30         183  
112              
113 30     30   89869 use Env;
  30         85486  
  30         194  
114 30     30   35545 use Time::HiRes;
  30         40493  
  30         195  
115 30     30   2490 use File::Basename;
  30         136  
  30         3423  
116 30     30   18517 use Time::Piece;
  30         498491  
  30         186  
117              
118             ########################################
119             ########################################
120             ########################################
121              
122             ##################
123             # Subroutine : x_now_ms
124             # Purpose : get current timestamp
125             sub x_now_ms {
126 3     3 0 303709 return int(Time::HiRes::time * 1000);
127             }
128              
129             ##################
130             # Subroutine : x_now_ts
131             # Purpose : get formated timestamp(YYYY-mm-ddTHH:MM:SS.SSS)
132             sub x_now_ts {
133 2     2 0 13 my $t = localtime();
134 2         195 my $ms = int(x_now_ms() % 1000);
135 2         12 return $t->strftime("%Y-%m-%dT%H:%M:%S") . "." . sprintf("%03d", $ms);
136             }
137              
138             ##################
139             sub i_log {
140 0     0 0 0 my $dest = shift;
141 0         0 my $level = shift;
142              
143             # locate the call info
144 0         0 my ($package, $filename, $lineno) = caller(1);
145 0         0 my @next_caller_info = caller(2);
146 0 0       0 my $next_subroutine = @next_caller_info ? $next_caller_info[3] : "::";
147             ##
148 0         0 my ($pkg_name, $sub_name) = split("::", $next_subroutine);
149 0         0 my ($basename, $dirname) = fileparse($filename);
150             #
151 0         0 my $now_ts = x_now_ts();
152             ## print the msg string line by line
153 0         0 my $joined_str = join("", @_);
154 0         0 my @lines = split(/\r?\n/, $joined_str);
155 0         0 foreach my $line (@lines) {
156 0         0 my $msg =
157             ( "["
158             . $now_ts . "] "
159             . $basename . ":"
160             . sprintf("%4d", $lineno) . ":"
161             . sprintf("%-20s", $sub_name) . " - "
162             . $level . ": "
163             . $line
164             . "\n");
165              
166             # https://www.perlmonks.org/?node_id=791373
167             # sub print_to {
168             # print {$_[0]} $_[1];
169             # }
170             # print_to (*STDOUT, "test stdout");
171             # print_to (*STDERR, "test stderr");
172 0         0 print {$dest} $msg;
  0         0  
173             }
174             }
175              
176             ##################
177             # Subroutine : x_log
178             # Purpose : print with the line number and subroutine name of caller to STDOUT.
179             sub x_log {
180 0     0 0 0 i_log(*STDOUT, "LOG", @_);
181             }
182              
183             ##################
184             # Subroutine : x_debug
185             # Purpose : print with the line number and subroutine name of caller to STDOUT for debug
186             sub x_debug {
187 0     0 0 0 i_log(*STDOUT, "DEBUG", @_);
188             }
189              
190             ##################
191             # Subroutine : x_error
192             # Purpose : print with the line number and subroutine name of caller to STDERR
193             sub x_error {
194 0     0 0 0 i_log(*STDERR, "ERROR", @_);
195             }
196              
197             ##################
198             # Subroutine : x_stack
199             # Purpose : print the call stack to STDERR.
200             sub x_stack {
201 1     1 0 2 my $capture = shift;
202             ##
203 1         2 my $output = [];
204 1         1 my $level = 0;
205 1         6 my @info = caller($level++);
206 1         2 while (@info) {
207 3         6 my $prefix = " " x ($level - 1);
208 3         6 my ($package, $filename, $lineno) = @info;
209 3         44 my ($basename) = fileparse($filename);
210 3         9 @info = caller($level++);
211 3 100       6 if (@info) {
212 2         6 my ($pkg_name, $sub_name) = split("::", $info[3]);
213 2 100       6 my $msg = "$prefix$basename:$lineno" . ($sub_name ? ", subroutine: $sub_name" : "");
214 2 50       3 if ($capture) {
215 2         5 push(@$output, $msg);
216             }
217             else {
218 0         0 i_log(*STDERR, "STACK", $msg);
219             }
220             }
221             }
222 1         2 return $output;
223             }
224              
225             ##################
226             # Subroutine : x_fatal
227             # Purpose : print the error, and exit the perl process with code 1.
228             sub x_fatal {
229 0     0 0 0 i_log(*STDERR, "FATAL", @_);
230 0         0 exit(1);
231             }
232              
233             ########################################
234             ######################################## (scalar)variable
235             ########################################
236              
237             ##################
238             # Subroutine : v_type_of
239             # Purpose : The v_type_of method returns a string indicating the type of the variable.
240             # Input : var
241             # Returns : string of (UNDEF, ARRAY, HASH, CODE, ..., STRING, NUMBER)
242             sub v_type_of {
243 574     574 0 1122 my $var = shift;
244 574 50       1131 if (!defined($var)) {
245 0         0 return "UNDEF";
246             }
247 574         933 my $ref_type = ref($var);
248 574 100       1022 if ($ref_type ne "") {
249 51         176 return uc($ref_type);
250             }
251             else {
252 523 50       927 if ($var eq "") {
253 0         0 return "STRING";
254             }
255             else {
256 523 100       1068 my $scalar_type = ($var ^ $var) ? "STRING" : "NUMBER";
257 523         1209 return $scalar_type;
258             }
259             }
260             }
261              
262             ##################
263             # Subroutine : v_cmp
264             # Purpose : compare function, usually it is used for sort.
265             # Input : x, y
266             # Returns : > 0: x after y, < 0: x before y, = 0: they are equals
267             sub v_cmp {
268 218     218 0 367838 my ($x, $y) = @_;
269              
270 218 100 100     1250 if (!defined($x) and !defined($y)) {
    100 66        
    100 66        
271 1         5 return 0;
272             }
273             elsif (!defined($x) and defined($y)) {
274 1         6 return -1;
275             }
276             elsif (defined($x) and !defined($y)) { ## undef first
277 1         24 return 1;
278             }
279             else {
280 215         405 my $tx = v_type_of($x);
281 215         390 my $ty = v_type_of($y);
282 215 100       461 if ($tx eq $ty) {
283 214 100       508 if ($tx eq "NUMBER") { # https://www.tutorialspoint.com/perl/perl_operators.htm
    100          
    50          
284 101         338 return $x <=> $y;
285             }
286             elsif ($tx eq "STRING") {
287 107         367 return $x cmp $y;
288             }
289             elsif ($tx eq "UNDEF") {
290 0         0 return 0;
291             }
292             else {
293             # https://stackoverflow.com/questions/37220558/how-can-i-check-for-reference-equality-in-perl
294 6 100       17 if ($x == $y) { # check if there are same references
295 2         11 return 0;
296             }
297             else {
298             # -1, only mean they are not same.
299             # return -1;
300             # if ($tx eq "ARRAY") {
301             # my $a_size = scalar @$x;
302             # my $b_size = scalar @$y;
303             # return $a_size <=> $b_size;
304             # } elsif ($tx eq "HASH") {
305             # my $a_size = scalar keys %$x;
306             # my $b_size = scalar keys %$y;
307             # return $a_size <=> $b_size;
308             # } else {
309             # # not able to compare.
310             # # die "Since they are different type of variables, not able to compare. type of x is $tx, type of y is $ty";
311             # return -1;
312             # }
313             # die "Not able to compare. type of x & y is $tx.";
314 4         43 die "Not able to compare.";
315             }
316             }
317             }
318             else {
319             ## undef first
320 1 50 33     4 if ($tx eq "UNDEF" and $ty ne "UNDEF") {
321 0         0 return -1;
322             }
323 1 50 33     13 if ($tx ne "UNDEF" and $ty eq "UNDEF") {
324 0         0 return 1;
325             }
326             ##################
327 1 50 33     6 if ($tx eq "NUMBER" and $ty eq "STRING") {
328 0         0 return $x <=> $y;
329             }
330 1 50 33     4 if ($tx eq "STRING" and $ty eq "NUMBER") {
331 0         0 return $x cmp $y;
332             }
333             #######
334             # die "Since they are different type of variables, not able to compare. type of x is $tx, type of y is $ty";
335 1         3 my $stack = x_stack(1);
336 1         2 unshift(@$stack, "Not able to compare. type of x is $tx, type of y is $ty.");
337 1         9 die join("\n", @$stack);
338             }
339             }
340             }
341              
342             ##################
343             # Subroutine : v_max
344             # Purpose : returns the largest of the element given as input parameters, or undef if there are no parameters.
345             # Input : array
346             # Returns : returns the largest element
347             sub v_max {
348 50     50 0 365997 my $the_one = undef;
349 50         141 my $cmp_func = \&v_cmp;
350 50         90 my $idx = 0;
351 50         101 foreach my $x (@_) {
352 100 50 66     302 if ($idx == 0 and v_type_of($x) eq "CODE") {
353 0         0 $cmp_func = $x;
354             }
355             else {
356 100 100       189 if ($idx == 0) {
357 49         101 $the_one = $x;
358             }
359             else {
360 51 100       136 if ($cmp_func->($the_one, $x) <= 0) {
361 33         54 $the_one = $x;
362             }
363             }
364 100         216 $idx = $idx + 1;
365             }
366             }
367 50         145 return $the_one;
368             }
369              
370             ##################
371             # Subroutine : v_min
372             # Purpose : returns the smallest of the numbers given as input parameters, or undef if there are no parameters.
373             # Input : array
374             # Returns : returns the smallest element
375             sub v_min {
376 50     50 0 89 my $the_one = undef;
377 50         124 my $cmp_func = \&v_cmp;
378 50         103 my $idx = 0;
379 50         106 foreach my $x (@_) {
380 100 50 66     285 if ($idx == 0 and v_type_of($x) eq "CODE") {
381 0         0 $cmp_func = $x;
382             }
383             else {
384 100 100       179 if ($idx == 0) {
385 49         108 $the_one = $x;
386             }
387             else {
388 51 100       103 if ($cmp_func->($the_one, $x) >= 0) {
389 14         27 $the_one = $x;
390             }
391             }
392 100         190 $idx = $idx + 1;
393             }
394             }
395 50         155 return $the_one;
396             }
397              
398             ########################################
399             ######################################## string
400             ########################################
401              
402             ##################
403             # Subroutine : s_pad
404             # Purpose : String padding.
405             # Input : 1. text
406             # 2. width - can be undef or -1 if you supply multiple texts, in which case the width will be determined from the longest text.
407             # 3. which(optional) - is either "r" or "right" for padding on the right (the default if not specified),
408             # "l" or "left" for padding on the right, or "c" or "center" or "centre" for left+right padding to center the text.
409             # Note that "r" will mean "left justified", while "l" will mean "right justified".
410             # 4. padchar(optional) - is whitespace if not specified. It should be string having the width of 1 column.
411             # 5. is_trunc(optional) - is boolean. When set to 1, then text will be truncated when it is longer than $width.
412             # Returns : Return $text padded with $padchar to $width columns.
413             # Can accept multiple texts (\@texts); in which case will return a new arrayref of padded texts.
414             sub s_pad {
415 18     18 0 362011 my ($text0, $width, $which, $padchar, $is_trunc) = @_;
416 18 100       51 if ($which) {
417 9         22 $which = substr($which, 0, 1);
418             }
419             else {
420 9         19 $which = "r";
421             }
422 18   100     78 $padchar //= " ";
423              
424 18 100       66 my $texts = ref $text0 eq 'ARRAY' ? [@$text0] : [$text0];
425              
426 18 100 66     106 if (!defined($width) || $width < 0) {
427 3         6 my $longest = 0;
428 3         9 for (@$texts) {
429 3         6 my $len = length($_);
430 3 50       11 $longest = $len if $longest < $len;
431             }
432 3         7 $width = $longest;
433             }
434              
435 18         70 for my $text (@$texts) {
436 19         36 my $w = length($text);
437 19 100 66     62 if ($is_trunc && $w > $width) {
438 1         5 $text = substr($text, 0, $width, 1);
439 1         4 $w = $width;
440             }
441             else {
442 18 100       72 if ($which eq 'l') {
    100          
443 30     30   55347 no warnings; # negative repeat count
  30         62  
  30         3482  
444 1         6 $text = ($padchar x ($width - $w)) . $text;
445             }
446             elsif ($which eq 'c') {
447 3         11 my $n = int(($width - $w) / 2);
448 3         15 $text = ($padchar x $n) . $text . ($padchar x ($width - $w - $n));
449             }
450             else {
451 30     30   188 no warnings; # negative repeat count
  30         57  
  30         27950  
452 14         52 $text .= ($padchar x ($width - $w));
453             }
454             }
455             } # for $text
456              
457 18 100       122 return ref $text0 eq 'ARRAY' ? $texts : $texts->[0];
458             }
459              
460             ##################
461             # Subroutine : s_left
462             # Purpose : Returns a string containing a specified number of characters from the left side of a string.
463             # Input : 1. string
464             # 2. length - a number indicating how many characters to return.
465             # If 0, a zero-length string ("") is returned.
466             # If greater than or equal to the number of characters in string, the entire string is returned.
467             # Returns : string
468             sub s_left {
469 10     10 0 346232 my $str = shift;
470 10         15 my $length = shift;
471             ###
472 10 100       33 return "" if ($length <= 0);
473 8         11 my $str_length = length($str);
474 8 100       20 if ($length > $str_length) {
475 1         3 return $str;
476             }
477 7         26 return substr($str, 0, $length);
478             }
479              
480             ##################
481             # Subroutine : s_starts_with
482             # Purpose : Check whether the string begins with the characters of a specified string, returning 1 or 0 as appropriate.
483             # Input : 1. string
484             # 2. string
485             # Returns :
486             sub s_starts_with {
487 2     2 0 309609 my $str1 = shift;
488 2         3 my $str2 = shift;
489 2         4 my $len1 = length($str1);
490 2         3 my $len2 = length($str2);
491 2 50       5 if ($len1 < $len2) {
492 0         0 return 0;
493             }
494 2         4 my $cutted = s_left($str1, $len2);
495 2 100       5 if ($cutted eq $str2) {
496 1         6 return 1;
497             }
498 1         3 return 0;
499             }
500              
501             ##################
502             # Subroutine : s_right
503             # Purpose : Returns a string containing a specified number of characters from the right side of a string.
504             # Input : 1. string
505             # 2. length - a number indicating how many characters to return.
506             # If 0, a zero-length string ("") is returned.
507             # If greater than or equal to the number of characters in string, the entire string is returned.
508             # Returns : string
509             sub s_right {
510 10     10 0 391981 my $str = shift;
511 10         21 my $length = shift;
512             ###
513 10 100       57 return "" if ($length <= 0);
514 8         15 my $str_length = length($str);
515 8 100       29 if ($length > $str_length) {
516 1         8 return $str;
517             }
518 7         52 return substr($str, $str_length - $length, $length);
519             }
520              
521             ##################
522             # Subroutine : s_ends_with
523             # Purpose : Check whether the string ends with the characters of a specified string, returning 1 or 0 as appropriate.
524             # Input : 1. string
525             # 2. string
526             # Returns :
527             sub s_ends_with {
528 2     2 0 4 my $str1 = shift;
529 2         2 my $str2 = shift;
530 2         3 my $len1 = length($str1);
531 2         3 my $len2 = length($str2);
532 2 50       6 if ($len1 < $len2) {
533 0         0 return 0;
534             }
535 2         4 my $cutted = s_right($str1, $len2);
536 2 100       5 if ($cutted eq $str2) {
537 1         3 return 1;
538             }
539 1         4 return 0;
540             }
541              
542             ##################
543             # Subroutine : s_trim
544             # Purpose : A new string representing str stripped of whitespace from both its beginning and end.
545             # Whitespace is defined as /\s/.
546             # Input : 1. string
547             # Returns : string
548             sub s_trim {
549 6     6 0 241203 my $str = shift;
550             ###
551 6         51 $str =~ s/^\s+|\s+$//g;
552 6         43 return $str;
553             }
554              
555             ##################
556             # Subroutine : s_ellipsis
557             # Purpose : truncate the string to the specified length and add ellipsis "..." at the end of the string to indicate that the string has been truncated.
558             # Input : 1. string
559             # 2. width
560             # 3. align
561             # 4. padchar
562             # Returns : string
563             sub s_ellipsis {
564 5     5 0 309305 my ($str, $width, $align, $padchar) = @_;
565 5 100       19 $align = defined($align) ? $align : "l";
566 5         16 $align = lc(substr($align, 0, 1));
567             ##
568 5 100       15 $padchar = defined($padchar) ? $padchar : " ";
569             ##
570 5         15 $str =~ s/\r?\n//g;
571             ##
572 5         9 my $length = length($str);
573             #
574 5 100       13 if ($length <= $width) {
575 2 50       13 $str = s_pad($str, $width, ($align eq "r" ? "l" : "r"), $padchar);
576             }
577             else {
578 3 100       15 if ($align eq "r") {
    100          
579 1         6 $str = "..." . s_right($str, $width - 3);
580             }
581             elsif ($align eq "c") {
582 1         3 my $m_odd = $width % 2;
583 1         30 my $m_mid_len = floor($width / 2);
584 1         8 my $head_str = s_left($str, $m_mid_len - 1);
585 1 50       7 my $tail_str = s_right($str, $m_mid_len - ($m_odd ? 1 : 2));
586 1         4 $str = $head_str . "..." . $tail_str;
587             }
588             else {
589 1         7 $str = s_left($str, $width - 3) . "...";
590             }
591             }
592              
593 5         30 return $str;
594             }
595              
596             ##################
597             # Subroutine : s_split
598             # Purpose : takes a pattern and divides this string into an ordered list of substrings by searching for the pattern, puts these substrings into an array, and returns the array.
599             # Input : 1. string
600             # 2. separator
601             # Returns : array
602             sub s_split {
603 1     1 0 323579 my ($str, $sep) = @_;
604 1         11 my @arr = split($sep, $str);
605 1         5 return \@arr;
606             }
607              
608             ##################
609             # Subroutine : s_match_glob
610             # Purpose : match globbing patterns against text
611             # Input : 1. pattern
612             # 2. string to match
613             # Returns : Returns the list of things which match the glob from the source list.
614             # Example :
615             # ```
616             # print "matched\n" if s_match_glob( "foo.*", "foo.bar" );
617             # > matched
618             # ```
619             # Reference : https://metacpan.org/pod/Text::Glob
620             sub s_match_glob {
621 30     30   254 use constant debug => 0;
  30         54  
  30         2595  
622 30     30   182 use constant strict_leading_dot => 0;
  30         69  
  30         1835  
623 30     30   170 use constant strict_wildcard_slash => 0;
  30         60  
  30         97627  
624             ###
625             sub glob_to_regex_string {
626 2     2 0 4 my $glob = shift;
627 2         4 my $seperator = quotemeta("/");
628 2         5 my ($regex, $in_curlies, $escaping);
629 2         4 local $_;
630 2         3 my $first_byte = 1;
631 2         19 for ($glob =~ m/(.)/gs) {
632 11 100       28 if ($first_byte) {
633 2         4 if (strict_leading_dot) {
634             $regex .= '(?=[^\.])' unless $_ eq '.';
635             }
636 2         4 $first_byte = 0;
637             }
638 11 50       28 if ($_ eq '/') {
639 0         0 $first_byte = 1;
640             }
641 11 100 66     172 if ( $_ eq '.'
    100 66        
    50 33        
    50 33        
    50 33        
    50 33        
    50 33        
      33        
      33        
      33        
642             || $_ eq '('
643             || $_ eq ')'
644             || $_ eq '|'
645             || $_ eq '+'
646             || $_ eq '^'
647             || $_ eq '$'
648             || $_ eq '@'
649             || $_ eq '%') {
650 2         5 $regex .= "\\$_";
651             }
652             elsif ($_ eq '*') {
653 2 50       6 $regex .=
654             $escaping ? "\\*"
655             : strict_wildcard_slash ? "(?:(?!$seperator).)*"
656             : ".*";
657             }
658             elsif ($_ eq '?') {
659 0 0       0 $regex .=
660             $escaping ? "\\?"
661             : strict_wildcard_slash ? "(?!$seperator)."
662             : ".";
663             }
664             elsif ($_ eq '{') {
665 0 0       0 $regex .= $escaping ? "\\{" : "(";
666 0 0       0 ++$in_curlies unless $escaping;
667             }
668             elsif ($_ eq '}' && $in_curlies) {
669 0 0       0 $regex .= $escaping ? "}" : ")";
670 0 0       0 --$in_curlies unless $escaping;
671             }
672             elsif ($_ eq ',' && $in_curlies) {
673 0 0       0 $regex .= $escaping ? "," : "|";
674             }
675             elsif ($_ eq "\\") {
676 0 0       0 if ($escaping) {
677 0         0 $regex .= "\\\\";
678 0         0 $escaping = 0;
679             }
680             else {
681 0         0 $escaping = 1;
682             }
683 0         0 next;
684             }
685             else {
686 7         32 $regex .= $_;
687 7         12 $escaping = 0;
688             }
689 11         22 $escaping = 0;
690             }
691 2         5 x_debug "# $glob $regex" if debug;
692              
693 2         8 return $regex;
694             }
695              
696             sub glob_to_regex {
697 2     2 0 4 my $glob = shift;
698 2         7 my $regex = glob_to_regex_string($glob);
699 2         47 return qr/^$regex$/;
700             }
701             ###
702 2     2 0 242652 my ($glob, $str) = @_;
703 2         9 my $regex = glob_to_regex($glob);
704 2         16 my $matched = $str =~ $regex;
705 2         5 x_debug "$str =~ $regex = $matched " if debug;
706 2 100       19 return $matched ? 1 : 0;
707             }
708              
709             ########################################
710             ######################################## array
711             ########################################
712              
713             ##################
714             # Subroutine : a_join
715             # Purpose : creates and returns a new string by concatenating all of the elements in this array, separated by commas or a specified separator string.
716             # If the array has only one item, then that item will be returned without using the separator.
717             # Input : array, separator
718             # Returns : joined string
719             sub a_join {
720 5     5 0 293760 my ($arr, $separator) = @_;
721 5 50       16 $separator = defined($separator) ? $separator : ",";
722 5         36 return join($separator, @$arr);
723             }
724              
725             ##################
726             # Subroutine : a_concat
727             # Purpose : merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
728             # Input : ...array_list
729             # Returns : A new array
730             sub a_concat {
731 3     3 0 322637 my $result = [];
732 3         11 foreach my $x (@_) {
733 6         12 a_push($result, @{$x});
  6         16  
734             }
735 3         29 return $result;
736             }
737              
738             ##################
739             # Subroutine : a_push
740             # Purpose : adds the specified elements to the end of an array and returns the new length of the array.
741             #
742             # Input : ...elements
743             # Returns : new length of the array
744             sub a_push {
745 29     29 0 307642 my $target = shift;
746 29         68 push(@$target, @_);
747 29         62 return scalar @$target;
748             }
749              
750             ##################
751             # Subroutine : a_pop
752             # Purpose : removes the last element from an array and returns that element. This method changes the length of the array.
753             #
754             # Input : array
755             # Returns : The removed element from the array; undef if the array is empty.
756             sub a_pop {
757 3     3 0 7 my $arr = shift;
758 3         5 my $item = pop(@$arr);
759 3         14 return $item;
760             }
761              
762             ##################
763             # Subroutine : a_splice
764             # Purpose : changes the contents of an array by removing or replacing existing elements and/or adding new elements in place.
765             #
766             # Input : array
767             # a_splice(array, start)
768             # a_splice(array, start, deleteCount)
769             # a_splice(array, start, deleteCount, item1)
770             # a_splice(array, start, deleteCount, item1, item2)
771             # a_splice(array, start, deleteCount, item1, item2, /* …, */ itemN)
772             # Returns : An array containing the deleted elements.
773             sub a_splice {
774 5     5 0 343210 my $arr = shift;
775 5         9 my $start = shift;
776             ####
777 5         9 my $deleted = [];
778             ####
779 5         8 my $arr_size = scalar @$arr;
780 5         8 my $optional_args_size = scalar @_;
781 5 100       14 my $deleteCount = ($optional_args_size == 0 ? ($arr_size - $start) : shift);
782 5         16 my $count = v_max(v_min($deleteCount, $arr_size - $start), 0);
783              
784 5         13 for my $i (1 .. $count) {
785 8         16 my $the_one_item = splice(@$arr, $start, 1);
786 8         16 a_push($deleted, $the_one_item);
787             }
788 5         13 splice(@$arr, $start, 0, @_);
789 5         27 return $deleted;
790             }
791              
792             ##################
793             # Subroutine : a_shift
794             # Purpose : removes the first element from an array and returns that removed element. This method changes the length of the array.
795             #
796             # Input : array
797             # Returns : The removed element from the array; undef if the array is empty.
798             sub a_shift {
799 1     1 0 1 my $arr = shift;
800 1         4 return shift(@$arr);
801             }
802              
803             ##################
804             # Subroutine : a_unshift
805             # Purpose : adds the specified elements to the beginning of an array and returns the new length of the array.
806             #
807             # Input : element1, …, elementN
808             # Returns : The new length property of the object upon which the method was called.
809             sub a_unshift {
810 3     3 0 331007 my $arr = shift;
811 3         16 return unshift(@$arr, @_);
812             }
813              
814             #################
815             # Subroutine : a_filter
816             # Purpose : creates a shallow copy of a portion of a given array, filtered down to just the elements from the given array that pass the test implemented by the provided function.
817             # Input : array, callback
818             # Returns : A shallow copy of the given array containing just the elements that pass the test. If no elements pass the test, an empty array is returned.
819             sub a_filter {
820 2     2 0 310216 my ($arr, $callbackFn) = @_;
821 2         5 my $result = [];
822 2         40 my $count = scalar @$arr;
823 2 50       11 if ($count > 0) {
824 2         7 for my $i (0 .. ($count - 1)) {
825 8 100       38 if ($callbackFn->($arr->[$i], $i, $arr)) {
826 2         19 a_push($result, $arr->[$i]);
827             }
828             }
829             }
830 2         23 return $result;
831             }
832              
833             ##################
834             # Subroutine : a_find_index
835             # Purpose : returns the index of the first element in an array that satisfies the provided testing function. If no elements satisfy the testing function, -1 is returned.
836             # Input : array, callback,
837             # Returns : The index of the first element in the array that passes the test. Otherwise, -1.
838             sub a_find_index {
839 23     23 0 293577 my ($arr, $callbackFn, $fromIndex) = @_;
840             my $callbackFnA = v_type_of($callbackFn) eq "CODE" ? $callbackFn : sub {
841 0     0   0 my ($itm, $idx) = @_;
842 0         0 return v_cmp($itm, $callbackFn) == 0;
843 23 50       58 };
844              
845 23         61 my $idx = -1;
846 23         42 my $count = scalar @$arr;
847 23 50       71 if ($count > 0) {
848 23 100       81 my $sidx = v_max(v_min((defined($fromIndex) ? $fromIndex : 0), $count - 1), 0);
849 23         46 my $eidx = $count - 1;
850             ##
851 23         32 my $i = $sidx;
852 23         63 while ($i <= $eidx) {
853 70 100       149 if ($callbackFnA->($arr->[$i], $i, $arr)) {
854 20         44 $idx = $i;
855 20         40 last;
856             }
857             #####
858 50         143 $i++;
859             }
860             }
861 23         60 return $idx;
862             }
863              
864             ##################
865             # Subroutine : a_find_last_index
866             # Purpose : iterates the array in reverse order and returns the index of the first element that satisfies the provided testing function.
867             # If no elements satisfy the testing function, -1 is returned.
868             # Input : array, callback,
869             # Returns : The index of the last (highest-index) element in the array that passes the test. Otherwise -1 if no matching element is found.
870             sub a_find_last_index {
871 7     7 0 17 my ($arr, $callbackFn, $toIndex) = @_;
872              
873             my $callbackFnA = v_type_of($callbackFn) eq "CODE" ? $callbackFn : sub {
874 0     0   0 my ($itm, $idx) = @_;
875 0         0 return v_cmp($itm, $callbackFn) == 0;
876 7 50       19 };
877 7         14 my $idx = -1;
878 7         13 my $count = scalar @$arr;
879 7 50       21 if ($count > 0) {
880 7 100       26 my $sidx = v_min(v_max((defined($toIndex) ? $toIndex : $count - 1), 0), $count - 1);
881 7         12 my $eidx = 0;
882             ##
883 7         10 my $i = $sidx;
884 7         17 while ($i >= $eidx) {
885 21 100       93 if ($callbackFnA->($arr->[$i], $i, $arr)) {
886 4         14 $idx = $i;
887 4         10 last;
888             }
889             #####
890 17         81 $i--;
891             }
892             }
893 7         20 return $idx;
894             }
895              
896             ##################
897             # Subroutine : a_find
898             # Purpose : returns the first element in the provided array that satisfies the provided testing function.
899             # If no values satisfy the testing function, undef is returned.
900             # Input : array, callback,
901             # Returns : The first element in the array that satisfies the provided testing function. Otherwise, undef is returned.
902             sub a_find {
903 2     2 0 270398 my ($arr, $callbackFn, $fromIndex) = @_;
904             my $callbackFnA = v_type_of($callbackFn) eq "CODE" ? $callbackFn : sub {
905 0     0   0 my ($itm, $idx) = @_;
906 0         0 return v_cmp($itm, $callbackFn) == 0;
907 2 50       8 };
908 2         9 my $idx = a_find_index($arr, $callbackFnA, $fromIndex);
909 2 100       7 if ($idx != -1) {
910 1         9 return $arr->[$idx];
911             }
912 1         5 return undef;
913             }
914              
915             ##################
916             # Subroutine : a_find_last
917             # Purpose : iterates the array in reverse order and returns the value of the first element that satisfies the provided testing function.
918             # If no elements satisfy the testing function, undef is returned.
919             # Input : array, callback,
920             # Returns : The last (highest-index) element in the array that satisfies the provided testing function; undef if no matching element is found.
921             sub a_find_last {
922 2     2 0 7 my ($arr, $callbackFn, $toIndex) = @_;
923             my $callbackFnA = v_type_of($callbackFn) eq "CODE" ? $callbackFn : sub {
924 0     0   0 my ($itm, $idx) = @_;
925 0         0 return v_cmp($itm, $callbackFn) == 0;
926 2 50       6 };
927 2         9 my $idx = a_find_last_index($arr, $callbackFnA, $toIndex);
928 2 100       7 if ($idx != -1) {
929 1         6 return $arr->[$idx];
930             }
931 1         10 return undef;
932             }
933              
934             ##################
935             # Subroutine : a_index_of
936             # Purpose : returns the first index at which a given element can be found in the array, or -1 if it is not present.
937             # Input : array, searchElement, fromIndex(optional)
938             # Returns : The first index of searchElement in the array; -1 if not found.
939             sub a_index_of {
940 19     19 0 234415 my ($arr, $searchElement, $fromIndex) = @_;
941             my $idx = a_find_index(
942             $arr,
943             sub {
944 58     58   107 my ($itm) = @_;
945 58         127 return v_cmp($searchElement, $itm) == 0;
946             },
947 19         112 $fromIndex
948             );
949 19         112 return $idx;
950             }
951              
952             ##################
953             # Subroutine : a_last_index_of
954             # Purpose : returns the first index at which a given element can be found in the array, or -1 if it is not present.
955             # Input : array, searchElement, fromIndex(optional)
956             # Returns : The first index of searchElement in the array; -1 if not found.
957             sub a_last_index_of {
958 3     3 0 6 my ($arr, $searchElement, $toIndex) = @_;
959             my $idx = a_find_last_index(
960             $arr,
961             sub {
962 9     9   11 my ($itm) = @_;
963 9         10 return v_cmp($searchElement, $itm) == 0;
964             },
965 3         14 $toIndex
966             );
967 3         19 return $idx;
968             }
969              
970             ##################
971             # Subroutine : a_every
972             # Purpose : tests whether all elements in the array pass the test implemented by the provided function.
973             # It returns 1 or 0.
974             # It doesn't modify the array.
975             # Input : array, callback(element, index, the_array)
976             # Returns : 1 unless callbackFn returns a falsy value for an array element, in which case 0 is immediately returned.
977             sub a_every {
978 2     2 0 287107 my ($arr, $callbackFn) = @_;
979              
980 2         5 my $count = scalar @$arr;
981 2 50       9 if ($count > 0) {
982 2         27 my $sidx = 0;
983 2         5 my $eidx = $count - 1;
984             ##
985 2         5 my $i = $sidx;
986 2         6 while ($i <= $eidx) {
987 7 100       21 if (!$callbackFn->($arr->[$i], $i, $arr)) {
988 1         13 return 0;
989             }
990             #####
991 6         40 $i++;
992             }
993             }
994 1         7 return 1;
995             }
996              
997             ##################
998             # Subroutine : a_some
999             # Purpose : tests whether at least one element in the array passes the test implemented by the provided function.
1000             # It returns 1 if, in the array, it finds an element for which the provided function returns 1;
1001             # otherwise it returns 0.
1002             # It doesn't modify the array.
1003             # Input : array, callback(element, index, the_array)
1004             # Returns : 0 unless callbackFn returns a truthy value for an array element, in which case 1 is immediately returned.
1005             sub a_some {
1006 2     2 0 8 my ($arr, $callbackFn) = @_;
1007              
1008 2         5 my $count = scalar @$arr;
1009 2 50       8 if ($count > 0) {
1010 2         5 my $sidx = 0;
1011 2         6 my $eidx = $count - 1;
1012             ##
1013 2         3 my $i = $sidx;
1014 2         8 while ($i <= $eidx) {
1015 8 100       20 if ($callbackFn->($arr->[$i], $i, $arr)) {
1016 1         11 return 1;
1017             }
1018             #####
1019 7         79 $i++;
1020             }
1021             }
1022 1         6 return 0;
1023             }
1024              
1025             ##################
1026             # Subroutine : a_map
1027             # Purpose : creates a new array populated with the results of calling a provided function on every element in the calling array.
1028             # Input : array, callback(element, index, the_array)
1029             # Returns : A new array with each element being the result of the callback function.
1030             sub a_map {
1031 1     1 0 344133 my ($arr, $callbackFn) = @_;
1032 1 50       6 if (!defined($arr)) {
1033 0         0 return $arr;
1034             }
1035 1         3 my $result = [];
1036 1         3 my $count = scalar @$arr;
1037 1 50       5 if ($count > 0) {
1038 1         5 for my $i (0 .. ($count - 1)) {
1039 5         14 a_push($result, $callbackFn->($arr->[$i], $i, $arr));
1040             }
1041             }
1042 1         11 return $result;
1043             }
1044              
1045             ##################
1046             # Subroutine : a_reduce
1047             # Purpose : executes a user-supplied "reducer" callback function on each element of the array,
1048             # in order, passing in the return value from the calculation on the preceding element.
1049             # The final result of running the reducer across all elements of the array is a single value.
1050             # Input : array, callback(accumulator, currentValue, currentIndex, theArray), initialValue
1051             # Returns : The value that results from running the "reducer" callback function to completion over the entire array.
1052             sub a_reduce {
1053 3     3 0 734224 my ($arr, $callbackFn, $initialValue) = @_;
1054 3         20 my $result = $initialValue;
1055 3         9 my $count = scalar @$arr;
1056 3 50       31 if ($count > 0) {
1057 3         14 for my $i (0 .. ($count - 1)) {
1058 16         108 $result = $callbackFn->($result, $arr->[$i], $i, $arr);
1059             }
1060             }
1061 3         23 return $result;
1062             }
1063              
1064             ##################
1065             # Subroutine : a_slice
1066             # Purpose : returns a shallow copy of a portion of an array into a new array
1067             # The original array will not be modified.
1068             # Input : 1. array
1069             # 2. start index
1070             # 3. end index (optional)
1071             # Returns : A new array containing the extracted elements.
1072             sub a_slice {
1073 9     9 0 232055 my ($arr, $sidx, $eidx) = @_;
1074 9 50       15 if (!defined($arr)) {
1075 0         0 return $arr;
1076             }
1077 9         19 my $count = scalar @$arr;
1078 9         11 my $result = [];
1079 9 50       15 if ($count > 0) {
1080 9 100       15 $eidx = defined($eidx) ? $eidx : $count;
1081 9         17 for my $j ($sidx .. $eidx - 1) {
1082 20         50 push(@$result, $arr->[$j]);
1083             }
1084             }
1085 9         51 return $result;
1086             }
1087              
1088             ##################
1089             # Subroutine : a_left
1090             # Purpose : Returns an array containing a specified number of elements from the left side of an array.
1091             # Input : 1. array
1092             # 2. length - a number indicating how many elements to return.
1093             # Returns : array
1094             sub a_left {
1095 5     5 0 298888 my ($arr, $length) = @_;
1096             ###
1097 5 100       19 return [] if ($length <= 0);
1098             ##
1099 4         5 my $arr_length = scalar @$arr;
1100 4 50       7 return [] if ($arr_length <= 0);
1101             ##
1102 4         9 my $end_idx = v_min($length, $arr_length) - 1;
1103 4         10 return a_slice($arr, 0, $end_idx + 1);
1104             }
1105              
1106             ##################
1107             # Subroutine : a_right
1108             # Purpose : Returns an array containing a specified number of elements from the right side of an array.
1109             # Input : 1. array
1110             # 2. length - a number indicating how many elements to return.
1111             # Returns : array
1112             sub a_right {
1113 5     5 0 10 my ($arr, $length) = @_;
1114             ###
1115 5 100       14 return [] if ($length <= 0);
1116             ##
1117 4         6 my $arr_length = scalar @$arr;
1118 4 50       6 return [] if ($arr_length <= 0);
1119             ##
1120 4         9 my $start_idx = v_max($arr_length - $length, 0);
1121 4         6 return a_slice($arr, $start_idx);
1122             }
1123              
1124             ##################
1125             # Subroutine : a_sort
1126             # Purpose : The a_sort method returns a sorted array by callbackFn
1127             # The original array will not be modified.
1128             # Input : array, callback(a, b),
1129             # Returns : The new sorted array by the callbackFn
1130             # Example :
1131             # my $sorted_arr = a_sort(@array, sub {
1132             # my ($a, $b) = @_;
1133             # return $a cmp $b;
1134             # });
1135             sub a_sort {
1136 5     5 0 320786 my ($arr, $callbackFn) = @_;
1137 5 100       39 $callbackFn = defined($callbackFn) ? $callbackFn : \&v_cmp;
1138 5         28 my @sorted_arr = sort { $callbackFn->($a, $b) } @$arr;
  41         117  
1139 5         54 return \@sorted_arr;
1140             }
1141              
1142             ########################################
1143             ######################################## hash
1144             ########################################
1145              
1146              
1147             ##################
1148             # Subroutine : h_keys
1149             # Purpose : returns an array of a given hash's own enumerable names.
1150             # Input : hash
1151             # Returns : An array of strings representing the given hash's own enumerable keys.
1152             sub h_keys {
1153 1     1 0 10 my $hash = shift;
1154 1         5 my @ks = keys %$hash;
1155 1         5 return \@ks;
1156             }
1157              
1158             ##################
1159             # Subroutine : h_values
1160             # Purpose : returns an array of a given hash's own enumerable values.
1161             # Input : hash
1162             # Returns : An array containing the given object's own enumerable values.
1163             sub h_values {
1164 1     1 0 3 my $hash = shift;
1165 1         4 my @vs = values %$hash;
1166 1         5 return \@vs;
1167             }
1168              
1169             ##################
1170             # Subroutine : h_find
1171             # Purpose : returns the k-v tuple in the provided hash that satisfies the provided testing function.
1172             # If no values satisfy the testing function, undef is returned.
1173             # Input : $hash, callback,
1174             # Returns : The he k-v tuple in the provided hash that satisfies the provided testing function. Otherwise, undef is returned.
1175             sub h_find {
1176 2     2 0 3920 my ($hash, $callbackFn) = @_;
1177             ###
1178             my $callbackFnA = v_type_of($callbackFn) eq "CODE" ? $callbackFn : sub {
1179 0     0   0 my ($val, $key) = @_;
1180 0         0 return v_cmp($val, $callbackFn) == 0;
1181 2 50       8 };
1182             ###########
1183             # my $ks = h_keys($hash);
1184             # my $count = scalar @$ks;
1185             # if ($count > 0) {
1186             # my $eidx = $count - 1;
1187             # my $i = 0;
1188             # while ($i <= $eidx) {
1189             # my $key = $ks->[$i];
1190             # my $val = $hash->{$key};
1191             # ##
1192             # if ($callbackFnA->($val, $key, $hash)) {
1193             # return ($key, $val);
1194             # }
1195             # #####
1196             # $i++;
1197             # }
1198             # }
1199             ###########
1200 2         10 while (my ($key, $val) = each %$hash) {
1201 6 100       65 if ($callbackFnA->($val, $key, $hash)) {
1202 1         9 return ($key, $val);
1203             }
1204             }
1205 1         12 return undef;
1206             }
1207              
1208             ##################
1209             # Subroutine : h_group_by
1210             # Purpose : groups the elements of a given iterable according to the string values returned by a provided callback function.
1211             # Input : array, callback(element, idx)
1212             # the callback function should return a value that can get coerced into a key
1213             # Returns : A hash object with keys for all groups,
1214             # each assigned to an array containing the elements of the associated group.
1215             sub h_group_by {
1216 1     1 0 1745 my ($arr, $callbackFn) = @_;
1217             my $group_hash = a_reduce(
1218             $arr,
1219             sub {
1220 6     6   16 my ($hash, $element, $idx) = @_;
1221 6         14 my $group_key = $callbackFn->($element, $idx);
1222 6 50       33 if (v_type_of($group_key) ne "STRING") {
1223 0         0 my $stack = x_stack(1);
1224 0         0 unshift(@$stack, "the callback function of group_by should return a string");
1225 0         0 die join("\n", @$stack);
1226             }
1227 6 100       17 if (!defined($hash->{$group_key})) {
1228 5         40 $hash->{$group_key} = [];
1229             }
1230 6         18 a_push($hash->{$group_key}, $element);
1231             #####
1232 6         11 return $hash;
1233             },
1234             {}
1235 1         10 );
1236 1         37 return $group_hash;
1237             }
1238              
1239             ##################
1240             # Subroutine : h_assign
1241             # Purpose : Copies all key/value from one or more source objects to a target object.
1242             # Input : target_hash, source_hash_1, source_hash_2, ....
1243             # Returns : returns the modified target object.
1244             sub h_assign {
1245 2     2 0 1006 my $target_hash = shift;
1246 2         7 foreach my $source_hash (@_) {
1247 2 50       8 if (v_type_of($source_hash) eq "HASH") {
1248 2         36 while (my ($key, $val) = each %$source_hash) {
1249 7         56 $target_hash->{$key} = $val;
1250             }
1251             }
1252             else {
1253             ## raise an error?
1254             }
1255             }
1256 2         7 return $target_hash;
1257             }
1258              
1259             1;