@Override
    public List<SearchResult<SequenceMatcher>> searchForwards(final WindowReader reader, 
            final long fromPosition, final long toPosition) throws IOException {
        // Initialise:
        final int longestMatchEndPosition = sequences.getMaximumLength() - 1;
        long searchPosition = fromPosition > 0?
                              fromPosition : 0;
        
        // While there is data to search in:
        Window window;
        while (searchPosition <= toPosition &&
               (window = reader.getWindow(searchPosition)) != null) {
            
            // Does the sequence fit into the searchable bytes of this window?
            // It may not if the start position of the window is already close
            // to the end of the window, or the sequence is long (potentially
            // could be longer than any single window - but mostly won't be):
            final long windowStartPosition = window.getWindowPosition();
            final int windowLength = window.length();
            final int arrayStartPosition = reader.getWindowOffset(searchPosition);  
            final int arrayLastPosition = windowLength - 1;             
            if (arrayStartPosition + longestMatchEndPosition <= arrayLastPosition) {

                // Find the last point in the array where the sequence still fits
                // inside the array, or the toPosition if it is smaller.
                final int lastMatchingPosition = arrayLastPosition - longestMatchEndPosition;
                final long distanceToEnd = toPosition - windowStartPosition;                
                final int arrayMaxPosition = distanceToEnd < lastMatchingPosition?
                                       (int) distanceToEnd : lastMatchingPosition; 
                        
                // Search forwards in the byte array of the window:
                final List<SearchResult<SequenceMatcher>> arrayResult = 
                        searchForwards(window.getArray(), arrayStartPosition, arrayMaxPosition);
                // Did we find a match?
                if (!arrayResult.isEmpty()) {
                    final long readerOffset = searchPosition - arrayStartPosition;
                    return SearchUtils.addPositionToResults(arrayResult, readerOffset);
                }
                
                // Continue the search one on from where we last looked:
                searchPosition += (arrayMaxPosition - arrayStartPosition + 1);

                // Did we pass the final toPosition?  In which case, we're finished.
                if (searchPosition > toPosition) {
                    return SearchUtils.noResults();
                }
            }

            // From the current search position, the sequence could cross over in to
            // the next window, so we can't search directly in the window byte array.
            // We must use the reader interface on the sequence to let it match
            // over more bytes than this window potentially has available.
            
            // Search up to the last position in the window, or the toPosition,
            // whichever comes first:
            final long lastWindowPosition = windowStartPosition + arrayLastPosition;
            final long lastSearchPosition = toPosition < lastWindowPosition?
                                            toPosition : lastWindowPosition;
            final List<SearchResult<SequenceMatcher>> readerResult = 
                    doSearchForwards(reader, searchPosition, lastSearchPosition);
            
            // Did we find a match?
            if (!readerResult.isEmpty()) {
                return readerResult;
            }
            
            // Continue the search one on from where we last looked:
            searchPosition = lastSearchPosition + 1;
        }
        
        return SearchUtils.noResults();
    }