--- ./scripts/maf-convert	2015-05-11 19:14:38.000000000 -0400
+++ ./scripts/maf-convert	2016-02-27 21:23:52.000000000 -0500
@@ -1,4 +1,5 @@
 #! /usr/bin/env python
+from __future__ import print_function
 # Copyright 2010, 2011, 2013, 2014 Martin C. Frith
 # Read MAF-format alignments: write them in other formats.
 # Seems to work with Python 2.x, x>=4
@@ -10,12 +11,12 @@
 import sys, os, fileinput, math, operator, optparse, signal, string
 
 def maxlen(s):
-    return max(map(len, s))
+    return max(list(map(len, s)))
 
 def joined(things, delimiter):
     return delimiter.join(map(str, things))
 
-identityTranslation = string.maketrans("", "")
+identityTranslation = None
 def deleted(myString, deleteChars):
     return myString.translate(identityTranslation, deleteChars)
 
@@ -28,12 +29,12 @@
         self.namesAndValues = dict(i.split("=") for i in aLine[1:])
 
         if not sLines: raise Exception("empty alignment")
-        cols = zip(*sLines)
+        cols = list(zip(*sLines))
         self.seqNames = cols[1]
-        self.alnStarts = map(int, cols[2])
-        self.alnSizes = map(int, cols[3])
+        self.alnStarts = list(map(int, cols[2]))
+        self.alnSizes = list(map(int, cols[3]))
         self.strands = cols[4]
-        self.seqSizes = map(int, cols[5])
+        self.seqSizes = list(map(int, cols[5]))
         self.alnStrings = cols[6]
 
         self.qLines = qLines
@@ -56,7 +57,7 @@
     else: return 1
 
 def mafLetterSizes(maf):
-    return map(coordinatesPerLetter, maf.alnStrings, maf.alnSizes)
+    return list(map(coordinatesPerLetter, maf.alnStrings, maf.alnSizes))
 
 def alnLetterSizes(sLines):
     return [coordinatesPerLetter(i[6], int(i[3])) for i in sLines]
@@ -96,9 +97,9 @@
             sizeOfGaplessBlock = len(list(v))
             yield str(sizeOfGaplessBlock)
         else:
-            blockRows = izip(*v)
-            blockRows = imap(''.join, blockRows)
-            insertSizes = imap(insertionSize, blockRows, letterSizes)
+            blockRows = zip(*v)
+            blockRows = map(''.join, blockRows)
+            insertSizes = map(insertionSize, blockRows, letterSizes)
             yield joined(insertSizes, ":")
 
 ##### Routines for converting to AXT format: #####
@@ -110,21 +111,21 @@
         raise Exception("for AXT, the 1st strand in each alignment must be +")
 
     # Convert to AXT's 1-based coordinates:
-    alnStarts = imap(operator.add, maf.alnStarts, repeat(1))
-    alnEnds = imap(operator.add, maf.alnStarts, maf.alnSizes)
+    alnStarts = map(operator.add, maf.alnStarts, repeat(1))
+    alnEnds = map(operator.add, maf.alnStarts, maf.alnSizes)
 
-    rows = zip(maf.seqNames, alnStarts, alnEnds, maf.strands)
+    rows = list(zip(maf.seqNames, alnStarts, alnEnds, maf.strands))
     head, tail = rows[0], rows[1:]
 
     outWords = []
-    outWords.append(axtCounter.next())
+    outWords.append(next(axtCounter))
     outWords.extend(head[:3])
     outWords.extend(chain(*tail))
     outWords.append(maf.namesAndValues["score"])
 
-    print joined(outWords, " ")
-    for i in maf.alnStrings: print i
-    print  # print a blank line at the end
+    print(joined(outWords, " "))
+    for i in maf.alnStrings: print(i)
+    print()  # print a blank line at the end
 
 ##### Routines for converting to tabular format: #####
 
@@ -143,13 +144,13 @@
     for i in sLines: outWords.extend(i[1:6])
 
     alnStrings = [i[6] for i in sLines]
-    alignmentColumns = zip(*alnStrings)
+    alignmentColumns = list(zip(*alnStrings))
     letterSizes = alnLetterSizes(sLines)
     gapStrings = matchAndInsertSizes(alignmentColumns, letterSizes)
     gapWord = ",".join(gapStrings)
     outWords.append(gapWord)
 
