]> git.donarmstrong.com Git - biopieces.git/blobdiff - code_perl/Maasha/KISS.pm
shifted to NC list in BGB
[biopieces.git] / code_perl / Maasha / KISS.pm
index a2b3f152f6071b9992102bdb32be972c1f3ddd13..7cfdbcd86ba62a8cc5648f90b53e387595d8943c 100644 (file)
@@ -35,7 +35,9 @@ use strict;
 use Data::Dumper;
 use Maasha::Common;
 use Maasha::Filesys;
+use Maasha::NClist;
 use Maasha::Align;
+
 use vars qw( @ISA @EXPORT );
 
 @ISA = qw( Exporter );
@@ -56,6 +58,14 @@ use constant {
     INDEX_BLOCK_SIZE => 100,
     INDEX_LEVEL      => 100_000_000,
     INDEX_FACTOR     => 100,
+
+    BUCKET_SIZE      => 100,
+    COUNT            => 0,
+    OFFSET           => 1,
+
+    INDEX_BEG        => 1,
+    INDEX_END        => 2,
+    INDEX            => 12,
 };
 
 
@@ -73,7 +83,7 @@ sub kiss_entry_get
 
     # Returns a hashref.
 
-    my ( $line, @fields, %entry );
+    my ( $line, @fields );
 
     while ( $line = <$fh> )
     {
@@ -85,20 +95,7 @@ sub kiss_entry_get
 
         Maasha::Common::error( qq(BAD kiss entry: $line) ) if not @fields == 12;
         
-        $entry{ 'S_ID' }        = $fields[ S_ID ];
-        $entry{ 'S_BEG' }       = $fields[ S_BEG ];
-        $entry{ 'S_END' }       = $fields[ S_END ];
-        $entry{ 'Q_ID' }        = $fields[ Q_ID ];
-        $entry{ 'SCORE' }       = $fields[ SCORE ];
-        $entry{ 'STRAND' }      = $fields[ STRAND ];
-        $entry{ 'HITS' }        = $fields[ HITS ];
-        $entry{ 'ALIGN' }       = $fields[ ALIGN ];
-        $entry{ 'BLOCK_COUNT' } = $fields[ BLOCK_COUNT ];
-        $entry{ 'BLOCK_BEGS' }  = $fields[ BLOCK_BEGS ];
-        $entry{ 'BLOCK_LENS' }  = $fields[ BLOCK_LENS ];
-        $entry{ 'BLOCK_TYPE' }  = $fields[ BLOCK_TYPE ];
-
-        return wantarray ? %entry : \%entry;
+        return wantarray ? @fields : \@fields;
     }
 }
 
