#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using cd = complex<double>;
constexpr int maxn = 1e5 + 100;
constexpr int maxval = 1 << 17;
constexpr int mod[2] = {(int)1e9 + 7, (int)1e9 + 9};
constexpr int modular_inverse[2] = {742744451, 54252625};
int tt[maxn] = {};
bool done[maxn] = {};
vector<int> vec[maxn];
map<int, int> edges[maxn];
vector<vector<int>> choices;
void dfs(int curr) {
  for (auto next : vec[curr]) {
    if (next == tt[curr] || done[next])
      continue;
    else if (tt[next]) {
      choices.emplace_back(1, edges[curr][next]);
      for (int i = curr; i != next; i = tt[i]) {
        choices.back().push_back(edges[i][tt[i]]);
      }
    } else {
      tt[next] = curr;
      dfs(next);
    }
  }
  done[curr] = true;
}
template <int M>
static void fast_walsh_hadamard_transform(int a[maxval]) {
  for (int h = 1; h < maxval; h *= 2) {
    for (int i = 0; i < maxval; i += 2 * h) {
      for (int j = i; j < i + h; ++j) {
        int x = a[j], y = a[j + h];
        a[j] = (x + y) % M;
        a[j + h] = (x - y + M) % M;
      }
    }
  }
}
template <int M, int inv>
static void xor_convo(int a[maxval], int b[maxval]) {
  fast_walsh_hadamard_transform<M>(a);
  fast_walsh_hadamard_transform<M>(b);
  for (int i = 0; i < maxval; ++i) a[i] = ((ll)a[i] * b[i]) % M;
  fast_walsh_hadamard_transform<M>(a);
  for (int i = 0; i < maxval; ++i) a[i] = ((ll)a[i] * inv) % M;
}
int v[2][maxval], tmp[2][maxval];
pair<int, int> find_ret() {
  v[0][0] = 1;
  v[1][0] = 1;
  for (const auto& ch : choices) {
    memset(tmp, 0, sizeof(tmp));
    for (auto x : ch) tmp[0][x]++, tmp[1][x]++;
    xor_convo<mod[0], modular_inverse[0]>(v[0], tmp[0]);
    xor_convo<mod[1], modular_inverse[1]>(v[1], tmp[1]);
  }
  int ret = 0;
  while (v[0][ret] == 0 && v[1][ret] == 0) ++ret;
  return make_pair(ret, v[0][ret]);
}
int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(nullptr);
  int n, m;
  cin >> n >> m;
  choices.emplace_back(1, 0);
  for (int i = 0, x, y, z; i < m; ++i) {
    cin >> x >> y >> z;
    choices[0][0] ^= z;
    vec[x].push_back(y);
    vec[y].push_back(x);
    edges[x][y] = edges[y][x] = z;
  }
  tt[1] = 1;
  dfs(1);
  auto ret = find_ret();
  cout << ret.first << ' ' << ret.second << endl;
  return 0;
}
