From c705e8d0e6a3b1a739f2ddf6cd1ee40a3241d40a Mon Sep 17 00:00:00 2001
From: pmla <pete.mahler.larsen@gmail.com>
Date: Wed, 19 Sep 2018 20:46:48 -0400
Subject: [PATCH] renamed files for LAMMPS build system compatibility

---
 src/PTM/compute_ptm_atom.cpp            |  307 ++++
 src/PTM/compute_ptm_atom.h              |   48 +
 src/PTM/ptm_alloy_types.cpp             |  101 ++
 src/PTM/ptm_alloy_types.h               |    9 +
 src/PTM/ptm_canonical_coloured.cpp      |  167 ++
 src/PTM/ptm_canonical_coloured.h        |    9 +
 src/PTM/ptm_constants.h                 |  174 ++
 src/PTM/ptm_convex_hull_incremental.cpp |  363 ++++
 src/PTM/ptm_convex_hull_incremental.h   |   27 +
 src/PTM/ptm_deformation_gradient.cpp    |   37 +
 src/PTM/ptm_deformation_gradient.h      |  142 ++
 src/PTM/ptm_functions.h                 |   27 +
 src/PTM/ptm_fundamental_mappings.h      |  180 ++
 src/PTM/ptm_graph_data.cpp              | 2059 +++++++++++++++++++++++
 src/PTM/ptm_graph_data.h                |   37 +
 src/PTM/ptm_graph_tools.cpp             |   52 +
 src/PTM/ptm_graph_tools.h               |   11 +
 src/PTM/ptm_index.cpp                   |  218 +++
 src/PTM/ptm_initialize_data.cpp         |   71 +
 src/PTM/ptm_initialize_data.h           |   61 +
 src/PTM/ptm_neighbour_ordering.cpp      |  203 +++
 src/PTM/ptm_neighbour_ordering.h        |   13 +
 src/PTM/ptm_normalize_vertices.cpp      |   55 +
 src/PTM/ptm_normalize_vertices.h        |    8 +
 src/PTM/ptm_polar.cpp                   |  337 ++++
 src/PTM/ptm_polar.h                     |   12 +
 src/PTM/ptm_quat.cpp                    |  396 +++++
 src/PTM/ptm_quat.h                      |   32 +
 src/PTM/ptm_structure_matcher.cpp       |  294 ++++
 src/PTM/ptm_structure_matcher.h         |   21 +
 src/PTM/ptm_voronoi_cell.cpp            | 1368 +++++++++++++++
 src/PTM/ptm_voronoi_cell.h              |  324 ++++
 src/PTM/ptm_voronoi_config.h            |  129 ++
 33 files changed, 7292 insertions(+)
 create mode 100644 src/PTM/compute_ptm_atom.cpp
 create mode 100644 src/PTM/compute_ptm_atom.h
 create mode 100644 src/PTM/ptm_alloy_types.cpp
 create mode 100644 src/PTM/ptm_alloy_types.h
 create mode 100644 src/PTM/ptm_canonical_coloured.cpp
 create mode 100644 src/PTM/ptm_canonical_coloured.h
 create mode 100644 src/PTM/ptm_constants.h
 create mode 100644 src/PTM/ptm_convex_hull_incremental.cpp
 create mode 100644 src/PTM/ptm_convex_hull_incremental.h
 create mode 100644 src/PTM/ptm_deformation_gradient.cpp
 create mode 100644 src/PTM/ptm_deformation_gradient.h
 create mode 100644 src/PTM/ptm_functions.h
 create mode 100644 src/PTM/ptm_fundamental_mappings.h
 create mode 100644 src/PTM/ptm_graph_data.cpp
 create mode 100644 src/PTM/ptm_graph_data.h
 create mode 100644 src/PTM/ptm_graph_tools.cpp
 create mode 100644 src/PTM/ptm_graph_tools.h
 create mode 100644 src/PTM/ptm_index.cpp
 create mode 100644 src/PTM/ptm_initialize_data.cpp
 create mode 100644 src/PTM/ptm_initialize_data.h
 create mode 100644 src/PTM/ptm_neighbour_ordering.cpp
 create mode 100644 src/PTM/ptm_neighbour_ordering.h
 create mode 100644 src/PTM/ptm_normalize_vertices.cpp
 create mode 100644 src/PTM/ptm_normalize_vertices.h
 create mode 100644 src/PTM/ptm_polar.cpp
 create mode 100644 src/PTM/ptm_polar.h
 create mode 100644 src/PTM/ptm_quat.cpp
 create mode 100644 src/PTM/ptm_quat.h
 create mode 100644 src/PTM/ptm_structure_matcher.cpp
 create mode 100644 src/PTM/ptm_structure_matcher.h
 create mode 100644 src/PTM/ptm_voronoi_cell.cpp
 create mode 100644 src/PTM/ptm_voronoi_cell.h
 create mode 100644 src/PTM/ptm_voronoi_config.h

