// Simulated annealing for n-card layouts with many sets, over the whole
// 81-card deck (AG(4,3)). A lower-bound search: whatever it finds is a real
// layout, recounted from scratch before it's printed.
//
//   cc -O2 -o anneal anneal.c && ./anneal <n> <seconds> <seed>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <time.h>

static int third[81][81];
static int pairs[81][40][2];  // the 40 lines through each card, as its two partners

static uint64_t rng = 88172645463325252ull;
static inline uint64_t next(void) { rng ^= rng << 13; rng ^= rng >> 7; rng ^= rng << 17; return rng; }
static inline double unit(void) { return (next() >> 11) * (1.0 / 9007199254740992.0); }

static int count(const char* in) {
  int c = 0;
  for (int a = 0; a < 81; a++)
    for (int b = a + 1; b < 81; b++) {
      int r = third[a][b];
      if (r > b && in[a] && in[b] && in[r]) c++;
    }
  return c;
}

int main(int argc, char** argv) {
  if (argc < 4) { fprintf(stderr, "usage: anneal n seconds seed\n"); return 1; }
  int n = atoi(argv[1]);
  double secs = atof(argv[2]);
  rng ^= (uint64_t)atoll(argv[3]) * 0x9E3779B97F4A7C15ull;
  for (int a = 0; a < 81; a++)
    for (int b = 0; b < 81; b++) {
      int c = 0, pa = a, pb = b, w = 1;
      for (int i = 0; i < 4; i++, pa /= 3, pb /= 3, w *= 3) c += ((6 - pa % 3 - pb % 3) % 3) * w;
      third[a][b] = c;
    }
  for (int p = 0; p < 81; p++) {
    int k = 0;
    for (int q = 0; q < 81; q++)
      if (q != p && q < third[p][q]) { pairs[p][k][0] = q; pairs[p][k][1] = third[p][q]; k++; }
  }
  char in[81], best_in[81];
  int best = -1;
  clock_t t0 = clock();
  while ((double)(clock() - t0) / CLOCKS_PER_SEC < secs) {
    // a random start
    int pts[81];
    for (int i = 0; i < 81; i++) pts[i] = i;
    for (int i = 80; i > 0; i--) { int j = next() % (i + 1); int t = pts[i]; pts[i] = pts[j]; pts[j] = t; }
    for (int i = 0; i < 81; i++) in[i] = 0;
    for (int i = 0; i < n; i++) in[pts[i]] = 1;
    int L = count(in);
    double T = 2.0;
    for (long it = 0; it < 4000000; it++) {
      int p = next() % 81, q = next() % 81;
      if (!in[p] || in[q]) continue;
      // remove p, add q
      int lose = 0, gain = 0;
      for (int k = 0; k < 40; k++) if (in[pairs[p][k][0]] && in[pairs[p][k][1]]) lose++;
      in[p] = 0;
      for (int k = 0; k < 40; k++) if (in[pairs[q][k][0]] && in[pairs[q][k][1]]) gain++;
      int d = gain - lose;
      if (d >= 0 || unit() < exp(d / T)) { in[q] = 1; L += d; }
      else in[p] = 1;
      if (L > best) { best = L; for (int i = 0; i < 81; i++) best_in[i] = in[i]; }
      if ((it & 4095) == 0) T *= 0.995;
    }
  }
  if (count(best_in) != best) { fprintf(stderr, "count mismatch\n"); return 1; }
  printf("%d %d ", n, best);
  for (int i = 0; i < 81; i++) if (best_in[i]) printf(" %d%d%d%d", i % 3, i / 3 % 3, i / 9 % 3, i / 27);
  printf("\n");
  return 0;
}
