#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <pthread.h>
#endif

//
// Globale Konstanten und Macros
//
#ifdef _WIN32
typedef __int64 BitVector;
#else
typedef long long BitVector;
#endif

struct BitVectorNumb
{
   BitVector v;
   int       n_bits;
   int       bits[60];
};

#define NUMBER_TRIPEL 25000000
#ifndef FIRST_TRIPEL_NUM
#define FIRST_TRIPEL_NUM 30
#endif
#define NUMBER_PRESELECT 15000000
#ifndef PRESELECT_LEVEL
#define PRESELECT_LEVEL 5
#endif
#ifndef MAX_THREAD
#define MAX_THREAD 8 
#endif

#define FIT(v1,v2)        !((v1) & (v2))
#define ADD(v1,v2)        ((v1) |= (v2))
#define REMOVE(v1,v2)     ((v1) &= (~v2))
#define SET_BIT(v,n)      ((v) |= ((BitVector)1<<(n)))
#define SET_BITC(v,x,y,z) (SET_BIT((v),coord_to_bit((x),(y),(z))))
#define CLEAR_BIT(v,n)    ((v) &= ~((BitVector)1<<(n)))
#define GET_BIT(v,n)      ((v) & ((BitVector)1<<(n)))
#define GET_BITC(v,x,y,z) (GET_BIT((v),coord_to_bit((x),(y),(z))))
#define NEG(v)            ((~v) & (BitVector)0xfffffffffffffff)

//
// Globale Variable
//

// Mapping Tabelle fuer Bitnummern zu Koordinaten
//
// +---+---+---+---+
// | 2 | 3 | 4 | 5 |
// +---+---+---+---+
// | 1 |10 |11 | 6 |
// +---+---+---+---+
// | 0 | 9 | 8 | 7 |
// +---+---+---+---+
//
static const int tab1[60][3] = 
   {{0,0,0}, {0,0,1}, {0,0,2}, {0,1,2}, {0,2,2}, {0,3,2}, {0,3,1}, {0,3,0}, {0,2,0}, {0,1,0}, {0,1,1}, {0,2,1},
    {1,0,0}, {1,0,1}, {1,0,2}, {1,1,2}, {1,2,2}, {1,3,2}, {1,3,1}, {1,3,0}, {1,2,0}, {1,1,0}, {1,1,1}, {1,2,1}, 
    {2,0,0}, {2,0,1}, {2,0,2}, {2,1,2}, {2,2,2}, {2,3,2}, {2,3,1}, {2,3,0}, {2,2,0}, {2,1,0}, {2,1,1}, {2,2,1}, 
    {3,0,0}, {4,0,0}, {3,0,1}, {4,0,1}, {3,0,2}, {4,0,2}, {3,1,0}, {4,1,0}, {3,1,1}, {4,1,1}, {3,1,2}, {4,1,2}, 
    {3,2,0}, {4,2,0}, {3,2,1}, {4,2,1}, {3,2,2}, {4,2,2}, {3,3,0}, {4,3,0}, {3,3,1}, {4,3,1}, {3,3,2}, {4,3,2}};

// Mapping Tabelle fuer Koordinaten zu Bitnummern
static int tab2[5][4][3];

// Anzahl der Wuerfel fuer ein Teil
static const int n_cubes[12] = {5,5,6,5,5,5,5,5,4,5,5,5};

// Es werden alle Lagen eines Teiles gelistet
// Dabei werden diese Lagen nach der Nummer des
// ersten gesetzten Bits sortiert.
static BitVector part_pos[12][57][24];
static int n_part_pos[12][57];
static int last_bit_7;

// Nachbarschaftvector der Teile 
static BitVectorNumb part_neighbour[12][57][24];

// Nachbarschaftbits eines Bits
static BitVectorNumb bit_neighbour[60];

// Liste aller Tripel von Teilen, 
// deren erstes gesetztes Bit >= FIRST_TRIPEL_NUM ist
// Achtung, die Liste umfasst etwa 167 000 000 Byte
// und fuehrt zu Zeitverzoegerungen, wenn
// nicht mindestens 256 MByte Memory vorhanden sind
static BitVector *tripel;
static int n_tripel[47-FIRST_TRIPEL_NUM][47-FIRST_TRIPEL_NUM][10][10][10];
static int ind_tripel[47-FIRST_TRIPEL_NUM][47-FIRST_TRIPEL_NUM][10][10][10];

// Aus den ersten 5 Teilen erzeugte Liste 
static int n_presel;
BitVector *presel;
short *presel_parts;

// Thread Parameter
int par[MAX_THREAD][2];

// Threadloesungen
int res[MAX_THREAD];

// Zeitmessung
time_t start_time;


// Gibt die zu einer Bitnummer gehoerenden Koordinaten zurueck
void bit_to_coord(const int n, int &x, int &y, int &z)
{
   x = tab1[n][0];
   y = tab1[n][1];
   z = tab1[n][2];
}


// Gibt die den Koodrdinaten gehoerende Bitnummer zurueck
int coord_to_bit(int x, int y, int z)
{
   return tab2[x][y][z];
}

// Erzeugt ein um 180 Grad gedrehtes Teil
BitVector rot(BitVector v,int mode)
{
   int x,y,z;
   BitVector res = (BitVector)0;
   for (int i = 0; i < 60; i++)
   {
      if (GET_BIT(v,i))
      {
         bit_to_coord(i,x,y,z);
         switch(mode)
         {
            case 1: // 180 Grad Rotation um x-Achse
               y = 3 - y;
               z = 2 - z;
               break;
            case 2: // 180 Grad Rotation um y-Achse
               x = 4 - x;
               z = 2 - z;
               break;
            case 3: // 180 Grad Rotation um z-Achse
               x = 4 - x;
               y = 3 - y;
               break;
         }
         SET_BITC(res,x,y,z);
      }
   }
   return res;
}
   
// Gibt die Nummer des naechsten gesetzten Bits
// ab bit_nr zurueck
int get_next_set(BitVector v,int bit_nr)
{
   for (int i = bit_nr; i < 60; i++)
      if (GET_BIT(v,i)) return i;
         
   return -1;
}
   
// Gibt die Nummer des naechsten geloeschten Bits 
// ab bit_nr zurueck
int get_next_clear(BitVector v,int bit_nr)
{
   for (int i = bit_nr; i < 60; i++)
      if (!GET_BIT(v,i)) return i;
   return -1;     
}

