/* KSP 15-1-3
 * Kulovy Blesk
 *
 * Petr Baudis
 * Rocnik: 3
 * Skola: Ad Fontes
 */


/*
 * Vstup:
 * N(bytu)\n
 *
 * Vystup:
 * N(faze)\n
 * N1->N2\n * faze
 *
 * Algoritmus:
 *
 * Vstup si muzeme transponovat do listu binarniho stromu (samozrejme take
 * nemusime a muzeme to resit jinak, ovsem takhle to bude zajimavejsi ;) :
 *
 * 1  2  3  4  5  6  7  8  9 10
 *  \/    \/    \/    \/    \/
 * &2\    /&4  &6\    /&8   /&10
 *    \  /        \  /     /
 *     \/          \/     /
 *    &4\          /&8   /&10
 *       \        /     /
 *        \      /     /
 *         \    /     /
 *          \  /     /
 *           \/     /
 *          &8\    /&10
 *             \  /
 *              ~~
 *
 * Na tomto stromu si staci definovat pouze kyzenou operaci prohozeni obou
 * synu.  Zacneme odshora, nejdrive prohodime vzdy oba listy. Levy list je nyni
 * pravym listem, tedy se dostal presne do toho mista, kde ho chceme mit - za
 * to levy list je nyni pravym listem, ale my ho chceme nekde uplne jinde. Tedy
 * nastavime jako hodnotu rodice techto dvou listu adresu leveho listu (napr. 2
 * - vzdy to bude maximum z hodnot synu stromu, protoze list presouvame
 * _zprava_, kde jsou vyssi hodnoty nez vlevo). Nyni obsah teto adresy staci
 * prohodit s obsahem adresy sousedniho uzlu - v soucasnosti je prave cislo
 * opet jiz tam kde chceme (na pozici n+1), ovsem leve cislo jeste ne. Tedy
 * jdeme opet o uroven vyse a pokracujeme.. Po te, co se dostaneme az ke koreni
 * stromu, je pak prvek N presunut z praveho konce stromu na levy konec stromu,
 * ovsem dal jiz stoupat nemuzeme - ale my ho vlastne chceme presunout z pozice
 * N na pozici 1, a to jsme prave udelali.
 *
 * Pri implementaci si staci teoreticky v jedne chvili udrzovat prehled pouze o
 * listech a jedne urovni stromu, tedy pametova narocnost by byla O(N). Ovsem
 * protoze listy jsou ciselna rada, nemusime si je vubec pamatovat; protoze v
 * jednotlivych uzlech stromu ukladame pouze odkazy na listy samotne (vzdy na
 * stejne pozici v rade), a uzly jsou rozmistene pravidelne, nemusime si
 * pamatovat ani je - v kteremkoliv okamziku jejich hodnoty snadno zjistime.
 * Staci si tedy pamatovat aktualni hloubku. Jedina nepravidelnost rozmisteni
 * uzlu je na konci ciselne rady, pokud jeji delka neni mocninou dvou - v tom
 * pripade nam bohate staci vzit z tohoto prebytecneho segmentu jako uzel
 * referenci na maximum tohot segmentu. Pametova narocnost O(1).
 *
 * Casova narocnost je taktez O(N) : kazdy uzel stromu navstivime prave jednou,
 * vzhledem k tomu ze strom ma maximalni hloubku log N a v urcite hloubce je
 * 2^h uzlu, slozitost bude 2^0 + 2^1 + .. 2 ^ (log N), ktere pro velka N roste
 * podobne jako 2 ^ log(N); a 2 ^ log(N) muzeme zapsat jako N. Q.E.D.
 */


#include <stdio.h>
#include <stdlib.h>


int
main() {
  char inb[256];
  int len, n, num = 0;
  int skip; /* ekvivalent hloubky - intervaly rozmisteni uzlu v dane hloubce */

  fgets(inb, 256, stdin);
  len = atoi(inb);
  if (! len) {
    printf("NE\n");
    exit(1);
  }

  for (skip = 1; skip < len; skip *= 2)
    for (n = skip; n < len; n += skip * 2)
      num++,
      printf("%d <-> %d\n", n, n + skip > len ? len : n + skip);

  printf("%d\n");

  return 0;
}
