
#include <cstdio>
#include <vector>
#include <map>
#include <algorithm>

#ifndef FRAME_WIDTH
#  define FRAME_WIDTH           32
#endif
#ifndef FRAME_HEIGHT
#  define FRAME_HEIGHT          24
#endif
#ifndef FRAMES_PER_BLOCK
#  define FRAMES_PER_BLOCK      7
#endif
#ifndef CHARACTER_HEIGHT
#  define CHARACTER_HEIGHT      8
#endif
#ifndef CHRSET_FIXED
#  define CHRSET_FIXED          60
#endif
#ifndef BLOCK_SIZE
#  define BLOCK_SIZE            16384
#endif

struct Frame {
  std::vector< unsigned char > buf;
  Frame()
    : buf(FRAME_WIDTH * FRAME_HEIGHT * CHARACTER_HEIGHT, 0)
  {
  }
  bool read(std::FILE *f)
  {
    for (size_t i = 0; i < buf.size(); i++) {
      int     c = std::fgetc(f);
      if (c == EOF)
        return false;
      buf[i] = (unsigned char) c;
    }
    return true;
  }
  unsigned char& operator[](size_t n)
  {
    return *(&(buf.front()) + n);
  }
  unsigned long long character(int x, int y) const
  {
    unsigned long long  r = 0;
    for (int i = CHARACTER_HEIGHT - 1; i >= 0; i--)
      r = (r << 8) | buf[(y * CHARACTER_HEIGHT + i) * FRAME_WIDTH + x];
    return r;
  }
};

struct Character {
  unsigned char b[CHARACTER_HEIGHT];
  Character()
  {
    for (int i = 0; i < CHARACTER_HEIGHT; i++)
      b[i] = 0;
  }
  Character(unsigned long long r)
  {
    for (int i = 0; i < CHARACTER_HEIGHT; i++) {
      b[i] = (unsigned char) (r & 0xFF);
      r = r >> 8;
    }
  }
  unsigned char& operator[](size_t n)
  {
    return b[n];
  }
  operator unsigned long long() const
  {
    unsigned long long  r = 0;
    for (int i = CHARACTER_HEIGHT - 1; i >= 0; i--)
      r = (r << 8) | b[i];
    return r;
  }
  int calculateError(unsigned long long c) const
  {
    unsigned long long  d = (unsigned long long) (*this) ^ c;
    d = (d & 0x5555555555555555ULL) + ((d >> 1) & 0x5555555555555555ULL);
    d = (d & 0x3333333333333333ULL) + ((d >> 2) & 0x3333333333333333ULL);
    d = (d & 0x0F0F0F0F0F0F0F0FULL) + ((d >> 4) & 0x0F0F0F0F0F0F0F0FULL);
    d = (d & 0x00FF00FF00FF00FFULL) + ((d >> 8) & 0x00FF00FF00FF00FFULL);
    d = (d & 0x0000FFFF0000FFFFULL) + ((d >> 16) & 0x0000FFFF0000FFFFULL);
    d = (d & 0x00000000FFFFFFFFULL) + ((d >> 32) & 0x00000000FFFFFFFFULL);
    return int(d);
  }
};

struct CharactersMerged {
  int     chrCnt;
  int     totalError;
  int     cnts1[CHARACTER_HEIGHT * 8];
  CharactersMerged()
    : chrCnt(0),
      totalError(0)
  {
    for (int i = 0; i < (CHARACTER_HEIGHT * 8); i++)
      cnts1[i] = 0;
  }
  CharactersMerged(const CharactersMerged& r)
    : chrCnt(r.chrCnt),
      totalError(r.totalError)
  {
    for (int i = 0; i < (CHARACTER_HEIGHT * 8); i++)
      cnts1[i] = r.cnts1[i];
  }
  CharactersMerged& operator=(const CharactersMerged& r)
  {
    chrCnt = r.chrCnt;
    totalError = r.totalError;
    for (int i = 0; i < (CHARACTER_HEIGHT * 8); i++)
      cnts1[i] = r.cnts1[i];
    return (*this);
  }
  void updateError()
  {
    int     err = 0;
    for (int i = 0; i < (CHARACTER_HEIGHT * 8); i++) {
      int     c0 = chrCnt - cnts1[i];
      err = err + (cnts1[i] < c0 ? cnts1[i] : c0);
    }
    totalError = err;
  }
  CharactersMerged& operator+=(const CharactersMerged& r)
  {
    chrCnt += r.chrCnt;
    for (int i = 0; i < (CHARACTER_HEIGHT * 8); i++)
      cnts1[i] += r.cnts1[i];
    updateError();
    return (*this);
  }
  void addCharacter(unsigned long long c, int cnt)
  {
    chrCnt = chrCnt + cnt;
    for (int i = 0; i < (CHARACTER_HEIGHT * 8); i++) {
      if (c & 1UL)
        cnts1[i] = cnts1[i] + cnt;
      c = c >> 1;
    }
    updateError();
  }
  operator unsigned long long() const
  {
    unsigned long long  c = 0UL;
    for (int i = (CHARACTER_HEIGHT * 8) - 1; i >= 0; i--)
      c = (c << 1) | (unsigned char) (cnts1[i] >= ((chrCnt + 1) >> 1));
    return c;
  }
  int testError(const CharactersMerged& r) const
  {
    int     err = 0;
    for (int i = 0; i < (CHARACTER_HEIGHT * 8); i++) {
      int     cnt = chrCnt + r.chrCnt;
      int     c1 = cnts1[i] + r.cnts1[i];
      err = err + (c1 < (cnt - c1) ? c1 : (cnt - c1));
    }
    return (err - (totalError + r.totalError));
  }
};

