001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021package org.biojava.nbio.genome.parsers.gff;
022
023import org.biojava.nbio.genome.App;
024import org.slf4j.Logger;
025import org.slf4j.LoggerFactory;
026
027
028/**
029 * A location on a sequence.
030 * A location is a contiguous range of indices, with a single start and end point.
031 * <br><br>
032 * Internally, location indices are stored in Java "half-open" format: the start is the (origin 0) index of
033 * the first symbol in the range; the end is the origin 0 index of the first symbol PAST the
034 * end of the range, so that end - start == length.
035 * <br><br>
036 * Location objects, once constructed, cannot be changed. Instead, all methods return a new
037 * location. This allows the use of "method chaining" to implement a particular calculation.
038 * For example, consider the chained statement "loc.prefix( 100 ).suffix( 10 )",
039 * which first applies the prefix method to
040 * the variable named loc, and then the suffix method to the result.
041 * Together, the chained operations create a new location object of length 10
042 * whose start is the index of the 90th symbol.
043 * Here's another example. This one returns a location object holding the coordinates of the intron between
044 * the first exon (location exon1) and
045 * the second exon (location exon2) on a sequence (seq): "seq.prefix( exon2 ).suffix( exon1 )"
046 * <br><br>
047 * About the negative (reverse) strand: The location object stores reverse strand locations as
048 * negative indices. For example, the positive strand location from index 12 to index 97 is
049 * on the opposite side as index -97 (start) to index -12 (end). Note that the larger index is
050 * always downstream from the smaller index, (i.e. start &lt;= end, regardless of strand).
051 * Obviously this representation makes it trivial
052 * to convert a location from one strand to the other.
053 * <br><br>
054 * Additional points regarding the use of locations on opposite strands:
055 *<br>
056 * (1) Opposite strand locations cannot be compared, eg isBefore() will
057 * throw an exception.<br>
058 * (2) Containment queries ( eg overlaps(), contains() ) also throw exceptions.
059 *<br>
060 * (3) The plus() method will map a location to its positive strand equivalent; use it on both args
061 * before calling, for example the intersection() method,
062 * if your code needs to be indifferent to strand.
063 *<br><br>
064 * Exceptions and how they are (typically) used:
065 *<br>
066 * IllegalArgumentException - the location given as a parameter is not on the same strand as the location.
067 *<br>
068 * IndexOutOfBoundsException - often means the operation caused the location to span the origin, ie
069 * be partially on positive and partially on negative strand.
070 *<br>
071 * @author Hanno Hinsch
072 */
073public class Location implements Iterable<Location>
074{
075        private static final Logger logger = LoggerFactory.getLogger(App.class);
076
077        private int mStart;
078        private int mEnd;
079
080//      /**
081//       */
082//      private Location()
083//      {
084//      }
085
086        /**
087         * Construct new location from coordinates.
088         * See package description of coordinate format.
089         * @param start Origin 0 index of first symbol.
090         * @param end Origin 0 index of last symbol + 1.
091         * @throws IllegalArgumentException End is not after start, or location spans the origin
092         */
093        public Location( int start, int end )
094        {
095                mStart= start;
096                mEnd= end;
097
098                if( !isHealthy() )
099                {
100                        throw new IllegalArgumentException( "Improper location parameters: (" + start + "," + end + ")" );
101                }
102
103        }
104
105        /**
106         * Clone other location.
107         * @param other The location to clone.
108         */
109        public Location( Location other )
110        {
111                mStart= other.mStart;
112                mEnd= other.mEnd;
113
114                assert isHealthy(): toString();
115        }
116
117                public int getBegin(){
118                        if(isNegative())
119                                return mEnd;
120                        else
121                                return mStart;
122                }
123
124                public int getEnd(){
125                        if(isNegative())
126                                return mStart;
127                        else
128                                return mEnd;
129                }
130
131
132        /**
133         * Create location from "biocoordinates", as in GFF file. In biocoordinates,
134         * the start index of a range is represented in origin 1 (ie the very first index is 1, not 0),
135         * and end= start + length - 1.
136         *
137         * @param start Origin 1 index of first symbol.
138         * @param end Origin 1 index of last symbol.
139         * @param strand '+' or '-' or '.' ('.' is interpreted as '+').
140         * @return Created location.
141         * @throws IllegalArgumentException strand must be '+', '-' or '.'
142         */
143        public static Location fromBio( int start, int end, char strand )
144        {
145                int s= start - 1;
146                int e= end;
147
148                if( !( strand == '-' || strand == '+' || strand == '.' ))
149                {
150                        throw new IllegalArgumentException( "Strand must be '+', '-', or '.'" );
151                }
152
153                if( strand == '-' )
154                {
155                        //negate
156                        s= - end;
157                        e= - ( start - 1);
158                }
159
160                return new Location( s, e );
161        }
162
163        /**
164         * Create a location from MAF file coordinates, which represent negative
165         * strand locations as the distance from the end of the sequence.
166         *
167         * @param start Origin 1 index of first symbol.
168         * @param length Number of symbols in range.
169         * @param strand '+' or '-' or '.' ('.' is interpreted as '+').
170         * @param totalLength Total number of symbols in sequence.
171         * @throws IllegalArgumentException Strand must be '+', '-', '.'
172         *
173         */
174         public static Location fromBioExt( int start, int length, char strand, int totalLength )
175         {
176                int s= start;
177                int e= s + length;
178
179                if( !( strand == '-' || strand == '+' || strand == '.' ))
180                {
181                        throw new IllegalArgumentException( "Strand must be '+', '-', or '.'" );
182                }
183
184                if( strand == '-' )
185                {
186                        s= s - totalLength;
187                        e= e - totalLength;
188                }
189
190                return new Location( s, e );
191         }
192
193        /**
194         * Get character representation of strand.
195         *
196         * @return '+' or '-'
197         */
198        public char bioStrand()
199        {
200                return ( isNegative() )?'-':'+';
201        }
202
203        /**
204         * Get start index, in biocoordinates.
205         *
206         * @return The origin 1 index of the first symbol in location.
207         */
208        public int bioStart()
209        {
210                return plus().start() + 1;
211        }
212
213        /**
214         * Get end index, in biocoordinates.
215         *
216         * @return The origin 1 index of the final symbol in location.
217         */
218        public int bioEnd()
219        {
220                return plus().end();
221        }
222
223
224
225        /**
226         * Return location that is in same position on plus strand. If location is already
227         * on plus strand, just return the location unchanged.
228         *
229         * @return Location on plus strand.
230         */
231        public Location plus()
232        {
233                if( isNegative() )
234                {
235                        return opposite();
236                }
237                else
238                {
239                        return this;
240                }
241        }
242
243        /**
244         * Return location that is in same position on negative strand. If location is already
245         * on negative strand, just return the location unchanged.
246         *
247         * @return Location on negative strand.
248         */
249        public Location minus()
250        {
251                if( isNegative() )
252                {
253                        return this;
254                }
255                else
256                {
257                        return opposite();
258                }
259        }
260
261
262        /**
263        *  Return the union.
264        * <br>
265        *
266         * @param other The location to join.
267         * @return The union is a range that starts at the lesser of the two starting indices and ends at the
268         * greater of the two ends.
269         * @throws IllegalArgumentException Locations are on opposite strands.
270        */
271        public Location union( Location other )
272        {
273
274                if( !isSameStrand( other ))
275                {
276                        throw new IllegalArgumentException( "Locations are on opposite strands." );
277                }
278                else
279                {
280                        int start= (other.mStart < mStart)? other.mStart: mStart;
281                        int end= (other.mEnd > mEnd)? other.mEnd: mEnd;
282
283                        return new Location( start, end );
284                }
285
286        }
287
288        /**
289         * Return the intersection, or null if no overlap.
290         *
291         * @param other
292         *            The location to intersect.
293         * @return The maximal location that is contained by both. Returns null if
294         *         no overlap!
295         * @throws IllegalArgumentException
296         *             Locations are on opposite strands.
297         */
298        public Location intersection(Location other) {
299                if (isSameStrand(other)) {
300                        return intersect(mStart, mEnd, other.mStart, other.mEnd);
301                } else {
302                        throw new IllegalArgumentException("Locations are on opposite strands.");
303                }
304        }
305        
306        private Location intersect(int a1, int a2, int b1, int b2) {
307                if (a1 > b1) {
308                        return intersect(b1, b2, a1, a2);
309                }
310                // Safe to assume a1 <= b1
311                if (b1 >= a2) {
312                        // b starts after a ends
313                        return null;
314                } else if (b1 < a2 && b2 <= a2) {
315                        // b starts after a starts and ends before or at where a ends
316                        return new Location(b1, b2);
317                } else if (b1 >= a1 && a2 <= b2) {
318                        // b starts after a but extends after the end of a
319                        return new Location(b1, a2);
320                }
321                return null;
322        }       
323
324
325        /**
326         * Get starting index (origin 0).
327         *
328         * @return The start index.
329         */
330        public int start()
331        {
332                return mStart;
333        }
334
335        /**
336         * Get the ending index.
337         *
338         * @return The index of last symbol + 1 (remember Java half-open coordinates).
339         */
340        public int end()
341        {
342                return mEnd;
343        }
344
345        /**
346         * Get length of range.
347         *
348         * @return The length of the range (end - start).
349         */
350        public int length()
351        {
352                return mEnd - mStart;
353        }
354
355
356        /**
357         * Enable a "sliding window" iteration over a location
358         * to use with Java's "for" loop construct.
359         * The returned helper object implements the Iterable interface; the windowSize and increment semantics are implemented
360         * by an underlying LocIterator.
361         * <br><br>
362         * For example, given a location variable "loc":
363         *<br>
364<pre>
365        //use window size of 3 and increment of +3
366        for( Location temp: loc.window( 3, 3 ))
367        {
368        //at each iteration, temp will be the location of the next 3 symbols
369        }
370</pre>
371         *
372         * @param windowSize The number of symbols to get on each iteration.
373         * @param increment The direction and number of symbols to advance at each iteration.
374         * @return An anonymous iterable object to use with Java's for( ... ) loop construct.
375         */
376        public Iterable<Location> window( final int windowSize, final int increment )
377        {
378                final Location loc= this;
379
380                //return iterable anonymous inner class
381                return new Iterable<Location> ()
382                        {
383                                @Override
384                                public LocIterator iterator()
385                                {
386                                        return new LocIterator( loc, windowSize, increment );
387                                }
388
389                        };
390        }
391
392        /**
393         * Create a location iterator over this location with a window size of 1 and
394         * an increment of +1 (successive symbols from start to end).
395         *
396         * @return An iterator over a Location (a LocIterator object).
397         */
398        @Override
399        public LocIterator iterator()
400        {
401                return new LocIterator( this, 1, 1 );
402        }
403
404        /**
405         * Create a location iterator over this location,
406         * using specified window size and increment.
407         *
408         * @param windowSize The number of symbols to get on each iteration.
409         * @param increment The direction and number of symbols to advance at each iteration.
410         * @return An iterator over a Location (a LocIterator object).
411         */
412        public LocIterator iterator( int windowSize, int increment )
413        {
414                return new LocIterator( this, windowSize, increment );
415        }
416
417
418        /**
419         * The part of this location before the specified position. If position is negative,
420         * count backwards from the end.
421         * <br><br>
422         * For position >= 0, return Location( start, start + position ).
423         * <br>
424         * For position < 0, return Location( start, end + position ).
425         * <br>
426         * @return New location from start of this location to directly before position.
427         * @param position Where the prefix ends.
428         * @throws IndexOutOfBoundsException Specified prefix is longer than location.
429         */
430        public Location prefix( int position )
431        {
432                int end;
433                if( position >= 0 )
434                {
435                        if( (mStart + position <= mEnd) )
436                        {
437                                end= mStart + position;
438                        }
439                        else
440                        {
441                                throw new IndexOutOfBoundsException( "Specified prefix longer than location." );
442                        }
443                }
444                else
445                {
446                        if( (mEnd + position > mStart))
447                        {
448                                end= mEnd + position;
449                        }
450                        else
451                        {
452                                throw new IndexOutOfBoundsException( "Specified prefix longer than location." );
453                        }
454                }
455
456                return new Location( mStart, end );
457        }
458
459
460        /**
461         * The part of this location after the specified position. If position is negative, count backwards
462         * from the end.
463         * <br><br>
464         * For position >= 0, return Location( start + position, end ).
465         * <br>
466         * For position < 0, return Location( end - position, end ).
467         * <br>
468         * @return New location from position to end of this location.
469         * @param position Where the suffix starts.
470         * @throws IndexOutOfBoundsException Specified suffix is longer than location.
471         */
472        public Location suffix( int position )
473        {
474                int start;
475                if( position >= 0 )
476                {
477                        if( mStart + position <= mEnd ) // Scooter willis when 60 + 60 = 120 no remainder
478                        {
479                                start= mStart + position;
480                        }
481                        else
482                        {
483                                throw new IndexOutOfBoundsException( "Specified suffix longer than location." );
484                        }
485                }
486                else
487                {
488                        if( mEnd + position >= mStart )
489                        {
490                                start= mEnd + position;
491                        }
492                        else
493                        {
494                                throw new IndexOutOfBoundsException( "Specified suffix longer than location." );
495                        }
496                }
497
498                return new Location( start, mEnd );
499        }
500
501        /**
502         * The part of this location before the other location (not inclusive).
503         *
504         * @param other The other location.
505         * @return The part of this location before the other location.
506         * @throws IllegalArgumentException Locations are on opposite strands.
507         * @throws IndexOutOfBoundsException This location does not contain other location.
508         */
509        public Location prefix( Location other )
510        {
511
512                if( isSameStrand( other ) )
513                {
514                        if( other.mStart >= mStart )
515                        {
516                                return new Location( mStart, (other.mStart < mEnd)? other.mStart: mEnd );
517                        }
518                        else
519                        {
520                                //other is out of bounds -- no prefix
521                                throw new IndexOutOfBoundsException( "Specified location not within this location." );
522                        }
523                }
524                else
525                {
526                        throw new IllegalArgumentException( "Locations are on opposite strands." );
527                }
528        }
529
530        /**
531         * The part of this location after the other location (not inclusive).
532         *
533         * @param other The other location.
534         * @return The part of this location after the other location.
535         * @throws IllegalArgumentException Locations are on opposite strands.
536         * @throws IndexOutOfBoundsException This location does not contain other location.
537         */
538        public Location suffix( Location other )
539        {
540                if( isSameStrand( other ))
541                {
542                        if( other.mEnd <= mEnd )
543                        {
544                                return new Location( (other.mEnd > mStart)? other.mEnd: mStart, mEnd );
545                        }
546                        else
547                        {
548                                //other is out of bounds -- no suffix
549                                throw new IndexOutOfBoundsException( "Specified location not within this location." );
550                        }
551                }
552                else
553                {
554                        throw new IllegalArgumentException( "Locations are on opposite strands." );
555                }
556
557        }
558
559        /**
560         * Return the adjacent location of specified length directly upstream of this location.
561         *
562         * @return Upstream location.
563         * @param length The length of the upstream location.
564         * @throws IndexOutOfBoundsException Specified length causes crossing of origin.
565         */
566        public Location upstream( int length )
567        {
568                if( length < 0 )
569                {
570                        throw new IllegalArgumentException( "Parameter must be >= 0; is=" + length );
571                }
572
573                if( Math.signum( mStart - length) == Math.signum( mStart ) || 0 == Math.signum( mStart - length ) )
574                {
575                        return new Location(mStart - length, mStart );
576                }
577                else
578                {
579                        throw new IndexOutOfBoundsException( "Specified length causes crossing of origin: " + length + "; " + toString() );
580                }
581        }
582
583        /**
584         * Return the adjacent location of specified length directly downstream of this location.
585         *
586         * @return The downstream location.
587         * @param length The length of the downstream location.
588         * @throws IndexOutOfBoundsException Specified length causes crossing of origin.
589         */
590        public Location downstream( int length )
591        {
592                if( length < 0 )
593                {
594                        throw new IllegalArgumentException( "Parameter must be >= 0; is=" + length );
595                }
596
597                if( Math.signum( mEnd + length) == Math.signum( mEnd ) || 0 == Math.signum( mEnd + length ) )
598                {
599                        return new Location( mEnd, mEnd + length );
600                }
601                else
602                {
603                        throw new IndexOutOfBoundsException( "Specified length causes crossing of origin: " + length + "; " + toString() );
604                }
605
606        }
607
608
609
610        /**
611        *   Return distance between this location and the other location.
612        *
613        *       Distance is defined only if both locations are on same strand.
614         *
615         * @param other The location to compare.
616         * @return The integer distance. Returns -1 if they overlap; 0 if directly adjacent.
617         * @throws IllegalArgumentException Locations are on opposite strands.
618         */
619        public int distance( Location other )
620        {
621                if( isSameStrand( other ))
622                {
623                        if( overlaps( other ))
624                        {
625                                return -1;
626                        }
627                        else
628                        {
629                                return ( mEnd <= other.mStart )? (other.mStart - mEnd) : (mStart - other.mEnd);
630                        }
631                }
632                else
633                {
634                        throw new IllegalArgumentException( "Locations are on opposite strands." );
635                }
636        }
637
638        /**
639         * Return percent overlap of two locations.
640         *
641         * @param other The location to compare.
642         * @return 100.0 * intersection(other).length() / this.length()
643         * @throws IllegalArgumentException Locations are on opposite strands.
644         */
645        public double percentOverlap( Location other )
646        {
647                if( length() > 0 && overlaps( other ))
648                {
649                        return 100.0 * (((double) intersection( other ).length()) / (double) length());
650                }
651                else
652                {
653                        return 0;
654                }
655        }
656
657        /**
658         * Check if this location and other location overlap.
659         *
660         * @param other The location to compare.
661         * @return True if they overlap.
662         * @throws IllegalArgumentException Locations are on opposite strands.
663         */
664        public boolean overlaps( Location other )
665        {
666                if( isSameStrand( other ))
667                {
668                        return !( mStart >= other.mEnd || mEnd <= other.mStart );
669                }
670                else
671                {
672                        throw new IllegalArgumentException( "Locations are on opposite strands." );
673                }
674        }
675
676        /**
677         * Check if this location contains the other.
678         *
679         * @param other The location to compare.
680         * @return True if other is entirely contained by this location.
681         * @throws IllegalArgumentException Locations are on opposite strands.
682         */
683        public boolean contains( Location other )
684        {
685                if( isSameStrand( other ))
686                {
687                        return ( mStart <= other.mStart && mEnd >= other.mEnd );
688                }
689                else
690                {
691                        throw new IllegalArgumentException( "Locations are on opposite strands." );
692                }
693        }
694
695
696        /**
697         * Check if this location starts after the other location starts.
698         * The locations may overlap.
699         *
700         * @param other The location to compare.
701         * @return True if this starts after other.
702         * @throws IllegalArgumentException Locations are on opposite strands.
703         */
704        public boolean startsAfter( Location other )
705        {
706                if( isSameStrand( other ))
707                {
708                        return mStart > other.mStart;
709                }
710                else
711                {
712                        throw new IllegalArgumentException( "Locations are on opposite strands." );
713                }
714        }
715
716        /**
717         * Check if this location starts before other location starts.
718         * The locations may overlap.
719         *
720         * @param other The location to compare.
721         * @return True if this starts before other.
722         * @throws IllegalArgumentException Locations are on opposite strands.
723         */
724        public boolean startsBefore( Location other )
725        {
726                if( isSameStrand( other ))
727                {
728                        return mStart < other.mStart;
729                }
730                else
731                {
732                        throw new IllegalArgumentException( "Locations are on opposite strands." );
733                }
734        }
735
736        /**
737         * Check if this location ends after other location ends.
738         * The locations may overlap.
739         *
740         * @param other The location to compare.
741         * @return True if location ends after other.
742         * @throws IllegalArgumentException Locations are on opposite strands.
743         */
744        public boolean endsAfter( Location other )
745        {
746                if( isSameStrand( other ) )
747                {
748                        return mEnd > other.mEnd;
749                }
750                else
751                {
752                        throw new IllegalArgumentException( "Locations are on opposite strands." );
753                }
754        }
755
756        /**
757         * Check if this location ends before other location ends.
758         * The locations may overlap.
759         *
760         * @param other The location to compare.
761         * @return True if this ends before other.
762         * @throws IllegalArgumentException Locations are on opposite strands.
763         */
764        public boolean endsBefore( Location other )
765        {
766                if( isSameStrand( other ) )
767                {
768                        return mEnd < other.mEnd;
769                }
770                else
771                {
772                        throw new IllegalArgumentException( "Locations are on opposite strands." );
773                }
774        }
775
776        /**
777         * Check if this location is entirely after the other location (no overlap).
778         *
779         * @param other The location to compare.
780         * @return True if this is after other.
781         * @throws IllegalArgumentException Locations are on opposite strands.
782         */
783        public boolean isAfter( Location other )
784        {
785                if( isSameStrand( other ) )
786                {
787                        return mStart >= other.mEnd;
788                }
789                else
790                {
791                        throw new IllegalArgumentException( "Locations are on opposite strands." );
792                }
793        }
794
795        /**
796         * Check if this location is entirely before other location (no overlap).
797         *
798         * @param other The location to compare.
799         * @return True if this is before other.
800         * @throws IllegalArgumentException Locations are on opposite strands.
801         */
802        public boolean isBefore( Location other )
803        {
804                if( isSameStrand( other ) )
805                {
806                        return mEnd <= other.mStart;
807                }
808                else
809                {
810                        throw new IllegalArgumentException( "Locations are on opposite strands." );
811                }
812        }
813
814        /**
815         * Check if location is on negative strand.
816         * Note that Location( 0, 0 ) is by construction defined to be on the
817         * positive strand.
818         *
819         * @return True if on negative (reverse) strand.
820         */
821        public boolean isNegative()
822        {
823                return ( mStart <= 0 && mEnd <= 0 );
824        }
825
826        /**
827         * Return location that is in same position on opposite strand.
828         *
829         * @return Location on opposite strand.
830         */
831        public Location opposite()
832        {
833                return new Location( - mEnd, - mStart );
834        }
835
836        /**
837         * Check if this location is on same strand as other location.
838         *
839         * @param other The location to compare.
840         * @return True if on same strand.
841         */
842        public boolean isSameStrand( Location other )
843        {
844                return ( isNegative() && other.isNegative() ) || ( !isNegative() && !other.isNegative() );
845        }
846
847
848        /**
849         * Return a string representation of location.
850         *
851         * @return Text string.
852         */
853        @Override
854        public String toString()
855        {
856                return new String( "[L=" + (mEnd - mStart) + "; S=" + mStart + "; E=" + mEnd +"]" );
857        }
858
859        /* (non-Javadoc)
860         * @see java.lang.Object#hashCode()
861         */
862        @Override
863        public int hashCode() {
864                final int prime = 31;
865                int result = 1;
866                result = prime * result + mEnd;
867                result = prime * result + mStart;
868                return result;
869        }
870
871        /* (non-Javadoc)
872         * @see java.lang.Object#equals(java.lang.Object)
873         */
874        @Override
875        public boolean equals(Object obj) {
876                if (this == obj)
877                        return true;
878                if (obj == null)
879                        return false;
880                if (getClass() != obj.getClass())
881                        return false;
882                Location other = (Location) obj;
883                if (mEnd != other.mEnd)
884                        return false;
885                if (mStart != other.mStart)
886                        return false;
887                return true;
888        }
889
890        /**
891         *
892         */
893        private boolean isHealthy()
894        {
895                return ( mStart <= mEnd ) && (( mStart <= 0 && mEnd <= 0 ) || (mStart >= 0 && mEnd >= 0));
896        }
897
898        //shorthand for testing
899        static private Location L( int s, int e )
900        {
901           return new Location( s, e );
902        }
903
904        @SuppressWarnings("unused")
905        static private Location R( int s, int e )
906        {
907           return new Location( -e, -s );
908        }
909
910
911        /**
912         * @deprecated
913         */
914        @Deprecated
915        @SuppressWarnings("unused")
916        public static void main(String[] args )
917        throws Exception
918        {
919                Location p3_7= new Location( 3, 7 );
920                Location p16_19= new Location( 16, 19 );
921                Location p15_19= new Location( 15, 19 );
922                Location p15_16= new Location( 15, 16 );
923                Location p10_17= new Location( 10, 17 );
924                Location p10_12= new Location( 10, 12 );
925                Location p14_17= new Location( 14, 17 );
926                Location p14_14= new Location( 14, 14 );
927
928                Location r13_17= new Location( 13, 17 );
929                Location r21_25= new Location( 21, 25 );
930
931                Location r4_7= new Location( 4, 7 );
932                Location r2_5= new Location( 2, 5 );
933                Location r0_3= new Location( 0, 3 );
934                Location r5_8= new Location( 5, 8 );
935
936                //distance
937                assert L(14,14).distance( L(3,7) ) == 7;
938                assert L(3,7).distance( L(14,14) ) == 7;
939                assert L(1,4).distance( L(7, 10) ) == 3;
940
941                //union
942                assert p10_12.union( p14_17 ).equals( p10_17 );
943                assert p14_17.union( p10_12 ).equals( p10_17 );
944                assert p15_19.union( p15_16).equals( p15_19 );
945
946                //intersection
947                assert r13_17.union( r21_25 ).intersection( r21_25 ).equals( new Location( 21, 25 ));
948
949
950                //isBefore
951                assert r2_5.isBefore( r5_8 );
952                assert !r2_5.isBefore( r4_7 );
953
954                //isAfter
955                assert r5_8.isAfter( r2_5 );
956                assert !r5_8.isAfter( r4_7 );
957
958                //contains
959                assert p15_19.contains( p16_19 );
960
961                //overlaps
962                assert r2_5.overlaps( r4_7 );
963                assert r2_5.overlaps( r0_3 );
964                assert !r5_8.overlaps( r2_5 );
965                assert !r2_5.overlaps( r5_8 );
966
967
968                //prefix
969                assert L(2,20).prefix(1).equals( L(2,3));
970                assert L(2,20).prefix(-1).equals( L(2,19));
971                assert L(2,20).prefix( L(10,12)).equals( L(2,10));
972
973                //suffix
974                assert L(2,20).suffix(1).equals( L(3,20));
975                assert L(2,20).suffix(-1).equals( L(19,20));
976                assert L(2,20).suffix( L(10,12)).equals( L(12,20));
977
978
979                //upstream
980                //downstream
981
982                //startsBefore
983                //startsAfter
984                //endsBefore
985                //endsAfter
986
987                //equals
988
989                //percentoverlap
990
991
992                //plus
993                //minus
994                //isNegative
995                //oppposite
996
997                //fromBio, etc.
998
999                logger.info("JavaGene.Location Passed.");
1000        }
1001
1002}