sync w/ main trunk
[bioperl-live.git] / Bio / Range.pm
blobc00e54ada503cf21ae2c6f28355b45dcae6b83ef
1 # $Id$
3 # BioPerl module for Bio::Range
5 # Please direct questions and support issues to <bioperl-l@bioperl.org>
7 # Cared for by Heikki Lehvaslaiho <heikki-at-bioperl-dot-org>
9 # Copywright Matthew Pocock
11 # You may distribute this module under the same terms as perl itself
13 # POD documentation - main docs before the code
16 =head1 NAME
18 Bio::Range - Pure perl RangeI implementation
20 =head1 SYNOPSIS
22 $range = Bio::Range->new(-start=>10, -end=>30, -strand=>+1);
23 $r2 = Bio::Range->new(-start=>15, -end=>200, -strand=>+1);
25 print join(', ', $range->union($r2)), "\n";
26 print join(', ', $range->intersection($r2)), "\n";
28 print $range->overlaps($r2), "\n";
29 print $range->contains($r2), "\n";
31 =head1 DESCRIPTION
33 This provides a pure perl implementation of the BioPerl range
34 interface.
36 Ranges are modeled as having (start, end, length, strand). They use
37 Bio-coordinates - all points E<gt>= start and E<lt>= end are within the
38 range. End is always greater-than or equal-to start, and length is
39 greather than or equal to 1. The behaviour of a range is undefined if
40 ranges with negative numbers or zero are used.
42 So, in summary:
44 length = end - start + 1
45 end >= start
46 strand = (-1 | 0 | +1)
48 =head1 FEEDBACK
50 =head2 Mailing Lists
52 User feedback is an integral part of the evolution of this and other
53 Bioperl modules. Send your comments and suggestions preferably to one
54 of the Bioperl mailing lists. Your participation is much appreciated.
56 bioperl-l@bioperl.org - General discussion
57 http://bioperl.org/wiki/Mailing_lists - About the mailing lists
59 =head2 Support
61 Please direct usage questions or support issues to the mailing list:
63 L<bioperl-l@bioperl.org>
65 rather than to the module maintainer directly. Many experienced and
66 reponsive experts will be able look at the problem and quickly
67 address it. Please include a thorough description of the problem
68 with code and data examples if at all possible.
70 =head2 Reporting Bugs
72 Report bugs to the Bioperl bug tracking system to help us keep track
73 the bugs and their resolution. Bug reports can be submitted via the web:
75 http://bugzilla.open-bio.org/
77 =head1 AUTHOR - Heikki Lehvaslaiho
79 Email heikki-at-bioperl-dot-org
81 =head1 APPENDIX
83 The rest of the documentation details each of the object
84 methods. Internal methods are usually preceded with a _
86 =cut
88 package Bio::Range;
90 use strict;
91 use Carp;
92 use integer;
95 use base qw(Bio::Root::Root Bio::RangeI);
97 =head1 Constructors
99 =head2 new
101 Title : new
102 Usage : $range = Bio::Range->new(-start => 100, -end=> 200, -strand = +1);
103 Function: generates a new Bio::Range
104 Returns : a new range
105 Args : -strand (defaults to 0) and any two of (-start, -end, -length),
106 the third will be calculated
108 =cut
110 sub new {
111 my ($caller, @args) = @_;
112 my $self = $caller->SUPER::new(@args);
113 my ($strand, $start, $end, $length) =
114 $self->_rearrange([qw(STRAND
115 START
116 END
117 LENGTH
118 )],@args);
119 $self->strand($strand || 0);
121 if(defined $start ) {
122 $self->start($start);
123 if(defined $end) {
124 $self->end($end);
125 } elsif(defined $length) {
126 $self->end($self->start()+ $length - 1);
128 } elsif(defined $end && defined $length ) {
129 $self->end($end);
130 $self->start($self->end() - $length + 1);
132 return $self;
135 =head2 unions
137 Title : unions
138 Usage : @unions = Bio::Range->unions(@ranges);
139 Function: generate a list of non-intersecting Bio::Range objects
140 from a list of Bio::Range objects which may intersect
141 Returns : a list of Bio::Range objects
142 Args : a list of Bio::Range objects
145 =cut
147 sub unions {
148 my ($class,@i) = @_;
150 my $i = 0;
151 my %i = map { $i++ => $_ } @i;
153 my $lastsize = scalar(keys %i);
155 do {
157 foreach my $j (sort { $i{$a}->start <=> $i{$b}->start } keys %i){
158 foreach my $k (sort { $i{$a}->start <=> $i{$b}->start } keys %i){
160 #it may have been replaced by a union under the key of
161 #the overlapping range, we are altering the hash in-place
162 next unless $i{$j};
164 next if $i{$k}->end < $i{$j}->start;
165 last if $i{$k}->start > $i{$j}->end;
167 if($i{$j}->overlaps($i{$k})){
168 my($start,$end,$strand) = $i{$j}->union($i{$k});
169 delete($i{$k});
170 $i{$j} = Bio::Range->new( -start => $start , -end => $end , -strand => $strand );
175 goto DONE if scalar(keys %i) == $lastsize;
176 $lastsize = scalar(keys %i);
178 #warn $lastsize;
180 } while(1);
182 DONE:
184 return values %i;
188 =head1 Member variable access
190 These methods let you get at and set the member variables
192 =head2 start
194 Title : start
195 Function : return or set the start co-ordinate
196 Example : $s = $range->start(); $range->start(7);
197 Returns : the value of the start co-ordinate
198 Args : optionally, the new start co-ordinate
199 Overrides: Bio::RangeI::start
201 =cut
203 sub start {
204 my ($self,$value) = @_;
205 if( defined $value) {
206 $self->throw("'$value' is not an integer.\n")
207 unless $value =~ /^[-+]?\d+$/;
208 $self->{'start'} = $value;
210 return $self->{'start'};
213 =head2 end
215 Title : end
216 Function : return or set the end co-ordinate
217 Example : $e = $range->end(); $range->end(2000);
218 Returns : the value of the end co-ordinate
219 Args : optionally, the new end co-ordinate
220 Overrides: Bio::RangeI::end
222 =cut
224 sub end {
226 my ($self,$value) = @_;
227 if( defined $value) {
228 $self->throw("'$value' is not an integer.\n")
229 unless $value =~ /^[-+]?\d+$/;
230 $self->{'end'} = $value;
232 return $self->{'end'};
235 =head2 strand
237 Title : strand
238 Function : return or set the strandedness
239 Example : $st = $range->strand(); $range->strand(-1);
240 Returns : the value of the strandedness (-1, 0 or 1)
241 Args : optionally, the new strand - (-1, 0, 1) or (-, ., +).
242 Overrides: Bio::RangeI::strand
244 =cut
246 sub strand {
247 my $self = shift;
248 if(@_) {
249 my $val = shift;
250 $val =~ tr/+/1/;
251 $val =~ tr/-/-1/;
252 $val =~ tr/./0/;
253 if($val == -1 || $val == 0 || $val == 1 ) {
254 $self->{'strand'} = $val;
257 return $self->{'strand'};
260 =head2 length
262 Title : length
263 Function : returns the length of this range
264 Example : $length = $range->length();
265 Returns : the length of this range, equal to end - start + 1
266 Args : if you attempt to set the length an exception will be thrown
267 Overrides: Bio::RangeI::Length
269 =cut
271 sub length {
272 my $self = shift;
273 if(@_) {
274 confess ref($self), "->length() is read-only";
276 return $self->end() - $self->start() + 1;
279 =head2 toString
281 Title : toString
282 Function: stringifies this range
283 Example : print $range->toString(), "\n";
284 Returns : a string representation of this range
286 =cut
288 sub toString {
289 my $self = shift;
290 return "(${\$self->start}, ${\$self->end}) strand=${\$self->strand}";
293 =head1 Boolean Methods
295 These methods return true or false.
297 $range->overlaps($otherRange) && print "Ranges overlap\n";
299 =head2 overlaps
301 Title : overlaps
302 Usage : if($r1->overlaps($r2)) { do stuff }
303 Function : tests if $r2 overlaps $r1
304 Args : a range to test for overlap with
305 Returns : true if the ranges overlap, false otherwise
306 Inherited: Bio::RangeI
308 =head2 contains
310 Title : contains
311 Usage : if($r1->contains($r2) { do stuff }
312 Function : tests whether $r1 totally contains $r2
313 Args : a range to test for being contained
314 Returns : true if the argument is totally contained within this range
315 Inherited: Bio::RangeI
317 =head2 equals
319 Title : equals
320 Usage : if($r1->equals($r2))
321 Function : test whether $r1 has the same start, end, length as $r2
322 Args : a range to test for equality
323 Returns : true if they are describing the same range
324 Inherited: Bio::RangeI
326 =head1 Geometrical methods
328 These methods do things to the geometry of ranges, and return
329 triplets (start, end, strand) from which new ranges could be built.
331 =head2 intersection
333 Title : intersection
334 Usage : ($start, $stop, $strand) = $r1->intersection($r2)
335 Function : gives the range that is contained by both ranges
336 Args : a range to compare this one to
337 Returns : nothing if they do not overlap, or the range that they do overlap
338 Inherited: Bio::RangeI::intersection
340 =cut
342 =head2 union
344 Title : union
345 Usage : ($start, $stop, $strand) = $r1->union($r2);
346 : ($start, $stop, $strand) = Bio::Range->union(@ranges);
347 Function : finds the minimal range that contains all of the ranges
348 Args : a range or list of ranges
349 Returns : the range containing all of the ranges
350 Inherited: Bio::RangeI::union
352 =cut