diff --git a/src/PTM/compute_ptm_atom.cpp b/src/PTM/compute_ptm_atom.cpp
new file mode 100644
index 0000000000..b6b4a9786c
--- /dev/null
+++ b/src/PTM/compute_ptm_atom.cpp
@@ -0,0 +1,307 @@
+/* ----------------------------------------------------------------------
+         LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+         http://lammps.sandia.gov, Sandia National Laboratories
+         Steve Plimpton, sjplimp@sandia.gov
+
+         Copyright (2003) Sandia Corporation.	Under the terms of Contract
+         DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+         certain rights in this software.	This software is distributed
+under
+         the GNU General Public License.
+
+         See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+/* ----------------------------------------------------------------------
+         Contributing author: PM Larsen (MIT)
+------------------------------------------------------------------------- */
+
+#include <algorithm>
+#include <cmath>
+#include <cstdlib>
+#include <cstring>
+
+#include "atom.h"
+#include "comm.h"
+#include "compute_ptm_atom.h"
+#include "error.h"
+#include "force.h"
+#include "memory.h"
+#include "modify.h"
+#include "neigh_list.h"
+#include "neigh_request.h"
+#include "neighbor.h"
+#include "pair.h"
+#include "update.h"
+
+#include "ptm_functions.h"
+
+#define MAX_NEIGHBORS 30
+#define NUM_COLUMNS 7
+#define UNKNOWN 0
+#define OTHER 8
+
+using namespace LAMMPS_NS;
+
+static const char cite_user_ptm_package[] =
+    "USER-PTM package:\n\n"
+    "@Article{larsen2016ptm,\n"
+    " author={Larsen, Peter Mahler and Schmidt, S{\o}ren and Schi{\o}tz, "
+    "Jakob},\n"
+    " title={Robust structural identification via polyhedral template "
+    "matching},\n"
+    " journal={Modelling~Simul.~Mater.~Sci.~Eng.},\n"
+    " year={2016},\n"
+    " number={5},\n"
+    " volume={24},\n"
+    " pages={055007},\n"
+    " DOI = {10.1088/0965-0393/24/5/055007}"
+    "}\n\n";
+
+/* ---------------------------------------------------------------------- */
+
+ComputePTMAtom::ComputePTMAtom(LAMMPS *lmp, int narg, char **arg)
+    : Compute(lmp, narg, arg), list(NULL), output(NULL) {
+  if (narg != 5)
+    error->all(FLERR, "Illegal compute ptm/atom command");
+
+  char *structures = arg[3];
+  char *ptr = structures;
+
+  const char *strings[] = {"fcc",  "hcp",  "bcc", "ico",    "sc",
+                           "dcub", "dhex", "all", "default"};
+  int32_t flags[] = {
+      PTM_CHECK_FCC,
+      PTM_CHECK_HCP,
+      PTM_CHECK_BCC,
+      PTM_CHECK_ICO,
+      PTM_CHECK_SC,
+      PTM_CHECK_DCUB,
+      PTM_CHECK_DHEX,
+      PTM_CHECK_ALL,
+      PTM_CHECK_FCC | PTM_CHECK_HCP | PTM_CHECK_BCC | PTM_CHECK_ICO};
+
+  input_flags = 0;
+  while (*ptr != '\0') {
+
+    bool found = false;
+    for (int i = 0; i < 9; i++) {
+      int len = strlen(strings[i]);
+      if (strncmp(ptr, strings[i], len) == 0) {
+        input_flags |= flags[i];
+        ptr += len;
+        found = true;
+        break;
+      }
+    }
+
+    if (!found)
+      error->all(FLERR,
+                 "Illegal compute ptm/atom command (invalid structure type)");
+
+    if (*ptr == '\0')
+      break;
+
+    if (*ptr != '-')
+      error->all(FLERR,
+                 "Illegal compute ptm/atom command (invalid structure type)");
+
+    ptr++;
+  }
+
+  double threshold = force->numeric(FLERR, arg[4]);
+  if (threshold < 0.0)
+    error->all(FLERR,
+               "Illegal compute ptm/atom command (threshold is negative)");
+  rmsd_threshold = threshold;
+  if (rmsd_threshold == 0)
+    rmsd_threshold = INFINITY;
+
+  peratom_flag = 1;
+  size_peratom_cols = NUM_COLUMNS;
+  create_attribute = 1;
+  nmax = 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+ComputePTMAtom::~ComputePTMAtom() { memory->destroy(output); }
+
+/* ---------------------------------------------------------------------- */
+
+void ComputePTMAtom::init() {
+  if (force->pair == NULL)
+    error->all(FLERR, "Compute ptm/atom requires a pair style be defined");
+
+  int count = 0;
+  for (int i = 0; i < modify->ncompute; i++)
+    if (strcmp(modify->compute[i]->style, "ptm/atom") == 0)
+      count++;
+  if (count > 1 && comm->me == 0)
+    error->warning(FLERR, "More than one compute ptm/atom defined");
+
+  // need an occasional full neighbor list
+
+  int irequest = neighbor->request(this, instance_me);
+  neighbor->requests[irequest]->pair = 0;
+  neighbor->requests[irequest]->compute = 1;
+  neighbor->requests[irequest]->half = 0;
+  neighbor->requests[irequest]->full = 1;
+  neighbor->requests[irequest]->occasional = 1;
+}
+
+/* ---------------------------------------------------------------------- */
+
+void ComputePTMAtom::init_list(int id, NeighList *ptr) { list = ptr; }
+
+/* ---------------------------------------------------------------------- */
+
+typedef struct {
+  int index;
+  double d;
+} ptmnbr_t;
+
+static bool sorthelper_compare(ptmnbr_t const &a, ptmnbr_t const &b) {
+  return a.d < b.d;
+}
+
+static int get_neighbors(double *pos, int jnum, int *jlist, double **x,
+                         double (*nbr)[3]) {
+
+  ptmnbr_t *nbr_order = new ptmnbr_t[jnum];
+
+  for (int jj = 0; jj < jnum; jj++) {
+    int j = jlist[jj];
+    j &= NEIGHMASK;
+
+    double dx = pos[0] - x[j][0];
+    double dy = pos[1] - x[j][1];
+    double dz = pos[2] - x[j][2];
+    double rsq = dx * dx + dy * dy + dz * dz;
+
+    nbr_order[jj].index = j;
+    nbr_order[jj].d = rsq;
+  }
+
+  std::sort(nbr_order, nbr_order + jnum, &sorthelper_compare);
+  int num_nbrs = std::min(MAX_NEIGHBORS, jnum);
+
+  nbr[0][0] = nbr[0][1] = nbr[0][2] = 0;
+  for (int jj = 0; jj < num_nbrs; jj++) {
+
+    int j = nbr_order[jj].index;
+    nbr[jj + 1][0] = x[j][0] - pos[0];
+    nbr[jj + 1][1] = x[j][1] - pos[1];
+    nbr[jj + 1][2] = x[j][2] - pos[2];
+  }
+
+  delete[] nbr_order;
+  return num_nbrs;
+}
+
+void ComputePTMAtom::compute_peratom() {
+  // PTM global initialization.  If already initialized this function does
+  // nothing.
+  ptm_initialize_global();
+
+  // initialize PTM local storage
+  ptm_local_handle_t local_handle = ptm_initialize_local();
+
+  invoked_peratom = update->ntimestep;
+
+  // grow arrays if necessary
+  if (atom->nmax > nmax) {
+    memory->destroy(output);
+    nmax = atom->nmax;
+
+    memory->create(output, nmax, NUM_COLUMNS, "ptm:ptm_output");
+    array_atom = output;
+  }
+
+  // invoke full neighbor list (will copy or build if necessary)
+  neighbor->build_one(list);
+
+  int inum = list->inum;
+  int *ilist = list->ilist;
+  int *numneigh = list->numneigh;
+  int **firstneigh = list->firstneigh;
+
+  double **x = atom->x;
+  int *mask = atom->mask;
+  int nlocal = atom->nlocal;
+
+  for (int ii = 0; ii < inum; ii++) {
+
+    int i = ilist[ii];
+    output[i][0] = UNKNOWN;
+    if (!(mask[i] & groupbit))
+      continue;
+
+    double *pos = x[i];
+
+    int *jlist = firstneigh[i];
+    int jnum = numneigh[i];
+    if (jnum <= 0)
+      continue;
+
+    // get neighbours ordered by increasing distance
+    double nbr[MAX_NEIGHBORS + 1][3];
+    int num_nbrs = get_neighbors(pos, jnum, jlist, x, nbr);
+
+    // check that we have enough neighbours for the desired structure types
+    int32_t flags = 0;
+    if (num_nbrs >= PTM_NUM_NBRS_SC && (input_flags & PTM_CHECK_SC))
+      flags |= PTM_CHECK_SC;
+    if (num_nbrs >= PTM_NUM_NBRS_FCC && (input_flags & PTM_CHECK_FCC))
+      flags |= PTM_CHECK_FCC;
+    if (num_nbrs >= PTM_NUM_NBRS_HCP && (input_flags & PTM_CHECK_HCP))
+      flags |= PTM_CHECK_HCP;
+    if (num_nbrs >= PTM_NUM_NBRS_ICO && (input_flags & PTM_CHECK_ICO))
+      flags |= PTM_CHECK_ICO;
+    if (num_nbrs >= PTM_NUM_NBRS_BCC && (input_flags & PTM_CHECK_BCC))
+      flags |= PTM_CHECK_BCC;
+    if (num_nbrs >= PTM_NUM_NBRS_DCUB && (input_flags & PTM_CHECK_DCUB))
+      flags |= PTM_CHECK_DCUB;
+    if (num_nbrs >= PTM_NUM_NBRS_DHEX && (input_flags & PTM_CHECK_DHEX))
+      flags |= PTM_CHECK_DHEX;
+
+    // now run PTM
+    int8_t mapping[MAX_NEIGHBORS + 1];
+    int32_t type, alloy_type;
+    double scale, rmsd, interatomic_distance, lattice_constant;
+    double q[4], F[9], F_res[3], U[9], P[9];
+    ptm_index(local_handle, flags, num_nbrs + 1, nbr, NULL, true, &type,
+              &alloy_type, &scale, &rmsd, q, F, F_res, U, P, mapping,
+              &interatomic_distance, &lattice_constant);
+
+    if (rmsd > rmsd_threshold) {
+      type = PTM_MATCH_NONE;
+    }
+
+    // printf("%d type=%d rmsd=%f\n", i, type, rmsd);
+
+    if (type == PTM_MATCH_NONE)
+      type = OTHER;
+
+    output[i][0] = type;
+    output[i][1] = rmsd;
+    output[i][2] = interatomic_distance;
+    output[i][3] = q[0];
+    output[i][4] = q[1];
+    output[i][5] = q[2];
+    output[i][6] = q[3];
+  }
+
+  // printf("finished ptm analysis\n");
+  ptm_uninitialize_local(local_handle);
+}
+
+/* ----------------------------------------------------------------------
+         memory usage of local atom-based array
+------------------------------------------------------------------------- */
+
+double ComputePTMAtom::memory_usage() {
+  double bytes = nmax * NUM_COLUMNS * sizeof(double);
+  bytes += nmax * sizeof(double);
+  return bytes;
+}
diff --git a/src/PTM/compute_ptm_atom.h b/src/PTM/compute_ptm_atom.h
new file mode 100644
index 0000000000..5c10e0c443
--- /dev/null
+++ b/src/PTM/compute_ptm_atom.h
@@ -0,0 +1,48 @@
+/* -*- c++ -*- ----------------------------------------------------------
+   LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
+   http://lammps.sandia.gov, Sandia National Laboratories
+   Steve Plimpton, sjplimp@sandia.gov
+
+   Copyright (2003) Sandia Corporation.  Under the terms of Contract
+   DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
+   certain rights in this software.  This software is distributed under
+   the GNU General Public License.
+
+   See the README file in the top-level LAMMPS directory.
+------------------------------------------------------------------------- */
+
+#ifdef COMPUTE_CLASS
+
+ComputeStyle(ptm/atom,ComputePTMAtom)
+
+#else
+
+#ifndef LMP_COMPUTE_PTM_ATOM_H
+#define LMP_COMPUTE_PTM_ATOM_H
+
+#include "compute.h"
+
+namespace LAMMPS_NS {
+
+class ComputePTMAtom : public Compute {
+ public:
+  ComputePTMAtom(class LAMMPS *, int, char **);
+  ~ComputePTMAtom();
+  void init();
+  void init_list(int, class NeighList *);
+  void compute_peratom();
+  double memory_usage();
+
+ private:
+  int nmax;
+  int32_t input_flags;
+  double rmsd_threshold;
+  class NeighList *list;
+  double **output;
+};
+
+}
+
+#endif
+#endif
+
diff --git a/src/PTM/ptm_alloy_types.cpp b/src/PTM/ptm_alloy_types.cpp
new file mode 100644
index 0000000000..e14d06db99
--- /dev/null
+++ b/src/PTM/ptm_alloy_types.cpp
@@ -0,0 +1,101 @@
+#include <algorithm>
+#include "ptm_constants.h"
+#include "ptm_initialize_data.h"
+
+
+#define NUM_ALLOY_TYPES 3
+static uint32_t typedata[NUM_ALLOY_TYPES][3] = {
+	{PTM_MATCH_FCC, PTM_ALLOY_L10,    0x000001fe},
+	{PTM_MATCH_FCC, PTM_ALLOY_L12_CU, 0x0000001e},
+	{PTM_MATCH_FCC, PTM_ALLOY_L12_AU, 0x00001ffe},
+};
+
+static bool test_pure(int num_nbrs, int32_t* numbers)
+{
+	for (int i=1;i<num_nbrs + 1;i++)
+		if (numbers[i] != numbers[0])
+			return false;
+	return true;
+}
+
+static bool test_binary(int num_nbrs, int32_t* numbers)
+{
+	int a = numbers[0], b = -1;
+	for (int i=1;i<num_nbrs + 1;i++)
+	{
+		if (numbers[i] != a)
+		{
+			if (b == -1)
+				b = numbers[i];
+			else if (numbers[i] != b)
+				return false;
+		}
+	}
+
+	return true;
+}
+
+static bool test_shell_structure(const refdata_t* ref, int8_t* mapping, int32_t* numbers, int num_inner)
+{
+	int8_t binary[PTM_MAX_POINTS];
+	for (int i=0;i<ref->num_nbrs+1;i++)
+		binary[i] = numbers[mapping[i]] == numbers[0] ? 0 : 1;
+
+	for (int i=1;i<num_inner + 1;i++)
+		if (binary[i] == binary[0])
+			return false;
+
+	for (int i=num_inner+1;i<ref->num_nbrs+1;i++)
+		if (binary[i] != binary[0])
+			return false;
+
+	return true;
+}
+
+static int32_t canonical_alloy_representation(const refdata_t* ref, int8_t* mapping, int32_t* numbers)
+{
+	int8_t binary[PTM_MAX_POINTS];
+	for (int i=0;i<ref->num_nbrs+1;i++)
+		binary[i] = numbers[mapping[i]] == numbers[0] ? 0 : 1;
+
+	int8_t temp[PTM_MAX_POINTS];
+	uint32_t best = 0xFFFFFFFF;
+	for (int j=0;j<ref->num_mappings;j++)
+	{
+		for (int i=0;i<ref->num_nbrs+1;i++)
+			temp[ref->mapping[j][i]] = binary[i];
+
+		uint32_t code = 0;
+		for (int i=0;i<ref->num_nbrs+1;i++)
+			code |= (temp[i] << i);
+
+		best = std::min(best, code);
+	}
+
+	return best;
+}
+
+int32_t find_alloy_type(const refdata_t* ref, int8_t* mapping, int32_t* numbers)
+{
+	if (test_pure(ref->num_nbrs, numbers))
+		return PTM_ALLOY_PURE;
+
+	if (!test_binary(ref->num_nbrs, numbers))
+		return PTM_ALLOY_NONE;
+
+	uint32_t code = canonical_alloy_representation(ref, mapping, numbers);
+	for (int i=0;i<NUM_ALLOY_TYPES;i++)
+		if ((uint32_t)ref->type == typedata[i][0] && code == typedata[i][2])
+			return typedata[i][1];
+
+	if (ref->type == PTM_MATCH_BCC)
+		if (test_shell_structure(ref, mapping, numbers, 8))
+			return PTM_ALLOY_B2;
+
+	if (ref->type == PTM_MATCH_DCUB || ref->type == PTM_MATCH_DHEX)
+		if (test_shell_structure(ref, mapping, numbers, 4))
+			return PTM_ALLOY_SIC;
+
+	return PTM_ALLOY_NONE;
+}
+
diff --git a/src/PTM/ptm_alloy_types.h b/src/PTM/ptm_alloy_types.h
new file mode 100644
index 0000000000..6ea6ed1b8a
--- /dev/null
+++ b/src/PTM/ptm_alloy_types.h
@@ -0,0 +1,9 @@
+#ifndef PTM_ALLOY_TYPES_H
+#define PTM_ALLOY_TYPES_H
+
+#include "ptm_initialize_data.h"
+
+int32_t find_alloy_type(const refdata_t* ref, int8_t* mapping, int32_t* numbers);
+
+#endif
+
diff --git a/src/PTM/ptm_canonical_coloured.cpp b/src/PTM/ptm_canonical_coloured.cpp
new file mode 100644
index 0000000000..551f52d7e4
--- /dev/null
+++ b/src/PTM/ptm_canonical_coloured.cpp
@@ -0,0 +1,167 @@
+#include <string.h>
+#include <climits>
+#include <algorithm>
+#include "ptm_graph_tools.h"
+#include "ptm_constants.h"
+
+
+static bool weinberg_coloured(int num_nodes, int num_edges, int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS], int8_t* colours, int8_t* best_code, int8_t* canonical_labelling, int a, int b)
+{
+	bool m[PTM_MAX_NBRS][PTM_MAX_NBRS];
+	memset(m, 0, sizeof(bool) * PTM_MAX_NBRS * PTM_MAX_NBRS);
+
+	int8_t index[PTM_MAX_NBRS];
+	memset(index, -1, sizeof(int8_t) * PTM_MAX_NBRS);
+
+
+	int n = 0;
+	index[a] = colours[a] * num_nodes + n++;
+	if (index[a] > best_code[0])
+		return false;
+
+	bool winning = false;
+	if (index[a] < best_code[0])
+	{
+		best_code[0] = index[a];
+		winning = true;
+	}
+
+	int c = -1;
+	for (int it=1;it<2*num_edges;it++)
+	{
+		bool newvertex = index[b] == -1;
+
+		if (newvertex)
+			index[b] = colours[b] * num_nodes + n++;
+
+		if (!winning && index[b] > best_code[it])
+			return false;
+
+		if (winning || index[b] < best_code[it])
+		{
+			winning = true;
+			best_code[it] = index[b];
+		}
+
+		if (newvertex)
+		{
+			//When a new vertex is reached, take the right-most edge
+			//relative to the edge on which the vertex is reached.
+
+			c = common[a][b];
+		}
+		else if (m[b][a] == false)
+		{
+			//When an old vertex is reached on a new path, go back
+			//in the opposite direction.
+
+			c = a;
+		}
+		else
+		{
+			//When an old vertex is reached on an old path, leave the
+			//vertex on the right-most edge that has not previously
+			//been traversed in that direction.
+
+			c = common[a][b];
+			while (m[b][c] == true)
+				c = common[c][b];
+		}
+
+		m[a][b] = true;
+		a = b;
+		b = c;
+	}
+
+	if (winning)
+	{
+		memcpy(canonical_labelling, index, sizeof(int8_t) * num_nodes);
+		return true;
+	}
+
+	return false;
+}
+
+int canonical_form_coloured(int num_facets, int8_t facets[][3], int num_nodes, int8_t* degree, int8_t* colours, int8_t* canonical_labelling, int8_t* best_code, uint64_t* p_hash)
+{
+	int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS] = {{0}};
+	int num_edges = 3 * num_facets / 2;
+	if (!build_facet_map(num_facets, facets, common))
+		return -1;
+
+	memset(best_code, SCHAR_MAX, sizeof(int8_t) * 2 * PTM_MAX_EDGES);
+
+	bool equal = true;
+	for (int i = 1;i<num_nodes;i++)
+		if (degree[i] != degree[0] || colours[i] != colours[0])
+			equal = false;
+
+	if (equal)
+	{
+		weinberg_coloured(num_nodes, num_edges, common, colours, best_code, canonical_labelling, facets[0][0], facets[0][1]);
+	}
+	else
+	{
+		uint32_t best_degree = 0;
+		for (int i = 0;i<num_facets;i++)
+		{
+			int a = facets[i][0];
+			int b = facets[i][1];
+			int c = facets[i][2];
+
+			//int da = colours[a] * num_nodes + degree[a];
+			//int db = colours[b] * num_nodes + degree[b];
+			//int dc = colours[c] * num_nodes + degree[c];
+
+			int da = degree[a];
+			int db = degree[b];
+			int dc = degree[c];
+
+			best_degree = std::max(best_degree, ((uint32_t)da << 16) | ((uint32_t)db << 8) | ((uint32_t)dc << 0));
+			best_degree = std::max(best_degree, ((uint32_t)da << 0) | ((uint32_t)db << 16) | ((uint32_t)dc << 8));
+			best_degree = std::max(best_degree, ((uint32_t)da << 8) | ((uint32_t)db << 0) | ((uint32_t)dc << 16));
+		}
+
+		for (int i = 0;i<num_facets;i++)
+		{
+			int a = facets[i][0];
+			int b = facets[i][1];
+			int c = facets[i][2];
+
+			//int da = colours[a] * num_nodes + degree[a];
+			//int db = colours[b] * num_nodes + degree[b];
+			//int dc = colours[c] * num_nodes + degree[c];
+
+			int da = degree[a];
+			int db = degree[b];
+			int dc = degree[c];
+
+			if (best_degree == (((uint32_t)da << 16) | ((uint32_t)db << 8) | ((uint32_t)dc << 0)))
+				weinberg_coloured(num_nodes, num_edges, common, colours, best_code, canonical_labelling, a, b);
+
+			if (best_degree == (((uint32_t)da << 0) | ((uint32_t)db << 16) | ((uint32_t)dc << 8)))
+				weinberg_coloured(num_nodes, num_edges, common, colours, best_code, canonical_labelling, b, c);
+
+			if (best_degree == (((uint32_t)da << 8) | ((uint32_t)db << 0) | ((uint32_t)dc << 16)))
+				weinberg_coloured(num_nodes, num_edges, common, colours, best_code, canonical_labelling, c, a);
+		}
+	}
+
+	for (int i = num_nodes-1;i>=0;i--)
+		canonical_labelling[i+1] = (canonical_labelling[i] % num_nodes) + 1;
+	canonical_labelling[0] = 0;
+
+	uint64_t hash = 0;
+	for (int i = 0;i<2 * num_edges;i++)
+	{
+		uint64_t e = best_code[i];
+		e += i % 8;
+		e &= 0xF;
+		e <<= (4 * i) % 64;
+		hash ^= e;
+	}
+
+	*p_hash = hash;
+	return PTM_NO_ERROR;
+}
+
diff --git a/src/PTM/ptm_canonical_coloured.h b/src/PTM/ptm_canonical_coloured.h
new file mode 100644
index 0000000000..e71bb08bfc
--- /dev/null
+++ b/src/PTM/ptm_canonical_coloured.h
@@ -0,0 +1,9 @@
+#ifndef PTM_CANONICAL_COLOURED_H
+#define PTM_CANONICAL_COLOURED_H
+
+#include <stdint.h>
+
+int canonical_form_coloured(int num_facets, int8_t facets[][3], int num_nodes, int8_t* degree, int8_t* colours, int8_t* canonical_labelling, int8_t* best_code, uint64_t* p_hash);
+
+#endif
+
diff --git a/src/PTM/ptm_constants.h b/src/PTM/ptm_constants.h
new file mode 100644
index 0000000000..f868f51e84
--- /dev/null
+++ b/src/PTM/ptm_constants.h
@@ -0,0 +1,174 @@
+#ifndef PTM_CONSTANTS_H
+#define PTM_CONSTANTS_H
+
+//------------------------------------
+//    definitions
+//------------------------------------
+#define PTM_NO_ERROR	0
+
+
+#define PTM_CHECK_FCC	(1 << 0)
+#define PTM_CHECK_HCP	(1 << 1)
+#define PTM_CHECK_BCC	(1 << 2)
+#define PTM_CHECK_ICO	(1 << 3)
+#define PTM_CHECK_SC	(1 << 4)
+#define PTM_CHECK_DCUB	(1 << 5)
+#define PTM_CHECK_DHEX	(1 << 6)
+#define PTM_CHECK_NONDIAMOND	(PTM_CHECK_SC | PTM_CHECK_FCC | PTM_CHECK_HCP | PTM_CHECK_ICO | PTM_CHECK_BCC)
+#define PTM_CHECK_ALL	(PTM_CHECK_SC | PTM_CHECK_FCC | PTM_CHECK_HCP | PTM_CHECK_ICO | PTM_CHECK_BCC | PTM_CHECK_DCUB | PTM_CHECK_DHEX)
+
+#define PTM_MATCH_NONE	0
+#define PTM_MATCH_FCC	1
+#define PTM_MATCH_HCP	2
+#define PTM_MATCH_BCC	3
+#define PTM_MATCH_ICO	4
+#define PTM_MATCH_SC	5
+#define PTM_MATCH_DCUB	6
+#define PTM_MATCH_DHEX	7
+
+#define PTM_ALLOY_NONE		0
+#define PTM_ALLOY_PURE		1
+#define PTM_ALLOY_L10		2
+#define PTM_ALLOY_L12_CU	3
+#define PTM_ALLOY_L12_AU	4
+#define PTM_ALLOY_B2		5
+#define PTM_ALLOY_SIC		6
+
+
+#define PTM_MAX_INPUT_POINTS 35
+#define PTM_MAX_NBRS	16
+#define PTM_MAX_POINTS	(PTM_MAX_NBRS + 1)
+#define PTM_MAX_FACETS	28	//2 * PTM_MAX_NBRS - 4
+#define PTM_MAX_EDGES   42	//3 * PTM_MAX_NBRS - 6
+
+
+//------------------------------------
+//    number of neighbours
+//------------------------------------
+#define PTM_NUM_NBRS_FCC 12
+#define PTM_NUM_NBRS_HCP 12
+#define PTM_NUM_NBRS_BCC 14
+#define PTM_NUM_NBRS_ICO 12
+#define PTM_NUM_NBRS_SC  6
+#define PTM_NUM_NBRS_DCUB  16
+#define PTM_NUM_NBRS_DHEX  16
+
+#define PTM_NUM_POINTS_FCC  (PTM_NUM_NBRS_FCC + 1)
+#define PTM_NUM_POINTS_HCP  (PTM_NUM_NBRS_HCP + 1)
+#define PTM_NUM_POINTS_BCC  (PTM_NUM_NBRS_BCC + 1)
+#define PTM_NUM_POINTS_ICO  (PTM_NUM_NBRS_ICO + 1)
+#define PTM_NUM_POINTS_SC   (PTM_NUM_NBRS_SC  + 1)
+#define PTM_NUM_POINTS_DCUB (PTM_NUM_NBRS_DCUB  + 1)
+#define PTM_NUM_POINTS_DHEX (PTM_NUM_NBRS_DHEX  + 1)
+
+const int ptm_num_nbrs[8] = {0, PTM_NUM_NBRS_FCC, PTM_NUM_NBRS_HCP, PTM_NUM_NBRS_BCC, PTM_NUM_NBRS_ICO, PTM_NUM_NBRS_SC, PTM_NUM_NBRS_DCUB, PTM_NUM_NBRS_DHEX};
+
+//------------------------------------
+//    template structures
+//------------------------------------
+
+//these point sets have barycentre {0, 0, 0} and are scaled such that the mean neighbour distance is 1
+
+const double ptm_template_fcc[PTM_NUM_POINTS_FCC][3] = {	{  0.            ,  0.            ,  0.             },
+								{  0.            ,  0.707106781187,  0.707106781187 },
+								{  0.            , -0.707106781187, -0.707106781187 },
+								{  0.            ,  0.707106781187, -0.707106781187 },
+								{  0.            , -0.707106781187,  0.707106781187 },
+								{  0.707106781187,  0.            ,  0.707106781187 },
+								{ -0.707106781187,  0.            , -0.707106781187 },
+								{  0.707106781187,  0.            , -0.707106781187 },
+								{ -0.707106781187,  0.            ,  0.707106781187 },
+								{  0.707106781187,  0.707106781187,  0.             },
+								{ -0.707106781187, -0.707106781187,  0.             },
+								{  0.707106781187, -0.707106781187,  0.             },
+								{ -0.707106781187,  0.707106781187,  0.             }	};
+
+const double ptm_template_hcp[PTM_NUM_POINTS_HCP][3] = {	{  0.            ,  0.            ,  0.             },
+								{  0.707106781186,  0.            ,  0.707106781186 },
+								{ -0.235702260395, -0.942809041583, -0.235702260395 },
+								{  0.707106781186,  0.707106781186,  0.             },
+								{ -0.235702260395, -0.235702260395, -0.942809041583 },
+								{  0.            ,  0.707106781186,  0.707106781186 },
+								{ -0.942809041583, -0.235702260395, -0.235702260395 },
+								{ -0.707106781186,  0.707106781186,  0.             },
+								{  0.            ,  0.707106781186, -0.707106781186 },
+								{  0.707106781186,  0.            , -0.707106781186 },
+								{  0.707106781186, -0.707106781186,  0.             },
+								{ -0.707106781186,  0.            ,  0.707106781186 },
+								{  0.            , -0.707106781186,  0.707106781186 }	};
+
+const double ptm_template_bcc[PTM_NUM_POINTS_BCC][3] = {	{  0.            ,  0.            ,  0.             },
+								{ -0.541451884327, -0.541451884327, -0.541451884327 },
+								{  0.541451884327,  0.541451884327,  0.541451884327 },
+								{  0.541451884327, -0.541451884327, -0.541451884327 },
+								{ -0.541451884327,  0.541451884327,  0.541451884327 },
+								{ -0.541451884327,  0.541451884327, -0.541451884327 },
+								{  0.541451884327, -0.541451884327,  0.541451884327 },
+								{ -0.541451884327, -0.541451884327,  0.541451884327 },
+								{  0.541451884327,  0.541451884327, -0.541451884327 },
+								{  0.            ,  0.            , -1.082903768655 },
+								{  0.            ,  0.            ,  1.082903768655 },
+								{  0.            , -1.082903768655,  0.             },
+								{  0.            ,  1.082903768655,  0.             },
+								{ -1.082903768655,  0.            ,  0.             },
+								{  1.082903768655,  0.            ,  0.             }	};
+
+const double ptm_template_ico[PTM_NUM_POINTS_ICO][3] = {	{  0.            ,  0.            ,  0.             },
+								{  0.            ,  0.525731112119,  0.850650808352 },
+								{  0.            , -0.525731112119, -0.850650808352 },
+								{  0.            ,  0.525731112119, -0.850650808352 },
+								{  0.            , -0.525731112119,  0.850650808352 },
+								{ -0.525731112119, -0.850650808352,  0.             },
+								{  0.525731112119,  0.850650808352,  0.             },
+								{  0.525731112119, -0.850650808352,  0.             },
+								{ -0.525731112119,  0.850650808352,  0.             },
+								{ -0.850650808352,  0.            , -0.525731112119 },
+								{  0.850650808352,  0.            ,  0.525731112119 },
+								{  0.850650808352,  0.            , -0.525731112119 },
+								{ -0.850650808352,  0.            ,  0.525731112119 }	};
+
+const double ptm_template_sc[PTM_NUM_POINTS_SC][3] = {		{  0.            ,  0.            ,  0.             },
+								{  0.            ,  0.            , -1.             },
+								{  0.            ,  0.            ,  1.             },
+								{  0.            , -1.            ,  0.             },
+								{  0.            ,  1.            ,  0.             },
+								{ -1.            ,  0.            ,  0.             },
+								{  1.            ,  0.            ,  0.             }	};
+
+const double ptm_template_dcub[PTM_NUM_POINTS_DCUB][3] = {	{  0.            ,  0.            ,  0.             },
+								{ -0.391491627053,  0.391491627053,  0.391491627053 },
+								{ -0.391491627053, -0.391491627053, -0.391491627053 },
+								{  0.391491627053, -0.391491627053,  0.391491627053 },
+								{  0.391491627053,  0.391491627053, -0.391491627053 },
+								{ -0.782983254107,  0.            ,  0.782983254107 },
+								{ -0.782983254107,  0.782983254107,  0.             },
+								{  0.            ,  0.782983254107,  0.782983254107 },
+								{ -0.782983254107, -0.782983254107,  0.             },
+								{ -0.782983254107,  0.            , -0.782983254107 },
+								{  0.            , -0.782983254107, -0.782983254107 },
+								{  0.            , -0.782983254107,  0.782983254107 },
+								{  0.782983254107, -0.782983254107,  0.             },
+								{  0.782983254107,  0.            ,  0.782983254107 },
+								{  0.            ,  0.782983254107, -0.782983254107 },
+								{  0.782983254107,  0.            , -0.782983254107 },
+								{  0.782983254107,  0.782983254107,  0.             }	};
+
+const double ptm_template_dhex[PTM_NUM_POINTS_DHEX][3] = {	{  0.            ,  0.            ,  0.             },
+								{ -0.391491627053, -0.391491627053, -0.391491627053 },
+								{  0.391491627053, -0.391491627053,  0.391491627053 },
+								{ -0.391491627053,  0.391491627053,  0.391491627053 },
+								{  0.391491627053,  0.391491627053, -0.391491627053 },
+								{ -0.260994418036, -1.043977672142, -0.260994418036 },
+								{ -1.043977672142, -0.260994418036, -0.260994418036 },
+								{ -0.260994418036, -0.260994418036, -1.043977672142 },
+								{  0.782983254107,  0.            ,  0.782983254107 },
+								{  0.782983254107, -0.782983254107,  0.             },
+								{  0.            , -0.782983254107,  0.782983254107 },
+								{  0.            ,  0.782983254107,  0.782983254107 },
+								{ -0.782983254107,  0.782983254107,  0.             },
+								{ -0.782983254107,  0.            ,  0.782983254107 },
+								{  0.782983254107,  0.782983254107,  0.             },
+								{  0.            ,  0.782983254107, -0.782983254107 },
+								{  0.782983254107,  0.            , -0.782983254107 }	};
+#endif
+
diff --git a/src/PTM/ptm_convex_hull_incremental.cpp b/src/PTM/ptm_convex_hull_incremental.cpp
new file mode 100644
index 0000000000..c996b17b58
--- /dev/null
+++ b/src/PTM/ptm_convex_hull_incremental.cpp
@@ -0,0 +1,363 @@
+#include <cmath>
+#include <cfloat>
+#include <string.h>
+#include <cassert>
+#include <algorithm>
+#include "ptm_convex_hull_incremental.h"
+#include "ptm_constants.h"
+
+
+#define VISIBLE 1
+#define INVISIBLE 2
+#define BOTH 3
+#define TOLERANCE 1E-8
+
+static double norm_squared(double* p)
+{
+	double x = p[0];
+	double y = p[1];
+	double z = p[2];
+
+	return x*x + y*y + z*z;
+}
+
+static double dot_product(const double* a, const double* b)
+{
+	return a[0]*b[0] + a[1]*b[1] + a[2]*b[2];
+}
+
+static void cross_product(double* a, double* b, double* c)
+{
+	c[0] = a[1] * b[2] - a[2] * b[1];
+	c[1] = a[2] * b[0] - a[0] * b[2];
+	c[2] = a[0] * b[1] - a[1] * b[0];
+}
+
+static void calculate_plane_normal(const double (*points)[3], int a, int b, int c, double* plane_normal)
+{
+	double u[3] = {	points[b][0] - points[a][0],
+			points[b][1] - points[a][1],
+			points[b][2] - points[a][2]	};
+
+	double v[3] = {	points[c][0] - points[a][0],
+			points[c][1] - points[a][1],
+			points[c][2] - points[a][2]	};
+
+	cross_product(u, v, plane_normal);
+	double norm = sqrt(norm_squared(plane_normal));
+	plane_normal[0] /= norm;
+	plane_normal[1] /= norm;
+	plane_normal[2] /= norm;
+}
+
+static double point_plane_distance(const double* w, const double* plane_point, const double* plane_cross)
+{
+	return	  plane_cross[0] * (plane_point[0] - w[0])
+		+ plane_cross[1] * (plane_point[1] - w[1])
+		+ plane_cross[2] * (plane_point[2] - w[2]);
+}
+
+static bool calc_max_extent(int num_points, const double (*points)[3], int* min_index, int* max_index)
+{
+	for (int j=0;j<3;j++)
+	{
+		double dmin = DBL_MAX, dmax = -DBL_MAX;
+		int imin = 0, imax = 0;
+
+		for (int i = 0;i<num_points;i++)
+		{
+			double d = points[i][j];
+			if (d < dmin)
+			{
+				dmin = d;
+				imin = i;
+			}
+			if (d > dmax)
+			{
+				dmax = d;
+				imax = i;
+			}
+		}
+
+		if (imin == imax)
+			return false;	//degenerate point set
+
+		min_index[j] = imin;
+		max_index[j] = imax;
+	}
+
+	return true;
+}
+
+static bool find_third_point(int num_points, const double (*points)[3], int a, int b, int* p_c)
+{
+	const double* x1 = points[a];
+	const double* x2 = points[b];
+
+	double x2x1[3] = {x2[0] - x1[0], x2[1] - x1[1], x2[2] - x1[2]};
+	double ns_x2x1 = norm_squared(x2x1);
+
+	int bi = -1;
+	double max_dist = 0.0;
+	for (int i = 0;i<num_points;i++)
+	{
+		if (i == a || i == b)
+			continue;
+
+		const double* x0 = points[i];
+
+		double x1x0[3] = {x1[0] - x0[0], x1[1] - x0[1], x1[2] - x0[2]};
+		double dot = dot_product(x1x0, x2x1);
+		double dist = (norm_squared(x1x0) * ns_x2x1 - dot*dot) / ns_x2x1;
+
+		if (dist > max_dist)
+		{
+			max_dist = dist;
+			bi = i;
+		}
+	}
+
+	*p_c = bi;
+	return max_dist > TOLERANCE;
+}
+
+static bool find_fourth_point(int num_points, const double (*points)[3], int a, int b, int c, int* p_d)
+{
+	double plane_normal[3];
+	calculate_plane_normal(points, a, b, c, plane_normal);
+
+
+	int bi = -1;
+	double max_dist = 0.0;
+	for (int i = 0;i<num_points;i++)
+	{
+		if (i == a || i == b || i == c)
+			continue;
+
+		const double* x0 = points[i];
+		double dist = fabs(point_plane_distance(x0, points[a], plane_normal));
+		if (dist > max_dist)
+		{
+			max_dist = dist;
+			bi = i;
+		}
+	}
+
+	*p_d = bi;
+	return max_dist > TOLERANCE;
+}
+
+static int initial_simplex(int num_points, const double (*points)[3], int* initial_vertices)
+{
+	int min_index[3] = {0};
+	int max_index[3] = {0};
+	if (!calc_max_extent(num_points, points, min_index, max_index))
+		return -1;
+
+	int bi = -1;
+	double max_dist = 0.0;
+	for (int i = 0;i<3;i++)
+	{
+		int a = min_index[i], b = max_index[i];
+		double delta[3] = {	points[a][0] - points[b][0],
+					points[a][1] - points[b][1],
+					points[a][2] - points[b][2]	};
+		double dist = norm_squared(delta);
+		if (dist > max_dist)
+		{
+			bi = i;
+			max_dist = dist;
+		}
+	}
+
+	//first two points are (a, b)
+	int a = min_index[bi], b = max_index[bi], c = -1, d = -1;
+
+	if (!find_third_point(num_points, points, a, b, &c))
+		return -2;
+
+	if (!find_fourth_point(num_points, points, a, b, c, &d))
+		return -3;
+
+	initial_vertices[0] = a;
+	initial_vertices[1] = b;
+	initial_vertices[2] = c;
+	initial_vertices[3] = d;
+	return 0;
+}
+
+static bool visible(const double* w, const double* plane_point, const double* plane_normal)
+{
+	return point_plane_distance(w, plane_point, plane_normal) > 0;
+}
+
+void add_facet(const double (*points)[3], int a, int b, int c, int8_t* facet, double* plane_normal, double* barycentre)
+{
+	calculate_plane_normal(points, a, b, c, plane_normal);
+	if (visible(barycentre, points[a], plane_normal))
+	{
+		plane_normal[0] = -plane_normal[0];
+		plane_normal[1] = -plane_normal[1];
+		plane_normal[2] = -plane_normal[2];
+
+		facet[0] = b;
+		facet[1] = a;
+		facet[2] = c;
+	}
+	else
+	{
+		facet[0] = a;
+		facet[1] = b;
+		facet[2] = c;
+	}
+}
+
+static int initialize_convex_hull(int num_points, const double (*points)[3], int8_t facets[][3], double plane_normal[][3], bool* processed, int* initial_vertices, double* barycentre)
+{
+	memset(processed, 0, PTM_MAX_POINTS * sizeof(bool));
+	memset(barycentre, 0, 3 * sizeof(double));
+	int ret = initial_simplex(num_points, points, initial_vertices);
+	if (ret != 0)
+		return ret;
+
+	for (int i = 0;i<4;i++)
+	{
+		int a = initial_vertices[i];
+		processed[a] = true;
+
+		barycentre[0] += points[a][0];
+		barycentre[1] += points[a][1];
+		barycentre[2] += points[a][2];
+	}
+	barycentre[0] /= 4;
+	barycentre[1] /= 4;
+	barycentre[2] /= 4;
+
+	add_facet(points, initial_vertices[0], initial_vertices[1], initial_vertices[2], facets[0], plane_normal[0], barycentre);
+	add_facet(points, initial_vertices[0], initial_vertices[1], initial_vertices[3], facets[1], plane_normal[1], barycentre);
+	add_facet(points, initial_vertices[0], initial_vertices[2], initial_vertices[3], facets[2], plane_normal[2], barycentre);
+	add_facet(points, initial_vertices[1], initial_vertices[2], initial_vertices[3], facets[3], plane_normal[3], barycentre);
+	return 0;
+}
+
+int get_convex_hull(int num_points, const double (*points)[3], convexhull_t* ch, int8_t simplex[][3])
+{
+	assert(	num_points == PTM_NUM_POINTS_FCC
+		|| num_points == PTM_NUM_POINTS_HCP
+		|| num_points == PTM_NUM_POINTS_BCC
+		|| num_points == PTM_NUM_POINTS_ICO
+		|| num_points == PTM_NUM_POINTS_SC
+		|| num_points == PTM_NUM_POINTS_DCUB
+		|| num_points == PTM_NUM_POINTS_DHEX);
+
+	int ret = 0;
+	int num_prev = ch->num_prev;
+	ch->num_prev = num_points;
+	if (!ch->ok || 0)
+	{
+		ret = initialize_convex_hull(num_points, points, ch->facets, ch->plane_normal, ch->processed, ch->initial_vertices, ch->barycentre);
+		if (ret != 0)
+			return ret;
+
+		ch->num_facets = 4;
+		num_prev = 0;
+	}
+
+	for (int i = num_prev;i<num_points;i++)
+	{
+		if (ch->processed[i])
+			continue;
+		ch->processed[i] = true;
+
+		int num_to_add = 0;
+		int8_t to_add[PTM_MAX_FACETS][3];
+		int8_t edge_visible[PTM_MAX_POINTS][PTM_MAX_POINTS];
+		memset(edge_visible, 0, sizeof(int8_t) * PTM_MAX_POINTS * PTM_MAX_POINTS);
+		for (int j = 0;j<ch->num_facets;j++)
+		{
+			int a = ch->facets[j][0];
+			int b = ch->facets[j][1];
+			int c = ch->facets[j][2];
+
+			int u = 0, v = 0, w = 0;
+
+			double distance = point_plane_distance(points[i], points[a], ch->plane_normal[j]);
+			bool vis = distance > TOLERANCE;
+			if (vis)
+			{
+				u = edge_visible[a][b] |= VISIBLE;
+				edge_visible[b][a] |= VISIBLE;
+
+				v = edge_visible[b][c] |= VISIBLE;
+				edge_visible[c][b] |= VISIBLE;
+
+				w = edge_visible[c][a] |= VISIBLE;
+				edge_visible[a][c] |= VISIBLE;
+
+				memcpy(ch->facets[j], ch->facets[ch->num_facets-1], 3 * sizeof(int8_t));
+				memcpy(ch->plane_normal[j], ch->plane_normal[ch->num_facets-1], 3 * sizeof(double));
+				ch->num_facets--;
+				j--;
+			}
+			else
+			{
+				u = edge_visible[a][b] |= INVISIBLE;
+				edge_visible[b][a] |= INVISIBLE;
+
+				v = edge_visible[b][c] |= INVISIBLE;
+				edge_visible[c][b] |= INVISIBLE;
+
+				w = edge_visible[c][a] |= INVISIBLE;
+				edge_visible[a][c] |= INVISIBLE;
+			}
+
+			if (u == BOTH)
+			{
+				to_add[num_to_add][0] = i;
+				to_add[num_to_add][1] = a;
+				to_add[num_to_add][2] = b;
+				num_to_add++;
+			}
+
+			if (v == BOTH)
+			{
+				to_add[num_to_add][0] = i;
+				to_add[num_to_add][1] = b;
+				to_add[num_to_add][2] = c;
+				num_to_add++;
+			}
+
+			if (w == BOTH)
+			{
+				to_add[num_to_add][0] = i;
+				to_add[num_to_add][1] = c;
+				to_add[num_to_add][2] = a;
+				num_to_add++;
+			}
+		}
+
+		for (int j = 0;j<num_to_add;j++)
+		{
+			if (ch->num_facets >= PTM_MAX_FACETS)
+				return -4;
+
+			add_facet(points, to_add[j][0], to_add[j][1], to_add[j][2], ch->facets[ch->num_facets], ch->plane_normal[ch->num_facets], ch->barycentre); ch->num_facets++;
+		}
+	}
+
+	for (int i=0;i<ch->num_facets;i++)
+	{
+		int a = ch->facets[i][0];
+		int b = ch->facets[i][1];
+		int c = ch->facets[i][2];
+		if (a == 0 || b == 0 || c == 0)
+			return 1;		//central atom contained in convex hull
+
+		simplex[i][0] = a - 1;
+		simplex[i][1] = b - 1;
+		simplex[i][2] = c - 1;
+	}
+
+	return ret;
+}
+
diff --git a/src/PTM/ptm_convex_hull_incremental.h b/src/PTM/ptm_convex_hull_incremental.h
new file mode 100644
index 0000000000..563a0c436a
--- /dev/null
+++ b/src/PTM/ptm_convex_hull_incremental.h
@@ -0,0 +1,27 @@
+#ifndef PTM_CONVEX_HULL_INCREMENTAL_H
+#define PTM_CONVEX_HULL_INCREMENTAL_H
+
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ptm_constants.h"
+
+
+typedef struct
+{
+	int8_t facets[PTM_MAX_FACETS][3];
+	double plane_normal[PTM_MAX_FACETS][3];
+	bool processed[PTM_MAX_POINTS];
+	int initial_vertices[4];
+	double barycentre[3];
+	int num_facets;
+	int num_prev;
+	bool ok;
+
+} convexhull_t;
+
+void add_facet(const double (*points)[3], int a, int b, int c, int8_t* facet, double* plane_normal, double* barycentre);
+int get_convex_hull(int num_points, const double (*points)[3], convexhull_t* ch, int8_t simplex[][3]);
+
+#endif
+
diff --git a/src/PTM/ptm_deformation_gradient.cpp b/src/PTM/ptm_deformation_gradient.cpp
new file mode 100644
index 0000000000..d566d5ca11
--- /dev/null
+++ b/src/PTM/ptm_deformation_gradient.cpp
@@ -0,0 +1,37 @@
+#include "ptm_deformation_gradient.h"
+
+
+void calculate_deformation_gradient(int num_points, const double (*ideal_points)[3], int8_t* mapping, double (*normalized)[3], const double (*penrose)[3], double* F, double* res)
+{
+	for (int i = 0;i<3;i++)
+	{
+		for (int j = 0;j<3;j++)
+		{
+			double acc = 0.0;
+			for (int k = 0;k<num_points;k++)
+				acc += penrose[k][j] * normalized[mapping[k]][i];
+
+			F[i*3 + j] = acc;
+		}
+	}
+
+	res[0] = 0;
+	res[1] = 0;
+	res[2] = 0;
+
+	for (int k = 0;k<num_points;k++)
+	{
+		for (int i = 0;i<3;i++)
+		{
+			double acc = 0.0;
+			for (int j = 0;j<3;j++)
+			{
+				acc += F[i*3 + j] * ideal_points[k][j];
+			}
+
+			double delta = acc - normalized[mapping[k]][i];
+			res[i] += delta * delta;
+		}
+	}
+}
+
diff --git a/src/PTM/ptm_deformation_gradient.h b/src/PTM/ptm_deformation_gradient.h
new file mode 100644
index 0000000000..7decab4b76
--- /dev/null
+++ b/src/PTM/ptm_deformation_gradient.h
@@ -0,0 +1,142 @@
+#ifndef PTM_DEFORMATION_GRADIENT_H
+#define PTM_DEFORMATION_GRADIENT_H
+
+#include <stdint.h>
+#include "ptm_constants.h"
+
+void calculate_deformation_gradient(int num_points, const double (*ideal_points)[3], int8_t* mapping, double (*normalized)[3], const double (*penrose)[3], double* F, double* res);
+
+//sc
+#define k_sc 0.5
+const double penrose_sc[PTM_NUM_POINTS_SC][3] = {	
+					{0, 0, 0},
+					{0, 0, -k_sc},
+					{0, 0, k_sc},
+					{0, -k_sc, 0},
+					{0, k_sc, 0},
+					{-k_sc, 0, 0},
+					{k_sc, 0, 0},
+				};
+
+//fcc
+#define k_fcc 0.17677669529663678216
+const double penrose_fcc[PTM_NUM_POINTS_FCC][3] = {
+					{0, 0, 0},
+					{0, k_fcc, k_fcc},
+					{0, -k_fcc, -k_fcc},
+					{0, k_fcc, -k_fcc},
+					{0, -k_fcc, k_fcc},
+					{k_fcc, 0, k_fcc},
+					{-k_fcc, 0, -k_fcc},
+					{k_fcc, 0, -k_fcc},
+					{-k_fcc, 0, k_fcc},
+					{k_fcc, k_fcc, -0},
+					{-k_fcc, -k_fcc, 0},
+					{k_fcc, -k_fcc, 0},
+					{-k_fcc, k_fcc, -0},
+				};
+
+//hcp
+#define k_hcp 0.17677669529663678216
+const double penrose_hcp[PTM_NUM_POINTS_HCP][3] = {
+					{0, 0, 0},
+					{k_hcp, 0, k_hcp},
+					{-k_hcp/3, -4*k_hcp/3, -k_hcp/3},
+					{k_hcp, k_hcp, 0},
+					{-k_hcp/3, -k_hcp/3, -4*k_hcp/3},
+					{0, k_hcp, k_hcp},
+					{-4*k_hcp/3, -k_hcp/3, -k_hcp/3},
+					{-k_hcp, k_hcp, -0},
+					{0, k_hcp, -k_hcp},
+					{k_hcp, 0, -k_hcp},
+					{k_hcp, -k_hcp, 0},
+					{-k_hcp, 0, k_hcp},
+					{0, -k_hcp, k_hcp},
+				};
+
+//ico
+#define k_ico 0.13143277802974323576
+#define phi 1.61803398874989490253
+//((1.0 + sqrt(5)) / 2)
+const double penrose_ico[PTM_NUM_POINTS_ICO][3] = {
+					{0, 0, 0},
+					{0, k_ico, phi*k_ico},
+					{0, -k_ico, -phi*k_ico},
+					{0, k_ico, -phi*k_ico},
+					{0, -k_ico, phi*k_ico},
+					{-k_ico, -phi*k_ico, -0},
+					{k_ico, phi*k_ico, 0},
+					{k_ico, -phi*k_ico, 0},
+					{-k_ico, phi*k_ico, -0},
+					{-phi*k_ico, 0, -k_ico},
+					{phi*k_ico, 0, k_ico},
+					{phi*k_ico, 0, -k_ico},
+					{-phi*k_ico, 0, k_ico},
+				};
+
+//bcc
+#define k_bcc 0.11543038598460284017
+const double penrose_bcc[PTM_NUM_POINTS_BCC][3] = {
+					{0, 0, 0},
+					{-k_bcc, -k_bcc, -k_bcc},
+					{k_bcc, k_bcc, k_bcc},
+					{k_bcc, -k_bcc, -k_bcc},
+					{-k_bcc, k_bcc, k_bcc},
+					{-k_bcc, k_bcc, -k_bcc},
+					{k_bcc, -k_bcc, k_bcc},
+					{-k_bcc, -k_bcc, k_bcc},
+					{k_bcc, k_bcc, -k_bcc},
+					{0, 0, -2*k_bcc},
+					{0, 0, 2*k_bcc},
+					{0, -2*k_bcc, 0},
+					{0, 2*k_bcc, 0},
+					{-2*k_bcc, 0, 0},
+					{2*k_bcc, 0, -0},
+				};
+
+//dcub
+#define kdcub 0.07095369570691034689
+const double penrose_dcub[PTM_NUM_POINTS_DCUB][3] = {
+					{          0,          0,          0 },
+					{     -kdcub,      kdcub,      kdcub },
+					{     -kdcub,     -kdcub,     -kdcub },
+					{      kdcub,     -kdcub,      kdcub },
+					{      kdcub,      kdcub,     -kdcub },
+					{ -2 * kdcub,          0,  2 * kdcub },
+					{ -2 * kdcub,  2 * kdcub,          0 },
+					{          0,  2 * kdcub,  2 * kdcub },
+					{ -2 * kdcub, -2 * kdcub,          0 },
+					{ -2 * kdcub,          0, -2 * kdcub },
+					{          0, -2 * kdcub, -2 * kdcub },
+					{          0, -2 * kdcub,  2 * kdcub },
+					{  2 * kdcub, -2 * kdcub,          0 },
+					{  2 * kdcub,          0,  2 * kdcub },
+					{          0,  2 * kdcub, -2 * kdcub },
+					{  2 * kdcub,          0, -2 * kdcub },
+				 	{  2 * kdcub,  2 * kdcub,          0 },
+				};
+
+
+#define kdhex 0.04730246380471011397
+const double penrose_dhex[PTM_NUM_POINTS_DHEX][3] = {
+					{          0,          0,           0 },
+					{     -kdcub,     -kdcub,      -kdcub },
+					{      kdcub,     -kdcub,       kdcub },
+					{     -kdcub,      kdcub,       kdcub },
+					{      kdcub,      kdcub,      -kdcub },
+					{     -kdhex, -4 * kdhex,      -kdhex },
+					{ -4 * kdhex,     -kdhex,      -kdhex },
+					{     -kdhex,     -kdhex,  -4 * kdhex },
+					{  2 * kdcub,          0,   2 * kdcub },
+					{  2 * kdcub, -2 * kdcub,           0 },
+					{          0, -2 * kdcub,   2 * kdcub },
+					{          0,  2 * kdcub,   2 * kdcub },
+					{ -2 * kdcub,  2 * kdcub,           0 },
+					{ -2 * kdcub,          0,   2 * kdcub },
+					{  2 * kdcub,  2 * kdcub,           0 },
+					{          0,  2 * kdcub,  -2 * kdcub },
+					{  2 * kdcub,          0,  -2 * kdcub },
+				};
+#endif
+
+
diff --git a/src/PTM/ptm_functions.h b/src/PTM/ptm_functions.h
new file mode 100644
index 0000000000..cd67d4940d
--- /dev/null
+++ b/src/PTM/ptm_functions.h
@@ -0,0 +1,27 @@
+#ifndef PTM_FUNCTIONS_H
+#define PTM_FUNCTIONS_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "ptm_initialize_data.h"
+#include "ptm_constants.h"
+
+
+//------------------------------------
+//    function declarations
+//------------------------------------
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+int ptm_index(	ptm_local_handle_t local_handle, int32_t flags, int num_points, double (*atomic_positions)[3], int32_t* atomic_numbers, bool topological_ordering,	//inputs
+		int32_t* p_type, int32_t* p_alloy_type, double* p_scale, double* p_rmsd, double* q, double* F, double* F_res, double* U, double* P, int8_t* mapping, double* p_interatomic_distance, double* p_lattice_constant);	//outputs
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/PTM/ptm_fundamental_mappings.h b/src/PTM/ptm_fundamental_mappings.h
new file mode 100644
index 0000000000..3dd7c39cbb
--- /dev/null
+++ b/src/PTM/ptm_fundamental_mappings.h
@@ -0,0 +1,180 @@
+#ifndef PTM_FUNDAMENTAL_MAPPINGS_H
+#define PTM_FUNDAMENTAL_MAPPINGS_H
+
+#include <stdint.h>
+
+#define NUM_CUBIC_MAPPINGS 24
+#define NUM_ICO_MAPPINGS 60
+#define NUM_HEX_MAPPINGS 6
+#define NUM_DCUB_MAPPINGS 12
+#define NUM_DHEX_MAPPINGS 3
+
+const int8_t mapping_sc[NUM_CUBIC_MAPPINGS][PTM_MAX_POINTS] = {
+					{0, 1, 2, 3, 4, 5, 6},
+					{0, 2, 1, 4, 3, 5, 6},
+					{0, 2, 1, 3, 4, 6, 5},
+					{0, 1, 2, 4, 3, 6, 5},
+					{0, 3, 4, 5, 6, 1, 2},
+					{0, 5, 6, 2, 1, 4, 3},
+					{0, 6, 5, 1, 2, 4, 3},
+					{0, 4, 3, 5, 6, 2, 1},
+					{0, 5, 6, 1, 2, 3, 4},
+					{0, 4, 3, 6, 5, 1, 2},
+					{0, 3, 4, 6, 5, 2, 1},
+					{0, 6, 5, 2, 1, 3, 4},
+					{0, 3, 4, 2, 1, 5, 6},
+					{0, 6, 5, 3, 4, 1, 2},
+					{0, 1, 2, 5, 6, 4, 3},
+					{0, 4, 3, 1, 2, 5, 6},
+					{0, 5, 6, 3, 4, 2, 1},
+					{0, 1, 2, 6, 5, 3, 4},
+					{0, 2, 1, 5, 6, 3, 4},
+					{0, 5, 6, 4, 3, 1, 2},
+					{0, 3, 4, 1, 2, 6, 5},
+					{0, 2, 1, 6, 5, 4, 3},
+					{0, 6, 5, 4, 3, 2, 1},
+					{0, 4, 3, 2, 1, 6, 5}	};
+
+const int8_t mapping_fcc[NUM_CUBIC_MAPPINGS][PTM_MAX_POINTS] = {
+					{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
+					{0, 2, 1, 4, 3, 7, 8, 5, 6, 11, 12, 9, 10},
+					{0, 3, 4, 1, 2, 6, 5, 8, 7, 12, 11, 10, 9},
+					{0, 4, 3, 2, 1, 8, 7, 6, 5, 10, 9, 12, 11},
+					{0, 9, 10, 11, 12, 1, 2, 4, 3, 5, 6, 8, 7},
+					{0, 7, 8, 6, 5, 11, 12, 10, 9, 2, 1, 4, 3},
+					{0, 8, 7, 5, 6, 10, 9, 11, 12, 4, 3, 2, 1},
+					{0, 11, 12, 9, 10, 2, 1, 3, 4, 7, 8, 6, 5},
+					{0, 5, 6, 8, 7, 9, 10, 12, 11, 1, 2, 3, 4},
+					{0, 10, 9, 12, 11, 4, 3, 1, 2, 8, 7, 5, 6},
+					{0, 12, 11, 10, 9, 3, 4, 2, 1, 6, 5, 7, 8},
+					{0, 6, 5, 7, 8, 12, 11, 9, 10, 3, 4, 1, 2},
+					{0, 3, 4, 2, 1, 9, 10, 11, 12, 7, 8, 5, 6},
+					{0, 12, 11, 9, 10, 8, 7, 5, 6, 1, 2, 4, 3},
+					{0, 5, 6, 7, 8, 4, 3, 2, 1, 11, 12, 10, 9},
+					{0, 4, 3, 1, 2, 11, 12, 9, 10, 5, 6, 7, 8},
+					{0, 9, 10, 12, 11, 7, 8, 6, 5, 3, 4, 2, 1},
+					{0, 8, 7, 6, 5, 1, 2, 3, 4, 12, 11, 9, 10},
+					{0, 7, 8, 5, 6, 3, 4, 1, 2, 9, 10, 12, 11},
+					{0, 11, 12, 10, 9, 5, 6, 8, 7, 4, 3, 1, 2},
+					{0, 1, 2, 4, 3, 12, 11, 10, 9, 8, 7, 6, 5},
+					{0, 6, 5, 8, 7, 2, 1, 4, 3, 10, 9, 11, 12},
+					{0, 10, 9, 11, 12, 6, 5, 7, 8, 2, 1, 3, 4},
+					{0, 2, 1, 3, 4, 10, 9, 12, 11, 6, 5, 8, 7}	};
+
+const int8_t mapping_bcc[NUM_CUBIC_MAPPINGS][PTM_MAX_POINTS] = {
+					{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14},
+					{0, 4, 3, 2, 1, 7, 8, 5, 6, 10, 9, 12, 11, 13, 14},
+					{0, 6, 5, 7, 8, 2, 1, 3, 4, 10, 9, 11, 12, 14, 13},
+					{0, 8, 7, 5, 6, 3, 4, 2, 1, 9, 10, 12, 11, 14, 13},
+					{0, 1, 2, 7, 8, 3, 4, 5, 6, 11, 12, 13, 14, 9, 10},
+					{0, 4, 3, 7, 8, 5, 6, 2, 1, 13, 14, 10, 9, 12, 11},
+					{0, 8, 7, 3, 4, 2, 1, 5, 6, 14, 13, 9, 10, 12, 11},
+					{0, 4, 3, 5, 6, 2, 1, 7, 8, 12, 11, 13, 14, 10, 9},
+					{0, 1, 2, 5, 6, 7, 8, 3, 4, 13, 14, 9, 10, 11, 12},
+					{0, 8, 7, 2, 1, 5, 6, 3, 4, 12, 11, 14, 13, 9, 10},
+					{0, 6, 5, 3, 4, 7, 8, 2, 1, 11, 12, 14, 13, 10, 9},
+					{0, 6, 5, 2, 1, 3, 4, 7, 8, 14, 13, 10, 9, 11, 12},
+					{0, 7, 8, 6, 5, 1, 2, 4, 3, 11, 12, 10, 9, 13, 14},
+					{0, 3, 4, 6, 5, 8, 7, 1, 2, 14, 13, 11, 12, 9, 10},
+					{0, 5, 6, 1, 2, 8, 7, 4, 3, 9, 10, 13, 14, 12, 11},
+					{0, 5, 6, 8, 7, 4, 3, 1, 2, 12, 11, 9, 10, 13, 14},
+					{0, 7, 8, 1, 2, 4, 3, 6, 5, 13, 14, 11, 12, 10, 9},
+					{0, 3, 4, 8, 7, 1, 2, 6, 5, 9, 10, 14, 13, 11, 12},
+					{0, 7, 8, 4, 3, 6, 5, 1, 2, 10, 9, 13, 14, 11, 12},
+					{0, 5, 6, 4, 3, 1, 2, 8, 7, 13, 14, 12, 11, 9, 10},
+					{0, 3, 4, 1, 2, 6, 5, 8, 7, 11, 12, 9, 10, 14, 13},
+					{0, 2, 1, 6, 5, 4, 3, 8, 7, 10, 9, 14, 13, 12, 11},
+					{0, 2, 1, 8, 7, 6, 5, 4, 3, 14, 13, 12, 11, 10, 9},
+					{0, 2, 1, 4, 3, 8, 7, 6, 5, 12, 11, 10, 9, 14, 13}	};
+
+const int8_t mapping_ico[NUM_ICO_MAPPINGS][PTM_MAX_POINTS] = {
+					{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
+					{0, 10, 9, 8, 7, 5, 6, 2, 1, 12, 11, 3, 4},
+					{0, 1, 2, 9, 10, 7, 8, 11, 12, 5, 6, 3, 4},
+					{0, 4, 3, 8, 7, 2, 1, 11, 12, 9, 10, 6, 5},
+					{0, 6, 5, 9, 10, 4, 3, 7, 8, 12, 11, 2, 1},
+					{0, 12, 11, 3, 4, 7, 8, 10, 9, 2, 1, 6, 5},
+					{0, 4, 3, 6, 5, 9, 10, 2, 1, 8, 7, 11, 12},
+					{0, 8, 7, 2, 1, 4, 3, 10, 9, 5, 6, 11, 12},
+					{0, 10, 9, 3, 4, 12, 11, 5, 6, 8, 7, 2, 1},
+					{0, 12, 11, 6, 5, 2, 1, 7, 8, 3, 4, 10, 9},
+					{0, 1, 2, 11, 12, 9, 10, 5, 6, 3, 4, 7, 8},
+					{0, 8, 7, 11, 12, 5, 6, 4, 3, 2, 1, 10, 9},
+					{0, 6, 5, 2, 1, 12, 11, 4, 3, 9, 10, 7, 8},
+					{0, 3, 4, 5, 6, 1, 2, 10, 9, 12, 11, 7, 8},
+					{0, 3, 4, 7, 8, 12, 11, 1, 2, 5, 6, 10, 9},
+					{0, 6, 5, 7, 8, 9, 10, 12, 11, 2, 1, 4, 3},
+					{0, 9, 10, 11, 12, 4, 3, 1, 2, 7, 8, 6, 5},
+					{0, 11, 12, 9, 10, 1, 2, 4, 3, 8, 7, 5, 6},
+					{0, 8, 7, 5, 6, 10, 9, 11, 12, 4, 3, 2, 1},
+					{0, 10, 9, 2, 1, 8, 7, 12, 11, 3, 4, 5, 6},
+					{0, 12, 11, 2, 1, 10, 9, 6, 5, 7, 8, 3, 4},
+					{0, 9, 10, 6, 5, 7, 8, 4, 3, 11, 12, 1, 2},
+					{0, 8, 7, 10, 9, 2, 1, 5, 6, 11, 12, 4, 3},
+					{0, 6, 5, 12, 11, 7, 8, 2, 1, 4, 3, 9, 10},
+					{0, 11, 12, 8, 7, 4, 3, 5, 6, 1, 2, 9, 10},
+					{0, 4, 3, 11, 12, 8, 7, 9, 10, 6, 5, 2, 1},
+					{0, 4, 3, 9, 10, 11, 12, 6, 5, 2, 1, 8, 7},
+					{0, 12, 11, 10, 9, 3, 4, 2, 1, 6, 5, 7, 8},
+					{0, 5, 6, 8, 7, 11, 12, 10, 9, 3, 4, 1, 2},
+					{0, 7, 8, 6, 5, 12, 11, 9, 10, 1, 2, 3, 4},
+					{0, 10, 9, 12, 11, 2, 1, 3, 4, 5, 6, 8, 7},
+					{0, 7, 8, 1, 2, 9, 10, 3, 4, 12, 11, 6, 5},
+					{0, 5, 6, 1, 2, 3, 4, 11, 12, 8, 7, 10, 9},
+					{0, 7, 8, 12, 11, 3, 4, 6, 5, 9, 10, 1, 2},
+					{0, 1, 2, 5, 6, 11, 12, 3, 4, 7, 8, 9, 10},
+					{0, 11, 12, 1, 2, 5, 6, 9, 10, 4, 3, 8, 7},
+					{0, 5, 6, 3, 4, 10, 9, 1, 2, 11, 12, 8, 7},
+					{0, 5, 6, 10, 9, 8, 7, 3, 4, 1, 2, 11, 12},
+					{0, 3, 4, 12, 11, 10, 9, 7, 8, 1, 2, 5, 6},
+					{0, 9, 10, 7, 8, 1, 2, 6, 5, 4, 3, 11, 12},
+					{0, 9, 10, 1, 2, 11, 12, 7, 8, 6, 5, 4, 3},
+					{0, 7, 8, 3, 4, 1, 2, 12, 11, 6, 5, 9, 10},
+					{0, 11, 12, 5, 6, 8, 7, 1, 2, 9, 10, 4, 3},
+					{0, 1, 2, 7, 8, 3, 4, 9, 10, 11, 12, 5, 6},
+					{0, 3, 4, 10, 9, 5, 6, 12, 11, 7, 8, 1, 2},
+					{0, 2, 1, 4, 3, 8, 7, 6, 5, 12, 11, 10, 9},
+					{0, 2, 1, 12, 11, 6, 5, 10, 9, 8, 7, 4, 3},
+					{0, 9, 10, 4, 3, 6, 5, 11, 12, 1, 2, 7, 8},
+					{0, 11, 12, 4, 3, 9, 10, 8, 7, 5, 6, 1, 2},
+					{0, 2, 1, 10, 9, 12, 11, 8, 7, 4, 3, 6, 5},
+					{0, 5, 6, 11, 12, 1, 2, 8, 7, 10, 9, 3, 4},
+					{0, 10, 9, 5, 6, 3, 4, 8, 7, 2, 1, 12, 11},
+					{0, 12, 11, 7, 8, 6, 5, 3, 4, 10, 9, 2, 1},
+					{0, 7, 8, 9, 10, 6, 5, 1, 2, 3, 4, 12, 11},
+					{0, 2, 1, 8, 7, 10, 9, 4, 3, 6, 5, 12, 11},
+					{0, 8, 7, 4, 3, 11, 12, 2, 1, 10, 9, 5, 6},
+					{0, 6, 5, 4, 3, 2, 1, 9, 10, 7, 8, 12, 11},
+					{0, 2, 1, 6, 5, 4, 3, 12, 11, 10, 9, 8, 7},
+					{0, 3, 4, 1, 2, 7, 8, 5, 6, 10, 9, 12, 11},
+					{0, 4, 3, 2, 1, 6, 5, 8, 7, 11, 12, 9, 10}	};
+
+const int8_t mapping_hcp[NUM_HEX_MAPPINGS][PTM_MAX_POINTS] = {
+					{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12},
+					{0, 5, 6, 1, 2, 3, 4, 9, 10, 12, 11, 8, 7},
+					{0, 3, 4, 5, 6, 1, 2, 12, 11, 7, 8, 10, 9},
+					{0, 4, 3, 2, 1, 6, 5, 11, 12, 10, 9, 7, 8},
+					{0, 2, 1, 6, 5, 4, 3, 8, 7, 11, 12, 9, 10},
+					{0, 6, 5, 4, 3, 2, 1, 10, 9, 8, 7, 12, 11}	};
+
+const int8_t mapping_dcub[NUM_DCUB_MAPPINGS][PTM_MAX_POINTS] = {
+					{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
+					{0, 2, 1, 4, 3, 9, 8, 10, 6, 5, 7, 14, 16, 15, 11, 13, 12},
+					{0, 4, 3, 2, 1, 15, 16, 14, 12, 13, 11, 10, 8, 9, 7, 5, 6},
+					{0, 3, 4, 1, 2, 13, 12, 11, 16, 15, 14, 7, 6, 5, 10, 9, 8},
+					{0, 4, 2, 1, 3, 14, 15, 16, 9, 10, 8, 6, 5, 7, 12, 11, 13},
+					{0, 4, 1, 3, 2, 16, 14, 15, 7, 6, 5, 13, 11, 12, 9, 8, 10},
+					{0, 1, 4, 2, 3, 6, 7, 5, 14, 16, 15, 9, 10, 8, 13, 12, 11},
+					{0, 3, 1, 2, 4, 11, 13, 12, 5, 7, 6, 8, 9, 10, 16, 14, 15},
+					{0, 3, 2, 4, 1, 12, 11, 13, 10, 8, 9, 15, 14, 16, 5, 6, 7},
+					{0, 2, 4, 3, 1, 10, 9, 8, 15, 14, 16, 12, 13, 11, 6, 7, 5},
+					{0, 1, 3, 4, 2, 7, 5, 6, 13, 11, 12, 16, 15, 14, 8, 10, 9},
+					{0, 2, 3, 1, 4, 8, 10, 9, 11, 12, 13, 5, 7, 6, 15, 16, 14}	};
+
+const int8_t mapping_dhex[NUM_DHEX_MAPPINGS][PTM_MAX_POINTS] = {
+					{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16},
+					{0, 1, 3, 4, 2, 6, 7, 5, 11, 13, 12, 14, 16, 15, 8, 9, 10},
+					{0, 1, 4, 2, 3, 7, 5, 6, 14, 15, 16, 8, 10, 9, 11, 13, 12}	};
+
+#endif
+
diff --git a/src/PTM/ptm_graph_data.cpp b/src/PTM/ptm_graph_data.cpp
new file mode 100644
index 0000000000..a591dbf993
--- /dev/null
+++ b/src/PTM/ptm_graph_data.cpp
@@ -0,0 +1,2059 @@
+#include "ptm_graph_data.h"
+
+
+int8_t automorphisms[65][17] = {
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, -1, -1, -1, -1},
+	{  0,  4,  3, 10,  9,  5,  6, 12, 11,  8,  7,  1,  2, -1, -1, -1, -1},
+	{  0,  5,  6, 11, 12,  8,  7,  2,  1,  4,  3, 10,  9, -1, -1, -1, -1},
+	{  0,  8,  7,  1,  2,  4,  3,  9, 10,  5,  6, 11, 12, -1, -1, -1, -1},
+	{  0,  8,  7, 10,  9,  1,  2,  6,  5, 12, 11,  3,  4, -1, -1, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, -1, -1, -1, -1},
+	{  0, 12,  3,  2,  7, 10,  8,  4,  6, 11,  5,  9,  1, -1, -1, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, -1, -1, -1, -1},
+	{  0,  4, 11,  8,  1,  9, 12, 10,  3,  5,  7,  2,  6, -1, -1, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, -1, -1, -1, -1},
+	{  0,  2,  1,  6,  5,  4,  3,  9,  8,  7, 11, 10, 12, -1, -1, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, -1, -1, -1, -1},
+	{  0,  1,  7, 10, 11,  9,  6,  4,  2, 12,  5,  8,  3, -1, -1, -1, -1},
+	{  0,  1, 11,  9,  2,  3,  6,  8,  4, 10, 12,  7,  5, -1, -1, -1, -1},
+	{  0,  3,  6,  1, 11,  9,  2, 10, 12,  5,  7,  4,  8, -1, -1, -1, -1},
+	{  0,  3, 11,  9, 12,  8,  2,  4, 10,  1,  5,  6,  7, -1, -1, -1, -1},
+	{  0,  8,  2,  3, 11,  9, 12,  1,  5,  7,  6, 10,  4, -1, -1, -1, -1},
+	{  0,  9,  2,  3,  6,  1, 11,  5,  7,  8,  4, 12, 10, -1, -1, -1, -1},
+	{  0,  9,  6,  1,  7, 10, 11, 12,  5,  3,  8,  2,  4, -1, -1, -1, -1},
+	{  0,  9, 12,  8,  2,  3, 11,  7,  6,  4, 10,  5,  1, -1, -1, -1, -1},
+	{  0, 10, 11,  9,  6,  1,  7,  3,  8,  4,  2,  5, 12, -1, -1, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, -1, -1, -1, -1},
+	{  0,  3,  2,  8,  6,  5, 12, 11,  7,  4,  9,  1, 10, -1, -1, -1, -1},
+	{  0,  3, 11, 10,  6,  9,  7,  4,  2, 12,  1,  8,  5, -1, -1, -1, -1},
+	{  0,  3, 12,  9,  6,  8, 11,  7,  4,  2, 10,  5,  1, -1, -1, -1, -1},
+	{  0,  5, 12,  3,  2,  8,  6,  4,  9, 10,  1,  7, 11, -1, -1, -1, -1},
+	{  0,  8,  6,  5, 12,  3,  2, 10,  1, 11,  7,  9,  4, -1, -1, -1, -1},
+	{  0,  8, 11,  3, 12,  9,  6,  2, 10,  1,  5,  4,  7, -1, -1, -1, -1},
+	{  0,  9,  6,  8, 11,  3, 12,  1,  5,  7,  4, 10,  2, -1, -1, -1, -1},
+	{  0,  9,  7,  3, 11, 10,  6, 12,  1,  5,  8,  2,  4, -1, -1, -1, -1},
+	{  0, 10,  6,  9,  7,  3, 11,  5,  8,  4,  2,  1, 12, -1, -1, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0,  3,  4,  6,  5,  2,  1,  9, 10, 14, 13, 11, 12,  8,  7, -1, -1},
+	{  0,  4,  3,  1,  2,  5,  6, 10,  9, 13, 14,  7,  8, 12, 11, -1, -1},
+	{  0,  6,  5,  1,  2,  4,  3, 14, 13,  7,  8, 11, 12, 10,  9, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0, 12, 11, 10,  9, 13, 14,  8,  7,  4,  3,  2,  1,  5,  6, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0,  9, 10, 13, 14, 11, 12,  8,  7,  1,  2,  5,  6,  3,  4, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0,  6,  5,  4,  3,  2,  1, 11, 12, 10,  9,  7,  8, 14, 13, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0, 12, 11, 10,  9, 13, 14,  8,  7,  4,  3,  2,  1,  5,  6, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0,  9, 10, 13, 14, 11, 12,  8,  7,  1,  2,  5,  6,  3,  4, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0,  3, 10, 14,  5,  9,  7, 13,  2,  8,  4, 11, 12,  1,  6, -1, -1},
+	{  0, 13,  8,  1, 10,  4, 14,  6,  9,  5,  2, 11, 12,  7,  3, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0, 11, 12, 14, 13,  9, 10,  7,  8,  3,  4,  6,  5,  1,  2, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, -1, -1},
+	{  0, 13, 14, 11, 12,  5,  6, 10,  9,  1,  2,  7,  8,  4,  3, -1, -1},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16},
+	{  0,  4,  3,  2,  1, 15, 14, 16, 13, 12, 11, 10,  9,  8,  6,  5,  7},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16},
+	{  0,  4,  1,  3,  2, 15, 14, 16,  5,  7,  6, 13, 11, 12, 10,  8,  9},
+	{  0,  4,  2,  1,  3, 16, 15, 14, 10,  8,  9,  5,  6,  7, 13, 12, 11},
+	{  0,  4,  3,  2,  1, 14, 16, 15, 13, 12, 11, 10,  9,  8,  5,  7,  6},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16},
+	{  0,  3,  4,  1,  2, 12, 13, 11, 16, 14, 15,  7,  5,  6,  9, 10,  8},
+	{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16},
+	{  0,  4,  1,  3,  2, 16, 15, 14,  6,  5,  7, 13, 11, 12, 10,  8,  9},
+	{  0,  4,  2,  1,  3, 14, 16, 15, 10,  8,  9,  6,  7,  5, 13, 12, 11},
+	{  0,  4,  3,  2,  1, 15, 14, 16, 13, 12, 11, 10,  9,  8,  6,  5,  7},
+};
+
+graph_t graphs_sc[NUM_SC_GRAPHS] = {
+
+{0,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,4},{1,3,5},{0,3,4},{0,3,5},{1,2,5},{1,2,4},{0,2,4},{0,2,5}}},
+
+};
+
+graph_t graphs_ico[NUM_ICO_GRAPHS] = {
+
+{0,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,5,9},{1,2,8},{0,5,7},{2,7,8},{2,5,7},{1,4,8},{1,2,10},{5,9,10},{2,5,10},{4,8,11},{7,8,11},{0,7,11},{0,3,9},{0,3,11},{3,4,11},{3,6,9},{3,4,6},{6,9,10},{1,4,6},{1,6,10}}},
+
+};
+
+graph_t graphs_fcc[NUM_FCC_GRAPHS] = {
+
+{0,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{5,7,11},{5,7,9},{1,2,6},{1,2,5},{1,3,10},{1,3,9},{4,6,10},{4,6,8}}},
+
+{1,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{5,7,11},{5,7,9},{1,2,6},{1,2,5},{1,3,10},{1,3,9},{6,8,10},{4,8,10}}},
+
+{2,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{5,7,11},{5,7,9},{1,2,6},{1,2,5},{3,9,10},{1,9,10},{6,8,10},{4,8,10}}},
+
+{3,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{5,7,11},{5,7,9},{2,5,6},{1,5,6},{1,3,10},{1,3,9},{4,6,10},{4,6,8}}},
+
+{4,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{5,7,11},{5,7,9},{2,5,6},{1,5,6},{1,3,10},{1,3,9},{6,8,10},{4,8,10}}},
+
+{5,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{5,7,11},{5,7,9},{2,5,6},{1,5,6},{3,9,10},{1,9,10},{4,6,10},{4,6,8}}},
+
+{6,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{7,9,11},{5,9,11},{1,2,6},{1,2,5},{3,9,10},{1,9,10},{4,6,10},{4,6,8}}},
+
+{7,
+0,
+1,
+5,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{0,4,8},{3,7,9},{2,5,11},{0,7,11},{1,5,9},{2,6,8},{3,4,10},{1,6,10},{3,4,7},{0,4,7},{0,2,11},{0,2,8},{7,9,11},{5,9,11},{2,5,6},{1,5,6},{1,3,10},{1,3,9},{6,8,10},{4,8,10}}},
+
+};
+
+graph_t graphs_hcp[NUM_HCP_GRAPHS] = {
+
+{0,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{2,8,9},{0,2,9},{0,10,11},{0,4,10},{2,6,7},{2,4,6}}},
+
+{1,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{2,8,9},{0,2,9},{0,10,11},{0,4,10},{4,6,7},{2,4,7}}},
+
+{2,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{2,8,9},{0,2,9},{4,10,11},{0,4,11},{2,6,7},{2,4,6}}},
+
+{3,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{2,8,9},{0,2,9},{4,10,11},{0,4,11},{4,6,7},{2,4,7}}},
+
+{4,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{0,8,9},{0,2,8},{0,10,11},{0,4,10},{2,6,7},{2,4,6}}},
+
+{5,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{0,8,9},{0,2,8},{0,10,11},{0,4,10},{4,6,7},{2,4,7}}},
+
+{6,
+0,
+6,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{0,8,9},{0,2,8},{4,10,11},{0,4,11},{2,6,7},{2,4,6}}},
+
+{7,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{3,8,9},{1,3,9},{0,8,9},{0,2,8},{4,10,11},{0,4,11},{4,6,7},{2,4,7}}},
+
+{8,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{1,8,9},{1,3,8},{2,8,9},{0,2,9},{0,10,11},{0,4,10},{2,6,7},{2,4,6}}},
+
+{9,
+0,
+8,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{1,8,9},{1,3,8},{2,8,9},{0,2,9},{0,10,11},{0,4,10},{4,6,7},{2,4,7}}},
+
+{10,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{1,8,9},{1,3,8},{2,8,9},{0,2,9},{4,10,11},{0,4,11},{4,6,7},{2,4,7}}},
+
+{11,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{1,8,9},{1,3,8},{0,8,9},{0,2,8},{0,10,11},{0,4,10},{4,6,7},{2,4,7}}},
+
+{12,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{3,6,7},{3,5,6},{1,8,9},{1,3,8},{0,8,9},{0,2,8},{4,10,11},{0,4,11},{2,6,7},{2,4,6}}},
+
+{13,
+0,
+10,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{5,6,7},{3,5,7},{3,8,9},{1,3,9},{2,8,9},{0,2,9},{0,10,11},{0,4,10},{4,6,7},{2,4,7}}},
+
+{14,
+0,
+12,
+10,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{1,10,11},{1,5,10},{5,6,7},{3,5,7},{3,8,9},{1,3,9},{0,8,9},{0,2,8},{4,10,11},{0,4,11},{2,6,7},{2,4,6}}},
+
+{15,
+0,
+22,
+10,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,5},{5,6,10},{0,9,11},{1,9,11},{3,7,8},{2,7,8},{4,6,10},{0,2,4},{5,10,11},{1,5,11},{3,6,7},{3,5,6},{1,8,9},{1,3,8},{2,8,9},{0,2,9},{0,10,11},{0,4,10},{4,6,7},{2,4,7}}},
+
+};
+
+graph_t graphs_bcc[NUM_BCC_GRAPHS] = {
+
+{0,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{1,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{2,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{3,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{4,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{5,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{6,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{7,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{8,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{9,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{10,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{11,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{12,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{13,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{14,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{15,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{16,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{17,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{18,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{19,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{20,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{21,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{22,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{23,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{24,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{25,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{26,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{27,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{28,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{29,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{30,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{31,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{32,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{33,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{34,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{35,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{36,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{37,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{38,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{39,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{40,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{41,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{42,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{43,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{44,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{45,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{46,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{47,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{48,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{49,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{50,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{51,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{52,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{53,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{54,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{55,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{56,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{57,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{58,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{59,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{60,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{61,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{62,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{63,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{64,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{65,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{66,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{67,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{68,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{69,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{70,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{71,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{72,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{73,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{74,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{75,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{76,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{77,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{78,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{79,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{80,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{81,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{82,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{83,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{84,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{85,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{86,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{87,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{88,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{89,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{90,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{91,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{92,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{93,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{94,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{95,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{96,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{97,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{98,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{99,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{100,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{101,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{102,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{103,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{104,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{105,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{106,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{107,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{108,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{109,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{110,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{111,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{112,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{113,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{114,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{115,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{116,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{117,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{118,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{119,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{120,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{121,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{122,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{123,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{124,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{125,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{126,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{127,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{128,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{129,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{130,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{131,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{132,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{133,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{134,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{135,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{136,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{137,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{138,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{139,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{140,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{141,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{142,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{143,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{144,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{145,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{146,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{147,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{148,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{149,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{150,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{151,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{152,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{153,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{154,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{155,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{156,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{157,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{158,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{159,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{160,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{161,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{162,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{163,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{164,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{165,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{7,11,13},{1,11,13},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{166,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{167,
+0,
+32,
+4,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{168,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{169,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{170,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{171,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{172,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{173,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{174,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{175,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{176,
+0,
+36,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{5,9,13},{1,9,13},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{177,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{178,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{179,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{180,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{6,9,12},{3,9,12},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{181,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{182,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{183,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{184,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{185,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{186,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{187,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{188,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{189,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{190,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{191,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{192,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{193,
+0,
+38,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{194,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{195,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{196,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{197,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{198,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{199,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{200,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{201,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{202,
+0,
+40,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{2,8,10},{0,8,10}}},
+
+{203,
+0,
+42,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{204,
+0,
+44,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{205,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{4,11,12},{3,11,12},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{206,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{207,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{6,9,10},{5,9,10},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{208,
+0,
+46,
+3,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{209,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{210,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{7,8,11},{4,8,11},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{211,
+0,
+49,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{5,10,13},{2,10,13},{0,2,10},{0,2,8}}},
+
+{212,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{4,8,12},{0,8,12},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{213,
+0,
+51,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{7,8,13},{2,8,13},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{214,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{6,10,12},{0,10,12},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{215,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{2,8,10},{0,8,10}}},
+
+{216,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{3,9,11},{1,9,11},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+{217,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{1,3,11},{1,3,9},{3,4,12},{3,4,11},{1,7,13},{1,7,11},{4,7,11},{4,7,8},{1,5,13},{1,5,9},{3,6,12},{3,6,9},{5,6,10},{5,6,9},{0,4,12},{0,4,8},{0,6,12},{0,6,10},{2,7,13},{2,7,8},{2,5,13},{2,5,10},{0,2,10},{0,2,8}}},
+
+};
+
+graph_t graphs_dcub[NUM_DCUB_GRAPHS] = {
+
+{0,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{5,13,15},{5,6,15},{9,13,14},{8,9,13},{11,14,15},{11,12,15}}},
+
+{1,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{5,13,15},{5,6,15},{9,13,14},{8,9,13},{12,14,15},{11,12,14}}},
+
+{2,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{5,13,15},{5,6,15},{8,13,14},{8,9,14},{11,14,15},{11,12,15}}},
+
+{3,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{5,13,15},{5,6,15},{8,13,14},{8,9,14},{12,14,15},{11,12,14}}},
+
+{4,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{6,13,15},{5,6,13},{9,13,14},{8,9,13},{11,14,15},{11,12,15}}},
+
+{5,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{6,13,15},{5,6,13},{8,13,14},{8,9,14},{11,14,15},{11,12,15}}},
+
+{6,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{6,13,15},{5,6,13},{8,13,14},{8,9,14},{12,14,15},{11,12,14}}},
+
+{7,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{9,10,11},{7,9,10},{5,13,15},{5,6,15},{8,13,14},{8,9,14},{12,14,15},{11,12,14}}},
+
+{8,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{9,10,11},{7,9,10},{6,13,15},{5,6,13},{9,13,14},{8,9,13},{11,14,15},{11,12,15}}},
+
+{9,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{6,10,12},{4,6,10},{9,10,11},{7,9,10},{6,13,15},{5,6,13},{8,13,14},{8,9,14},{11,14,15},{11,12,15}}},
+
+{10,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{5,7,8},{4,5,7},{4,10,12},{4,6,12},{9,10,11},{7,9,10},{6,13,15},{5,6,13},{8,13,14},{8,9,14},{11,14,15},{11,12,15}}},
+
+{11,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{4,7,10},{0,5,6},{0,4,5},{0,4,6},{2,11,12},{2,10,12},{2,10,11},{1,7,8},{1,8,9},{1,7,9},{6,12,15},{5,8,13},{3,13,15},{3,13,14},{9,11,14},{3,14,15},{4,7,8},{4,5,8},{6,10,12},{4,6,10},{7,10,11},{7,9,11},{5,13,15},{5,6,15},{9,13,14},{8,9,13},{12,14,15},{11,12,14}}},
+
+};
+
+graph_t graphs_dhex[NUM_DHEX_GRAPHS] = {
+
+{0,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{11,13,14},{10,11,13},{6,11,14},{5,6,11},{4,8,15},{4,6,15},{7,13,15},{7,8,15}}},
+
+{1,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{11,13,14},{10,11,13},{6,11,14},{5,6,11},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{2,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{11,13,14},{10,11,13},{6,11,14},{5,6,11},{6,8,15},{4,6,8},{7,13,15},{7,8,15}}},
+
+{3,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{11,13,14},{10,11,13},{6,11,14},{5,6,11},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{4,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{11,13,14},{10,11,13},{5,11,14},{5,6,14},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{5,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{11,13,14},{10,11,13},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{7,13,15},{7,8,15}}},
+
+{6,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{11,13,14},{10,11,13},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{7,
+0,
+53,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{6,11,14},{5,6,11},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{8,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{6,11,14},{5,6,11},{6,8,15},{4,6,8},{7,13,15},{7,8,15}}},
+
+{9,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{6,11,14},{5,6,11},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{10,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{5,11,14},{5,6,14},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{11,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{7,13,15},{7,8,15}}},
+
+{12,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{13,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{4,9,12},{4,5,12},{11,13,14},{10,11,13},{5,11,14},{5,6,14},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{14,
+0,
+55,
+4,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{4,9,12},{4,5,12},{11,13,14},{10,11,13},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{7,13,15},{7,8,15}}},
+
+{15,
+0,
+59,
+2,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{4,9,12},{4,5,12},{11,13,14},{10,11,13},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{16,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{4,9,12},{4,5,12},{10,13,14},{10,11,14},{6,11,14},{5,6,11},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{17,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{4,9,12},{4,5,12},{10,13,14},{10,11,14},{6,11,14},{5,6,11},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{18,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{4,9,12},{4,5,12},{10,13,14},{10,11,14},{5,11,14},{5,6,14},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{19,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{9,10,12},{7,9,10},{4,9,12},{4,5,12},{10,13,14},{10,11,14},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{20,
+0,
+61,
+4,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{7,10,12},{7,9,12},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{6,11,14},{5,6,11},{4,8,15},{4,6,15},{8,13,15},{7,8,13}}},
+
+{21,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{7,10,12},{7,9,12},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{6,11,14},{5,6,11},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{22,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{7,10,12},{7,9,12},{5,9,12},{4,5,9},{10,13,14},{10,11,14},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+{23,
+0,
+0,
+1,
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+{{7,10,13},{5,11,12},{6,14,15},{0,4,5},{0,5,6},{0,4,6},{4,8,9},{2,10,12},{2,10,11},{2,11,12},{3,13,14},{3,13,15},{3,14,15},{1,7,9},{1,7,8},{1,8,9},{7,10,12},{7,9,12},{4,9,12},{4,5,12},{10,13,14},{10,11,14},{5,11,14},{5,6,14},{6,8,15},{4,6,8},{8,13,15},{7,8,13}}},
+
+};
+
diff --git a/src/PTM/ptm_graph_data.h b/src/PTM/ptm_graph_data.h
new file mode 100644
index 0000000000..11f46a471f
--- /dev/null
+++ b/src/PTM/ptm_graph_data.h
@@ -0,0 +1,37 @@
+#ifndef PTM_GRAPH_DATA_H
+#define PTM_GRAPH_DATA_H
+
+#include <stdint.h>
+#include "ptm_constants.h"
+
+
+typedef struct
+{
+	int id;
+	uint64_t hash;
+	int automorphism_index;
+	int num_automorphisms;
+	int8_t canonical_labelling[PTM_MAX_POINTS];
+	int8_t facets[PTM_MAX_FACETS][3];
+} graph_t;
+
+#define NUM_SC_GRAPHS 1
+#define NUM_ICO_GRAPHS 1
+#define NUM_FCC_GRAPHS 8
+#define NUM_HCP_GRAPHS 16
+#define NUM_BCC_GRAPHS 218
+#define NUM_DCUB_GRAPHS 12
+#define NUM_DHEX_GRAPHS 24
+
+extern int8_t automorphisms[][PTM_MAX_POINTS];
+
+extern graph_t graphs_sc[NUM_SC_GRAPHS];
+extern graph_t graphs_fcc[NUM_FCC_GRAPHS];
+extern graph_t graphs_hcp[NUM_HCP_GRAPHS];
+extern graph_t graphs_ico[NUM_ICO_GRAPHS];
+extern graph_t graphs_bcc[NUM_BCC_GRAPHS];
+extern graph_t graphs_dcub[NUM_DCUB_GRAPHS];
+extern graph_t graphs_dhex[NUM_DHEX_GRAPHS];
+
+#endif
+
diff --git a/src/PTM/ptm_graph_tools.cpp b/src/PTM/ptm_graph_tools.cpp
new file mode 100644
index 0000000000..89d07fc87a
--- /dev/null
+++ b/src/PTM/ptm_graph_tools.cpp
@@ -0,0 +1,52 @@
+#include <string.h>
+#include <algorithm>
+#include "ptm_graph_tools.h"
+#include "ptm_constants.h"
+
+
+bool build_facet_map(int num_facets, int8_t facets[][3], int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS])
+{
+	memset(common, -1, sizeof(int8_t) * PTM_MAX_NBRS * PTM_MAX_NBRS);
+
+	for (int i = 0;i<num_facets;i++)
+	{
+		int a = facets[i][0];
+		int b = facets[i][1];
+		int c = facets[i][2];
+
+		//assert(common[a][b] == -1);
+		//assert(common[b][c] == -1);
+		//assert(common[c][a] == -1);
+		if (common[a][b] != -1 || common[b][c] != -1 || common[c][a] != -1)
+			return false;
+
+		common[a][b] = c;
+		common[b][c] = a;
+		common[c][a] = b;
+	}
+
+	return true;
+}
+
+int graph_degree(int num_facets, int8_t facets[][3], int num_nodes, int8_t* degree)
+{
+	memset(degree, 0, sizeof(int8_t) * num_nodes);
+
+	for (int i = 0;i<num_facets;i++)
+	{
+		int a = facets[i][0];
+		int b = facets[i][1];
+		int c = facets[i][2];
+
+		degree[a]++;
+		degree[b]++;
+		degree[c]++;
+	}
+
+	int8_t max_degree = 0;
+	for (int i = 0;i<num_nodes;i++)
+		max_degree = std::max(max_degree, degree[i]);
+
+	return max_degree;
+}
+
diff --git a/src/PTM/ptm_graph_tools.h b/src/PTM/ptm_graph_tools.h
new file mode 100644
index 0000000000..78934e87c1
--- /dev/null
+++ b/src/PTM/ptm_graph_tools.h
@@ -0,0 +1,11 @@
+#ifndef PTM_GRAPH_TOOLS_H
+#define PTM_GRAPH_TOOLS_H
+
+#include <stdint.h>
+#include "ptm_constants.h"
+
+bool build_facet_map(int num_facets, int8_t facets[][3], int8_t common[PTM_MAX_NBRS][PTM_MAX_NBRS]);
+int graph_degree(int num_facets, int8_t facets[][3], int num_nodes, int8_t* degree);
+
+#endif
+
diff --git a/src/PTM/ptm_index.cpp b/src/PTM/ptm_index.cpp
new file mode 100644
index 0000000000..7b6618848e
--- /dev/null
+++ b/src/PTM/ptm_index.cpp
@@ -0,0 +1,218 @@
+#include <cstdio>
+#include <cstdlib>
+#include <string.h>
+#include <cmath>
+#include <cfloat>
+#include <cassert>
+#include <algorithm>
+#include "ptm_convex_hull_incremental.h"
+#include "ptm_graph_data.h"
+#include "ptm_deformation_gradient.h"
+#include "ptm_alloy_types.h"
+#include "ptm_neighbour_ordering.h"
+#include "ptm_normalize_vertices.h"
+#include "ptm_quat.h"
+#include "ptm_polar.h"
+#include "ptm_initialize_data.h"
+#include "ptm_structure_matcher.h"
+#include "ptm_functions.h"
+#include "ptm_constants.h"
+
+
+//todo: verify that c == norm(template[1])
+static double calculate_interatomic_distance(int type, double scale)
+{
+	assert(type >= 1 && type <= 7);
+	double c[8] = {0, 1, 1, (7. - 3.5 * sqrt(3)), 1, 1, sqrt(3) * 4. / (6 * sqrt(2) + sqrt(3)), sqrt(3) * 4. / (6 * sqrt(2) + sqrt(3))};
+	return c[type] / scale;
+}
+
+static double calculate_lattice_constant(int type, double interatomic_distance)
+{
+	assert(type >= 1 && type <= 7);
+	double c[8] = {0, 2 / sqrt(2), 2 / sqrt(2), 2. / sqrt(3), 2 / sqrt(2), 1, 4 / sqrt(3), 4 / sqrt(3)};
+	return c[type] * interatomic_distance;
+}
+
+static int rotate_into_fundamental_zone(int type, double* q)
+{
+	if (type == PTM_MATCH_SC)	return rotate_quaternion_into_cubic_fundamental_zone(q);
+	if (type == PTM_MATCH_FCC)	return rotate_quaternion_into_cubic_fundamental_zone(q);
+	if (type == PTM_MATCH_BCC)	return rotate_quaternion_into_cubic_fundamental_zone(q);
+	if (type == PTM_MATCH_ICO)	return rotate_quaternion_into_icosahedral_fundamental_zone(q);
+	if (type == PTM_MATCH_HCP)	return rotate_quaternion_into_hcp_fundamental_zone(q);
+	if (type == PTM_MATCH_DCUB)	return rotate_quaternion_into_diamond_cubic_fundamental_zone(q);
+	if (type == PTM_MATCH_DHEX)	return rotate_quaternion_into_diamond_hexagonal_fundamental_zone(q);
+	return -1;
+}
+
+static void order_points(ptm_local_handle_t local_handle, int num_points, double (*unpermuted_points)[3], int32_t* unpermuted_numbers, bool topological_ordering,
+			int8_t* ordering, double (*points)[3], int32_t* numbers)
+{
+	if (topological_ordering)
+	{
+		double normalized_points[PTM_MAX_INPUT_POINTS][3];
+		normalize_vertices(num_points, unpermuted_points, normalized_points);
+		int ret = calculate_neighbour_ordering((void*)local_handle, num_points, (const double (*)[3])normalized_points, ordering);
+		if (ret != 0)
+			topological_ordering = false;
+	}
+
+	if (!topological_ordering)
+		for (int i=0;i<num_points;i++)
+			ordering[i] = i;
+
+	for (int i=0;i<num_points;i++)
+	{
+		memcpy(points[i], &unpermuted_points[ordering[i]], 3 * sizeof(double));
+
+		if (unpermuted_numbers != NULL)
+			numbers[i] = unpermuted_numbers[ordering[i]];
+	}
+}
+
+static void output_data(result_t* res, int num_points, int32_t* unpermuted_numbers, double (*points)[3], int32_t* numbers, int8_t* ordering,
+			int32_t* p_type, int32_t* p_alloy_type, double* p_scale, double* p_rmsd, double* q, double* F, double* F_res,
+			double* U, double* P, int8_t* mapping, double* p_interatomic_distance, double* p_lattice_constant)
+{
+	*p_type = PTM_MATCH_NONE;
+	if (p_alloy_type != NULL)
+		*p_alloy_type = PTM_ALLOY_NONE;
+
+	if (mapping != NULL)
+		memset(mapping, -1, num_points * sizeof(int8_t));
+
+	const refdata_t* ref = res->ref_struct;
+	if (ref == NULL)
+		return;
+
+	*p_type = ref->type;
+	if (p_alloy_type != NULL && unpermuted_numbers != NULL)
+		*p_alloy_type = find_alloy_type(ref, res->mapping, numbers);
+
+	int bi = rotate_into_fundamental_zone(ref->type, res->q);
+	int8_t temp[PTM_MAX_POINTS];
+	for (int i=0;i<ref->num_nbrs+1;i++)
+		temp[ref->mapping[bi][i]] = res->mapping[i];
+
+	memcpy(res->mapping, temp, (ref->num_nbrs+1) * sizeof(int8_t));
+
+	if (F != NULL && F_res != NULL)
+	{
+		double scaled_points[PTM_MAX_INPUT_POINTS][3];
+
+		subtract_barycentre(ref->num_nbrs + 1, points, scaled_points);
+		for (int i = 0;i<ref->num_nbrs + 1;i++)
+		{
+			scaled_points[i][0] *= res->scale;
+			scaled_points[i][1] *= res->scale;
+			scaled_points[i][2] *= res->scale;
+		}
+		calculate_deformation_gradient(ref->num_nbrs + 1, ref->points, res->mapping, scaled_points, ref->penrose, F, F_res);
+
+		if (P != NULL && U != NULL)
+			polar_decomposition_3x3(F, false, U, P);
+	}
+
+	if (mapping != NULL)
+		for (int i=0;i<ref->num_nbrs + 1;i++)
+			mapping[i] = ordering[res->mapping[i]];
+
+	double interatomic_distance = calculate_interatomic_distance(ref->type, res->scale);
+	double lattice_constant = calculate_lattice_constant(ref->type, interatomic_distance);
+
+	if (p_interatomic_distance != NULL)
+		*p_interatomic_distance = interatomic_distance;
+
+	if (p_lattice_constant != NULL)
+		*p_lattice_constant = lattice_constant;
+
+	*p_rmsd = res->rmsd;
+	*p_scale = res->scale;
+	memcpy(q, res->q, 4 * sizeof(double));
+}
+
+
+extern bool ptm_initialized;
+
+int ptm_index(	ptm_local_handle_t local_handle, int32_t flags,
+		int num_points, double (*unpermuted_points)[3], int32_t* unpermuted_numbers, bool topological_ordering,
+		int32_t* p_type, int32_t* p_alloy_type, double* p_scale, double* p_rmsd, double* q, double* F, double* F_res,
+		double* U, double* P, int8_t* mapping, double* p_interatomic_distance, double* p_lattice_constant)
+{
+	assert(ptm_initialized);
+	assert(num_points <= PTM_MAX_INPUT_POINTS);
+
+	if (flags & PTM_CHECK_SC)
+		assert(num_points >= PTM_NUM_POINTS_SC);
+
+	if (flags & PTM_CHECK_BCC)
+		assert(num_points >= PTM_NUM_POINTS_BCC);
+
+	if (flags & (PTM_CHECK_FCC | PTM_CHECK_HCP | PTM_CHECK_ICO))
+		assert(num_points >= PTM_NUM_POINTS_FCC);
+
+	if (flags & (PTM_CHECK_DCUB | PTM_CHECK_DHEX))
+		assert(num_points >= PTM_NUM_POINTS_DCUB);
+
+	int ret = 0;
+	result_t res;
+	res.ref_struct = NULL;
+	res.rmsd = INFINITY;
+
+	int8_t ordering[PTM_MAX_INPUT_POINTS];
+	double points[PTM_MAX_POINTS][3];
+	int32_t numbers[PTM_MAX_POINTS];
+
+	int8_t dordering[PTM_MAX_INPUT_POINTS];
+	double dpoints[PTM_MAX_POINTS][3];
+	int32_t dnumbers[PTM_MAX_POINTS];
+
+	convexhull_t ch;
+	double ch_points[PTM_MAX_INPUT_POINTS][3];
+
+	if (flags & (PTM_CHECK_SC | PTM_CHECK_FCC | PTM_CHECK_HCP | PTM_CHECK_ICO | PTM_CHECK_BCC))
+	{
+		int num_lpoints = std::min(std::min(PTM_MAX_POINTS, 20), num_points);
+		order_points(local_handle, num_lpoints, unpermuted_points, unpermuted_numbers, topological_ordering, ordering, points, numbers);
+		normalize_vertices(num_lpoints, points, ch_points);
+		ch.ok = false;
+
+		if (flags & PTM_CHECK_SC)
+			ret = match_general(&structure_sc, ch_points, points, &ch, &res);
+
+		if (flags & (PTM_CHECK_FCC | PTM_CHECK_HCP | PTM_CHECK_ICO))
+			ret = match_fcc_hcp_ico(ch_points, points, flags, &ch, &res);
+
+		if (flags & PTM_CHECK_BCC)
+			ret = match_general(&structure_bcc, ch_points, points, &ch, &res);
+	}
+
+	if (flags & (PTM_CHECK_DCUB | PTM_CHECK_DHEX))
+	{
+		ret = calculate_diamond_neighbour_ordering(num_points, unpermuted_points, unpermuted_numbers, dordering, dpoints, dnumbers);
+		if (ret == 0)
+		{
+			normalize_vertices(PTM_NUM_NBRS_DCUB + 1, dpoints, ch_points);
+			ch.ok = false;
+
+			ret = match_dcub_dhex(ch_points, dpoints, flags, &ch, &res);
+		}
+	}
+
+	if (res.ref_struct != NULL && (res.ref_struct->type == PTM_MATCH_DCUB || res.ref_struct->type == PTM_MATCH_DHEX))
+	{
+		output_data(	&res, num_points, unpermuted_numbers, dpoints, dnumbers, dordering,
+				p_type, p_alloy_type, p_scale, p_rmsd, q, F, F_res,
+				U, P, mapping, p_interatomic_distance, p_lattice_constant);
+	}
+	else
+	{
+		output_data(	&res, num_points, unpermuted_numbers, points, numbers, ordering,
+				p_type, p_alloy_type, p_scale, p_rmsd, q, F, F_res,
+				U, P, mapping, p_interatomic_distance, p_lattice_constant);
+	}
+
+	return PTM_NO_ERROR;
+}
+
diff --git a/src/PTM/ptm_initialize_data.cpp b/src/PTM/ptm_initialize_data.cpp
new file mode 100644
index 0000000000..6157ff862f
--- /dev/null
+++ b/src/PTM/ptm_initialize_data.cpp
@@ -0,0 +1,71 @@
+#include <cstdio>
+#include <cstdlib>
+#include <string.h>
+#include <cmath>
+#include <cfloat>
+#include <cassert>
+#include <algorithm>
+#include "ptm_initialize_data.h"
+
+
+static void make_facets_clockwise(int num_facets, int8_t (*facets)[3], const double (*points)[3])
+{
+	double plane_normal[3];
+	double origin[3] = {0, 0, 0};
+
+	for (int i = 0;i<num_facets;i++)
+		add_facet(points, facets[i][0], facets[i][1], facets[i][2], facets[i], plane_normal, origin);
+}
+
+static int initialize_graphs(const refdata_t* s, int8_t* colours)
+{
+	for (int i = 0;i<s->num_graphs;i++)
+	{
+		int8_t code[2 * PTM_MAX_EDGES];
+		int8_t degree[PTM_MAX_NBRS];
+		int _max_degree = graph_degree(s->num_facets, s->graphs[i].facets, s->num_nbrs, degree);
+		assert(_max_degree <= s->max_degree);
+
+		make_facets_clockwise(s->num_facets, s->graphs[i].facets, &s->points[1]);
+		int ret = canonical_form_coloured(s->num_facets, s->graphs[i].facets, s->num_nbrs, degree, colours, s->graphs[i].canonical_labelling, (int8_t*)&code[0], &s->graphs[i].hash);
+		if (ret != 0)
+			return ret;		
+	}
+
+	return PTM_NO_ERROR;
+}
+
+bool ptm_initialized = false;
+int ptm_initialize_global()
+{
+	if (ptm_initialized)
+		return PTM_NO_ERROR;
+
+	int8_t colours[PTM_MAX_POINTS] = {0};
+	int8_t dcolours[PTM_MAX_POINTS] = {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+	int ret = initialize_graphs(&structure_sc, colours);
+	ret |= initialize_graphs(&structure_fcc, colours);
+	ret |= initialize_graphs(&structure_hcp, colours);
+	ret |= initialize_graphs(&structure_ico, colours);
+	ret |= initialize_graphs(&structure_bcc, colours);
+	ret |= initialize_graphs(&structure_dcub, dcolours);
+	ret |= initialize_graphs(&structure_dhex, dcolours);
+
+	if (ret == PTM_NO_ERROR)
+		ptm_initialized = true;
+
+	return ret;
+}
+
+ptm_local_handle_t ptm_initialize_local()
+{
+	assert(ptm_initialized);
+	return (ptm_local_handle_t)voronoi_initialize_local();
+}
+
+void ptm_uninitialize_local(ptm_local_handle_t ptr)
+{
+	voronoi_uninitialize_local(ptr);
+}
+
diff --git a/src/PTM/ptm_initialize_data.h b/src/PTM/ptm_initialize_data.h
new file mode 100644
index 0000000000..f381dd864b
--- /dev/null
+++ b/src/PTM/ptm_initialize_data.h
@@ -0,0 +1,61 @@
+#ifndef PTM_INITIALIZE_DATA_H
+#define PTM_INITIALIZE_DATA_H
+
+
+#include "ptm_graph_data.h"
+#include "ptm_graph_tools.h"
+#include "ptm_deformation_gradient.h"
+#include "ptm_fundamental_mappings.h"
+#include "ptm_neighbour_ordering.h"
+#include "ptm_canonical_coloured.h"
+#include "ptm_convex_hull_incremental.h"
+
+
+typedef struct
+{
+	int type;
+	int num_nbrs;
+	int num_facets;
+	int max_degree;
+	int num_graphs;
+	int num_mappings;
+	graph_t* graphs;
+	const double (*points)[3];
+	const double (*penrose)[3];
+	const int8_t (*mapping)[PTM_MAX_POINTS];
+} refdata_t;
+
+
+//refdata_t structure_sc =  { .type = PTM_MATCH_SC,  .num_nbrs =  6, .num_facets =  8, .max_degree = 4, .num_graphs = NUM_SC_GRAPHS,  .graphs = graphs_sc,  .points = ptm_template_sc,  .penrose = penrose_sc , .mapping = mapping_sc };
+const refdata_t structure_sc =   { PTM_MATCH_SC,    6,  8, 4, NUM_SC_GRAPHS,   NUM_CUBIC_MAPPINGS, graphs_sc,   ptm_template_sc,   penrose_sc,   mapping_sc   };
+const refdata_t structure_fcc =  { PTM_MATCH_FCC,  12, 20, 6, NUM_FCC_GRAPHS,  NUM_CUBIC_MAPPINGS, graphs_fcc,  ptm_template_fcc,  penrose_fcc,  mapping_fcc  };
+const refdata_t structure_hcp =  { PTM_MATCH_HCP,  12, 20, 6, NUM_HCP_GRAPHS,  NUM_HEX_MAPPINGS,   graphs_hcp,  ptm_template_hcp,  penrose_hcp,  mapping_hcp  };
+const refdata_t structure_ico =  { PTM_MATCH_ICO,  12, 20, 6, NUM_ICO_GRAPHS,  NUM_ICO_MAPPINGS,   graphs_ico,  ptm_template_ico,  penrose_ico,  mapping_ico  };
+const refdata_t structure_bcc =  { PTM_MATCH_BCC,  14, 24, 8, NUM_BCC_GRAPHS,  NUM_CUBIC_MAPPINGS, graphs_bcc,  ptm_template_bcc,  penrose_bcc,  mapping_bcc  };
+const refdata_t structure_dcub = { PTM_MATCH_DCUB, 16, 28, 8, NUM_DCUB_GRAPHS, NUM_DCUB_MAPPINGS,  graphs_dcub, ptm_template_dcub, penrose_dcub, mapping_dcub };
+const refdata_t structure_dhex = { PTM_MATCH_DHEX, 16, 28, 8, NUM_DHEX_GRAPHS, NUM_DHEX_MAPPINGS,  graphs_dhex, ptm_template_dhex, penrose_dhex, mapping_dhex };
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ptm_local_handle* ptm_local_handle_t;
+ptm_local_handle_t ptm_initialize_local();
+void ptm_uninitialize_local(ptm_local_handle_t ptr);
+
+int ptm_initialize_global();
+
+//------------------------------------
+//    global initialization switch
+//------------------------------------
+extern bool ptm_initialized;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif
+
diff --git a/src/PTM/ptm_neighbour_ordering.cpp b/src/PTM/ptm_neighbour_ordering.cpp
new file mode 100644
index 0000000000..6b5ac9601a
--- /dev/null
+++ b/src/PTM/ptm_neighbour_ordering.cpp
@@ -0,0 +1,203 @@
+#include <cstdlib>
+#include <cmath>
+#include <cstring>
+#include <cassert>
+#include <algorithm>
+#include "ptm_constants.h"
+#include "ptm_voronoi_cell.h"
+using namespace voro;
+
+
+
+typedef struct
+{
+	double area;
+	double dist;
+	int index;
+} sorthelper_t;
+
+static bool sorthelper_compare(sorthelper_t const& a, sorthelper_t const& b)
+{
+	if (a.area > b.area)
+		return true;
+
+	if (a.area < b.area)
+		return false;
+
+	if (a.dist < b.dist)
+		return true;
+
+	return false;
+}
+
+//todo: change voronoi code to return errors rather than exiting
+static int calculate_voronoi_face_areas(int num_points, const double (*_points)[3], double* normsq, double max_norm, voronoicell_neighbor* v, std::vector<int>& nbr_indices, std::vector<double>& face_areas)
+{
+	const double k = 1000 * max_norm;	//todo: reduce this constant
+	v->init(-k,k,-k,k,-k,k);
+
+	for (int i=1;i<num_points;i++)
+	{
+		double x = _points[i][0] - _points[0][0];
+		double y = _points[i][1] - _points[0][1];
+		double z = _points[i][2] - _points[0][2];
+		v->nplane(x,y,z,normsq[i],i);
+	}
+
+	v->neighbors(nbr_indices);
+	v->face_areas(face_areas);
+	return 0;
+}
+
+int calculate_neighbour_ordering(void* _voronoi_handle, int num_points, const double (*_points)[3], int8_t* ordering)
+{
+	assert(num_points <= PTM_MAX_INPUT_POINTS);
+
+	voronoicell_neighbor* voronoi_handle = (voronoicell_neighbor*)_voronoi_handle;
+
+	double max_norm = 0;
+	double points[PTM_MAX_INPUT_POINTS][3];
+	double normsq[PTM_MAX_INPUT_POINTS];
+	for (int i = 0;i<num_points;i++)
+	{
+		double x = _points[i][0] - _points[0][0];
+		double y = _points[i][1] - _points[0][1];
+		double z = _points[i][2] - _points[0][2];
+		points[i][0] = x;
+		points[i][1] = y;
+		points[i][2] = z;
+
+		normsq[i] = x*x + y*y + z*z;
+		max_norm = std::max(max_norm, normsq[i]);
+#ifdef DEBUG
+		printf("point %d: %f\t%f\t%f\t%f\n", i, x, y, z, x*x + y*y + z*z);
+#endif
+	}
+
+	max_norm = sqrt(max_norm);
+
+	std::vector<int> nbr_indices(num_points + 6);
+	std::vector<double> face_areas(num_points + 6);
+	int ret = calculate_voronoi_face_areas(num_points, points, normsq, max_norm, voronoi_handle, nbr_indices, face_areas);
+	if (ret != 0)
+		return ret;
+
+	double areas[PTM_MAX_INPUT_POINTS];
+	memset(areas, 0, num_points * sizeof(double));
+	areas[0] = INFINITY;
+	for (size_t i=0;i<nbr_indices.size();i++)
+	{
+		int index = nbr_indices[i];
+		if (index > 0)
+			areas[index] = face_areas[i];
+	}
+
+	sorthelper_t data[PTM_MAX_INPUT_POINTS];
+	for (int i=0;i<num_points;i++)
+	{
+		assert(areas[i] == areas[i]);
+		data[i].area = areas[i];
+		data[i].dist = normsq[i];
+		data[i].index = i;
+	}
+
+	std::sort(data, data + num_points, &sorthelper_compare);
+
+#ifdef DEBUG
+	for (int i=0;i<num_points;i++)
+		printf("%d %f\n", data[i].index, data[i].area);
+#endif
+
+	for (int i=0;i<num_points;i++)
+		ordering[i] = data[i].index;
+
+	return ret;
+}
+
+void* voronoi_initialize_local()
+{
+	voronoicell_neighbor* ptr = new voronoicell_neighbor;
+	return (void*)ptr;
+}
+
+void voronoi_uninitialize_local(void* _ptr)
+{
+	voronoicell_neighbor* ptr = (voronoicell_neighbor*)_ptr;
+	delete ptr;
+}
+
+
+typedef struct
+{
+	double dist;
+	int p;
+	int index;
+} diamond_t;
+
+static bool diamond_compare(diamond_t const& a, diamond_t const& b)
+{
+	return a.dist < b.dist;
+}
+
+int calculate_diamond_neighbour_ordering(	int num_points, double (*unpermuted_points)[3], int32_t* unpermuted_numbers,
+						int8_t* ordering, double (*points)[3], int32_t* numbers)
+{
+	assert(num_points <= PTM_MAX_INPUT_POINTS);
+
+	diamond_t data[4 * (PTM_MAX_INPUT_POINTS - 5)];
+	int index = 0;
+	for (int i=5;i<num_points;i++)
+	{
+		for (int j=1;j<5;j++)
+		{
+			double dx = unpermuted_points[i][0] - unpermuted_points[j][0];
+			double dy = unpermuted_points[i][1] - unpermuted_points[j][1];
+			double dz = unpermuted_points[i][2] - unpermuted_points[j][2];
+
+			double d = dx*dx + dy*dy + dz*dz;
+
+			data[index].p = j - 1;
+			data[index].index = i;
+			data[index].dist = d;
+			index++;
+		}
+	}
+	int n = index;
+
+	std::sort(data, data + n, &diamond_compare);
+
+	for (index=0;index<5;index++)
+		ordering[index] = index;
+
+	int num_found = 0;
+	bool hit[PTM_MAX_INPUT_POINTS] = {0};
+	int counts[4] = {0};
+	for (int i=0;i<n;i++)
+	{
+		int p = data[i].p;
+		int q = data[i].index;
+		if (hit[q] || counts[p] >= 3)
+			continue;
+
+		ordering[1 + 4 + 3 * p + counts[p]] = q;
+		counts[p]++;
+		index++;
+		num_found++;
+		if (num_found >= 12)
+			break;
+	}
+
+	if (num_found != 12)
+		return -1;
+
+	for (int i=0;i<PTM_NUM_NBRS_DCUB+1;i++)
+	{
+		memcpy(points[i], &unpermuted_points[ordering[i]], 3 * sizeof(double));
+
+		if (unpermuted_numbers != NULL)
+			numbers[i] = unpermuted_numbers[ordering[i]];
+	}
+
+	return 0;
+}
+
diff --git a/src/PTM/ptm_neighbour_ordering.h b/src/PTM/ptm_neighbour_ordering.h
new file mode 100644
index 0000000000..ce4dfca2c0
--- /dev/null
+++ b/src/PTM/ptm_neighbour_ordering.h
@@ -0,0 +1,13 @@
+#ifndef PTM_NEIGHBOUR_ORDERING_H
+#define PTM_NEIGHBOUR_ORDERING_H
+
+int calculate_neighbour_ordering(void* voronoi_handle, int num_points, const double (*_points)[3], int8_t* ordering);
+
+int calculate_diamond_neighbour_ordering(	int num_points, double (*unpermuted_points)[3], int32_t* unpermuted_numbers,
+						int8_t* ordering, double (*points)[3], int32_t* numbers);
+
+void* voronoi_initialize_local();
+void voronoi_uninitialize_local(void* ptr);
+
+#endif
+
diff --git a/src/PTM/ptm_normalize_vertices.cpp b/src/PTM/ptm_normalize_vertices.cpp
new file mode 100644
index 0000000000..61dca5006f
--- /dev/null
+++ b/src/PTM/ptm_normalize_vertices.cpp
@@ -0,0 +1,55 @@
+#include <cmath>
+
+
+void subtract_barycentre(int num, double (*points)[3], double (*normalized)[3])
+{
+	//calculate barycentre
+	double sum[3] = {0, 0, 0};
+	for (int i=0;i<num;i++)
+	{
+		sum[0] += points[i][0];
+		sum[1] += points[i][1];
+		sum[2] += points[i][2];
+	}
+
+	sum[0] /= num;
+	sum[1] /= num;
+	sum[2] /= num;
+
+	//subtract barycentre
+	for (int i=0;i<num;i++)
+	{
+		normalized[i][0] = points[i][0] - sum[0];
+		normalized[i][1] = points[i][1] - sum[1];
+		normalized[i][2] = points[i][2] - sum[2];
+	}
+}
+
+double normalize_vertices(int num, double (*points)[3], double (*normalized)[3])
+{
+	subtract_barycentre(num, points, normalized);
+
+	//calculate mean length
+	double scale = 0.0;
+	for (int i=1;i<num;i++)
+	{
+		double x = normalized[i][0];
+		double y = normalized[i][1];
+		double z = normalized[i][2];
+
+		double norm = sqrt(x*x + y*y + z*z);
+		scale += norm;
+	}
+	scale /= num;
+
+	//scale vertices such that mean length is 1
+	for (int i=0;i<num;i++)
+	{
+		normalized[i][0] /= scale;
+		normalized[i][1] /= scale;
+		normalized[i][2] /= scale;
+	}
+
+	return scale;
+}
+
diff --git a/src/PTM/ptm_normalize_vertices.h b/src/PTM/ptm_normalize_vertices.h
new file mode 100644
index 0000000000..2c7b722752
--- /dev/null
+++ b/src/PTM/ptm_normalize_vertices.h
@@ -0,0 +1,8 @@
+#ifndef PTM_NORMALIZE_VERTICES_H
+#define PTM_NORMALIZE_VERTICES_H
+
+void subtract_barycentre(int num, double (*points)[3], double (*normalized)[3]);
+double normalize_vertices(int num, double (*points)[3], double (*normalized)[3]);
+
+#endif
+
diff --git a/src/PTM/ptm_polar.cpp b/src/PTM/ptm_polar.cpp
new file mode 100644
index 0000000000..9089b327b9
--- /dev/null
+++ b/src/PTM/ptm_polar.cpp
@@ -0,0 +1,337 @@
+/*******************************************************************************
+ *  -/_|:|_|_\- 
+ *
+ *  This code is a modification of D.L. Theobald's QCP rotation code.
+ *  It has been adapted to calculate the polar decomposition of a 3x3 matrix
+ *  Adaption by P.M. Larsen
+ *
+ *  Original Author(s):	  Douglas L. Theobald
+ *				  Department of Biochemistry
+ *				  MS 009
+ *				  Brandeis University
+ *				  415 South St
+ *				  Waltham, MA  02453
+ *				  USA
+ *
+ *				  dtheobald@brandeis.edu
+ *				  
+ *				  Pu Liu
+ *				  Johnson & Johnson Pharmaceutical Research and Development, L.L.C.
+ *				  665 Stockton Drive
+ *				  Exton, PA  19341
+ *				  USA
+ *
+ *				  pliu24@its.jnj.com
+ * 
+ *
+ *	If you use this QCP rotation calculation method in a publication, please
+ *	reference:
+ *
+ *	  Douglas L. Theobald (2005)
+ *	  "Rapid calculation of RMSD using a quaternion-based characteristic
+ *	  polynomial."
+ *	  Acta Crystallographica A 61(4):478-480.
+ *
+ *	  Pu Liu, Dmitris K. Agrafiotis, and Douglas L. Theobald (2009)
+ *	  "Fast determination of the optimal rotational matrix for macromolecular 
+ *	  superpositions."
+ *	  Journal of Computational Chemistry 31(7):1561-1563.
+ *
+ *
+ *  Copyright (c) 2009-2013 Pu Liu and Douglas L. Theobald
+ *  All rights reserved.
+ *
+ *  Redistribution and use in source and binary forms, with or without modification, are permitted
+ *  provided that the following conditions are met:
+ *
+ *  * Redistributions of source code must retain the above copyright notice, this list of
+ *	conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright notice, this list
+ *	of conditions and the following disclaimer in the documentation and/or other materials
+ *	provided with the distribution.
+ *  * Neither the name of the <ORGANIZATION> nor the names of its contributors may be used to
+ *	endorse or promote products derived from this software without specific prior written
+ *	permission.
+ *
+ *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ *  HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ *
+ *  Source:		 started anew.
+ *
+ *  Change History:
+ *	2009/04/13	  Started source
+ *	2010/03/28	  Modified FastCalcRMSDAndRotation() to handle tiny qsqr
+ *					If trying all rows of the adjoint still gives too small
+ *					qsqr, then just return identity matrix. (DLT)
+ *	2010/06/30	  Fixed prob in assigning A[9] = 0 in InnerProduct()
+ *					invalid mem access
+ *	2011/02/21	  Made CenterCoords use weights
+ *	2011/05/02	  Finally changed CenterCoords declaration in qcprot.h
+ *					Also changed some functions to static
+ *	2011/07/08	  put in fabs() to fix taking sqrt of small neg numbers, fp error
+ *	2012/07/26	  minor changes to comments and main.c, more info (v.1.4)
+ *
+ *      2016/05/29        QCP method adapted for polar decomposition of a 3x3 matrix,
+ *			  for use in Polyhedral Template Matching.
+ *  
+ ******************************************************************************/
+
+#include <cmath>
+#include <algorithm>
+#include <string.h>
+#include "ptm_quat.h"
+
+
+static void matmul_3x3(double* A, double* x, double* b)
+{
+	b[0] = A[0] * x[0] + A[1] * x[3] + A[2] * x[6];
+	b[3] = A[3] * x[0] + A[4] * x[3] + A[5] * x[6];
+	b[6] = A[6] * x[0] + A[7] * x[3] + A[8] * x[6];
+
+	b[1] = A[0] * x[1] + A[1] * x[4] + A[2] * x[7];
+	b[4] = A[3] * x[1] + A[4] * x[4] + A[5] * x[7];
+	b[7] = A[6] * x[1] + A[7] * x[4] + A[8] * x[7];
+
+	b[2] = A[0] * x[2] + A[1] * x[5] + A[2] * x[8];
+	b[5] = A[3] * x[2] + A[4] * x[5] + A[5] * x[8];
+	b[8] = A[6] * x[2] + A[7] * x[5] + A[8] * x[8];
+}
+
+static double matrix_determinant_3x3(double* A)
+{
+	return    A[0] * (A[4]*A[8] - A[5]*A[7])
+		- A[1] * (A[3]*A[8] - A[5]*A[6])
+		+ A[2] * (A[3]*A[7] - A[4]*A[6]);
+}
+
+static void flip_matrix(double* A)
+{
+	for (int i=0;i<9;i++)
+		A[i] = -A[i];
+}
+
+static bool optimal_quaternion(double* A, bool polar, double E0, double* p_nrmsdsq, double* qopt)
+{
+	const double evecprec = 1e-6;
+	const double evalprec = 1e-11;
+
+	double	Sxx = A[0], Sxy = A[1], Sxz = A[2],
+		Syx = A[3], Syy = A[4], Syz = A[5],
+		Szx = A[6], Szy = A[7], Szz = A[8];
+
+	double	Sxx2 = Sxx * Sxx, Syy2 = Syy * Syy, Szz2 = Szz * Szz,
+		Sxy2 = Sxy * Sxy, Syz2 = Syz * Syz, Sxz2 = Sxz * Sxz,
+		Syx2 = Syx * Syx, Szy2 = Szy * Szy, Szx2 = Szx * Szx;
+
+	double fnorm_squared = Sxx2 + Syy2 + Szz2 + Sxy2 + Syz2 + Sxz2 + Syx2 + Szy2 + Szx2;
+
+	double SyzSzymSyySzz2 = 2.0 * (Syz * Szy - Syy * Szz);
+	double Sxx2Syy2Szz2Syz2Szy2 = Syy2 + Szz2 - Sxx2 + Syz2 + Szy2;
+	double SxzpSzx = Sxz + Szx;
+	double SyzpSzy = Syz + Szy;
+	double SxypSyx = Sxy + Syx;
+	double SyzmSzy = Syz - Szy;
+	double SxzmSzx = Sxz - Szx;
+	double SxymSyx = Sxy - Syx;
+	double SxxpSyy = Sxx + Syy;
+	double SxxmSyy = Sxx - Syy;
+	double Sxy2Sxz2Syx2Szx2 = Sxy2 + Sxz2 - Syx2 - Szx2;
+
+	double C[3];
+	C[0] = Sxy2Sxz2Syx2Szx2 * Sxy2Sxz2Syx2Szx2
+		 + (Sxx2Syy2Szz2Syz2Szy2 + SyzSzymSyySzz2) * (Sxx2Syy2Szz2Syz2Szy2 - SyzSzymSyySzz2)
+		 + (-(SxzpSzx)*(SyzmSzy)+(SxymSyx)*(SxxmSyy-Szz)) * (-(SxzmSzx)*(SyzpSzy)+(SxymSyx)*(SxxmSyy+Szz))
+		 + (-(SxzpSzx)*(SyzpSzy)-(SxypSyx)*(SxxpSyy-Szz)) * (-(SxzmSzx)*(SyzmSzy)-(SxypSyx)*(SxxpSyy+Szz))
+		 + (+(SxypSyx)*(SyzpSzy)+(SxzpSzx)*(SxxmSyy+Szz)) * (-(SxymSyx)*(SyzmSzy)+(SxzpSzx)*(SxxpSyy+Szz))
+		 + (+(SxypSyx)*(SyzmSzy)+(SxzmSzx)*(SxxmSyy-Szz)) * (-(SxymSyx)*(SyzpSzy)+(SxzmSzx)*(SxxpSyy-Szz));
+
+	C[1] = 8.0 * (Sxx*Syz*Szy + Syy*Szx*Sxz + Szz*Sxy*Syx - Sxx*Syy*Szz - Syz*Szx*Sxy - Szy*Syx*Sxz);
+	C[2] = -2.0 * fnorm_squared;
+
+	//Newton-Raphson
+	double mxEigenV = polar ? sqrt(3 * fnorm_squared) : E0;
+	if (mxEigenV > evalprec)
+	{
+		for (int i=0;i<50;i++)
+		{
+			double oldg = mxEigenV;
+			double x2 = mxEigenV*mxEigenV;
+			double b = (x2 + C[2])*mxEigenV;
+			double a = b + C[1];
+			double delta = ((a * mxEigenV + C[0]) / (2 * x2 * mxEigenV + b + a));
+			mxEigenV -= delta;
+			if (fabs(mxEigenV - oldg) < fabs(evalprec * mxEigenV))
+				break;
+		}
+	}
+	else
+	{
+		mxEigenV = 0.0;
+	}
+
+	(*p_nrmsdsq) = std::max(0.0, 2.0 * (E0 - mxEigenV));
+
+	double a11 = SxxpSyy + Szz - mxEigenV;
+	double a12 = SyzmSzy;
+	double a13 = -SxzmSzx;
+	double a14 = SxymSyx;
+
+	double a21 = SyzmSzy;
+	double a22 = SxxmSyy - Szz  -mxEigenV;
+	double a23 = SxypSyx;
+	double a24 = SxzpSzx;
+
+	double a31 = a13;
+	double a32 = a23;
+	double a33 = Syy - Sxx - Szz - mxEigenV;
+	double a34 = SyzpSzy;
+
+	double a41 = a14;
+	double a42 = a24;
+	double a43 = a34;
+	double a44 = Szz - SxxpSyy - mxEigenV;
+
+	double a3344_4334 = a33 * a44 - a43 * a34;
+	double a3244_4234 = a32 * a44 - a42 * a34;
+	double a3243_4233 = a32 * a43 - a42 * a33;
+	double a3143_4133 = a31 * a43 - a41 * a33;
+	double a3144_4134 = a31 * a44 - a41 * a34;
+	double a3142_4132 = a31 * a42 - a41 * a32;
+	double a1324_1423 = a13 * a24 - a14 * a23;
+	double a1224_1422 = a12 * a24 - a14 * a22;
+	double a1223_1322 = a12 * a23 - a13 * a22;
+	double a1124_1421 = a11 * a24 - a14 * a21;
+	double a1123_1321 = a11 * a23 - a13 * a21;
+	double a1122_1221 = a11 * a22 - a12 * a21;
+
+	double q[4][4];
+	q[0][0] =  a12 * a3344_4334 - a13 * a3244_4234 + a14 * a3243_4233;
+	q[0][1] = -a11 * a3344_4334 + a13 * a3144_4134 - a14 * a3143_4133;
+	q[0][2] =  a11 * a3244_4234 - a12 * a3144_4134 + a14 * a3142_4132;
+	q[0][3] = -a11 * a3243_4233 + a12 * a3143_4133 - a13 * a3142_4132;
+
+	q[1][0] =  a22 * a3344_4334 - a23 * a3244_4234 + a24 * a3243_4233;
+	q[1][1] = -a21 * a3344_4334 + a23 * a3144_4134 - a24 * a3143_4133;
+	q[1][2] =  a21 * a3244_4234 - a22 * a3144_4134 + a24 * a3142_4132;
+	q[1][3] = -a21 * a3243_4233 + a22 * a3143_4133 - a23 * a3142_4132;
+
+	q[2][0] =  a32 * a1324_1423 - a33 * a1224_1422 + a34 * a1223_1322;
+	q[2][1] = -a31 * a1324_1423 + a33 * a1124_1421 - a34 * a1123_1321;
+	q[2][2] =  a31 * a1224_1422 - a32 * a1124_1421 + a34 * a1122_1221;
+	q[2][3] = -a31 * a1223_1322 + a32 * a1123_1321 - a33 * a1122_1221;
+
+	q[3][0] =  a42 * a1324_1423 - a43 * a1224_1422 + a44 * a1223_1322;
+	q[3][1] = -a41 * a1324_1423 + a43 * a1124_1421 - a44 * a1123_1321;
+	q[3][2] =  a41 * a1224_1422 - a42 * a1124_1421 + a44 * a1122_1221;
+	q[3][3] = -a41 * a1223_1322 + a42 * a1123_1321 - a43 * a1122_1221;
+
+	double qsqr[4];
+	for (int i=0;i<4;i++)
+		qsqr[i] = q[i][0]*q[i][0] + q[i][1]*q[i][1] + q[i][2]*q[i][2] + q[i][3]*q[i][3];
+
+	int bi = 0;
+	double max = 0;
+	for (int i=0;i<4;i++)
+	{
+		if (qsqr[i] > max)
+		{
+			bi = i;
+			max = qsqr[i];
+		}
+	}
+
+	bool too_small = false;
+	if (qsqr[bi] < evecprec)
+	{
+		//if qsqr is still too small, return the identity rotation.
+		q[bi][0] = 1;
+		q[bi][1] = 0;
+		q[bi][2] = 0;
+		q[bi][3] = 0;
+		too_small = true;
+	}
+	else
+	{
+		double normq = sqrt(qsqr[bi]);
+		q[bi][0] /= normq;
+		q[bi][1] /= normq;
+		q[bi][2] /= normq;
+		q[bi][3] /= normq;
+	}
+
+	memcpy(qopt, q[bi], 4 * sizeof(double));
+	return !too_small;
+}
+
+int polar_decomposition_3x3(double* _A, bool right_sided, double* U, double* P)
+{
+	double A[9];
+	memcpy(A, _A, 9 * sizeof(double));
+
+	double det = matrix_determinant_3x3(A);
+	if (det < 0)
+		flip_matrix(A);
+
+	double q[4];
+	double nrmsdsq = 0;
+	optimal_quaternion(A, true, -1, &nrmsdsq, q);
+	q[0] = -q[0];
+	quaternion_to_rotation_matrix(q, U);
+
+	if (det < 0)
+		flip_matrix(U);
+
+	double UT[9] = {U[0], U[3], U[6], U[1], U[4], U[7], U[2], U[5], U[8]};
+
+	if (right_sided)
+		matmul_3x3(UT, _A, P);
+	else
+		matmul_3x3(_A, UT, P);
+
+	return 0;
+}
+
+void InnerProduct(double *A, int num, const double (*coords1)[3], double (*coords2)[3], int8_t* permutation)
+{
+	A[0] = A[1] = A[2] = A[3] = A[4] = A[5] = A[6] = A[7] = A[8] = 0.0;
+
+	for (int i = 0; i < num; ++i)
+	{
+		double x1 = coords1[i][0];
+		double y1 = coords1[i][1];
+		double z1 = coords1[i][2];
+
+		double x2 = coords2[permutation[i]][0];
+		double y2 = coords2[permutation[i]][1];
+		double z2 = coords2[permutation[i]][2];
+
+		A[0] += x1 * x2;
+		A[1] += x1 * y2;
+		A[2] += x1 * z2;
+
+		A[3] += y1 * x2;
+		A[4] += y1 * y2;
+		A[5] += y1 * z2;
+
+		A[6] += z1 * x2;
+		A[7] += z1 * y2;
+		A[8] += z1 * z2;  
+	}
+}
+
+int FastCalcRMSDAndRotation(double *A, double E0, double *p_nrmsdsq, double *q, double* U)
+{
+	optimal_quaternion(A, false, E0, p_nrmsdsq, q);
+	quaternion_to_rotation_matrix(q, U);
+	return 0;
+}
+
diff --git a/src/PTM/ptm_polar.h b/src/PTM/ptm_polar.h
new file mode 100644
index 0000000000..d5aa3d9540
--- /dev/null
+++ b/src/PTM/ptm_polar.h
@@ -0,0 +1,12 @@
+#ifndef PTM_POLAR_H
+#define PTM_POLAR_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+int polar_decomposition_3x3(double* _A, bool right_sided, double* U, double* P);
+void InnerProduct(double *A, int num, const double (*coords1)[3], double (*coords2)[3], int8_t* permutation);
+int FastCalcRMSDAndRotation(double *A, double E0, double *p_nrmsdsq, double *q, double* U);
+
+#endif
+
diff --git a/src/PTM/ptm_quat.cpp b/src/PTM/ptm_quat.cpp
new file mode 100644
index 0000000000..f55aff3d2b
--- /dev/null
+++ b/src/PTM/ptm_quat.cpp
@@ -0,0 +1,396 @@
+#include <string.h>
+#include <cmath>
+#include <cfloat>
+
+
+#define SIGN(x) (x >= 0 ? 1 : -1)
+#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
+#define MAX(X, Y) (((X) > (Y)) ? (X) : (Y))
+
+
+#define SQRT_2         1.4142135623730951454746218587388284504414
+#define HALF_SQRT_2    0.7071067811865474617150084668537601828575
+
+#define PHI            1.6180339887498949025257388711906969547272
+#define HALF_PHI       0.8090169943749474512628694355953484773636
+
+#define INV_PHI        0.6180339887498947915034364086750429123640
+#define HALF_INV_PHI   0.3090169943749473957517182043375214561820
+
+#define SQRT_5_        2.23606797749978969640917366873127623544061835961152572427089
+#define SQRT_2_3       0.8164965809277260344600790631375275552273
+#define SQRT_1_6       0.4082482904638630172300395315687637776136
+
+
+double generator_cubic[24][4] = {		{1,	0,	0,	0	},
+						{0,	1,	0,	0	},
+						{0,	0,	1,	0	},
+						{0,	0,	0,	1	},
+						{0.5,	0.5,	0.5,	0.5	},
+						{0.5,	0.5,	-0.5,	0.5	},
+						{0.5,	-0.5,	0.5,	0.5	},
+						{0.5,	-0.5,	-0.5,	0.5	},
+						{-0.5,	0.5,	0.5,	0.5	},
+						{-0.5,	0.5,	-0.5,	0.5	},
+						{-0.5,	-0.5,	0.5,	0.5	},
+						{-0.5,	-0.5,	-0.5,	0.5	},
+						{HALF_SQRT_2,	HALF_SQRT_2,	0,	0	},
+						{HALF_SQRT_2,	0,	HALF_SQRT_2,	0	},
+						{HALF_SQRT_2,	0,	0,	HALF_SQRT_2	},
+						{-HALF_SQRT_2,	HALF_SQRT_2,	0,	0	},
+						{-HALF_SQRT_2,	0,	HALF_SQRT_2,	0	},
+						{-HALF_SQRT_2,	0,	0,	HALF_SQRT_2	},
+						{0,	HALF_SQRT_2,	HALF_SQRT_2,	0	},
+						{0,	HALF_SQRT_2,	0,	HALF_SQRT_2	},
+						{0,	0,	HALF_SQRT_2,	HALF_SQRT_2	},
+						{0,	-HALF_SQRT_2,	HALF_SQRT_2,	0	},
+						{0,	-HALF_SQRT_2,	0,	HALF_SQRT_2	},
+						{0,	0,	-HALF_SQRT_2,	HALF_SQRT_2	}	};
+
+double generator_diamond_cubic[12][4] = {	{1,	0,	0,	0	},
+						{0,	1,	0,	0	},
+						{0,	0,	1,	0	},
+						{0,	0,	0,	1	},
+						{0.5,	0.5,	0.5,	0.5	},
+						{0.5,	0.5,	-0.5,	0.5	},
+						{0.5,	-0.5,	0.5,	0.5	},
+						{0.5,	-0.5,	-0.5,	0.5	},
+						{-0.5,	0.5,	0.5,	0.5	},
+						{-0.5,	0.5,	-0.5,	0.5	},
+						{-0.5,	-0.5,	0.5,	0.5	},
+						{-0.5,	-0.5,	-0.5,	0.5	}	};
+
+double generator_hcp[6][4] = {			{1, 0, 0, 0},
+						{0.5, 0.5, 0.5, 0.5},
+						{0.5, -0.5, -0.5, -0.5},
+						{0, SQRT_2_3, -SQRT_1_6, -SQRT_1_6},
+						{0, SQRT_1_6, -SQRT_2_3, SQRT_1_6},
+						{0, SQRT_1_6, SQRT_1_6, -SQRT_2_3}	};
+
+double generator_diamond_hexagonal[3][4] = {	{1, 0, 0, 0},
+						{0.5, 0.5, 0.5, 0.5},
+						{0.5, -0.5, -0.5, -0.5}	};
+
+double generator_icosahedral[60][4] = {		{1, 0, 0, 0},
+						{HALF_PHI, -HALF_INV_PHI, -0.5, 0},
+						{HALF_PHI, 0, -HALF_INV_PHI, -0.5},
+						{HALF_PHI, -0.5, 0, -HALF_INV_PHI},
+						{HALF_PHI, HALF_INV_PHI, -0.5, 0},
+						{HALF_PHI, 0, HALF_INV_PHI, -0.5},
+						{HALF_PHI, -0.5, 0, HALF_INV_PHI},
+						{HALF_PHI, 0.5, 0, -HALF_INV_PHI},
+						{HALF_PHI, 0, -HALF_INV_PHI, 0.5},
+						{HALF_PHI, -HALF_INV_PHI, 0.5, 0},
+						{HALF_PHI, 0, HALF_INV_PHI, 0.5},
+						{HALF_PHI, HALF_INV_PHI, 0.5, 0},
+						{HALF_PHI, 0.5, 0, HALF_INV_PHI},
+						{0.5, HALF_PHI, -HALF_INV_PHI, 0},
+						{0.5, HALF_PHI, HALF_INV_PHI, 0},
+						{0.5, 0.5, 0.5, 0.5},
+						{0.5, 0.5, 0.5, -0.5},
+						{0.5, 0.5, -0.5, 0.5},
+						{0.5, 0.5, -0.5, -0.5},
+						{0.5, HALF_INV_PHI, 0, HALF_PHI},
+						{0.5, HALF_INV_PHI, 0, -HALF_PHI},
+						{0.5, 0, HALF_PHI, -HALF_INV_PHI},
+						{0.5, 0, HALF_PHI, HALF_INV_PHI},
+						{0.5, 0, -HALF_PHI, -HALF_INV_PHI},
+						{0.5, 0, -HALF_PHI, HALF_INV_PHI},
+						{0.5, -HALF_INV_PHI, 0, HALF_PHI},
+						{0.5, -HALF_INV_PHI, 0, -HALF_PHI},
+						{0.5, -0.5, 0.5, 0.5},
+						{0.5, -0.5, 0.5, -0.5},
+						{0.5, -0.5, -0.5, 0.5},
+						{0.5, -0.5, -0.5, -0.5},
+						{0.5, -HALF_PHI, -HALF_INV_PHI, 0},
+						{0.5, -HALF_PHI, HALF_INV_PHI, 0},
+						{HALF_INV_PHI, -HALF_PHI, 0, -0.5},
+						{HALF_INV_PHI, 0, -0.5, -HALF_PHI},
+						{HALF_INV_PHI, -0.5, -HALF_PHI, 0},
+						{HALF_INV_PHI, 0, 0.5, -HALF_PHI},
+						{HALF_INV_PHI, -HALF_PHI, 0, 0.5},
+						{HALF_INV_PHI, 0.5, -HALF_PHI, 0},
+						{HALF_INV_PHI, HALF_PHI, 0, -0.5},
+						{HALF_INV_PHI, -0.5, HALF_PHI, 0},
+						{HALF_INV_PHI, 0, -0.5, HALF_PHI},
+						{HALF_INV_PHI, HALF_PHI, 0, 0.5},
+						{HALF_INV_PHI, 0, 0.5, HALF_PHI},
+						{HALF_INV_PHI, 0.5, HALF_PHI, 0},
+						{0, 1, 0, 0},
+						{0, HALF_PHI, -0.5, HALF_INV_PHI},
+						{0, HALF_PHI, -0.5, -HALF_INV_PHI},
+						{0, HALF_PHI, 0.5, HALF_INV_PHI},
+						{0, HALF_PHI, 0.5, -HALF_INV_PHI},
+						{0, 0.5, HALF_INV_PHI, -HALF_PHI},
+						{0, 0.5, HALF_INV_PHI, HALF_PHI},
+						{0, 0.5, -HALF_INV_PHI, -HALF_PHI},
+						{0, 0.5, -HALF_INV_PHI, HALF_PHI},
+						{0, HALF_INV_PHI, -HALF_PHI, 0.5},
+						{0, HALF_INV_PHI, -HALF_PHI, -0.5},
+						{0, HALF_INV_PHI, HALF_PHI, 0.5},
+						{0, HALF_INV_PHI, HALF_PHI, -0.5},
+						{0, 0, 1, 0},
+						{0, 0, 0, 1}	};
+
+static void quat_rot(double* r, double* a, double* b)
+{
+	b[0] = (r[0] * a[0] - r[1] * a[1] - r[2] * a[2] - r[3] * a[3]);
+	b[1] = (r[0] * a[1] + r[1] * a[0] + r[2] * a[3] - r[3] * a[2]);
+	b[2] = (r[0] * a[2] - r[1] * a[3] + r[2] * a[0] + r[3] * a[1]);
+	b[3] = (r[0] * a[3] + r[1] * a[2] - r[2] * a[1] + r[3] * a[0]);
+}
+
+static int rotate_quaternion_into_fundamental_zone(int num_generators, double (*generator)[4], double* q)
+{
+	double max = 0.0;
+	int i = 0, bi = -1;
+	for (i=0;i<num_generators;i++)
+	{
+		double* g = generator[i];
+		double t = fabs(q[0] * g[0] - q[1] * g[1] - q[2] * g[2] - q[3] * g[3]);
+		if (t > max)
+		{
+			max = t;
+			bi = i;
+		}
+	}
+
+	double f[4];
+	quat_rot(q, generator[bi], f);
+	memcpy(q, &f, 4 * sizeof(double));
+	if (q[0] < 0)
+	{
+		q[0] = -q[0];
+		q[1] = -q[1];
+		q[2] = -q[2];
+		q[3] = -q[3];
+	}
+
+	return bi;
+}
+
+int rotate_quaternion_into_cubic_fundamental_zone(double* q)
+{
+	return rotate_quaternion_into_fundamental_zone(24, generator_cubic, q);
+}
+
+int rotate_quaternion_into_diamond_cubic_fundamental_zone(double* q)
+{
+	return rotate_quaternion_into_fundamental_zone(12, generator_diamond_cubic, q);
+}
+
+int rotate_quaternion_into_icosahedral_fundamental_zone(double* q)
+{
+	return rotate_quaternion_into_fundamental_zone(60, generator_icosahedral, q);
+}
+
+int rotate_quaternion_into_hcp_fundamental_zone(double* q)
+{
+	return rotate_quaternion_into_fundamental_zone(6, generator_hcp, q);
+}
+
+int rotate_quaternion_into_diamond_hexagonal_fundamental_zone(double* q)
+{
+	return rotate_quaternion_into_fundamental_zone(3, generator_diamond_hexagonal, q);
+}
+
+double quat_dot(double* a, double* b)
+{
+	return	  a[0] * b[0]
+		+ a[1] * b[1]
+		+ a[2] * b[2]
+		+ a[3] * b[3];
+}
+
+double quat_size(double* q)
+{
+	return sqrt(quat_dot(q, q));
+}
+
+void normalize_quaternion(double* q)
+{
+	double size = quat_size(q);
+
+	q[0] /= size;
+	q[1] /= size;
+	q[2] /= size;
+	q[3] /= size;
+}
+
+void rotation_matrix_to_quaternion(double* u, double* q)
+{
+	double r11 = u[0];
+	double r12 = u[1];
+	double r13 = u[2];
+	double r21 = u[3];
+	double r22 = u[4];
+	double r23 = u[5];
+	double r31 = u[6];
+	double r32 = u[7];
+	double r33 = u[8];
+
+	q[0] = (1.0 + r11 + r22 + r33) / 4.0;
+	q[1] = (1.0 + r11 - r22 - r33) / 4.0;
+	q[2] = (1.0 - r11 + r22 - r33) / 4.0;
+	q[3] = (1.0 - r11 - r22 + r33) / 4.0;
+
+	q[0] = sqrt(MAX(0, q[0]));
+	q[1] = sqrt(MAX(0, q[1]));
+	q[2] = sqrt(MAX(0, q[2]));
+	q[3] = sqrt(MAX(0, q[3]));
+
+	double m0 = MAX(q[0], q[1]);
+	double m1 = MAX(q[2], q[3]);
+	double max = MAX(m0, m1);
+
+	int i = 0;
+	for (i=0;i<4;i++)
+		if (q[i] == max)
+			break;
+
+	if (i == 0)
+	{
+		q[1] *= SIGN(r32 - r23);
+		q[2] *= SIGN(r13 - r31);
+		q[3] *= SIGN(r21 - r12);
+	}
+	else if (i == 1)
+	{
+		q[0] *= SIGN(r32 - r23);
+		q[2] *= SIGN(r21 + r12);
+		q[3] *= SIGN(r13 + r31);
+	}
+	else if (i == 2)
+	{
+		q[0] *= SIGN(r13 - r31);
+		q[1] *= SIGN(r21 + r12);
+		q[3] *= SIGN(r32 + r23);
+	}
+	else if (i == 3)
+	{
+		q[0] *= SIGN(r21 - r12);
+		q[1] *= SIGN(r31 + r13);
+		q[2] *= SIGN(r32 + r23);
+	}
+
+	normalize_quaternion(q);
+}
+
+void quaternion_to_rotation_matrix(double* q, double* u)
+{
+	double a = q[0];
+	double b = q[1];
+	double c = q[2];
+	double d = q[3];
+
+	u[0] = a*a + b*b - c*c - d*d;
+	u[1] = 2*b*c - 2*a*d;
+	u[2] = 2*b*d + 2*a*c;
+
+	u[3] = 2*b*c + 2*a*d;
+	u[4] = a*a - b*b + c*c - d*d;
+	u[5] = 2*c*d - 2*a*b;
+
+	u[6] = 2*b*d - 2*a*c;
+	u[7] = 2*c*d + 2*a*b;
+	u[8] = a*a - b*b - c*c + d*d;
+}
+
+double quat_quick_misorientation(double* q1, double* q2)
+{
+	double t = quat_dot(q1, q2);
+	t = MIN(1, MAX(-1, t));
+	return 2 * t * t - 1;
+}
+
+double quat_misorientation(double* q1, double* q2)
+{
+	return acos(quat_quick_misorientation(q1, q2));
+}
+
+
+double quat_quick_disorientation_cubic(double* q0, double* q1)
+{
+	double qrot[4];
+	double qinv[4] = {q0[0], -q0[1], -q0[2], -q0[3]};
+	quat_rot(qinv, q1, qrot);
+
+	rotate_quaternion_into_cubic_fundamental_zone(qrot);
+	double t = qrot[0];
+	t = MIN(1, MAX(-1, t));
+	return 2 * t * t - 1;
+}
+
+double quat_disorientation_cubic(double* q0, double* q1)
+{
+	return acos(quat_quick_disorientation_cubic(q0, q1));
+}
+
+double quat_quick_disorientation_diamond_cubic(double* q0, double* q1)
+{
+	double qrot[4];
+	double qinv[4] = {q0[0], -q0[1], -q0[2], -q0[3]};
+	quat_rot(qinv, q1, qrot);
+
+	rotate_quaternion_into_diamond_cubic_fundamental_zone(qrot);
+	double t = qrot[0];
+	t = MIN(1, MAX(-1, t));
+	return 2 * t * t - 1;
+}
+
+double quat_disorientation_diamond_cubic(double* q0, double* q1)
+{
+	return acos(quat_quick_disorientation_diamond_cubic(q0, q1));
+}
+
+double quat_quick_disorientation_hcp(double* q0, double* q1)
+{
+	double qrot[4];
+	double qinv[4] = {q0[0], -q0[1], -q0[2], -q0[3]};
+	quat_rot(qinv, q1, qrot);
+
+	rotate_quaternion_into_hcp_fundamental_zone(qrot);
+	double t = qrot[0];
+	t = MIN(1, MAX(-1, t));
+	return 2 * t * t - 1;
+}
+
+double quat_disorientation_hcp(double* q0, double* q1)
+{
+	return acos(quat_quick_disorientation_hcp(q0, q1));
+}
+
+double quat_quick_disorientation_diamond_hexagonal(double* q0, double* q1)
+{
+	double qrot[4];
+	double qinv[4] = {q0[0], -q0[1], -q0[2], -q0[3]};
+	quat_rot(qinv, q1, qrot);
+
+	rotate_quaternion_into_diamond_hexagonal_fundamental_zone(qrot);
+	double t = qrot[0];
+	t = MIN(1, MAX(-1, t));
+	return 2 * t * t - 1;
+}
+
+double quat_disorientation_diamond_hexagonal(double* q0, double* q1)
+{
+	return acos(quat_quick_disorientation_diamond_hexagonal(q0, q1));
+}
+
+double quat_quick_disorientation_icosahedral(double* q0, double* q1)
+{
+	double qrot[4];
+	double qinv[4] = {q0[0], -q0[1], -q0[2], -q0[3]};
+	quat_rot(qinv, q1, qrot);
+
+	rotate_quaternion_into_icosahedral_fundamental_zone(qrot);
+	double t = qrot[0];
+	t = MIN(1, MAX(-1, t));
+	return 2 * t * t - 1;
+}
+
+double quat_disorientation_icosahedral(double* q0, double* q1)
+{
+	return acos(quat_quick_disorientation_icosahedral(q0, q1));
+}
+
diff --git a/src/PTM/ptm_quat.h b/src/PTM/ptm_quat.h
new file mode 100644
index 0000000000..381c3ce876
--- /dev/null
+++ b/src/PTM/ptm_quat.h
@@ -0,0 +1,32 @@
+#ifndef PTM_QUAT_H
+#define PTM_QUAT_H
+
+int rotate_quaternion_into_cubic_fundamental_zone(double* q);
+int rotate_quaternion_into_diamond_cubic_fundamental_zone(double* q);
+int rotate_quaternion_into_icosahedral_fundamental_zone(double* q);
+int rotate_quaternion_into_hcp_fundamental_zone(double* q);
+int rotate_quaternion_into_diamond_hexagonal_fundamental_zone(double* q);
+
+void normalize_quaternion(double* q);
+void quaternion_to_rotation_matrix(double* q, double* U);
+void rotation_matrix_to_quaternion(double* u, double* q);
+double quat_dot(double* a, double* b);
+double quat_quick_misorientation(double* q1, double* q2);
+double quat_misorientation(double* q1, double* q2);
+
+double quat_quick_disorientation_cubic(double* q0, double* q1);
+double quat_disorientation_cubic(double* q0, double* q1);
+double quat_quick_disorientation_diamond_cubic(double* q0, double* q1);
+double quat_disorientation_diamond_cubic(double* q0, double* q1);
+double quat_quick_disorientation_hcp(double* q0, double* q1);
+double quat_disorientation_hcp(double* q0, double* q1);
+double quat_quick_disorientation_diamond_hexagonal(double* q0, double* q1);
+double quat_disorientation_diamond_hexagonal(double* q0, double* q1);
+double quat_quick_disorientation_icosahedral(double* q0, double* q1);
+double quat_disorientation_icosahedral(double* q0, double* q1);
+
+#endif
+
+
+
+
diff --git a/src/PTM/ptm_structure_matcher.cpp b/src/PTM/ptm_structure_matcher.cpp
new file mode 100644
index 0000000000..7eb0a44143
--- /dev/null
+++ b/src/PTM/ptm_structure_matcher.cpp
@@ -0,0 +1,294 @@
+#include <cstdio>
+#include <cstdlib>
+#include <string.h>
+#include <cmath>
+#include <cfloat>
+#include <cassert>
+#include <algorithm>
+#include "ptm_convex_hull_incremental.h"
+#include "ptm_canonical_coloured.h"
+#include "ptm_graph_data.h"
+#include "ptm_graph_tools.h"
+#include "ptm_normalize_vertices.h"
+#include "ptm_polar.h"
+#include "ptm_structure_matcher.h"
+#include "ptm_constants.h"
+
+
+static double calc_rmsd(int num_points, const double (*ideal_points)[3], double (*normalized)[3], int8_t* mapping,
+			double G1, double G2, double E0, double* q, double* p_scale)
+{
+	double A0[9];
+	InnerProduct(A0, num_points, ideal_points, normalized, mapping);
+
+	double nrmsdsq, rot[9];
+	FastCalcRMSDAndRotation(A0, E0, &nrmsdsq, q, rot);
+
+	double k0 = 0;
+	for (int i=0;i<num_points;i++)
+	{
+		for (int j=0;j<3;j++)
+		{
+			double v = 0.0;
+			for (int k=0;k<3;k++)
+				v += rot[j*3+k] * ideal_points[i][k];
+
+			k0 += v * normalized[mapping[i]][j];
+		}
+	}
+
+	double scale = k0 / G2;
+	*p_scale = scale;
+	return sqrt(fabs(G1 - scale*k0) / num_points);
+}
+
+static void check_graphs(	const refdata_t* s,
+				uint64_t hash,
+				int8_t* canonical_labelling,
+				double (*normalized)[3],
+				result_t* res)
+{
+	int num_points = s->num_nbrs + 1;
+	const double (*ideal_points)[3] = s->points;
+	int8_t inverse_labelling[PTM_MAX_POINTS];
+	int8_t mapping[PTM_MAX_POINTS];
+
+	for (int i=0; i<num_points; i++)
+		inverse_labelling[ canonical_labelling[i] ] = i;
+
+	double G1 = 0, G2 = 0;
+	for (int i=0;i<num_points;i++)
+	{
+		double x1 = ideal_points[i][0];
+		double y1 = ideal_points[i][1];
+		double z1 = ideal_points[i][2];
+
+		double x2 = normalized[i][0];
+		double y2 = normalized[i][1];
+		double z2 = normalized[i][2];
+
+		G1 += x1 * x1 + y1 * y1 + z1 * z1;
+		G2 += x2 * x2 + y2 * y2 + z2 * z2;
+	}
+	double E0 = (G1 + G2) / 2;
+
+	for (int i = 0;i<s->num_graphs;i++)
+	{
+		if (hash != s->graphs[i].hash)
+			continue;
+
+		graph_t* gref = &s->graphs[i];
+		for (int j = 0;j<gref->num_automorphisms;j++)
+		{
+			for (int k=0;k<num_points;k++)
+				mapping[automorphisms[gref->automorphism_index + j][k]] = inverse_labelling[ gref->canonical_labelling[k] ];
+
+			double q[4], scale = 0;
+			double rmsd = calc_rmsd(num_points, ideal_points, normalized, mapping, G1, G2, E0, q, &scale);
+			if (rmsd < res->rmsd)
+			{
+				res->rmsd = rmsd;
+				res->scale = scale;
+				res->ref_struct = s;
+				memcpy(res->q, q, 4 * sizeof(double));
+				memcpy(res->mapping, mapping, sizeof(int8_t) * num_points);
+			}
+		}
+	}
+}
+
+int match_general(const refdata_t* s, double (*ch_points)[3], double (*points)[3], convexhull_t* ch, result_t* res)
+{
+	int8_t degree[PTM_MAX_NBRS];
+	int8_t facets[PTM_MAX_FACETS][3];
+
+	int ret = get_convex_hull(s->num_nbrs + 1, (const double (*)[3])ch_points, ch, facets);
+	ch->ok = ret >= 0;
+	if (ret != 0)
+		return PTM_NO_ERROR;
+
+	if (ch->num_facets != s->num_facets)
+		return PTM_NO_ERROR;			//incorrect number of facets in convex hull
+
+	int max_degree = graph_degree(s->num_facets, facets, s->num_nbrs, degree);
+	if (max_degree > s->max_degree)
+		return PTM_NO_ERROR;
+
+	if (s->type == PTM_MATCH_SC)
+		for (int i = 0;i<s->num_nbrs;i++)
+			if (degree[i] != 4)
+				return PTM_NO_ERROR;
+
+	double normalized[PTM_MAX_POINTS][3];
+	subtract_barycentre(s->num_nbrs + 1, points, normalized);
+
+	int8_t code[2 * PTM_MAX_EDGES];
+	int8_t colours[PTM_MAX_POINTS] = {0};
+	int8_t canonical_labelling[PTM_MAX_POINTS];
+	uint64_t hash = 0;
+	ret = canonical_form_coloured(s->num_facets, facets, s->num_nbrs, degree, colours, canonical_labelling, &code[0], &hash);
+	if (ret != PTM_NO_ERROR)
+		return ret;
+
+	check_graphs(s, hash, canonical_labelling, normalized, res);
+	return PTM_NO_ERROR;
+}
+
+int match_fcc_hcp_ico(double (*ch_points)[3], double (*points)[3], int32_t flags, convexhull_t* ch, result_t* res)
+{
+	int num_nbrs = structure_fcc.num_nbrs;
+	int num_facets = structure_fcc.num_facets;
+	int max_degree = structure_fcc.max_degree;
+
+	int8_t degree[PTM_MAX_NBRS];
+	int8_t facets[PTM_MAX_FACETS][3];
+
+	int ret = get_convex_hull(num_nbrs + 1, (const double (*)[3])ch_points, ch, facets);
+	ch->ok = ret >= 0;
+	if (ret != 0)
+		return PTM_NO_ERROR;
+
+	if (ch->num_facets != num_facets)
+		return PTM_NO_ERROR;			//incorrect number of facets in convex hull
+
+	int _max_degree = graph_degree(num_facets, facets, num_nbrs, degree);
+	if (_max_degree > max_degree)
+		return PTM_NO_ERROR;
+
+	double normalized[PTM_MAX_POINTS][3];
+	subtract_barycentre(num_nbrs + 1, points, normalized);
+
+	int8_t code[2 * PTM_MAX_EDGES];
+	int8_t colours[PTM_MAX_POINTS] = {0};
+	int8_t canonical_labelling[PTM_MAX_POINTS];
+	uint64_t hash = 0;
+	ret = canonical_form_coloured(num_facets, facets, num_nbrs, degree, colours, canonical_labelling, &code[0], &hash);
+	if (ret != PTM_NO_ERROR)
+		return ret;
+
+	if (flags & PTM_CHECK_FCC)	check_graphs(&structure_fcc, hash, canonical_labelling, normalized, res);
+	if (flags & PTM_CHECK_HCP)	check_graphs(&structure_hcp, hash, canonical_labelling, normalized, res);
+	if (flags & PTM_CHECK_ICO)	check_graphs(&structure_ico, hash, canonical_labelling, normalized, res);
+	return PTM_NO_ERROR;
+}
+
+int match_dcub_dhex(double (*ch_points)[3], double (*points)[3], int32_t flags, convexhull_t* ch, result_t* res)
+{
+	int num_nbrs = structure_dcub.num_nbrs;
+	int num_facets = structure_fcc.num_facets;
+	int max_degree = structure_dcub.max_degree;
+
+
+	int8_t facets[PTM_MAX_FACETS][3];
+	int ret = get_convex_hull(num_nbrs + 1, (const double (*)[3])ch_points, ch, facets);
+	ch->ok = ret >= 0;
+	if (ret != 0)
+		return PTM_NO_ERROR;
+
+	//check for facets with multiple inner atoms
+	bool inverted[4] = {false, false, false, false};
+	for (int i=0;i<ch->num_facets;i++)
+	{
+		int n = 0;
+		for (int j=0;j<3;j++)
+		{
+			if (facets[i][j] <= 3)
+			{
+				inverted[facets[i][j]] = true;
+				n++;
+			}
+		}
+		if (n > 1)
+			return PTM_NO_ERROR;
+	}
+
+	int num_inverted = 0;
+	for (int i=0;i<4;i++)
+		num_inverted += inverted[i] ? 1 : 0;
+
+	if (ch->num_facets != num_facets + 2 * num_inverted)
+		return PTM_NO_ERROR;			//incorrect number of facets in convex hull
+
+	int8_t degree[PTM_MAX_NBRS];
+	int _max_degree = graph_degree(num_facets, facets, num_nbrs, degree);
+	if (_max_degree > max_degree)
+		return PTM_NO_ERROR;
+
+	int num_found = 0;
+	int8_t toadd[4][3];
+	for (int i=0;i<ch->num_facets;i++)
+	{
+		int a = facets[i][0];
+		int b = facets[i][1];
+		int c = facets[i][2];
+		if (a <= 3 || b <= 3 || c <= 3)
+			continue;
+
+		int i0 = (a - 4) / 3;
+		int i1 = (b - 4) / 3;
+		int i2 = (c - 4) / 3;
+
+		if (i0 == i1 && i0 == i2)
+		{
+			if (num_found + num_inverted >= 4)
+				return PTM_NO_ERROR;
+
+			toadd[num_found][0] = a;
+			toadd[num_found][1] = b;
+			toadd[num_found][2] = c;
+			num_found++;
+
+			memcpy(&facets[i], &facets[ch->num_facets - 1], 3 * sizeof(int8_t));
+			ch->num_facets--;
+			i--;
+		}
+	}
+
+	if (num_found + num_inverted != 4)
+		return PTM_NO_ERROR;
+
+	for (int i=0;i<num_found;i++)
+	{
+		int a = toadd[i][0];
+		int b = toadd[i][1];
+		int c = toadd[i][2];
+
+		int i0 = (a - 4) / 3;
+
+		facets[ch->num_facets][0] = i0;
+		facets[ch->num_facets][1] = b;
+		facets[ch->num_facets][2] = c;
+		ch->num_facets++;
+
+		facets[ch->num_facets][0] = a;
+		facets[ch->num_facets][1] = i0;
+		facets[ch->num_facets][2] = c;
+		ch->num_facets++;
+
+		facets[ch->num_facets][0] = a;
+		facets[ch->num_facets][1] = b;
+		facets[ch->num_facets][2] = i0;
+		ch->num_facets++;
+	}
+
+	_max_degree = graph_degree(ch->num_facets, facets, num_nbrs, degree);
+	if (_max_degree > max_degree)
+		return PTM_NO_ERROR;
+
+	double normalized[PTM_MAX_POINTS][3];
+	subtract_barycentre(num_nbrs + 1, points, normalized);
+
+	int8_t code[2 * PTM_MAX_EDGES];
+	int8_t colours[PTM_MAX_POINTS] = {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+	int8_t canonical_labelling[PTM_MAX_POINTS];
+	uint64_t hash = 0;
+	ret = canonical_form_coloured(ch->num_facets, facets, num_nbrs, degree, colours, canonical_labelling, &code[0], &hash);
+	if (ret != PTM_NO_ERROR)
+		return ret;
+
+	if (flags & PTM_CHECK_DCUB)	check_graphs(&structure_dcub, hash, canonical_labelling, normalized, res);
+	if (flags & PTM_CHECK_DHEX)	check_graphs(&structure_dhex, hash, canonical_labelling, normalized, res);
+
+	return PTM_NO_ERROR;
+}
+
diff --git a/src/PTM/ptm_structure_matcher.h b/src/PTM/ptm_structure_matcher.h
new file mode 100644
index 0000000000..4b6f969597
--- /dev/null
+++ b/src/PTM/ptm_structure_matcher.h
@@ -0,0 +1,21 @@
+#ifndef PTM_STRUCTURE_MATCHER_H
+#define PTM_STRUCTURE_MATCHER_H
+
+#include "ptm_initialize_data.h"
+#include "ptm_constants.h"
+
+typedef struct
+{
+	double rmsd;
+	double scale;
+	double q[4];		//rotation in quaternion form (rigid body transformation)
+	int8_t mapping[PTM_MAX_POINTS];
+	const refdata_t* ref_struct;
+} result_t;
+
+int match_general(const refdata_t* s, double (*ch_points)[3], double (*points)[3], convexhull_t* ch, result_t* res);
+int match_fcc_hcp_ico(double (*ch_points)[3], double (*points)[3], int32_t flags, convexhull_t* ch, result_t* res);
+int match_dcub_dhex(double (*ch_points)[3], double (*points)[3], int32_t flags, convexhull_t* ch, result_t* res);
+
+#endif
+
diff --git a/src/PTM/ptm_voronoi_cell.cpp b/src/PTM/ptm_voronoi_cell.cpp
new file mode 100644
index 0000000000..6503ea16c6
--- /dev/null
+++ b/src/PTM/ptm_voronoi_cell.cpp
@@ -0,0 +1,1368 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author   : Chris H. Rycroft (LBL / UC Berkeley)
+// Email    : chr@alum.mit.edu
+// Date     : August 30th 2011
+//
+// Modified by PM Larsen for use in Polyhedral Template Matching
+
+/** \file cell.cc
+ * \brief Function implementations for the voronoicell and related classes. */
+
+#include <cmath>
+#include <cstring>
+#include <cstdlib>
+#include "ptm_voronoi_config.h"
+#include "ptm_voronoi_cell.h"
+
+namespace voro {
+
+inline void voro_fatal_error(const char *p,int status) {
+	fprintf(stderr,"voro++: %s\n",p);
+	exit(status);
+	//return -1;//status;
+}
+
+/** Constructs a Voronoi cell and sets up the initial memory. */
+voronoicell_base::voronoicell_base() :
+	current_vertices(init_vertices), current_vertex_order(init_vertex_order),
+	current_delete_size(init_delete_size), current_delete2_size(init_delete2_size),
+	ed(new int*[current_vertices]), nu(new int[current_vertices]),
+	pts(new double[3*current_vertices]), mem(new int[current_vertex_order]),
+	mec(new int[current_vertex_order]), mep(new int*[current_vertex_order]),
+	ds(new int[current_delete_size]), stacke(ds+current_delete_size),
+	ds2(new int[current_delete2_size]), stacke2(ds2+current_delete_size),
+	current_marginal(init_marginal), marg(new int[current_marginal]) {
+	int i;
+	for(i=0;i<3;i++) {
+		mem[i]=init_n_vertices;mec[i]=0;
+		mep[i]=new int[init_n_vertices*((i<<1)+1)];
+	}
+	mem[3]=init_3_vertices;mec[3]=0;
+	mep[3]=new int[init_3_vertices*7];
+	for(i=4;i<current_vertex_order;i++) {
+		mem[i]=init_n_vertices;mec[i]=0;
+		mep[i]=new int[init_n_vertices*((i<<1)+1)];
+	}
+}
+
+/** The voronoicell destructor deallocates all the dynamic memory. */
+voronoicell_base::~voronoicell_base() {
+	for(int i=current_vertex_order-1;i>=0;i--) if(mem[i]>0) delete [] mep[i];
+	delete [] marg;
+	delete [] ds2;delete [] ds;
+	delete [] mep;delete [] mec;
+	delete [] mem;delete [] pts;
+	delete [] nu;delete [] ed;
+}
+
+/** Ensures that enough memory is allocated prior to carrying out a copy.
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in] vb a pointered to the class to be copied. */
+template<class vc_class>
+void voronoicell_base::check_memory_for_copy(vc_class &vc,voronoicell_base* vb) {
+	while(current_vertex_order<vb->current_vertex_order) add_memory_vorder(vc);
+	for(int i=0;i<current_vertex_order;i++) while(mem[i]<vb->mec[i]) add_memory(vc,i,ds2);
+	while(current_vertices<vb->p) add_memory_vertices(vc);
+}
+
+/** Increases the memory storage for a particular vertex order, by increasing
+ * the size of the of the corresponding mep array. If the arrays already exist,
+ * their size is doubled; if they don't exist, then new ones of size
+ * init_n_vertices are allocated. The routine also ensures that the pointers in
+ * the ed array are updated, by making use of the back pointers. For the cases
+ * where the back pointer has been temporarily overwritten in the marginal
+ * vertex code, the auxiliary delete stack is scanned to find out how to update
+ * the ed value. If the template has been instantiated with the neighbor
+ * tracking turned on, then the routine also reallocates the corresponding mne
+ * array.
+ * \param[in] i the order of the vertex memory to be increased. */
+template<class vc_class>
+void voronoicell_base::add_memory(vc_class &vc,int i,int *stackp2) {
+	int s=(i<<1)+1;
+	if(mem[i]==0) {
+		vc.n_allocate(i,init_n_vertices);
+		mep[i]=new int[init_n_vertices*s];
+		mem[i]=init_n_vertices;
+#if VOROPP_VERBOSE >=2
+		fprintf(stderr,"Order %d vertex memory created\n",i);
+#endif
+	} else {
+		int j=0,k,*l;
+		mem[i]<<=1;
+		if(mem[i]>max_n_vertices) voro_fatal_error("Point memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+		fprintf(stderr,"Order %d vertex memory scaled up to %d\n",i,mem[i]);
+#endif
+		l=new int[s*mem[i]];
+		int m=0;
+		vc.n_allocate_aux1(i);
+		while(j<s*mec[i]) {
+			k=mep[i][j+(i<<1)];
+			if(k>=0) {
+				ed[k]=l+j;
+				vc.n_set_to_aux1_offset(k,m);
+			} else {
+				int *dsp;
+				for(dsp=ds2;dsp<stackp2;dsp++) {
+					if(ed[*dsp]==mep[i]+j) {
+						ed[*dsp]=l+j;
+						vc.n_set_to_aux1_offset(*dsp,m);
+						break;
+					}
+				}
+				if(dsp==stackp2) voro_fatal_error("Couldn't relocate dangling pointer",VOROPP_INTERNAL_ERROR);
+#if VOROPP_VERBOSE >=3
+				fputs("Relocated dangling pointer",stderr);
+#endif
+			}
+			for(k=0;k<s;k++,j++) l[j]=mep[i][j];
+			for(k=0;k<i;k++,m++) vc.n_copy_to_aux1(i,m);
+		}
+		delete [] mep[i];
+		mep[i]=l;
+		vc.n_switch_to_aux1(i);
+	}
+}
+
+/** Doubles the maximum number of vertices allowed, by reallocating the ed, nu,
+ * and pts arrays. If the allocation exceeds the absolute maximum set in
+ * max_vertices, then the routine exits with a fatal error. If the template has
+ * been instantiated with the neighbor tracking turned on, then the routine
+ * also reallocates the ne array. */
+template<class vc_class>
+void voronoicell_base::add_memory_vertices(vc_class &vc) {
+
+printf("nope: %d\n", current_vertices);
+exit(3);
+
+	int i=(current_vertices<<1),j,**pp,*pnu;
+	if(i>max_vertices) voro_fatal_error("Vertex memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+	fprintf(stderr,"Vertex memory scaled up to %d\n",i);
+#endif
+	double *ppts;
+	pp=new int*[i];
+	for(j=0;j<current_vertices;j++) pp[j]=ed[j];
+	delete [] ed;ed=pp;
+	vc.n_add_memory_vertices(i);
+	pnu=new int[i];
+	for(j=0;j<current_vertices;j++) pnu[j]=nu[j];
+	delete [] nu;nu=pnu;
+	ppts=new double[3*i];
+	for(j=0;j<3*current_vertices;j++) ppts[j]=pts[j];
+	delete [] pts;pts=ppts;
+	current_vertices=i;
+}
+
+/** Doubles the maximum allowed vertex order, by reallocating mem, mep, and mec
+ * arrays. If the allocation exceeds the absolute maximum set in
+ * max_vertex_order, then the routine causes a fatal error. If the template has
+ * been instantiated with the neighbor tracking turned on, then the routine
+ * also reallocates the mne array. */
+template<class vc_class>
+void voronoicell_base::add_memory_vorder(vc_class &vc) {
+	int i=(current_vertex_order<<1),j,*p1,**p2;
+	if(i>max_vertex_order) voro_fatal_error("Vertex order memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+	fprintf(stderr,"Vertex order memory scaled up to %d\n",i);
+#endif
+	p1=new int[i];
+	for(j=0;j<current_vertex_order;j++) p1[j]=mem[j];while(j<i) p1[j++]=0;
+	delete [] mem;mem=p1;
+	p2=new int*[i];
+	for(j=0;j<current_vertex_order;j++) p2[j]=mep[j];
+	delete [] mep;mep=p2;
+	p1=new int[i];
+	for(j=0;j<current_vertex_order;j++) p1[j]=mec[j];while(j<i) p1[j++]=0;
+	delete [] mec;mec=p1;
+	vc.n_add_memory_vorder(i);
+	current_vertex_order=i;
+}
+
+/** Doubles the size allocation of the main delete stack. If the allocation
+ * exceeds the absolute maximum set in max_delete_size, then routine causes a
+ * fatal error. */
+void voronoicell_base::add_memory_ds(int *&stackp) {
+	current_delete_size<<=1;
+	if(current_delete_size>max_delete_size) voro_fatal_error("Delete stack 1 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+	fprintf(stderr,"Delete stack 1 memory scaled up to %d\n",current_delete_size);
+#endif
+	int *dsn=new int[current_delete_size],*dsnp=dsn,*dsp=ds;
+	while(dsp<stackp) *(dsnp++)=*(dsp++);
+	delete [] ds;ds=dsn;stackp=dsnp;
+	stacke=ds+current_delete_size;
+}
+
+/** Doubles the size allocation of the auxiliary delete stack. If the
+ * allocation exceeds the absolute maximum set in max_delete2_size, then the
+ * routine causes a fatal error. */
+void voronoicell_base::add_memory_ds2(int *&stackp2) {
+	current_delete2_size<<=1;
+	if(current_delete2_size>max_delete2_size) voro_fatal_error("Delete stack 2 memory allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+	fprintf(stderr,"Delete stack 2 memory scaled up to %d\n",current_delete2_size);
+#endif
+	int *dsn=new int[current_delete2_size],*dsnp=dsn,*dsp=ds2;
+	while(dsp<stackp2) *(dsnp++)=*(dsp++);
+	delete [] ds2;ds2=dsn;stackp2=dsnp;
+	stacke2=ds2+current_delete2_size;
+}
+
+/** Initializes a Voronoi cell as a rectangular box with the given dimensions.
+ * \param[in] (xmin,xmax) the minimum and maximum x coordinates.
+ * \param[in] (ymin,ymax) the minimum and maximum y coordinates.
+ * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */
+void voronoicell_base::init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) {
+	for(int i=0;i<current_vertex_order;i++) mec[i]=0;up=0;
+	mec[3]=p=8;xmin*=2;xmax*=2;ymin*=2;ymax*=2;zmin*=2;zmax*=2;
+	*pts=xmin;pts[1]=ymin;pts[2]=zmin;
+	pts[3]=xmax;pts[4]=ymin;pts[5]=zmin;
+	pts[6]=xmin;pts[7]=ymax;pts[8]=zmin;
+	pts[9]=xmax;pts[10]=ymax;pts[11]=zmin;
+	pts[12]=xmin;pts[13]=ymin;pts[14]=zmax;
+	pts[15]=xmax;pts[16]=ymin;pts[17]=zmax;
+	pts[18]=xmin;pts[19]=ymax;pts[20]=zmax;
+	pts[21]=xmax;pts[22]=ymax;pts[23]=zmax;
+	int *q=mep[3];
+	*q=1;q[1]=4;q[2]=2;q[3]=2;q[4]=1;q[5]=0;q[6]=0;
+	q[7]=3;q[8]=5;q[9]=0;q[10]=2;q[11]=1;q[12]=0;q[13]=1;
+	q[14]=0;q[15]=6;q[16]=3;q[17]=2;q[18]=1;q[19]=0;q[20]=2;
+	q[21]=2;q[22]=7;q[23]=1;q[24]=2;q[25]=1;q[26]=0;q[27]=3;
+	q[28]=6;q[29]=0;q[30]=5;q[31]=2;q[32]=1;q[33]=0;q[34]=4;
+	q[35]=4;q[36]=1;q[37]=7;q[38]=2;q[39]=1;q[40]=0;q[41]=5;
+	q[42]=7;q[43]=2;q[44]=4;q[45]=2;q[46]=1;q[47]=0;q[48]=6;
+	q[49]=5;q[50]=3;q[51]=6;q[52]=2;q[53]=1;q[54]=0;q[55]=7;
+	*ed=q;ed[1]=q+7;ed[2]=q+14;ed[3]=q+21;
+	ed[4]=q+28;ed[5]=q+35;ed[6]=q+42;ed[7]=q+49;
+	*nu=nu[1]=nu[2]=nu[3]=nu[4]=nu[5]=nu[6]=nu[7]=3;
+}
+
+/** Starting from a point within the current cutting plane, this routine attempts
+ * to find an edge to a point outside the cutting plane. This prevents the plane
+ * routine from .
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in,out] up */
+template<class vc_class>
+inline bool voronoicell_base::search_for_outside_edge(vc_class &vc,int &up) {
+	int i,lp,lw,*j(ds2),*stackp2(ds2);
+	double l;
+	*(stackp2++)=up;
+	while(j<stackp2) {
+		up=*(j++);
+		for(i=0;i<nu[up];i++) {
+			lp=ed[up][i];
+			lw=m_test(lp,l);
+			if(lw==-1) return true;
+			else if(lw==0) add_to_stack(vc,lp,stackp2);
+		}
+	}
+	return false;
+}
+
+/** Adds a point to the auxiliary delete stack if it is not already there.
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in] lp the index of the point to add.
+ * \param[in,out] stackp2 a pointer to the end of the stack entries. */
+template<class vc_class>
+inline void voronoicell_base::add_to_stack(vc_class &vc,int lp,int *&stackp2) {
+	for(int *k(ds2);k<stackp2;k++) if(*k==lp) return;
+	if(stackp2==stacke2) add_memory_ds2(stackp2);
+	*(stackp2++)=lp;
+}
+
+/** Cuts the Voronoi cell by a particle whose center is at a separation of
+ * (x,y,z) from the cell center. The value of rsq should be initially set to
+ * \f$x^2+y^2+z^2\f$.
+ * \param[in] vc a reference to the specialized version of the calling class.
+ * \param[in] (x,y,z) the normal vector to the plane.
+ * \param[in] rsq the distance along this vector of the plane.
+ * \param[in] p_id the plane ID (for neighbor tracking only).
+ * \return False if the plane cut deleted the cell entirely, true otherwise. */
+template<class vc_class>
+bool voronoicell_base::nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id) {
+	int count=0,i,j,k,lp=up,cp,qp,rp,*stackp(ds),*stackp2(ds2),*dsp;
+	int us=0,ls=0,qs,iqs,cs,uw,qw,lw;
+	int *edp,*edd;
+	double u,l,r,q;bool complicated_setup=false,new_double_edge=false,double_edge=false;
+
+	// Initialize the safe testing routine
+	n_marg=0;px=x;py=y;pz=z;prsq=rsq;
+
+	// Test approximately sqrt(n)/4 points for their proximity to the plane
+	// and keep the one which is closest
+	uw=m_test(up,u);
+
+	// Starting from an initial guess, we now move from vertex to vertex,
+	// to try and find an edge which intersects the cutting plane,
+	// or a vertex which is on the plane
+	try {
+		if(uw==1) {
+
+			// The test point is inside the cutting plane.
+			us=0;
+			do {
+				lp=ed[up][us];
+				lw=m_test(lp,l);
+				if(l<u) break;
+				us++;
+			} while (us<nu[up]);
+
+			if(us==nu[up]) {
+				return false;
+			}
+
+			ls=ed[up][nu[up]+us];
+			while(lw==1) {
+				if(++count>=p) throw true;
+				u=l;up=lp;
+				for(us=0;us<ls;us++) {
+					lp=ed[up][us];
+					lw=m_test(lp,l);
+					if(l<u) break;
+				}
+				if(us==ls) {
+					us++;
+					while(us<nu[up]) {
+						lp=ed[up][us];
+						lw=m_test(lp,l);
+						if(l<u) break;
+						us++;
+					}
+					if(us==nu[up]) {
+						return false;
+					}
+				}
+				ls=ed[up][nu[up]+us];
+			}
+
+			// If the last point in the iteration is within the
+			// plane, we need to do the complicated setup
+			// routine. Otherwise, we use the regular iteration.
+			if(lw==0) {
+				up=lp;
+				complicated_setup=true;
+			} else complicated_setup=false;
+		} else if(uw==-1) {
+			us=0;
+			do {
+				qp=ed[up][us];
+				qw=m_test(qp,q);
+				if(u<q) break;
+				us++;
+			} while (us<nu[up]);
+			if(us==nu[up]) return true;
+
+			while(qw==-1) {
+				qs=ed[up][nu[up]+us];
+				if(++count>=p) throw true;
+				u=q;up=qp;
+				for(us=0;us<qs;us++) {
+					qp=ed[up][us];
+					qw=m_test(qp,q);
+					if(u<q) break;
+				}
+				if(us==qs) {
+					us++;
+					while(us<nu[up]) {
+						qp=ed[up][us];
+						qw=m_test(qp,q);
+						if(u<q) break;
+						us++;
+					}
+					if(us==nu[up]) return true;
+				}
+			}
+			if(qw==1) {
+				lp=up;ls=us;l=u;
+				up=qp;us=ed[lp][nu[lp]+ls];u=q;
+				complicated_setup=false;
+			} else {
+				up=qp;
+				complicated_setup=true;
+			}
+		} else {
+
+			// Our original test point was on the plane, so we
+			// automatically head for the complicated setup
+			// routine
+			complicated_setup=true;
+		}
+	}
+	catch(bool except) {
+		// This routine is a fall-back, in case floating point errors
+		// cause the usual search routine to fail. In the fall-back
+		// routine, we just test every edge to find one straddling
+		// the plane.
+#if VOROPP_VERBOSE >=1
+		fputs("Bailed out of convex calculation\n",stderr);
+#endif
+		qw=1;lw=0;
+		for(qp=0;qp<p;qp++) {
+			qw=m_test(qp,q);
+			if(qw==1) {
+
+				// The point is inside the cutting space. Now
+				// see if we can find a neighbor which isn't.
+				for(us=0;us<nu[qp];us++) {
+					lp=ed[qp][us];
+					if(lp<qp) {
+						lw=m_test(lp,l);
+						if(lw!=1) break;
+					}
+				}
+				if(us<nu[qp]) {
+					up=qp;
+					if(lw==0) {
+						complicated_setup=true;
+					} else {
+						complicated_setup=false;
+						u=q;
+						ls=ed[up][nu[up]+us];
+					}
+					break;
+				}
+			} else if(qw==-1) {
+
+				// The point is outside the cutting space. See
+				// if we can find a neighbor which isn't.
+				for(ls=0;ls<nu[qp];ls++) {
+					up=ed[qp][ls];
+					if(up<qp) {
+						uw=m_test(up,u);
+						if(uw!=-1) break;
+					}
+				}
+				if(ls<nu[qp]) {
+					if(uw==0) {
+						up=qp;
+						complicated_setup=true;
+					} else {
+						complicated_setup=false;
+						lp=qp;l=q;
+						us=ed[lp][nu[lp]+ls];
+					}
+					break;
+				}
+			} else {
+
+				// The point is in the plane, so we just
+				// proceed with the complicated setup routine
+				up=qp;
+				complicated_setup=true;
+				break;
+			}
+		}
+		if(qp==p) return qw==-1?true:false;
+	}
+
+	// We're about to add the first point of the new facet. In either
+	// routine, we have to add a point, so first check there's space for
+	// it.
+	if(p==current_vertices) add_memory_vertices(vc);
+
+	if(complicated_setup) {
+
+		// We want to be strict about reaching the conclusion that the
+		// cell is entirely within the cutting plane. It's not enough
+		// to find a vertex that has edges which are all inside or on
+		// the plane. If the vertex has neighbors that are also on the
+		// plane, we should check those too.
+		if(!search_for_outside_edge(vc,up)) return false;
+
+		// The search algorithm found a point which is on the cutting
+		// plane. We leave that point in place, and create a new one at
+		// the same location.
+		pts[3*p]=pts[3*up];
+		pts[3*p+1]=pts[3*up+1];
+		pts[3*p+2]=pts[3*up+2];
+
+		// Search for a collection of edges of the test vertex which
+		// are outside of the cutting space. Begin by testing the
+		// zeroth edge.
+		i=0;
+		lp=*ed[up];
+		lw=m_test(lp,l);
+		if(lw!=-1) {
+
+			// The first edge is either inside the cutting space,
+			// or lies within the cutting plane. Test the edges
+			// sequentially until we find one that is outside.
+			rp=lw;
+			do {
+				i++;
+
+				// If we reached the last edge with no luck
+				// then all of the vertices are inside
+				// or on the plane, so the cell is completely
+				// deleted
+				if(i==nu[up]) return false;
+				lp=ed[up][i];
+				lw=m_test(lp,l);
+			} while (lw!=-1);
+			j=i+1;
+
+			// We found an edge outside the cutting space. Keep
+			// moving through these edges until we find one that's
+			// inside or on the plane.
+			while(j<nu[up]) {
+				lp=ed[up][j];
+				lw=m_test(lp,l);
+				if(lw!=-1) break;
+				j++;
+			}
+
+			// Compute the number of edges for the new vertex. In
+			// general it will be the number of outside edges
+			// found, plus two. But we need to recognize the
+			// special case when all but one edge is outside, and
+			// the remaining one is on the plane. For that case we
+			// have to reduce the edge count by one to prevent
+			// doubling up.
+			if(j==nu[up]&&i==1&&rp==0) {
+				nu[p]=nu[up];
+				double_edge=true;
+			} else nu[p]=j-i+2;
+			k=1;
+
+			// Add memory for the new vertex if needed, and
+			// initialize
+			while (nu[p]>=current_vertex_order) add_memory_vorder(vc);
+			if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2);
+			vc.n_set_pointer(p,nu[p]);
+			ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++;
+			ed[p][nu[p]<<1]=p;
+
+			// Copy the edges of the original vertex into the new
+			// one. Delete the edges of the original vertex, and
+			// update the relational table.
+			us=cycle_down(i,up);
+			while(i<j) {
+				qp=ed[up][i];
+				qs=ed[up][nu[up]+i];
+				vc.n_copy(p,k,up,i);
+				ed[p][k]=qp;
+				ed[p][nu[p]+k]=qs;
+				ed[qp][qs]=p;
+				ed[qp][nu[qp]+qs]=k;
+				ed[up][i]=-1;
+				i++;k++;
+			}
+			qs=i==nu[up]?0:i;
+		} else {
+
+			// In this case, the zeroth edge is outside the cutting
+			// plane. Begin by searching backwards from the last
+			// edge until we find an edge which isn't outside.
+			i=nu[up]-1;
+			lp=ed[up][i];
+			lw=m_test(lp,l);
+			while(lw==-1) {
+				i--;
+
+				// If i reaches zero, then we have a point in
+				// the plane all of whose edges are outside
+				// the cutting space, so we just exit
+				if(i==0) return true;
+				lp=ed[up][i];
+				lw=m_test(lp,l);
+			}
+
+			// Now search forwards from zero
+			j=1;
+			qp=ed[up][j];
+			qw=m_test(qp,q);
+			while(qw==-1) {
+				j++;
+				qp=ed[up][j];
+				qw=m_test(qp,l);
+			}
+
+			// Compute the number of edges for the new vertex. In
+			// general it will be the number of outside edges
+			// found, plus two. But we need to recognize the
+			// special case when all but one edge is outside, and
+			// the remaining one is on the plane. For that case we
+			// have to reduce the edge count by one to prevent
+			// doubling up.
+			if(i==j&&qw==0) {
+				double_edge=true;
+				nu[p]=nu[up];
+			} else {
+				nu[p]=nu[up]-i+j+1;
+			}
+
+			// Add memory to store the vertex if it doesn't exist
+			// already
+			k=1;
+			while(nu[p]>=current_vertex_order) add_memory_vorder(vc);
+			if(mec[nu[p]]==mem[nu[p]]) add_memory(vc,nu[p],stackp2);
+
+			// Copy the edges of the original vertex into the new
+			// one. Delete the edges of the original vertex, and
+			// update the relational table.
+			vc.n_set_pointer(p,nu[p]);
+			ed[p]=mep[nu[p]]+((nu[p]<<1)+1)*mec[nu[p]]++;
+			ed[p][nu[p]<<1]=p;
+			us=i++;
+			while(i<nu[up]) {
+				qp=ed[up][i];
+				qs=ed[up][nu[up]+i];
+				vc.n_copy(p,k,up,i);
+				ed[p][k]=qp;
+				ed[p][nu[p]+k]=qs;
+				ed[qp][qs]=p;
+				ed[qp][nu[qp]+qs]=k;
+				ed[up][i]=-1;
+				i++;k++;
+			}
+			i=0;
+			while(i<j) {
+				qp=ed[up][i];
+				qs=ed[up][nu[up]+i];
+				vc.n_copy(p,k,up,i);
+				ed[p][k]=qp;
+				ed[p][nu[p]+k]=qs;
+				ed[qp][qs]=p;
+				ed[qp][nu[qp]+qs]=k;
+				ed[up][i]=-1;
+				i++;k++;
+			}
+			qs=j;
+		}
+		if(!double_edge) {
+			vc.n_copy(p,k,up,qs);
+			vc.n_set(p,0,p_id);
+		} else vc.n_copy(p,0,up,qs);
+
+		// Add this point to the auxiliary delete stack
+		if(stackp2==stacke2) add_memory_ds2(stackp2);
+		*(stackp2++)=up;
+
+		// Look at the edges on either side of the group that was
+		// detected. We're going to commence facet computation by
+		// moving along one of them. We are going to end up coming back
+		// along the other one.
+		cs=k;
+		qp=up;q=u;
+		i=ed[up][us];
+		us=ed[up][nu[up]+us];
+		up=i;
+		ed[qp][nu[qp]<<1]=-p;
+
+	} else {
+
+		// The search algorithm found an intersected edge between the
+		// points lp and up. Create a new vertex between them which
+		// lies on the cutting plane. Since u and l differ by at least
+		// the tolerance, this division should never screw up.
+		if(stackp==stacke) add_memory_ds(stackp);
+		*(stackp++)=up;
+		r=u/(u-l);l=1-r;
+		pts[3*p]=pts[3*lp]*r+pts[3*up]*l;
+		pts[3*p+1]=pts[3*lp+1]*r+pts[3*up+1]*l;
+		pts[3*p+2]=pts[3*lp+2]*r+pts[3*up+2]*l;
+
+		// This point will always have three edges. Connect one of them
+		// to lp.
+		nu[p]=3;
+		if(mec[3]==mem[3]) add_memory(vc,3,stackp2);
+		vc.n_set_pointer(p,3);
+		vc.n_set(p,0,p_id);
+		vc.n_copy(p,1,up,us);
+		vc.n_copy(p,2,lp,ls);
+		ed[p]=mep[3]+7*mec[3]++;
+		ed[p][6]=p;
+		ed[up][us]=-1;
+		ed[lp][ls]=p;
+		ed[lp][nu[lp]+ls]=1;
+		ed[p][1]=lp;
+		ed[p][nu[p]+1]=ls;
+		cs=2;
+
+		// Set the direction to move in
+		qs=cycle_up(us,up);
+		qp=up;q=u;
+	}
+
+	// When the code reaches here, we have initialized the first point, and
+	// we have a direction for moving it to construct the rest of the facet
+	cp=p;rp=p;p++;
+	while(qp!=up||qs!=us) {
+
+		// We're currently tracing round an intersected facet. Keep
+		// moving around it until we find a point or edge which
+		// intersects the plane.
+		lp=ed[qp][qs];
+		lw=m_test(lp,l);
+
+		if(lw==1) {
+
+			// The point is still in the cutting space. Just add it
+			// to the delete stack and keep moving.
+			qs=cycle_up(ed[qp][nu[qp]+qs],lp);
+			qp=lp;
+			q=l;
+			if(stackp==stacke) add_memory_ds(stackp);
+			*(stackp++)=qp;
+
+		} else if(lw==-1) {
+
+			// The point is outside of the cutting space, so we've
+			// found an intersected edge. Introduce a regular point
+			// at the point of intersection. Connect it to the
+			// point we just tested. Also connect it to the previous
+			// new point in the facet we're constructing.
+			if(p==current_vertices) add_memory_vertices(vc);
+			r=q/(q-l);l=1-r;
+			pts[3*p]=pts[3*lp]*r+pts[3*qp]*l;
+			pts[3*p+1]=pts[3*lp+1]*r+pts[3*qp+1]*l;
+			pts[3*p+2]=pts[3*lp+2]*r+pts[3*qp+2]*l;
+			nu[p]=3;
+			if(mec[3]==mem[3]) add_memory(vc,3,stackp2);
+			ls=ed[qp][qs+nu[qp]];
+			vc.n_set_pointer(p,3);
+			vc.n_set(p,0,p_id);
+			vc.n_copy(p,1,qp,qs);
+			vc.n_copy(p,2,lp,ls);
+			ed[p]=mep[3]+7*mec[3]++;
+			*ed[p]=cp;
+			ed[p][1]=lp;
+			ed[p][3]=cs;
+			ed[p][4]=ls;
+			ed[p][6]=p;
+			ed[lp][ls]=p;
+			ed[lp][nu[lp]+ls]=1;
+			ed[cp][cs]=p;
+			ed[cp][nu[cp]+cs]=0;
+			ed[qp][qs]=-1;
+			qs=cycle_up(qs,qp);
+			cp=p++;
+			cs=2;
+		} else {
+
+			// We've found a point which is on the cutting plane.
+			// We're going to introduce a new point right here, but
+			// first we need to figure out the number of edges it
+			// has.
+			if(p==current_vertices) add_memory_vertices(vc);
+
+			// If the previous vertex detected a double edge, our
+			// new vertex will have one less edge.
+			k=double_edge?0:1;
+			qs=ed[qp][nu[qp]+qs];
+			qp=lp;
+			iqs=qs;
+
+			// Start testing the edges of the current point until
+			// we find one which isn't outside the cutting space
+			do {
+				k++;
+				qs=cycle_up(qs,qp);
+				lp=ed[qp][qs];
+				lw=m_test(lp,l);
+			} while (lw==-1);
+
+			// Now we need to find out whether this marginal vertex
+			// we are on has been visited before, because if that's
+			// the case, we need to add vertices to the existing
+			// new vertex, rather than creating a fresh one. We also
+			// need to figure out whether we're in a case where we
+			// might be creating a duplicate edge.
+			j=-ed[qp][nu[qp]<<1];
+	 		if(qp==up&&qs==us) {
+
+				// If we're heading into the final part of the
+				// new facet, then we never worry about the
+				// duplicate edge calculation.
+				new_double_edge=false;
+				if(j>0) k+=nu[j];
+			} else {
+				if(j>0) {
+
+					// This vertex was visited before, so
+					// count those vertices to the ones we
+					// already have.
+					k+=nu[j];
+
+					// The only time when we might make a
+					// duplicate edge is if the point we're
+					// going to move to next is also a
+					// marginal point, so test for that
+					// first.
+					if(lw==0) {
+
+						// Now see whether this marginal point
+						// has been visited before.
+						i=-ed[lp][nu[lp]<<1];
+						if(i>0) {
+
+							// Now see if the last edge of that other
+							// marginal point actually ends up here.
+							if(ed[i][nu[i]-1]==j) {
+								new_double_edge=true;
+								k-=1;
+							} else new_double_edge=false;
+						} else {
+
+							// That marginal point hasn't been visited
+							// before, so we probably don't have to worry
+							// about duplicate edges, except in the
+							// case when that's the way into the end
+							// of the facet, because that way always creates
+							// an edge.
+							if(j==rp&&lp==up&&ed[qp][nu[qp]+qs]==us) {
+								new_double_edge=true;
+								k-=1;
+							} else new_double_edge=false;
+						}
+					} else new_double_edge=false;
+				} else {
+
+					// The vertex hasn't been visited
+					// before, but let's see if it's
+					// marginal
+					if(lw==0) {
+
+						// If it is, we need to check
+						// for the case that it's a
+						// small branch, and that we're
+						// heading right back to where
+						// we came from
+						i=-ed[lp][nu[lp]<<1];
+						if(i==cp) {
+							new_double_edge=true;
+							k-=1;
+						} else new_double_edge=false;
+					} else new_double_edge=false;
+				}
+			}
+
+			// k now holds the number of edges of the new vertex
+			// we are forming. Add memory for it if it doesn't exist
+			// already.
+			while(k>=current_vertex_order) add_memory_vorder(vc);
+			if(mec[k]==mem[k]) add_memory(vc,k,stackp2);
+
+			// Now create a new vertex with order k, or augment
+			// the existing one
+			if(j>0) {
+
+				// If we're augmenting a vertex but we don't
+				// actually need any more edges, just skip this
+				// routine to avoid memory confusion
+				if(nu[j]!=k) {
+					// Allocate memory and copy the edges
+					// of the previous instance into it
+					vc.n_set_aux1(k);
+					edp=mep[k]+((k<<1)+1)*mec[k]++;
+					i=0;
+					while(i<nu[j]) {
+						vc.n_copy_aux1(j,i);
+						edp[i]=ed[j][i];
+						edp[k+i]=ed[j][nu[j]+i];
+						i++;
+					}
+					edp[k<<1]=j;
+
+					// Remove the previous instance with
+					// fewer vertices from the memory
+					// structure
+					edd=mep[nu[j]]+((nu[j]<<1)+1)*--mec[nu[j]];
+					if(edd!=ed[j]) {
+						for(lw=0;lw<=(nu[j]<<1);lw++) ed[j][lw]=edd[lw];
+						vc.n_set_aux2_copy(j,nu[j]);
+						vc.n_copy_pointer(edd[nu[j]<<1],j);
+						ed[edd[nu[j]<<1]]=ed[j];
+					}
+					vc.n_set_to_aux1(j);
+					ed[j]=edp;
+				} else i=nu[j];
+			} else {
+
+				// Allocate a new vertex of order k
+				vc.n_set_pointer(p,k);
+				ed[p]=mep[k]+((k<<1)+1)*mec[k]++;
+				ed[p][k<<1]=p;
+				if(stackp2==stacke2) add_memory_ds2(stackp2);
+				*(stackp2++)=qp;
+				pts[3*p]=pts[3*qp];
+				pts[3*p+1]=pts[3*qp+1];
+				pts[3*p+2]=pts[3*qp+2];
+				ed[qp][nu[qp]<<1]=-p;
+				j=p++;
+				i=0;
+			}
+			nu[j]=k;
+
+			// Unless the previous case was a double edge, connect
+			// the first available edge of the new vertex to the
+			// last one in the facet
+			if(!double_edge) {
+				ed[j][i]=cp;
+				ed[j][nu[j]+i]=cs;
+				vc.n_set(j,i,p_id);
+				ed[cp][cs]=j;
+				ed[cp][nu[cp]+cs]=i;
+				i++;
+			}
+
+			// Copy in the edges of the underlying vertex,
+			// and do one less if this was a double edge
+			qs=iqs;
+			while(i<(new_double_edge?k:k-1)) {
+				qs=cycle_up(qs,qp);
+				lp=ed[qp][qs];ls=ed[qp][nu[qp]+qs];
+				vc.n_copy(j,i,qp,qs);
+				ed[j][i]=lp;
+				ed[j][nu[j]+i]=ls;
+				ed[lp][ls]=j;
+				ed[lp][nu[lp]+ls]=i;
+				ed[qp][qs]=-1;
+				i++;
+			}
+			qs=cycle_up(qs,qp);
+			cs=i;
+			cp=j;
+			vc.n_copy(j,new_double_edge?0:cs,qp,qs);
+
+			// Update the double_edge flag, to pass it
+			// to the next instance of this routine
+			double_edge=new_double_edge;
+		}
+	}
+
+	// Connect the final created vertex to the initial one
+	ed[cp][cs]=rp;
+	*ed[rp]=cp;
+	ed[cp][nu[cp]+cs]=0;
+	ed[rp][nu[rp]]=cs;
+
+	// Delete points: first, remove any duplicates
+	dsp=ds;
+	while(dsp<stackp) {
+		j=*dsp;
+		if(ed[j][nu[j]]!=-1) {
+			ed[j][nu[j]]=-1;
+			dsp++;
+		} else *dsp=*(--stackp);
+	}
+
+	// Add the points in the auxiliary delete stack,
+	// and reset their back pointers
+	for(dsp=ds2;dsp<stackp2;dsp++) {
+		j=*dsp;
+		ed[j][nu[j]<<1]=j;
+		if(ed[j][nu[j]]!=-1) {
+			ed[j][nu[j]]=-1;
+			if(stackp==stacke) add_memory_ds(stackp);
+			*(stackp++)=j;
+		}
+	}
+
+	// Scan connections and add in extras
+	for(dsp=ds;dsp<stackp;dsp++) {
+		cp=*dsp;
+		for(edp=ed[cp];edp<ed[cp]+nu[cp];edp++) {
+			qp=*edp;
+			if(qp!=-1&&ed[qp][nu[qp]]!=-1) {
+				if(stackp==stacke) {
+					int dis=stackp-dsp;
+					add_memory_ds(stackp);
+					dsp=ds+dis;
+				}
+				*(stackp++)=qp;
+				ed[qp][nu[qp]]=-1;
+			}
+		}
+	}
+	up=0;
+
+	// Delete them from the array structure
+	while(stackp>ds) {
+		--p;
+		while(ed[p][nu[p]]==-1) {
+			j=nu[p];
+			edp=ed[p];edd=(mep[j]+((j<<1)+1)*--mec[j]);
+			while(edp<ed[p]+(j<<1)+1) *(edp++)=*(edd++);
+			vc.n_set_aux2_copy(p,j);
+			vc.n_copy_pointer(ed[p][(j<<1)],p);
+			ed[ed[p][(j<<1)]]=ed[p];
+			--p;
+		}
+		up=*(--stackp);
+		if(up<p) {
+
+			// Vertex management
+			pts[3*up]=pts[3*p];
+			pts[3*up+1]=pts[3*p+1];
+			pts[3*up+2]=pts[3*p+2];
+
+			// Memory management
+			j=nu[up];
+			edp=ed[up];edd=(mep[j]+((j<<1)+1)*--mec[j]);
+			while(edp<ed[up]+(j<<1)+1) *(edp++)=*(edd++);
+			vc.n_set_aux2_copy(up,j);
+			vc.n_copy_pointer(ed[up][j<<1],up);
+			vc.n_copy_pointer(up,p);
+			ed[ed[up][j<<1]]=ed[up];
+
+			// Edge management
+			ed[up]=ed[p];
+			nu[up]=nu[p];
+			for(i=0;i<nu[up];i++) ed[ed[up][i]][ed[up][nu[up]+i]]=up;
+			ed[up][nu[up]<<1]=up;
+		} else up=p++;
+	}
+
+	// Check for any vertices of zero order
+	if(*mec>0) voro_fatal_error("Zero order vertex formed",VOROPP_INTERNAL_ERROR);
+
+	// Collapse any order 2 vertices and exit
+	return collapse_order2(vc);
+}
+
+/** During the creation of a new facet in the plane routine, it is possible
+ * that some order two vertices may arise. This routine removes them.
+ * Suppose an order two vertex joins c and d. If there's a edge between
+ * c and d already, then the order two vertex is just removed; otherwise,
+ * the order two vertex is removed and c and d are joined together directly.
+ * It is possible this process will create order two or order one vertices,
+ * and the routine is continually run until all of them are removed.
+ * \return False if the vertex removal was unsuccessful, indicative of the cell
+ *         reducing to zero volume and disappearing; true if the vertex removal
+ *         was successful. */
+template<class vc_class>
+inline bool voronoicell_base::collapse_order2(vc_class &vc) {
+	if(!collapse_order1(vc)) return false;
+	int a,b,i,j,k,l;
+	while(mec[2]>0) {
+
+		// Pick a order 2 vertex and read in its edges
+		i=--mec[2];
+		j=mep[2][5*i];k=mep[2][5*i+1];
+		if(j==k) {
+#if VOROPP_VERBOSE >=1
+			fputs("Order two vertex joins itself",stderr);
+#endif
+			return false;
+		}
+
+		// Scan the edges of j to see if joins k
+		for(l=0;l<nu[j];l++) {
+			if(ed[j][l]==k) break;
+		}
+
+		// If j doesn't already join k, join them together.
+		// Otherwise delete the connection to the current
+		// vertex from j and k.
+		a=mep[2][5*i+2];b=mep[2][5*i+3];i=mep[2][5*i+4];
+		if(l==nu[j]) {
+			ed[j][a]=k;
+			ed[k][b]=j;
+			ed[j][nu[j]+a]=b;
+			ed[k][nu[k]+b]=a;
+		} else {
+			if(!delete_connection(vc,j,a,false)) return false;
+			if(!delete_connection(vc,k,b,true)) return false;
+		}
+
+		// Compact the memory
+		--p;
+		if(up==i) up=0;
+		if(p!=i) {
+			if(up==p) up=i;
+			pts[3*i]=pts[3*p];
+			pts[3*i+1]=pts[3*p+1];
+			pts[3*i+2]=pts[3*p+2];
+			for(k=0;k<nu[p];k++) ed[ed[p][k]][ed[p][nu[p]+k]]=i;
+			vc.n_copy_pointer(i,p);
+			ed[i]=ed[p];
+			nu[i]=nu[p];
+			ed[i][nu[i]<<1]=i;
+		}
+
+		// Collapse any order 1 vertices if they were created
+		if(!collapse_order1(vc)) return false;
+	}
+	return true;
+}
+
+/** Order one vertices can potentially be created during the order two collapse
+ * routine. This routine keeps removing them until there are none left.
+ * \return False if the vertex removal was unsuccessful, indicative of the cell
+ *         having zero volume and disappearing; true if the vertex removal was
+ *         successful. */
+template<class vc_class>
+inline bool voronoicell_base::collapse_order1(vc_class &vc) {
+	int i,j,k;
+	while(mec[1]>0) {
+		up=0;
+#if VOROPP_VERBOSE >=1
+		fputs("Order one collapse\n",stderr);
+#endif
+		i=--mec[1];
+		j=mep[1][3*i];k=mep[1][3*i+1];
+		i=mep[1][3*i+2];
+		if(!delete_connection(vc,j,k,false)) return false;
+		--p;
+		if(up==i) up=0;
+		if(p!=i) {
+			if(up==p) up=i;
+			pts[3*i]=pts[3*p];
+			pts[3*i+1]=pts[3*p+1];
+			pts[3*i+2]=pts[3*p+2];
+			for(k=0;k<nu[p];k++) ed[ed[p][k]][ed[p][nu[p]+k]]=i;
+			vc.n_copy_pointer(i,p);
+			ed[i]=ed[p];
+			nu[i]=nu[p];
+			ed[i][nu[i]<<1]=i;
+		}
+	}
+	return true;
+}
+
+/** This routine deletes the kth edge of vertex j and reorganizes the memory.
+ * If the neighbor computation is enabled, we also have to supply an handedness
+ * flag to decide whether to preserve the plane on the left or right of the
+ * connection.
+ * \return False if a zero order vertex was formed, indicative of the cell
+ *         disappearing; true if the vertex removal was successful. */
+template<class vc_class>
+inline bool voronoicell_base::delete_connection(vc_class &vc,int j,int k,bool hand) {
+	int q=hand?k:cycle_up(k,j);
+	int i=nu[j]-1,l,*edp,*edd,m;
+#if VOROPP_VERBOSE >=1
+	if(i<1) {
+		fputs("Zero order vertex formed\n",stderr);
+		return false;
+	}
+#endif
+	if(mec[i]==mem[i]) add_memory(vc,i,ds2);
+	vc.n_set_aux1(i);
+	for(l=0;l<q;l++) vc.n_copy_aux1(j,l);
+	while(l<i) {
+		vc.n_copy_aux1_shift(j,l);
+		l++;
+	}
+	edp=mep[i]+((i<<1)+1)*mec[i]++;
+	edp[i<<1]=j;
+	for(l=0;l<k;l++) {
+		edp[l]=ed[j][l];
+		edp[l+i]=ed[j][l+nu[j]];
+	}
+	while(l<i) {
+		m=ed[j][l+1];
+		edp[l]=m;
+		k=ed[j][l+nu[j]+1];
+		edp[l+i]=k;
+		ed[m][nu[m]+k]--;
+		l++;
+	}
+
+	edd=mep[nu[j]]+((nu[j]<<1)+1)*--mec[nu[j]];
+	for(l=0;l<=(nu[j]<<1);l++) ed[j][l]=edd[l];
+	vc.n_set_aux2_copy(j,nu[j]);
+	vc.n_set_to_aux2(edd[nu[j]<<1]);
+	vc.n_set_to_aux1(j);
+	ed[edd[nu[j]<<1]]=edd;
+	ed[j]=edp;
+	nu[j]=i;
+	return true;
+}
+
+/** Calculates the areas of each face of the Voronoi cell and prints the
+ * results to an output stream.
+ * \param[out] v the vector to store the results in. */
+void voronoicell_base::face_areas(std::vector<double> &v) {
+	double area;
+	v.clear();
+	int i,j,k,l,m,n;
+	double ux,uy,uz,vx,vy,vz,wx,wy,wz;
+	for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+		k=ed[i][j];
+		if(k>=0) {
+			area=0;
+			ed[i][j]=-1-k;
+			l=cycle_up(ed[i][nu[i]+j],k);
+			m=ed[k][l];ed[k][l]=-1-m;
+			while(m!=i) {
+				n=cycle_up(ed[k][nu[k]+l],m);
+				ux=pts[3*k]-pts[3*i];
+				uy=pts[3*k+1]-pts[3*i+1];
+				uz=pts[3*k+2]-pts[3*i+2];
+				vx=pts[3*m]-pts[3*i];
+				vy=pts[3*m+1]-pts[3*i+1];
+				vz=pts[3*m+2]-pts[3*i+2];
+				wx=uy*vz-uz*vy;
+				wy=uz*vx-ux*vz;
+				wz=ux*vy-uy*vx;
+				area+=sqrt(wx*wx+wy*wy+wz*wz);
+				k=m;l=n;
+				m=ed[k][l];ed[k][l]=-1-m;
+			}
+			v.push_back(0.125*area);
+		}
+	}
+	reset_edges();
+}
+
+/** Several routines in the class that gather cell-based statistics internally
+ * track their progress by flipping edges to negative so that they know what
+ * parts of the cell have already been tested. This function resets them back
+ * to positive. When it is called, it assumes that every edge in the routine
+ * should have already been flipped to negative, and it bails out with an
+ * internal error if it encounters a positive edge. */
+inline void voronoicell_base::reset_edges() {
+	int i,j;
+	for(i=0;i<p;i++) for(j=0;j<nu[i];j++) {
+		if(ed[i][j]>=0) voro_fatal_error("Edge reset routine found a previously untested edge",VOROPP_INTERNAL_ERROR);
+		ed[i][j]=-1-ed[i][j];
+	}
+}
+
+/** Checks to see if a given vertex is inside, outside or within the test
+ * plane. If the point is far away from the test plane, the routine immediately
+ * returns whether it is inside or outside. If the routine is close the the
+ * plane and within the specified tolerance, then the special check_marginal()
+ * routine is called.
+ * \param[in] n the vertex to test.
+ * \param[out] ans the result of the scalar product used in evaluating the
+ *                 location of the point.
+ * \return -1 if the point is inside the plane, 1 if the point is outside the
+ *         plane, or 0 if the point is within the plane. */
+inline int voronoicell_base::m_test(int n,double &ans) {
+	double *pp=pts+n+(n<<1);
+	ans=*(pp++)*px;
+	ans+=*(pp++)*py;
+	ans+=*pp*pz-prsq;
+	if(ans<-tolerance2) {
+		return -1;
+	} else if(ans>tolerance2) {
+		return 1;
+	}
+	return check_marginal(n,ans);
+}
+
+/** Checks to see if a given vertex is inside, outside or within the test
+ * plane, for the case when the point has been detected to be very close to the
+ * plane. The routine ensures that the returned results are always consistent
+ * with previous tests, by keeping a table of any marginal results. The routine
+ * first sees if the vertex is in the table, and if it finds a previously
+ * computed result it uses that. Otherwise, it computes a result for this
+ * vertex and adds it the table.
+ * \param[in] n the vertex to test.
+ * \param[in] ans the result of the scalar product used in evaluating
+ *                the location of the point.
+ * \return -1 if the point is inside the plane, 1 if the point is outside the
+ *         plane, or 0 if the point is within the plane. */
+int voronoicell_base::check_marginal(int n,double &ans) {
+	int i;
+	for(i=0;i<n_marg;i+=2) if(marg[i]==n) return marg[i+1];
+	if(n_marg==current_marginal) {
+		current_marginal<<=1;
+		if(current_marginal>max_marginal)
+			voro_fatal_error("Marginal case buffer allocation exceeded absolute maximum",VOROPP_MEMORY_ERROR);
+#if VOROPP_VERBOSE >=2
+		fprintf(stderr,"Marginal cases buffer scaled up to %d\n",i);
+#endif
+		int *pmarg=new int[current_marginal];
+		for(int j=0;j<n_marg;j++) pmarg[j]=marg[j];
+		delete [] marg;
+		marg=pmarg;
+	}
+	marg[n_marg++]=n;
+	marg[n_marg++]=ans>tolerance?1:(ans<-tolerance?-1:0);
+	return marg[n_marg-1];
+}
+
+/** This initializes the class to be a rectangular box. It calls the base class
+ * initialization routine to set up the edge and vertex information, and then
+ * sets up the neighbor information, with initial faces being assigned ID
+ * numbers from -1 to -6.
+ * \param[in] (xmin,xmax) the minimum and maximum x coordinates.
+ * \param[in] (ymin,ymax) the minimum and maximum y coordinates.
+ * \param[in] (zmin,zmax) the minimum and maximum z coordinates. */
+void voronoicell_neighbor::init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax) {
+	init_base(xmin,xmax,ymin,ymax,zmin,zmax);
+	int *q=mne[3];
+	*q=-5;q[1]=-3;q[2]=-1;
+	q[3]=-5;q[4]=-2;q[5]=-3;
+	q[6]=-5;q[7]=-1;q[8]=-4;
+	q[9]=-5;q[10]=-4;q[11]=-2;
+	q[12]=-6;q[13]=-1;q[14]=-3;
+	q[15]=-6;q[16]=-3;q[17]=-2;
+	q[18]=-6;q[19]=-4;q[20]=-1;
+	q[21]=-6;q[22]=-2;q[23]=-4;
+	*ne=q;ne[1]=q+3;ne[2]=q+6;ne[3]=q+9;
+	ne[4]=q+12;ne[5]=q+15;ne[6]=q+18;ne[7]=q+21;
+}
+
+/** This routine checks to make sure the neighbor information of each face is
+ * consistent. */
+void voronoicell_neighbor::check_facets() {
+	int i,j,k,l,m,q;
+	for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+		k=ed[i][j];
+		if(k>=0) {
+			ed[i][j]=-1-k;
+			q=ne[i][j];
+			l=cycle_up(ed[i][nu[i]+j],k);
+			do {
+				m=ed[k][l];
+				ed[k][l]=-1-m;
+				if(ne[k][l]!=q) fprintf(stderr,"Facet error at (%d,%d)=%d, started from (%d,%d)=%d\n",k,l,ne[k][l],i,j,q);
+				l=cycle_up(ed[k][nu[k]+l],m);
+				k=m;
+			} while (k!=i);
+		}
+	}
+	reset_edges();
+}
+
+/** The class constructor allocates memory for storing neighbor information. */
+voronoicell_neighbor::voronoicell_neighbor() {
+	int i;
+	mne=new int*[current_vertex_order];
+	ne=new int*[current_vertices];
+	for(i=0;i<3;i++) mne[i]=new int[init_n_vertices*i];
+	mne[3]=new int[init_3_vertices*3];
+	for(i=4;i<current_vertex_order;i++) mne[i]=new int[init_n_vertices*i];
+}
+
+/** The class destructor frees the dynamically allocated memory for storing
+ * neighbor information. */
+voronoicell_neighbor::~voronoicell_neighbor() {
+	for(int i=current_vertex_order-1;i>=0;i--) if(mem[i]>0) delete [] mne[i];
+	delete [] mne;
+	delete [] ne;
+}
+
+/** Computes a vector list of neighbors. */
+void voronoicell_neighbor::neighbors(std::vector<int> &v) {
+	v.clear();
+	int i,j,k,l,m;
+	for(i=1;i<p;i++) for(j=0;j<nu[i];j++) {
+		k=ed[i][j];
+		if(k>=0) {
+			v.push_back(ne[i][j]);
+			ed[i][j]=-1-k;
+			l=cycle_up(ed[i][nu[i]+j],k);
+			do {
+				m=ed[k][l];
+				ed[k][l]=-1-m;
+				l=cycle_up(ed[k][nu[k]+l],m);
+				k=m;
+			} while (k!=i);
+		}
+	}
+	reset_edges();
+}
+
+// Explicit instantiation
+template bool voronoicell_base::nplane(voronoicell_neighbor&,double,double,double,double,int);
+template void voronoicell_base::check_memory_for_copy(voronoicell_neighbor&,voronoicell_base*);
+
+}
+
diff --git a/src/PTM/ptm_voronoi_cell.h b/src/PTM/ptm_voronoi_cell.h
new file mode 100644
index 0000000000..80a0501b3c
--- /dev/null
+++ b/src/PTM/ptm_voronoi_cell.h
@@ -0,0 +1,324 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author   : Chris H. Rycroft (LBL / UC Berkeley)
+// Email    : chr@alum.mit.edu
+// Date     : August 30th 2011
+//
+// Modified by PM Larsen for use in Polyhedral Template Matching
+
+/** \file cell.hh
+ * \brief Header file for the voronoicell and related classes. */
+
+#ifndef PTM_VOROPP_CELL_HH
+#define PTM_VOROPP_CELL_HH
+
+#include <vector>
+#include <cstdio>
+
+#include "ptm_voronoi_config.h"
+
+namespace voro {
+
+/** \brief A class representing a single Voronoi cell.
+ *
+ * This class represents a single Voronoi cell, as a collection of vertices
+ * that are connected by edges. The class contains routines for initializing
+ * the Voronoi cell to be simple shapes such as a box, tetrahedron, or octahedron.
+ * It the contains routines for recomputing the cell based on cutting it
+ * by a plane, which forms the key routine for the Voronoi cell computation.
+ * It contains numerous routine for computing statistics about the Voronoi cell,
+ * and it can output the cell in several formats.
+ *
+ * This class is not intended for direct use, but forms the base of the
+ * voronoicell and voronoicell_neighbor classes, which extend it based on
+ * whether neighboring particle ID information needs to be tracked. */
+class voronoicell_base {
+	public:
+		/** This holds the current size of the arrays ed and nu, which
+		 * hold the vertex information. If more vertices are created
+		 * than can fit in this array, then it is dynamically extended
+		 * using the add_memory_vertices routine. */
+		int current_vertices;
+		/** This holds the current maximum allowed order of a vertex,
+		 * which sets the size of the mem, mep, and mec arrays. If a
+		 * vertex is created with more vertices than this, the arrays
+		 * are dynamically extended using the add_memory_vorder routine.
+		 */
+		int current_vertex_order;
+		/** This sets the size of the main delete stack. */
+		int current_delete_size;
+		/** This sets the size of the auxiliary delete stack. */
+		int current_delete2_size;
+		/** This sets the total number of vertices in the current cell.
+		 */
+		int p;
+		/** This is the index of particular point in the cell, which is
+		 * used to start the tracing routines for plane intersection
+		 * and cutting. These routines will work starting from any
+		 * point, but it's often most efficient to start from the last
+		 * point considered, since in many cases, the cell construction
+		 * algorithm may consider many planes with similar vectors
+		 * concurrently. */
+		int up;
+		/** This is a two dimensional array that holds information
+		 * about the edge connections of the vertices that make up the
+		 * cell. The two dimensional array is not allocated in the
+		 * usual method. To account for the fact the different vertices
+		 * have different orders, and thus require different amounts of
+		 * storage, the elements of ed[i] point to one-dimensional
+		 * arrays in the mep[] array of different sizes.
+		 *
+		 * More specifically, if vertex i has order m, then ed[i]
+		 * points to a one-dimensional array in mep[m] that has 2*m+1
+		 * entries. The first m elements hold the neighboring edges, so
+		 * that the jth edge of vertex i is held in ed[i][j]. The next
+		 * m elements hold a table of relations which is redundant but
+		 * helps speed up the computation. It satisfies the relation
+		 * ed[ed[i][j]][ed[i][m+j]]=i. The final entry holds a back
+		 * pointer, so that ed[i+2*m]=i. The back pointers are used
+		 * when rearranging the memory. */
+		int **ed;
+		/** This array holds the order of the vertices in the Voronoi
+		 * cell. This array is dynamically allocated, with its current
+		 * size held by current_vertices. */
+		int *nu;
+		/** This in an array with size 3*current_vertices for holding
+		 * the positions of the vertices. */
+		double *pts;
+		voronoicell_base();
+		virtual ~voronoicell_base();
+		void init_base(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax);
+		void init_octahedron_base(double l);
+		void init_tetrahedron_base(double x0,double y0,double z0,double x1,double y1,double z1,double x2,double y2,double z2,double x3,double y3,double z3);
+		void translate(double x,double y,double z);
+		double volume();
+		double max_radius_squared();
+		double total_edge_distance();
+		double surface_area();
+		void centroid(double &cx,double &cy,double &cz);
+		int number_of_faces();
+		int number_of_edges();
+		void vertex_orders(std::vector<int> &v);
+		void vertices(std::vector<double> &v);
+		void vertices(double x,double y,double z,std::vector<double> &v);
+		void face_areas(std::vector<double> &v);
+		void face_orders(std::vector<int> &v);
+		void face_freq_table(std::vector<int> &v);
+		void face_vertices(std::vector<int> &v);
+		void face_perimeters(std::vector<double> &v);
+		void normals(std::vector<double> &v);
+		template<class vc_class>
+		bool nplane(vc_class &vc,double x,double y,double z,double rsq,int p_id);
+		bool plane_intersects(double x,double y,double z,double rsq);
+		bool plane_intersects_guess(double x,double y,double z,double rsq);
+		void construct_relations();
+		void check_relations();
+		void check_duplicates();
+		/** Returns a list of IDs of neighboring particles
+		 * corresponding to each face.
+		 * \param[out] v a reference to a vector in which to return the
+		 *               results. If no neighbor information is
+		 *               available, a blank vector is returned. */
+		virtual void neighbors(std::vector<int> &v) {v.clear();}
+		/** This a virtual function that is overridden by a routine to
+		 * print the neighboring particle IDs for a given vertex. By
+		 * default, when no neighbor information is available, the
+		 * routine does nothing.
+		 * \param[in] i the vertex to consider. */
+		/** This is a simple inline function for picking out the index
+		 * of the next edge counterclockwise at the current vertex.
+		 * \param[in] a the index of an edge of the current vertex.
+		 * \param[in] p the number of the vertex.
+		 * \return 0 if a=nu[p]-1, or a+1 otherwise. */
+		inline int cycle_up(int a,int p) {return a==nu[p]-1?0:a+1;}
+		/** This is a simple inline function for picking out the index
+		 * of the next edge clockwise from the current vertex.
+		 * \param[in] a the index of an edge of the current vertex.
+		 * \param[in] p the number of the vertex.
+		 * \return nu[p]-1 if a=0, or a-1 otherwise. */
+		inline int cycle_down(int a,int p) {return a==0?nu[p]-1:a-1;}
+	protected:
+		/** This a one dimensional array that holds the current sizes
+		 * of the memory allocations for them mep array.*/
+		int *mem;
+		/** This is a one dimensional array that holds the current
+		 * number of vertices of order p that are stored in the mep[p]
+		 * array. */
+		int *mec;
+		/** This is a two dimensional array for holding the information
+		 * about the edges of the Voronoi cell. mep[p] is a
+		 * one-dimensional array for holding the edge information about
+		 * all vertices of order p, with each vertex holding 2*p+1
+		 * integers of information. The total number of vertices held
+		 * on mep[p] is stored in mem[p]. If the space runs out, the
+		 * code allocates more using the add_memory() routine. */
+		int **mep;
+		inline void reset_edges();
+		template<class vc_class>
+		void check_memory_for_copy(vc_class &vc,voronoicell_base* vb);
+		void copy(voronoicell_base* vb);
+	private:
+		/** This is the delete stack, used to store the vertices which
+		 * are going to be deleted during the plane cutting procedure.
+		 */
+		int *ds,*stacke;
+		/** This is the auxiliary delete stack, which has size set by
+		 * current_delete2_size. */
+		int *ds2,*stacke2;
+		/** This stores the current memory allocation for the marginal
+		 * cases. */
+		int current_marginal;
+		/** This stores the total number of marginal points which are
+		 * currently in the buffer. */
+		int n_marg;
+		/** This array contains a list of the marginal points, and also
+		 * the outcomes of the marginal tests. */
+		int *marg;
+		/** The x coordinate of the normal vector to the test plane. */
+		double px;
+		/** The y coordinate of the normal vector to the test plane. */
+		double py;
+		/** The z coordinate of the normal vector to the test plane. */
+		double pz;
+		/** The magnitude of the normal vector to the test plane. */
+		double prsq;
+		template<class vc_class>
+		void add_memory(vc_class &vc,int i,int *stackp2);
+		template<class vc_class>
+		void add_memory_vertices(vc_class &vc);
+		template<class vc_class>
+		void add_memory_vorder(vc_class &vc);
+		void add_memory_ds(int *&stackp);
+		void add_memory_ds2(int *&stackp2);
+		template<class vc_class>
+		inline bool collapse_order1(vc_class &vc);
+		template<class vc_class>
+		inline bool collapse_order2(vc_class &vc);
+		template<class vc_class>
+		inline bool delete_connection(vc_class &vc,int j,int k,bool hand);
+		template<class vc_class>
+		inline bool search_for_outside_edge(vc_class &vc,int &up);
+		template<class vc_class>
+		inline void add_to_stack(vc_class &vc,int lp,int *&stackp2);
+		inline bool plane_intersects_track(double x,double y,double z,double rs,double g);
+		inline void normals_search(std::vector<double> &v,int i,int j,int k);
+		inline bool search_edge(int l,int &m,int &k);
+		inline int m_test(int n,double &ans);
+		int check_marginal(int n,double &ans);
+		friend class voronoicell;
+		friend class voronoicell_neighbor;
+};
+
+/** \brief Extension of the voronoicell_base class to represent a Voronoi cell
+ * with neighbor information.
+ *
+ * This class is an extension of the voronoicell_base class, in cases when the
+ * IDs of neighboring particles associated with each face of the Voronoi cell.
+ * It contains additional data structures mne and ne for storing this
+ * information. */
+class voronoicell_neighbor : public voronoicell_base {
+	public:
+		using voronoicell_base::nplane;
+		/** This two dimensional array holds the neighbor information
+		 * associated with each vertex. mne[p] is a one dimensional
+		 * array which holds all of the neighbor information for
+		 * vertices of order p. */
+		int **mne;
+		/** This is a two dimensional array that holds the neighbor
+		 * information associated with each vertex. ne[i] points to a
+		 * one-dimensional array in mne[nu[i]]. ne[i][j] holds the
+		 * neighbor information associated with the jth edge of vertex
+		 * i. It is set to the ID number of the plane that made the
+		 * face that is clockwise from the jth edge. */
+		int **ne;
+		voronoicell_neighbor();
+		~voronoicell_neighbor();
+		void operator=(voronoicell_neighbor &c);
+		/** Cuts the Voronoi cell by a particle whose center is at a
+		 * separation of (x,y,z) from the cell center. The value of rsq
+		 * should be initially set to \f$x^2+y^2+z^2\f$.
+		 * \param[in] (x,y,z) the normal vector to the plane.
+		 * \param[in] rsq the distance along this vector of the plane.
+		 * \param[in] p_id the plane ID (for neighbor tracking only).
+		 * \return False if the plane cut deleted the cell entirely,
+		 * true otherwise. */
+		inline bool nplane(double x,double y,double z,double rsq,int p_id) {
+			return nplane(*this,x,y,z,rsq,p_id);
+		}
+		/** This routine calculates the modulus squared of the vector
+		 * before passing it to the main nplane() routine with full
+		 * arguments.
+		 * \param[in] (x,y,z) the vector to cut the cell by.
+		 * \param[in] p_id the plane ID (for neighbor tracking only).
+		 * \return False if the plane cut deleted the cell entirely,
+		 *         true otherwise. */
+		inline bool nplane(double x,double y,double z,int p_id) {
+			double rsq=x*x+y*y+z*z;
+			return nplane(*this,x,y,z,rsq,p_id);
+		}
+		/** This version of the plane routine just makes up the plane
+		 * ID to be zero. It will only be referenced if neighbor
+		 * tracking is enabled.
+		 * \param[in] (x,y,z) the vector to cut the cell by.
+		 * \param[in] rsq the modulus squared of the vector.
+		 * \return False if the plane cut deleted the cell entirely,
+		 *         true otherwise. */
+		inline bool plane(double x,double y,double z,double rsq) {
+			return nplane(*this,x,y,z,rsq,0);
+		}
+		/** Cuts a Voronoi cell using the influence of a particle at
+		 * (x,y,z), first calculating the modulus squared of this
+		 * vector before passing it to the main nplane() routine. Zero
+		 * is supplied as the plane ID, which will be ignored unless
+		 * neighbor tracking is enabled.
+		 * \param[in] (x,y,z) the vector to cut the cell by.
+		 * \return False if the plane cut deleted the cell entirely,
+		 *         true otherwise. */
+		inline bool plane(double x,double y,double z) {
+			double rsq=x*x+y*y+z*z;
+			return nplane(*this,x,y,z,rsq,0);
+		}
+		void init(double xmin,double xmax,double ymin,double ymax,double zmin,double zmax);
+		void check_facets();
+		virtual void neighbors(std::vector<int> &v);
+
+	private:
+		int *paux1;
+		int *paux2;
+		inline void n_allocate(int i,int m) {mne[i]=new int[m*i];}
+		inline void n_add_memory_vertices(int i) {
+			int **pp=new int*[i];
+			for(int j=0;j<current_vertices;j++) pp[j]=ne[j];
+			delete [] ne;ne=pp;
+		}
+		inline void n_add_memory_vorder(int i) {
+			int **p2=new int*[i];
+			for(int j=0;j<current_vertex_order;j++) p2[j]=mne[j];
+			delete [] mne;mne=p2;
+		}
+		inline void n_set_pointer(int p,int n) {
+			ne[p]=mne[n]+n*mec[n];
+		}
+		inline void n_copy(int a,int b,int c,int d) {ne[a][b]=ne[c][d];}
+		inline void n_set(int a,int b,int c) {ne[a][b]=c;}
+		inline void n_set_aux1(int k) {paux1=mne[k]+k*mec[k];}
+		inline void n_copy_aux1(int a,int b) {paux1[b]=ne[a][b];}
+		inline void n_copy_aux1_shift(int a,int b) {paux1[b]=ne[a][b+1];}
+		inline void n_set_aux2_copy(int a,int b) {
+			paux2=mne[b]+b*mec[b];
+			for(int i=0;i<b;i++) ne[a][i]=paux2[i];
+		}
+		inline void n_copy_pointer(int a,int b) {ne[a]=ne[b];}
+		inline void n_set_to_aux1(int j) {ne[j]=paux1;}
+		inline void n_set_to_aux2(int j) {ne[j]=paux2;}
+		inline void n_allocate_aux1(int i) {paux1=new int[i*mem[i]];}
+		inline void n_switch_to_aux1(int i) {delete [] mne[i];mne[i]=paux1;}
+		inline void n_copy_to_aux1(int i,int m) {paux1[m]=mne[i][m];}
+		inline void n_set_to_aux1_offset(int k,int m) {ne[k]=paux1+m;}
+		friend class voronoicell_base;
+};
+
+}
+
+#endif
+
diff --git a/src/PTM/ptm_voronoi_config.h b/src/PTM/ptm_voronoi_config.h
new file mode 100644
index 0000000000..86257e60cc
--- /dev/null
+++ b/src/PTM/ptm_voronoi_config.h
@@ -0,0 +1,129 @@
+// Voro++, a 3D cell-based Voronoi library
+//
+// Author   : Chris H. Rycroft (LBL / UC Berkeley)
+// Email    : chr@alum.mit.edu
+// Date     : August 30th 2011
+//
+// Modified by PM Larsen for use in Polyhedral Template Matching
+
+/** \file config.hh
+ * \brief Master configuration file for setting various compile-time options. */
+
+#ifndef PTM_VOROPP_CONFIG_HH
+#define PTM_VOROPP_CONFIG_HH
+
+namespace voro {
+
+// These constants set the initial memory allocation for the Voronoi cell
+/** The initial memory allocation for the number of vertices. */
+const int init_vertices=256;
+/** The initial memory allocation for the maximum vertex order. */
+const int init_vertex_order=64;
+/** The initial memory allocation for the number of regular vertices of order
+ * 3. */
+const int init_3_vertices=256;
+/** The initial memory allocation for the number of vertices of higher order.
+ */
+const int init_n_vertices=8;
+/** The initial buffer size for marginal cases used by the suretest class. */
+const int init_marginal=64;
+/** The initial size for the delete stack. */
+const int init_delete_size=256;
+/** The initial size for the auxiliary delete stack. */
+const int init_delete2_size=256;
+/** The initial size for the wall pointer array. */
+const int init_wall_size=32;
+/** The default initial size for the ordering class. */
+const int init_ordering_size=4096;
+/** The initial size of the pre_container chunk index. */
+const int init_chunk_size=256;
+
+// If the initial memory is too small, the program dynamically allocates more.
+// However, if the limits below are reached, then the program bails out.
+/** The maximum memory allocation for the number of vertices. */
+const int max_vertices=16777216;
+/** The maximum memory allocation for the maximum vertex order. */
+const int max_vertex_order=2048;
+/** The maximum memory allocation for the any particular order of vertex. */
+const int max_n_vertices=16777216;
+/** The maximum buffer size for marginal cases used by the suretest class. */
+const int max_marginal=16777216;
+/** The maximum size for the delete stack. */
+const int max_delete_size=16777216;
+/** The maximum size for the auxiliary delete stack. */
+const int max_delete2_size=16777216;
+/** The maximum amount of particle memory allocated for a single region. */
+const int max_particle_memory=16777216;
+/** The maximum size for the wall pointer array. */
+const int max_wall_size=2048;
+/** The maximum size for the ordering class. */
+const int max_ordering_size=67108864;
+/** The maximum size for the pre_container chunk index. */
+const int max_chunk_size=65536;
+
+/** The chunk size in the pre_container classes. */
+const int pre_container_chunk_size=1024;
+
+#ifndef VOROPP_VERBOSE
+/** Voro++ can print a number of different status and debugging messages to
+ * notify the user of special behavior, and this macro sets the amount which
+ * are displayed. At level 0, no messages are printed. At level 1, messages
+ * about unusual cases during cell construction are printed, such as when the
+ * plane routine bails out due to floating point problems. At level 2, general
+ * messages about memory expansion are printed. At level 3, technical details
+ * about memory management are printed. */
+#define VOROPP_VERBOSE 0
+#endif
+
+/** If a point is within this distance of a cutting plane, then the code
+ * assumes that point exactly lies on the plane. */
+const double tolerance=1e-11;
+
+/** If a point is within this distance of a cutting plane, then the code stores
+ * whether this point is inside, outside, or exactly on the cutting plane in
+ * the marginal cases buffer, to prevent the test giving a different result on
+ * a subsequent evaluation due to floating point rounding errors. */
+const double tolerance2=2e-11;
+
+/** The square of the tolerance, used when deciding whether some squared
+ * quantities are large enough to be used. */
+const double tolerance_sq=tolerance*tolerance;
+
+/** A large number that is used in the computation. */
+const double large_number=1e30;
+
+/** A radius to use as a placeholder when no other information is available. */
+const double default_radius=0.5;
+
+/** The maximum number of shells of periodic images to test over. */
+const int max_unit_voro_shells=10;
+
+/** A guess for the optimal number of particles per block, used to set up the
+ * container grid. */
+const double optimal_particles=5.6;
+
+/** If this is set to 1, then the code reports any instances of particles being
+ * put outside of the container geometry. */
+#define VOROPP_REPORT_OUT_OF_BOUNDS 0
+
+/** Voro++ returns this status code if there is a file-related error, such as
+ * not being able to open file. */
+#define VOROPP_FILE_ERROR 1
+
+/** Voro++ returns this status code if there is a memory allocation error, if
+ * one of the safe memory limits is exceeded. */
+#define VOROPP_MEMORY_ERROR 2
+
+/** Voro++ returns this status code if there is any type of internal error, if
+ * it detects that representation of the Voronoi cell is inconsistent. This
+ * status code will generally indicate a bug, and the developer should be
+ * contacted. */
+#define VOROPP_INTERNAL_ERROR 3
+
+/** Voro++ returns this status code if it could not interpret the command line
+ * arguments passed to the command line utility. */
+#define VOROPP_CMD_LINE_ERROR 4
+
+}
+
+#endif
-- 
GitLab