File Coverage

blib/lib/Data/Cmp/StrOrNumeric.pm
Criterion Covered Total %
statement 64 65 98.4
branch 34 36 94.4
condition 21 30 70.0
subroutine 6 6 100.0
pod 1 1 100.0
total 126 138 91.3


line stmt bran cond sub pod time code
1             package Data::Cmp::StrOrNumeric;
2              
3             our $DATE = '2021-04-12'; # DATE
4             our $VERSION = '0.008'; # VERSION
5              
6 1     1   73144 use 5.010001;
  1         12  
7 1     1   7 use strict;
  1         8  
  1         32  
8 1     1   6 use warnings;
  1         2  
  1         34  
9              
10 1     1   5 use Scalar::Util qw(looks_like_number blessed reftype refaddr);
  1         2  
  1         642  
11              
12             require Exporter;
13             our @ISA = qw(Exporter);
14             our @EXPORT_OK = qw(cmp_data);
15              
16             our $EPSILON;
17              
18             # for when dealing with circular refs
19             my %_seen_refaddrs;
20              
21             sub _cmp_data {
22 39     39   54 my $d1 = shift;
23 39         53 my $d2 = shift;
24              
25 39         54 my $def1 = defined $d1;
26 39         54 my $def2 = defined $d2;
27 39 100       68 if ($def1) {
28 37 100       83 return 1 if !$def2;
29             } else {
30 2 100       10 return $def2 ? -1 : 0;
31             }
32              
33             # so both are defined ...
34              
35 36         77 my $reftype1 = reftype($d1);
36 36         57 my $reftype2 = reftype($d2);
37 36 100 100     162 if (!$reftype1 && !$reftype2) {
    100 75        
38 15         45 my $lln1 = looks_like_number($d1);
39 15         24 my $lln2 = looks_like_number($d2);
40 15 100 66     44 if ($lln1 && $lln2) {
41 9 50 33     30 if (defined $EPSILON && abs($d1 - $d2) < $EPSILON) {
42 0         0 return 0;
43             } else {
44 9         30 return $d1 <=> $d2;
45             }
46             } else {
47 6         25 return $d1 cmp $d2;
48             }
49 2         10 } elsif ( $reftype1 xor $reftype2) { return 2 }
50              
51             # so both are refs ...
52              
53 19 100       43 return 2 if $reftype1 ne $reftype2;
54              
55             # so both are refs of the same type ...
56              
57 18         33 my $pkg1 = blessed($d1);
58 18         30 my $pkg2 = blessed($d2);
59 18 100       32 if (defined $pkg1) {
60 2 100 66     13 return 2 unless defined $pkg2 && $pkg1 eq $pkg2;
61             } else {
62 16 50       32 return 2 if defined $pkg2;
63             }
64              
65             # so both are non-objects or objects of the same class ...
66              
67 17         32 my $refaddr1 = refaddr($d1);
68 17         25 my $refaddr2 = refaddr($d2);
69              
70 17 100 66     142 if ($reftype1 eq 'ARRAY' && !$_seen_refaddrs{$refaddr1} && !$_seen_refaddrs{$refaddr2}) {
    100 66        
      66        
      66        
71 7         14 $_seen_refaddrs{$refaddr1}++;
72 7         16 $_seen_refaddrs{$refaddr2}++;
73             ELEM:
74 7 100       9 for my $i (0..($#{$d1} < $#{$d2} ? $#{$d1} : $#{$d2})) {
  7         15  
  7         13  
  2         6  
  5         14  
75 4         12 my $cmpres = _cmp_data($d1->[$i], $d2->[$i]);
76 4 100       18 return $cmpres if $cmpres;
77             }
78 4         12 return $#{$d1} <=> $#{$d2};
  4         8  
  4         35  
79             } elsif ($reftype1 eq 'HASH' && !$_seen_refaddrs{$refaddr1} && !$_seen_refaddrs{$refaddr2}) {
80 8         16 $_seen_refaddrs{$refaddr1}++;
81 8         13 $_seen_refaddrs{$refaddr2}++;
82 8         20 my $nkeys1 = keys %$d1;
83 8         12 my $nkeys2 = keys %$d2;
84             KEY:
85 8         29 for my $k (sort keys %$d1) {
86 9 100 100     22 unless (exists $d2->{$k}) { return $nkeys1 <=> $nkeys2 || 2 }
  4         25  
87 5         11 my $cmpres = _cmp_data($d1->{$k}, $d2->{$k});
88 5 100       17 return $cmpres if $cmpres;
89             }
90 3         16 return $nkeys1 <=> $nkeys2;
91             } else {
92 2 100       15 return $refaddr1 == $refaddr2 ? 0 : 2;
93             }
94             }
95              
96             sub cmp_data {
97 30     30 1 21510 my $d1 = shift;
98 30         38 my $d2 = shift;
99              
100 30         61 %_seen_refaddrs = ();
101 30         68 _cmp_data($d1, $d2);
102             }
103              
104             1;
105             # ABSTRACT: Compare two data structures, return -1/0/1 like cmp or <=>
106              
107             __END__