/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2022 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "libc/fmt/itoa.h"
#include "tool/lambda/lib/blc.h"

#define FREEBIES u"ÉqÉpÇÉÆÉ¥Ä±É¾Ê×É¯uodbÉ¹sÊnÊÊxÊz"

/* clang-format off */
#define ALPHABET                \
  u"abcdefghijklmnopqrsuvwxyz"  \
  u"Î±Î²Î³Î´ÎµÎ¶Î·Î¸Î¹Î¼Î¾ÏÏÏÏÏÏÏÏÏÏÏ"     \
  u"âµâ¶â·â¸"                       \
  u"Ð¸Ð´ÑÐ¶Ð»Ñ"                     \
  u"âââÃ¾Ã¦ÃÂ§Â£Â¥â¿"                 \
  u"ð¢ð£ð¤ð¥ð¦ð§ð¨ð©ðªð«ð¬ð­ð®ð¯ð°ð±ð²ð³ð´ðµð¶ð·ð¸ð¹ðºð»" \
  u"ððððððððððððððð ð¡ð¢ð£ð¤ð¥ð¦ð§ð¨ð©ðªð«" \
  u"ð®ð¯ð°ð±ð²ð³ð´ðµð¶ð·ð¸ð¹ðºð»ð¼ð½ð¾ð¿ðððððððð"
/* clang-format on */

static char kFalse[] = {
    ABS,     //  0: false
    ABS,     //  1: (Î» 0)
    VAR, 0,  //  2: 0
};

static char kTrue[] = {
    ABS,     //  0: true
    ABS,     //  1: (Î» 1)
    VAR, 1,  //  2: 1
};

static char kOne[] = {
    ABS,     //  4: (Î»ab.ab)
    ABS,     //  5: (Î»a.Éa)
    APP, 2,  //  6: qÉ
    VAR, 1,  //  8: q
    VAR, 0,  // 10: É
};

static char kTwo[] = {
    ABS,     // 12: (Î»ab.a(ab))
    ABS,     // 13: (Î»a.É(Éa))
    APP, 2,  // 14: q(qÉ)
    VAR, 1,  // 16: q
    APP, 2,  // 18: qÉ
    VAR, 1,  // 20: q
    VAR, 0,  // 22: É
};

static char kThree[] = {
    ABS,     // 24: (Î»ab.a(a(ab)))
    ABS,     // 25: (Î»a.É(É(Éa)))
    APP, 2,  // 26: q(q(qÉ))
    VAR, 1,  // 28: q
    APP, 2,  // 30: q(qÉ)
    VAR, 1,  // 32: q
    APP, 2,  // 34: qÉ
    VAR, 1,  // 36: q
    VAR, 0,  // 38: É
};

static char kFour[] = {
    ABS,     // 40: (Î»ab.a(a(a(ab))))
    ABS,     // 41: (Î»a.É(É(É(Éa))))
    APP, 2,  // 42: q(q(q(qÉ)))
    VAR, 1,  // 44: q
    APP, 2,  // 46: q(q(qÉ))
    VAR, 1,  // 48: q
    APP, 2,  // 50: q(qÉ)
    VAR, 1,  // 52: q
    APP, 2,  // 54: qÉ
    VAR, 1,  // 56: q
    VAR, 0,  // 58: É
};

static char kFive[] = {
    ABS,     // 60: (Î»ab.a(a(a(a(ab)))))
    ABS,     // 61: (Î»a.É(É(É(É(Éa)))))
    APP, 2,  // 62: q(q(q(q(qÉ))))
    VAR, 1,  // 64: q
    APP, 2,  // 66: q(q(q(qÉ)))
    VAR, 1,  // 68: q
    APP, 2,  // 70: q(q(qÉ))
    VAR, 1,  // 72: q
    APP, 2,  // 74: q(qÉ)
    VAR, 1,  // 76: q
    APP, 2,  // 78: qÉ
    VAR, 1,  // 80: q
    VAR, 0,  // 82: É
};

static char kSix[] = {
    ABS,     // 84: (Î»ab.a(a(a(a(a(ab))))))
    ABS,     // 85: (Î»a.É(É(É(É(É(Éa))))))
    APP, 2,  // 86: q(q(q(q(q(qÉ)))))
    VAR, 1,  // 88: q
    APP, 2,  // 90: q(q(q(q(qÉ))))
    VAR, 1,  // 92: q
    APP, 2,  // 94: q(q(q(qÉ)))
    VAR, 1,  // 96: q
    APP, 2,  // 98: q(q(qÉ))
    VAR, 1,  // 100: q
    APP, 2,  // 102: q(qÉ)
    VAR, 1,  // 104: q
    APP, 2,  // 106: qÉ
    VAR, 1,  // 108: q
    VAR, 0,  // 110: É
};

static char kSeven[] = {
    ABS,     // 112: (Î»ab.a(a(a(a(a(a(ab)))))))
    ABS,     // 113: (Î»a.É(É(É(É(É(É(Éa)))))))
    APP, 2,  // 114: q(q(q(q(q(q(qÉ))))))
    VAR, 1,  // 116: q
    APP, 2,  // 118: q(q(q(q(q(qÉ)))))
    VAR, 1,  // 120: q
    APP, 2,  // 122: q(q(q(q(qÉ))))
    VAR, 1,  // 124: q
    APP, 2,  // 126: q(q(q(qÉ)))
    VAR, 1,  // 128: q
    APP, 2,  // 130: q(q(qÉ))
    VAR, 1,  // 132: q
    APP, 2,  // 134: q(qÉ)
    VAR, 1,  // 136: q
    APP, 2,  // 138: qÉ
    VAR, 1,  // 140: q
    VAR, 0,  // 142: É
};