// Gibt die Nummer des ersten geloeschten Bits zurueck   
int get_next_clear(BitVector v)
{
   v = ~v;
   v &= -v;
   if (v == ((BitVector)1<< 29)) return 29;
   if (v < ((BitVector)1<< 29))
   {
      if (v == ((BitVector)1<< 14)) return          14;
      if (v < ((BitVector)1<< 14))
      {
         if (v == ((BitVector)1<< 7)) return         7;
         if (v < ((BitVector)1<< 7))
         {
            if (v == ((BitVector)1<< 3)) return      3;
            if (v < ((BitVector)1<< 3))
            {
               if (v ==((BitVector)1<< 0)) return    0;
               if (v == ((BitVector)1<< 1)) return   1;
               return                                2;
            }
            else
            {
               if (v == ((BitVector)1<< 4)) return   4;
               if (v == ((BitVector)1<< 5)) return   5;
               return                                6;
            }
         }
         else
         {
            if (v == ((BitVector)1<< 10)) return    10;
            if (v < ((BitVector)1<< 10))
            {
               if (v == ((BitVector)1<< 8)) return   8;
               return                                9;
            }
            else
            {
               if (v == ((BitVector)1<< 11)) return 11;
               if (v == ((BitVector)1<< 12)) return 12;
               return                               13;
            }
         }
      }
      else
      {
         if (v == ((BitVector)1<< 21)) return       21;
         if (v < ((BitVector)1<< 21))
         {
            if (v == ((BitVector)1<< 18)) return    18;
            if (v < ((BitVector)1<< 18))
            {
               if (v == ((BitVector)1<< 15)) return 15;
               if (v == ((BitVector)1<< 16)) return 16;
               return                               17;
            }
            else
            {
               if (v == ((BitVector)1<< 19)) return 19;
               return                               20;
            }
         }
         else
         {
            if (v == ((BitVector)1<< 25)) return    25;
            if (v < ((BitVector)1<< 25))
            {
               if (v == ((BitVector)1<< 22)) return 22;
               if (v == ((BitVector)1<< 23)) return 23;
               return                               24;
            }
            else
            {
               if (v == ((BitVector)1<< 26)) return 26;
               if (v == ((BitVector)1<< 27)) return 27;
               return                               28;
            }
         }
      }
   }
   else
   {
      if (v == ((BitVector)1<< 44)) return          44;
      if (v < ((BitVector)1<< 44))
      {
         if (v == ((BitVector)1<< 37)) return       37;
         if (v < ((BitVector)1<< 37))
         {
            if (v == ((BitVector)1<< 33)) return    33;
            if (v < ((BitVector)1<< 33))
            {
               if (v ==((BitVector)1<< 30)) return  30;
               if (v == ((BitVector)1<< 31)) return 31;
               return                               32;
            }
            else
            {
               if (v == ((BitVector)1<< 34)) return 34;
               if (v == ((BitVector)1<< 35)) return 35;
               return                               36;
            }
         }
         else
         {
            if (v == ((BitVector)1<< 40)) return    40;
            if (v < ((BitVector)1<< 40))
            {
               if (v == ((BitVector)1<< 38)) return 38;
               return                               39;
            }
            else
            {
               if (v == ((BitVector)1<< 41)) return 41;
               if (v == ((BitVector)1<< 42)) return 42;
               return                               43;
            }
         }
      }
      else
      {
         if (v == ((BitVector)1<< 52)) return       52;
         if (v < ((BitVector)1<< 52))
         {
            if (v == ((BitVector)1<< 48)) return    48;
            if (v < ((BitVector)1<< 48))
            {
               if (v == ((BitVector)1<< 45)) return 45;
               if (v == ((BitVector)1<< 46)) return 46;
               return                               47;
            }
            else
            {
               if (v == ((BitVector)1<< 49)) return 49;
               if (v == ((BitVector)1<< 50)) return 50;
               return                               51;
            }
         }
         else
         {
            if (v == ((BitVector)1<< 56)) return    56;
            if (v < ((BitVector)1<< 56))
            {
               if (v == ((BitVector)1<< 53)) return 53;
               if (v == ((BitVector)1<< 54)) return 54;
               return                               55;
            }
            else
            {
               if (v == ((BitVector)1<< 57)) return 57;
               if (v == ((BitVector)1<< 58)) return 58;
               return                                59;
            }
         }
      }
   }
}

// Gibt den Nachbarschaftsbitvector zurueck
BitVector neighbour(BitVector v)
{
   BitVector res = (BitVector)0;
   int i,x,y,z;
   
   for (i = 0; i < 60; i++)
   {
      if (GET_BIT(v,i))
      {
         bit_to_coord(i,x,y,z);
         if (x < 4 && !GET_BITC(v,x+1,y,z))
            SET_BITC(res,x+1,y,z);
         if (x > 0 && !GET_BITC(v,x-1,y,z))
            SET_BITC(res,x-1,y,z);
         if (y < 3 && !GET_BITC(v,x,y+1,z))
            SET_BITC(res,x,y+1,z);
         if (y > 0 && !GET_BITC(v,x,y-1,z))
            SET_BITC(res,x,y-1,z);
         if (z < 2 && !GET_BITC(v,x,y,z+1))
            SET_BITC(res,x,y,z+1);
         if (z > 0 && !GET_BITC(v,x,y,z-1))
            SET_BITC(res,x,y,z-1);
      }
   }
   
   return res;
}
   
// Gibt die Bitbelegung als hexadezimalen string zurueck.
// (Fuer debug Zwecke)
void as_string(const BitVector v,char *str)
{
   sprintf(str,"%16.16llx",v);
   return;
}

// Druckt die Bitbelegung aus
// (Debug)
void printv(const BitVector v,FILE *fptr)
{
   char str1[20] = {"+--+--+--+--+--+"};
   char str2[20] = {"|  |  |  |  |  |"};
   char table[3][9][20];
   int i,x,y,z;

   for (i = 0; i < 3; i++)
   {
      strcpy(table[i][0],str1);
      strcpy(table[i][1],str2);
      strcpy(table[i][2],str1);
      strcpy(table[i][3],str2);
      strcpy(table[i][4],str1);
      strcpy(table[i][5],str2);
      strcpy(table[i][6],str1);
      strcpy(table[i][7],str2);
      strcpy(table[i][8],str1);
   }
   for (i = 0; i < 60; i++)
   {
      if (GET_BIT(v,i))
      {
         bit_to_coord(i,x,y,z);
         table[z][7-2*y][3*x+1] = 'x';
         table[z][7-2*y][3*x+2] = 'x';
      }
   }
   for (i = 0; i < 9; i++)
   {
      fprintf(fptr,"%s %s %s\n",table[0][i],table[1][i],table[2][i]);
   }
   fprintf(fptr,"\n");
   fflush(fptr);
}

// Berechnet das Feld der gesetzten Bitnummern
void compute_numbers(BitVectorNumb &v)
{
   v.n_bits = 0;
   for (int i = 0; i < 60; i++)
   {
      if (GET_BIT(v.v,i))
      {
         v.bits[v.n_bits] = i;
         (v.n_bits)++;
      }
   }
}

