def simplexInCenter(vs):
    print "        in simplexInCenter"
    n = len(vs)
    nDims = len(vs[0])

    # 5 points in 2 dims is hopeless
    if n >= nDims+3:
        print "            hopeless!"
        print "        out simplexInCenter"
        return None

    weights = []
    for i in xrange(n):
        oppositeFacet = Mat([vs[j] for j in xrange(n) if j != i])
        #do('oppositeFacet')
        M = Mat([v-oppositeFacet[0] for v in oppositeFacet[1:]])
        #do('M')
        M2 = M * M.transposed()
        #do('M2')
        det = M2.det()
        #do('det')
        oppositeFacetContent = sqrt(det)
        weights.append(oppositeFacetContent)
    weights = Vec(weights)
    weights /= sum(weights)
    answer = Vec(weights) * Mat(vs)
    do('answer')
    print "        out simplexInCenter"
    return answer
 def compose(self,rhs):
     lhs = self
     nDims = len(self.t)
     t = rhs.apply(self.t) # = rhs(lhs(0))
     if True:
         R = Mat.identity(nDims)
         for i in xrange(nDims):
             R[i] = translate(rhs.apply(lhs.apply(R[i])), -t) # R[i] = Isometry(I,t)^-1(rhs(lhs(I[i])))
         R = Mat(R)
     else:
         # Argh, I thought this was right, but it's not?? wtf?
         R = self.R * rhs.R
     return HyperbolicIsometry(R,t)
def inCenter4(vs):
    vs = [Vec(v) for v in vs]
    n = len(vs)
    assert n == 4
    inwardNormals = [(vs[(i+1)%n]-vs[i]).perpDot().normalized() for i in xrange(n)]
    offsets = [inwardNormals[i].dot(vs[i]) for i in xrange(n)]
    if use_numpy:
        M = numpy.matrix([
            list(inwardNormals[0])+[1,0],
            list(inwardNormals[1])+[0,1],
            list(inwardNormals[2])+[1,0],
            list(inwardNormals[3])+[0,1],
        ])
        xyrr = numpy.linalg.solve(M,offsets)
    else:
        M = Mat([
            list(inwardNormals[0])+[1,0],
            list(inwardNormals[1])+[0,1],
            list(inwardNormals[2])+[1,0],
            list(inwardNormals[3])+[0,1],
        ])
        xyrr = M.inverse() * Vec(offsets)
    x,y,r,R = xyrr
    return Vec(x,y)
 def __init__(self,R=None,t=None):
     # R and t can't both be None, or we wouldn't know the dimension
     if R == None: R = Mat.identity(len(t))
     if t == None: t = [0]*len(R)
     self.R = Mat(R)
     self.t = Vec(t)
 def identity(nDims):
     return HyperbolicIsometry(Mat.identity(nDims), [0]*nDims)
class HyperbolicIsometry:

    @staticmethod
    def identity(nDims):
        return HyperbolicIsometry(Mat.identity(nDims), [0]*nDims)

    def __init__(self,R=None,t=None):
        # R and t can't both be None, or we wouldn't know the dimension
        if R == None: R = Mat.identity(len(t))
        if t == None: t = [0]*len(R)
        self.R = Mat(R)
        self.t = Vec(t)
    def apply(self,p):
        return translate(p * self.R, self.t)
    def applyInverse(self,p):
        return self.R * translate(p,-self.t) # R * p = p * R^-1 since R is orthogonal
    # Return f such that for all p,
    # f(p) = rhs(self(p))
    def compose(self,rhs):
        lhs = self
        nDims = len(self.t)
        t = rhs.apply(self.t) # = rhs(lhs(0))
        if True:
            R = Mat.identity(nDims)
            for i in xrange(nDims):
                R[i] = translate(rhs.apply(lhs.apply(R[i])), -t) # R[i] = Isometry(I,t)^-1(rhs(lhs(I[i])))
            R = Mat(R)
        else:
            # Argh, I thought this was right, but it's not?? wtf?
            R = self.R * rhs.R
        return HyperbolicIsometry(R,t)
    def inverse(self):
        return HyperbolicIsometry(None,-self.t).compose(HyperbolicIsometry(self.R.transposed(),None))
    def dist2(self,rhs):
        return (rhs.R-self.R).length2() + (rhs.t-self.t).length2()
    def __repr__(self):
        return 'HyperbolicIsometry('+`self.R`+','+`self.t`+')'
    def __str__(self):
        return self.__repr__()

    #
    # Operator notation:
    #    f(p) = f.apply(p)
    #     p*f = f.apply(p)
    #   f0*f1 = f0.compose(f1)
    # note that
    #     (f0*f1)*f2 == f0*(f1*f2)
    # and  (p*f0)*f1 == p*(f0*f1)
    #
    def __call__(self,p):
        return self.apply(p)
    def __rmul__(self,lhs):
        # actually relies on fact that Vec's __mul__
        # explicitly calls rhs.__rmul__ when rhs is unrecognized type
        return self.apply(lhs)
    def __mul__(self,rhs):
        return self.compose(rhs)
    def __pow__(self,rhs):
        assert type(rhs) == int
        if rhs == -1: # most common case
            return self.inverse()
        if rhs < 0:
            # either of the following work
            if True:
                return (self^-rhs).inverse()
            else:
                return self.inverse()^-rhs
        if rhs > 1:
            return self**int(rhs/2) * self**(rhs-int(rhs/2))
        if rhs == 1:
            return self
        assert rhs == 0
        return HyperbolicIsometry.identity(len(self.t))
    # XXX TODO: I think this is a bad idea, since ^ binds looser than * and even +
    def __xor__(self,rhs):
        return self.__pow__(rhs)