static char kEight[] = {
    ABS,     // 144: (Î»ab.a(a(a(a(a(a(a(ab))))))))
    ABS,     // 145: (Î»a.É(É(É(É(É(É(É(Éa))))))))
    APP, 2,  // 146: q(q(q(q(q(q(q(qÉ)))))))
    VAR, 1,  // 148: q
    APP, 2,  // 150: q(q(q(q(q(q(qÉ))))))
    VAR, 1,  // 152: q
    APP, 2,  // 154: q(q(q(q(q(qÉ)))))
    VAR, 1,  // 156: q
    APP, 2,  // 158: q(q(q(q(qÉ))))
    VAR, 1,  // 160: q
    APP, 2,  // 162: q(q(q(qÉ)))
    VAR, 1,  // 164: q
    APP, 2,  // 166: q(q(qÉ))
    VAR, 1,  // 168: q
    APP, 2,  // 170: q(qÉ)
    VAR, 1,  // 172: q
    APP, 2,  // 174: qÉ
    VAR, 1,  // 176: q
    VAR, 0,  // 178: É
};

static char kNine[] = {
    ABS,     // 180: (Î»ab.a(a(a(a(a(a(a(a(ab)))))))))
    ABS,     // 181: (Î»a.É(É(É(É(É(É(É(É(Éa)))))))))
    APP, 2,  // 182: q(q(q(q(q(q(q(q(qÉ))))))))
    VAR, 1,  // 184: q
    APP, 2,  // 186: q(q(q(q(q(q(q(qÉ)))))))
    VAR, 1,  // 188: q
    APP, 2,  // 190: q(q(q(q(q(q(qÉ))))))
    VAR, 1,  // 192: q
    APP, 2,  // 194: q(q(q(q(q(qÉ)))))
    VAR, 1,  // 196: q
    APP, 2,  // 198: q(q(q(q(qÉ))))
    VAR, 1,  // 200: q
    APP, 2,  // 202: q(q(q(qÉ)))
    VAR, 1,  // 204: q
    APP, 2,  // 206: q(q(qÉ))
    VAR, 1,  // 208: q
    APP, 2,  // 210: q(qÉ)
    VAR, 1,  // 212: q
    APP, 2,  // 214: qÉ
    VAR, 1,  // 216: q
    VAR, 0,  // 218: É
};

static char kSelf[] = {
    ABS,     //  0: Î»a.aa
    APP, 2,  //  1: ÉÉ
    VAR, 0,  //  3: É
    VAR, 0,  //  5: É
};

static char kOmega[] = {
    APP, 7,  //  0: Ï Ï
    ABS,     //  2: Ï
    APP, 2,  //  3: É É
    VAR, 0,  //  5: É
    VAR, 0,  //  7: É
    ABS,     //  9: Ï
    APP, 2,  // 10: É É
    VAR, 0,  // 12: É
    VAR, 0,  // 14: É
};

static char kIf[] = {
    ABS,     //  0: if
    ABS,     //  1: (Î»Î» 2 1 0)
    ABS,     //  2: (Î» 2 1 0)
    APP, 6,  //  3: 2 1 0
    APP, 2,  //  5: 2 1
    VAR, 2,  //  7: 2
    VAR, 1,  //  9: 1
    VAR, 0,  // 11: 0
};

static char kOr[] = {
    ABS,     // 32: Î»ab.bba
    ABS,     // 33: Î»a.aaÉ
    APP, 6,  // 34: ÉÉq
    APP, 2,  // 36: ÉÉ
    VAR, 0,  // 38: É
    VAR, 0,  // 40: É
    VAR, 1,  // 42: q
};

static char kAnd[] = {
    ABS,     // 32: Î»ab.bab
    ABS,     // 33: Î»a.aÉa
    APP, 6,  // 34: ÉqÉ
    APP, 2,  // 36: Éq
    VAR, 0,  // 38: É
    VAR, 1,  // 40: q
    VAR, 0,  // 42: É
};

static char kNot[] = {
    ABS,     // 32: Î»abc.acb
    ABS,     // 33: Î»ab.Éba
    ABS,     // 34: Î»a.qaÉ
    APP, 6,  // 35: ÉÉq
    APP, 2,  // 37: ÉÉ
    VAR, 2,  // 39: É
    VAR, 0,  // 41: É
    VAR, 1,  // 43: q
};

static char kPair[] = {
    ABS,     //  0: (Î»Î»Î» 0 2 1)
    ABS,     //  1: (Î»Î» 0 2 1)
    ABS,     //  2: (Î» 0 2 1)
    APP, 6,  //  3: 0 2 1
    APP, 2,  //  5: 0 2
    VAR, 0,  //  7: 0
    VAR, 2,  //  9: 2
    VAR, 1,  // 11: 1
};

static char kFirst[] = {
    ABS,     //  0: (Î» 0 false)
    APP, 2,  //  1: 0 false
    VAR, 0,  //  3: 0
    ABS,     //  5: false
    ABS,     //  6: (Î» 0)
    VAR, 0,  //  7: 0
};

static char kSecond[] = {
    ABS,     //  0: (Î» 0 true)
    APP, 2,  //  1: 0 true
    VAR, 0,  //  3: 0
    ABS,     //  5: true
    ABS,     //  6: (Î» 1)
    VAR, 1,  //  7: 1
};

static char kSucc[] = {
    ABS,     //  0: (Î»Î»Î» 1 (2 1 0))
    ABS,     //  1: (Î»Î» 1 (2 1 0))
    ABS,     //  2: (Î» 1 (2 1 0))
    APP, 2,  //  3: 1 (2 1 0)
    VAR, 1,  //  5: 1
    APP, 6,  //  7: 2 1 0
    APP, 2,  //  9: 2 1
    VAR, 2,  // 11: 2
    VAR, 1,  // 13: 1
    VAR, 0,  // 15: 0
};

static char kCompose[] = {
    ABS,     //  0: (Î»Î»Î» 2 (1 0))
    ABS,     //  1: (Î»Î» 2 (1 0))
    ABS,     //  2: (Î» 2 (1 0))
    APP, 2,  //  3: 2 (1 0)
    VAR, 2,  //  5: 2
    APP, 2,  //  7: 1 0
    VAR, 1,  //  9: 1
    VAR, 0,  // 11: 0
};