static void optimizeChrSet(std::map< unsigned long long, int >& chrCnts,
                           size_t maxSize,
                           const std::vector< Character >& chrsetBase,
                           size_t fixedChrs)
{
  std::vector< CharactersMerged > tmp(fixedChrs);
  for (size_t i = 0; i < fixedChrs; i++)
    tmp[i].addCharacter((unsigned long long) chrsetBase[i], 0x00FFFFFF);
  for (std::map< unsigned long long, int >::iterator i = chrCnts.begin();
       i != chrCnts.end(); i++) {
    bool    chrFound = false;
    for (size_t j = 0; j < fixedChrs; j++) {
      if ((unsigned long long) chrsetBase[j] == i->first) {
        chrFound = true;
        break;
      }
    }
    if (!chrFound) {
      CharactersMerged  c;
      c.addCharacter(i->first, i->second);
      tmp.push_back(c);
    }
  }
  chrCnts.clear();
  size_t  n = tmp.size();
  for ( ; n > (fixedChrs + maxSize); n--) {
    size_t  bestChr0 = 0;
    size_t  bestChr1 = 0;
    int     minErr = 0x7FFFFFFF;
    for (size_t i = 0; i < n; i++) {
      for (size_t j = (i < fixedChrs ? fixedChrs : (i + 1)); j < n; j++) {
        int     err = tmp[i].testError(tmp[j]);
        if (err < minErr) {
          minErr = err;
          bestChr0 = i;
          bestChr1 = j;
          if (err < 1)
            break;
        }
      }
      if (minErr < 1)
        break;
    }
    tmp[bestChr0] += tmp[bestChr1];
    tmp[bestChr1] = tmp[n - 1];
  }
  for (size_t i = fixedChrs; i < n; i++) {
    chrCnts.insert(std::pair< unsigned long long, int >(
                       (unsigned long long) tmp[i], tmp[i].chrCnt));
  }
}

