#include <algorithm>
#include <bitset>
#include <complex>
#include <deque>
#include <exception>
#include <fstream>
#include <functional>
#include <iomanip>
#include <ios>
#include <iosfwd>
#include <iostream>
#include <istream>
#include <iterator>
#include <limits>
#include <list>
#include <locale>
#include <map>
#include <memory>
#include <new>
#include <numeric>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <stdexcept>
#include <streambuf>
#include <string>
#include <typeinfo>
#include <utility>
#include <valarray>
#include <vector>
#include <climits>

#define rep(i, m, n) for(int i=int(m);i<int(n);i++)
#define EACH(i, c) for (auto &(i): c)
#define all(c) begin(c),end(c)
#define EXIST(s, e) ((s).find(e)!=(s).end())
#define SORT(c) sort(begin(c),end(c))
#define pb emplace_back
#define MP make_pair
#define SZ(a) int((a).size())

//#define LOCAL 0
//#ifdef LOCAL
//#define DEBUG(s) cout << (s) << endl
//#define dump(x)  cerr << #x << " = " << (x) << endl
//#define BR cout << endl;
//#else
//#define DEBUG(s) do{}while(0)
//#define dump(x) do{}while(0)
//#define BR
//#endif


//改造
typedef long long int ll;
using namespace std;
#define INF (1 << 30) - 1
#define INFl (ll)5e15
#define DEBUG 0 //デバッグする時1にしてね
#define dump(x)  cerr << #x << " = " << (x) << endl
#define MOD 1000000007
//ここから編集する

/**
 * 重み付きUnionFind
 * Abelは重さの型UnionFind<int>とかで
 * 0-indexed
 */
template<class Abel>
struct UnionFind {
    vector<int> par;
    vector<int> rank;
    vector<Abel> diff_weight;

    /**
     * 初期化
     * @param n
     * @param SUM_UNITY
     */
    UnionFind(int n = 1, Abel SUM_UNITY = 0) {
        init(n, SUM_UNITY);
    }

    /**
     * 大きさnで初期化
     * @param n
     * @param SUM_UNITY
     */
    void init(int n = 1, Abel SUM_UNITY = 0) {
        par.resize(n);
        rank.resize(n);
        diff_weight.resize(n);
        for (int i = 0; i < n; ++i) par[i] = i, rank[i] = 0, diff_weight[i] = SUM_UNITY;
    }

    /**
     * 自分の所属しているグループを返す
     * @param x
     * @return
     */
    int find(int x) {
        if (par[x] == x) {
            return x;
        } else {
            int r = find(par[x]);
            diff_weight[x] += diff_weight[par[x]];
            return par[x] = r;
        }
    }

    /**
     * 重み
     * @param x
     * @return
     */
    Abel weight(int x) {
        find(x);
        return diff_weight[x];
    }

    /**
     * xとyが同じグループに所属しているかを返す
     * @param x
     * @param y
     * @return
     */
    bool same(int x, int y) {
        return find(x) == find(y);
    }

    /**
     * weight(y) = weight(x) + w となるように x と y を統合
     * yにxを統合
     * @param x
     * @param y
     * @param w
     * @return
     */
    bool unite(int x, int y, Abel w) {
        w += weight(x);
        w -= weight(y);
        x = find(x);
        y = find(y);
        if (x == y) return false;
        if (rank[x] < rank[y]) swap(x, y), w = -w;
        if (rank[x] == rank[y]) ++rank[x];
        par[y] = x;
        diff_weight[y] = w;
        return true;
    }

    /**
     * (yの重さ) - (xの重さ)を返す
     * @param x
     * @param y
     * @return
     */
    Abel diff(int x, int y) {
        return weight(y) - weight(x);
    }
};

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);

    int n, q;
    cin >> n >> q;

    UnionFind<int> uf(n);
    while (q--) {
        int cmd;
        cin >> cmd;
        if (cmd) {
            int x, y;
            cin >> x >> y;
            if (uf.same(x, y)) {
                cout << uf.diff(x, y) << endl;
            } else {
                cout << "?" << endl;
            }
        } else {
            int x, y, z;
            cin >> x >> y >> z;
//            cout << uf.unite(x, y, z) << endl;
            uf.unite(x, y, z);
        }
    }


    return 0;
}



Measured memory_bytes: 4148