static char kMap[] = {
    ABS,      //  0: (Î»Î»Î»Î» 2 (compose 1 3) 0)
    ABS,      //  1: (Î»Î»Î» 2 (compose 1 3) 0)
    ABS,      //  2: (Î»Î» 2 (compose 1 3) 0)
    ABS,      //  3: (Î» 2 (compose 1 3) 0)
    APP, 25,  //  4: 2 (compose 1 3) 0
    APP, 2,   //  6: 2 (compose 1 3)
    VAR, 2,   //  8: 2
    APP, 17,  // 10: compose 1 3
    APP, 13,  // 12: compose 1
    ABS,      // 14: compose
    ABS,      // 15: (Î»Î» 2 (1 0))
    ABS,      // 16: (Î» 2 (1 0))
    APP, 2,   // 17: 2 (1 0)
    VAR, 2,   // 19: 2
    APP, 2,   // 21: 1 0
    VAR, 1,   // 23: 1
    VAR, 0,   // 25: 0
    VAR, 1,   // 27: 1
    VAR, 3,   // 29: 3
    VAR, 0,   // 31: 0
};

static char kCons[] = {
    ABS,     //  0: (Î»Î»Î»Î» 1 3 (2 1 0))
    ABS,     //  1: (Î»Î»Î» 1 3 (2 1 0))
    ABS,     //  2: (Î»Î» 1 3 (2 1 0))
    ABS,     //  3: (Î» 1 3 (2 1 0))
    APP, 6,  //  4: 1 3 (2 1 0)
    APP, 2,  //  6: 1 3
    VAR, 1,  //  8: 1
    VAR, 3,  // 10: 3
    APP, 6,  // 12: 2 1 0
    APP, 2,  // 14: 2 1
    VAR, 2,  // 16: 2
    VAR, 1,  // 18: 1
    VAR, 0,  // 20: 0
};

static char kY[] = {
    ABS,     // 32: Î»a.(Î»b.bb)(Î»b.a(bb))
    APP, 7,  // 33: (Î»a.aa)(Î»a.É(aa))
    ABS,     // 35: Î»a.aa
    APP, 2,  // 36: ÉÉ
    VAR, 0,  // 38: É
    VAR, 0,  // 40: É
    ABS,     // 42: Î»a.É(aa)
    APP, 2,  // 43: q(ÉÉ)
    VAR, 1,  // 45: q
    APP, 2,  // 47: ÉÉ
    VAR, 0,  // 49: É
    VAR, 0,  // 51: É
};

static char kYCurry[] = {
    ABS,      //  0: (Î» (Î» 1 (0 0)) (Î» 1 (0 0)))
    APP, 11,  //  1: (Î» 1 (0 0)) (Î» 1 (0 0))
    ABS,      //  3: (Î» 1 (0 0))
    APP, 2,   //  4: 1 (0 0)
    VAR, 1,   //  6: 1
    APP, 2,   //  8: 0 0
    VAR, 0,   // 10: 0
    VAR, 0,   // 12: 0
    ABS,      // 14: (Î» 1 (0 0))
    APP, 2,   // 15: 1 (0 0)
    VAR, 1,   // 17: 1
    APP, 2,   // 19: 0 0
    VAR, 0,   // 21: 0
    VAR, 0,   // 23: 0
};

static char kIszero[] = {
    ABS,     // 32: Î»abc.a(Î»d.c)b
    ABS,     // 33: Î»ab.É(Î»c.b)a
    ABS,     // 34: Î»a.q(Î»b.a)É
    APP, 7,  // 35: É(Î»a.É)q
    APP, 2,  // 37: É(Î»a.É)
    VAR, 2,  // 39: É
    ABS,     // 41: Î»a.É
    VAR, 1,  // 42: q
    VAR, 1,  // 44: q
};

static char kPred[] = {
    ABS,      //  0: Î»abc.a(Î»de.e(db))(Î»d.c)(Î»d.d)
    ABS,      //  1: Î»ab.É(Î»cd.d(ca))(Î»c.b)(Î»c.c)
    ABS,      //  2: Î»a.q(Î»bc.c(bÉ))(Î»b.a)(Î»b.b)
    APP, 21,  //  3: É(Î»ab.b(aq))(Î»a.É)(Î»a.a)
    APP, 16,  //  5: É(Î»ab.b(aq))(Î»a.É)
    APP, 2,   //  7: É(Î»ab.b(aq))
    VAR, 2,   //  9: É
    ABS,      // 11: Î»ab.b(aq)
    ABS,      // 12: Î»a.a(ÉÉ)
    APP, 2,   // 13: É(qp)
    VAR, 0,   // 15: É
    APP, 2,   // 17: qp
    VAR, 1,   // 19: q
    VAR, 3,   // 21: p
    ABS,      // 23: Î»a.É
    VAR, 1,   // 24: q
    ABS,      // 26: Î»a.a
    VAR, 0,   // 27: É
};

static char kXor[] = {
    ABS,      // 32: Î»ab.a(Î»cd.bdc)b
    ABS,      // 33: Î»a.É(Î»bc.acb)a
    APP, 16,  // 34: q(Î»ab.Éba)É
    APP, 2,   // 36: q(Î»ab.Éba)
    VAR, 1,   // 38: q
    ABS,      // 40: Î»ab.Éba
    ABS,      // 41: Î»a.qaÉ
    APP, 6,   // 42: ÉÉq
    APP, 2,   // 44: ÉÉ
    VAR, 2,   // 46: É
    VAR, 0,   // 48: É
    VAR, 1,   // 50: q
    VAR, 0,   // 52: É
};

static char kAdd[] = {
    ABS,     // 29: Î»abcd.ac(bcd)
    ABS,     // 30: Î»abc.Éb(abc)
    ABS,     // 31: Î»ab.qa(Éab)
    ABS,     // 32: Î»a.ÉÉ(qÉa)
    APP, 6,  // 33: pq(ÉqÉ)
    APP, 2,  // 35: pq
    VAR, 3,  // 37: p
    VAR, 1,  // 39: q
    APP, 6,  // 41: ÉqÉ
    APP, 2,  // 43: Éq
    VAR, 2,  // 45: É
    VAR, 1,  // 47: q
    VAR, 0,  // 49: É
};