// Berechnung der Teilepositionen
void compute_part_positions()
{
//
// 0. Teil (C):
//    +--+--+
//    |     |
//    +  +--+
//    |  |
//    +  +--+
//    |     |
//    +-----+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegen
//    kann 4 Grundpositionen, so dass sich jede moegliche
//    Lage durch die Verschiebung von 12 Grundpositionen 
//    ergibt.
//

// 
// 1. Teil:
//    +--+
//    |  |
//    +  +--+--+
//    |        |
//    +--+  +--+
//       |  |
//       +--+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegt
//    8 Grundpositionen, so dass sich alle Lagen durch
//    die Verschiebung von 24 Grundpositionen ergibt.
//

//
// 2. Teil (F):
//    +--+--+
//    |     |
//    +  +--+
//    |  |
//    +  +--+
//    |     |
//    +  +--+
//    |  |
//    +--+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegt
//    8 Grundpositionen, so dass sich alle Lagen durch
//    die Verschiebung von 24 Grundpositionen ergibt.
//

//
//
// 3. Teil (T):
//    +--+--+--+
//    |        |
//    +--+  +--+
//       |  |
//       +  +
//       |  |
//       +--+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegen
//    kann 4 Grundpositionen, so dass sich jede moegliche
//    Lage durch die Verschiebung von 12 Grundpositionen 
//    ergibt.
//

//
// 4. Teil (S):
//       +--+--+
//       |     |
//       +  +--+
//       |  |
//    +--+  +
//    |     |
//    +--+--+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegen
//    kann 4 Grundpositionen, so dass sich jede moegliche
//    Lage durch die Verschiebung von 12 Grundpositionen 
//    ergibt.
//

//
// 5. Teil:
//    +--+--+
//    |     |
//    +     +
//    |     |
//    +  +--+
//    |  |
//    +--+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegt
//    8 Grundpositionen, so dass sich alle Lage durch
//    die Verschiebung von 24 Grundpositionen ergibt.
//

//
//
// 6. Teil (4 Wuerfel +1 aufgesetzt)
// 
//    Es gibt fuer jede der 3 Ebenen in der sich die 4 Wuerfel
//    befinden 8 Grundpositionen (der Aufsatz kann sich ober und
//    unterhalb jedes der 4 Wuerfel befinden). Zur Symmetriereduktion
//    werden 4 Grundpositionen verwendet, die nicht durch Spiegelungen
//    ineinander uebergehen:
//    +--+--+   +--+--+   +--+--+   +--+--+
//    |  |  |   |  |  |   |  |V |   |H |  |  V - Wuerfel liegt vorn
//    +--+--+   +--+--+   +--+--+   +--+--+
//    |V |  |   |  |H |   |  |  |   |  |  |  H - Wuerfel liegt hinten
//    +--+--+   +--+--+   +--+--+   +--+--+

//
// 7. Teil (+):
//       +--+
//       |  |
//    +--+  +--+
//    |        |
//    +--+  +--+
//       |  |
//       +--+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegt
//    1 Grundposition, so dass sich alle Lagen durch
//    die Verschiebung von 3 Grundpositionen ergibt.
//

//
// 8. Teil (4 Wuerfel)
//    +--+
//    |  |
//    +::+--+
//    |  |  |
//    +--+--+
//
//    Es gibt fuer jede der 3 Ebenen,
//    die durch dieses Teil definiert werden
//    8 Grundpositionen.
//
//    Dieses Teil (und das Teil 6) werden zur Symmetrieeinschraenkung
//    benutzt. Die Symmetrien ergeben sich durch Rotation um
//    180 Grad um die x,y und z-Achse und durch Spiegelungen
//    um die x-y, y-z, x-z - Ebene. 
//    Das abgebildete Teil hat (als einziges) die Eigenschaft, dass es durch
//    eine der Symmetrietransformationen immer in eine Position
//    uebergeht, die nicht durch einfache Translation erreicht
//    werden kann. Man kann damit die symmetrischen Loesungen
//    ausschliessen, indem man fuer jede Ebene nur eine 
//    Grundposition zulaesst. Besser ist es allerdings
//    eine Grundposition und eine dazu spiegelbildliche zu nehmen.
//    Aus den 8 Grundpositionen des Teiles 6 werden dann noch 4 ausgewaehlt,
//    die nicht durch einfaches Spiegeln ineienander ueberfuehrt
//    werden koennen. Fuer jede Translation eines Teiles 8 kann
//    man nun 3 weitere berechnen, die durch Rotationen um 180 Grad
//    entstehen. Von diesen 4 Moeglichkeiten nimmt man jetzt diejenige
//    deren erstes gesetztes Bit am greoessten ist. Damit taucht das
//    Teil 8 bei den ersten Teilen ueberhaupt nicht auf.
//
 
//
//  9. Teil (L):
//    +--+
//    |  |
//    +  +
//    |  |
//    +  +
//    |  |
//    +  +--+
//    |     |
//    +-----+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegt
//    8 Grundpositionen. Da das lange Teil nicht in die
//    Z-Richtung passt, gibt es nur 16 Grundpositionen,
//    aus der sich alle Lagen durch Verschiebung ergeben.
//

//
// 10. Teil (W):
//    +--+
//    |  |
//    +  +--+
//    |     |
//    +--+  +--+
//       |     |
//       +-----+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegen
//    kann 4 Grundpositionen, so dass sich jede moegliche
//    Lage durch die Verschiebung von 12 Grundpositionen 
//    ergibt.
//

//
// 11. Teil:
//    +--+
//    |  |
//    +  +--+
//    |     |
//    +  +--+
//    |  |
//    +  +
//    |  |
//    +--+
//
//    Es gibt fuer jede der 3 Ebenen in der das Teil liegt
//    8 Grundpositionen. Da das lange Teil nicht in die
//    Z-Richtung passt, gibt es nur 16 Grundpositionen,
//    aus der sich alle Lagen durch Verschiebung ergeben.
//
 
//
// Grundpositionen:
//   Es wird nur die Grundposition jeweils in einer
//   Ebene beschrieben, die anderen ergeben sich durch
//   Vertauschung von y<->z und x<->z.
//
//
//
int basic_pos[12][8][18] = 
   {
     // 0. Teil 
     {
         {0,0,0, 1,0,0, 0,1,0, 0,2,0, 1,2,0},
         {0,0,0, 0,1,0, 1,1,0, 2,1,0, 2,0,0},
         {0,0,0, 1,0,0, 1,1,0, 1,2,0, 0,2,0},
         {0,1,0, 0,0,0, 1,0,0, 2,0,0, 2,1,0},
     },
     // 1. Teil 
     {
        {0,1,0, 1,1,0, 2,1,0, 0,2,0, 1,0,0},
        {0,1,0, 1,1,0, 2,1,0, 0,0,0, 1,2,0},
        {0,1,0, 1,1,0, 2,1,0, 2,2,0, 1,0,0},
        {0,1,0, 1,1,0, 2,1,0, 2,0,0, 1,2,0},
        {1,0,0, 1,1,0, 1,2,0, 0,0,0, 2,1,0},
        {1,0,0, 1,1,0, 1,2,0, 2,0,0, 0,1,0},
        {1,0,0, 1,1,0, 1,2,0, 0,2,0, 2,1,0},
        {1,0,0, 1,1,0, 1,2,0, 2,2,0, 0,1,0}
     },
     // 2. Teil 
     {
        {0,0,0, 0,1,0, 0,2,0, 0,3,0, 1,1,0, 1,3,0},
        {0,0,0, 0,1,0, 0,2,0, 0,3,0, 1,0,0, 1,2,0},
        {1,0,0, 1,1,0, 1,2,0, 1,3,0, 0,1,0, 0,3,0},
        {1,0,0, 1,1,0, 1,2,0, 1,3,0, 0,0,0, 0,2,0},
        {0,0,0, 1,0,0, 2,0,0, 3,0,0, 1,1,0, 3,1,0},
        {0,0,0, 1,0,0, 2,0,0, 3,0,0, 0,1,0, 2,1,0},
        {0,1,0, 1,1,0, 2,1,0, 3,1,0, 1,0,0, 3,0,0},
        {0,1,0, 1,1,0, 2,1,0, 3,1,0, 0,0,0, 2,0,0}
     },
     // 3. Teil 
     {
        {0,2,0, 1,2,0, 2,2,0, 1,0,0, 1,1,0},
        {0,0,0, 1,0,0, 2,0,0, 1,1,0, 1,2,0},
        {0,0,0, 0,1,0, 0,2,0, 1,1,0, 2,1,0},
        {2,0,0, 2,1,0, 2,2,0, 0,1,0, 1,1,0}
     },
     // 4. Teil 
     {
        {1,0,0, 1,1,0, 1,2,0, 0,0,0, 2,2,0},
        {1,0,0, 1,1,0, 1,2,0, 0,2,0, 2,0,0},
        {0,1,0, 1,1,0, 2,1,0, 0,0,0, 2,2,0},
        {0,1,0, 1,1,0, 2,1,0, 0,2,0, 2,0,0}
     },
     // 5. Teil 
     {
        {0,0,0, 0,1,0, 1,0,0, 1,1,0, 2,0,0},
        {0,0,0, 0,1,0, 1,0,0, 1,1,0, 2,1,0},
        {0,0,0, 0,1,0, 1,0,0, 1,1,0, 0,2,0},
        {0,0,0, 0,1,0, 1,0,0, 1,1,0, 1,2,0},
        {0,1,0, 1,1,0, 0,2,0, 1,2,0, 0,0,0},
        {0,1,0, 1,1,0, 0,2,0, 1,2,0, 1,0,0},
        {1,0,0, 2,0,0, 1,1,0, 2,1,0, 0,0,0},
        {1,0,0, 2,0,0, 1,1,0, 2,1,0, 0,1,0}
     },
     // 6. Teil 
     {
        {0,0,0, 0,1,0, 1,0,0, 1,1,0, 0,0,1},
        {0,0,0, 0,1,0, 1,0,0, 1,1,0, 1,1,1},
        {0,0,1, 0,1,1, 1,0,1, 1,1,1, 0,1,0},
        {0,0,1, 0,1,1, 1,0,1, 1,1,1, 1,0,0}
     },
     // 7. Teil 
     {
        {0,1,0, 1,0,0, 1,1,0, 1,2,0, 2,1,0}
     },
     // 8. Teil 
     {
        {0,0,0, 0,1,0, 0,0,1, 1,0,1},
        {0,0,1, 0,1,1, 0,0,0, 1,0,0}
     },
     // 9. Teil 
     {
        {0,0,0, 0,1,0, 0,2,0, 0,3,0, 1,0,0},
        {0,0,0, 0,1,0, 0,2,0, 0,3,0, 1,3,0},
        {1,0,0, 1,1,0, 1,2,0, 1,3,0, 0,0,0},
        {1,0,0, 1,1,0, 1,2,0, 1,3,0, 0,3,0},
        {0,0,0, 1,0,0, 2,0,0, 3,0,0, 0,1,0},
        {0,0,0, 1,0,0, 2,0,0, 3,0,0, 3,1,0},
        {0,1,0, 1,1,0, 2,1,0, 3,1,0, 0,0,0},
        {0,1,0, 1,1,0, 2,1,0, 3,1,0, 3,0,0}
     },
     // 10. Teil 
     {
        {0,2,0, 0,1,0, 1,1,0, 1,0,0, 2,0,0},
        {0,0,0, 0,1,0, 1,1,0, 1,2,0, 2,2,0},
        {0,2,0, 1,2,0, 1,1,0, 2,1,0, 2,0,0},
        {0,0,0, 1,0,0, 1,1,0, 2,1,0, 2,2,0}
     },
     // 11. Teil
     {
        {0,0,0, 0,1,0, 0,2,0, 0,3,0, 1,1,0},
        {0,0,0, 0,1,0, 0,2,0, 0,3,0, 1,2,0},
        {1,0,0, 1,1,0, 1,2,0, 1,3,0, 0,1,0},
        {1,0,0, 1,1,0, 1,2,0, 1,3,0, 0,2,0},
        {0,0,0, 1,0,0, 2,0,0, 3,0,0, 1,1,0},
        {0,0,0, 1,0,0, 2,0,0, 3,0,0, 2,1,0},
        {0,1,0, 1,1,0, 2,1,0, 3,1,0, 1,0,0},
        {0,1,0, 1,1,0, 2,1,0, 3,1,0, 2,0,0}
     }
   };

   int n_basic_pos[12] = {4,8,8,4,4,8,4,1,2,8,4,8};
   int i,j,k,l,m,n,xmax,ymax,zmax,first;
   BitVector v,vm[8];
   
   // Berechnung aller Positionen der Teile
   // Jede Position wird als Bitvektor gespeichert

   for (i = 0; i < 12; i++)
   {
      // Berechnung fuer das Teil i 
      memset(n_part_pos[i],0,57*sizeof(int));
      for (j = 0; j < n_basic_pos[i]; j++)
      {
         // Lagen in x,y Ebene
         // Bounding Box 
         xmax = 0, ymax = 0, zmax = 0;
         for (k = 0; k < n_cubes[i]; k++)
         {
            if (xmax < basic_pos[i][j][3*k])
               xmax = basic_pos[i][j][3*k];
            if (ymax < basic_pos[i][j][3*k+1])
               ymax = basic_pos[i][j][3*k+1];
            if (zmax < basic_pos[i][j][3*k+2])
               zmax = basic_pos[i][j][3*k+2];
         }
         // Translieren 
         for (k = 0; k < 5-xmax; k++)
         {
            for (l = 0; l < 4-ymax; l++)
            {
               for (m = 0; m < 3-zmax; m++)
               {
                  v = (BitVector)0;
                  for (n = 0; n < n_cubes[i]; n++)
                  {
                     SET_BITC(v,basic_pos[i][j][3*n]+k,
                                basic_pos[i][j][3*n+1]+l,
                                basic_pos[i][j][3*n+2]+m);
                  }
                  first = get_next_set(v,0);
                  if (i == 8)
                  {
                     // Sonderbehandlung Teil 8
                     // Ermittelung aller Drehungen
                     // Auswahl desjenigen Teiles, das am
                     // weitesten weg liegt.
                     vm[0] = rot(v,1);
                     vm[1] = rot(v,2);
                     vm[2] = rot(v,3);
                     for (n = 0; n < 3; n++)
                     {
                        if (get_next_set(vm[n],0) > first)
                        {
                           first = get_next_set(vm[n],0);
                           v = vm[n];
                        }
                     }
                  }
                  part_pos[i][first][n_part_pos[i][first]] = v;
                  part_neighbour[i][first][n_part_pos[i][first]].v = neighbour(v);
                  compute_numbers(part_neighbour[i][first][n_part_pos[i][first]]);
                  n_part_pos[i][first]++;
               }
            }
         }
         // Lagen in x,z Ebene
         // y und z vertauschen 
         k = ymax;
         ymax = zmax;
         zmax = k;
         // Translieren 
         for (k = 0; k < 5-xmax; k++)
         {
            for (l = 0; l < 4-ymax; l++)
            {
               for (m = 0; m < 3-zmax; m++)
               {
                  v = (BitVector)0;
                  for (n = 0; n < n_cubes[i]; n++)
                  {
                     SET_BITC(v,basic_pos[i][j][3*n]+k,
                                basic_pos[i][j][3*n+2]+l,
                                basic_pos[i][j][3*n+1]+m);
                  }
                  first = get_next_set(v,0);
                  if (i == 8)
                  {
                     // Sonderbehandlung Teil 8
                     // Ermittelung aller Drehungen
                     // Auswahl desjenigen Teiles, das am
                     // weitesten weg liegt.
                     vm[0] = rot(v,1);
                     vm[1] = rot(v,2);
                     vm[2] = rot(v,3);
                     for (n = 0; n < 3; n++)
                     {
                        if (get_next_set(vm[n],0) > first)
                        {
                           first = get_next_set(vm[n],0);
                           v = vm[n];
                        }
                     }
                  }
                  part_pos[i][first][n_part_pos[i][first]] = v;
                  part_neighbour[i][first][n_part_pos[i][first]].v = neighbour(v);
                  compute_numbers(part_neighbour[i][first][n_part_pos[i][first]]);
                  n_part_pos[i][first]++;
               }
            }
         }
         // Lagen in z,y Ebene
         // x und z vertauschen 
         k = xmax;
         xmax = ymax;
         ymax = zmax;
         zmax = k;
         // Translieren 
         for (k = 0; k < 5-xmax; k++)
         {
            for (l = 0; l < 4-ymax; l++)
            {
               for (m = 0; m < 3-zmax; m++)
               {
                  v = (BitVector)0;
                  for (n = 0; n < n_cubes[i]; n++)
                  {
                     SET_BITC(v,basic_pos[i][j][3*n+2]+k,
                                basic_pos[i][j][3*n+1]+l,
                                basic_pos[i][j][3*n]+m);
                  }
                  first = get_next_set(v,0);
                  if (i == 8)
                  {
                     // Sonderbehandlung Teil 8
                     // Ermittelung aller Drehungen
                     // Auswahl desjenigen Teiles, das am
                     // weitesten weg liegt.
                     vm[0] = rot(v,1);
                     vm[1] = rot(v,2);
                     vm[2] = rot(v,3);
                     for (n = 0; n < 3; n++)
                     {
                        if (get_next_set(vm[n],0) > first)
                        {
                           first = get_next_set(vm[n],0);
                           v = vm[n];
                        }
                     }
                  }
                  part_pos[i][first][n_part_pos[i][first]] = v;
                  part_neighbour[i][first][n_part_pos[i][first]].v = neighbour(v);
                  compute_numbers(part_neighbour[i][first][n_part_pos[i][first]]);
                  n_part_pos[i][first]++;
               }
            }
         }
      }
   }
   last_bit_7 = 57;
   while(!n_part_pos[7][last_bit_7])
      last_bit_7--;
}

