File Coverage

blib/lib/Number/Tolerant/Union.pm
Criterion Covered Total %
statement 44 44 100.0
branch 30 30 100.0
condition n/a
subroutine 14 14 100.0
pod 2 2 100.0
total 90 90 100.0


line stmt bran cond sub pod time code
1 3     3   3084 use strict;
  3         6  
  3         93  
2 3     3   12 use warnings;
  3         5  
  3         1463  
3             package Number::Tolerant::Union 1.710;
4             # ABSTRACT: unions of tolerance ranges
5              
6             #pod =head1 SYNOPSIS
7             #pod
8             #pod use Number::Tolerant;
9             #pod
10             #pod my $range1 = tolerance(10 => to => 12);
11             #pod my $range2 = tolerance(14 => to => 16);
12             #pod
13             #pod my $union = $range1 | $range2;
14             #pod
15             #pod if ($11 == $union) { ... } # this will happen
16             #pod if ($12 == $union) { ... } # so will this
17             #pod
18             #pod if ($13 == $union) { ... } # nothing will happen here
19             #pod
20             #pod if ($14 == $union) { ... } # this will happen
21             #pod if ($15 == $union) { ... } # so will this
22             #pod
23             #pod =head1 DESCRIPTION
24             #pod
25             #pod Number::Tolerant::Union is used by L to represent the union
26             #pod of multiple tolerances. A subset of the same operators that function on a
27             #pod tolerance will function on a union of tolerances, as listed below.
28             #pod
29             #pod =head1 METHODS
30             #pod
31             #pod =head2 new
32             #pod
33             #pod my $union = Number::Tolerant::Union->new(@list_of_tolerances);
34             #pod
35             #pod There is a C method on the Number::Tolerant::Union class, but unions are
36             #pod meant to be created with the C<|> operator on a Number::Tolerant tolerance.
37             #pod
38             #pod The arguments to C are a list of numbers or tolerances to be unioned.
39             #pod
40             #pod Intersecting ranges are not converted into a single range, but this may change
41             #pod in the future. (For example, the union of "5 to 10" and "7 to 12" is not "5 to
42             #pod 12.")
43             #pod
44             #pod =cut
45              
46             sub new {
47 12     12 1 350 my $class = shift;
48 12         50 bless { options => [ @_ ] } => $class;
49             }
50              
51             #pod =head2 options
52             #pod
53             #pod This method will return a list of all the acceptable options for the union.
54             #pod
55             #pod =cut
56              
57             sub options {
58 97     97 1 596 my $self = shift;
59 97         112 return @{$self->{options}};
  97         253  
60             }
61              
62             #pod =head2 Overloading
63             #pod
64             #pod Tolerance unions overload a few operations, mostly comparisons.
65             #pod
66             #pod =over
67             #pod
68             #pod =item numification
69             #pod
70             #pod Unions numify to undef. If there's a better idea, I'd love to hear it.
71             #pod
72             #pod =item stringification
73             #pod
74             #pod A tolerance stringifies to a short description of itself. This is a set of the
75             #pod union's options, parentheses-enclosed and joined by the word "or"
76             #pod
77             #pod =item equality
78             #pod
79             #pod A number is equal to a union if it is equal to any of its options.
80             #pod
81             #pod =item comparison
82             #pod
83             #pod A number is greater than a union if it is greater than all its options.
84             #pod
85             #pod A number is less than a union if it is less than all its options.
86             #pod
87             #pod =item union intersection
88             #pod
89             #pod An intersection (C<&>) with a union is commutted across all options. In other
90             #pod words:
91             #pod
92             #pod (a | b | c) & d ==yields==> ((a & d) | (b & d) | (c & d))
93             #pod
94             #pod Options that have no intersection with the new element are dropped. The
95             #pod intersection of a constant number and a union yields that number, if the number
96             #pod was in the union's ranges and otherwise yields nothing.
97             #pod
98             #pod =back
99             #pod
100             #pod =cut
101              
102             use overload
103 1     1   4 '0+' => sub { undef },
104 1     1   3 '""' => sub { join(' or ', map { "($_)" } $_[0]->options) },
  2         41  
105 32 100   32   1719 '==' => sub { for ($_[0]->options) { return 1 if $_ == $_[1] } return 0 },
  68         160  
  3         13  
106 23 100   23   1165 '!=' => sub { for ($_[0]->options) { return 0 if $_ == $_[1] } return 1 },
  75         171  
  22         87  
107             '>' =>
108             sub {
109 19 100   19   37 if ($_[2]) { for ($_[0]->options) { return 0 unless $_[1] > $_ } return 1 }
  6 100       14  
  9         20  
  1         6  
110 13 100       43 else { for ($_[0]->options) { return 0 unless $_[1] < $_ } return 1 }
  16         49  
  3         19  
111             },
112             '<' =>
113             sub {
114 18 100   18   44 if ($_[2]) { for ($_[0]->options) { return 0 unless $_[1] < $_ } return 1 }
  5 100       12  
  6         16  
  1         4  
115 13 100       25 else { for ($_[0]->options) { return 0 unless $_[1] > $_ } return 1 }
  20         49  
  3         16  
116             },
117             '<=>' =>
118             sub {
119 8 100   8   21 if ($_[2]) { $_[0] < $_[1] ? 1 : $_[0] > $_[1] ? -1 : 0 }
  4 100       10  
    100          
120 4 100       9 else { $_[0] > $_[1] ? 1 : $_[0] < $_[1] ? -1 : 0 }
    100          
121             },
122 2     2   738 '|' => sub { __PACKAGE__->new($_[0]->options,$_[1]); },
123             '&' => sub {
124 3         23 eval { $_[1]->isa('Number::Tolerant') }
125 3 100   3   400 ? __PACKAGE__->new(map { $_ & $_[1] } $_[0]->options )
  2 100       7  
126             : $_[1] == $_[0]
127             ? $_[1]
128             : ();
129             },
130 3     3   25 fallback => 1;
  3         5  
  3         66  
131              
132             #pod =head1 TODO
133             #pod
134             #pod Who knows. Collapsing overlapping options, probably.
135             #pod
136             #pod =cut
137              
138             1;
139              
140             __END__