| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Types::Numbers; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | our $AUTHORITY = 'cpan:BBYRD'; # AUTHORITY | 
| 4 |  |  |  |  |  |  | our $VERSION = '0.94'; # VERSION | 
| 5 |  |  |  |  |  |  | # ABSTRACT: Type constraints for numbers | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | ############################################################################# | 
| 8 |  |  |  |  |  |  | # Modules | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 5 |  |  | 5 |  | 96344 | use v5.8.8; | 
|  | 5 |  |  |  |  | 14 |  | 
|  | 5 |  |  |  |  | 170 |  | 
| 11 | 5 |  |  | 5 |  | 17 | use strict; | 
|  | 5 |  |  |  |  | 6 |  | 
|  | 5 |  |  |  |  | 120 |  | 
| 12 | 5 |  |  | 5 |  | 17 | use warnings; | 
|  | 5 |  |  |  |  | 6 |  | 
|  | 5 |  |  |  |  | 203 |  | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | our @EXPORT_OK = (); | 
| 15 |  |  |  |  |  |  |  | 
| 16 | 5 |  |  | 5 |  | 2277 | use Type::Library -base; | 
|  | 5 |  |  |  |  | 91890 |  | 
|  | 5 |  |  |  |  | 41 |  | 
| 17 | 5 |  |  | 5 |  | 3323 | use Type::Tiny::Intersection; | 
|  | 5 |  |  |  |  | 4889 |  | 
|  | 5 |  |  |  |  | 175 |  | 
| 18 | 5 |  |  | 5 |  | 2107 | use Type::Tiny::Union; | 
|  | 5 |  |  |  |  | 5815 |  | 
|  | 5 |  |  |  |  | 128 |  | 
| 19 | 5 |  |  | 5 |  | 2690 | use Types::Standard v0.030 ();  # support for Error::TypeTiny | 
|  | 5 |  |  |  |  | 177666 |  | 
|  | 5 |  |  |  |  | 244 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 5 |  |  | 5 |  | 38 | use Scalar::Util 1.20 (qw(blessed looks_like_number));  # support for overloaded/blessed looks_like_number | 
|  | 5 |  |  |  |  | 118 |  | 
|  | 5 |  |  |  |  | 304 |  | 
| 22 | 5 |  |  | 5 |  | 2901 | use POSIX 'ceil'; | 
|  | 5 |  |  |  |  | 23283 |  | 
|  | 5 |  |  |  |  | 24 |  | 
| 23 | 5 |  |  | 5 |  | 8672 | use Math::BigInt   1.92;  # somewhat a stab in the dark for a passable version | 
|  | 5 |  |  |  |  | 81542 |  | 
|  | 5 |  |  |  |  | 27 |  | 
| 24 | 5 |  |  | 5 |  | 61600 | use Math::BigFloat 1.65;  # earliest version that passes tests | 
|  | 5 |  |  |  |  | 78531 |  | 
|  | 5 |  |  |  |  | 26 |  | 
| 25 | 5 |  |  | 5 |  | 5850 | use Data::Float; | 
|  | 5 |  |  |  |  | 32332 |  | 
|  | 5 |  |  |  |  | 269 |  | 
| 26 | 5 |  |  | 5 |  | 2687 | use Data::Integer; | 
|  | 5 |  |  |  |  | 30932 |  | 
|  | 5 |  |  |  |  | 344 |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | use constant { | 
| 29 | 5 |  |  |  |  | 414 | _BASE2_LOG => log(2) / log(10), | 
| 30 | 5 |  |  | 5 |  | 29 | }; | 
|  | 5 |  |  |  |  | 7 |  | 
| 31 |  |  |  |  |  |  |  | 
| 32 | 0 |  |  | 0 |  | 0 | sub _croak ($;@) { require Error::TypeTiny; goto \&Error::TypeTiny::croak } | 
|  | 0 |  |  |  |  | 0 |  | 
| 33 |  |  |  |  |  |  |  | 
| 34 | 5 |  |  | 5 |  | 25 | no warnings;  # don't warn on type checks | 
|  | 5 |  |  |  |  | 5 |  | 
|  | 5 |  |  |  |  | 23023 |  | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | ############################################################################# | 
| 37 |  |  |  |  |  |  | # Basic globals | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | my $bigtwo = Math::BigFloat->new(2); | 
| 40 |  |  |  |  |  |  | my $bigten = Math::BigFloat->new(10); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | # Large 64-bit floats (long doubles) tend to stringify themselves in exponent notation, even | 
| 43 |  |  |  |  |  |  | # though the number is still pristine.  IOW, the numeric form is perfect, but the string form | 
| 44 |  |  |  |  |  |  | # loses information.  This can be a problem for stringified inlines. | 
| 45 |  |  |  |  |  |  | my @df_max_int_parts = Data::Float::float_parts( Data::Float::max_integer ); | 
| 46 |  |  |  |  |  |  | my $DF_MAX_INT = $bigtwo->copy->bpow($df_max_int_parts[1])->bmul($df_max_int_parts[2])->as_int; | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | my $SAFE_NUM_MIN = Math::BigInt->new( | 
| 49 |  |  |  |  |  |  | Data::Integer::min_signed_natint   < $DF_MAX_INT * -1 ? | 
| 50 |  |  |  |  |  |  | Data::Integer::min_signed_natint   : $DF_MAX_INT * -1 | 
| 51 |  |  |  |  |  |  | ); | 
| 52 |  |  |  |  |  |  | my $SAFE_NUM_MAX = Math::BigInt->new( | 
| 53 |  |  |  |  |  |  | Data::Integer::max_unsigned_natint > $DF_MAX_INT *  1 ? | 
| 54 |  |  |  |  |  |  | Data::Integer::max_unsigned_natint : $DF_MAX_INT *  1, | 
| 55 |  |  |  |  |  |  | ); | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | my $meta = __PACKAGE__->meta; | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | ############################################################################# | 
| 60 |  |  |  |  |  |  | # Framework types | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | ### TODO: Coercions where safe ### | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | # Moose and Type::Tiny types both don't seem to support Math::Big* = Num. | 
| 65 |  |  |  |  |  |  | # So, we have to start almost from stratch. | 
| 66 |  |  |  |  |  |  | my $_NumLike = $meta->add_type( | 
| 67 |  |  |  |  |  |  | name       => 'NumLike', | 
| 68 |  |  |  |  |  |  | parent     => Types::Standard::Defined, | 
| 69 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 70 |  |  |  |  |  |  | constraint => sub { looks_like_number $_ }, | 
| 71 |  |  |  |  |  |  | inlined    => sub { "Scalar::Util::looks_like_number($_[1])" }, | 
| 72 |  |  |  |  |  |  | ); | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | my $_NumRange = $meta->add_type( | 
| 75 |  |  |  |  |  |  | name       => 'NumRange', | 
| 76 |  |  |  |  |  |  | parent     => $_NumLike, | 
| 77 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 78 |  |  |  |  |  |  | # kinda pointless without the parameters | 
| 79 |  |  |  |  |  |  | constraint_generator => sub { | 
| 80 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 81 |  |  |  |  |  |  | my ($min, $max) = (shift, shift); | 
| 82 |  |  |  |  |  |  | looks_like_number($min) or _croak( "First parameter to NumRange[`n, `p] expected to be a number; got $min"); | 
| 83 |  |  |  |  |  |  | looks_like_number($max) or _croak("Second parameter to NumRange[`n, `p] expected to be a number; got $max"); | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | my ($Imin, $Imax) = ($min, $max); | 
| 86 |  |  |  |  |  |  | $Imin = blessed($min)."\->new('$min')" if (blessed $min); | 
| 87 |  |  |  |  |  |  | $Imax = blessed($max)."\->new('$max')" if (blessed $max); | 
| 88 |  |  |  |  |  |  |  | 
| 89 |  |  |  |  |  |  | Type::Tiny->new( | 
| 90 |  |  |  |  |  |  | display_name => "NumRange[$min, $max]", | 
| 91 |  |  |  |  |  |  | parent     => $self, | 
| 92 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 93 |  |  |  |  |  |  | constraint => sub { | 
| 94 |  |  |  |  |  |  | my $val = $_; | 
| 95 |  |  |  |  |  |  | $val >= $min && $val <= $max; | 
| 96 |  |  |  |  |  |  | }, | 
| 97 |  |  |  |  |  |  | inlined    => sub { | 
| 98 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 99 |  |  |  |  |  |  | $self->parent->inline_check($val)." && $val >= $Imin && $val <= $Imax"; | 
| 100 |  |  |  |  |  |  | }, | 
| 101 |  |  |  |  |  |  | ); | 
| 102 |  |  |  |  |  |  | }, | 
| 103 |  |  |  |  |  |  | ); | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | # we need to optimize out all of the NumLike checks | 
| 106 |  |  |  |  |  |  | my $_NumRange_perlsafe = Type::Tiny->new( | 
| 107 |  |  |  |  |  |  | display_name => "_NumRange_perlsafe", | 
| 108 |  |  |  |  |  |  | parent     => $_NumLike, | 
| 109 |  |  |  |  |  |  | # no equals because MAX+1 = MAX after truncation | 
| 110 |  |  |  |  |  |  | constraint => sub { $_ > $SAFE_NUM_MIN && $_ < $SAFE_NUM_MAX }, | 
| 111 |  |  |  |  |  |  | inlined    => sub { "$_[1] > ".$SAFE_NUM_MIN." && $_[1] < ".$SAFE_NUM_MAX }, | 
| 112 |  |  |  |  |  |  | ); | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | ### XXX: This string equality check is necessary because Math::BigInt seems to think 1.5 == 1. | 
| 115 |  |  |  |  |  |  | ### However, this is problematic with long doubles that stringify into E notation. | 
| 116 |  |  |  |  |  |  | my $_IntLike = $meta->add_type( | 
| 117 |  |  |  |  |  |  | name       => 'IntLike', | 
| 118 |  |  |  |  |  |  | parent     => $_NumLike, | 
| 119 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 120 |  |  |  |  |  |  | constraint => sub { /\d+/ && int($_) == $_ && (int($_) eq $_ || !ref($_)) }, | 
| 121 |  |  |  |  |  |  | inlined    => sub { | 
| 122 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 123 |  |  |  |  |  |  | $self->parent->inline_check($val)." && $val =~ /\\d+/ && int($val) == $val && (int($val) eq $val || !ref($val))"; | 
| 124 |  |  |  |  |  |  | }, | 
| 125 |  |  |  |  |  |  | ); | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | # This is basically LaxNum with a different parent | 
| 128 |  |  |  |  |  |  | my $_PerlNum = $meta->add_type( | 
| 129 |  |  |  |  |  |  | name       => 'PerlNum', | 
| 130 |  |  |  |  |  |  | parent     => $_NumLike, | 
| 131 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 132 |  |  |  |  |  |  | constraint => Types::Standard::LaxNum->constraint, | 
| 133 |  |  |  |  |  |  | inlined    => Types::Standard::LaxNum->inlined, | 
| 134 |  |  |  |  |  |  | ); | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | my $_BlessedNum = $meta->add_type( Type::Tiny::Intersection->new( | 
| 137 |  |  |  |  |  |  | name         => 'BlessedNum', | 
| 138 |  |  |  |  |  |  | display_name => 'BlessedNum', | 
| 139 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 140 |  |  |  |  |  |  | type_constraints => [ $_NumLike, Types::Standard::Object ], | 
| 141 |  |  |  |  |  |  | constraint_generator => sub { | 
| 142 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 143 |  |  |  |  |  |  | my $digits = shift; | 
| 144 |  |  |  |  |  |  | $digits =~ /\A[0-9]+\z/ or _croak("Parameter to BlessedNum[`d] expected to be a positive integer; got $digits"); | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | my $parent = $self; | 
| 147 |  |  |  |  |  |  | Type::Tiny->new( | 
| 148 |  |  |  |  |  |  | display_name => "BlessedNum[$digits]", | 
| 149 |  |  |  |  |  |  | parent     => $self, | 
| 150 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 151 |  |  |  |  |  |  | constraint => sub { | 
| 152 |  |  |  |  |  |  | my $val = $_; | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | $val->can('accuracy')  && $val->accuracy  >= $digits || | 
| 155 |  |  |  |  |  |  | $val->can('div_scale') && $val->div_scale >= $digits; | 
| 156 |  |  |  |  |  |  | }, | 
| 157 |  |  |  |  |  |  | inlined    => sub { | 
| 158 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | $parent->inline_check($val).' && ( '. | 
| 161 |  |  |  |  |  |  | "$val->can('accuracy')  && $val->accuracy  && $val->accuracy  >= $digits || ". | 
| 162 |  |  |  |  |  |  | "$val->can('div_scale') && $val->div_scale && $val->div_scale >= $digits ". | 
| 163 |  |  |  |  |  |  | ')'; | 
| 164 |  |  |  |  |  |  | }, | 
| 165 |  |  |  |  |  |  | ); | 
| 166 |  |  |  |  |  |  | }, | 
| 167 |  |  |  |  |  |  | ) ); | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | my $_NaN = $meta->add_type( | 
| 170 |  |  |  |  |  |  | name       => 'NaN', | 
| 171 |  |  |  |  |  |  | parent     => $_NumLike, | 
| 172 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 173 |  |  |  |  |  |  | constraint => sub { | 
| 174 |  |  |  |  |  |  | my $val = $_; | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | Types::Standard::Object->check($val) && $val->can('is_nan') && $val->is_nan || | 
| 177 |  |  |  |  |  |  | Data::Float::float_is_nan($val); | 
| 178 |  |  |  |  |  |  | }, | 
| 179 |  |  |  |  |  |  | inlined    => sub { | 
| 180 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 181 |  |  |  |  |  |  | $self->parent->inline_check($val).' && ('. | 
| 182 |  |  |  |  |  |  | '   '.Types::Standard::Object->inline_check($val)." && $val->can('is_nan') && $val->is_nan ||". | 
| 183 |  |  |  |  |  |  | '   '."Data::Float::float_is_nan($val)". | 
| 184 |  |  |  |  |  |  | ')'; | 
| 185 |  |  |  |  |  |  | }, | 
| 186 |  |  |  |  |  |  | ); | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | my $_Inf = $meta->add_type( | 
| 189 |  |  |  |  |  |  | name       => 'Inf', | 
| 190 |  |  |  |  |  |  | parent     => $_NumLike, | 
| 191 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 192 |  |  |  |  |  |  | constraint => sub { | 
| 193 |  |  |  |  |  |  | my $val = $_; | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | Types::Standard::Object->check($val) && $val->can('is_inf') && ($val->is_inf('+') || $val->is_inf('-')) || | 
| 196 |  |  |  |  |  |  | Data::Float::float_is_infinite($val); | 
| 197 |  |  |  |  |  |  | }, | 
| 198 |  |  |  |  |  |  | inlined    => sub { | 
| 199 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 200 |  |  |  |  |  |  | $self->parent->inline_check($val).' && ('. | 
| 201 |  |  |  |  |  |  | '   '.Types::Standard::Object->inline_check($val)." && $val->can('is_inf') && ($val->is_inf('+') || $val->is_inf('-')) ||". | 
| 202 |  |  |  |  |  |  | '   '."Data::Float::float_is_infinite($val)". | 
| 203 |  |  |  |  |  |  | ')'; | 
| 204 |  |  |  |  |  |  | }, | 
| 205 |  |  |  |  |  |  | constraint_generator => sub { | 
| 206 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 207 |  |  |  |  |  |  | my $sign = shift; | 
| 208 |  |  |  |  |  |  | $sign =~ /\A[+\-]\z/ or _croak("Parameter to Inf[`s] expected to be a plus or minus sign; got $sign"); | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | Type::Tiny->new( | 
| 211 |  |  |  |  |  |  | display_name => "Inf[$sign]", | 
| 212 |  |  |  |  |  |  | parent     => $self, | 
| 213 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 214 |  |  |  |  |  |  | constraint => sub { | 
| 215 |  |  |  |  |  |  | my $val = $_; | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | Types::Standard::Object->check($val) && $val->can('is_inf') && $val->is_inf($sign) || | 
| 218 |  |  |  |  |  |  | Data::Float::float_is_infinite($val) && Data::Float::float_sign($val) eq $sign; | 
| 219 |  |  |  |  |  |  | }, | 
| 220 |  |  |  |  |  |  | inlined    => sub { | 
| 221 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | $self->parent->inline_check($val).' && ( '. | 
| 224 |  |  |  |  |  |  | Types::Standard::Object->inline_check($val)." && $val->can('is_inf') && $val->is_inf('$sign') || ". | 
| 225 |  |  |  |  |  |  | "Data::Float::float_is_infinite($val) && Data::Float::float_sign($val) eq '$sign' ". | 
| 226 |  |  |  |  |  |  | ')'; | 
| 227 |  |  |  |  |  |  | }, | 
| 228 |  |  |  |  |  |  | ); | 
| 229 |  |  |  |  |  |  | }, | 
| 230 |  |  |  |  |  |  | ); | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | # this is used a lot for floats, but we need to optimize out all of the NumLike checks | 
| 233 |  |  |  |  |  |  | my $_NaNInf = Type::Tiny::Union->new( | 
| 234 |  |  |  |  |  |  | type_constraints => [ $_NaN, $_Inf ], | 
| 235 |  |  |  |  |  |  | )->create_child_type( | 
| 236 |  |  |  |  |  |  | name       => 'NaNInf', | 
| 237 |  |  |  |  |  |  | constraint => sub { | 
| 238 |  |  |  |  |  |  | # looks_like_number($_) && | 
| 239 |  |  |  |  |  |  | Types::Standard::Object->check($_) && ( | 
| 240 |  |  |  |  |  |  | $_->can('is_nan') && $_->is_nan || | 
| 241 |  |  |  |  |  |  | $_->can('is_inf') && ($_->is_inf('+') || $_->is_inf('-')) | 
| 242 |  |  |  |  |  |  | ) || Data::Float::float_is_nan($_) || Data::Float::float_is_infinite($_) | 
| 243 |  |  |  |  |  |  | }, | 
| 244 |  |  |  |  |  |  | inlined    => sub { | 
| 245 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 246 |  |  |  |  |  |  | # looks_like_number($val) && | 
| 247 |  |  |  |  |  |  | Types::Standard::Object->inline_check($val)." && ( ". | 
| 248 |  |  |  |  |  |  | "$val->can('is_nan') && $val->is_nan || ". | 
| 249 |  |  |  |  |  |  | "$val->can('is_inf') && ($val->is_inf('+') || $val->is_inf('-')) ". | 
| 250 |  |  |  |  |  |  | ") || Data::Float::float_is_nan($val) || Data::Float::float_is_infinite($val)"; | 
| 251 |  |  |  |  |  |  | }, | 
| 252 |  |  |  |  |  |  | ); | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | my $_not_NaNInf = $_NaNInf->complementary_type; | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | my $_RealNum = $meta->add_type( Type::Tiny::Intersection->new( | 
| 257 |  |  |  |  |  |  | name       => 'RealNum', | 
| 258 |  |  |  |  |  |  | display_name => 'RealNum', | 
| 259 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 260 |  |  |  |  |  |  | type_constraints => [ $_NumLike, $_not_NaNInf ], | 
| 261 |  |  |  |  |  |  | ) ); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | ############################################################################# | 
| 264 |  |  |  |  |  |  | # Integer types | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | # Helper subs | 
| 267 |  |  |  |  |  |  | sub __integer_bits_vars { | 
| 268 | 12 |  |  | 12 |  | 17 | my ($bits, $is_unsigned) = @_; | 
| 269 |  |  |  |  |  |  |  | 
| 270 | 12 |  |  |  |  | 20 | my $sbits = $bits - 1; | 
| 271 |  |  |  |  |  |  |  | 
| 272 | 12 |  |  |  |  | 48 | my ($neg, $spos, $upos) = ( | 
| 273 |  |  |  |  |  |  | $bigtwo->copy->bpow($sbits)->bmul(-1), | 
| 274 |  |  |  |  |  |  | $bigtwo->copy->bpow($sbits)->bsub(1), | 
| 275 |  |  |  |  |  |  | $bigtwo->copy->bpow( $bits)->bsub(1), | 
| 276 |  |  |  |  |  |  | ); | 
| 277 | 12 |  |  |  |  | 20107 | my $sdigits = ceil( $sbits * _BASE2_LOG ); | 
| 278 | 12 |  |  |  |  | 24 | my $udigits = ceil(  $bits * _BASE2_LOG ); | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 12 | 100 |  |  |  | 62 | return $is_unsigned ? | 
| 281 |  |  |  |  |  |  | (0,    $upos, $udigits) : | 
| 282 |  |  |  |  |  |  | ($neg, $spos, $sdigits) | 
| 283 |  |  |  |  |  |  | ; | 
| 284 |  |  |  |  |  |  | } | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | my $_PerlSafeInt = $meta->add_type( Type::Tiny::Intersection->new( | 
| 287 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 288 |  |  |  |  |  |  | type_constraints => [ $_PerlNum, $_IntLike, $_NumRange_perlsafe ], | 
| 289 |  |  |  |  |  |  | )->create_child_type( | 
| 290 |  |  |  |  |  |  | name       => 'PerlSafeInt', | 
| 291 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 292 |  |  |  |  |  |  | inlined    => sub { | 
| 293 |  |  |  |  |  |  | my $val = $_[1]; | 
| 294 |  |  |  |  |  |  | "defined $val && !ref($val) && $val =~ /\\d+/ && int($val) == $val && ".$_NumRange_perlsafe->inline_check($val); | 
| 295 |  |  |  |  |  |  | }, | 
| 296 |  |  |  |  |  |  | ) ); | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | my $_BlessedInt = $meta->add_type( Type::Tiny::Intersection->new( | 
| 299 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 300 |  |  |  |  |  |  | type_constraints => [ $_BlessedNum, $_IntLike ], | 
| 301 |  |  |  |  |  |  | )->create_child_type( | 
| 302 |  |  |  |  |  |  | name       => 'BlessedInt', | 
| 303 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 304 |  |  |  |  |  |  | inlined    => sub { | 
| 305 |  |  |  |  |  |  | my $val = $_[1]; | 
| 306 |  |  |  |  |  |  | Types::Standard::Object->inline_check($val)." && $val =~ /\\d+/ && int($val) == $val && int($val) eq $val"; | 
| 307 |  |  |  |  |  |  | }, | 
| 308 |  |  |  |  |  |  | constraint_generator => sub { | 
| 309 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 310 |  |  |  |  |  |  | my $digits = shift; | 
| 311 |  |  |  |  |  |  | $digits =~ /\A[0-9]+\z/ or _croak("Parameter to BlessedInt[`d] expected to be a positive integer; got $digits"); | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | my $_BlessedNum_param = $_BlessedNum->parameterize($digits); | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | Type::Tiny->new( | 
| 316 |  |  |  |  |  |  | display_name => "BlessedInt[$digits]", | 
| 317 |  |  |  |  |  |  | parent     => $self, | 
| 318 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 319 |  |  |  |  |  |  | constraint => sub { | 
| 320 |  |  |  |  |  |  | $_IntLike->check($_) && $_BlessedNum_param->check($_) && do { | 
| 321 |  |  |  |  |  |  | my $num = $_; | 
| 322 |  |  |  |  |  |  | $num =~ s/\D+//g; | 
| 323 |  |  |  |  |  |  | length($num) <= $digits | 
| 324 |  |  |  |  |  |  | } | 
| 325 |  |  |  |  |  |  | }, | 
| 326 |  |  |  |  |  |  | inlined    => sub { | 
| 327 |  |  |  |  |  |  | my $val = $_[1]; | 
| 328 |  |  |  |  |  |  | $_BlessedNum_param->inline_check($val).' && '. | 
| 329 |  |  |  |  |  |  | "$val =~ /\\d+/ && int($val) == $val && int($val) eq $val && do { ". | 
| 330 |  |  |  |  |  |  | 'my $num = '.$val.'; '. | 
| 331 |  |  |  |  |  |  | '$num =~ s/\D+//g; '. | 
| 332 |  |  |  |  |  |  | 'length($num) <= '.$digits.' '. | 
| 333 |  |  |  |  |  |  | '}'; | 
| 334 |  |  |  |  |  |  | }, | 
| 335 |  |  |  |  |  |  | ); | 
| 336 |  |  |  |  |  |  | }, | 
| 337 |  |  |  |  |  |  | ) ); | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | $meta->add_type( Type::Tiny::Union->new( | 
| 340 |  |  |  |  |  |  | #parent     => $_IntLike, | 
| 341 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 342 |  |  |  |  |  |  | type_constraints => [ $_PerlSafeInt, $_BlessedInt ], | 
| 343 |  |  |  |  |  |  | )->create_child_type( | 
| 344 |  |  |  |  |  |  | name       => 'SignedInt', | 
| 345 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 346 |  |  |  |  |  |  | inlined    => sub { | 
| 347 |  |  |  |  |  |  | my $val = $_[1]; | 
| 348 |  |  |  |  |  |  | $_IntLike->inline_check($val).' && ('. | 
| 349 |  |  |  |  |  |  | $_NumRange_perlsafe->inline_check($val).' || '.Types::Standard::Object->inline_check($val). | 
| 350 |  |  |  |  |  |  | ')'; | 
| 351 |  |  |  |  |  |  | }, | 
| 352 |  |  |  |  |  |  | constraint_generator => sub { | 
| 353 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 354 |  |  |  |  |  |  | my $bits = shift; | 
| 355 |  |  |  |  |  |  | $bits =~ /\A[0-9]+\z/ or _croak("Parameter to SignedInt[`b] expected to be a positive integer; got $bits"); | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | my ($min, $max, $digits) = __integer_bits_vars($bits, 0); | 
| 358 |  |  |  |  |  |  | my $_BlessedInt_param = $_BlessedInt->parameterize($digits); | 
| 359 |  |  |  |  |  |  | my $_NumRange_param   = $_NumRange  ->parameterize($min, $max); | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | Type::Tiny::Intersection->new( | 
| 362 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 363 |  |  |  |  |  |  | type_constraints => [ $self, ($_PerlSafeInt|$_BlessedInt_param), $_NumRange_param ], | 
| 364 |  |  |  |  |  |  | )->create_child_type( | 
| 365 |  |  |  |  |  |  | display_name => "SignedInt[$bits]", | 
| 366 |  |  |  |  |  |  | inlined    => sub { | 
| 367 |  |  |  |  |  |  | my $val = $_[1]; | 
| 368 |  |  |  |  |  |  | '('.$_PerlSafeInt->inline_check($val).' || '.$_BlessedInt_param->inline_check($val).') && '. | 
| 369 |  |  |  |  |  |  | $_NumRange_param->inline_check($val); | 
| 370 |  |  |  |  |  |  | }, | 
| 371 |  |  |  |  |  |  | ); | 
| 372 |  |  |  |  |  |  | }, | 
| 373 |  |  |  |  |  |  | ) ); | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | $meta->add_type( | 
| 376 |  |  |  |  |  |  | name       => 'UnsignedInt', | 
| 377 |  |  |  |  |  |  | parent     => $_IntLike, | 
| 378 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 379 |  |  |  |  |  |  | constraint => sub { $_IntLike->check($_) && $_ >= 0 && ($_PerlSafeInt->check($_) || $_BlessedNum->check($_)) }, | 
| 380 |  |  |  |  |  |  | inlined    => sub { | 
| 381 |  |  |  |  |  |  | my $val = $_[1]; | 
| 382 |  |  |  |  |  |  | $_IntLike->inline_check($val)." && $val >= 0 && (". | 
| 383 |  |  |  |  |  |  | $_NumRange_perlsafe->inline_check($val).' || '.Types::Standard::Object->inline_check($val). | 
| 384 |  |  |  |  |  |  | ')'; | 
| 385 |  |  |  |  |  |  | }, | 
| 386 |  |  |  |  |  |  | constraint_generator => sub { | 
| 387 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 388 |  |  |  |  |  |  | my $bits = shift; | 
| 389 |  |  |  |  |  |  | $bits =~ /\A[0-9]+\z/ or _croak("Parameter to UnsignedInt[`b] expected to be a positive integer; got $bits"); | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | my ($min, $max, $digits) = __integer_bits_vars($bits, 1); | 
| 392 |  |  |  |  |  |  | my $_BlessedNum_param = $_BlessedNum->parameterize($digits);  # IntLike check extracted out | 
| 393 |  |  |  |  |  |  | my $_NumRange_param   = $_NumRange  ->parameterize($min, $max); | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | # inline will already have the IntLike check, and maybe not need the extra NumRange check | 
| 396 |  |  |  |  |  |  | my $perlsafe_inline = $min >= $SAFE_NUM_MIN && $max <= $SAFE_NUM_MAX ? | 
| 397 |  |  |  |  |  |  | sub { Types::Standard::Str->inline_check($_[0]) } : | 
| 398 |  |  |  |  |  |  | sub { '('.Types::Standard::Str->inline_check($_[0]).' && '.$_NumRange_perlsafe->inline_check($_[0]).')' } | 
| 399 |  |  |  |  |  |  | ; | 
| 400 |  |  |  |  |  |  |  | 
| 401 |  |  |  |  |  |  | Type::Tiny->new( | 
| 402 |  |  |  |  |  |  | display_name => "UnsignedInt[$bits]", | 
| 403 |  |  |  |  |  |  | parent     => $self, | 
| 404 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 405 |  |  |  |  |  |  | constraint => sub { | 
| 406 |  |  |  |  |  |  | $_IntLike->check($_) && $_NumRange_param->check($_) && | 
| 407 |  |  |  |  |  |  | ($_PerlSafeInt->check($_) || $_BlessedNum_param->check($_)); | 
| 408 |  |  |  |  |  |  | }, | 
| 409 |  |  |  |  |  |  | inlined    => sub { | 
| 410 |  |  |  |  |  |  | my $val = $_[1]; | 
| 411 |  |  |  |  |  |  | $_IntLike->inline_check($val).' && '.$_NumRange_param->inline_check($val).' && '. | 
| 412 |  |  |  |  |  |  | '('.$perlsafe_inline->($val).' || '.$_BlessedNum_param->inline_check($val).')'; | 
| 413 |  |  |  |  |  |  | }, | 
| 414 |  |  |  |  |  |  | ); | 
| 415 |  |  |  |  |  |  | }, | 
| 416 |  |  |  |  |  |  | ); | 
| 417 |  |  |  |  |  |  |  | 
| 418 |  |  |  |  |  |  | ############################################################################# | 
| 419 |  |  |  |  |  |  | # Float/fixed types | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | my $_BlessedFloat = $meta->add_type( | 
| 422 |  |  |  |  |  |  | name       => 'BlessedFloat', | 
| 423 |  |  |  |  |  |  | parent     => $_BlessedNum, | 
| 424 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 425 |  |  |  |  |  |  | constraint => sub { blessed($_)->new(1.2) == 1.2 }, | 
| 426 |  |  |  |  |  |  | inlined    => sub { | 
| 427 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 428 |  |  |  |  |  |  | $self->parent->inline_check($val)." && Scalar::Util::blessed($val)\->new(1.2) == 1.2"; | 
| 429 |  |  |  |  |  |  | }, | 
| 430 |  |  |  |  |  |  | constraint_generator => sub { | 
| 431 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 432 |  |  |  |  |  |  | my $digits = shift; | 
| 433 |  |  |  |  |  |  | $digits =~ /\A[0-9]+\z/ or _croak("Parameter to BlessedFloat[`d] expected to be a positive integer; got $digits"); | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | my $_BlessedNum_param = $_BlessedNum->parameterize($digits); | 
| 436 |  |  |  |  |  |  |  | 
| 437 |  |  |  |  |  |  | Type::Tiny->new( | 
| 438 |  |  |  |  |  |  | display_name => "BlessedFloat[$digits]", | 
| 439 |  |  |  |  |  |  | parent     => $self, | 
| 440 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 441 |  |  |  |  |  |  | constraint => sub { $_BlessedNum_param->check($_) && blessed($_)->new(1.2) == 1.2 }, | 
| 442 |  |  |  |  |  |  | inlined    => sub { | 
| 443 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 444 |  |  |  |  |  |  | $_BlessedNum_param->inline_check($val)." && Scalar::Util::blessed($val)\->new(1.2) == 1.2"; | 
| 445 |  |  |  |  |  |  | }, | 
| 446 |  |  |  |  |  |  | ); | 
| 447 |  |  |  |  |  |  | }, | 
| 448 |  |  |  |  |  |  | ); | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | my $_PerlSafeFloat = $meta->add_type( | 
| 451 |  |  |  |  |  |  | name       => 'PerlSafeFloat', | 
| 452 |  |  |  |  |  |  | parent     => $_PerlNum, | 
| 453 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 454 |  |  |  |  |  |  | constraint => sub { $_NumRange_perlsafe->check($_) || Data::Float::float_is_nan($_) || Data::Float::float_is_infinite($_) }, | 
| 455 |  |  |  |  |  |  | inlined    => sub { | 
| 456 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 457 |  |  |  |  |  |  | $self->parent->inline_check($val).' && ('. | 
| 458 |  |  |  |  |  |  | $_NumRange_perlsafe->inline_check($val)." || Data::Float::float_is_nan($val) || Data::Float::float_is_infinite($val)". | 
| 459 |  |  |  |  |  |  | ')'; | 
| 460 |  |  |  |  |  |  | }, | 
| 461 |  |  |  |  |  |  | ); | 
| 462 |  |  |  |  |  |  |  | 
| 463 |  |  |  |  |  |  | my $_FloatSafeNum = $meta->add_type( Type::Tiny::Union->new( | 
| 464 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 465 |  |  |  |  |  |  | type_constraints => [ $_PerlSafeFloat, $_BlessedFloat ], | 
| 466 |  |  |  |  |  |  | )->create_child_type( | 
| 467 |  |  |  |  |  |  | name       => 'FloatSafeNum', | 
| 468 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 469 |  |  |  |  |  |  | inlined    => sub { | 
| 470 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 471 |  |  |  |  |  |  | $self->parent->inline_check($val).' && ('. | 
| 472 |  |  |  |  |  |  | "!ref($val) && (". | 
| 473 |  |  |  |  |  |  | $_NumRange_perlsafe->inline_check($val)." || Data::Float::float_is_nan($val) || Data::Float::float_is_infinite($val)". | 
| 474 |  |  |  |  |  |  | ') || '. | 
| 475 |  |  |  |  |  |  | Types::Standard::Object->inline_check($val)." && Scalar::Util::blessed($val)->new(1.2) == 1.2". | 
| 476 |  |  |  |  |  |  | ')'; | 
| 477 |  |  |  |  |  |  | }, | 
| 478 |  |  |  |  |  |  | ) ); | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | my $_RealSafeNum = $meta->add_type( Type::Tiny::Intersection->new( | 
| 481 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 482 |  |  |  |  |  |  | type_constraints => [ $_RealNum, $_FloatSafeNum ], | 
| 483 |  |  |  |  |  |  | )->create_child_type( | 
| 484 |  |  |  |  |  |  | name       => 'RealSafeNum', | 
| 485 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 486 |  |  |  |  |  |  | inlined    => sub { | 
| 487 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 488 |  |  |  |  |  |  | $_NumLike->inline_check($val).' && ('. | 
| 489 |  |  |  |  |  |  | "( !ref($val) && ".$_NumRange_perlsafe->inline_check($val)." && not (". | 
| 490 |  |  |  |  |  |  | "Data::Float::float_is_nan($val) || Data::Float::float_is_infinite($val))". | 
| 491 |  |  |  |  |  |  | ') || ('. | 
| 492 |  |  |  |  |  |  | Types::Standard::Object->inline_check($val)." && Scalar::Util::blessed($val)->new(1.2) == 1.2 && ". | 
| 493 |  |  |  |  |  |  | "not ($val->can('is_nan') && $val->is_nan || $val->can('is_inf') && ($val->is_inf('+') || $val->is_inf('-')) )". | 
| 494 |  |  |  |  |  |  | ')'. | 
| 495 |  |  |  |  |  |  | ')'; | 
| 496 |  |  |  |  |  |  | }, | 
| 497 |  |  |  |  |  |  | ) ); | 
| 498 |  |  |  |  |  |  |  | 
| 499 |  |  |  |  |  |  | ### NOTE: These two are very close to another type, but there's just too many variables | 
| 500 |  |  |  |  |  |  | ### to throw into a typical type | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | sub __real_constraint_generator { | 
| 503 | 24 |  |  | 24 |  | 46 | my ($is_perl_safe, $digits, $_NumRange_param, $no_naninf) = @_; | 
| 504 | 24 |  |  |  |  | 60 | my $_BlessedFloat_param = $_BlessedFloat->parameterize($digits); | 
| 505 |  |  |  |  |  |  |  | 
| 506 | 24 | 100 |  |  |  | 7601 | if ($no_naninf) { | 
| 507 |  |  |  |  |  |  | return $is_perl_safe ? | 
| 508 | 0 | 0 | 0 | 0 |  | 0 | sub { ( $_PerlNum->check($_) || $_BlessedFloat_param->check($_) ) && $_NumRange_param->check($_) } : | 
| 509 | 0 | 0 |  | 0 |  | 0 | sub { $_BlessedFloat_param->check($_) && $_NumRange_param->check($_) } | 
| 510 | 12 | 100 |  |  |  | 67 | ; | 
| 511 |  |  |  |  |  |  | } | 
| 512 |  |  |  |  |  |  | else { | 
| 513 |  |  |  |  |  |  | return $is_perl_safe ? | 
| 514 | 0 | 0 | 0 | 0 |  | 0 | sub { ( $_PerlNum->check($_) || $_BlessedFloat_param->check($_) ) && $_NumRange_param->check($_) || $_NaNInf->check($_) } : | 
|  |  |  | 0 |  |  |  |  | 
| 515 | 0 | 0 | 0 | 0 |  | 0 | sub { $_BlessedFloat_param->check($_) && ( $_NumRange_param->check($_) || $_NaNInf->check($_) ); } | 
| 516 | 12 | 100 |  |  |  | 76 | ; | 
| 517 |  |  |  |  |  |  | } | 
| 518 |  |  |  |  |  |  | } | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | sub __real_inline_generator { | 
| 521 | 24 |  |  | 24 |  | 37 | my ($is_perl_safe, $digits, $_NumRange_param, $no_naninf) = @_; | 
| 522 | 24 |  |  |  |  | 53 | my $_BlessedFloat_param = $_BlessedFloat->parameterize($digits); | 
| 523 |  |  |  |  |  |  |  | 
| 524 | 24 | 100 |  |  |  | 1086 | if ($no_naninf) { | 
| 525 |  |  |  |  |  |  | return $is_perl_safe ? | 
| 526 |  |  |  |  |  |  | sub { | 
| 527 | 222 |  |  | 222 |  | 659122 | '( '.$_PerlNum->inline_check($_[1]).' || '.$_BlessedFloat_param->inline_check($_[1]).' )'. | 
| 528 |  |  |  |  |  |  | ' && '.$_NumRange_param->inline_check($_[1]) | 
| 529 |  |  |  |  |  |  | } : | 
| 530 | 78 |  |  | 78 |  | 470113 | sub { $_BlessedFloat_param->inline_check($_[1]).' && '.$_NumRange_param->inline_check($_[1]) } | 
| 531 | 12 | 100 |  |  |  | 85 | ; | 
| 532 |  |  |  |  |  |  | } | 
| 533 |  |  |  |  |  |  | else { | 
| 534 |  |  |  |  |  |  | return $is_perl_safe ? | 
| 535 |  |  |  |  |  |  | sub { | 
| 536 | 222 |  |  | 222 |  | 2228455 | '( '.$_PerlNum->inline_check($_[1]).' || '.$_BlessedFloat_param->inline_check($_[1]).' )'. | 
| 537 |  |  |  |  |  |  | ' && ( '.$_NumRange_param->inline_check($_[1]).' || '.$_NaNInf->inline_check($_[1]).' )' | 
| 538 |  |  |  |  |  |  | } : | 
| 539 | 78 |  |  | 78 |  | 2212860 | sub { $_BlessedFloat_param->inline_check($_[1]).' && ('.$_NumRange_param->inline_check($_[1]).' || '.$_NaNInf->inline_check($_[1]).')' } | 
| 540 | 12 | 100 |  |  |  | 114 | ; | 
| 541 |  |  |  |  |  |  | } | 
| 542 |  |  |  |  |  |  | } | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | $meta->add_type( | 
| 545 |  |  |  |  |  |  | name       => 'FloatBinary', | 
| 546 |  |  |  |  |  |  | parent     => $_FloatSafeNum, | 
| 547 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 548 |  |  |  |  |  |  | # kinda pointless without the parameters | 
| 549 |  |  |  |  |  |  | constraint_generator => sub { | 
| 550 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 551 |  |  |  |  |  |  | my ($bits, $ebits) = (shift, shift); | 
| 552 |  |  |  |  |  |  | $bits  =~ /\A[0-9]+\z/ or _croak( "First parameter to FloatBinary[`b, `e] expected to be a positive integer; got $bits"); | 
| 553 |  |  |  |  |  |  | $ebits =~ /\A[0-9]+\z/ or _croak("Second parameter to FloatBinary[`b, `e] expected to be a positive integer; got $ebits"); | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  | my $sbits = $bits - 1 - $ebits;  # remove sign bit and exponent bits = significand precision | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | # MAX = (2 - 2**(-$sbits-1)) * 2**($ebits-1) | 
| 558 |  |  |  |  |  |  | my $emax = $bigtwo->copy->bpow($ebits-1)->bsub(1);             # Y = (2**($ebits-1)-1) | 
| 559 |  |  |  |  |  |  | my $smin = $bigtwo->copy->bpow(-$sbits-1)->bmul(-1)->badd(2);  # Z = (2 - X) = -X + 2  (where X = 2**(-$sbits-1) ) | 
| 560 |  |  |  |  |  |  | my $max  = $bigtwo->copy->bpow($emax)->bmul($smin);            # MAX = 2**Y * Z | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | my $digits = ceil( $sbits * _BASE2_LOG ); | 
| 563 |  |  |  |  |  |  |  | 
| 564 |  |  |  |  |  |  | my $is_perl_safe = ( | 
| 565 |  |  |  |  |  |  | Data::Float::significand_bits >= $sbits && | 
| 566 |  |  |  |  |  |  | Data::Float::max_finite_exp   >= 2 ** $ebits - 1 && | 
| 567 |  |  |  |  |  |  | Data::Float::have_infinite && | 
| 568 |  |  |  |  |  |  | Data::Float::have_nan | 
| 569 |  |  |  |  |  |  | ); | 
| 570 |  |  |  |  |  |  |  | 
| 571 |  |  |  |  |  |  | my $_NumRange_param = $_NumRange->parameterize(-$max, $max); | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  | Type::Tiny->new( | 
| 574 |  |  |  |  |  |  | display_name => "FloatBinary[$bits, $ebits]", | 
| 575 |  |  |  |  |  |  | parent     => $self, | 
| 576 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 577 |  |  |  |  |  |  | constraint => __real_constraint_generator($is_perl_safe, $digits, $_NumRange_param), | 
| 578 |  |  |  |  |  |  | inlined    => __real_inline_generator    ($is_perl_safe, $digits, $_NumRange_param), | 
| 579 |  |  |  |  |  |  | ); | 
| 580 |  |  |  |  |  |  | }, | 
| 581 |  |  |  |  |  |  | ); | 
| 582 |  |  |  |  |  |  |  | 
| 583 |  |  |  |  |  |  | $meta->add_type( | 
| 584 |  |  |  |  |  |  | name       => 'FloatDecimal', | 
| 585 |  |  |  |  |  |  | parent     => $_FloatSafeNum, | 
| 586 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 587 |  |  |  |  |  |  | # kinda pointless without the parameters | 
| 588 |  |  |  |  |  |  | constraint_generator => sub { | 
| 589 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 590 |  |  |  |  |  |  | my ($digits, $emax) = (shift, shift); | 
| 591 |  |  |  |  |  |  | $digits =~ /\A[0-9]+\z/ or _croak( "First parameter to FloatDecimal[`d, `e] expected to be a positive integer; got $digits"); | 
| 592 |  |  |  |  |  |  | $emax   =~ /\A[0-9]+\z/ or _croak("Second parameter to FloatDecimal[`d, `e] expected to be a positive integer; got $emax"); | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | # We're not going to worry about the (extreme) edge case that | 
| 595 |  |  |  |  |  |  | # Perl might be compiled with decimal float NVs, but we still | 
| 596 |  |  |  |  |  |  | # need to convert to base-2. | 
| 597 |  |  |  |  |  |  | my $sbits = ceil( $digits / _BASE2_LOG ); | 
| 598 |  |  |  |  |  |  | my $emax2 = ceil( $emax   / _BASE2_LOG ); | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | my $max = $bigten->copy->bpow($emax)->bmul( '9.'.('9' x ($digits-1)) ); | 
| 601 |  |  |  |  |  |  |  | 
| 602 |  |  |  |  |  |  | my $is_perl_safe = ( | 
| 603 |  |  |  |  |  |  | Data::Float::significand_bits >= $sbits && | 
| 604 |  |  |  |  |  |  | Data::Float::max_finite_exp   >= $emax2 && | 
| 605 |  |  |  |  |  |  | Data::Float::have_infinite && | 
| 606 |  |  |  |  |  |  | Data::Float::have_nan | 
| 607 |  |  |  |  |  |  | ); | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | my $_NumRange_param = $_NumRange->parameterize(-$max, $max); | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | Type::Tiny->new( | 
| 612 |  |  |  |  |  |  | display_name => "FloatDecimal[$digits, $emax]", | 
| 613 |  |  |  |  |  |  | parent     => $self, | 
| 614 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 615 |  |  |  |  |  |  | constraint => __real_constraint_generator($is_perl_safe, $digits, $_NumRange_param), | 
| 616 |  |  |  |  |  |  | inlined    => __real_inline_generator    ($is_perl_safe, $digits, $_NumRange_param), | 
| 617 |  |  |  |  |  |  | ); | 
| 618 |  |  |  |  |  |  | }, | 
| 619 |  |  |  |  |  |  | ); | 
| 620 |  |  |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | $meta->add_type( | 
| 622 |  |  |  |  |  |  | name       => 'FixedBinary', | 
| 623 |  |  |  |  |  |  | parent     => $_RealSafeNum, | 
| 624 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 625 |  |  |  |  |  |  | # kinda pointless without the parameters | 
| 626 |  |  |  |  |  |  | constraint_generator => sub { | 
| 627 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 628 |  |  |  |  |  |  | my ($bits, $scale) = (shift, shift); | 
| 629 |  |  |  |  |  |  | $bits  =~ /\A[0-9]+\z/ or _croak( "First parameter to FixedBinary[`b, `s] expected to be a positive integer; got $bits"); | 
| 630 |  |  |  |  |  |  | $scale =~ /\A[0-9]+\z/ or _croak("Second parameter to FixedBinary[`b, `s] expected to be a positive integer; got $scale"); | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | my $sbits = $bits - 1; | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | # So, we have a base-10 scale and a base-2 set of $bits.  Lovely. | 
| 635 |  |  |  |  |  |  | # We can't actually figure out if it's Perl safe until we find the | 
| 636 |  |  |  |  |  |  | # $max, adjust with the $scale, and then go BACK to base-2 limits. | 
| 637 |  |  |  |  |  |  | my $div = $bigten->copy->bpow($scale); | 
| 638 |  |  |  |  |  |  | my ($neg, $pos) = ( | 
| 639 |  |  |  |  |  |  | # bdiv returns (quo,rem) in list context :/ | 
| 640 |  |  |  |  |  |  | scalar $bigtwo->copy->bpow($sbits)->bmul(-1)->bdiv($div), | 
| 641 |  |  |  |  |  |  | scalar $bigtwo->copy->bpow($sbits)->bsub(1)->bdiv($div), | 
| 642 |  |  |  |  |  |  | ); | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | my $digits = ceil( $sbits * _BASE2_LOG ); | 
| 645 |  |  |  |  |  |  | my $emin2  = ceil( $scale / _BASE2_LOG ); | 
| 646 |  |  |  |  |  |  |  | 
| 647 |  |  |  |  |  |  | my $is_perl_safe = ( | 
| 648 |  |  |  |  |  |  | Data::Float::significand_bits >= $sbits && | 
| 649 |  |  |  |  |  |  | Data::Float::min_finite_exp   <= -$emin2 | 
| 650 |  |  |  |  |  |  | ); | 
| 651 |  |  |  |  |  |  |  | 
| 652 |  |  |  |  |  |  | my $_NumRange_param = $_NumRange->parameterize($neg, $pos); | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | Type::Tiny->new( | 
| 655 |  |  |  |  |  |  | display_name => "FixedBinary[$bits, $scale]", | 
| 656 |  |  |  |  |  |  | parent     => $self, | 
| 657 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 658 |  |  |  |  |  |  | constraint => __real_constraint_generator($is_perl_safe, $digits, $_NumRange_param, 1), | 
| 659 |  |  |  |  |  |  | inlined    => __real_inline_generator    ($is_perl_safe, $digits, $_NumRange_param, 1), | 
| 660 |  |  |  |  |  |  | ); | 
| 661 |  |  |  |  |  |  | }, | 
| 662 |  |  |  |  |  |  | ); | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | $meta->add_type( | 
| 665 |  |  |  |  |  |  | name       => 'FixedDecimal', | 
| 666 |  |  |  |  |  |  | parent     => $_RealSafeNum, | 
| 667 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 668 |  |  |  |  |  |  | # kinda pointless without the parameters | 
| 669 |  |  |  |  |  |  | constraint_generator => sub { | 
| 670 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 671 |  |  |  |  |  |  | my ($digits, $scale) = (shift, shift); | 
| 672 |  |  |  |  |  |  | $digits =~ /\A[0-9]+\z/ or _croak( "First parameter to FixedDecimal[`d, `s] expected to be a positive integer; got $digits"); | 
| 673 |  |  |  |  |  |  | $scale  =~ /\A[0-9]+\z/ or _croak("Second parameter to FixedDecimal[`d, `s] expected to be a positive integer; got $scale"); | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | my $sbits = ceil( $digits / _BASE2_LOG ); | 
| 676 |  |  |  |  |  |  | my $emin2 = ceil( $scale  / _BASE2_LOG ); | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | my $is_perl_safe = ( | 
| 679 |  |  |  |  |  |  | Data::Float::significand_bits >= $sbits && | 
| 680 |  |  |  |  |  |  | Data::Float::min_finite_exp   <= -$emin2 | 
| 681 |  |  |  |  |  |  | ); | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | my $div = $bigten->copy->bpow($scale); | 
| 684 |  |  |  |  |  |  | my $max = $bigten->copy->bpow($digits)->bsub(1)->bdiv($div); | 
| 685 |  |  |  |  |  |  |  | 
| 686 |  |  |  |  |  |  | my $_NumRange_param = $_NumRange->parameterize(-$max, $max); | 
| 687 |  |  |  |  |  |  |  | 
| 688 |  |  |  |  |  |  | Type::Tiny->new( | 
| 689 |  |  |  |  |  |  | display_name => "FixedDecimal[$digits, $scale]", | 
| 690 |  |  |  |  |  |  | parent     => $self, | 
| 691 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 692 |  |  |  |  |  |  | constraint => __real_constraint_generator($is_perl_safe, $digits, $_NumRange_param, 1), | 
| 693 |  |  |  |  |  |  | inlined    => __real_inline_generator    ($is_perl_safe, $digits, $_NumRange_param, 1), | 
| 694 |  |  |  |  |  |  | ); | 
| 695 |  |  |  |  |  |  | }, | 
| 696 |  |  |  |  |  |  | ); | 
| 697 |  |  |  |  |  |  |  | 
| 698 |  |  |  |  |  |  | ############################################################################# | 
| 699 |  |  |  |  |  |  | # Character types | 
| 700 |  |  |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | $meta->add_type( | 
| 702 |  |  |  |  |  |  | name       => 'Char', | 
| 703 |  |  |  |  |  |  | parent     => Types::Standard::Str, | 
| 704 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 705 |  |  |  |  |  |  | constraint => sub { length($_) == 1 },  # length() will do a proper Unicode char length | 
| 706 |  |  |  |  |  |  | inlined    => sub { | 
| 707 |  |  |  |  |  |  | my ($self, $val) = @_; | 
| 708 |  |  |  |  |  |  | $self->parent->inline_check($val)." && length($val) == 1"; | 
| 709 |  |  |  |  |  |  | }, | 
| 710 |  |  |  |  |  |  | constraint_generator => sub { | 
| 711 |  |  |  |  |  |  | my $self = $Type::Tiny::parameterize_type; | 
| 712 |  |  |  |  |  |  | my ($bits) = (shift); | 
| 713 |  |  |  |  |  |  | $bits =~ /\A[0-9]+\z/ or _croak("Parameter to Char[`b] expected to be a positive integer; got $bits"); | 
| 714 |  |  |  |  |  |  |  | 
| 715 |  |  |  |  |  |  | Type::Tiny->new( | 
| 716 |  |  |  |  |  |  | display_name => "Char[$bits]", | 
| 717 |  |  |  |  |  |  | parent     => $self, | 
| 718 |  |  |  |  |  |  | library    => __PACKAGE__, | 
| 719 |  |  |  |  |  |  | constraint => sub { ord($_) < 2**$bits }, | 
| 720 |  |  |  |  |  |  | inlined    => sub { | 
| 721 |  |  |  |  |  |  | my $val = $_[1]; | 
| 722 |  |  |  |  |  |  | Types::Standard::Str->inline_check($val)." && length($val) == 1 && ord($val) < 2**$bits"; | 
| 723 |  |  |  |  |  |  | }, | 
| 724 |  |  |  |  |  |  | ); | 
| 725 |  |  |  |  |  |  | }, | 
| 726 |  |  |  |  |  |  | ); | 
| 727 |  |  |  |  |  |  |  | 
| 728 |  |  |  |  |  |  | 42; | 
| 729 |  |  |  |  |  |  |  | 
| 730 |  |  |  |  |  |  | __END__ |