static char kSub[] = {
    ABS,      // 51: Î»ab.b(Î»cde.c(Î»fg.g(fd))(Î»f.e)(Î»f.f))a
    ABS,      // 52: Î»a.a(Î»bcd.b(Î»ef.f(ec))(Î»e.d)(Î»e.e))É
    APP, 33,  // 53: É(Î»abc.a(Î»de.e(db))(Î»d.c)(Î»d.d))q
    APP, 2,   // 55: É(Î»abc.a(Î»de.e(db))(Î»d.c)(Î»d.d))
    VAR, 0,   // 57: É
    ABS,      // 59: Î»abc.a(Î»de.e(db))(Î»d.c)(Î»d.d)
    ABS,      // 60: Î»ab.É(Î»cd.d(ca))(Î»c.b)(Î»c.c)
    ABS,      // 61: Î»a.q(Î»bc.c(bÉ))(Î»b.a)(Î»b.b)
    APP, 21,  // 62: É(Î»ab.b(aq))(Î»a.É)(Î»a.a)
    APP, 16,  // 64: É(Î»ab.b(aq))(Î»a.É)
    APP, 2,   // 66: É(Î»ab.b(aq))
    VAR, 2,   // 68: É
    ABS,      // 70: Î»ab.b(aq)
    ABS,      // 71: Î»a.a(ÉÉ)
    APP, 2,   // 72: É(qp)
    VAR, 0,   // 74: É
    APP, 2,   // 76: qp
    VAR, 1,   // 78: q
    VAR, 3,   // 80: p
    ABS,      // 82: Î»a.É
    VAR, 1,   // 83: q
    ABS,      // 85: Î»a.a
    VAR, 0,   // 86: É
    VAR, 1,   // 88: q
};

static char kLe[] = {
    ABS,      //  0: Î»ab.iszero(- a b)
    ABS,      //  1: Î»a.iszero(- É a)
    APP, 16,  //  2: iszero(- q É)
    ABS,      //  4: iszero
    APP, 9,   //  5: É (Î»abc.c) â¤
    APP, 2,   //  7: É (Î»abc.c)
    VAR, 0,   //  9: É
    ABS,      // 11: Î»abc.c
    ABS,      // 12: â¥
    ABS,      // 13: Î»a.a
    VAR, 0,   // 14: É
    ABS,      // 16: â¤
    ABS,      // 17: Î»a.É
    VAR, 1,   // 18: q
    APP, 43,  // 20: - q É
    APP, 39,  // 22: - q
    ABS,      // 24: -
    ABS,      // 25: Î»a.a dec É
    APP, 33,  // 26: É dec q
    APP, 2,   // 28: É dec
    VAR, 0,   // 30: É
    ABS,      // 32: dec
    ABS,      // 33: Î»ab.É (Î»cd.d(c a)) (Î»c.b) (Î»c.c)
    ABS,      // 34: Î»a.q (Î»bc.c(b É)) (Î»b.a) (Î»b.b)
    APP, 21,  // 35: É (Î»ab.b(a q)) (Î»a.É) (Î»a.a)
    APP, 16,  // 37: É (Î»ab.b(a q)) (Î»a.É)
    APP, 2,   // 39: É (Î»ab.b(a q))
    VAR, 2,   // 41: É
    ABS,      // 43: Î»ab.b(a q)
    ABS,      // 44: Î»a.a(É É)
    APP, 2,   // 45: É(q p)
    VAR, 0,   // 47: É
    APP, 2,   // 49: q p
    VAR, 1,   // 51: q
    VAR, 3,   // 53: p
    ABS,      // 55: Î»a.É
    VAR, 1,   // 56: q
    ABS,      // 58: Î»a.a
    VAR, 0,   // 59: É
    VAR, 1,   // 61: q
    VAR, 1,   // 63: q
    VAR, 0,   // 65: É
};