-    print "\t".join(outWords + endWords)
+    print("\t".join(outWords + endWords))
 
 ##### Routines for converting to PSL format: #####
 
@@ -183,7 +184,7 @@
 
 def gapRunCount(letters):
     """Get the number of runs of gap characters."""
-    uniqLetters = map(operator.itemgetter(0), groupby(letters))
+    uniqLetters = list(map(operator.itemgetter(0), groupby(letters)))
     return uniqLetters.count("-")
 
 def pslEndpoints(alnStart, alnSize, strand, seqSize):
@@ -208,8 +209,8 @@
 def writePsl(maf, isProtein):
     dieUnlessPairwise(maf)
 
-    alnStrings = map(str.upper, maf.alnStrings)
-    alignmentColumns = zip(*alnStrings)
+    alnStrings = list(map(str.upper, maf.alnStrings))
+    alignmentColumns = list(zip(*alnStrings))
     letterSizes = mafLetterSizes(maf)
 
     matchCounts = caseSensitivePairwiseMatchCounts(alignmentColumns, isProtein)
@@ -231,19 +232,19 @@
     tName, qName = maf.seqNames
     tSeqSize, qSeqSize = maf.seqSizes
 
-    rows = zip(maf.alnStarts, maf.alnSizes, maf.strands, maf.seqSizes)
+    rows = list(zip(maf.alnStarts, maf.alnSizes, maf.strands, maf.seqSizes))
     tStart, tEnd = pslEndpoints(*rows[0])
     qStart, qEnd = pslEndpoints(*rows[1])
 
     blocks = list(pslBlocks(alignmentColumns, maf.alnStarts, letterSizes))
     blockCount = len(blocks)
-    blockSizes, tStarts, qStarts = map(pslCommaString, zip(*blocks))
+    blockSizes, tStarts, qStarts = list(map(pslCommaString, list(zip(*blocks))))
 
     outWords = (matches, mismatches, repMatches, nCount,
                 qNumInsert, qBaseInsert, tNumInsert, tBaseInsert, strand,
                 qName, qSeqSize, qStart, qEnd, tName, tSeqSize, tStart, tEnd,
                 blockCount, blockSizes, qStarts, tStarts)
-    print joined(outWords, "\t")
+    print(joined(outWords, "\t"))
 
 ##### Routines for converting to SAM format: #####
 
@@ -278,15 +279,15 @@
     return naturalSortKey(s)
 
 def writeSamHeader(sequenceLengths, dictFile, readGroupItems):
-    print "@HD\tVN:1.3\tSO:unsorted"
+    print("@HD\tVN:1.3\tSO:unsorted")
     for k in sorted(sequenceLengths, key=karyotypicSortKey):
-        print "@SQ\tSN:%s\tLN:%s" % (k, sequenceLengths[k])
+        print("@SQ\tSN:%s\tLN:%s" % (k, sequenceLengths[k]))
     if dictFile:
         for i in fileinput.input(dictFile):
-            if i.startswith("@SQ"): print i,
+            if i.startswith("@SQ"): print(i, end=' ')
             elif not i.startswith("@"): break
     if readGroupItems:
-        print "@RG\t" + "\t".join(readGroupItems)
+        print("@RG\t" + "\t".join(readGroupItems))
 
 mapqMissing = "255"
 mapqMaximum = "254"
@@ -357,7 +358,7 @@
     pos = str(int(rStart) + 1)  # convert to 1-based coordinate
 
     for s, qName, qStart, qAlnSize, qStrand, qSeqSize, qAlnString in tail:
-        alignmentColumns = zip(rAlnString.upper(), qAlnString.upper())
+        alignmentColumns = list(zip(rAlnString.upper(), qAlnString.upper()))
 
         qStart = int(qStart)
         qRevStart = int(qSeqSize) - qStart - int(qAlnSize)
@@ -371,7 +372,7 @@
             # don't try to handle multiple alignments for now (YAGNI):
             if len(qLines) > 1 or len(tail) > 1 or qualityName != qName:
                 raise Exception("can't interpret the quality data")
