This function is the actual series of iterations that a BWT creation will perform.  It's separate so we can build a function
    for resuming construction if we fail for some reason.
    TODO: build a recovery function to start midway through BWT construction.
    TODO: @param values need explanation

def iterateCreateFromSeqs(startingColumn, fmStarts, fmDeltas, allFirstCounts, allBwtCounts, cOffset, totalCounts, numValidChars,
                          mergedFN, seqFNPrefix, offsetFN, insertFNs, numProcs, areUniform, depth, logger):
    '''
    This function is the actual series of iterations that a BWT creation will perform.  It's separate so we can build a function
    for resuming construction if we fail for some reason.
    TODO: build a recovery function to start midway through BWT construction.
    TODO: @param values need explanation
    '''
    bwt = np.load(mergedFN, 'r+')
    
    column = startingColumn
    newInserts = True
    while newInserts:
        st = time.time()
        
        #iterate through the sorted keys
        keySort = sorted(fmStarts.keys())
        for i, key in enumerate(keySort):
            #all deltas get copied
            for c2 in xrange(0, numValidChars):
                #copy only to the ones after key
                allFirstCounts[int(key[0])] += fmDeltas[key][c2]
                allBwtCounts[c2] += fmDeltas[key][c2]
                for key2 in keySort[i+1:]:
                    fmStarts[key2][c2] += fmDeltas[key][c2]
                    
                    if key[0] == key2[0]:
                        cOffset[key2] += fmDeltas[key][c2]
                    
                fmDeltas[key][c2] = 0
        
        #blank out the next insertions and make sure we set up the keys we already know about
        nextInsertFNs = {}
        for key in keySort:
            nextInsertFNs[key] = []
        
        #default to having no new insertions
        newInserts = False
        
        #generate tuples of data packets for processing
        tups = []
        for key in keySort:
            prevIterFN = mergedFN+'.'+key+'.'+str(column-1)+'.npy'
            cOff = cOffset[key]
            tup = (key, seqFNPrefix, offsetFN, mergedFN, prevIterFN, column, copy.deepcopy(fmStarts[key]), insertFNs[key], cOff, areUniform)
            tups.append(tup)
        
        if numProcs > 1:
            #TODO: chunksize?
            #create a pool of processes based on the input
            myPool = multiprocessing.Pool(numProcs)
            rets = myPool.imap(bwtPartialInsertPoolCall, tups, 1)
        else:
            rets = []
            for tup in tups:
                rets.append(bwtPartialInsertPoolCall(tup))
        
        for i, ret in enumerate(rets):
            (retFmDelta, retInsertFNs, retShape) = ret
            key = keySort[i]
            
            for c in xrange(1, numValidChars):
                if retInsertFNs[c] == None:
                    continue
                
                nextKey = (str(c)+key)[0:depth]
                if not fmStarts.has_key(nextKey):
                    fmDeltas[nextKey] = [0]*numValidChars
                        
                    keyInd = bisect.bisect(keySort, nextKey)
                    if keyInd < len(keySort):
                        fmStarts[nextKey] = copy.deepcopy(fmStarts[keySort[keyInd]])
                    else:
                        fmStarts[nextKey] = copy.deepcopy(allBwtCounts)
                    
                    if keyInd == len(keySort) or keySort[keyInd][0] != nextKey[0]:
                        cOffset[nextKey] = allFirstCounts[int(nextKey[0])]
                    else:
                        cOffset[nextKey] = cOffset[keySort[keyInd]]
                    
                    np.lib.format.open_memmap(mergedFN+'.'+nextKey+'.'+str(column)+'.npy', 'w+', '<u8,<u1,<u4', (0,))
                
                for c2 in xrange(0, numValidChars):
                    fmDeltas[nextKey][c2] += retFmDelta[c][c2]
                
                if not nextInsertFNs.has_key(nextKey):
                    nextInsertFNs[nextKey] = []
                
                if retInsertFNs[c] != None:
                    nextInsertFNs[nextKey].append(retInsertFNs[c])
                    newInserts = True
            
            #None means we didn't change anything
            if retShape != None:
                totalCounts[key] = retShape
        
        if numProcs > 1:
            myPool.terminate()
            myPool.join()
            myPool = None
        
        #at this point, we know everything is over, so we can clean up the previous step
        for key in keySort:
            prevIterFN = mergedFN+'.'+key+'.'+str(column-1)+'.npy'
            try:
                os.remove(prevIterFN)
            except:
                pass
            for fn in insertFNs[key]:
                try:
                    os.remove(fn)
                except:
                    pass
        
        insertFNs = nextInsertFNs
            
        #copy inserts and move to the next column
        column += 1
        
        et = time.time()
        logger.info('Finished iteration '+str(column-2)+' in '+str(et-st)+' seconds...')
            
    
    logger.info('Creating final output...')
    
    ei = 0
    sortedKeys = sorted(totalCounts.keys())
    for key in sortedKeys:
        copyArr = np.load(mergedFN+'.'+key+'.'+str(column-1)+'.npy', 'r')
        si = ei
        ei += totalCounts[key]
        bwt[si:ei] = copyArr[:]
        
    for key in sortedKeys:
        os.remove(mergedFN+'.'+key+'.'+str(column-1)+'.npy')