#include <bits/stdc++.h>
using namespace std;
template <typename T1, typename T2>
istream& operator>>(istream& in, pair<T1, T2>& t) {
  return in >> t.first >> t.second;
}
template <typename T1, typename T2>
ostream& operator<<(ostream& out, pair<T1, T2>& t) {
  return out << t.first << " " << t.second << endl;
}
template <typename T>
istream& operator>>(istream& in, vector<T>& t) {
  for (int i = 0; i < t.size(); i++) in >> t[i];
  return in;
}
template <typename T>
ostream& operator<<(ostream& out, vector<T>& t) {
  for (int i = 0; i < t.size(); i++) out << t[i] << " ";
  return out;
}
vector<int> is;
vector<vector<int>> answ;
struct G {
  map<int, int> to;
};
vector<G> g;
int dfs1(int v, int p = -1) {
  for (auto i : g[v].to) {
    g[i.first].to.erase(v);
  }
  int last = -1;
  for (auto it = g[v].to.begin(); it != g[v].to.end();) {
    auto& i = *it;
    int ans = dfs1(i.first, v);
    if (ans != -1) {
      answ.push_back({v, i.first, ans});
      int t = i.first;
      g[i.first].to.erase(ans);
      g[v].to.erase(i.first);
      it = g[v].to.upper_bound(t);
      continue;
    }
    if (last == -1) {
      last = i.first;
      it++;
    } else {
      answ.push_back({last, v, i.first});
      int t = i.first;
      g[v].to.erase(last);
      g[v].to.erase(i.first);
      it = g[v].to.upper_bound(t);
      last = -1;
      continue;
    }
  }
  return last;
}
int main() {
  int n, m;
  scanf(
      "%"
      "d",
      &n);
  scanf(
      "%"
      "d",
      &m);
  g.resize(n);
  is.resize(m);
  for (int i = 0; i < m; i++) {
    int a, b;
    scanf(
        "%"
        "d",
        &a);
    scanf(
        "%"
        "d",
        &b);
    a--;
    b--;
    g[a].to[b] = i;
    g[b].to[a] = i;
  }
  if (dfs1(0) != -1) {
    puts("No solution");
    return 0;
  }
  for (int i = 0; i < answ.size(); i++) {
    printf("%d %d %d\n", answ[i][0] + 1, answ[i][1] + 1, answ[i][2] + 1);
  }
  return 0;
}
