#include <bits/stdc++.h>
int32_t n;
int32_t partition(int32_t vertex, std::vector<int32_t>* graph,
                  std::unordered_set<int64_t>& blocked,
                  std::vector<std::vector<int32_t> >& answer) {
  std::vector<int32_t> not_blocked;
  for (int32_t i = 0; i < graph[vertex].size(); i++)
    if (blocked.find(vertex * (int64_t)n + graph[vertex][i]) == blocked.end()) {
      blocked.insert(graph[vertex][i] * (int64_t)n + vertex);
      blocked.insert(vertex * (int64_t)n + graph[vertex][i]);
      not_blocked.push_back(graph[vertex][i]);
    }
  std::vector<int32_t> from_below;
  for (int32_t i = 0; i < not_blocked.size(); i++) {
    int32_t prev = partition(not_blocked[i], graph, blocked, answer);
    if (prev != -1) {
      answer.push_back({vertex, not_blocked[i], prev});
    } else {
      if (from_below.size() >= 1) {
        answer.push_back({from_below[0], vertex, not_blocked[i]});
        from_below.pop_back();
      } else
        from_below.push_back(not_blocked[i]);
    }
  }
  if (from_below.size() == 0)
    return -1;
  else
    return from_below[0];
}
int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(0);
  std::cout.tie(0);
  int32_t m;
  std::cin >> n >> m;
  std::vector<int32_t>* graph = new std::vector<int32_t>[n];
  for (int32_t i = 0; i < m; i++) {
    int32_t v1, v2;
    std::cin >> v1 >> v2;
    v1--;
    v2--;
    graph[v1].push_back(v2);
    graph[v2].push_back(v1);
    v2++;
    if (v2 == n) {
      v1++;
      v2 = v1 + 1;
    }
  }
  std::unordered_set<int64_t> blocked;
  std::vector<std::vector<int32_t> > answer;
  partition(0, graph, blocked, answer);
  if (answer.size() * 2 == m) {
    for (int32_t i = 0; i < answer.size(); i++)
      std::cout << answer[i][0] + 1 << " " << answer[i][1] + 1 << " "
                << answer[i][2] + 1 << "\n";
  } else {
    std::cout << "No solution";
  }
  return 0;
}
