diff --git a/pyext/professor2/eigentunes.py b/pyext/professor2/eigentunes.py
new file mode 100644
--- /dev/null
+++ b/pyext/professor2/eigentunes.py
@@ -0,0 +1,230 @@
+
+def exploreValley(center_t, direction_t, TRAFO, GOFdef,):
+    exec GOFdef in globals() # Note globals!
+    def getVal(a):
+        temp_t = center_t +  a*direction_t
+        temp = TRAFO * temp_t.transpose()
+        temp_r = temp.transpose().tolist()[0]
+        return profGoF(*temp_r) - target
+
+
+def mkNewParamCorrelation(T_trans, T, point, GOFdef, target):
+    import professor2 as prof
+    exec GOFdef in locals() # Note globals!
+
+    from numpy import matrix
+    rv = matrix(point.values())
+    center_t = (T_trans * rv.transpose()).transpose()
+
+    DIM = len(point.values())
+    from scipy.optimize import fsolve
+    from numpy import zeros,diag
+
+    def getVal(a, direction):
+        temp_t = center_t +  a*direction
+        temp = T * temp_t.transpose()
+        temp_r = temp.transpose().tolist()[0]
+        return profGoF(*temp_r) - target
+
+    def getX(a, direction):
+        return center_t +  a*direction
+
+    newdiag = []
+    for i in xrange(DIM):
+        ev = zeros(DIM)
+        ev[i] = 1
+        temp  = fsolve(lambda x:getVal(x, ev), 1)
+        temp2 = fsolve(lambda x:getVal(x, -ev), 1)
+        if temp > temp2:
+            newdiag.append(temp)
+        else:
+            newdiag.append(temp2)
+
+    N = diag([x[0] for x in newdiag])
+    return T_trans*N*T
+
+
+
+
+def calcHistoCov(h, COV_P, result):
+    """
+    Propagate the parameter covariance onto the histogram covariance
+    using the ipol gradients.
+    """
+    IBINS = h.bins
+    from numpy import zeros
+    COV_H = zeros((h.nbins, h.nbins))
+    from numpy import array
+    for i in xrange(len(IBINS)):
+        GRD_i = array(IBINS[i].grad(result))
+        for j in xrange(len(IBINS)):
+            GRD_j = array(IBINS[j].grad(result))
+            pc =GRD_i.dot(COV_P).dot(GRD_j)
+            COV_H[i][j] = pc
+    return COV_H
+
+
+def mkErrorPropagationHistos(IHISTOS, point, COV, combine=False):
+    covs = {}
+    properrs = {}
+    ipolerrs = {}
+    ipolvals = {}
+    from numpy import sqrt
+    for k, v in IHISTOS.iteritems():
+        covs[k]     = calcHistoCov(v, COV, point)
+        properrs[k] = sqrt(0.5*covs[k].diagonal())
+        ipolerrs[k] = [b.err  for b in v.toDataHisto(point).bins]
+        ipolvals[k] = [b.val  for b in v.toDataHisto(point).bins]
+
+    scatters=[]
+    for k, v in IHISTOS.iteritems():
+        T = v.toDataHisto(point)
+        for i in xrange(T.nbins):
+            T.bins[i].errs=properrs[k][i] if combine is False else sqrt(properrs[k][i]**2 + ipolerrs[k][i]**2)
+        scatters.append(T.toScatter2D())
+    return scatters
+
+
+def mkScatters(masterbox, mastercenter, ppoint):
+    import professor2 as prof
+    boxdict=None
+    try:
+        ppoint = ppoint.values()
+    except:
+        pass
+    for box, bdict in masterbox.iteritems():
+        if prof.pInBOX(ppoint, box):
+            boxdict = bdict
+            break
+    if boxdict is None:
+        distances={}
+        for c in mastercenter.keys():
+            distances[prof.pBoxDistance(ppoint, c)] = c
+        winner = min(distances.keys())
+        boxdict = mastercenter[distances[winner]]
+    ipolH=boxdict["IHISTOS"]
+
+    scatters_e =[]
+    for k in sorted(ipolH.keys()):
+        v = ipolH[k]
+        T = v.toDataHisto(ppoint)
+        scatters_e.append(T.toScatter2D())
+    return scatters_e
+
+
+def mkEnvelopes(central, etunes):
+    ret = {}
+    for i in xrange(1, len(etunes.keys())/2 + 1):
+        ret[i] = []
+        Hplus  = etunes[i]
+        Hminus = etunes[-i]
+        for num_h, h in enumerate(central):
+            temp = h.clone()
+            for num_p, p in enumerate(temp.points):
+                yplus  = Hplus[num_h].points[num_p].y
+                yminus = Hminus[num_h].points[num_p].y
+                if yplus > p.y:
+                    eplus  = yplus - p.y
+                    eminus = p.y - yminus
+                else:
+                    eplus  = yminus - p.y
+                    eminus  = p.y - yplus
+                p.yErrs = (eminus, eplus)
+            ret[i].append(temp)
+    return ret
+
+
+def mkTotvelopes(central, etunes):
+    ret = []
+    for num_h, h in enumerate(central):
+        temp = h.clone()
+        allThis = [x[num_h] for x in etunes.values()]
+        for num_p, p in enumerate(temp.points):
+            dybin = [x.points[num_p].y - p.y for x in allThis]
+            pos = [x for x in dybin if x>=0]
+            neg = [x for x in dybin if x<0]
+            eplus = max(pos) if len(pos) > 0 else 0
+            eminus = abs(min(neg)) if len(neg) > 0 else 0
+            p.yErrs = (eminus, eplus)
+        ret.append(temp)
+    return ret
+
+
+def mkAddvelopes(central, etunes, addLinear=False):
+    ret = []
+    for num_h, h in enumerate(central):
+        temp = h.clone()
+        allThis = [x[num_h] for x in etunes.values()]
+        for num_p, p in enumerate(temp.points):
+            dybin = [x.points[num_p].y-p.y for x in allThis]
+            pos = [x for x in dybin if x>=0]
+            neg = [x for x in dybin if x<0]
+            from math import sqrt
+            if addLinear:
+                eplus  = sum(pos)                   if len(pos) > 0 else 0
+                eminus = sum([abs(x) for x in neg]) if len(neg) > 0 else 0
+            else:
+                eplus  = sqrt(sum([x*x for x in pos])) if len(pos) > 0 else 0
+                eminus = sqrt(sum([x*x for x in neg])) if len(neg) > 0 else 0
+            p.yErrs = (eminus, eplus)
+        ret.append(temp)
+    return ret
+
+
+def readFromFile(fname):
+    nbins = 0
+    params = []
+    ret = []
+    with open(fname) as f:
+        for line in f:
+            l = line.strip()
+            if l.startswith("#"):
+                if "Nbins" in l:
+                    nbins = int(l.split()[-1])
+                elif "Parameters" in l:
+                    params = l.split()[2:]
+            else:
+                ret.append(map(float, l.split()))
+    return ret, nbins, params
+
+# https://stackoverflow.com/questions/9535954/printing-lists-as-tabular-data
+def mkTabulate(head, rows):
+    from tabulate import tabulate
+    return tabulate(rows, headers=head,  tablefmt='orgtbl')
+
+def mkTable(head, rows):
+    t = head[0]
+    for h in head[1:]:
+        t+="\t%s"%str(h)
+
+    for row in rows:
+        t+="\n%s"%row[0]
+        for r in row[1:]:
+            t+="\t%s"%str(r)
+    t+="\n"
+
+    return t
+
+def mkEigenTable(ETs, rfile, latex=False):
+    import professor2 as prof
+    P_min, OTH = prof.readResult(rfile)
+
+    head = ["Tune"] + P_min.keys()
+
+    rows = [["Central"] + P_min.values()]
+
+    for e in sorted(list(set([abs(x) for x in ETs.keys()]))):
+        rows.append(["%i+"%e] + list(ETs[e]) )
+        rows.append(["%i-"%e] + list(ETs[-e]))
+
+    s="\nEigentune summary:\n\n"
+
+    try:
+        from tabulate import tabulate
+        s+=mkTabulate(head, rows)
+    except ImportError, e:
+        print e
+        print "Fallback to simple table"
+        s+=mkTable(head, rows)
+    return s
+