int compare(short part1,BitVector v1,short part2,BitVector v2)
{
   if (part1 < part2) return -1;
   if (part1 > part2) return 1;
   if (v1 < v2) return -1;
   if (v1 > v2) return 1;
   return 0;
}

void sort(BitVector *pos, short *part, int n_pos)
{
   // Quick Sort the positions in ascending order.
   // The quicksort algorithm is described in detail in Knuth's algorithm
   // book and 'Advanced Programming Techniques' (Hughes/Pfleeger/Rose).
   // It finds a 'dividing' point, such that all elements on one side of
   // the dividing point are less than the dividing point, and all items
   // on the other side are greater than it. This then produces two
   // lists which may be sorted independently, since the elements in the
   // first list will all be smaller than those in the second list. Each
   // of these lists will be smaller than the original list, so this
   // 'divide and conquer' technique can be applied iteratively to each
   //  successively smaller list until all of their items are sorted.
   
   if (n_pos < 2) return;
   
   int level, lower, iupper;
   BitVector divpos, posh;
   short divpart, parth; 

   // compute the stack size:
   // n_pos = 6*(2**(maxlev-2)-1) + 4
   
   const int maxlev = 30;
   int levlow[maxlev],levupp[maxlev];

   // initialize first level range
   level = 0;
   levlow[level] = 0;
   levupp[level] = n_pos-1;

   while (level>=0)
   {
      // check if current level is sorted (if so, sort previous level)
      if (levlow[level]>=levupp[level]) 
      {
         level--;
         continue;
      }
      //
      //  Sort current level, which consists of a range of position elements from
      //  levlow(level) to levupp(level). Use the last/high element
      //  in this range as the dividing point, and separate all other elements
      //  based on whether they are lower or higher (before or after) the
      //  dividing point.
      //
      //  The elements before the dividing point will be placed in the lower
      //  part of the level range, and the elements after the dividing point
      //  will placed in the upper part of the level range. The level range
      //  is then modified to contain only the elements in the larger of the
      //  two new groups. The smaller group is then sorted, using the next
      //  highest level and a corresponding new level range.
      //
      //  find the dividing point for this level
      lower = levlow[level] - 1;
      iupper = levupp[level];
      divpos = pos[iupper];
      divpart = part[iupper];
      //  check if sorting at this level is finished
      while(lower<iupper)
      {
         //  find next element in lower range that comes after the dividing point
         lower++;
         for(;;)
         {
           if (compare(part[lower],pos[lower],divpart,divpos) < 0) 
           {
              lower++;
              continue;
           }
           break;
         }
         //  find next element in upper range that comes before the dividing point
         iupper--;
         while (iupper >lower) 
         {
            if (compare(part[iupper],pos[iupper],divpart,divpos) > 0) 
            {
               iupper--;
               continue;
            }
            else
            {
               //  interchange elements from lower and upper ranges
               posh = pos[lower];
               pos[lower] = pos[iupper];
               pos[iupper] = posh;
               parth = part[lower];
               part[lower] = part[iupper];
               part[iupper] = parth;
               break;
            }
         }
      }
      //
      //  current level sorted (put dividing point between lower/higher values)
      //
      iupper = levupp[level];
      posh = pos[lower];
      pos[lower] = pos[iupper];
      pos[iupper] = posh;
      parth = part[lower];
      part[lower] = part[iupper];
      part[iupper] = parth;
      //  determine which new group is smaller and set new level ranges
      if (lower-levlow[level] < levupp[level]-lower) 
      {
         levlow[level+1] = levlow[level];
         levupp[level+1] = lower - 1;
         levlow[level] = lower + 1;
      }
      else
      {
         levlow[level+1] = lower + 1;
         levupp[level+1] = levupp[level];
         levupp[level] = lower - 1;
      }
      //  increment the level
      level++;
   }
   return;
}

