def right_qr_maxvol(nd):
    cr = nd.core.copy()
    r1, n, r2 = cr.shape
    Rv = nd.edges[1].Rv.copy()
    cr = np.tensordot(cr, Rv, 1)
    r2 = cr.shape[2]
    cr = reshape(cr, (r1, -1))
    cr = cr.T
    q, Rv = np.linalg.qr(cr)
    ind, c = maxvol(q)
    Rv = np.dot(q[ind, :], Rv)
    #q = np.linalg.solve(q[ind, :].T, q.T).T
    q = c.copy()
    nd.edges[0].Rv = Rv.T.copy()
    q = reshape(q.T, (-1, n, r2))
    nd.core = q.copy()
    nd.maxvol_right = np.unravel_index(ind, (n, r2), order='F')
    """ Recomputation of right indices goes here """
    """ Finally, update indices """
    i_right = nd.edges[1].ind_right
    w1 = mkron(np.ones((r2, 1), dtype=np.int32),
               reshape(np.arange(n, dtype=np.int32), (-1, 1)))
    try:
        w2 = mkron(i_right, np.ones((n, 1), dtype=np.int32))
        i_next = np.hstack((w1, w2))
    except:
        i_next = w1

    i_next = reshape(i_next, (n * r2, -1))
    i_next = i_next[ind, :]
    nd.edges[0].ind_right = i_next.copy()
    nd.edges[0].ind_right_add = i_next.copy()
def right_qr_maxvol(nd):
    cr = nd.core.copy()
    r1, n, r2 = cr.shape
    Rv = nd.edges[1].Rv.copy()
    cr = np.tensordot(cr, Rv, 1)
    r2 = cr.shape[2]
    cr = reshape(cr, (r1, -1))
    cr = cr.T
    q, Rv = np.linalg.qr(cr)
    ind, c = maxvol(q)
    Rv = np.dot(q[ind, :], Rv)
    #q = np.linalg.solve(q[ind, :].T, q.T).T
    q = c.copy()
    nd.edges[0].Rv = Rv.T.copy()
    q = reshape(q.T, (-1, n, r2))
    nd.core = q.copy()
    nd.maxvol_right = np.unravel_index(ind, (n, r2), order='F')
    """ Recomputation of right indices goes here """ 
    """ Finally, update indices """
    i_right = nd.edges[1].ind_right
    w1 = mkron(np.ones((r2, 1), dtype=np.int32), reshape(np.arange(n, dtype=np.int32),(-1, 1)))
    try:
        w2 = mkron(i_right, np.ones((n, 1), dtype=np.int32))
        i_next = np.hstack((w1, w2))
    except:
        i_next = w1

    
    i_next = reshape(i_next, (n * r2, -1))
    i_next = i_next[ind, :]
    nd.edges[0].ind_right = i_next.copy()
    nd.edges[0].ind_right_add = i_next.copy()
def left_qr_maxvol(nd):
    cr = nd.core.copy()
    r1, n1, r2 = cr.shape
    cr = np.tensordot(nd.edges[0].Ru, cr, 1)
    #nd.edges[0].Ru = np.ones((1, 1))
    r1 = cr.shape[0]
    cr = reshape(cr, (r1 * n1, r2))
    q, Ru = qr(cr)
    ind, c = maxvol(q)
    Ru = np.dot(q[ind, :], Ru)
    q = c.copy()
    nd.core = reshape(q, (r1, n1, r2)).copy()
    nd.edges[1].Ru = Ru.copy()
    nd.maxvol_left = np.unravel_index(ind, (r1, n1), order='F')
    #The philosophical question if this index should be stored on the edge or in the node
    #The typical recomputation:
    #Take left index somewhere and update. For the first node it comes from the left edge
    #So, we can store ind_left on an edge, whereas ind_left_add in the node
    """ This is a logically separate function """
    i_left = nd.edges[0].ind_left
    #Update index
    w1 = mkron(np.ones((n1, 1), dtype=np.int32), i_left)
    w2 = mkron(reshape(np.arange(n1, dtype=np.int32), (-1, 1)),
               np.ones((r1, 1), dtype=np.int32))
    i_next = np.hstack((w1, w2))
    i_next = reshape(i_next, (r1 * n1, -1))
    i_next = i_next[ind, :]

    nd.edges[1].ind_left = i_next.copy()
    nd.edges[1].ind_left_add = i_next.copy()
def left_qr_maxvol(nd):
    cr = nd.core.copy()
    r1, n1, r2 = cr.shape
    cr = np.tensordot(nd.edges[0].Ru, cr, 1)
    #nd.edges[0].Ru = np.ones((1, 1))
    r1 = cr.shape[0]
    cr = reshape(cr, (r1 * n1, r2))
    q, Ru = qr(cr)
    ind, c = maxvol(q)
    Ru = np.dot(q[ind, :], Ru)
    q = c.copy()
    nd.core = reshape(q, (r1, n1, r2)).copy()
    nd.edges[1].Ru = Ru.copy()
    nd.maxvol_left = np.unravel_index(ind, (r1, n1), order='F')
    #The philosophical question if this index should be stored on the edge or in the node
    #The typical recomputation:
    #Take left index somewhere and update. For the first node it comes from the left edge
    #So, we can store ind_left on an edge, whereas ind_left_add in the node
    """ This is a logically separate function """
    i_left = nd.edges[0].ind_left
    #Update index
    w1 = mkron(np.ones((n1, 1), dtype=np.int32), i_left)
    w2 = mkron(reshape(np.arange(n1, dtype=np.int32),(-1, 1)), np.ones((r1, 1), dtype=np.int32))
    i_next = np.hstack((w1, w2))
    i_next = reshape(i_next, (r1 * n1, -1))
    i_next = i_next[ind, :]
    
    nd.edges[1].ind_left = i_next.copy()
    nd.edges[1].ind_left_add = i_next.copy()