#include <bits/stdc++.h>
#pragma GCC target("avx2")
#pragma GCC optimize("O3")
#pragma GCC optimize("unroll-loops")
using namespace std;
vector<long long int> graph[200010], backIn[200010], backOut[200010],
    tree[200010];
long long int dep[200010], par[200010];
void dfs(long long int i, long long int p) {
  par[i] = p;
  dep[i] = dep[p] + 1;
  for (long long int j : graph[i])
    if (j - p) {
      if (!dep[j]) {
        tree[i].push_back(j);
        dfs(j, i);
      } else if (dep[j] < dep[i]) {
        backIn[j].push_back(i);
        backOut[i].push_back(j);
      }
    }
}
vector<tuple<long long int, long long int, long long int>> triples;
long long int root;
bool fillTriples(long long int i) {
  vector<long long int> edges = backOut[i];
  for (long long int j : tree[i])
    if (!fillTriples(j)) edges.push_back(j);
  for (long long int k = 0; k + 1 < edges.size(); k += 2)
    triples.push_back({edges[k], i, edges[k + 1]});
  if ((edges.size() & 1) && i != root)
    triples.push_back({edges.back(), i, par[i]});
  return edges.size() & 1;
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  long long int n;
  cin >> n;
  long long int m;
  cin >> m;
  while (m--) {
    long long int u;
    cin >> u;
    long long int v;
    cin >> v;
    graph[u].push_back(v);
    graph[v].push_back(u);
  }
  for (long long int i = 1; i < (n + 1); ++i)
    if (!dep[i]) {
      dfs(i, i);
      root = i;
      fillTriples(i);
    }
  cout << triples.size() << '\n';
  for (auto a : triples)
    cout << get<0>(a) << ' ' << get<1>(a) << ' ' << get<2>(a) << '\n';
}