int main()
{
  std::vector< Frame >  frames;
  {
    Frame   tmp;
    while (tmp.read(stdin))
      frames.push_back(tmp);
  }
  if (frames.size() < 1)
    return -1;
  while ((frames.size() % FRAMES_PER_BLOCK) != 0) {
    Frame   tmp;
    frames.push_back(tmp);
  }

  std::map< unsigned long long, int > chrCnts;
  for (size_t i = 0; i < frames.size(); i++) {
    for (int y = 0; y < FRAME_HEIGHT; y++) {
      for (int x = 0; x < FRAME_WIDTH; x++) {
        unsigned long long  c = frames[i].character(x, y);
        if (chrCnts.find(c) == chrCnts.end())
          chrCnts.insert(std::pair< unsigned long long, int >(c, 1));
        else
          chrCnts[c]++;
      }
    }
  }
  std::vector< Character >  chrsetBase(256);
  {
    std::vector< unsigned long long > tmp1;
    std::vector< unsigned long long > tmp2;
    for (std::map< unsigned long long, int >::iterator i = chrCnts.begin();
         i != chrCnts.end(); i++) {
      tmp2.push_back(((unsigned long long) i->second << 32) | tmp1.size());
      tmp1.push_back(i->first);
    }
    std::sort(tmp2.begin(), tmp2.end());
    for (size_t i = 0; i < 256 && i < tmp2.size(); i++) {
      int     c = int(tmp2[tmp2.size() - (i + 1)] & 0xFFFFFFFFUL);
      chrsetBase[i] = Character(tmp1[c]);
      if (i < CHRSET_FIXED) {
        for (int l = 0; l < CHARACTER_HEIGHT; l++)
          std::fputc(chrsetBase[i][l], stdout);
      }
    }
    std::fflush(stdout);
  }

  std::vector< unsigned char >  outBuf1;
  std::vector< unsigned char >  outBuf2;
  std::vector< Character >  chrset(256);
  for (size_t i = 0; i < frames.size(); i++) {
    for (int k = 0; k < 256; k++)
      chrset[k] = Character((unsigned long long) chrsetBase[k]);
    std::map< unsigned long long, int > chrCnts2;
    std::vector< bool >   chrUsed(256, false);
    for (int y = 0; y < FRAME_HEIGHT; y++) {
      for (int x = 0; x < FRAME_WIDTH; x++) {
        unsigned long long  c = frames[i].character(x, y);
        if (chrCnts2.find(c) != chrCnts2.end()) {
          chrCnts2[c]++;
        }
        else {
          bool    chrFound = false;
          for (int k = 0; k < CHRSET_FIXED; k++) {
            if ((unsigned long long) chrsetBase[k] == c) {
              chrUsed[k] = true;
              chrFound = true;
              break;
            }
          }
          if (!chrFound)
            chrCnts2.insert(std::pair< unsigned long long, int >(c, 1));
        }
      }
    }
    if (chrCnts2.size() > (256 - CHRSET_FIXED)) {
      optimizeChrSet(chrCnts2, 256 - CHRSET_FIXED, chrsetBase, CHRSET_FIXED);
    }
    {
      int     lossyCnt = 0;
      for (std::map< unsigned long long, int >::iterator j = chrCnts2.begin();
           j != chrCnts2.end(); j++) {
        bool    chrFound = false;
        for (int y = 0; y < FRAME_HEIGHT; y++) {
          for (int x = 0; x < FRAME_WIDTH; x++) {
            unsigned long long  c = frames[i].character(x, y);
            if (c == j->first) {
              chrFound = true;
              break;
            }
          }
          if (chrFound)
            break;
        }
        if (!chrFound)
          lossyCnt++;
      }
      for (int y = 0; y < FRAME_HEIGHT; y++) {
        for (int x = 0; x < FRAME_WIDTH; x++) {
          unsigned long long  c = frames[i].character(x, y);
          std::map< unsigned long long, int >::iterator j = chrCnts2.find(c);
          if (j == chrCnts2.end())
            continue;
          chrCnts2.erase(j);
          bool    chrFound = false;
          for (int k = 0; k < (256 - lossyCnt); k++) {
            if ((unsigned long long) chrset[k] == c) {
              chrFound = true;
              chrUsed[k] = true;
              break;
            }
          }
          if (!chrFound) {
            for (int k = 255 - lossyCnt; k >= CHRSET_FIXED; k--) {
              if (!chrUsed[k]) {
                chrUsed[k] = true;
                chrset[k] = Character(c);
                break;
              }
            }
          }
        }
      }
      for (int k = 255; chrCnts2.size() > 0; k--) {
        std::map< unsigned long long, int >::iterator minChr = chrCnts2.begin();
        for (std::map< unsigned long long, int >::iterator j = chrCnts2.begin();
             j != chrCnts2.end(); j++) {
          if (j->second < minChr->second ||
              (j->second == minChr->second && j->first > minChr->first)) {
            minChr = j;
          }
        }
        chrUsed[k] = true;
        chrset[k] = Character(minChr->first);
        chrCnts2.erase(minChr);
      }
    }
    int     totalError = 0;
    int     totalErrChrs = 0;
    int     maxChrErr = 0;
    for (int y = 0; y < FRAME_HEIGHT; y++) {
      for (int x = 0; x < FRAME_WIDTH; x++) {
        unsigned long long  c = frames[i].character(x, y);
        int     bestChr = 0;
        int     minErr = 0x7FFFFFFF;
        for (int k = 0; k < 256; k++) {
          if ((unsigned long long) chrset[k] == c) {
            minErr = 0;
            bestChr = k;
            break;
          }
          int     err = chrset[k].calculateError(c);
          if (err < minErr) {
            minErr = err;
            bestChr = k;
          }
        }
        totalError += minErr;
        totalErrChrs += int(minErr > 0);
        if (minErr > maxChrErr)
          maxChrErr = minErr;
        outBuf1.push_back((unsigned char) bestChr);
      }
    }
    if (totalError > 0) {
      std::fprintf(stderr, "Frame %4d: lossy conversion: "
                           "chrs: %3d, pixels: %4d, max. pixels/chr: %2d\n",
                   int(i), totalErrChrs, totalError, maxChrErr);
    }
    for (int k = CHRSET_FIXED; k < 256; k++) {
      for (int l = 0; l < CHARACTER_HEIGHT; l++) {
        if (!((k - CHRSET_FIXED) & 1))
          outBuf2.push_back(chrset[k][l]);
        else
          outBuf2.push_back(chrset[k][(CHARACTER_HEIGHT - 1) - l]);
      }
    }
    if ((i % FRAMES_PER_BLOCK) == (FRAMES_PER_BLOCK - 1)) {
      std::fwrite(&(outBuf2.front()),
                    sizeof(unsigned char), outBuf2.size(), stdout);
      for (size_t j = (outBuf1.size() + outBuf2.size()); j < BLOCK_SIZE; j++)
        std::fputc(0, stdout);
      std::fwrite(&(outBuf1.front()),
                    sizeof(unsigned char), outBuf1.size(), stdout);
      std::fflush(stdout);
      outBuf1.clear();
      outBuf2.clear();
    }
  }

  return 0;
}

