#include <bits/stdc++.h>
using namespace std;
int getint() {
  unsigned int c;
  int x = 0;
  while (((c = getchar()) - '0') >= 10) {
    if (c == '-') return -getint();
    if (!~c) exit(0);
  }
  do {
    x = (x << 3) + (x << 1) + (c - '0');
  } while (((c = getchar()) - '0') < 10);
  return x;
}
int getstr(char* s) {
  int c, n = 0;
  while ((c = getchar()) <= ' ') {
    if (!~c) exit(0);
  }
  do {
    s[n++] = c;
  } while ((c = getchar()) > ' ');
  s[n] = 0;
  return n;
}
template <class T>
inline bool chmin(T& a, T b) {
  return a > b ? a = b, 1 : 0;
}
template <class T>
inline bool chmax(T& a, T b) {
  return a < b ? a = b, 1 : 0;
}
template <class S, class T, class U>
struct Tuple {
  S s;
  T t;
  U u;
  Tuple() {}
  Tuple(S s, T t, U u) : s(s), t(t), u(u) {}
  bool operator==(const Tuple<S, T, U>& a) const {
    return (s == a.s and t == a.t and u == a.u) ? 1 : 0;
  }
  bool operator!=(const Tuple<S, T, U>& a) const {
    return !this->operator==(a);
  }
};
template <class S>
struct Compare {
  bool operator()(const S& a, const S& b) const {
    if (a.s != b.s) return a.s < b.s;
    if (a.t != b.t) return a.t < b.t;
    if (a.u != b.u) return a.u < b.u;
    return 0;
  }
};
template <class S, class T, class U>
ostream& operator<<(ostream& os, const Tuple<S, T, U>& a) {
  os << '(' << a.s << ' ' << a.t << ' ' << a.u << ')';
  return os;
}
struct edge {
  int to, rev, used;
  edge(int to, int rev, int used) : to(to), rev(rev), used(used){};
  edge() { used = 0; }
};
int n, m;
vector<edge> graph[100100];
vector<Tuple<int, int, int> > res;
void add_edge(int u, int v) {
  int us = graph[u].size();
  int vs = graph[v].size();
  graph[u].push_back(edge(v, vs, 0));
  graph[v].push_back(edge(u, us, 0));
}
int dfs(int u, int par) {
  int i, j;
  vector<int> child;
  for (i = 0; i < graph[u].size(); i++) {
    int v = graph[u][i].to;
    if (graph[u][i].used == 1) continue;
    graph[u][i].used = 1;
    int revidx = graph[u][i].rev;
    graph[v][revidx].used = 1;
    if (v != par) {
      int remu = dfs(v, u);
      if (remu != -1) {
        res.push_back(Tuple<int, int, int>(u + 1, v + 1, remu + 1));
      } else {
        child.push_back(v);
      }
    }
  }
  for (i = 0; i + 1 < child.size(); i += 2) {
    res.push_back(Tuple<int, int, int>(child[i] + 1, u + 1, child[i + 1] + 1));
  }
  if (child.size() & 1) {
    return child.back();
  }
  return -1;
}
int main() {
  int i, j, tcc, tc = 1 << 28;
  for (tcc = 0; tcc < tc; tcc++) {
    n = getint(), m = getint();
    for (i = 0; i < m; i++) {
      int u = getint() - 1;
      int v = getint() - 1;
      add_edge(u, v);
    }
    if (m & 1) {
      puts("No solution");
      continue;
    }
    dfs(0, -1);
    for (i = 0; i < res.size(); i++) {
      cout << res[i].s << ' ' << res[i].t << ' ' << res[i].u << endl;
    }
  }
  return 0;
}
