#include <bits/stdc++.h>
using namespace std;
template <class T, class L>
inline bool smax(T &x, L y) {
  return x < y ? (x = y, 1) : 0;
}
template <class T, class L>
inline bool smin(T &x, L y) {
  return y < x ? (x = y, 1) : 0;
}
const int maxn = 1 << 17, mod = 1e9 + 7;
int n, m, q, edge[maxn], comp[maxn], h[maxn], gp, po[maxn], w[maxn];
bool bl[maxn], seen[maxn], is[maxn];
vector<int> g[maxn];
pair<int, int> e[maxn];
int hi(int v = 0, int p = -1) {
  int ret = h[v];
  seen[v] = 1;
  for (auto i : g[v]) {
    int u = edge[i] ^ v;
    if (!seen[u]) {
      h[u] = h[v] + 1;
      int t = hi(u, v);
      if (t == h[u]) bl[i] = 1;
      smin(ret, t);
    } else if (u != p)
      smin(ret, h[u]);
  }
  return ret;
}
void hello(int v) {
  comp[v] = gp;
  for (auto i : g[v]) {
    int u = edge[i] ^ v;
    if (!bl[i] && comp[u] == -1) hello(u);
  }
}
void dfs(int v = 0, int p = -1) {
  for (auto u : g[v])
    if (u != p) {
      dfs(u, v);
    }
}
int must, cnt[maxn];
vector<int> vec[maxn];
void build_tree() {
  fill(g, g + n, vector<int>());
  for (int i = 0; i < m; i++)
    if (bl[i]) {
      must ^= w[i];
      g[comp[e[i].first]].push_back(comp[e[i].second]),
          g[comp[e[i].second]].push_back(comp[e[i].first]);
    } else
      vec[comp[e[i].first]].push_back(w[i]);
  dfs();
}
void sdlfkjsjkf(int a[], int b[]) {
  for (int i = 0; i < maxn; i++) a[i] = (a[i] - b[i] + mod) % mod;
}
void fwht(int *data, int dim) {
  for (int len = 1; 2 * len <= dim; len <<= 1) {
    for (int i = 0; i < dim; i += 2 * len) {
      for (int j = 0; j < len; j++) {
        int a = data[i + j];
        int b = data[i + j + len];
        data[i + j] = (a + b) % mod;
        data[i + j + len] = (mod + a - b) % mod;
      }
    }
  }
}
long long poww(long long a, long long b) {
  long long ans = 1;
  while (b) {
    if (b & 1) ans = (ans * a) % mod;
    b >>= 1;
    a = (a * a) % mod;
  }
  return ans;
}
void raise(int data[]) {
  fwht(data, maxn);
  for (int i = 0; i < maxn; i++) data[i] = poww(data[i], 2);
  fwht(data, maxn);
  int inv = poww(maxn, mod - 2);
  for (int i = 0; i < maxn; i++) {
    data[i] = ((long long)data[i] * inv) % mod;
  }
}
int all[maxn];
void fftxor(int a[], int b[], int c[maxn]) {
  for (int i = 0; i < maxn; i++) all[i] = (a[i] + b[i]) % mod;
  raise(all);
  raise(a);
  raise(b);
  sdlfkjsjkf(all, a);
  sdlfkjsjkf(all, b);
  int r2 = poww(2, mod - 2);
  for (int i = 0; i < maxn; i++) all[i] = (long long)all[i] * r2 % mod;
  memcpy(c, all, maxn * 4);
}
int my[maxn], my2[maxn], cnt2[maxn];
int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for (int i = 0, v, u; i < m; i++) {
    cin >> v >> u >> w[i], v--, u--;
    edge[i] = v ^ u, e[i] = {v, u};
    g[v].push_back(i), g[u].push_back(i);
  }
  hi();
  memset(comp, -1, sizeof comp);
  for (int i = 0; i < n; i++)
    if (comp[i] == -1) {
      hello(i);
      gp++;
    }
  build_tree();
  cnt[0] = 1;
  cnt2[0] = 1;
  for_each(vec, vec + gp, [](vector<int> gp) {
    if (gp.empty()) return;
    memset(my, 0, sizeof my);
    memset(my2, 0, sizeof my2);
    int all = 0;
    for (auto x : gp) all ^= x;
    for (auto x : gp) {
      my[all ^ x]++;
      my2[all ^ x] = 1;
    }
    fftxor(cnt, my, cnt);
    fftxor(cnt2, my2, cnt2);
    for (int i = 0; i < maxn; i++)
      if (cnt2[i]) cnt2[i] = 1;
  });
  int ans = maxn, ccnt = 0;
  for (int i = 0; i < maxn; i++)
    if (cnt2[i])
      if ((must ^ i) < ans) ans = must ^ i, ccnt = cnt[i];
  cout << ans << ' ' << ccnt << '\n';
}
