#include <bits/stdc++.h>
using namespace std;
template <int POS, class TUPLE>
void deploy(std::ostream &os, const TUPLE &tuple) {}
template <int POS, class TUPLE, class H, class... Ts>
void deploy(std::ostream &os, const TUPLE &t) {
  os << (POS == 0 ? "" : ", ") << get<POS>(t);
  deploy<POS + 1, TUPLE, Ts...>(os, t);
}
template <class... Ts>
std::ostream &operator<<(std::ostream &os, const std::tuple<Ts...> &t) {
  os << "(";
  deploy<0, std::tuple<Ts...>, Ts...>(os, t);
  os << ")";
  return os;
}
template <class T>
std::ostream &operator<<(std::ostream &os, std::vector<T> &v) {
  int remain = v.size();
  os << "{";
  for (auto e : v) os << e << (--remain == 0 ? "" : ", ");
  os << "}";
  return os;
}
template <class T>
std::ostream &operator<<(std::ostream &os, std::set<T> &v) {
  int remain = v.size();
  os << "{";
  for (auto e : v) os << e << (--remain == 0 ? "" : ", ");
  os << "}";
  return os;
}
template <class T>
std::ostream &operator<<(std::ostream &os, std::queue<T> &q) {
  os << "{";
  for (; !q.empty(); q.pop()) {
    os << q.front() << (q.size() != 1 ? ", " : "");
  }
  os << "}";
  return os;
}
template <class T>
std::ostream &operator<<(std::ostream &os, std::priority_queue<T> &q) {
  os << "{";
  for (; !q.empty(); q.pop()) {
    os << q.top() << (q.size() != 1 ? ", " : "");
  }
  os << "}";
  return os;
}
template <class T, class K>
std::ostream &operator<<(std::ostream &os, std::pair<T, K> &p) {
  os << "(" << p.first << ", " << p.second << ")";
  return os;
}
template <class T, class K>
std::ostream &operator<<(std::ostream &os, std::map<T, K> &mp) {
  int remain = mp.size();
  os << "{";
  for (auto e : mp)
    os << "(" << e.first << " -> " << e.second << ")"
       << (--remain == 0 ? "" : ", ");
  os << "}";
  return os;
}
template <class T, class K>
std::ostream &operator<<(std::ostream &os, std::unordered_map<T, K> &mp) {
  int remain = mp.size();
  os << "{";
  for (auto e : mp)
    os << "(" << e.first << " -> " << e.second << ")"
       << (--remain == 0 ? "" : ", ");
  os << "}";
  return os;
}
const int64_t MAX = 100500;
bool used[MAX];
bool edge_used[MAX];
vector<tuple<int, int>> edges[MAX];
vector<tuple<int, int, int>> ans;
bool recur(int64_t pos, int64_t parent) {
  if (used[pos]) return true;
  used[pos] = true;
  int dat = -1;
  for (auto t : edges[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) {
      ans.push_back(make_tuple(dat, pos, e));
      dat = -1;
    } else {
      dat = e;
    }
  }
  if (dat == -1) return true;
  ans.push_back(make_tuple(dat, pos, parent));
  return false;
}
int main() {
  memset(used, false, sizeof(used));
  memset(edge_used, false, sizeof(used));
  int n, m;
  scanf("%d %d", &n, &m);
  for (int64_t i = 0; i < ((int64_t)(m)); ++i) {
    int a, b;
    scanf("%d %d", &a, &b);
    edges[a].push_back(make_tuple(b, i));
    edges[b].push_back(make_tuple(a, i));
  }
  if (m % 2) {
    printf("No solution\n");
    return 0;
  }
  recur(1, -1);
  for (int64_t i = 0; i < ((int64_t)(((int64_t)((ans).size())))); ++i)
    printf("%d %d %d\n", get<0>(ans[i]), get<1>(ans[i]), get<2>(ans[i]));
}
