#include <bits/stdc++.h>
using namespace std;
int solve() {
  int n, m;
  cin >> n >> m;
  vector<vector<int>> gr(n + 1);
  for (int i = 0; i < m; ++i) {
    int to, from;
    cin >> from >> to;
    gr[from].push_back(to);
    gr[to].push_back(from);
  }
  stack<pair<int, int>> st;
  set<int> seen;
  set<pair<int, int>> seen_edges;
  vector<int> cnt(n + 1);
  vector<tuple<int, int, int>> ans;
  int cur = 0, par = 0;
  for (int i = 1; i <= n; ++i) {
    if (seen.count(i)) continue;
    st.push({i, i});
    seen.insert(i);
    while (!st.empty()) {
      tie(cur, par) = st.top();
      st.pop();
      if (cur < 0) {
        cur *= -1;
        vector<int> h;
        for (int i = 0; i < gr[cur].size(); i++) {
          if (seen_edges.count({cur, gr[cur][i]}) || gr[cur][i] == par)
            continue;
          h.push_back(gr[cur][i]);
        }
        for (int i = 1; i < h.size(); i += 2) {
          ans.push_back({h[i - 1], cur, h[i]});
          seen_edges.insert({h[i - 1], cur});
          seen_edges.insert({cur, h[i - 1]});
          seen_edges.insert({h[i], cur});
          seen_edges.insert({cur, h[i]});
        }
        if (cur != i && h.size() % 2 == 1 && !seen_edges.count({par, cur})) {
          ans.push_back({h.back(), cur, par});
          seen_edges.insert({par, cur});
          seen_edges.insert({cur, par});
          seen_edges.insert({h.back(), cur});
          seen_edges.insert({cur, h.back()});
        }
        continue;
      }
      cnt[cur] = gr[cur].size();
      st.push({-cur, par});
      for (auto& to : gr[cur]) {
        if (seen.count(to)) {
          continue;
        }
        st.push({to, cur});
        seen.insert(to);
      }
    }
  }
  cout << ans.size() << endl;
  for (auto& t : ans) {
    int x, y, z;
    tie(x, y, z) = t;
    cout << x << ' ' << y << ' ' << z << '\n';
  }
  return 0;
}
int main() {
  ios_base::sync_with_stdio(0);
  cin.tie(0);
  cout.tie(0);
  solve();
  return 0;
}