// Berechnung der Teiletripel
void compute_tripel()
{
   int index = 0,i,part1,part2,part3,bit1,bit2,bit3,n1,n2,n3,ind,nr;
   short *second_bit;
   second_bit = (short *)malloc(200000*sizeof(short));
   tripel = (BitVector *)malloc(NUMBER_TRIPEL*sizeof(BitVector));

   memset(n_tripel[0][0][0][0],0,10*10*10*(47-FIRST_TRIPEL_NUM)*(47-FIRST_TRIPEL_NUM)*sizeof(int));
   BitVector v;

   for (i = FIRST_TRIPEL_NUM; i < 47; i++)
   {
      for (part1 = 0; part1 < 10; part1++)
      {
         for (part2 = part1 + 1; part2 < 11; part2++)
         {
            for (part3 = part2 + 1; part3 < 12; part3++)
            {
               nr = 0;
               ind = index;
               for (n1 = 0; n1 < n_part_pos[part1][i]; n1++)
               {
                  v = part_pos[part1][i][n1];
                  for (bit2 = get_next_clear(v,i+1); bit2 <= 60-n_cubes[part2]; bit2++)
                  {
                     for (n2 = 0; n2 < n_part_pos[part2][bit2]; n2++)
                     {
                        if (FIT(v,part_pos[part2][bit2][n2]))
                        {
                           ADD(v,part_pos[part2][bit2][n2]);
                           for (bit3 = get_next_clear(v,i+1); bit3 <= 60-n_cubes[part3]; bit3++)
                           {
                              if (bit3 == bit2) continue;
                              for (n3 = 0; n3 < n_part_pos[part3][bit3]; n3++)
                              {
                                 if (FIT(v,part_pos[part3][bit3][n3]))
                                 {
                                    tripel[index] = v;
                                    ADD(tripel[index],part_pos[part3][bit3][n3]);
                                    second_bit[nr] = get_next_set(tripel[index],i+1);
                                    nr++;
                                    index++;
                                 }
                              }
                           }
                           REMOVE(v,part_pos[part2][bit2][n2]);
                        }
                     }
                  }
               }
               for (n2 = 0; n2 < n_part_pos[part2][i]; n2++)
               {
                  v = part_pos[part2][i][n2];
                  for (bit1 = get_next_clear(v,i+1); bit1 <= 60-n_cubes[part1]; bit1++)
                  {
                     for (n1 = 0; n1 < n_part_pos[part1][bit1]; n1++)
                     {
                        if (FIT(v,part_pos[part1][bit1][n1]))
                        {
                           ADD(v,part_pos[part1][bit1][n1]);
                           for (bit3 = get_next_clear(v,i+1); bit3 <= 60-n_cubes[part3]; bit3++)
                           {
                              if (bit3 == bit1) continue;
                              for (n3 = 0; n3 < n_part_pos[part3][bit3]; n3++)
                              {
                                 if (FIT(v,part_pos[part3][bit3][n3]))
                                 {
                                    tripel[index] = v;
                                    ADD(tripel[index],part_pos[part3][bit3][n3]);
                                    second_bit[nr] = get_next_set(tripel[index],i+1);
                                    nr++;
                                    index++;
                                 }
                              }
                           }
                           REMOVE(v,part_pos[part1][bit1][n1]);
                        }
                     }
                  }
               }
               for (n3 = 0; n3 < n_part_pos[part3][i]; n3++)
               {
                  v = part_pos[part3][i][n3];
                  for (bit1 = get_next_clear(v,i+1); bit1 <= 60-n_cubes[part1]; bit1++)
                  {
                     for (n1 = 0; n1 < n_part_pos[part1][bit1]; n1++)
                     {
                        if (FIT(v,part_pos[part1][bit1][n1]))
                        {
                           ADD(v,part_pos[part1][bit1][n1]);
                           for (bit2 = get_next_clear(v,i+1); bit2 <= 60-n_cubes[part2]; bit2++)
                           {
                              if (bit2 == bit1) continue;
                              for (n2 = 0; n2 < n_part_pos[part2][bit2]; n2++)
                              {
                                 if (FIT(v,part_pos[part2][bit2][n2]))
                                 {
                                    tripel[index] = v;
                                    ADD(tripel[index],part_pos[part2][bit2][n2]);
                                    second_bit[nr] = get_next_set(tripel[index],i+1);
                                    nr++;
                                    index++;
                                 }
                              }
                           }
                           REMOVE(v,part_pos[part1][bit1][n1]);
                        }
                     }
                  }
               }
               sort(&(tripel[ind]),second_bit,nr);
               if (nr == 0) continue;
               int np = 1, next_bit, ind1 = 0;
               next_bit = second_bit[0];
               ind_tripel[i-FIRST_TRIPEL_NUM][next_bit-FIRST_TRIPEL_NUM-1][part1][part2-1][part3-2] = ind;
               nr--;
               ind++;
               ind1++;
               while(nr)
               {
                  if (next_bit == second_bit[ind1])
                     np++;
                  else
                  {
                     n_tripel[i-FIRST_TRIPEL_NUM][next_bit-FIRST_TRIPEL_NUM-1][part1][part2-1][part3-2] = np;
                     next_bit = second_bit[ind1];
                     np = 1;
                     ind_tripel[i-FIRST_TRIPEL_NUM][next_bit-FIRST_TRIPEL_NUM-1][part1][part2-1][part3-2] = ind;
                  }
                  ind++;
                  ind1++;
                  nr--;
               }
               n_tripel[i-FIRST_TRIPEL_NUM][next_bit-FIRST_TRIPEL_NUM-1][part1][part2-1][part3-2] = np;
            }
         }
      }
   }
   free(second_bit);
}

