#include <bits/stdc++.h>
using namespace std;
int N, M;
bool used[200000];
bool edge_used[200000];
vector<pair<int, int> > G[200000];
vector<tuple<int, int, int> > ans;
int U[200000], R[200000];
int find(int x) {
  if (U[x] == x) return x;
  return U[x] = find(U[x]);
}
void unite(int x, int y) {
  x = find(x), y = find(y);
  if (x == y) return;
  if (R[x] < R[y]) swap(x, y);
  U[y] = x;
  R[x] += R[y];
  R[y] = 0;
}
bool same(int x, int y) { return find(x) == find(y); }
bool recur(int pos, int parent) {
  if (used[pos]) return true;
  used[pos] = true;
  int dat = -1;
  for (auto t : G[pos]) {
    int e = get<0>(t);
    int edge_index = get<1>(t);
    if (edge_used[edge_index]) continue;
    edge_used[edge_index] = true;
    if (!recur(e, pos)) continue;
    if (dat != -1) {
      if (e != -1) ans.push_back(make_tuple(dat, pos, e));
      dat = -1;
    } else {
      dat = e;
    }
  }
  if (dat == -1) return true;
  if (parent != -1) ans.push_back(make_tuple(dat, pos, parent));
  return false;
}
vector<int> S[200000];
int num_edges[200000];
signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  cin >> N >> M;
  for (int i = 0; i < N; i++) U[i] = i, R[i] = 1;
  vector<pair<int, int> > edges;
  for (int i = 0; i < (M); i++) {
    int a, b;
    cin >> a >> b;
    a--, b--;
    edges.push_back(pair<int, int>(a, b));
    G[a].push_back(pair<int, int>(b, i));
    G[b].push_back(pair<int, int>(a, i));
    unite(a, b);
  }
  for (int i = 0; i < (N); i++) S[find(i)].push_back(i);
  for (pair<int, int> p : edges) num_edges[find(p.first)]++;
  for (int i = 0; i < (N); i++) {
    if (S[i].empty()) continue;
    recur(S[i][0], -1);
  }
  cout << ans.size() << "\n";
  for (int i = 0; i < (ans.size()); i++) {
    cout << get<0>(ans[i]) + 1 << " " << get<1>(ans[i]) + 1 << " "
         << get<2>(ans[i]) + 1 << "\n";
  }
  return 0;
}