static char kEq[] = {
    ABS,      //  0: Î»ab.â§(â¤ a b)(â¤ b a)
    ABS,      //  1: Î»a.â§(â¤ É a)(â¤ a É)
    APP, 89,  //  2: â§(â¤ q É)(â¤ É q)
    APP, 12,  //  4: â§(â¤ q É)
    ABS,      //  6: â§
    ABS,      //  7: Î»a.É a É
    APP, 6,   //  8: q É q
    APP, 2,   // 10: q É
    VAR, 1,   // 12: q
    VAR, 0,   // 14: É
    VAR, 1,   // 16: q
    APP, 71,  // 18: â¤ q É
    APP, 67,  // 20: â¤ q
    ABS,      // 22: â¤
    ABS,      // 23: Î»a.iszero(- É a)
    APP, 16,  // 24: iszero(- q É)
    ABS,      // 26: iszero
    APP, 9,   // 27: É (Î»abc.c) â¤
    APP, 2,   // 29: É (Î»abc.c)
    VAR, 0,   // 31: É
    ABS,      // 33: Î»abc.c
    ABS,      // 34: â¥
    ABS,      // 35: Î»a.a
    VAR, 0,   // 36: É
    ABS,      // 38: â¤
    ABS,      // 39: Î»a.É
    VAR, 1,   // 40: q
    APP, 43,  // 42: - q É
    APP, 39,  // 44: - q
    ABS,      // 46: -
    ABS,      // 47: Î»a.a dec É
    APP, 33,  // 48: É dec q
    APP, 2,   // 50: É dec
    VAR, 0,   // 52: É
    ABS,      // 54: dec
    ABS,      // 55: Î»ab.É (Î»cd.d(c a)) (Î»c.b) (Î»c.c)
    ABS,      // 56: Î»a.q (Î»bc.c(b É)) (Î»b.a) (Î»b.b)
    APP, 21,  // 57: É (Î»ab.b(a q)) (Î»a.É) (Î»a.a)
    APP, 16,  // 59: É (Î»ab.b(a q)) (Î»a.É)
    APP, 2,   // 61: É (Î»ab.b(a q))
    VAR, 2,   // 63: É
    ABS,      // 65: Î»ab.b(a q)
    ABS,      // 66: Î»a.a(É É)
    APP, 2,   // 67: É(q p)
    VAR, 0,   // 69: É
    APP, 2,   // 71: q p
    VAR, 1,   // 73: q
    VAR, 3,   // 75: p
    ABS,      // 77: Î»a.É
    VAR, 1,   // 78: q
    ABS,      // 80: Î»a.a
    VAR, 0,   // 81: É
    VAR, 1,   // 83: q
    VAR, 1,   // 85: q
    VAR, 0,   // 87: É
    VAR, 1,   // 89: q
    VAR, 0,   // 91: É
    APP, 71,  // 93: â¤ É q
    APP, 67,  // 95: â¤ É
    ABS,      // 97: â¤
    ABS,      // 98: Î»a.iszero(- É a)
    APP, 16,  // 99: iszero(- q É)
    ABS,      // 101: iszero
    APP, 9,   // 102: É (Î»abc.c) â¤
    APP, 2,   // 104: É (Î»abc.c)
    VAR, 0,   // 106: É
    ABS,      // 108: Î»abc.c
    ABS,      // 109: â¥
    ABS,      // 110: Î»a.a
    VAR, 0,   // 111: É
    ABS,      // 113: â¤
    ABS,      // 114: Î»a.É
    VAR, 1,   // 115: q
    APP, 43,  // 117: - q É
    APP, 39,  // 119: - q
    ABS,      // 121: -
    ABS,      // 122: Î»a.a dec É
    APP, 33,  // 123: É dec q
    APP, 2,   // 125: É dec
    VAR, 0,   // 127: É
    ABS,      // 129: dec
    ABS,      // 130: Î»ab.É (Î»cd.d(c a)) (Î»c.b) (Î»c.c)
    ABS,      // 131: Î»a.q (Î»bc.c(b É)) (Î»b.a) (Î»b.b)
    APP, 21,  // 132: É (Î»ab.b(a q)) (Î»a.É) (Î»a.a)
    APP, 16,  // 134: É (Î»ab.b(a q)) (Î»a.É)
    APP, 2,   // 136: É (Î»ab.b(a q))
    VAR, 2,   // 138: É
    ABS,      // 140: Î»ab.b(a q)
    ABS,      // 141: Î»a.a(É É)
    APP, 2,   // 142: É(q p)
    VAR, 0,   // 144: É
    APP, 2,   // 146: q p
    VAR, 1,   // 148: q
    VAR, 3,   // 150: p
    ABS,      // 152: Î»a.É
    VAR, 1,   // 153: q
    ABS,      // 155: Î»a.a
    VAR, 0,   // 156: É
    VAR, 1,   // 158: q
    VAR, 1,   // 160: q
    VAR, 0,   // 162: É
    VAR, 0,   // 164: É
    VAR, 1,   // 166: q
};

static int termcmp(const int* p, const char* q, size_t n) {
  int c;
  size_t i;
  for (i = 0; i < n; ++i) {
    if ((c = p[i] - q[i])) {
      return c;
    }
  }
  return 0;
}

void PrintVar(int i, FILE* f) {
  char ibuf[22];
  switch (style) {
    case 0:
      FormatInt64(ibuf, i);
      fputs(ibuf, f);
      break;
    case 1:
      if (0 <= i && i < sizeof(ALPHABET) / sizeof(*ALPHABET) - 1) {
        fputwc(ALPHABET[i], f);
      } else if (i < 0 && ~i < sizeof(FREEBIES) / sizeof(*FREEBIES) - 1) {
        fputwc(FREEBIES[~i], f);
      } else {
        ibuf[0] = '?';
        FormatInt64(ibuf + 1, i);
        fputs(ibuf, f);
      }
      break;
    default:
      do {
        fputc('1', f);
      } while (i-- > 0);
      fputc('0', f);
      break;
  }
}