int is_part(int part, int bit, BitVector v)
{
   for (int i = 0; i < n_part_pos[part][bit]; i++)
   {
      if (v == part_pos[part][bit][i])
         return 1;
   }
   return 0;
}

// Finden von "schlechten" Zusammenhangskomponenten
int bad_component(const BitVector v, int part, int bit, int pos, int *used_parts)
{
   BitVector component, proofed = (BitVector)0;
   const int max_number = 3;
   int component_bits[max_number],n_component,i,j,k,nr,nr1,too_many,min_nr;
   int part8_used = used_parts[8];
   int part2_used = used_parts[2];

   // Es werden alle Zusammenhangskomponenten gesucht, die
   // das aktuelle Teil (part,pos) als Grenze haben.
   // Es werden deshalb die Nachbarschaftsbits des aktuellen Teiles
   // durchlaufen.
   for (i = 0; i < part_neighbour[part][bit][pos].n_bits; i++)
   {
      component = (BitVector)0;
      nr = part_neighbour[part][bit][pos].bits[i];
      if (GET_BIT(v,nr)  && !GET_BIT(proofed,nr))
      {
         // Das erste Bit der neuen Komponente wurde gefunden.
         SET_BIT(component,nr);
         n_component = 1;
         component_bits[0] = nr;
         min_nr = nr;
         for (j = 0;;j++)
         {
            nr = component_bits[j];
            // Ausgehend vom aktuellen Bit nr wird jetzt nach
            // Bits gefahndet, die zu nr benachbart sind und damit
            // die Zusammenhangskomponente vergroessern.
            BitVector b = bit_neighbour[nr].v & v & (~component);
            too_many = 0;
            if (b)
            {
               for (k = 0; k < bit_neighbour[nr].n_bits; k++)
               {
                  nr1 = bit_neighbour[nr].bits[k];
                  if (GET_BIT(b,nr1))
                  {
                     if (n_component == max_number || GET_BIT(proofed,nr1))
                     {
                        too_many = 1;
                        ADD(proofed,component);
                        break;
                     }
                     component_bits[n_component] = nr1;
                     SET_BIT(component,nr1);
                     n_component++;
                     if (nr1 < min_nr) min_nr = nr1;
                  }
               }
            }
            if (too_many)
               // Zusammenhangskomponente ist zu gross und wird nicht weiter
               // verfolgt oder eckt an eine zuvor abgebrochene Komponente an.
               break;
            if (j == (n_component -1))
            {
               if (n_component <= 3)
                  return 1;
               else if (n_component == 4)
               {
                  // Pruefen, ob das Teil 8 hier reinpasst
                  if (part8_used)
                     return 1;
                  // Pruefen, ob die Komponente das Teil 8 ist
                  if (!is_part(8,min_nr,component))
                     return 1;
                  part8_used = 1;
               }
               else if (n_component == 5)
               {
                  int found = 0;
                  for (j = 0; j < 12; j++)
                  {
                     if (j == 2) continue; // Teil hat 6 Wuerfel
                     if (j == 8) continue; // Teil hat 4 Wuerfel
                     if (used_parts[j]) continue;
                     // Pruefen, ob die Komponente das Teil j ist
                     if (is_part(j,min_nr,component))
                     {
                        found = 1;
                        break;
                     }
                  }
                  if (!found)
                     return 1;
               }
               // Hier kommt man eigentlich nicht mehr hin,
               // da die Komponentenlaenge auf 5 beschraenkt wurde.
               else if (n_component == 6)
               {
                  // Pruefen, ob das Teil 2 hier reinpasst
                  if (part2_used)
                     return 1;
                  // Pruefen, ob die Komponente das Teil 2 ist
                  if (!is_part(2,min_nr,component))
                     return 1;
                  part2_used = 1;
               }
               else
               {
                  int rem = n_component % 5;
                  if (rem == 2 || rem == 3)
                     return 1;
                  if (rem == 1 && part2_used)
                     return 1;
                  if (rem == 4 && part8_used)
                     return 1;
               }
               ADD(proofed,component);
               break;
            }
         }
      }
   }
   return 0;
}

