Buggy python submission (verdict: Time Limit Exceeded):
# Disjoint Set: Union Find Tree
[p, rank, d] = [[], [], []]
w = {}

def makeSet(x):
    global p, rank, w
    p.append(x)
    rank.append(0)

def findSet(x):
    global p
    if x != p[x]:
        p[x] = findSet(p[x])
    return p[x]

def link(x, y):
    global p, rank
    if rank[x] > rank[y]:
        p[y] = x
    else:
        p[x] = y
        if rank[x] == rank[y]:
            rank[y] += 1

def relate(x, y, z):
    global w
    w[(x, y)] = z
    union(x, y)

def cost(x, y, n, total):
    global w, d
    for i in range(n):
        if ((x, i) in w.keys()) == True and d[i] == 0:
            d[i] = 1
            if i == y:
                total += w[(x, i)]
                return total
            result = cost(i, y, n, total)
            if result != None:
                total = result + w[(x, i)]
                return total     

        if ((i, x) in w.keys()) == True and d[i] == 0:
            d[i] = 1
            if i == y:
                total -= w[(i, x)]
                return total
            result = cost(i, y, n, total)
            if result != None:
                total = result - w[(i, x)]
                return total

def diff(x, y, n):
    if same(x, y) == 1:
        global d
        total = 0
        d = [0 for i in range(n)]
        d[x] = 1
        return cost(x, y, n, total)
    else:
        return "?"     

def union(x, y):
    link(findSet(x), findSet(y))    

def same(x, y):
    if findSet(x) == findSet(y):
        return 1
    else:
        return 0   

[n, q] = list(map(int, input().split()))
for i in range(n):
    makeSet(i)
'''
print(p)
print(rank)
print(w)
print("\n")
'''

for i in range(q):
    data = list(map(int, input().split()))
    if data[0] == 0:
        relate(data[1], data[2], data[3])
    else:
        print(diff(data[1], data[2], n))
    '''
    print("\n")
    print(p)
    print(rank)
    print(w)
    print("\n")
    '''


Fixed submission:
# Disjoint Set: Union Find Trees
[p, rank] = [[], []]
[n, q] = list(map(int, input().split()))
w = [0 for i in range(n)]

def makeSet(x):
    global p, rank
    p.append(x)
    rank.append(0)

def findSet(x):
    global p
    if x != p[x]:
        pre_parent = p[x]
        p[x] = findSet(p[x])
        w[x] += w[pre_parent]
    return p[x]

def link(x, y, z):
    global p, rank, w
    if rank[x] > rank[y]:
        p[y] = x
        w[y] = z
    else:
        p[x] = y
        w[x] = -z
        if rank[x] == rank[y]:
            rank[y] += 1

def same(x, y):
    if findSet(x) == findSet(y):
        return 1
    else:
        return 0   

def relate(x, y, z):
    global w
    root_x = findSet(x)
    root_y = findSet(y)
    z += w[x]
    z -= w[y]
    link(root_x, root_y, z)
   
def diff(x, y):
    if same(x, y) == 1:
        global w
        return w[y] - w[x]
    else:
        return "?"     

for i in range(n):
    makeSet(i)

for i in range(q):
    data = list(map(int, input().split()))
    if data[0] == 0:
        relate(data[1], data[2], data[3])
    else:
        print(diff(data[1], data[2]))

Buggy python submission (verdict: Time Limit Exceeded):
# Disjoint Set: Union Find Tree
[p, rank, d] = [[], [], []]
[n, q] = list(map(int, input().split()))
#time = 0

def makeSet(x):
    global p, rank, w
    p.append(x)
    rank.append(0)

for i in range(n):
    makeSet(i)

w = [[] for i in range(n)]

def findSet(x):
    global p
    if x != p[x]:
        p[x] = findSet(p[x])
    return p[x]

def link(x, y):
    global p, rank
    if rank[x] > rank[y]:
        p[y] = x
    else:
        p[x] = y
        if rank[x] == rank[y]:
            rank[y] += 1

def relate(x, y, z):
    global w
    w[x].append((y, z))
    union(x, y)

def cost(x, y, n, total):
    global w, d, time
    #time += 1
    #if time > 5:
    #    return None
    for i in range(len(w[x])):
        if d[w[x][i][0]] == 0:
            #print(x, w[x][i][0])
            d[w[x][i][0]] = 1
            if y == w[x][i][0]:
                total += w[x][i][1]
                return total
            result = cost(w[x][i][0], y, n, total)
            if result != None:
                total = result + w[x][i][1]
                return total

    for i in range(n):
        for j in range(len(w[i])):
            if w[i][j][0] == x and d[i] == 0:
                #print(i, x)
                d[i] = 1
                if y == i:
                    total -= w[i][j][1]
                    return total
                result = cost(i, y, n, total)
                if result != None:
                    total = result - w[i][j][1]
                    return total

'''
        if ((x, i) in w.keys()) == True and d[i] == 0:
            d[i] = 1
            if i == y:
                total += w[(x, i)]
                return total
            result = cost(i, y, n, total)
            if result != None:
                total = result + w[(x, i)]
                return total     

        if ((i, x) in w.keys()) == True and d[i] == 0:
            d[i] = 1
            if i == y:
                total -= w[(i, x)]
                return total
            result = cost(i, y, n, total)
            if result != None:
                total = result - w[(i, x)]
                return total
                '''

def diff(x, y, n):
    if same(x, y) == 1:
        global d
        total = 0
        d = [0 for i in range(n)]
        d[x] = 1
        return cost(x, y, n, total)
    else:
        return "?"     

def union(x, y):
    link(findSet(x), findSet(y))    

def same(x, y):
    if findSet(x) == findSet(y):
        return 1
    else:
        return 0   

'''
print(p)
print(rank)
print(w)
print("\n")
'''

for i in range(q):
    data = list(map(int, input().split()))
    if data[0] == 0:
        relate(data[1], data[2], data[3])
    else:
        print(diff(data[1], data[2], n))
    '''
    print("\n")
    print(p)
    print(rank)
    print(w)
    print("\n")
    '''


Fixed submission:
# Disjoint Set: Union Find Trees
[p, rank] = [[], []]
[n, q] = list(map(int, input().split()))
w = [0 for i in range(n)]

def makeSet(x):
    global p, rank
    p.append(x)
    rank.append(0)

def findSet(x):
    global p
    if x != p[x]:
        pre_parent = p[x]
        p[x] = findSet(p[x])
        w[x] += w[pre_parent]
    return p[x]

def link(x, y, z):
    global p, rank, w
    if rank[x] > rank[y]:
        p[y] = x
        w[y] = z
    else:
        p[x] = y
        w[x] = -z
        if rank[x] == rank[y]:
            rank[y] += 1

def same(x, y):
    if findSet(x) == findSet(y):
        return 1
    else:
        return 0   

def relate(x, y, z):
    global w
    root_x = findSet(x)
    root_y = findSet(y)
    z += w[x]
    z -= w[y]
    link(root_x, root_y, z)
   
def diff(x, y):
    if same(x, y) == 1:
        global w
        return w[y] - w[x]
    else:
        return "?"     

for i in range(n):
    makeSet(i)

for i in range(q):
    data = list(map(int, input().split()))
    if data[0] == 0:
        relate(data[1], data[2], data[3])
    else:
        print(diff(data[1], data[2]))