AlignDB-IntSpan

 view release on metacpan or  search on metacpan

lib/AlignDB/IntSpan.pm  view on Meta::CPAN

package AlignDB::IntSpan;
use strict;
use warnings;

use Carp;
use Scalar::Util;
use Scalar::Util::Numeric;

use overload (
    q{0+}   => sub { Carp::confess "Can't numerify an AlignDB::IntSpan\n" },
    q{bool} => q{is_not_empty},
    q{""}   => q{as_string},

    # use Perl standard behaviours for other operations
    fallback => 1,
);

our $VERSION = '1.1.1';

my $POS_INF = 2_147_483_647 - 1;             # INT_MAX - 1
my $NEG_INF = ( -2_147_483_647 - 1 ) + 1;    # INT_MIN + 1

sub POS_INF {
    return $POS_INF - 1;
}

sub NEG_INF {
    return $NEG_INF;
}

sub EMPTY_STRING {
    return '-';
}

sub new {
    my $class = shift;
    my $self  = {};
    $self->{edges} = [];
    bless $self, $class;
    $self->add(@_) if @_ > 0;
    return $self;
}

sub valid {
    my $this    = shift;
    my $runlist = shift;

    my $class = ref($this) || $this;
    my $set = new $class;

    eval { $set->_runlist_to_ranges($runlist) };
    return $@ ? 0 : 1;
}

sub clear {
    my $self = shift;
    $self->{edges} = [];
    return $self;
}

sub edges_ref {
    my $self = shift;
    return $self->{edges};
}

sub edges {
    my $self = shift;
    return @{ $self->edges_ref };
}

sub edge_size {

lib/AlignDB/IntSpan.pm  view on Meta::CPAN

    my @sets;
    my @edges = $self->edges;
    while (@edges) {
        my $lower = shift @edges;
        my $upper = shift(@edges) - 1;
        push @sets, Scalar::Util::blessed($self)->new("$lower-$upper");
    }

    if (@sets) {
        return @sets;
    }
    else {
        return;
    }
}

sub runlists {
    my $self = shift;

    if ( $self->is_empty ) {
        return $self->EMPTY_STRING;
    }

    my @runlists;
    my @edges = $self->edges;
    while (@edges) {
        my $lower  = shift @edges;
        my $upper  = shift(@edges) - 1;
        my $string = $lower == $upper ? $lower : $lower . '-' . $upper;
        push @runlists, $string;
    }

    if (@runlists) {
        return @runlists;
    }
    else {
        return;
    }
}

sub cardinality {
    my $self = shift;

    my $cardinality = 0;
    my @edges       = $self->edges;
    while (@edges) {
        my $lower = shift @edges;
        my $upper = shift(@edges) - 1;
        $cardinality += $upper - $lower + 1;
    }

    return $cardinality;
}

sub is_empty {
    my $self = shift;
    my $result = $self->edge_size == 0 ? 1 : 0;
    return $result;
}

sub is_not_empty {
    my $self = shift;
    return !$self->is_empty;
}

sub is_neg_inf {
    my $self = shift;
    return $self->edges_ref->[0] == $NEG_INF;
}

sub is_pos_inf {
    my $self = shift;
    return $self->edges_ref->[-1] == $POS_INF;
}

sub is_infinite {
    my $self = shift;
    return $self->is_neg_inf || $self->is_pos_inf;
}

sub is_finite {
    my $self = shift;
    return !$self->is_infinite;
}

sub is_universal {
    my $self = shift;
    return $self->edge_size == 2 && $self->is_neg_inf && $self->is_pos_inf;
}

sub contains_all {
    my $self = shift;

    for my $i (@_) {
        my $pos = $self->_find_pos( $i + 1, 0 );
        return 0 unless $pos & 1;
    }

    return 1;
}

sub contains_any {
    my $self = shift;

    for my $i (@_) {
        my $pos = $self->_find_pos( $i + 1, 0 );
        return 1 if $pos & 1;
    }

    return 0;
}

