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