// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_1_B
#include "src/datastructure/disjoint_set_union_with_potential.hpp"
#include <iostream>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
DisjointSetUnionWithPotential<int> dsu(n);
while (q--) {
int f;
cin >> f;
if (f == 0) {
int x, y, z;
cin >> x >> y >> z;
dsu.unite(x, y, z);
} else {
int x, y;
cin >> x >> y;
if (dsu.same(x, y)) cout << dsu.difference(x, y) << endl;
else cout << '?' << endl;
}
}
return 0;
}
#line 1 "test/datastructure/disjoint_set_union_with_potential/aoj_dsl_1_b.test.cpp"
// verification-helper: PROBLEM https://onlinejudge.u-aizu.ac.jp/courses/library/3/DSL/all/DSL_1_B
#line 1 "src/datastructure/disjoint_set_union_with_potential.hpp"
#include <numeric>
#include <utility>
#include <vector>
template<class T>
class DisjointSetUnionWithPotential {
private:
std::vector<int> rank, size, p;
std::vector<T> pot;
int num{};
public:
DisjointSetUnionWithPotential(int n) : rank(n), size(n, 1), p(n), pot(n), num(n) {
std::iota(p.begin(), p.end(), 0);
}
bool same(int x, int y) { return root(x) == root(y); }
// potential(y) = potential(x) + d
bool unite(int x, int y, T d) {
d += potential(x);
d -= potential(y);
x = root(x);
y = root(y);
if (x == y) return d == 0;
--num;
if (rank[x] < rank[y]) {
std::swap(x, y);
d = -d;
}
if (rank[x] == rank[y]) ++rank[x];
p[y] = x;
pot[y] = d;
size[x] += size[y];
return true;
}
int root(int x) {
if (p[x] == x) return x;
int r = root(p[x]);
pot[x] += pot[p[x]];
return p[x] = r;
}
T potential(int x) {
root(x);
return pot[x];
}
T difference(int x, int y) { return potential(y) - potential(x); }
int get_size(int x) { return size[root(x)]; }
[[nodiscard]] int forest_size() const { return num; }
};
#line 4 "test/datastructure/disjoint_set_union_with_potential/aoj_dsl_1_b.test.cpp"
#include <iostream>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
DisjointSetUnionWithPotential<int> dsu(n);
while (q--) {
int f;
cin >> f;
if (f == 0) {
int x, y, z;
cin >> x >> y >> z;
dsu.unite(x, y, z);
} else {
int x, y;
cin >> x >> y;
if (dsu.same(x, y)) cout << dsu.difference(x, y) << endl;
else cout << '?' << endl;
}
}
return 0;
}