void PrintDebruijn(int x, int head, int depth, FILE* f) {
  char ibuf[22];
  if (0 <= x && x < TERMS) {
    if (mem[x] == ABS) {
      if (!noname) {
        if (x == 14) {
          fputs("put", f);
          return;
        }
        if (x + sizeof(kTrue) / sizeof(*kTrue) <= end &&
            !termcmp(mem + x, kTrue, sizeof(kTrue))) {
          if (asciiname) {
            fputs("true", f);
          } else {
            fputs("â¤", f);
          }
          return;
        }
        if (x + sizeof(kFalse) / sizeof(*kFalse) <= end &&
            !termcmp(mem + x, kFalse, sizeof(kFalse))) {
          if (asciiname) {
            fputs("false", f);
          } else {
            fputs("â¥", f);
          }
          return;
        }
        if (x + sizeof(kOmega) / sizeof(*kOmega) <= end &&
            !termcmp(mem + x, kOmega, sizeof(kOmega))) {
          if (asciiname) {
            fputs("omega", f);
          } else {
            fputs("Î©", f);
          }
          return;
        }
        if (x + sizeof(kSelf) / sizeof(*kSelf) <= end &&
            !termcmp(mem + x, kSelf, sizeof(kSelf))) {
          if (asciiname) {
            fputs("omega", f);
          } else {
            fputs("Ï", f);
          }
          return;
        }
        if (x + sizeof(kY) / sizeof(*kY) <= end &&
            !termcmp(mem + x, kY, sizeof(kY))) {
          fputs("Y", f);
          return;
        }
        if (x + sizeof(kYCurry) / sizeof(*kYCurry) <= end &&
            !termcmp(mem + x, kYCurry, sizeof(kYCurry))) {
          fputs("Y", f);
          return;
        }
        if (x + sizeof(kIf) / sizeof(*kIf) <= end &&
            !termcmp(mem + x, kIf, sizeof(kIf))) {
          fputs("if", f);
          return;
        }
        if (x + sizeof(kPair) / sizeof(*kPair) <= end &&
            !termcmp(mem + x, kPair, sizeof(kPair))) {
          fputs("pair", f);
          return;
        }
        if (x + sizeof(kNot) / sizeof(*kNot) <= end &&
            !termcmp(mem + x, kNot, sizeof(kNot))) {
          if (asciiname) {
            fputs("not", f);
          } else {
            fputwc(L'Â¬', f);
          }
          return;
        }
        if (x + sizeof(kOr) / sizeof(*kOr) <= end &&
            !termcmp(mem + x, kOr, sizeof(kOr))) {
          if (asciiname) {
            fputs("or", f);
          } else {
            fputwc(L'â¨', f);
          }
          return;
        }
        if (x + sizeof(kAnd) / sizeof(*kAnd) <= end &&
            !termcmp(mem + x, kAnd, sizeof(kAnd))) {
          if (asciiname) {
            fputs("and", f);
          } else {
            fputwc(L'â§', f);
          }
          return;
        }
        if (x + sizeof(kXor) / sizeof(*kXor) <= end &&
            !termcmp(mem + x, kXor, sizeof(kXor))) {
          if (asciiname) {
            fputs("xor", f);
          } else {
            fputwc(L'â»', f);
          }
          return;
        }
        if (x + sizeof(kLe) / sizeof(*kLe) <= end &&
            !termcmp(mem + x, kLe, sizeof(kLe))) {
          if (asciiname) {
            fputs("le", f);
          } else {
            fputwc(L'â¤', f);
          }
          return;
        }
        if (x + sizeof(kEq) / sizeof(*kEq) <= end &&
            !termcmp(mem + x, kEq, sizeof(kEq))) {
          fputwc(L'=', f);
          return;
        }
        if (x + sizeof(kAdd) / sizeof(*kAdd) <= end &&
            !termcmp(mem + x, kAdd, sizeof(kAdd))) {
          fputs("+", f);
          return;
        }
        if (x + sizeof(kSub) / sizeof(*kSub) <= end &&
            !termcmp(mem + x, kSub, sizeof(kSub))) {
          fputs("-", f);
          return;
        }
        if (x + sizeof(kCompose) / sizeof(*kCompose) <= end &&
            !termcmp(mem + x, kCompose, sizeof(kCompose))) {
          fputs("â", f);
          return;
        }
        if (x + sizeof(kSucc) / sizeof(*kSucc) <= end &&
            !termcmp(mem + x, kSucc, sizeof(kSucc))) {
          fputs("inc", f);
          return;
        }
        if (x + sizeof(kPred) / sizeof(*kPred) <= end &&
            !termcmp(mem + x, kPred, sizeof(kPred))) {
          fputs("dec", f);
          return;
        }
        if (x + sizeof(kSecond) / sizeof(*kSecond) <= end &&
            !termcmp(mem + x, kSecond, sizeof(kSecond))) {
          fputs("second", f);
          return;
        }
        if (x + sizeof(kFirst) / sizeof(*kFirst) <= end &&
            !termcmp(mem + x, kFirst, sizeof(kFirst))) {
          fputs("first", f);
          return;
        }
        if (x + sizeof(kMap) / sizeof(*kMap) <= end &&
            !termcmp(mem + x, kMap, sizeof(kMap))) {
          fputs("map", f);
          return;
        }
        if (x + sizeof(kIszero) / sizeof(*kIszero) <= end &&
            !termcmp(mem + x, kIszero, sizeof(kIszero))) {
          fputs("iszero", f);
          return;
        }
        if (x + sizeof(kCons) / sizeof(*kCons) <= end &&
            !termcmp(mem + x, kCons, sizeof(kCons))) {
          fputs("cons", f);
          return;
        }
        if (x + sizeof(kOne) / sizeof(*kOne) <= end &&
            !termcmp(mem + x, kOne, sizeof(kOne))) {
          fputs("one", f);
          return;
        }
        if (x + sizeof(kTwo) / sizeof(*kTwo) <= end &&
            !termcmp(mem + x, kTwo, sizeof(kTwo))) {
          fputs("two", f);
          return;
        }
        if (x + sizeof(kThree) / sizeof(*kThree) <= end &&
            !termcmp(mem + x, kThree, sizeof(kThree))) {
          fputs("three", f);
          return;
        }
        if (x + sizeof(kFour) / sizeof(*kFour) <= end &&
            !termcmp(mem + x, kFour, sizeof(kFour))) {
          fputs("four", f);
          return;
        }
        if (x + sizeof(kFive) / sizeof(*kFive) <= end &&
            !termcmp(mem + x, kFive, sizeof(kFive))) {
          fputs("five", f);
          return;
        }
        if (x + sizeof(kSix) / sizeof(*kSix) <= end &&
            !termcmp(mem + x, kSix, sizeof(kSix))) {
          fputs("six", f);
          return;
        }
        if (x + sizeof(kSeven) / sizeof(*kSeven) <= end &&
            !termcmp(mem + x, kSeven, sizeof(kSeven))) {
          fputs("seven", f);
          return;
        }
        if (x + sizeof(kEight) / sizeof(*kEight) <= end &&
            !termcmp(mem + x, kEight, sizeof(kEight))) {
          fputs("eight", f);
          return;
        }
        if (x + sizeof(kNine) / sizeof(*kNine) <= end &&
            !termcmp(mem + x, kNine, sizeof(kNine))) {
          fputs("nine", f);
          return;
        }
      }
      do {
        ++x;
        if (asciiname) {
          fputc('\\', f);
        } else {
          fputwc(L'Î»', f);
        }
        if (!(0 <= x && x < TERMS)) goto Overflow;
      } while (mem[x] == ABS);
      fputc(' ', f);
    }
    if (!(0 <= (x + 1) && (x + 1) < TERMS)) goto Overflow;
    if (mem[x] == APP) {
      fputc('[', f);
      PrintDebruijn(x + 2, 1, depth, f);
      fputc(' ', f);
      PrintDebruijn(x + 2 + mem[x + 1], 0, depth, f);
      fputc(']', f);
    } else if (mem[x] == VAR) {
      if (0 <= x + 1 && x + 1 < TERMS) {
        PrintVar(mem[x + 1], f);
      } else {
        fputc(L'â¼', f);
        FormatInt64(ibuf, x);
        fputs(ibuf, f);
      }
    } else if (mem[x] == IOP) {
      if (x < 22) {
        if (mem[x + 1] & 1) {
          fputs("put", f);
        } else if (x & 1) {
          fputs("wr1", f);
        } else {
          fputs("wr0", f);
        }
      } else if (x == end) {
        fputs(asciiname ? "gro" : "â¯", f);
      } else {
        fputc(L'!', f);
        FormatInt64(ibuf, x);
        fputs(ibuf, f);
      }
    } else {
      fputc(L'!', f);
      FormatInt64(ibuf, x);
      fputs(ibuf, f);
    }
    return;
  }
Overflow:
  fputc(L'â¼', f);
  FormatInt64(ibuf, x);
  fputs(ibuf, f);
}