-            qual = ''.join(j for i, j in izip(qAlnString, qualityString)
+            qual = ''.join(j for i, j in zip(qAlnString, qualityString)
                            if i != "-")
 
         # It's hard to get all the pair info, so this is very
@@ -398,7 +399,7 @@
         if score: outWords.append(score)
         if evalue: outWords.append(evalue)
         if rg: outWords.append(rg)
-        print "\t".join(outWords)
+        print("\t".join(outWords))
 
 ##### Routines for converting to BLAST-like format: #####
 
@@ -423,26 +424,26 @@
     letterSizes = mafLetterSizes(maf)
     coords = maf.alnStarts
     for chunkCols in chunker(alignmentColumns, lineSize):
-        chunkRows = zip(*chunkCols)
-        chunkRows = map(''.join, chunkRows)
+        chunkRows = list(zip(*chunkCols))
+        chunkRows = list(map(''.join, chunkRows))
         starts = [i + 1 for i in coords]  # change to 1-based coordinates
-        starts = map(blastCoordinate, starts, maf.strands, maf.seqSizes)
-        increments = map(insertionSize, chunkRows, letterSizes)
-        coords = map(operator.add, coords, increments)
-        ends = map(blastCoordinate, coords, maf.strands, maf.seqSizes)
+        starts = list(map(blastCoordinate, starts, maf.strands, maf.seqSizes))
+        increments = list(map(insertionSize, chunkRows, letterSizes))
+        coords = list(map(operator.add, coords, increments))
+        ends = list(map(blastCoordinate, coords, maf.strands, maf.seqSizes))
         yield chunkCols, chunkRows, starts, ends
 
 def writeBlast(maf, opts, oldQueryName):
     dieUnlessPairwise(maf)
 
     if maf.seqNames[1] != oldQueryName:
-        print "Query= %s" % maf.seqNames[1]
-        print "         (%s letters)" % maf.seqSizes[1]
-        print
-
-    print ">%s" % maf.seqNames[0]
-    print "          Length = %s" % maf.seqSizes[0]
-    print
+        print("Query= %s" % maf.seqNames[1])
+        print("         (%s letters)" % maf.seqSizes[1])
+        print()
+
+    print(">%s" % maf.seqNames[0])
+    print("          Length = %s" % maf.seqSizes[0])
+    print()
 
     score = maf.namesAndValues["score"]
     if opts.bitScoreA is not None and opts.bitScoreB is not None:
@@ -452,9 +453,9 @@
         scoreLine = " Score = %s" % score
     try: scoreLine += ", Expect = %s" % maf.namesAndValues["E"]
     except KeyError: pass
-    print scoreLine
+    print(scoreLine)
 
-    alignmentColumns = zip(*maf.alnStrings)
+    alignmentColumns = list(zip(*maf.alnStrings))
 
     alnSize = len(alignmentColumns)
     matches = quantify(alignmentColumns, isMatch)
@@ -463,21 +464,21 @@
     gaps = alnSize - quantify(alignmentColumns, isGapless)
     gapPercent = 100 * gaps // alnSize  # round down, like BLAST
     if gaps: identLine += ", Gaps = %s/%s (%s%%)" % (gaps, alnSize, gapPercent)
-    print identLine
+    print(identLine)
 
-    strands = map(strandText, maf.strands)
-    print " Strand = %s / %s" % (strands[1], strands[0])
-    print
+    strands = list(map(strandText, maf.strands))
+    print(" Strand = %s / %s" % (strands[1], strands[0]))
+    print()
 
     for chunk in blastChunker(maf, opts.linesize, alignmentColumns):
         cols, rows, starts, ends = chunk
         startWidth = maxlen(starts)
-        matchSymbols = map(pairwiseMatchSymbol, cols)
+        matchSymbols = list(map(pairwiseMatchSymbol, cols))
         matchSymbols = ''.join(matchSymbols)
-        print "Query: %-*s %s %s" % (startWidth, starts[1], rows[1], ends[1])
-        print "       %-*s %s"    % (startWidth, " ", matchSymbols)
-        print "Sbjct: %-*s %s %s" % (startWidth, starts[0], rows[0], ends[0])
-        print
+        print("Query: %-*s %s %s" % (startWidth, starts[1], rows[1], ends[1]))
+        print("       %-*s %s"    % (startWidth, " ", matchSymbols))
+        print("Sbjct: %-*s %s %s" % (startWidth, starts[0], rows[0], ends[0]))
+        print()
 
 def writeBlastTab(maf, opts):
     aLine, sLines, qLines, pLines = maf
@@ -490,7 +491,7 @@
 
     sLine, qLine = sLines
     alnStrings = [i[6] for i in sLines]
-    alignmentColumns = zip(*alnStrings)
+    alignmentColumns = list(zip(*alnStrings))
     alnSize = len(alignmentColumns)
     matches = quantify(alignmentColumns, isMatch)
     matchPercent = "%.2f" % (100.0 * matches / alnSize)
@@ -512,12 +513,12 @@
         if score and opts.bitScoreA is not None and opts.bitScoreB is not None:
             bitScore = opts.bitScoreA * float(score) - opts.bitScoreB
             out.append("%.3g" % bitScore)
-    print joined(out, "\t")
+    print(joined(out, "\t"))
 
 ##### Routines for converting to HTML format: #####
 
 def writeHtmlHeader():
-    print '''
+    print('''
 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
  "http://www.w3.org/TR/html4/strict.dtd">
 <html lang="en"><head>
@@ -544,7 +545,7 @@
 <pre class="key"><span class="e">  </span> prob &gt; 0.5  </pre>
 <pre class="key"><span class="f">  </span> prob &le; 0.5  </pre>
 </div>
-'''
+''')
 
 def probabilityClass(probabilityColumn):
     p = ord(min(probabilityColumn)) - 33
