#include <iostream>

class Piece {
 public:
  int x, y, o;
  bool xff;
  Piece(int xv, int yv, int ov, bool xffv)
    : x(xv), y(yv), o(ov), xff(xffv) {}
  Piece transpose() {
    if (o == 0) return Piece(x,y,0,!xff);
    if (o == 1) return Piece(-y+1,x-1,3,xff);
    if (o == 2) return Piece(-x+2,-y,2,!xff);
    if (o == 3) return Piece(y+1,-x+1,1,xff);
  }
};

class Position {
 public:
  int x, y, o;
  Position(int xv, int yv, int ov)
    : x(xv), y(yv), o(ov) {}
  Position addPiece(Piece p) {
    if (o == 0) return Position(x+p.x,y+p.y,(o+p.o)%4);
    if (o == 1) return Position(x+p.y,y-p.x,(o+p.o)%4);
    if (o == 2) return Position(x-p.x,y-p.y,(o+p.o)%4);
    if (o == 3) return Position(x-p.y,y+p.x,(o+p.o)%4);
  }
};

class Grid {
 public:
  static const int size = 35;
  static const int dsize = size*2;
  static const int darea = dsize*dsize;
  short raw[darea];
  Grid() {
    for (int i=0;i<darea;i++) raw[i] = false;
  }
  int index(int x, int y) {
    return (x+size)*dsize+(y+size);
  }
  bool filled(int x, int y) {
    return (raw[index(x,y)]!= 0);
  }
  void set(int x, int y, short value) {
// cout << "setting " << x << " " << y << 
// " to " << (value? "true":"false") << "\n";
    raw[index(x,y)] = value;
  }
  int recurse_holecount(int x, int y) {
    if (filled(x,y)) return 0;
    raw[index(x,y)] = -1;
    int answer = 1;
    if (x>-size) answer += recurse_holecount(x-1,y);
    if (y>-size) answer += recurse_holecount(x,y-1);
    if (x<size-1) answer += recurse_holecount(x+1,y);
    if (y<size-1) answer += recurse_holecount(x,y+1);
    return answer;
  }
  int holecount() {
    int answer = recurse_holecount(-size,-size);
    for (int x=-size; x<size; x++) for (int y=-size; y<size; y++) {
      if (raw[index(x,y)] == -1) raw[index(x,y)] = 0;
    }
    return (darea-64-answer);
  }
  bool fitpiece(Position pos, Piece pie) {
    int xinc = 0; int yinc = 0; int xdest; int ydest;
    if (pos.o == 0) {
      xinc++; xdest = pie.x; ydest = pie.y;
    } else if (pos.o == 1) {
      yinc--; xdest = pie.y; ydest = -pie.x;
    } else if (pos.o == 2) {
      xinc--; xdest = -pie.x; ydest = -pie.y;
    } else if (pos.o == 3) {
      yinc++; xdest = -pie.y; ydest = pie.x;
    } 
    
    if (filled(pos.x+xinc,pos.y+yinc)) return false;
    if (pie.xff ^ (pos.o % 2 == 1)) while (xinc != xdest) {
      xinc += (xdest>xinc)? 1 : -1;
      if (filled(pos.x+xinc,pos.y+yinc)) return false;
    }
    while (yinc != ydest) {
      yinc += (ydest>yinc)? 1 : -1;
      if (filled(pos.x+xinc,pos.y+yinc)) return false;
    }
    while (xinc != xdest) {
      xinc += (xdest>xinc)? 1 : -1;
      if (filled(pos.x+xinc,pos.y+yinc)) return false;
    }
    return true;
  }
  void setpiece(Position pos, Piece pie, short value) {
    int xinc = 0; int yinc = 0; int xdest; int ydest;
    if (pos.o == 0) {
      xinc++; xdest = pie.x; ydest = pie.y;
    } else if (pos.o == 1) {
      yinc--; xdest = pie.y; ydest = -pie.x;
    } else if (pos.o == 2) {
      xinc--; xdest = -pie.x; ydest = -pie.y;
    } else if (pos.o == 3) {
      yinc++; xdest = -pie.y; ydest = pie.x;
    } 

    set(pos.x+xinc,pos.y+yinc,value);
    if (pie.xff ^ (pos.o % 2 == 1)) while (xinc != xdest) {
      xinc += (xdest>xinc)? 1 : -1;
      set(pos.x+xinc,pos.y+yinc,value);
    }
    while (yinc != ydest) {
      yinc += (ydest>yinc)? 1 : -1;
      set(pos.x+xinc,pos.y+yinc,value);
    }
    while (xinc != xdest) {
      xinc += (xdest>xinc)? 1 : -1;
      set(pos.x+xinc,pos.y+yinc,value);
    }
  }
  int area() {
    int minx=size-1, miny=size-1, maxx=-size, maxy=-size;
    for (int x=-size; x<size; x++) for (int y=-size; y<size; y++) {
      if (y >= miny && y <= maxy && x >= minx && x <= maxx) continue;
      if (!filled(x,y)) continue;
      if (y < miny) miny = y;
      if (y > maxy) maxy = y;
      if (x < minx) minx = x;
      if (x > maxx) maxx = x;
    }
   return((maxx-minx+1)*(maxy-miny+1));
  }
  void printgrid() {
    int minx=size-1, miny=size-1, maxx=-size, maxy=-size;
    for (int x=-size; x<size; x++) for (int y=-size; y<size; y++) {
      if (y >= miny && y <= maxy && x >= minx && x <= maxx) continue;
      if (!filled(x,y)) continue;
      if (y < miny) miny = y;
      if (y > maxy) maxy = y;
      if (x < minx) minx = x;
      if (x > maxx) maxx = x;
    }
    for (int y=maxy;y>=miny;y--) {
      for (int x=minx;x<=maxx;x++) {
        if (!filled(x,y))
          cout << ".";
        else {
          int value = raw[(x+size)*dsize+(y+size)];
          if (value < 13)
            cout << (char)(value-1+'A');
          else
            cout << (char)(value-13+'a');
        }
      }
      cout << "\n";
    }
  }

};