int find(int bit_nr, int part1, int part2, int part3, BitVector v)
{
   int bit_nr1 = get_next_set(v,bit_nr+1);
   int n_pos = n_tripel[bit_nr-FIRST_TRIPEL_NUM][bit_nr1-FIRST_TRIPEL_NUM-1][part1][part2-1][part3-2];
   if (n_pos == 0) return 0;
   
   BitVector *pos = 
      &(tripel[ind_tripel[bit_nr-FIRST_TRIPEL_NUM][bit_nr1-FIRST_TRIPEL_NUM-1][part1][part2-1][part3-2]]);

   int low = 0, up = n_pos - 1, i;
   int middle, res = 0;
   
   for (;;)
   {
      middle = (low + up)/2;
      if (v == pos[middle])
         break;
      if (up - low <= 1)
      {
         if (up == low)
            return 0;
         if (middle == low)
            middle = up;
         else
            middle = low;
         if (v == pos[middle])
            break;
         return 0;
      }
      else
      {
         if (v < pos[middle])
            up = middle;
         else
            low = middle;
      }
   }
   res = 1;
   for (i = middle + 1; i < n_pos; i++)
   {
      if (v == pos[i])
         res++;
      else
         break;
   }
   for (i = middle - 1; i >= 0; i--)
   {
      if (v == pos[i])
         res++;
      else
         break;
   }
   return res;
}

// Loesen mit speichern
void solve1
(
   int        max_parts,
   BitVector  v, 
   int        level, 
   int        act_bit, 
   int       *used_parts, 
   int       &n_pos
)
{
   int act_part = 0,act_pos,i,n,next_bit;
   BitVector v1, *pv;
   while(act_part < 12)
   {
      if (!used_parts[act_part] && n_part_pos[act_part][act_bit])
      {
         n = n_part_pos[act_part][act_bit];
         pv = part_pos[act_part][act_bit];
         act_pos = 0;
         while (n--)
         {
            if (FIT(v,*pv))
            {
               v1 = v;
               ADD(v1,*pv);
               if (level  == max_parts - 1)
               {
                  used_parts[act_part] = 1;
                  if (level > 0 && level < 6 && 
                      bad_component(NEG(v1),act_part,act_bit,act_pos,used_parts))
                  {
                     used_parts[act_part]= 0;
                     act_pos++;
                     pv++;
                     continue;
                  }
                  presel[n_pos] = v1;
                  presel_parts[n_pos] = 0;
                  for (i = 0; i < 12; i++)
                  {
                     if (used_parts[i])
                        presel_parts[n_pos] |= (1<<i);
                  }
                  used_parts[act_part] = 0;
                  n_pos++;
                  if (max_parts == 12)
                     return;
                  else
                  {
                     act_pos++;
                     pv++;
                     continue;
                  }
               }
               used_parts[act_part] = 1;
               if (level > 0 && level < 6 && 
                  bad_component(NEG(v1),act_part,act_bit,act_pos,used_parts))
               {
                  used_parts[act_part]= 0;
                  act_pos++;
                  pv++;
                  continue;
               }
               next_bit = get_next_clear(v1);
               solve1(max_parts,v1,level+1,next_bit,used_parts,n_pos);
               used_parts[act_part] = 0;
            }
            act_pos++;
            pv++;
         }
      }
      act_part++;
   }
}

// Loesen ohne speichern
void solve2
(
   int       max_parts,
   BitVector v, 
   int       level, 
   int       act_bit, 
   int      *used_parts, 
   int      &n_pos
)
{
   int act_part = 0,i,part1,part2,part3,n,m,next_bit,nbit;
   BitVector v1, *pv;

   if (act_bit >= 24 && !used_parts[7])
   {
      // Es wird ueber alle Lagen des Teils "+" iteriert.
	  // Es gibt an dieser Stelle davon noch hoechstens 16
      used_parts[7] = 1;
      for (nbit = act_bit; nbit <= last_bit_7; nbit++)
      {
         n = n_part_pos[7][nbit];
         pv = part_pos[7][nbit];
         while (n--)
         {
            if (FIT(v,*pv))
            {
               if (level  == max_parts - 1)
               {
                  n_pos++;
                  if (max_parts == 12)
                     return;
                  else
                  {
                     pv++;
                     continue;
                  }
               }
               v1 = v;
               ADD(v1,*pv);
               next_bit = get_next_clear(v1);
               if ((v1 & bit_neighbour[next_bit].v) == bit_neighbour[next_bit].v)
               {
                  // Loch 
                  pv++;
                  continue;
               }
               if (level == 8 && next_bit >= FIRST_TRIPEL_NUM)
               {
                  part1 = -1;
                  part2 = -1;
                  part3 = -1;
                  for (i = 0; i < 12; i++)
                  {
                     if (!used_parts[i])
                     {
                        if (part1 < 0)
                           part1 = i;
                        else if (part2 < 0)
                           part2 = i;
                        else
                        {
                           part3 = i;
                           break;
                        }
                     }
                  }
                  m = find(next_bit,part1,part2,part3,NEG(v1));
                  n_pos += m;
                  pv++;
                  continue;
               }
               solve2(max_parts,v1,level+1,next_bit,used_parts,n_pos);
            }
            pv++;
         }
      }
      used_parts[7] = 0;
   }
   else
   {
      // Es wird ueber alle Teile iteriert, die mit dem act_bit beginnen
      while(act_part < 12)
      {
         if (!used_parts[act_part] && n_part_pos[act_part][act_bit])
         {
            n = n_part_pos[act_part][act_bit];
            pv = part_pos[act_part][act_bit];
            while (n--)
            {
               if (FIT(v,*pv))
               {
                  if (level  == max_parts - 1)
                  {
                     n_pos++;
                     if (max_parts == 12)
                        return;
                     else
                     {
                        pv++;
                        continue;
                     }
                  }
                  v1 = v;
                  ADD(v1,*pv);
                  next_bit = get_next_clear(v1);
                  if ((v1 & bit_neighbour[next_bit].v) == bit_neighbour[next_bit].v)
                  {
                     // Enklave von einem Wuerfel gefunden
                     pv++;
                     continue;
                  }
                  used_parts[act_part] = 1;
                  if (level == 8 && next_bit >= FIRST_TRIPEL_NUM)
                  {
                     part1 = -1;
                     part2 = -1;
                     part3 = -1;
                     for (i = 0; i < 12; i++)
                     {
                        if (!used_parts[i])
                        {
                           if (part1 < 0)
                              part1 = i;
                           else if (part2 < 0)
                              part2 = i;
                           else
                           {
                              part3 = i;
                              break;
                           }
                        }
                     }
                     m = find(next_bit,part1,part2,part3,NEG(v1));
                     n_pos += m;
                     used_parts[act_part] = 0;
                     pv++;
                     continue;
                  }
                  solve2(max_parts,v1,level+1,next_bit,used_parts,n_pos);
                  used_parts[act_part] = 0;
               }
               pv++;
            }
         }
         act_part++;
      }
   }
}

