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