#pragma GCC optimize("Ofast")
#define _USE_MATH_DEFINES
#include "bits/stdc++.h"

using namespace std;

using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using i8 = int8_t;
using i16 = int16_t;
using i32 = int32_t;
using i64 = int64_t;

using vi = vector<int>;

constexpr char newl = '\n';
constexpr double eps = 1e-10;

#define FOR(i,a,b) for (int i = (a); i < (b); i++)
#define F0R(i,b) FOR(i,0,b)
#define RFO(i,a,b) for (int i = ((b)-1); i >=(a); i--)
#define RF0(i,b) RFO(i,0,b)
#define fi first
#define se second
#define show(x) cout << #x << " = " << x << '\n';
#define rng(a) a.begin(),a.end()
#define rrng(a) a.rbegin(),a.rend()
#define sz(x) (int)(x).size()
#define YesNo {cout<<"Yes";}else{cout<<"No";}
#define YESNO {cout<<"YES";}else{cout<<"NO";}
#define v(T) vector<T>

template<typename T1, typename T2> inline void chmin(T1& a, T2 b) { if (a > b) a = b; }
template<typename T1, typename T2> inline void chmax(T1& a, T2 b) { if (a < b) a = b; }
template<class T> bool lcmp(const pair<T, T>& l, const pair<T, T>& r) {
  return l.first < r.first;
}
template<class T> istream& operator>>(istream& i, v(T)& v) {
  F0R(j, sz(v)) i >> v[j];
  return i;
}
template<class A, class B> istream& operator>>(istream& i, pair<A, B>& p) {
  return i >> p.first >> p.second;
}
template<class A, class B, class C> istream& operator>>(istream& i, tuple<A, B, C>& t) {
  return i >> get<0>(t) >> get<1>(t) >> get<2>(t);
}
template<class T> ostream& operator<<(ostream& o, const vector<T>& v) {
  F0R(i, v.size()) {
    o << v[i] << ' ';
  }
  o << newl;
  return o;
}
template<class T1, class T2> ostream& operator<<(ostream& o, const map<T1, T2>& m) {
  for (auto& p : m) {
    o << p.first << ": " << p.second << newl;
  }
  o << newl;
  return o;
}

#if 1

int N, M;
vector<vector<pair<int, int>>> G;

void solve(int L) {
  N = 2;
  int l = L;
  while ((l /= 3) > 1) {
    N++;
  }
  G.clear();
  G.resize(N);
  M = 0;
  l = L;
  RFO(i, 1, N) {
    int a = l % 3;
    while (a) {
      G[0].emplace_back(i, --l); M++;
      a--;
    }
    l /= 3;
    if (l) {
      G[i - 1].emplace_back(i, 0); M++;
      G[i - 1].emplace_back(i, l); M++;
      G[i - 1].emplace_back(i, l*2); M++;
    }
  }
}

// パスの数だけチェック
bool verify(int L) {
  int r = 0;
  auto dfs = [&](auto dfs, int v)->void {
    if (v == N - 1) {
      r++;
      return;
    }
    for (auto& p : G[v]) {
      dfs(dfs, p.first);
    }
  };
  dfs(dfs, 0);
  bool result = r == L;
  if (!result) {
    cerr << "verify failed!" << newl;
    show(L);
    show(r);
    return false;
  }
  return true;
}

// パスの数と長さをチェック
bool verify2(int L) {
  int r = 0;
  vector<bool> ls(L, false);
  auto dfs = [&](auto dfs, int v, int l)->void {
    if (v == N - 1) {
      r++;
      if (ls[l]) {
        cerr << "verify failed!" << newl;
        show(L);
        show(l);
      }
      ls[l] = true;
      return;
    }
    for (auto& p : G[v]) {
      dfs(dfs, p.first, l + p.second);
    }
  };
  dfs(dfs, 0, 0);
  bool result = r == L;
  if (!result) {
    cerr << "verify failed!" << newl;
    show(L);
    show(r);
    return false;
  }
  return true;
}

void print() {
  cout << N << ' ' << M << newl;
  F0R(i, N) {
    for (auto& p : G[i]) {
      cout << (i + 1) << ' ' << (p.first + 1) << ' ' << p.second << newl;
    }
  }
}

// INSERT ABOVE HERE
signed main() {
  cin.tie(0);
  ios_base::sync_with_stdio(false);

#if 1
  int L;
  cin >> L;
  solve(L);
  print();
#else
  FOR(L, 2, 1000001) {
    solve(L);
    if (!verify2(L)) return 0;
    if (L % 10000 == 0) show(L);
  }
#endif
}
#endif


Measured memory_bytes: 3664