#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <algorithm>
#define rep(i, l, r) for(int i = l; i <= r; i++)
#define down(i, l, r) for(int i = l; i >= r; i--)
#define MS 1234
#define MAX 1037471823
using namespace std;
struct node
{
int x, y, z;
bool operator < (const node &k) const { return z < k.z; }
} c[2345];
int n, m, h[1234][MS], t[1234], ans, now, a, o, h2[23][MS];
bool b[MS];
int Head(int x)
{
while (h[o][h[o][x]] != h[o][x]) h[o][x] = h[o][h[o][x]];
return h[o][x];
}
int Head2(int x, int o)
{
while (h2[o][h2[o][x]] != h2[o][x]) h2[o][x] = h2[o][h2[o][x]];
return h2[o][x];
}
void Search(int x, int y, int t, int o)
{
if (t == 0) { now++; return; }
rep(i, x, y-t+1) if (Head2(c[i].x, t) != Head2(c[i].y, t))
{
rep(j, 1, n) h2[t-1][j] = h2[t][j];
h2[t-1][h2[t-1][c[i].x]] = h2[t-1][c[i].y];
Search(i+1, y, t-1, o);
}
}
int main()
{
scanf("%d%d", &n, &m); ans = 1;
rep(i, 1, m) scanf("%d%d%d", &c[i].x, &c[i].y, &c[i].z);
rep(i, 1, m) b[c[i].x] = b[c[i].y] = true;
rep(i, 1, n) if (b[i] == false) { printf("0\n"); return 0; }
sort(c+1, c+1+m); c[m+1].z = MAX;
a = 0; rep(i, 1, m+1) if (c[i].z != c[i-1].z) { rep(j, a+1, i-1) c[j].z = c[a].z+1; a = i-1; }
rep(i, 1, n) h[0][i] = i;
a = o = 0; rep(i, 1, m)
{
if (c[i].z != c[i-1].z)
{
rep(j, 1, n) h[o+1][j] = h[o][j]; o++;
}
if (Head(c[i].x) != Head(c[i].y)) h[o][h[o][c[i].x]] = h[o][c[i].y], t[o]++;
}
a = 1; rep(i, 2, m+1) if (c[i].z != c[i-1].z)
{
rep(j, 1, n) h2[t[c[a].z]][j] = h[c[a].z-1][j]; now = 0;
Search(a, i-1, t[c[a].z], c[a].z-1);
a = i; ans = (ans * now) % 31011;
}
printf("%d\n", ans);
return 0;
}
I2luY2x1ZGUgPGNtYXRoPgojaW5jbHVkZSA8Y3N0cmluZz4KI2luY2x1ZGUgPGNzdGRpbz4KI2luY2x1ZGUgPGNzdGRsaWI+CiNpbmNsdWRlIDxpb3N0cmVhbT4KI2luY2x1ZGUgPGZzdHJlYW0+CiNpbmNsdWRlIDxhbGdvcml0aG0+CiAKI2RlZmluZSByZXAoaSwgbCwgcikgZm9yKGludCBpID0gbDsgaSA8PSByOyBpKyspCiNkZWZpbmUgZG93bihpLCBsLCByKSBmb3IoaW50IGkgPSBsOyBpID49IHI7IGktLSkKI2RlZmluZSBNUyAxMjM0CiNkZWZpbmUgTUFYIDEwMzc0NzE4MjMKIAp1c2luZyBuYW1lc3BhY2Ugc3RkOwogCnN0cnVjdCBub2RlCnsKICAgIGludCB4LCB5LCB6OwogICAgYm9vbCBvcGVyYXRvciA8IChjb25zdCBub2RlICZrKSBjb25zdCB7IHJldHVybiB6IDwgay56OyB9Cn0gY1syMzQ1XTsKIAppbnQgbiwgbSwgaFsxMjM0XVtNU10sIHRbMTIzNF0sIGFucywgbm93LCBhLCBvLCBoMlsyM11bTVNdOwpib29sIGJbTVNdOwogCmludCBIZWFkKGludCB4KQp7CiAgICB3aGlsZSAoaFtvXVtoW29dW3hdXSAhPSBoW29dW3hdKSBoW29dW3hdID0gaFtvXVtoW29dW3hdXTsKICAgIHJldHVybiBoW29dW3hdOwp9CiAKaW50IEhlYWQyKGludCB4LCBpbnQgbykKewogICAgd2hpbGUgKGgyW29dW2gyW29dW3hdXSAhPSBoMltvXVt4XSkgaDJbb11beF0gPSBoMltvXVtoMltvXVt4XV07CiAgICByZXR1cm4gaDJbb11beF07Cn0KIAp2b2lkIFNlYXJjaChpbnQgeCwgaW50IHksIGludCB0LCBpbnQgbykKewogICAgaWYgKHQgPT0gMCkgeyBub3crKzsgcmV0dXJuOyB9CiAgICByZXAoaSwgeCwgeS10KzEpIGlmIChIZWFkMihjW2ldLngsIHQpICE9IEhlYWQyKGNbaV0ueSwgdCkpIAogICAgewogICAgICAgIHJlcChqLCAxLCBuKSBoMlt0LTFdW2pdID0gaDJbdF1bal07CiAgICAgICAgaDJbdC0xXVtoMlt0LTFdW2NbaV0ueF1dID0gaDJbdC0xXVtjW2ldLnldOwogICAgICAgIFNlYXJjaChpKzEsIHksIHQtMSwgbyk7CiAgICB9Cn0KIAppbnQgbWFpbigpCnsKICAgIHNjYW5mKCIlZCVkIiwgJm4sICZtKTsgYW5zID0gMTsKICAgIHJlcChpLCAxLCBtKSBzY2FuZigiJWQlZCVkIiwgJmNbaV0ueCwgJmNbaV0ueSwgJmNbaV0ueik7CiAgICByZXAoaSwgMSwgbSkgYltjW2ldLnhdID0gYltjW2ldLnldID0gdHJ1ZTsKICAgIHJlcChpLCAxLCBuKSBpZiAoYltpXSA9PSBmYWxzZSkgeyBwcmludGYoIjBcbiIpOyByZXR1cm4gMDsgfSAKICAgIHNvcnQoYysxLCBjKzErbSk7IGNbbSsxXS56ID0gTUFYOwogICAgYSA9IDA7IHJlcChpLCAxLCBtKzEpIGlmIChjW2ldLnogIT0gY1tpLTFdLnopIHsgcmVwKGosIGErMSwgaS0xKSBjW2pdLnogPSBjW2FdLnorMTsgYSA9IGktMTsgfQogICAgIAogICAgcmVwKGksIDEsIG4pIGhbMF1baV0gPSBpOwogICAgYSA9IG8gPSAwOyByZXAoaSwgMSwgbSkKICAgIHsKICAgICAgICBpZiAoY1tpXS56ICE9IGNbaS0xXS56KQogICAgICAgIHsKICAgICAgICAgICAgcmVwKGosIDEsIG4pIGhbbysxXVtqXSA9IGhbb11bal07IG8rKzsKICAgICAgICB9CiAgICAgICAgaWYgKEhlYWQoY1tpXS54KSAhPSBIZWFkKGNbaV0ueSkpIGhbb11baFtvXVtjW2ldLnhdXSA9IGhbb11bY1tpXS55XSwgdFtvXSsrOwogICAgfQogICAgYSA9IDE7IHJlcChpLCAyLCBtKzEpIGlmIChjW2ldLnogIT0gY1tpLTFdLnopCiAgICB7CiAgICAgICAgcmVwKGosIDEsIG4pIGgyW3RbY1thXS56XV1bal0gPSBoW2NbYV0uei0xXVtqXTsgbm93ID0gMDsKICAgICAgICBTZWFyY2goYSwgaS0xLCB0W2NbYV0uel0sIGNbYV0uei0xKTsgCiAgICAgICAgYSA9IGk7IGFucyA9IChhbnMgKiBub3cpICUgMzEwMTE7CiAgICB9CiAgICBwcmludGYoIiVkXG4iLCBhbnMpOwogICAgcmV0dXJuIDA7Cn0=