void PrintLambda(int x, int head, int depth, int apps, FILE* f) {
  int close = 0;
  char ibuf[22];
  if (0 <= x && x < TERMS) {
    if (mem[x] == ABS) {
      if (!noname) {
        if (x == 14) {
          if (asciiname) {
            fputs("put", f);
          } else {
            fputs("â", f);
          }
          return;
        }
        if (x + sizeof(kTrue) / sizeof(*kTrue) <= end &&
            !termcmp(mem + x, kTrue, sizeof(kTrue))) {
          if (asciiname) {
            fputs("true", f);
          } else {
            fputs("â¤", f);
          }
          return;
        }
        if (x + sizeof(kFalse) / sizeof(*kFalse) <= end &&
            !termcmp(mem + x, kFalse, sizeof(kFalse))) {
          if (asciiname) {
            fputs("false", f);
          } else {
            fputs("â¥", f);
          }
          return;
        }
        if (x + sizeof(kY) / sizeof(*kY) <= end &&
            !termcmp(mem + x, kY, sizeof(kY))) {
          fputs("Y", f);
          return;
        }
        if (x + sizeof(kYCurry) / sizeof(*kYCurry) <= end &&
            !termcmp(mem + x, kYCurry, sizeof(kYCurry))) {
          fputs("Y", f);
          return;
        }
        if (x + sizeof(kOmega) / sizeof(*kOmega) <= end &&
            !termcmp(mem + x, kOmega, sizeof(kOmega))) {
          if (asciiname) {
            fputs("OMEGA", f);
          } else {
            fputs("Î©", f);
          }
          return;
        }
        if (x + sizeof(kSelf) / sizeof(*kSelf) <= end &&
            !termcmp(mem + x, kSelf, sizeof(kSelf))) {
          if (asciiname) {
            fputs("omega", f);
          } else {
            fputs("Ï", f);
          }
          return;
        }
        if (x + sizeof(kNot) / sizeof(*kNot) <= end &&
            !termcmp(mem + x, kNot, sizeof(kNot))) {
          if (asciiname) {
            fputs("not", f);
          } else {
            fputwc(L'Â¬', f);
          }
          return;
        }
        if (x + sizeof(kOr) / sizeof(*kOr) <= end &&
            !termcmp(mem + x, kOr, sizeof(kOr))) {
          if (asciiname) {
            fputs("or", f);
          } else {
            fputwc(L'â¨', f);
          }
          return;
        }
        if (x + sizeof(kXor) / sizeof(*kXor) <= end &&
            !termcmp(mem + x, kXor, sizeof(kXor))) {
          if (asciiname) {
            fputs("xor", f);
          } else {
            fputwc(L'â»', f);
          }
          return;
        }
        if (x + sizeof(kLe) / sizeof(*kLe) <= end &&
            !termcmp(mem + x, kLe, sizeof(kLe))) {
          if (asciiname) {
            fputs("le", f);
          } else {
            fputwc(L'â¤', f);
          }
          return;
        }
        if (x + sizeof(kEq) / sizeof(*kEq) <= end &&
            !termcmp(mem + x, kEq, sizeof(kEq))) {
          fputwc(L'=', f);
          return;
        }
        if (x + sizeof(kAnd) / sizeof(*kAnd) <= end &&
            !termcmp(mem + x, kAnd, sizeof(kAnd))) {
          if (asciiname) {
            fputs("and", f);
          } else {
            fputwc(L'â§', f);
          }
          return;
        }
        if (x + sizeof(kAdd) / sizeof(*kAdd) <= end &&
            !termcmp(mem + x, kAdd, sizeof(kAdd))) {
          fputs("+", f);
          return;
        }
        if (x + sizeof(kSub) / sizeof(*kSub) <= end &&
            !termcmp(mem + x, kSub, sizeof(kSub))) {
          fputs("-", f);
          return;
        }
        if (x + sizeof(kCompose) / sizeof(*kCompose) <= end &&
            !termcmp(mem + x, kCompose, sizeof(kCompose))) {
          if (asciiname) {
            fputs("compose", f);
          } else {
            fputs("â", f);
          }
          return;
        }
        if (x + sizeof(kOne) / sizeof(*kOne) <= end &&
            !termcmp(mem + x, kOne, sizeof(kOne))) {
          fputc('1', f);
          return;
        }
        if (x + sizeof(kTwo) / sizeof(*kTwo) <= end &&
            !termcmp(mem + x, kTwo, sizeof(kTwo))) {
          fputc('2', f);
          return;
        }
        if (x + sizeof(kThree) / sizeof(*kThree) <= end &&
            !termcmp(mem + x, kThree, sizeof(kThree))) {
          fputc('3', f);
          return;
        }
        if (x + sizeof(kFour) / sizeof(*kFour) <= end &&
            !termcmp(mem + x, kFour, sizeof(kFour))) {
          fputc('4', f);
          return;
        }
        if (x + sizeof(kFive) / sizeof(*kFive) <= end &&
            !termcmp(mem + x, kFive, sizeof(kFive))) {
          fputc('5', f);
          return;
        }
        if (x + sizeof(kSix) / sizeof(*kSix) <= end &&
            !termcmp(mem + x, kSix, sizeof(kSix))) {
          fputc('6', f);
          return;
        }
        if (x + sizeof(kSeven) / sizeof(*kSeven) <= end &&
            !termcmp(mem + x, kSeven, sizeof(kSeven))) {
          fputc('7', f);
          return;
        }
        if (x + sizeof(kEight) / sizeof(*kEight) <= end &&
            !termcmp(mem + x, kEight, sizeof(kEight))) {
          fputc('8', f);
          return;
        }
        if (x + sizeof(kNine) / sizeof(*kNine) <= end &&
            !termcmp(mem + x, kNine, sizeof(kNine))) {
          fputc('9', f);
          return;
        }
        if (x + sizeof(kIf) / sizeof(*kIf) <= end &&
            !termcmp(mem + x, kIf, sizeof(kIf))) {
          fputs("if", f);
          return;
        }
        if (x + sizeof(kPair) / sizeof(*kPair) <= end &&
            !termcmp(mem + x, kPair, sizeof(kPair))) {
          fputs("pair", f);
          return;
        }
        if (x + sizeof(kSucc) / sizeof(*kSucc) <= end &&
            !termcmp(mem + x, kSucc, sizeof(kSucc))) {
          fputs("inc", f);
          return;
        }
        if (x + sizeof(kPred) / sizeof(*kPred) <= end &&
            !termcmp(mem + x, kPred, sizeof(kPred))) {
          fputs("dec", f);
          return;
        }
        if (x + sizeof(kSecond) / sizeof(*kSecond) <= end &&
            !termcmp(mem + x, kSecond, sizeof(kSecond))) {
          fputs("second", f);
          return;
        }
        if (x + sizeof(kFirst) / sizeof(*kFirst) <= end &&
            !termcmp(mem + x, kFirst, sizeof(kFirst))) {
          fputs("first", f);
          return;
        }
        if (x + sizeof(kMap) / sizeof(*kMap) <= end &&
            !termcmp(mem + x, kMap, sizeof(kMap))) {
          fputs("map", f);
          return;
        }
        if (x + sizeof(kIszero) / sizeof(*kIszero) <= end &&
            !termcmp(mem + x, kIszero, sizeof(kIszero))) {
          fputs("iszero", f);
          return;
        }
        if (x + sizeof(kCons) / sizeof(*kCons) <= end &&
            !termcmp(mem + x, kCons, sizeof(kCons))) {
          fputs("cons", f);
          return;
        }
      }
      if (apps) {
        fputc('(', f);
        close = 1;
      }
      if (asciiname) {
        fputc('\\', f);
      } else {
        fputwc(L'Î»', f);
      }
      if (safer) {
        fputwc(ALPHABET[depth++], f);
        fputc('.', f);
        PrintLambda(x + 1, head, depth, apps + 1, f);
        if (close) {
          fputc(')', f);
        }
        return;
      }
      do {
        ++x;
        fputwc(ALPHABET[depth++], f);
        if (!(0 <= x && x < TERMS)) goto Overflow;
      } while (mem[x] == ABS);
      fputc('.', f);
    }
    if (!(0 <= (x + 1) && (x + 1) < TERMS)) goto Overflow;
    if (mem[x] == VAR) {
      if (0 <= x + 1 && x + 1 < TERMS) {
        PrintVar(depth - 1 - mem[x + 1], f);
      } else {
        fputc(L'â¼', f);
        FormatInt64(ibuf, x);
        fputs(ibuf, f);
      }
    } else if (mem[x] == APP) {
      if (!close && !head) {
        fputc('(', f);
        close = 1;
      }
      PrintLambda(x + 2, 1, depth, apps + 1, f);
      if (!(x + 2 + mem[x + 1] < TERMS && mem[x + 2 + mem[x + 1]] == APP)) {
        if (safer || !noname) fputc(' ', f);
      }
      PrintLambda(x + 2 + mem[x + 1], 0, depth, apps + 1, f);
    } else if (mem[x] == IOP) {
      if (x < 22) {
        if (mem[x + 1] & 1) {
          fputs(asciiname ? "put" : "â", f);
        } else if (x & 1) {
          fputs(asciiname ? "wr1" : "ââ", f);
        } else {
          fputs(asciiname ? "wr0" : "ââ", f);
        }
      } else if (x == end) {
        fputs(asciiname ? "gro" : "â¯", f);
      } else {
        fputc(L'!', f);
        FormatInt64(ibuf, x);
        fputs(ibuf, f);
      }
    } else {
      fputc(L'!', f);
      FormatInt64(ibuf, x);
      fputs(ibuf, f);
    }
    if (close) {
      fputc(')', f);
    }
    return;
  }
Overflow:
  fputc(L'â¼', f);
  FormatInt64(ibuf, x);
  fputs(ibuf, f);
}