// Anlegen von Teilen nach dem Prinzip des ersten freien
// Wuerfels. Dabei koennen schon vorgefertigte
// Kombinationen benutzt werden.
// Ab dem 9. Teil werden die 3 fehlenden Teile aus
// einer Tabelle entnommen, wenn die ersten 9 Teile
// alle Bits < FIRST_TRIPEL_NUM ausfuellen.
// Rueckkehrwert ist die Anzahl der gefundenen Kombinationen
int puzzle
(
   int        max_parts,       // I: maximale Anzahl zu berechnender Teile
   int        use_processed,   // I: vorgefertigte Teile verwenden
   int        store_result,    // I: Resultate speichern
   int        n_processed,     // I: Anzahl vorgefertigter Teile
   BitVector *processed,       // I: vorgefertigte Teile
   short     *processed_parts  // I: Teilenummern der vorgefertigten Teile 
)
{
   BitVector v;
   int i,j;
   int n_pos = 0,act_bit,n_pos_loc,used_parts[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
   int step = 0, print_step = n_processed/5;

   if (use_processed && (n_processed > 0))
   {
      for (i = 0; i < n_processed; i++)
      {
         step++;
         if (print_step > 0 && step >= print_step)
         {
            printf(".");
            fflush(stdout);
            step = 0;
         }
         if ((i > 0) && (processed_parts[i] == processed_parts[i-1]) &&
             (processed[i] == processed[i-1]))
         {
            // Hier liegt dasselbe Bitmuster noch einmal vor
            n_pos += n_pos_loc;
            step++;
            continue;
         }
         if (i == 0 || (processed_parts[i-1] != processed_parts[i]))
         {
            for (j = 0; j < 12; j++)
            {
               if (processed_parts[i] & (1 << j))
                  used_parts[j] = 1;
               else
                  used_parts[j] = 0;
            }
         }
         n_pos_loc = 0;
         v = processed[i];
         // naechstes freies Bit suchen
         act_bit = get_next_clear(v);
         solve2(max_parts,v,PRESELECT_LEVEL,act_bit,used_parts,n_pos_loc);
         n_pos += n_pos_loc;
      }
   }
   else
   {
      v = (BitVector)0;
      n_pos = 0;
      if (store_result)
         solve1(max_parts,v,0,0,used_parts,n_pos);
      else
         solve2(max_parts,v,0,0,used_parts,n_pos);
   }
   return n_pos;
}

//
// Thread Wrapper fuer puzzle
//
#ifdef _WIN32
DWORD WINAPI puzzle_thread(LPVOID thpar)
{
  int nr = ((int *)thpar)[0];
  int ind = par[nr][1];
  
  res[nr] = puzzle(12,1,0,par[nr][0],
                   &(presel[ind]),&(presel_parts[ind]));
  return 0;
}
#else
void *puzzle_thread(void *thpar)
{
  int nr = ((int *)thpar)[0];
  int ind = par[nr][1];
  
  res[nr] = puzzle(12,1,0,par[nr][0],
                   &(presel[ind]),&(presel_parts[ind]));
  return NULL;
}
#endif


int main(int argc, char **argv)
{
//  Beschreibung des Algorithmus:
//   Jeder Elementarwuerfel wird durch ein ganzzahliges
//   Koordinatentripel (x,y,z) beschrieben. Der Koordinatenraum
//   ist durch 0 <= x <= 4, 0 <= y <= 3, 0 <= z <= 2
//   eingeschraenkt.
//   Ein Teil wird durch eine Folge von derartigen Koordinaten
//   beschrieben, die der Lage seiner Elementarwuerfel
//   entsprechen.
//   Es werden zunaechst fuer jedes Teil alle moeglichen
//   Lagen gebildet. Dazu werden fuer ein Teil Grundpositionen festgelegt,
//   aus denen dann alle Positionen durch Translationen entstehen.
//   Im naechsten Schritt werden dann aus den
//   12 Teilen, 6 Paare von Teilen gebildet. Fuer jedes Paar
//   werden alle moegliche Lagen berechnet.
//   Von diesen 6 Paaren werden dann 3 Quadrupel gebildet,
//   von denen wieder alle moeglichen Lagen berechnet werden.
//   Schliesslich werden dann alle moeglichen Lagen der 
//   12 Teile berechnet.
//
//   Um symmetrische Lagen nicht doppelt zu berechnen,
//   wird fuer das aus 4 Elementen bestehende Teil 8 und
//   fuer das Teil 6 die Anzahl der Lagen eingeschraenkt.
//
//   Da es fuer einen Elementarwuerfel nur 60 Lagemoeglichkeiten
//   gibt, kann jedes Teil auch als Bitmuster mit 60
//   Bits beschrieben werden, bei dem jedes gesetzte Bit
//   der Lage eines Elementarwuerfels entspricht.

   int i;

   start_time = time(NULL);

   // Inverse Mappingtablle zu tab1
   for (i = 0; i < 60; i++)
      tab2[tab1[i][0]][tab1[i][1]][tab1[i][2]] = i;
   
   // Ermitteln aller Teilelagen
   compute_part_positions();

   // Fuer jedes der 60 Bits werden die Nachbarn des Bits berechnet
   BitVector v;
   for (i = 0; i < 60; i++)
   {
      v = (BitVector)0;
      SET_BIT(v,i);
      bit_neighbour[i].v = neighbour(v);
      compute_numbers(bit_neighbour[i]);
   }

   // Wir bestimmen jetzt alle moeglichen Teiletripel,
   // deren erstes gesetztes Bit >= FIRST_TRIPEL_NUM ist;
   compute_tripel();
   
   // Wir berechnen jetzt alle Kombinationen, 
   // die durch das Anlegen der ersten 5 Teile entstehen koennen.
   presel = (BitVector *)malloc(NUMBER_PRESELECT*sizeof(BitVector));
   presel_parts = (short *)malloc(NUMBER_PRESELECT*sizeof(short));


   n_presel = puzzle(PRESELECT_LEVEL,0,1,0,NULL,NULL);

   // Diese Kombinationen werden jetzt sortiert.
   sort(presel,presel_parts,n_presel);

   printf("Vorbereitungen beendet %d sec\n",time(NULL) - start_time);

   // Jetzt bestimmen wir alle Moeglichkeiten mit den 
   // gerade berechneten Kombinationen als Eingang

   // Threads vorbereiten
   int ind = 0;
   for (i = 0; i < MAX_THREAD; i++)
   {
      if (i == MAX_THREAD - 1)
         par[i][0] = n_presel - ind;
      else
         par[i][0] = n_presel/MAX_THREAD;
      par[i][1] = ind;
      ind += par[i][0];
   }
   
   int nr[MAX_THREAD];
      
   // Thread starten
   printf("----------------------------------------\n");
   fflush(stdout);
#ifdef _WIN32
   HANDLE th[MAX_THREAD];
   DWORD id[MAX_THREAD];
   for (i = 0; i < MAX_THREAD; i++)
   {
      nr[i] = i;
      th[i] = CreateThread(NULL,0,
                           (LPTHREAD_START_ROUTINE)puzzle_thread,
                           &(nr[i]),0,(LPDWORD)&id);
      
   }
   for ( i = 0; i < MAX_THREAD; i++)
      WaitForSingleObject(th[i],INFINITE);
#else
   pthread_t th[MAX_THREAD];
   for (i = 0; i < MAX_THREAD; i++)
   {
      nr[i] = i;
      pthread_create(&(th[i]),NULL,puzzle_thread,&(nr[i]));
   }
   for ( i = 0; i < MAX_THREAD; i++)
      pthread_join(th[i],NULL);
#endif

   int n_pos = 0;
   for (i = 0; i < MAX_THREAD; i++)
      n_pos += res[i];
   
   printf("\nAnzahl Loesungen %d in %d sec\n",n_pos,time(NULL)-start_time);

   return 0;
}
