Number-Range-0.12/0000755000175000017500000000000012350614052014166 5ustar lshatzerlshatzerNumber-Range-0.12/Makefile.PL0000644000175000017500000000045712334725151016153 0ustar lshatzerlshatzeruse ExtUtils::MakeMaker; WriteMakefile( NAME => 'Number::Range', VERSION_FROM => 'lib/Number/Range.pm', PREREQ_PM => {}, ($] >= 5.005 ? (ABSTRACT_FROM => 'lib/Number/Range.pm', AUTHOR => 'Larry Shatzer, Jr. ') : ()), ); Number-Range-0.12/MANIFEST0000644000175000017500000000023012334725151015317 0ustar lshatzerlshatzerChanges Makefile.PL MANIFEST README t/Number-Range.t lib/Number/Range.pm META.yml Module meta-data (added by MakeMaker) Number-Range-0.12/Changes0000644000175000017500000000254312350613763015475 0ustar lshatzerlshatzerRevision history for Perl extension Number::Range. 0.01 Fri Mar 19 12:02:36 2004 - original version; created by h2xs 1.23 with options -AX -n Number::Range 0.02 Sat Mar 20 2004 - Added the ability to add and delete ranges from existing range. 0.03 Mon Mar 22, 2004 - Allowed inrange() to take a list of tests, and return a list of true/false if in array context, or single true if all are true, or a false if one of them is false. 0.04 Tue Mar 23, 2004 - Created range() to output either a list of all numbers in the range, or to output a range format, depending on list context. 0.05 Mon May 17, 2004 - Cleaned up POD and documentation. - Created the size() method to return number of elements in the range. 0.06 Wed July 21, 2004 - Cleaned up POD - Added a SEE ALSO for Number::Tollerant 0.06 Tue April 29, 2008 - Fix #15478, if a range is 1,3,4,5,6 don't return 1..1,3..6 instead, return 1,3..6 Thanks to noodles 0.08 Fri May 18, 2012 - Now, for example, inrange('02') will return true for '1..12' range. 0.09 Wed June 20, 2012 - Now will handle large ranges. 0.10 Wed June 20, 2012 - Fix bug in 0.09 0.11 Wed May 14, 2014 - Add in rangeList (https://github.com/larrys/Number-Range/pull/4) 0.11 Thu June 19, 2014 - Fix rangeList when only large ranges are used (https://github.com/larrys/Number-Range/pull/6) Number-Range-0.12/README0000644000175000017500000000107012350613670015051 0ustar lshatzerlshatzerNumber-Range version 0.12 ========================= Number::Range is an object-oriented interface to test if a number exists in a given range, and to be able to manipulate the range. INSTALLATION To install this module type the following: perl Makefile.PL make make test make install DEPENDENCIES Nothing that is outside of the core Perl modules. COPYRIGHT AND LICENCE Copyright (C) 2004-14 by Larry Shatzer, Jr. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. Number-Range-0.12/t/0000755000175000017500000000000012350614052014431 5ustar lshatzerlshatzerNumber-Range-0.12/t/Number-Range.t0000644000175000017500000000413212350613646017110 0ustar lshatzerlshatzeruse Test::More tests => 40; BEGIN { use_ok('Number::Range') }; ok($range = Number::Range->new("10..100")); ok($range->inrange(10) == 1); ok($range->inrange(1000) == 0); $range = Number::Range->new("10..50,60..100"); ok($range->inrange(10) == 1); ok($range->inrange(55) == 0); ok($range->inrange(75) == 1); $range = Number::Range->new("10..100","150..200"); ok($range->inrange(10) == 1); ok($range->inrange(125) == 0); ok($range->inrange(155) == 1); $range = Number::Range->new("-10..10"); ok($range->inrange(10) == 1); ok($range->inrange(-10) == 1); ok($range->inrange(0) == 1); $range->addrange("20..30"); ok($range->inrange(25) == 1); ok($range->inrange(10) == 1); ok($range->inrange(15) == 0); $range->delrange("-10..0"); ok($range->inrange(-10) == 0); ok($range->inrange(10) == 1); ok($range->inrange(25) == 1); ok($range->inrange(10,25)); @test = $range->inrange(10,25,1000); @rc = qw/1 1 0/; is_deeply(\@rc, \@test); $range = Number::Range->new("1..100,150..200"); $rangeformat = $range->range; cmp_ok("1..100,150..200", 'eq', $rangeformat); ok($range->size == 151); $range = Number::Range->new("1,3,4,5,6"); ok($range->range eq "1,3..6"); $range = Number::Range->new("1,2,3,4,5,6"); ok($range->range eq "1..6"); ok($range->inrange("01")); # Tests for large range $range = Number::Range->new("0..99999999"); ok($range->inrange("1")); ok($range->inrange("99999999")); ok($range->inrange("09")); ok($range->size == 100000000); # Tests for rangeList function $range = Number::Range->new("1..10","150..200","300..300000","999999"); @rangeList = $range->rangeList(); ok($rangeList[0][0] == 1); ok($rangeList[0][1] == 10); ok($rangeList[1][0] == 150); ok($rangeList[1][1] == 200); ok($rangeList[2][0] == 999999); # Single entries will not have a second indice ok($rangeList[2][1] == undef); # Large ranges always end up at the end of the list because they are processed seperatly ok($rangeList[3][0] == 300); ok($rangeList[3][1] == 300000); # Test rangeList with only a single large range $range = Number::Range->new("300..300000"); @rangeList = $range->rangeList(); ok($rangeList[0][0] == 300); ok($rangeList[0][1] == 300000); Number-Range-0.12/lib/0000755000175000017500000000000012350614052014734 5ustar lshatzerlshatzerNumber-Range-0.12/lib/Number/0000755000175000017500000000000012350614052016164 5ustar lshatzerlshatzerNumber-Range-0.12/lib/Number/Range.pm0000644000175000017500000002417412350613717017575 0ustar lshatzerlshatzerpackage Number::Range; use strict; use warnings; use warnings::register; use Carp; use POSIX; # Only needed for max integer size check in large ranges require Exporter; our @ISA = qw(Exporter); our $VERSION = '0.12'; sub new { my $this = shift; my $class = ref($this) || $this; my $self = {}; bless $self, $class; # Max size of range before its stored as a pointer instead of hashed $self->{max_hash_size} = 1000; $self->initialize("add", @_); return $self; } sub initialize { my $self = shift; my $type = shift; my $rangesep = qr/(?:\.\.)/; my $sectsep = qr/(?:\s|,)/; my $validation = qr/(?: [^0-9,. -]| # These are the only allowed characters (Numbers and "separators") $rangesep$sectsep| # We don't want a range separator followed by section separator $sectsep$rangesep| # We don't want a section separator followed by range separator \d-\d| # We don't want 10-10 since - is for negative numbers ^$sectsep| # We don't want a section separator at the start ^$rangesep| # We don't want a range separator at the start $sectsep$| # We don't want a section separator at the end $rangesep$ # We don't want a range separator at the end )/x; foreach my $item (@_) { croak "$item contains invalid data" if ($item =~ m/$validation/g); foreach my $section (split(/$sectsep/, $item)) { if ($section =~ m/$rangesep/) { my ($start, $end) = split(/$rangesep/, $section, 2); if ($start > $end) { carp "$start is > $end" if (warnings::enabled()); ($start, $end) = ($end, $start); } if ($start == $end) { carp "$start:$end is pointless" if (warnings::enabled()); if ($type eq "add") { $self->_addnumbers($start); } elsif ($type eq "del") { $self->_delnumbers($start); } else { die "Neither 'add' nor 'del' was passed initialize()"; } } else { if ($type eq "add") { if(($end - $start) > $self->{max_hash_size}) { $self->_addrange($start, $end); } else { $self->_addnumbers($start .. $end); } } elsif ($type eq "del") { if($end - $start > $self->{max_hash_size}) { $self->_delrange($start, $end); } else { $self->_delnumbers($start .. $end); } } else { die "Neither 'add' nor 'del' was passed initialize()"; } } } else { if ($type eq "add") { $self->_addnumbers($section); } elsif ($type eq "del") { $self->_delnumbers($section); } else { die "Neither 'add' nor 'del' was passed initialize()"; } } } } } sub set_max_hash_size { my $self = shift; my $val = shift; if($val !~ m/^\d+$/) { return 0; } $self->{max_hash_size} = $val; } sub _addrange { my $self = shift; my $start = shift; my $end = shift; $self->{_largeRangehash}{"$start .. $end"} = [$start, $end]; } sub _delrange { my $self = shift; my $start = shift; my $end = shift; delete $self->{_largeRangehash}{"$start .. $end"}; } sub _testlarge { my $self = shift; my $test = shift; if(!exists($self->{_largeRangehash})) { return 0; } foreach my $rangeID (keys(%{$self->{_largeRangehash}})) { my $range = $self->{_largeRangehash}->{$rangeID}; if ($test >= @$range[0] && $test <= @$range[1]) { return 1; } } return 0; } sub _addnumbers { my $self = shift; foreach my $number (@_) { if (warnings::enabled()) { carp "$number already in range" if $self->inrange($number); } $self->{_rangehash}{$number} = 1; } } sub _delnumbers { my $self = shift; foreach my $number (@_) { if (warnings::enabled()) { carp "$number not in range or already removed" if (!$self->inrange($number)); } delete $self->{_rangehash}{$number}; } } sub inrange { my $self = shift; if (scalar(@_) == 1) { if ( exists($self->{_rangehash}{-+-$_[0]}) || $self->_testlarge($_[0])) { return 1; } else { return 0; } } else { if (wantarray) { my @returncodes; foreach my $test (@_) { push(@returncodes, ($self->inrange($test)) ? 1 : 0); } return @returncodes; } else { foreach my $test (@_) { if (!$self->inrange($test)) { return 0; } return 1; } } } } sub addrange { my $self = shift; $self->initialize("add", @_); } sub delrange { my $self = shift; $self->initialize("del", @_); } sub range { my $self = shift; my $excludeLarge = shift; if (wantarray) { my @range = keys(%{$self->{_rangehash}}); if(! $excludeLarge && exists($self->{_largeRangehash})) { foreach my $rangeID (keys(%{$self->{_largeRangehash}})) { my $range = $self->{_largeRangehash}->{$rangeID}; if ( @$range[0] > LONG_MAX || @$range[1] > LONG_MAX || ( @$range[1] - @$range[0]) > LONG_MAX ) { carp "Range to large to return" if (warnings::enabled()); return 0; } @range = (@range, @$range[0]..@$range[1]); } } my @sorted = sort {$a <=> $b} @range; return @sorted; } else { my @range = $self->range; my $previous = shift @range; my $format = "$previous"; foreach my $current (@range) { if ($current == ($previous + 1)) { $format =~ s/\.\.$previous$//; $format .= "..$current"; } else { $format .= ",$current"; } $previous = $current; } return $format; } } sub size { my $self = shift; my @temp = keys(%{$self->{_rangehash}});; my $size = scalar(@temp); if(exists($self->{_largeRangehash})) { foreach my $rangeID (keys(%{$self->{_largeRangehash}})) { my $range = $self->{_largeRangehash}->{$rangeID}; $size += (@$range[1] - @$range[0]) + 1; } } return $size; } sub rangeList { my $self = shift; my @return; # Get the range as an array (excluding large ones) my @range = $self->range(1); # If we have any ranges if (@range) { # Get the first element in the array range my $previous = shift(@range); my @sub = ($previous); # Process ranges stored as arrays foreach my $current (@range) { if ($current == ($previous + 1)) { $sub[1] = $current; } else { push(@return,[@sub]); @sub = ($current); } $previous = $current; } push(@return,[@sub]); } # Process ranges stored as large range hash entries if($self->{_largeRangehash}) { while(my @range = each(%{$self->{_largeRangehash}}) ) { push(@return, [int($range[1][0]), int($range[1][1])]); } } return @return; } 1; __END__ =head1 NAME Number::Range - Perl extension defining ranges of numbers and testing if a number is found in the range. You can also add and delete from this range. =head1 SYNOPSIS use Number::Range; my $range = Number::Range->new("-10..10,12,100..120"); if ($range->inrange("13")) { print "In range\n"; } else { print "Not in range\n"; } $range->addrange("200..300"); $range->delrange("250..255"); my $format = $range->range; # $format will be '-10..10,12,100..120,200..249,256..300' =head1 DESCRIPTION Number::Range will take a description of a range, and then allow you to test on if a number falls within the range. You can also add and delete from the range. =head2 RANGE FORMAT The format used for range is pretty straight forward. To separate sections of ranges it uses a C<,> or whitespace. To create the range, it uses C<..> to do this, much like Perl's own binary C<..> range operator in list context. =head2 METHODS =over =item new $range = Number::Range->new("10..20","25..30","100"); Creates the range object. It will accept any number of ranges as its input. =item addrange $range->addrange("22"); This will also take any number of ranges as input and add them to the existing range. =item delrange $range->delrange("10"); This will also take any number of ranges as input and delete them from the existing range. =item inrange $range->inrange("26"); my @results = $range->inrange("27","200"); This will take one or more numbers and check if each of them exists in the range. If passed a list, and in array context, it will return a list of C<0>'s or C<1>'s, depending if that one was true or false in the list position. If in scalar context, it will return a single C<1> if all are true, or a single C<0> if one of them failed. =item range $format = $range->range; @numbers = $range->range; Depending on context this will return either an array of all the numbers found in the range, for list context. For scalar context it will return a range string. =item size $size = $range->size; This will return the total number of entries in the range. =item rangeList @rangeList = $range->rangeList; Returns the range as an array list where each element in the list is an array representing the start and stop points of a range. Single element ranges are returned as single element arrays with only on indice. [ [10,20], [25,30], [100] ] =back =head2 EXPORT None by default. =head1 SEE ALSO L, L, and L for similar modules. =head1 AUTHOR Larry Shatzer, Jr., Elarrysh@cpan.orgE =head1 COPYRIGHT AND LICENSE Copyright (C) 2004-14 by Larry Shatzer, Jr. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut Number-Range-0.12/META.yml0000644000175000017500000000107312350614052015440 0ustar lshatzerlshatzer--- #YAML:1.0 name: Number-Range version: 0.12 abstract: Perl extension defining ranges of numbers and testing if a author: - Larry Shatzer, Jr. license: unknown distribution_type: module configure_requires: ExtUtils::MakeMaker: 0 build_requires: ExtUtils::MakeMaker: 0 requires: {} no_index: directory: - t - inc generated_by: ExtUtils::MakeMaker version 6.57_05 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4