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