@@ -579,24 +580,24 @@
         scoreLine += " score=%s" % maf.namesAndValues["score"]
         scoreLine += ", expect=%s" % maf.namesAndValues["E"]
     except KeyError: pass
-    print "<h3>%s:</h3>" % scoreLine
+    print("<h3>%s:</h3>" % scoreLine)
 
     if maf.pLines:
         probRows = [i[1] for i in maf.pLines]
-        probCols = izip(*probRows)
-        classes = imap(probabilityClass, probCols)
+        probCols = zip(*probRows)
+        classes = map(probabilityClass, probCols)
     else:
         classes = repeat(None)
 
     nameWidth = maxlen(maf.seqNames)
-    alignmentColumns = zip(*maf.alnStrings)
+    alignmentColumns = list(zip(*maf.alnStrings))
 
-    print '<pre>'
+    print('<pre>')
     for chunk in blastChunker(maf, lineSize, alignmentColumns):
         cols, rows, starts, ends = chunk
         startWidth = maxlen(starts)
         endWidth = maxlen(ends)
-        matchSymbols = map(multipleMatchSymbol, cols)
+        matchSymbols = list(map(multipleMatchSymbol, cols))
         matchSymbols = ''.join(matchSymbols)
         classChunk = islice(classes, lineSize)
         classRuns = list(identicalRuns(classChunk))
@@ -604,10 +605,10 @@
             spans = [htmlSpan(r, i) for i in classRuns]
             spans = ''.join(spans)
             formatParams = nameWidth, n, startWidth, s, spans, endWidth, e
-            print '%-*s %*s %s %*s' % formatParams
-        print ' ' * nameWidth, ' ' * startWidth, matchSymbols
-        print
-    print '</pre>'
+            print('%-*s %*s %s %*s' % formatParams)
+        print(' ' * nameWidth, ' ' * startWidth, matchSymbols)
+        print()
+    print('</pre>')
 
 ##### Routines for reading MAF format: #####
 
@@ -640,7 +641,7 @@
                     if k == "lambda": opts.bitScoreA = x / math.log(2)
                     if k == "K":      opts.bitScoreB = math.log(x, 2)
                 except ValueError: pass
-            if isKeepComments: print i,
+            if isKeepComments: print(i, end=' ')
     if a: yield a, s, q, p
 
 def isFormat(myString, myFormat):
@@ -676,7 +677,7 @@
         elif isFormat(format, "sam"): writeSam(maf, rg)
         elif isFormat(format, "tabular"): writeTab(maf)
         else: raise Exception("unknown format: " + format)
-    if isFormat(format, "html") and not opts.noheader: print "</body></html>"
+    if isFormat(format, "html") and not opts.noheader: print("</body></html>")
 
 if __name__ == "__main__":
     signal.signal(signal.SIGPIPE, signal.SIG_DFL)  # avoid silly error message
@@ -715,6 +716,6 @@
 
     try: mafConvert(opts, args)
     except KeyboardInterrupt: pass  # avoid silly error message
-    except Exception, e:
+    except Exception as e:
         prog = os.path.basename(sys.argv[0])
         sys.exit(prog + ": error: " + str(e))