def inCenter(vs):
    if False:
        do('vs')
    vs = [Vec(v) for v in vs]
    n = len(vs)
    inwardNormals = [(vs[(i+1)%n]-vs[i]).perpDot().normalized() for i in xrange(n)]
    offsets = [inwardNormals[i].dot(vs[i]) for i in xrange(n)]

    # compute n-2 tri-side in-centers...
    centers = []
    radii = []
    for i in xrange(n-2):
        if use_numpy:
            M = numpy.matrix([
                list(inwardNormals[ 0 ])+[-1],
                list(inwardNormals[i+1])+[-1],
                list(inwardNormals[i+2])+[-1],
            ])
            o = numpy.matrix([
                [offsets[ 0 ]],
                [offsets[i+1]],
                [offsets[i+2]],
            ])
            #x,y,r = numpy.linalg.solve(M,o)
            x,y,r = [float(x) for x in numpy.linalg.solve(M,o)]
        else:
            M = Mat([
                list(inwardNormals[ 0 ])+[-1],
                list(inwardNormals[i+1])+[-1],
                list(inwardNormals[i+2])+[-1],
            ])
            o = Vec([
                offsets[ 0 ],
                offsets[i+1],
                offsets[i+2],
            ])
            x,y,r = M.inverse() * o
        if False:
            # FUDGE
            r = abs(r)
        centers.append(Vec(x,y))
        radii.append(r)
        if False:
            #do('x')
            #do('y')
            do('r')


    if n == 3:
        # single weight will be zero in this case... no point in doing the undefined arithmetic
        return centers[0]

    if n == 4:
        # Either way works, but neither way is robust when cocircular
        if False:
            weights = [
                1./(inwardNormals[3].dot(centers[0]) - offsets[3] - radii[0]),
                1./(inwardNormals[n-3].dot(centers[n-2-1])-offsets[n-3] - radii[n-2-1])
            ]
        else:
            weights = [
                inwardNormals[1].dot(centers[1])-offsets[1] - radii[1],
                inwardNormals[3].dot(centers[0])-offsets[3] - radii[0],
            ]
            # fudge-- this shouldn't be needed, if I get a more robust formula to begin with
            if weights[0] == 0. and weights[1] == 0.:
                weights = [1.,1.]

    if n == 5:
        # I fear this doesn't really work
        weights = [
            (inwardNormals[1].dot(centers[1])-offsets[1] - radii[1])*(inwardNormals[2].dot(centers[2])-offsets[2] - radii[2]),
            (inwardNormals[2].dot(centers[2])-offsets[2] - radii[2])*(inwardNormals[3].dot(centers[0])-offsets[3] - radii[0]),
            (inwardNormals[3].dot(centers[0])-offsets[3] - radii[0])*(inwardNormals[4].dot(centers[1])-offsets[4] - radii[1]),
        ]


    if False: # XXX GET RID
        # weights other than first and last are
        # 1 / (distance from last side not involving that circle) / (distance from first side not involving that circle)
        for i in xrange(n-4):
            weights.append(1./((inwardNormals[1+i].dot(centers[1+i]) - offsets[1+i] - radii[1+i])
                             * (inwardNormals[4+i].dot(centers[1+i]) - offsets[4+i] - radii[1+i]) ))

    weightsSum = sum(weights)
    if weightsSum == 0.:
        return centers[0] # hack
    if False:
        do('[float(weight) for weight in weights]')
        do('weightsSum')
        do('[float(weight/weightsSum) for weight in weights]')

    return sum([center*(weight/weightsSum) for center,weight in zip(centers,weights)])