File Coverage

blib/lib/Data/Cmp/Numeric.pm
Criterion Covered Total %
statement 60 61 98.3
branch 32 34 94.1
condition 19 27 70.3
subroutine 6 6 100.0
pod 1 1 100.0
total 118 129 91.4


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