void PrintBinary(int x, int head, int depth, FILE* f) {
  char ibuf[22];
  if (0 <= x && x < TERMS) {
    if (mem[x] == ABS) {
      if (x == 14) {
        fputs("â", f);
        return;
      }
      do {
        ++x;
        ++depth;
        fputc('0', f);
        fputc('0', f);
        if (!(0 <= x && x < TERMS)) goto Overflow;
      } while (mem[x] == ABS);
    }
    if (!(0 <= (x + 1) && (x + 1) < TERMS)) goto Overflow;
    if (mem[x] == VAR) {
      if (0 <= x + 1 && x + 1 < TERMS) {
        PrintVar(mem[x + 1], f);
      } else {
        fputc(L'â¼', f);
        FormatInt64(ibuf, x);
        fputs(ibuf, f);
      }
    } else if (mem[x] == APP) {
      fputc('0', f);
      fputc('1', f);
      PrintBinary(x + 2, 0, 0, f);
      PrintBinary(x + 2 + mem[x + 1], 0, 0, f);
    } else if (mem[x] == IOP) {
      if (x < 22) {
        if (mem[x + 1] & 1) {
          fputs("â", f);
        } else if (x & 1) {
          fputs("ââ", f);
        } else {
          fputs("ââ", f);
        }
      } else {
        fputwc(L'â¯', f);
      }
    } else if (mem[x] == -1) {
      fputwc(L'â¯', f);
    } else {
      fputc(L'!', f);
      FormatInt64(ibuf, x);
      fputs(ibuf, f);
    }
    return;
  }
Overflow:
  fputc(L'â¼', f);
  FormatInt64(ibuf, x);
  fputs(ibuf, f);
}

void Print(int x, int head, int depth, FILE* f) {
  switch (style) {
    case 0:
      PrintDebruijn(x, head, depth, f);
      break;
    case 1:
      PrintLambda(x, head, depth, 0, f);
      break;
    default:
      PrintBinary(x, head, depth, f);
      break;
  }
}