Grid ggg;
int placed[12] = { 0,0,0,0,0,0,0,0,0,0,0,0 };
// 1-12, ordinal, 13-24, transpose

  Piece pieces[12] = {
    Piece(2,-2,1,true),
    Piece(3,-3,1,true),
    Piece(4,-1,1,true),
    Piece(5,-1,1,true),
    Piece(2,-4,1,true),
    Piece(2,-1,2,true),
    Piece(3,2,3,false),
    Piece(0,3,1,false),
    Piece(2,2,0,true),
    Piece(4,-2,3,false),
    Piece(6,1,2,true),
    Piece(6,-1,2,true),
  };

int dist[12] = { 4,6,5,6,6,3,5,5,4,6,7,7 };

void longprint() {
    Position pos(0,0,0);
    for (int p=1; p<13; p++) {
      for (int piece=0; piece<12; piece++) {
        if (placed[piece] == p) {
          cout << (char)('A' + piece);
          pos = pos.addPiece(pieces[piece]);
          cout << ":" << pos.x << ":" << pos.y << ":" << pos.o << "\n";
        }
        if (placed[piece] == p+12) {
          cout << (char)('a' + piece);
          pos = pos.addPiece(pieces[piece].transpose());
          cout << ":" << pos.x << ":" << pos.y << ":" << pos.o << "\n";
        }
      }
    }
    cout << "-----\n";
}

void print() {
    for (int p=1; p<13; p++) {
      for (int piece=0; piece<12; piece++) {
        if (placed[piece] == p) cout << (char)('A' + piece);
        if (placed[piece] == p+12) cout << (char)('a' + piece);
      }
    }
    cout << "\n";
}

long solcount = 0;
int holestat[300];

void recurse(int count, Position pos, int di) {
  if (count <= 2) {
    cout << solcount << " solutions so far; calculating ..";
    print();
  }
// cout << "called: " << count << "\n";
  if (count == 12) {
// cout << "possible sol, checking\n";
    if (pos.x != 0) return;
    if (pos.y != 0) return;
    if (pos.o != 0) return;
    solcount ++;
//    if (ggg.area() < 100) {
    int hc = ggg.holecount();
    if (hc < 300) ++(holestat[hc]);
    if (hc < 6 || hc > 119) {
       cout << "holecount " << hc << " small solution:\n";
       print();
       cout << "--------\n";
       ggg.printgrid();
       cout << "--------\n";
    }
//    if (solcount % 10000 == 0) {
//       cout << solcount << " solutions so far\n";
//       print();
//       ggg.printgrid();
//    }
  } else if (di < 
       (((pos.x>0)?pos.x:(-pos.x)) + ((pos.y>0)?pos.y:(-pos.y)))) {
// cout << "too far away, returning\n";
    return;
  } else {
    for (int piece=0; piece<12; piece++) {
// cout << "trying piece " << piece << "\n";
      if (placed[piece] != 0) continue;
      if (ggg.fitpiece(pos,pieces[piece])) {
        placed[piece] = count+1;
// cout << "fit good ";
// print();
        ggg.setpiece(pos,pieces[piece],piece+1);
        recurse(count+1, pos.addPiece(pieces[piece]), di-dist[piece]);
        placed[piece] = 0;
// cout << "rolling back\n";
// print();
        ggg.setpiece(pos,pieces[piece],0);
      } 
      Piece tp = pieces[piece].transpose();
      if (ggg.fitpiece(pos,tp)) {
        placed[piece] = count+13;
// cout << "trans fit good ";
// print();
        ggg.setpiece(pos,tp,piece+13);
        recurse(count+1, pos.addPiece(tp), di-dist[piece]);
        placed[piece] = 0;
// cout << "rolling back\n";
// print();
        ggg.setpiece(pos,tp,0);
      }
    }
  }
}

int main () {
  for (int i=0;i<300;i++) holestat[i] = 0;
  placed[0] = 1;
  Position pos = Position(0,0,0);
// print();
  ggg.setpiece(pos,pieces[0],true);
  pos = pos.addPiece(pieces[0]);
  recurse(1, pos, 60);
  cout << "total solutions: " << solcount << "\n";
  for (int i=0; i<300; i++) {
    if (holestat[i]==0) continue;
    cout << holestat[i] << " solutions with " << i << " hole area\n";
  }
}
