/*Bismillahir Rahmanir Rahim*///{
#include <bits/stdc++.h>
using namespace std;
using ll = long long; using ull = unsigned long long; using ld = long double;
const ll llinf = (1ll<<61)-1;
const double eps = 1e-6, ldeps = 1e-9;
struct Dbg {
static constexpr auto &os = cout;   // replace by cerr in interactive.
template<class C> static auto dud(C *x)->decltype(os << *x, 0);
template<class C> static char dud(...);
template<class C> typename enable_if<sizeof dud<C>(0)!=1, Dbg&>::type operator<<(const C &x) { os << x; return *this; }
template<class C> Dbg &dump(C b, C e) {
    *this << "\n[";
    int cur = 0;
    for (C i = b; i!=e and cur<26; i++, cur++) *this << ", "+2*(i==b) << *i;
    return *this << ']';
}
template<class C> typename enable_if<sizeof dud<C>(0)==1, Dbg&>::type operator<<(const C &x) {
    return dump(begin(x), end(x));
}
template<class C, size_t X> typename enable_if<!is_same<C, const char>::value, Dbg&>::type operator <<(C (&x)[X]) {
    return dump(begin(x), end(x));
}
template<class B, class C> Dbg &operator <<(const pair<B, C> &x) {
    return *this << '{' << x.first << ", " << x.second << '}';
}
template<class C, size_t Y> struct TP {
    void operator()(Dbg& os, const C &t) { TP<C, Y-1>()(os, t); os << ", " << get<Y-1>(t); }
};
template<class C> struct TP<C, 1> { void operator()(Dbg& os, const C &t) { os << get<0>(t); } };
template<class... C> Dbg& operator<<(const tuple<C...> &t) {
    os << '{', TP<decltype(t), sizeof...(C)>()(*this, t); return *this << '}';
}
Dbg &operator<<(ostream&(*x)(std::ostream&)) { os << x; return *this; }
} dbg;
void err(istringstream *iss) { delete iss; }
template<class C, class... Args> void err(istringstream *iss, const C &val, const Args &... args) {
    string name; *iss >> name; if (name.back()==',') name.pop_back();
    dbg << name << " = " << val << "; ", err(iss, args ...);
}
#define bug(args...) dbg << __LINE__ << ": ", err(new istringstream(string(#args)), args), dbg << endl
#define ASSERT(a, o, b) { if (!((a) o (b))) bug(a, b), assert((a) o (b)); }
#define sl(a) int(strlen(a))
#define sz(a) int(a.size())
int T, Q, cn;//}
const int inf = 1000000007, mxn = -1;

struct Edge
{
    int u, v, w;
    Edge(int _u, int _v, int _w) : u(_u), v(_v), w(_w) {}
};

#define test(m, p) (1&((m)>>(p)))

signed main() { ios::sync_with_stdio(0); cin.tie(NULL); cout.precision(11);

/*Kinda obvious that we should make a linear graph. Try to think of cases of 1-8 where at most 3 edges per layer
(between x and x+1) amortized. ==> Say number of paths from x to n is y, we can easily make y*2 paths from x-1 to n:
consider edges between 2 and 3 have weight 0 and 1, add 0 and 2 weighted edges between 1 to 2, in order to make double
paths. Just doubling is definitely not enough to get any number, but as we have one more edge left per layer, we can
easily add 1 more edge after doubling: this is exactly like binary number system. But we need 20 bits to make 1e6, where
we have only 19 layers. So, we need to modify a bit: If we make at least 3 edges between 19 and 20, 2*n+1 can reach 1e6:
We can give first 2 bits' (from MSB on bit) result in {n-1,n} and then reach any number using binary representation.*/

    int L, n = 20;
    cin >> L;
    vector<Edge> edges;
    int x = __builtin_clz(L)^31;
    int cur = 2+(x>0 and test(L, x-1)), u = n-2, v = n-1;
    for (int i = 0; i < cur; i++) {
        edges.emplace_back(n-1, n, i);
    }
    for (int i = x-2; i >= 0; i--, u--, v--) {
        edges.emplace_back(u, v, 0);
        edges.emplace_back(u, v, cur);
        cur <<= 1;
        if (test(L, i)) edges.emplace_back(u, n, cur), cur++;
    }
    while (v!=1) {
        edges.emplace_back(u, v, 0);
        u--, v--;
    }
    cout << n << ' ' << edges.size() << '\n';
    for (auto &i : edges) {
        cout << i.u << ' ' << i.v << ' ' << i.w << '\n';
    }
}

Measured memory_bytes: 256