@@ -107,6 +104,8 @@ sub kiss_entry_parse
 {
     # Martin A. Hansen, December 2009.
 
+    # TODO find out what uses this and kill it!
+
     # Parses a line with a KISS entry.
 
     my ( $line,   #  KISS line to parse
@@ -150,32 +149,29 @@ sub kiss_entry_put
 
     # Returns nothing.
     
-    my ( @fields );
+    Maasha::Common::error( qq(BAD kiss entry) ) if not scalar @{ $entry } == 12;
 
-    if ( defined $entry->{ 'S_ID' }  and 
-         defined $entry->{ 'S_BEG' } and
-         defined $entry->{ 'S_END' }
+    if ( defined $entry->[ S_ID ]  and 
+         defined $entry->[ S_BEG ] and
+         defined $entry->[ S_END ]
        )
     {
-        Maasha::Common::error( qq(Bad S_BEG value: $entry->{ 'S_BEG' } < 0 ) ) if $entry->{ 'S_BEG' } < 0;
-        Maasha::Common::error( qq(Bad S_END value: $entry->{ 'S_END' } < $entry->{ 'S_BEG' }) ) if $entry->{ 'S_END' } < $entry->{ 'S_BEG' };
+        Maasha::Common::error( qq(Bad S_BEG value: $entry->[ S_BEG ] < 0 ) ) if $entry->[ S_BEG ] < 0;
+        Maasha::Common::error( qq(Bad S_END value: $entry->[ S_END ] < $entry->[ S_BEG ] ) ) if $entry->[ S_END ] < $entry->[ S_BEG ];
 
         $fh ||= \*STDOUT;
     
-        $fields[ S_ID ]        = $entry->{ 'S_ID' };
-        $fields[ S_BEG ]       = $entry->{ 'S_BEG' };
-        $fields[ S_END ]       = $entry->{ 'S_END' };
-        $fields[ Q_ID ]        = $entry->{ 'Q_ID' }        || ".";
-        $fields[ SCORE ]       = $entry->{ 'SCORE' }       || ".";
-        $fields[ STRAND ]      = $entry->{ 'STRAND' }      || ".";
-        $fields[ HITS ]        = $entry->{ 'HITS' }        || ".";
-        $fields[ ALIGN ]       = $entry->{ 'ALIGN' }       || ".";
-        $fields[ BLOCK_COUNT ] = $entry->{ 'BLOCK_COUNT' } || ".";
-        $fields[ BLOCK_BEGS ]  = $entry->{ 'BLOCK_BEGS' }  || ".";
-        $fields[ BLOCK_LENS ]  = $entry->{ 'BLOCK_LENS' }  || ".";
-        $fields[ BLOCK_TYPE ]  = $entry->{ 'BLOCK_TYPE' }  || ".";
-
-        print $fh join( "\t", @fields ), "\n";
+        $entry->[ Q_ID ]        = "." if not defined $entry->[ Q_ID ];
+        $entry->[ SCORE ]       = "." if not defined $entry->[ SCORE ];
+        $entry->[ STRAND ]      = "." if not defined $entry->[ STRAND ];
+        $entry->[ HITS ]        = "." if not defined $entry->[ HITS ];
+        $entry->[ ALIGN ]       = "." if not defined $entry->[ ALIGN ];
+        $entry->[ BLOCK_COUNT ] = "." if not defined $entry->[ BLOCK_COUNT ];
+        $entry->[ BLOCK_BEGS ]  = "." if not defined $entry->[ BLOCK_BEGS ];
+        $entry->[ BLOCK_LENS ]  = "." if not defined $entry->[ BLOCK_LENS ];
+        $entry->[ BLOCK_TYPE ]  = "." if not defined $entry->[ BLOCK_TYPE ];
+
+        print $fh join( "\t", @{ $entry } ), "\n";
     }
 }
 
@@ -191,126 +187,116 @@ sub kiss_sort
 
     # Returns nothing.
 
-    `sort -k 2,2n -k 3,3n $file > $file.sort`;
+    `sort -k 2,2n -k 3,3nr $file > $file.sort`;
 
     rename "$file.sort", $file;
 }
 
 
-sub kiss_index
+sub kiss_index_old
 {
-    # Martin A, Hansen, November 2009.
+    # Martin A. Hansen, December 2009.
 
-    # Creates an index of a sorted KISS file.
+    # Creates a lookup index of a sorted KISS file.
 
-    my ( $file,   # KISS file
+    my ( $file,         # path to KISS file
        ) = @_;
 
-    # Returns a hashref.
-
-    my ( $tree, $offset, $fh, $line, $beg );
+    # Returns nothing.
 
-    $tree   = {};
-    $offset = 0;
+    my ( $fh, $offset, $line, $s_beg, $bucket, $index );
 
     $fh = Maasha::Filesys::file_read_open( $file );
 
+    $offset = 0;
+
     while ( $line = <$fh> )
     {
-        ( undef, $beg ) = split "\t", $line, 3;
+        ( undef, $s_beg ) = split "\t", $line, 3;
+
+        $bucket = int( $s_beg / BUCKET_SIZE ); 
+
+        $index->[ $bucket ]->[ COUNT ]++;
+        $index->[ $bucket ]->[ OFFSET ] = $offset if not defined $index->[ $bucket ]->[ OFFSET ];
 
-        kiss_index_node_add( $tree, INDEX_LEVEL, INDEX_FACTOR, $beg, $offset );
-                
         $offset += length $line;
     }
 
     close $fh;
 
-    kiss_index_store( "$file.index", $tree );
+    Maasha::KISS::kiss_index_store( "$file.index", $index );
 }
 
 
-sub kiss_index_node_add
+sub kiss_index
 {
-    # Martin A, Hansen, November 2009.
+    # Martin A. Hansen, February 2010.
 
-    # Recursive routine to add nodes to a tree.
+    # Creates a NC list index of a sorted KISS file.
 
-    my ( $node,
-         $level,
-         $factor,
-         $beg,
-         $offset,
-         $sum,
+    my ( $file,         # path to KISS file
        ) = @_;
-       
-    my ( $bucket );
-    
-    $sum  ||= 0;
-    $bucket = int( $beg / $level );
-    
-    if ( $level >= $factor )
+
+    # Returns nothing.
+
+    my ( $fh, $line, @fields, $nc_list );
+
+    $fh = Maasha::Filesys::file_read_open( $file );
+
+    while ( $line = <$fh> )
     {
-        $sum += $bucket * $level;
-        $beg -= $bucket * $level;
-        
-        $node->{ 'CHILDREN' }->[ $bucket ]->{ 'COUNT' }++; 
-        # $node->{ 'CHILDREN' }->[ $bucket ]->{ 'LEVEL' }  = $level;
-        # $node->{ 'CHILDREN' }->[ $bucket ]->{ 'BUCKET' } = $bucket;
-        $node->{ 'CHILDREN' }->[ $bucket ]->{ 'BEG' }    = $sum;
-        $node->{ 'CHILDREN' }->[ $bucket ]->{ 'END' }    = $sum + $level - 1;
-        $node->{ 'CHILDREN' }->[ $bucket ]->{ 'OFFSET' } = $offset if not defined $node->{ 'CHILDREN' }->[ $bucket ]->{ 'OFFSET' };
-        
-        kiss_index_node_add( $node->{ 'CHILDREN' }->[ $bucket ], $level / $factor, $factor, $beg, $offset, $sum );
-    }   
+        chomp $line;
+
+        @fields = split "\t", $line;
+
+        if ( not defined $nc_list ) {
+            $nc_list = [ [ @fields ] ];
+        } else {
+            Maasha::NClist::nc_list_add( $nc_list, [ @fields ], INDEX_END, INDEX );
+        }
+    }
+
+    close $fh;
+
+    Maasha::NClist::nc_list_store( $nc_list, "$file.json" );
 }
 
 
 sub kiss_index_offset
 {
-    # Martin A. Hansen, November 2009.
+    # Martin A. Hansen, December 2009.
 
     # Given a KISS index and a begin position,
     # locate the offset closest to the begin position,
     # and return this.
-    
+
     my ( $index,    # KISS index
          $beg,      # begin position
-         $level,    # index level  - OPTIONAL
-         $factor,   # index factor - OPTIONAL
        ) = @_;
 
     # Returns a number.
 
-    my ( $child, $offset );
+    my ( $bucket );
 
-    $level  ||= INDEX_LEVEL;
-    $factor ||= INDEX_FACTOR;
+    Maasha::Common::error( qq(Negative begin position: "$beg") ) if $beg < 0;
 
-    foreach $child ( @{ $index->{ 'CHILDREN' } } )
-    {
-        next if not defined $child;
+    $bucket = int( $beg / BUCKET_SIZE ); 
 
-        if ( $child->{ 'BEG' } <= $beg and $beg <= $child->{ 'END' } )
-        {
-            if ( $level == $factor ) {
-                $offset = $child->{ 'OFFSET' };
-            } else {
-                $offset = kiss_index_offset( $child, $beg, $level / $factor, $factor );
-            }
-        }
-    }
+    $bucket = scalar @{ $index } if $bucket > scalar @{ $index };
 
-    # Maasha::Common::error( "No offset" ) if not defined $offset;   # FIXME
+    while ( $bucket >= 0 )
+    {
+        return $index->[ $bucket ]->[ OFFSET ] if defined $index->[ $bucket ];
 
-    return $offset;
+        $bucket--;
+    }
 }
 
 
 sub kiss_index_count
 {
-    # Martin A. Hansen, November 2009.
-    
+    # Martin A. Hansen, December 2009.
+
     # Given a KISS index and a begin/end interval
     # sum the number of counts in that interval,
     # and return this.
@@ -318,171 +304,163 @@ sub kiss_index_count
     my ( $index,   # KISS index
          $beg,     # Begin position
          $end,     # End position
-         $level,   # index level  - OPTIONAL
-         $factor,  # index factor - OPTIONAL
        ) = @_;
 
     # Returns a number.
-    
-    my ( $count, $child );
 
-    $level  ||= INDEX_LEVEL;
-    $factor ||= INDEX_FACTOR;
-    $count  ||= 0;
+    my ( $count );
 
-    foreach $child ( @{ $index->{ 'CHILDREN' } } )
-    {
-        next if not defined $child;
+    Maasha::Common::error( qq(Negative begin position: "$beg") ) if $beg < 0;
 
-        if ( $level >= $factor )
-        {
-            if ( Maasha::Calc::overlap( $beg, $end, $child->{ 'BEG' }, $child->{ 'END' } ) )
-            {
-                $count += $child->{ 'COUNT' } if $level == $factor;
-                $count += kiss_index_count( $child, $beg, $end, $level / $factor, $factor );
-            }
-        }
-    }
+    $count = Maasha::NClist::nc_list_count_interval( $index, $beg, $end, INDEX_BEG, INDEX_END, INDEX );
 
     return $count;
 }
 
 
-sub kiss_index_store
+sub kiss_index_get_entries
 {
     # Martin A. Hansen, November 2009.
 
-    # Stores a KISS index to a file.
+    # Given a path to a KISS file and a KISS index
+    # along with a beg/end interval, locate all entries
+    # in that interval and return those.
 
-    my ( $path,    # path to KISS index
-         $index,   # KISS index
+    my ( $index,   # KISS index
+         $beg,     # interval begin
+         $end,     # interval end
        ) = @_;
 
-    # Returns nothing.
+    # Returns a list.
 
-    Maasha::Filesys::file_store( $path, $index );
+    my ( $features );
+
+    $features = Maasha::NClist::nc_list_get_interval( $index, $beg, $end, INDEX_BEG, INDEX_END, INDEX );
+
+    return wantarray ? @{ $features } : $features;
 }
 
 
-sub kiss_index_retrieve
+sub kiss_index_get_blocks
 {
-    # Martin A. Hansen, November 2009.
+    # Martin A. Hansen, December 2009.
 
-    # Retrieves a KISS index from a file.
+    # Given a KISS index returns blocks from this in a 
+    # given position interval. Blocks consisting of
+    # hashes of BEG/END/COUNT.
 
-    my ( $path,   # Path to KISS index
+    my ( $index,   # KISS index node
+         $beg,     # interval begin
+         $end,     # interval end
        ) = @_;
 
-    # Returns a data structure.
+    # Returns a list.
 
-    my ( $index );
+    my ( $bucket_beg, $bucket_end, $i, @blocks );
 
-    $index = Maasha::Filesys::file_retrieve( $path );
+    Maasha::Common::error( qq(Negative begin position: "$beg") ) if $beg < 0;
 
-    return wantarray ? @{ $index } : $index;
+    $bucket_beg = int( $beg / BUCKET_SIZE ); 
+    $bucket_end = int( $end / BUCKET_SIZE ); 
+
+    $bucket_end = scalar @{ $index } if $bucket_end > scalar @{ $index };
+
+    for ( $i = $bucket_beg; $i <= $bucket_end; $i++ )
+    {
+        if ( defined $index->[ $i ] )
+        {
+            push @blocks, {
+                BEG   => BUCKET_SIZE * $i,
+                END   => BUCKET_SIZE * ( $i + 1 ),
+                COUNT => $index->[ $i ]->[ COUNT ],
+            };
+        }
+    }
+
+    return wantarray ? @blocks : \@blocks;
 }
 
 
-sub kiss_index_get_entries
+sub kiss_intersect
 {
-    # Martin A. Hansen, November 2009.
+    # Martin A. Hansen, December 2009.
 
-    # Given a path to a KISS file and a KISS index
-    # along with a beg/end interval, locate all entries
-    # in that interval and return those.
+    # Given filehandles to two different unsorted KISS files
+    # intersect the entries so that all entries from file1 that
+    # overlap entries in file2 are returned - unless the inverse flag
+    # is given in which case entreis from file1 that does not
+    # overlap any entries in file2 are returned.
 
-    my ( $file,    # path to KISS file
-         $index,   # KISS index
-         $beg,     # interval begin
-         $end,     # interval end
+    my ( $fh1,       # filehandle to file1
+         $fh2,       # filehandle to file2
+         $inverse,   # flag indicating inverse matching - OPTIONAL
        ) = @_;
 
-    # Returns a list.
+    # Returns a list
 
-    my ( $offset, $fh, $entry, @entries );
+    my ( $entry, %lookup, $pos, $overlap, @entries );
 
-    $offset = kiss_index_offset( $index, $beg );
+    while ( $entry = kiss_entry_get( $fh2 ) ) {
+        map { $lookup{ $_ } = 1 } ( $entry->[ S_BEG ] .. $entry->[ S_END ] );
+    }
 
-    $fh = Maasha::Filesys::file_read_open( $file );
+    while ( $entry = kiss_entry_get( $fh1 ) )
+    {
+        $overlap = 0;
 
-    sysseek( $fh, $offset, 0 );
+        foreach $pos ( $entry->[ S_BEG ] .. $entry->[ S_END ] )
+        {
+            if ( exists $lookup{ $pos } )
+            {
+                $overlap = 1;
 
-    while ( $entry = kiss_entry_get( $fh ) )
-    {
-        push @entries, $entry if $entry->{ 'S_END' } > $beg;
+                last;
+            }
+        }
 
-        last if $entry->{ 'S_BEG' } > $end;
+        if ( $overlap and not $inverse ) {
+            push @entries, $entry;
+        } elsif ( not $overlap and $inverse ) {
+            push @entries, $entry;
+        }
     }
 
-    close $fh;
-
     return wantarray ? @entries : \@entries;
 }
 
 
-sub kiss_index_get_blocks
+sub kiss_index_store
 {
     # Martin A. Hansen, November 2009.
 
-    # Given a KISS index recursively traverse
-    # this into the appropriate node size determined
-    # by the size of the given beg/end interval.
-    # Blocks consisting of hashes of BEG/END/COUNT 
-    # are returned from the requested node size.
+    # Stores a KISS index to a file.
 
-    my ( $index,   # KISS index node
-         $beg,     # interval begin
-         $end,     # interval end
-         $level,   # index level  - OPTIONAL
-         $factor,  # index factor - OPTIONAL
-         $size,    # requested node size
+    my ( $path,    # path to KISS index
+         $index,   # KISS index
        ) = @_;
 
-    # Returns a list.
-    
-    my ( $len, @blocks, $child );
-
-    $level  ||= INDEX_LEVEL;
-    $factor ||= INDEX_FACTOR;
-
-    $size ||= 100;   # TODO: lazy list loading?
-
-#    if ( not defined $size )
-#    {
-#        $len = $end - $beg + 1;
-#    
-#        if ( $len > 100_000_000 ) {
-#            $size = 1_000_000;
-#        } elsif ( $len > 1_000_000 ) {
-#            $size = 10_000;
-#        } else {
-#            $size = 100;
-#        }
-#    }
-
-    if ( $level >= $size )
-    {
-        foreach $child ( @{ $index->{ 'CHILDREN' } } )
-        {
-            next if not defined $child;
+    # Returns nothing.
 
-            if ( Maasha::Calc::overlap( $beg, $end, $child->{ 'BEG' }, $child->{ 'END' } ) )
-            {
-                if ( $level == $size )
-                {
-                    push @blocks, {
-                        BEG   => $child->{ 'BEG' },
-                        END   => $child->{ 'END' },
-                        COUNT => $child->{ 'COUNT' },
-                    };
-                }
-
-                push @blocks, kiss_index_get_blocks( $child, $beg, $end, $level / $factor, $factor, $size );
-            }
-        }
-    }
+    Maasha::Filesys::file_store( $path, $index );
+}
 
-    return wantarray ? @blocks : \@blocks;
+
+sub kiss_index_retrieve
+{
+    # Martin A. Hansen, November 2009.
+
+    # Retrieves a KISS index from a file.
+
+    my ( $path,   # Path to KISS index
+       ) = @_;
+
+    # Returns a data structure.
+
+    my ( $index );
+
+    $index = Maasha::NClist::nc_list_retrieve( $path );
+
+    return wantarray ? @{ $index } : $index;
 }
 
 
@@ -502,7 +480,11 @@ sub kiss_align_enc
 
     my ( $i, $s, $q, @align );
 
-    Maasha::Common::error( "Sequence lengths don't match" ) if length ${ $s_seq } != length ${ $q_seq };
+    # Maasha::Common::error( "Sequence lengths don't match" ) if length ${ $s_seq } != length ${ $q_seq };
+
+    if ( length ${ $s_seq } != length ${ $q_seq } ) {   # for unknown reasons this situation may occur - TODO FIXME
+        return wantarray ? () : [];
+    }
 
     $offset ||= 0;
 
@@ -591,14 +573,30 @@ sub kiss2biopiece
     # Martin A. Hansen, November 2009.
 
     # Converts a KISS entry to a Biopiece record.
-    # TODO: Consistency checking
 
     my ( $entry,   # KISS entry
        ) = @_;
 
     # Returns a hashref
 
-    return wantarray ? %{ $entry } : $entry;
+    my ( %record );
+
+    Maasha::Common::error( qq(BAD kiss entry) ) if not scalar @{ $entry } == 12;
+
+    $record{ 'S_ID' }        = $entry->[ S_ID ];
+    $record{ 'S_BEG' }       = $entry->[ S_BEG ];
+    $record{ 'S_END' }       = $entry->[ S_END ];
+    $record{ 'Q_ID' }        = $entry->[ Q_ID ];
+    $record{ 'SCORE' }       = $entry->[ SCORE ];
+    $record{ 'STRAND' }      = $entry->[ STRAND ];
+    $record{ 'HITS' }        = $entry->[ HITS ];
+    $record{ 'ALIGN' }       = $entry->[ ALIGN ];
+    $record{ 'BLOCK_COUNT' } = $entry->[ BLOCK_COUNT ];
+    $record{ 'BLOCK_BEGS' }  = $entry->[ BLOCK_BEGS ];
+    $record{ 'BLOCK_LENS' }  = $entry->[ BLOCK_LENS ];
+    $record{ 'BLOCK_TYPE' }  = $entry->[ BLOCK_TYPE ];
+
+    return wantarray ? %record : \%record;
 }
 
 
@@ -613,15 +611,29 @@ sub biopiece2kiss
 
     # Returns a hashref
 
-    $record->{ 'SCORE' }       ||= $record->{ 'E_VAL' } || ".";
-    $record->{ 'HITS' }        ||= ".";
-    $record->{ 'BLOCK_COUNT' } ||= ".";
-    $record->{ 'BLOCK_BEGS' }  ||= ".";
-    $record->{ 'BLOCK_LENS' }  ||= ".";
-    $record->{ 'BLOCK_TYPE' }  ||= ".";
-    $record->{ 'ALIGN' }       ||= $record->{ 'DESCRIPTOR' } || ".";
+    my ( $entry );
+
+    if ( not defined $record->{ 'S_ID' }  and
+         not defined $record->{ 'S_BEG' } and
+         not defined $record->{ 'S_END' } )
+    {
+        return undef;
+    }
 
-    return wantarray ? %{ $record } : $record;
+    $entry->[ S_ID ]        = $record->{ 'S_ID' };
+    $entry->[ S_BEG ]       = $record->{ 'S_BEG' };
+    $entry->[ S_END ]       = $record->{ 'S_END' };
+    $entry->[ Q_ID ]        = $record->{ 'Q_ID' }        || ".";
+    $entry->[ SCORE ]       = $record->{ 'SCORE' }       || $record->{ 'E_VAL' } || ".";
+    $entry->[ STRAND ]      = $record->{ 'STRAND' }      || ".";
+    $entry->[ HITS ]        = $record->{ 'HITS' }        || ".";
+    $entry->[ ALIGN ]       = $record->{ 'ALIGN' }       || $record->{ 'DESCRIPTOR' } || ".";
+    $entry->[ BLOCK_COUNT ] = $record->{ 'BLOCK_COUNT' } || ".";
+    $entry->[ BLOCK_BEGS ]  = $record->{ 'BLOCK_BEGS' }  || ".";
+    $entry->[ BLOCK_LENS ]  = $record->{ 'BLOCK_LENS' }  || ".";
+    $entry->[ BLOCK_TYPE ]  = $record->{ 'BLOCK_TYPE' }  || ".";
+
+    return wantarray ? @{ $entry } : $entry;
 }