#@returns AlignDB::IntSpan
sub add_pair {
    my $self   = shift;
    my @ranges = @_;

    if ( scalar(@ranges) != 2 ) {
        Carp::confess "Number of ranges must be two: @ranges\n";
    }

lib/AlignDB/IntSpan.pm  view on Meta::CPAN


sub map_set {
    my $self     = shift;
    my $code_ref = shift;

    my @map_elements;
    for ( $self->elements ) {
        for my $element ( $code_ref->() ) {
            if ( defined $element ) {
                push @map_elements, $element;
            }
        }

    }
    my $map_set = Scalar::Util::blessed($self)->new(@map_elements);

    return $map_set;
}

sub substr_span {
    my $self   = shift;
    my $string = shift;

    my $sub_string = "";
    my @spans      = $self->spans;

    for (@spans) {
        my ( $lower, $upper ) = @$_;
        my $length = $upper - $lower + 1;

        $sub_string .= substr( $string, $lower - 1, $length );
    }

    return $sub_string;
}

#@returns AlignDB::IntSpan
sub banish_span {
    my $self  = shift;
    my $start = shift;
    my $end   = shift;

    my $remove_length = $end - $start + 1;

    my $new = $self->map_set(
        sub {
                  $_ < $start ? $_
                : $_ > $end   ? $_ - $remove_length
                :               ();
        }
    );

    return $new;
}

#@returns AlignDB::IntSpan
sub cover {
    my $self = shift;

    my $cover = Scalar::Util::blessed($self)->new;
    if ( $self->is_not_empty ) {
        $cover->add_pair( $self->min, $self->max );
    }
    return $cover;
}

#@returns AlignDB::IntSpan
sub holes {
    my $self = shift;

    my $holes = Scalar::Util::blessed($self)->new;

    if ( $self->is_empty or $self->is_universal ) {

        # empty set and universal set have no holes
    }
    else {
        my $c_set  = $self->complement;
        my @ranges = $c_set->ranges;

        # Remove infinite arms of complement set
        if ( $c_set->is_neg_inf ) {
            shift @ranges;
            shift @ranges;
        }
        if ( $c_set->is_pos_inf ) {
            pop @ranges;
            pop @ranges;
        }
        $holes->add_range(@ranges);
    }

    return $holes;
}

#@returns AlignDB::IntSpan
sub inset {
    my $self = shift;
    my $n    = shift;

    my $inset = Scalar::Util::blessed($self)->new;
    my @edges = $self->edges;
    while (@edges) {
        my $lower = shift @edges;
        my $upper = shift(@edges) - 1;
        if ( $lower != $self->NEG_INF ) {
            $lower += $n;
        }
        if ( $upper != $self->POS_INF ) {
            $upper -= $n;
        }
        $inset->add_pair( $lower, $upper )
            if $lower <= $upper;
    }

    return $inset;
}

#@returns AlignDB::IntSpan
sub trim {
    my $self = shift;

lib/AlignDB/IntSpan.pm  view on Meta::CPAN


=head2 B<INTERFACE: Set contents>

=head2 edges_ref

Return the internal used ArrayRef representing the set.

I don't think you should use this method.

=head2 edges

Return the internal used Array representing the set.

I don't think you should use this method.

=head2 edge_size

Return the number of edges

=head2 span_size

Return the number of spans

=head2 as_string

Return a string representation of the set.

=head2 as_array

Return an array containing all the members of the set in ascending order.

=head2 B<INTERFACE: Span contents>

=head2 ranges

Returns the runs in $set, as a list of ($lower, $upper)

=head2 spans

Returns the runs in $set, as a list of [$lower, $upper]

=head2 sets

Returns the runs in $set, as a list of AlignDB::IntSpan objects. The sets in
the list are in order.

=head2 runlists

Returns the runs in $set, as a list of "$lower-$upper"

=head2 B<INTERFACE: Set cardinality>

=head2 cardinality

Returns the number of elements in $set.

=head2 is_empty

Return true if the set is empty.

=head2 is_not_empty

Return true if the set is not empty.

=head2 is_neg_inf

Return true if the set is negtive infinite.

=head2 is_pos_inf

Return true if the set is positive infinite.

=head2 is_infinite

Return true if the set is infinite.

=head2 is_finite

Return true if the set is finite.

=head2 is_universal

Return true if the set contains all integers.

=head2 B<INTERFACE: Membership test>

=head2 contains_all

Return true if the set contains all of the specified numbers.

=head2 contains_any

Return true if the set contains any of the specified numbers.

=head2 B<INTERFACE: Member operations>

=head2 add_pair

    $set->add_pair($lower, $upper);

Add a pair of inclusive integers to the set.

A pair of arguments constitute a range

=head2 add_range

    $set->add_range($lower, $upper);

Add the inclusive range of integers to the set.

Multiple ranges may be specified. Each pair of arguments constitute a range

=head2 add_runlist

    $set->add_runlist($runlist);

Add the specified runlist to the set.

=head2 add

    $set->add($number1, $number2, $number3 ...)



( run in 0.423 second using v1.01-cache-2.11-cpan-2398b32b56e )