// AOJ DSL_1_B Weighted Union Find Trees
// 2018.4.28 bal4u

#include <stdio.h>

// <重み付きUnion Find>
#define MAX 100003

int par[MAX], ws[MAX];

void initWeightedUnionFind(int n)
{
	int i;
	for (i = 0; i < n; i++) par[i] = -1;
}

int find(int x)
{
	int p;
	if (par[x] < 0) return x;
    p = find(par[x]), ws[x] += ws[par[x]];
    return par[x] = p;
}
    
int weight(int x)
{
    find(x); 
    return ws[x];
}
    
int unite(int x, int y, int w)  // x <-(w)- y (x + w = y)
{
	int tmp;

    w += weight(x), w -= weight(y);
    x = find(x), y = find(y);
    if (x == y) return 0;
    if (par[y] < par[x]) // 多い方が根になるようにスワップする.
        tmp = x, x = y, y = tmp, w = -w;
    
    par[x] += par[y], par[y] = x;
    ws[y] = w;
    return 1;
}
    
int same(int x, int y)
{
	return find(x) == find(y);
}

// *ans = x - y. 比較不能なら return 0.
int diff(int *ans, int x, int y)
{
    if (find(x) != find(y)) return 0;
	*ans = ws[x] - ws[y];
	return 1;
}
// </重み付きUnion Find>

#define gc() getchar_unlocked()
int in()
{
	int n = 0, c = gc();
	do n = 10*n + (c & 0xf), c = gc();
	while (c >= '0');
	return n;
}

int main()
{
	int n, q, k, x, y, z;
	
	n = in(), q = in();
	initWeightedUnionFind(n);
	while (q--) {
		if (in()) {
			x = in(), y = in();
			z = diff(&k, x, y);
			if (!z) puts("?");
			else printf("%d\n", k);
		} else {
			x = in(), y = in(), z = in();
			unite(y, x, z);
		}
	}
	return 0;
}
