/* Copyright (C) 2010 Atsushi Togo */
/* All rights reserved. */

/* This file is part of spglib. */

/* 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 spglib project 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. */

#include "spacegroup.h"

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cell.h"
#include "debug.h"
#include "delaunay.h"
#include "hall_symbol.h"
#include "mathfunc.h"
#include "niggli.h"
#include "pointgroup.h"
#include "primitive.h"
#include "spg_database.h"
#include "symmetry.h"

#define REDUCE_RATE 0.95
#define NUM_ATTEMPT 100
#define INT_PREC 0.1
#define ZERO_PREC 1e-10

static double change_of_basis_monocli[48][3][3] = {
    {{1, 0, 0}, /* b  first turn; two axes are flipped in second turn */
     {0, 1, 0},
     {0, 0, 1}},
    {{0, 0, 1}, /* b */
     {0, -1, 0},
     {1, 0, 0}},
    {{0, 0, 1}, /* a */
     {1, 0, 0},
     {0, 1, 0}},
    {{1, 0, 0}, /* c */
     {0, 0, 1},
     {0, -1, 0}},
    {{0, 1, 0}, /* c */
     {0, 0, 1},
     {1, 0, 0}},
    {{0, -1, 0}, /* a */
     {1, 0, 0},
     {0, 0, 1}},
    {{-1, 0, 1}, /* b */
     {0, 1, 0},
     {-1, 0, 0}},
    {{1, 0, -1}, /* b */
     {0, -1, 0},
     {0, 0, -1}},
    {{0, 1, -1}, /* a */
     {1, 0, 0},
     {0, 0, -1}},
    {{-1, -1, 0}, /* c */
     {0, 0, 1},
     {-1, 0, 0}},
    {{1, -1, 0}, /* c */
     {0, 0, 1},
     {0, -1, 0}},
    {{0, 1, 1}, /* a */
     {1, 0, 0},
     {0, 1, 0}},
    {{0, 0, -1}, /* b */
     {0, 1, 0},
     {1, 0, -1}},
    {{-1, 0, 0}, /* b */
     {0, -1, 0},
     {-1, 0, 1}},
    {{0, -1, 0}, /* a */
     {1, 0, 0},
     {0, -1, 1}},
    {{0, 1, 0}, /* c */
     {0, 0, 1},
     {1, 1, 0}},
    {{-1, 0, 0}, /* c */
     {0, 0, 1},
     {-1, 1, 0}},
    {{0, 0, -1}, /* a */
     {1, 0, 0},
     {0, -1, -1}},
    {{1, 0, 0}, /* b  two axes are flipped to look for non-acute axes */
     {0, -1, 0},
     {0, 0, -1}},
    {{0, 0, -1}, /* b */
     {0, 1, 0},
     {1, 0, 0}},
    {{0, 0, 1}, /* a */
     {-1, 0, 0},
     {0, -1, 0}},
    {{-1, 0, 0}, /* c */
     {0, 0, -1},
     {0, -1, 0}},
    {{0, 1, 0}, /* c */
     {0, 0, -1},
     {-1, 0, 0}},
    {{0, 1, 0}, /* a */
     {-1, 0, 0},
     {0, 0, 1}},
    {{-1, 0, -1}, /* b */
     {0, -1, 0},
     {-1, 0, 0}},
    {{1, 0, 1}, /* b */
     {0, 1, 0},
     {0, 0, 1}},
    {{0, -1, -1}, /* a */
     {-1, 0, 0},
     {0, 0, -1}},
    {{1, -1, 0}, /* c */
     {0, 0, -1},
     {1, 0, 0}},
    {{-1, -1, 0}, /* c */
     {0, 0, -1},
     {0, -1, 0}},
    {{0, -1, 1}, /* a */
     {-1, 0, 0},
     {0, -1, 0}},
    {{0, 0, 1}, /* b */
     {0, -1, 0},
     {1, 0, 1}},
    {{-1, 0, 0}, /* b */
     {0, 1, 0},
     {-1, 0, -1}},
    {{0, 1, 0}, /* a */
     {-1, 0, 0},
     {0, 1, 1}},
    {{0, 1, 0}, /* c */
     {0, 0, -1},
     {-1, 1, 0}},
    {{1, 0, 0}, /* c */
     {0, 0, -1},
     {1, 1, 0}},
    {{0, 0, -1}, /* a */
     {-1, 0, 0},
     {0, 1, -1}},
    {{1, 0, 0}, /*  c1  Monoclinic/Oblique layer group; first turn */
     {0, 1, 0},
     {0, 0, 1}},
    {{0, 1, 0}, /* -c1 */
     {1, 0, 0},
     {0, 0, -1}},
    {{0, -1, 0}, /*  c2 */
     {1, -1, 0},
     {0, 0, 1}},
    {{-1, 0, 0}, /* -c2 */
     {-1, 1, 0},
     {0, 0, -1}},
    {{-1, 1, 0}, /*  c3 */
     {-1, 0, 0},
     {0, 0, 1}},
    {{1, -1, 0}, /* -c3 */
     {0, -1, 0},
     {0, 0, -1}},
    {{1, 0, 0}, /* -c1  Monoclinic/Oblique layer group; second turn */
     {0, -1, 0},
     {0, 0, -1}},
    {{0, -1, 0}, /*  c1 */
     {1, 0, 0},
     {0, 0, 1}},
    {{0, 1, 0}, /* -c2 */
     {1, 1, 0},
     {0, 0, -1}},
    {{-1, 0, 0}, /*  c2 */
     {-1, -1, 0},
     {0, 0, 1}},
    {{-1, -1, 0}, /* -c3 */
     {-1, 0, 0},
     {0, 0, -1}},
    {{1, 1, 0}, /*  c3 */
     {0, 1, 0},
     {0, 0, 1}},
};

/* Change of basis for Monoclinic/Rectangular layer group */
static int index_monocli_rectang[8] = {0, 5, 7, 8, 18, 23, 25, 26};

static Centering change_of_centering_monocli[36] = {
    C_FACE, /* first turn */
    A_FACE, B_FACE, B_FACE, A_FACE, C_FACE, A_FACE,
    C_FACE, C_FACE, A_FACE, B_FACE, B_FACE, BODY,
    BODY,   BODY,   BODY,   BODY,   BODY,   C_FACE, /* second turn */
    A_FACE, B_FACE, B_FACE, A_FACE, C_FACE, A_FACE,
    C_FACE, C_FACE, A_FACE, B_FACE, B_FACE, BODY,
    BODY,   BODY,   BODY,   BODY,   BODY};

static int change_of_unique_axis_monocli[48] = {
    1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0,
    1, 1, 0, 2, 2, 0, 1, 1, 0, 2, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};

// Transformations to {abc, bca, cab, ba-c, a-cb, -cba}
static double change_of_basis_ortho[6][3][3] = {
    {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}},  {{0, 0, 1}, {1, 0, 0}, {0, 1, 0}},
    {{0, 1, 0}, {0, 0, 1}, {1, 0, 0}},  {{0, 1, 0}, {1, 0, 0}, {0, 0, -1}},
    {{1, 0, 0}, {0, 0, 1}, {0, -1, 0}}, {{0, 0, 1}, {0, 1, 0}, {-1, 0, 0}}};

// Changed centerings of C-centering of abc to {abc, bca, cab, ba-c, a-cb, -cba}
static Centering change_of_centering_ortho[6] = {C_FACE, B_FACE, A_FACE,
                                                 C_FACE, B_FACE, A_FACE};
// Index of c axis in the changed coordinates system.
static int change_of_unique_axis_ortho[6] = {2, 1, 0, 2, 1, 0};

/* n_l : the index of L(g) in N_\epsilon(g) of SPG No.16-74 */
/* See ITA: Affine normalizer or highest symmetry Euclidean normalizer */
/* Previous implementation below was not correct for 67, 68, 73, 74, */
/* Cmma, Ccca, Ibca, Imma */
/* 6 / ((Number of hall symbols of each spg-type) / x) */
/* where x=1 and x=2 with without and with centring. */
/* 6, 2, 2, 6, 2, */
/* 2, 6, 6, 6, 2, 1, 2, 1, 1, 1, */
/* 1, 2, 1, 2, 2, 1, 2, 1, 1, 1, */
/* 1, 2, 2, 2, 2, 1, 6, 6, 2, 2, */
/* 1, 1, 1, 1, 2, 2, 1, 2, 2, 1, */
/* 3, 1, 1, 1, 2, 2, 1, 1, 6, 6, */
/* 6, 2, 3, 1 */
static int num_axis_choices_ortho[59] = {
    6, 2, 2, 6, 2,                /* 16-20 */
    2, 6, 6, 6, 2, 1, 2, 1, 1, 1, /* 21-30 */
    1, 2, 1, 2, 2, 1, 2, 1, 1, 1, /* 31-40 */
    1, 2, 2, 2, 2, 1, 6, 6, 2, 2, /* 41-50 */
    1, 1, 1, 1, 2, 2, 1, 2, 2, 1, /* 51-60 */
    3, 1, 1, 1, 2, 2, 2, 2, 6, 6, /* 61-70 */
    6, 2, 6, 2};                  /* 71-74 */

/* Use n_l of spacegroup with c != a and c != b */
static int layer_num_axis_choices_ortho[30] = {
    2, 1,                         /* 19-20 */
    2, 2, 2, 1, 2, 2, 1, 1, 1, 1, /* 21-30 */
    1, 1, 1, 1, 1, 1, 2, 1, 2, 1, /* 31-40 */
    1, 1, 1, 2, 1, 2, 2, 2};      /* 41-48 */

/* hR_to_hP is the inverse of rhombo_obverse and used until the */
/* commit  e0398ba9. But now this is included in */
/* change_of_basis_rhombo in a way of */
/* dot(change_of_basis_rhombo_hex,hR_to_hP). */
/* static double hR_to_hP[3][3] = {{ 1, 0, 1 }, */
/*                                 {-1, 1, 1 }, */
/*                                 { 0,-1, 1 }}; */

static double change_of_basis_rhombo[6][3][3] = {
    {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}},    {{0, 0, 1}, {1, 0, 0}, {0, 1, 0}},
    {{0, 1, 0}, {0, 0, 1}, {1, 0, 0}},    {{0, 0, -1}, {0, -1, 0}, {-1, 0, 0}},
    {{0, -1, 0}, {-1, 0, 0}, {0, 0, -1}}, {{-1, 0, 0}, {0, 0, -1}, {0, -1, 0}}};

static double change_of_basis_rhombo_hex[6][3][3] = {
    {{1, 0, 1}, {-1, 1, 1}, {0, -1, 1}},
    {{0, -1, 1}, {1, 0, 1}, {-1, 1, 1}},
    {{-1, 1, 1}, {0, -1, 1}, {1, 0, 1}},
    {{0, 1, -1}, {1, -1, -1}, {-1, 0, -1}},
    {{1, -1, -1}, {-1, 0, -1}, {0, 1, -1}},
    {{-1, 0, -1}, {0, 1, -1}, {1, -1, -1}}};

static double change_of_basis_C4[8][3][3] = {
    {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}},    {{0, -1, 0}, {1, 0, 0}, {0, 0, 1}},
    {{-1, 0, 0}, {0, -1, 0}, {0, 0, 1}},  {{0, 1, 0}, {-1, 0, 0}, {0, 0, 1}},
    {{0, 1, 0}, {1, 0, 0}, {0, 0, -1}},   {{-1, 0, 0}, {0, 1, 0}, {0, 0, -1}},
    {{0, -1, 0}, {-1, 0, 0}, {0, 0, -1}}, {{1, 0, 0}, {0, -1, 0}, {0, 0, -1}}};

static double change_of_basis_C6[12][3][3] = {{{1, 0, 0}, /* 1 */
                                               {0, 1, 0},
                                               {0, 0, 1}},
                                              {{1, -1, 0}, /* 2 */
                                               {1, 0, 0},
                                               {0, 0, 1}},
                                              {{0, -1, 0}, /* 3 */
                                               {1, -1, 0},
                                               {0, 0, 1}},
                                              {{-1, 0, 0}, /* 4 */
                                               {0, -1, 0},
                                               {0, 0, 1}},
                                              {{-1, 1, 0}, /* 5 */
                                               {-1, 0, 0},
                                               {0, 0, 1}},
                                              {{0, 1, 0}, /* 6 */
                                               {-1, 1, 0},
                                               {0, 0, 1}},
                                              {{0, 1, 0}, /* 7 */
                                               {1, 0, 0},
                                               {0, 0, -1}},
                                              {{-1, 1, 0}, /* 8 */
                                               {0, 1, 0},
                                               {0, 0, -1}},
                                              {{-1, 0, 0}, /* 9 */
                                               {-1, 1, 0},
                                               {0, 0, -1}},
                                              {{0, -1, 0}, /* 10 */
                                               {-1, 0, 0},
                                               {0, 0, -1}},
                                              {{1, -1, 0}, /* 11 */
                                               {0, -1, 0},
                                               {0, 0, -1}},
                                              {{1, 0, 0}, /* 12 */
                                               {1, -1, 0},
                                               {0, 0, -1}}};

/* Removed after commit 67997654 */
/* static double change_of_basis_501[3][3] = {{ 0, 0, 1}, */
/*                                            { 0,-1, 0}, */
/*                                            { 1, 0, 0}}; */

static int spacegroup_to_hall_number[230] = {
    1,   2,   3,   6,   9,   18,  21,  30,  39,  57,  60,  63,  72,  81,  90,
    108, 109, 112, 115, 116, 119, 122, 123, 124, 125, 128, 134, 137, 143, 149,
    155, 161, 164, 170, 173, 176, 182, 185, 191, 197, 203, 209, 212, 215, 218,
    221, 227, 228, 230, 233, 239, 245, 251, 257, 263, 266, 269, 275, 278, 284,
    290, 292, 298, 304, 310, 313, 316, 322, 334, 335, 337, 338, 341, 343, 349,
    350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 361, 363, 364, 366, 367,
    368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382,
    383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397,
    398, 399, 400, 401, 402, 404, 406, 407, 408, 410, 412, 413, 414, 416, 418,
    419, 420, 422, 424, 425, 426, 428, 430, 431, 432, 433, 435, 436, 438, 439,
    440, 441, 442, 443, 444, 446, 447, 448, 449, 450, 452, 454, 455, 456, 457,
    458, 460, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474,
    475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489,
    490, 491, 492, 493, 494, 495, 497, 498, 500, 501, 502, 503, 504, 505, 506,
    507, 508, 509, 510, 511, 512, 513, 514, 515, 516, 517, 518, 520, 521, 523,
    524, 525, 527, 529, 530,
};

static int layer_group_to_hall_number[116] = {
    -1,   -2,   -3,   -4,   -5,   -8,   -9,   -12,  -14,  -16,  -18,  -20,
    -22,  -24,  -26,  -28,  -30,  -32,  -34,  -35,  -37,  -38,  -39,  -40,
    -42,  -43,  -44,  -46,  -48,  -50,  -52,  -54,  -56,  -58,  -60,  -62,
    -64,  -65,  -67,  -68,  -70,  -72,  -74,  -76,  -77,  -79,  -80,  -81,
    -82,  -83,  -84,  -85,  -87,  -88,  -89,  -90,  -91,  -92,  -93,  -94,
    -95,  -96,  -98,  -99,  -101, -102, -103, -104, -105, -106, -107, -108,
    -109, -110, -111, -112, -113, -114, -115, -116,
};

static double identity[3][3] = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
static double monocli_i2c[3][3] = {{1, 0, -1}, {0, 1, 0}, {1, 0, 0}};
static double monocli_a2c[3][3] = {{0, 0, 1}, {0, -1, 0}, {1, 0, 0}};
static double rhombo_obverse[3][3] = {{2. / 3, -1. / 3, -1. / 3},
                                      {1. / 3, 1. / 3, -2. / 3},
                                      {1. / 3, 1. / 3, 1. / 3}};
static double rhomb_reverse[3][3] = {{1. / 3, -2. / 3, 1. / 3},
                                     {2. / 3, -1. / 3, -1. / 3},
                                     {1. / 3, 1. / 3, 1. / 3}};
static double a2c[3][3] = {{0, 0, 1}, {1, 0, 0}, {0, 1, 0}};
static double b2c[3][3] = {{0, 1, 0}, {0, 0, 1}, {1, 0, 0}};

static double A_mat[3][3] = {
    {1, 0, 0}, {0, 1. / 2, -1. / 2}, {0, 1. / 2, 1. / 2}};
static double C_mat[3][3] = {
    {1. / 2, 1. / 2, 0}, {-1. / 2, 1. / 2, 0}, {0, 0, 1}};
static double R_mat[3][3] = {{2. / 3, -1. / 3, -1. / 3},
                             {1. / 3, 1. / 3, -2. / 3},
                             {1. / 3, 1. / 3, 1. / 3}};
static double I_mat[3][3] = {{-1. / 2, 1. / 2, 1. / 2},
                             {1. / 2, -1. / 2, 1. / 2},
                             {1. / 2, 1. / 2, -1. / 2}};
static double F_mat[3][3] = {
    {0, 1. / 2, 1. / 2}, {1. / 2, 0, 1. / 2}, {1. / 2, 1. / 2, 0}};

static Spacegroup *search_spacegroup_with_symmetry(
    Primitive const *primitive, int const candidates[],
    int const num_candidates, Symmetry const *symmetry, double const symprec,
    double const angle_tolerance);
static Spacegroup *get_spacegroup(int const hall_number,
                                  double const origin_shift[3],
                                  double const conv_lattice[3][3]);
static int iterative_search_hall_number(
    double origin_shift[3], double conv_lattice[3][3], int const candidates[],
    int const num_candidates, Primitive const *primitive,
    Symmetry const *symmetry, double const symprec,
    double const angle_tolerance);
static int change_basis_tricli(int tmat_int[3][3],
                               double const conv_lattice[3][3],
                               double const primitive_lattice[3][3],
                               double const symprec, int const aperiodic_axis);
static int change_basis_monocli(int tmat_int[3][3],
                                double const conv_lattice[3][3],
                                double const primitive_lattice[3][3],
                                double const symprec,
                                int const aperiodic_axis_prim);
static Symmetry *get_initial_conventional_symmetry(Centering const centering,
                                                   double const tmat[3][3],
                                                   Symmetry const *symmetry);
static int search_hall_number(double origin_shift[3], double conv_lattice[3][3],
                              int const candidates[], int const num_candidates,
                              Primitive const *primitive,
                              Symmetry const *symmetry, double const symprec);
static int match_hall_symbol_db(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    int const pointgroup_number, Holohedry const holohedry,
    Centering const centering, Symmetry const *symmetry, double const symprec);
static int match_hall_symbol_db_monocli(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    int const group_number, Centering const centering,
    Symmetry const *conv_symmetry, double const symprec);
static int match_hall_symbol_db_monocli_in_loop(
    double origin_shift[3], double conv_lattice[3][3], double norms_squared[2],
    int const change_of_basis_index, double const (*orig_lattice)[3],
    int const check_norms, int const hall_number, Centering const centering,
    Symmetry const *conv_symmetry, double const symprec);
static int match_hall_symbol_db_ortho(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    Centering const centering, Symmetry const *symmetry,
    int const num_free_axes, double const symprec);
static int match_hall_symbol_db_ortho_in_loop(
    double origin_shift[3], double lattice[3][3],
    double const (*orig_lattice)[3], int const axis_choice_index,
    int const hall_number, Centering const centering, Symmetry const *symmetry,
    int const num_free_axes, double const symprec);
static int match_hall_symbol_db_cubic(double origin_shift[3],
                                      double conv_lattice[3][3],
                                      double const (*orig_lattice)[3],
                                      int const hall_number,
                                      Centering const centering,
                                      Symmetry const *conv_symmetry,
                                      double const symprec);
static int match_hall_symbol_db_cubic_in_loop(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const i, int const hall_number,
    Centering const centering, Symmetry const *conv_symmetry,
    double const symprec);
static int match_hall_symbol_db_rhombo(double origin_shift[3],
                                       double conv_lattice[3][3],
                                       double const (*orig_lattice)[3],
                                       int const hall_number,
                                       Symmetry const *conv_symmetry,
                                       double const symprec);
static int match_hall_symbol_db_others(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    Centering const centering, Holohedry const holohedry,
    Symmetry const *conv_symmetry, double const symprec);
static int match_hall_symbol_db_change_of_basis_loop(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], double const (*change_of_basis)[3][3],
    int const num_change_of_basis, int const hall_number,
    Centering const centering, Symmetry const *conv_symmetry,
    double const symprec);
static Symmetry *get_conventional_symmetry(double const tmat[3][3],
                                           Centering const centering,
                                           Symmetry const *primitive_sym);
static Centering get_centering(double correction_mat[3][3],
                               int const tmat[3][3], Laue const laue);
static Centering get_base_center(int const tmat[3][3]);
static int get_centering_shifts(double shift[3][3], Centering const centering);
static int is_equivalent_lattice(double tmat[3][3], int const allow_flip,
                                 double const lattice[3][3],
                                 double const orig_lattice[3][3],
                                 double const symprec);

/* Return NULL if failed */
Spacegroup *spa_search_spacegroup(Primitive const *primitive,
                                  int const hall_number, double const symprec,
                                  double const angle_tolerance) {
    Spacegroup *spacegroup;
    Symmetry *symmetry;
    int candidate[1];

    debug_print("search_spacegroup (tolerance = %f):\n", symprec);

    symmetry = NULL;
    spacegroup = NULL;

    if ((symmetry = sym_get_operation(primitive->cell, symprec,
                                      angle_tolerance)) == NULL) {
        return NULL;
    }

    if (hall_number != 0) {
        candidate[0] = hall_number;
    }

    if (hall_number) {
        spacegroup = search_spacegroup_with_symmetry(
            primitive, candidate, 1, symmetry, symprec, angle_tolerance);
    } else if (primitive->cell->aperiodic_axis == -1) {
        spacegroup = search_spacegroup_with_symmetry(
            primitive, spacegroup_to_hall_number, 230, symmetry, symprec,
            angle_tolerance);
    } else {
        spacegroup = search_spacegroup_with_symmetry(
            primitive, layer_group_to_hall_number, 116, symmetry, symprec,
            angle_tolerance);
    };

    sym_free_symmetry(symmetry);
    symmetry = NULL;

    return spacegroup;
}

/* Return NULL if failed */
/* Assume symmetry is transformed in primitive cell. */
Spacegroup *spa_search_spacegroup_with_symmetry(Symmetry const *symmetry,
                                                double const prim_lat[3][3],
                                                double const symprec) {
    int i;
    Spacegroup *spacegroup;
    Primitive *primitive;

    spacegroup = NULL;

    if ((primitive = prm_alloc_primitive(1)) == NULL) {
        return 0;
    }
    if ((primitive->cell = cel_alloc_cell(1, NOSPIN)) == NULL) {
        return 0;
    }
    mat_copy_matrix_d3(primitive->cell->lattice, prim_lat);
    for (i = 0; i < 3; i++) {
        primitive->cell->position[0][i] = 0;
    }
    spacegroup = search_spacegroup_with_symmetry(
        primitive, spacegroup_to_hall_number, 230, symmetry, symprec, -1.0);
    prm_free_primitive(primitive);
    primitive = NULL;

    return spacegroup;
}

/* Return NULL if failed */
Cell *spa_transform_to_primitive(int *mapping_table, Cell const *cell,
                                 double const trans_mat[3][3],
                                 Centering const centering,
                                 double const symprec) {
    double tmat[3][3], tmat_inv[3][3], prim_lat[3][3];
    Cell *primitive;

    primitive = NULL;

    if (!mat_inverse_matrix_d3(tmat_inv, trans_mat, symprec)) {
        goto err;
    }

    switch (centering) {
        case PRIMITIVE:
            mat_copy_matrix_d3(tmat, tmat_inv);
            break;
        case A_FACE:
            mat_multiply_matrix_d3(tmat, tmat_inv, A_mat);
            break;
        case C_FACE:
            mat_multiply_matrix_d3(tmat, tmat_inv, C_mat);
            break;
        case FACE:
            mat_multiply_matrix_d3(tmat, tmat_inv, F_mat);
            break;
        case BODY:
            mat_multiply_matrix_d3(tmat, tmat_inv, I_mat);
            break;
        case R_CENTER:
            mat_multiply_matrix_d3(tmat, tmat_inv, R_mat);
            break;
        default:
            goto err;
    }

    mat_multiply_matrix_d3(prim_lat, cell->lattice, tmat);
    if ((primitive = cel_trim_cell(mapping_table, prim_lat, cell, symprec)) ==
        NULL) {
        warning_print("spglib: cel_trim_cell failed.\n");
    }

    return primitive;

err:
    return NULL;
}

/* Return NULL if failed */
Cell *spa_transform_from_primitive(Cell const *primitive,
                                   Centering const centering,
                                   double const symprec) {
    int multi, i, j, k, num_atom;
    int *mapping_table;
    double tmat[3][3], inv_tmat[3][3], shift[3][3];
    Cell *std_cell, *trimmed_cell;

    mapping_table = NULL;
    trimmed_cell = NULL;
    std_cell = NULL;

    switch (centering) {
        case PRIMITIVE:
            break;
        case A_FACE:
            mat_copy_matrix_d3(tmat, A_mat);
            mat_inverse_matrix_d3(inv_tmat, A_mat, 0);
            break;
        case C_FACE:
            mat_copy_matrix_d3(tmat, C_mat);
            mat_inverse_matrix_d3(inv_tmat, C_mat, 0);
            break;
        case FACE:
            mat_copy_matrix_d3(tmat, F_mat);
            mat_inverse_matrix_d3(inv_tmat, F_mat, 0);
            break;
        case BODY:
            mat_copy_matrix_d3(tmat, I_mat);
            mat_inverse_matrix_d3(inv_tmat, I_mat, 0);
            break;
        case R_CENTER:
            mat_copy_matrix_d3(tmat, R_mat);
            mat_inverse_matrix_d3(inv_tmat, R_mat, 0);
            break;
        default:
            goto ret;
    }

    multi = get_centering_shifts(shift, centering);

    if ((mapping_table =
             (int *)malloc(sizeof(int) * primitive->size * multi)) == NULL) {
        warning_memory("mapping_table");
        goto ret;
    }

    if ((std_cell = cel_alloc_cell(primitive->size * multi,
                                   primitive->tensor_rank)) == NULL) {
        free(mapping_table);
        mapping_table = NULL;
        goto ret;
    }

    mat_multiply_matrix_d3(std_cell->lattice, primitive->lattice, inv_tmat);

    num_atom = 0;
    for (i = 0; i < primitive->size; i++) {
        mat_multiply_matrix_vector_d3(std_cell->position[num_atom], tmat,
                                      primitive->position[i]);
        std_cell->types[num_atom] = primitive->types[i];
        num_atom++;
    }

    for (i = 0; i < multi - 1; i++) {
        for (j = 0; j < primitive->size; j++) {
            mat_copy_vector_d3(std_cell->position[num_atom],
                               std_cell->position[j]);
            for (k = 0; k < 3; k++) {
                std_cell->position[num_atom][k] += shift[i][k];
            }
            std_cell->types[num_atom] = std_cell->types[j];
            num_atom++;
        }
    }

    trimmed_cell =
        cel_trim_cell(mapping_table, std_cell->lattice, std_cell, symprec);
    cel_free_cell(std_cell);
    std_cell = NULL;
    free(mapping_table);
    mapping_table = NULL;

ret:
    return trimmed_cell;
}

void spa_copy_spacegroup(Spacegroup *dst, Spacegroup const *src) {
    dst->number = src->number;
    dst->hall_number = src->hall_number;
    dst->pointgroup_number = src->pointgroup_number;
    strcpy(dst->schoenflies, src->schoenflies);
    strcpy(dst->hall_symbol, src->hall_symbol);
    strcpy(dst->international, src->international);
    strcpy(dst->international_long, src->international_long);
    strcpy(dst->international_short, src->international_short);
    strcpy(dst->choice, src->choice);
    mat_copy_matrix_d3(dst->bravais_lattice, src->bravais_lattice);
    mat_copy_vector_d3(dst->origin_shift, src->origin_shift);
}

/* Return NULL if failed */
static Spacegroup *search_spacegroup_with_symmetry(
    Primitive const *primitive, int const candidates[],
    int const num_candidates, Symmetry const *symmetry, double const symprec,
    double const angle_tolerance) {
    int hall_number;
    double conv_lattice[3][3];
    double origin_shift[3];
    Spacegroup *spacegroup;
    PointSymmetry pointsym;

    debug_print("search_spacegroup (tolerance = %f):\n", symprec);

    spacegroup = NULL;
    origin_shift[0] = 0;
    origin_shift[1] = 0;
    origin_shift[2] = 0;

    pointsym = ptg_get_pointsymmetry(symmetry->rot, symmetry->size);
    if (pointsym.size < symmetry->size) {
        info_print("spglib: Point symmetry of primitive cell is broken.\n");
        return NULL;
    }

    hall_number = iterative_search_hall_number(
        origin_shift, conv_lattice, candidates, num_candidates, primitive,
        symmetry, symprec, angle_tolerance);
    if (hall_number == 0) {
        return NULL;
    }

    spacegroup = get_spacegroup(hall_number, origin_shift, conv_lattice);

    return spacegroup;
}

/* Return spacegroup.number = 0 if failed */
static Spacegroup *get_spacegroup(int const hall_number,
                                  double const origin_shift[3],
                                  double const conv_lattice[3][3]) {
    Spacegroup *spacegroup;
    SpacegroupType spacegroup_type;

    spacegroup = NULL;

    if ((spacegroup = (Spacegroup *)malloc(sizeof(Spacegroup))) == NULL) {
        warning_memory("spacegroup");
        return NULL;
    }

    if (-117 < hall_number && hall_number < 531 && hall_number != 0) {
        spacegroup_type = spgdb_get_spacegroup_type(hall_number);
        mat_copy_matrix_d3(spacegroup->bravais_lattice, conv_lattice);
        mat_copy_vector_d3(spacegroup->origin_shift, origin_shift);
        spacegroup->number = spacegroup_type.number;
        spacegroup->hall_number = hall_number;
        spacegroup->pointgroup_number = spacegroup_type.pointgroup_number;
        memcpy(spacegroup->schoenflies, spacegroup_type.schoenflies, 7);
        memcpy(spacegroup->hall_symbol, spacegroup_type.hall_symbol, 17);
        memcpy(spacegroup->international, spacegroup_type.international, 32);
        memcpy(spacegroup->international_long,
               spacegroup_type.international_full, 20);
        memcpy(spacegroup->international_short,
               spacegroup_type.international_short, 11);
        memcpy(spacegroup->choice, spacegroup_type.choice, 6);
    }

    return spacegroup;
}

/* Return 0 if failed */
static int iterative_search_hall_number(
    double origin_shift[3], double conv_lattice[3][3], int const candidates[],
    int const num_candidates, Primitive const *primitive,
    Symmetry const *symmetry, double const symprec,
    double const angle_tolerance) {
    int attempt, hall_number;
    double tolerance;
    Symmetry *sym_reduced;

    debug_print("iterative_search_hall_number:\n");

    hall_number = 0;
    sym_reduced = NULL;

    hall_number =
        search_hall_number(origin_shift, conv_lattice, candidates,
                           num_candidates, primitive, symmetry, symprec);

    if (hall_number != 0) {
        goto ret;
    }

    tolerance = symprec;
    for (attempt = 0; attempt < NUM_ATTEMPT; attempt++) {
        debug_print("spglib: Attempt %d tolerance = %e failed", attempt,
                    tolerance);

        tolerance *= REDUCE_RATE;
        sym_reduced = sym_reduce_operation(primitive->cell, symmetry, tolerance,
                                           angle_tolerance);
        if (sym_reduced) {
            hall_number = search_hall_number(origin_shift, conv_lattice,
                                             candidates, num_candidates,
                                             primitive, sym_reduced, symprec);
            sym_free_symmetry(sym_reduced);
            sym_reduced = NULL;
        }
        if (hall_number != 0) {
            break;
        }
    }

ret:
    return hall_number;
}

/* Return 0 if failed */
static int search_hall_number(double origin_shift[3], double conv_lattice[3][3],
                              int const candidates[], int const num_candidates,
                              Primitive const *primitive,
                              Symmetry const *symmetry, double const symprec) {
    int i, hall_number, aperiodic_axis;
    Centering centering;
    Pointgroup pointgroup;
    Symmetry *conv_symmetry;
    int tmat_int[3][3];
    double correction_mat[3][3], tmat[3][3], conv_lattice_tmp[3][3];

    debug_print("search_hall_number:\n");

    hall_number = 0;
    conv_symmetry = NULL;

    aperiodic_axis = primitive->cell->aperiodic_axis;

    /* primitive->cell->lattice is set right handed. */
    /* tmat_int is set right handed. */

    /* For rhombohedral system, basis vectors for hP */
    /* (hexagonal lattice basis) are provided by tmat_int, */
    /* but may be either obverse or reverse setting. */
    pointgroup = ptg_get_transformation_matrix(tmat_int, symmetry->rot,
                                               symmetry->size, aperiodic_axis);

    debug_print("initial transformation matrix\n");
    debug_print_matrix_i3(tmat_int);

    if (pointgroup.number == 0) {
        goto err;
    }

    /* For LAUE1 and LAUE2M, update tmat_int by making smallest lattice */
    if (pointgroup.laue == LAUE1 || pointgroup.laue == LAUE2M) {
        mat_multiply_matrix_di3(conv_lattice_tmp, primitive->cell->lattice,
                                tmat_int);

        if (pointgroup.laue == LAUE1) {
            if (!change_basis_tricli(tmat_int, conv_lattice_tmp,
                                     primitive->cell->lattice, symprec,
                                     aperiodic_axis)) {
                goto err;
            }
        }

        /* The angle between non-unique axes may be acute or non-acute. */
        if (pointgroup.laue == LAUE2M) {
            if (!change_basis_monocli(tmat_int, conv_lattice_tmp,
                                      primitive->cell->lattice, symprec,
                                      aperiodic_axis)) {
                goto err;
            }
        }
    }

    /* For rhombohedral system, correction matrix to hR */
    /* (a=b=c primitive lattice basis) is returned judging */
    /* either obverse or reverse setting. centering=R_CENTER. */
    if ((centering = get_centering(correction_mat, tmat_int,
                                   pointgroup.laue)) == CENTERING_ERROR) {
        goto err;
    }

    mat_multiply_matrix_id3(tmat, tmat_int, correction_mat);
    mat_multiply_matrix_d3(conv_lattice, primitive->cell->lattice, tmat);

    debug_print("transformation matrix\n");
    debug_print_matrix_d3(tmat);

    /* For rhombohedral system, symmetry for a=b=c primitive lattice */
    /* basis is returned although centering == R_CENTER. */
    if ((conv_symmetry = get_initial_conventional_symmetry(centering, tmat,
                                                           symmetry)) == NULL) {
        goto err;
    }

    for (i = 0; i < num_candidates; i++) {
        /* Check if hall_number is that of rhombohedral. */
        if (match_hall_symbol_db(
                origin_shift, conv_lattice, /* <-- modified only matched */
                primitive->orig_lattice, candidates[i], pointgroup.number,
                pointgroup.holohedry, centering, conv_symmetry, symprec)) {
            debug_print("origin shift\n");
            debug_print_vector_d3(origin_shift);

            hall_number = candidates[i];
            break;
        }
    }

    sym_free_symmetry(conv_symmetry);
    conv_symmetry = NULL;

    return hall_number;

err:
    return 0;
}

// Perform Niggli cell reduction for Triclinic. Return 0 if failed.
static int change_basis_tricli(int tmat_int[3][3],
                               double const conv_lattice[3][3],
                               double const primitive_lattice[3][3],
                               double const symprec, int const aperiodic_axis) {
    int i, j;
    double niggli_cell[9];
    double smallest_lattice[3][3], inv_lattice[3][3], tmat[3][3];

    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            niggli_cell[i * 3 + j] = conv_lattice[i][j];
        }
    }

    if (!niggli_reduce(niggli_cell, symprec * symprec, aperiodic_axis)) {
        return 0;
    }

    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            smallest_lattice[i][j] = niggli_cell[i * 3 + j];
        }
    }
    if (mat_get_determinant_d3(smallest_lattice) < 0) {
        for (i = 0; i < 3; i++) {
            for (j = 0; j < 3; j++) {
                smallest_lattice[i][j] = -smallest_lattice[i][j];
            }
        }
    }
    mat_inverse_matrix_d3(inv_lattice, primitive_lattice, 0);
    mat_multiply_matrix_d3(tmat, inv_lattice, smallest_lattice);
    mat_cast_matrix_3d_to_3i(tmat_int, tmat);

    return 1;
}

/* Monoclinic: choose shortest a, c lattice vectors (|a| < |c|) */
/* Return 0 if failed */
static int change_basis_monocli(int tmat_int[3][3],
                                double const conv_lattice[3][3],
                                double const primitive_lattice[3][3],
                                double const symprec,
                                int const aperiodic_axis_prim) {
    double smallest_lattice[3][3], inv_lattice[3][3], tmat[3][3];
    int i, aperiodic_axis_conv, unique_axis;

    /* Unique axis of 3D monoclinic crystal system is b */
    /* For layer with 2D op/oc lattice, unique axis is a */
    if (aperiodic_axis_prim == -1) {
        aperiodic_axis_conv = -1;
        unique_axis = 1;
    } else {
        for (i = 0; i < 3; i++) {
            if (tmat_int[aperiodic_axis_prim][i] != 0) {
                aperiodic_axis_conv = i;
            }
        }
        unique_axis = 0;
    }

    if (!del_layer_delaunay_reduce_2D(smallest_lattice, conv_lattice,
                                      unique_axis, aperiodic_axis_conv,
                                      symprec)) {
        return 0;
    }

    /* For layer with 2D mp lattice, move the unique axis to c */
    if (aperiodic_axis_conv == 0) {
        mat_multiply_matrix_d3(smallest_lattice, smallest_lattice,
                               change_of_basis_monocli[2]);
    }

    mat_inverse_matrix_d3(inv_lattice, primitive_lattice, 0);
    mat_multiply_matrix_d3(tmat, inv_lattice, smallest_lattice);
    mat_cast_matrix_3d_to_3i(tmat_int, tmat);
    return 1;
}

/* Return NULL if failed */
static Symmetry *get_initial_conventional_symmetry(Centering const centering,
                                                   double const tmat[3][3],
                                                   Symmetry const *symmetry) {
    Symmetry *conv_symmetry;

    debug_print("get_initial_conventional_symmetry\n");

    conv_symmetry = NULL;

    if (centering == R_CENTER) {
        /* hP for rhombohedral */
        conv_symmetry = get_conventional_symmetry(tmat, PRIMITIVE, symmetry);
    } else {
        conv_symmetry = get_conventional_symmetry(tmat, centering, symmetry);
    }

    return conv_symmetry;
}

/* Return 0 if failed */
static int match_hall_symbol_db(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    int const pointgroup_number, Holohedry const holohedry,
    Centering const centering, Symmetry const *symmetry, double const symprec) {
    int is_found, num_free_axes;
    SpacegroupType spacegroup_type;
    Symmetry *changed_symmetry;
    double changed_lattice[3][3], inv_lattice[3][3], tmat[3][3];

    changed_symmetry = NULL;

    spacegroup_type = spgdb_get_spacegroup_type(hall_number);

    if (pointgroup_number != spacegroup_type.pointgroup_number) {
        goto err;
    }

    switch (holohedry) {
        case MONOCLI:
            if (match_hall_symbol_db_monocli(
                    origin_shift, conv_lattice, orig_lattice, hall_number,
                    spacegroup_type.number, centering, symmetry, symprec)) {
                return 1;
            }
            break;

        case ORTHO:
            if (hall_number > 0) {
                num_free_axes =
                    num_axis_choices_ortho[spacegroup_type.number - 16];
            } else if (hall_number < 0) {
                num_free_axes =
                    layer_num_axis_choices_ortho[spacegroup_type.number - 19];
            }
            assert(num_free_axes > 0);

            /* Switching two axes */
            /* Two steps: */
            /*   1. Finding principal axis for the representative hall symbol */
            /*      of the specified hall symbol without checking basis vector
             */
            /*      lengths preference. */
            /*   2. Finding transformation matrix and origin shift for the */
            /*      specified hall symbol. */
            if (hall_number > 0 && num_free_axes == 2) {
                mat_copy_matrix_d3(changed_lattice, conv_lattice);
                if (!match_hall_symbol_db_ortho(
                        origin_shift, changed_lattice, orig_lattice,
                        spacegroup_to_hall_number[spacegroup_type.number - 1],
                        centering, symmetry, 0, symprec)) {
                    break;
                }
                mat_inverse_matrix_d3(inv_lattice, conv_lattice, 0);
                mat_multiply_matrix_d3(tmat, inv_lattice, changed_lattice);

                if ((changed_symmetry = get_conventional_symmetry(
                         tmat, PRIMITIVE, symmetry)) == NULL) {
                    goto err;
                }

                is_found = match_hall_symbol_db_ortho(
                    origin_shift, changed_lattice, orig_lattice, hall_number,
                    centering, changed_symmetry, 2, symprec);
                sym_free_symmetry(changed_symmetry);
                changed_symmetry = NULL;
                if (is_found) {
                    mat_copy_matrix_d3(conv_lattice, changed_lattice);
                    return 1;
                }
                break;
            }

            if (match_hall_symbol_db_ortho(origin_shift, conv_lattice,
                                           orig_lattice, hall_number, centering,
                                           symmetry, num_free_axes, symprec)) {
                return 1;
            }
            break;

        case CUBIC:
            if (match_hall_symbol_db_cubic(origin_shift, conv_lattice,
                                           orig_lattice, hall_number, centering,
                                           symmetry, symprec)) {
                return 1;
            }
            break;

        case TRIGO:
            if ((centering == R_CENTER) &&
                (hall_number == 433 || hall_number == 434 ||
                 hall_number == 436 || hall_number == 437 ||
                 hall_number == 444 || hall_number == 445 ||
                 hall_number == 450 || hall_number == 451 ||
                 hall_number == 452 || hall_number == 453 ||
                 hall_number == 458 || hall_number == 459 ||
                 hall_number == 460 || hall_number == 461)) {
                /* Rhombohedral. symmetry is for a=b=c basis. */
                if (match_hall_symbol_db_rhombo(origin_shift, conv_lattice,
                                                orig_lattice, hall_number,
                                                symmetry, symprec)) {
                    return 1;
                }
                break;
            }
            /* Do not break for other trigonal cases */
        default: /* HEXA, TETRA, TRICLI and rest of TRIGO */
            if (match_hall_symbol_db_others(
                    origin_shift, conv_lattice, orig_lattice, hall_number,
                    centering, holohedry, symmetry, symprec)) {
                return 1;
            }
            break;
    }

err:
    return 0;
}

/* Return 0 if failed */
static int match_hall_symbol_db_monocli(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    int const group_number, Centering const centering,
    Symmetry const *conv_symmetry, double const symprec) {
    int i, check_norms, i_shortest, is_found_any, j, num_change_of_basis;
    int is_found[36];
    double shortest_norm_sum, norm_sum;
    double norms_squared[36][2];
    double all_conv_lattices[36][3][3], all_origin_shifts[36][3];

    /* E. Parthe and L. M. Gelato */
    /* "The best unit cell for monoclinic structure..." (1983) */
    // Space group: 3: P2, 4: P2_1, 6: Pm, 10: P2/m, 11: P2_1/m
    // Layer group: 3: p112, 4: p11m, 6: p112/m
    if ((hall_number > 0 &&
         (group_number == 3 || group_number == 4 || group_number == 6 ||
          group_number == 10 || group_number == 11)) ||
        (hall_number < 0 &&
         (group_number == 3 || group_number == 4 || group_number == 6))) {
        /* |a| < |c| for unique axis b. (This is as written in the paper 1983.)
         */
        /* |a| < |b| for unique axis c. (This is spgilb definition.) */
        /* |b| < |c| for unique axis a. (This is spgilb definition.) */
        check_norms = 1;
    } else {
        check_norms = 0;
    }

    /* For layer group, 3-11: Monoclinic/Oblique, 12-33: Monoclinic/Rectangular
     */
    num_change_of_basis = 0;
    if (hall_number > 0) {
        num_change_of_basis = 36;
    } else if (-11 <= hall_number && hall_number <= -3) {
        num_change_of_basis = 12;
    } else if (-33 <= hall_number && hall_number <= -12) {
        num_change_of_basis = 8;
    }
    assert(num_change_of_basis > 0);

    for (i = 0; i < num_change_of_basis; i++) {
        mat_copy_matrix_d3(all_conv_lattices[i], conv_lattice);

        if (hall_number > 0) {
            j = i;
        } else if (-11 <= hall_number && hall_number <= -3) {
            // Monoclinic/oblique
            // Try change_of_basis_monocli[36:48]
            j = i + 36;
        } else if (-33 <= hall_number && hall_number <= -12) {
            // Monoclinic/rectangular
            j = index_monocli_rectang[i];
        }

        /* all_origin_shifts[i] is possibly overwritten. */
        /* is_found == 0: Not found */
        /* is_found == 1: Found. conv_lattice may not be similar to orig_lattice
         */
        /* is_found == 2: Found. conv_lattice is similar to orig_lattice */
        is_found[i] = match_hall_symbol_db_monocli_in_loop(
            all_origin_shifts[i], all_conv_lattices[i], norms_squared[i], j,
            orig_lattice, check_norms, hall_number, centering, conv_symmetry,
            symprec);
    }

    is_found_any = 0;
    for (i = 0; i < num_change_of_basis; i++) {
        if (is_found[i]) {
            i_shortest = i;
            shortest_norm_sum =
                sqrt(norms_squared[i][0]) + sqrt(norms_squared[i][1]);
            is_found_any = 1;
            break;
        }
    }

    if (!is_found_any) {
        goto err;
    }

    /* Find shortest non-unique axes lengths */
    for (i = 0; i < num_change_of_basis; i++) {
        if (is_found[i]) {
            norm_sum = sqrt(norms_squared[i][0]) + sqrt(norms_squared[i][1]);
            if (shortest_norm_sum > norm_sum) {
                shortest_norm_sum = norm_sum;
            }
        }
    }

    /* Prefers is_found == 2, i.e., similar to orig_lattice */
    i_shortest = -1;
    for (i = 0; i < num_change_of_basis; i++) {
        if (is_found[i]) {
            norm_sum = sqrt(norms_squared[i][0]) + sqrt(norms_squared[i][1]);
            if (mat_Dabs(norm_sum - shortest_norm_sum) < symprec) {
                if (is_found[i] == 2) {
                    i_shortest = i;
                    break;
                }
                if (i_shortest < 0) {
                    i_shortest = i;
                }
            }
        }
    }

    mat_copy_vector_d3(origin_shift, all_origin_shifts[i_shortest]);
    mat_copy_matrix_d3(conv_lattice, all_conv_lattices[i_shortest]);
    return 1;

err:
    return 0;
}

static int match_hall_symbol_db_monocli_in_loop(
    double origin_shift[3], double conv_lattice[3][3], double norms_squared[2],
    int const change_of_basis_index, double const (*orig_lattice)[3],
    int const check_norms, int const hall_number, Centering const centering,
    Symmetry const *conv_symmetry, double const symprec) {
    int j, k, l, is_found, retval, unique_axis;
    double vec[2][3];
    double tmat[3][3], change_of_basis[3][3];
    Centering changed_centering;
    Symmetry *changed_symmetry;

    /* centring type should be P or C */
    if (centering == C_FACE) {
        changed_centering = change_of_centering_monocli[change_of_basis_index];
    } else { /* suppose PRIMITIVE */
        changed_centering = centering;
    }

    mat_copy_matrix_d3(change_of_basis,
                       change_of_basis_monocli[change_of_basis_index]);
    mat_multiply_matrix_d3(conv_lattice, conv_lattice, change_of_basis);
    unique_axis = change_of_unique_axis_monocli[change_of_basis_index];

    /* Make non-acute and length preference */
    /* norms_squared[0] and norms_squared[1] are the norms of the two */
    /* non unique axes among a,b,c. */
    l = 0;
    for (j = 0; j < 3; j++) {
        if (j == unique_axis) {
            continue;
        }
        for (k = 0; k < 3; k++) {
            vec[l][k] = conv_lattice[k][j];
        }
        norms_squared[l] = mat_norm_squared_d3(vec[l]);
        l++;
    }

    /* discard if principal angle is acute. */
    if (vec[0][0] * vec[1][0] + vec[0][1] * vec[1][1] + vec[0][2] * vec[1][2] >
        ZERO_PREC) {
        goto cont;
    }

    /* Choose |a| < |b| < |c| among two non-principles axes */
    /* if there are freedom. */
    if (check_norms) {
        if (norms_squared[0] > norms_squared[1] + ZERO_PREC) {
            goto cont;
        }
    }

    /* When orig_lattice is given not NULL, try to find similar (a, b, c) */
    /* choices to the input (a, b, c) by flipping a, b, c axes. */
    /* Here flipping means a -> -a, and so on. */
    /* Note that flipped (a,b,c) that match those of input should not */
    /* change centring for monoclinic case. */
    retval = 1;
    if (orig_lattice != NULL) {
        if (mat_get_determinant_d3(orig_lattice) > symprec) {
            /* This (mode=1) effectively checks C2 rotation along unique axis.
             */
            if (is_equivalent_lattice(tmat, 1, conv_lattice, orig_lattice,
                                      symprec)) {
                if (tmat[(unique_axis + 1) % 3][(unique_axis + 1) % 3] *
                        tmat[(unique_axis + 2) % 3][(unique_axis + 2) % 3] >
                    ZERO_PREC) {
                    mat_multiply_matrix_d3(conv_lattice, conv_lattice, tmat);
                    mat_multiply_matrix_d3(change_of_basis, change_of_basis,
                                           tmat);
                    retval = 2;
                }
            }
        }
    }

    if ((changed_symmetry = get_conventional_symmetry(
             change_of_basis, PRIMITIVE, conv_symmetry)) == NULL) {
        goto cont;
    }

    is_found =
        hal_match_hall_symbol_db(origin_shift, conv_lattice, hall_number,
                                 changed_centering, changed_symmetry, symprec);
    sym_free_symmetry(changed_symmetry);
    changed_symmetry = NULL;

    if (is_found) {
        return retval;
    }

cont:
    return 0;
}

/* Return 0 if failed */
static int match_hall_symbol_db_ortho(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    Centering const centering, Symmetry const *conv_symmetry,
    int const num_free_axes, double const symprec) {
    int i, j;

    // For layer, try axis choices abc and ba-c, which preserve the aperiodic
    // axis.
    j = hall_number > 0 ? 1 : 3;

    /* Try to find the best (a, b, c) by flipping axes. */
    if (orig_lattice != NULL) {
        if (mat_get_determinant_d3(orig_lattice) > symprec) {
            for (i = 0; i < 6; i += j) {
                if (match_hall_symbol_db_ortho_in_loop(
                        origin_shift, conv_lattice, orig_lattice, i,
                        hall_number, centering, conv_symmetry, num_free_axes,
                        symprec)) {
                    return 1;
                }
            }
        }
    }

    /* If flipping didn't work, usual search is made. */
    for (i = 0; i < 6; i += j) {
        if (match_hall_symbol_db_ortho_in_loop(
                origin_shift, conv_lattice, NULL, i, hall_number, centering,
                conv_symmetry, num_free_axes, symprec)) {
            return 1;
        }
    }

    return 0;
}

// @param[out] origin_shift
// @param[out] conv_lattice
// @param[in] orig_lattice
// @param[in] axis_choice_index Index of axis choices, {abc, bca, cab, ba-c,
// a-cb, -cba}
// @param[in] hall_number
// @param[in] centering
// @param[in] symmetry
// @param[in] num_free_axes
// @param[in] symprec
static int match_hall_symbol_db_ortho_in_loop(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const axis_choice_index,
    int const hall_number, Centering const centering, Symmetry const *symmetry,
    int const num_free_axes, double const symprec) {
    int j, k, l, is_found;
    double vec[3], norms[3];
    Centering changed_centering;
    Symmetry *changed_symmetry;
    double changed_lattice[3][3], tmat[3][3], change_of_basis[3][3];

    changed_symmetry = NULL;

    if (centering == C_FACE) {
        changed_centering = change_of_centering_ortho[axis_choice_index];
    } else {
        changed_centering = centering;
    }

    mat_copy_matrix_d3(change_of_basis,
                       change_of_basis_ortho[axis_choice_index]);
    mat_multiply_matrix_d3(changed_lattice, conv_lattice, change_of_basis);

    /* When orig_lattice is given not NULL, try to find similar (a, b, c) */
    /* choices to the input (a, b, c) by flipping a, b, c axes. */
    /* Here flipping means a -> -a, and so on. */
    /* Note that flipping of axes doesn't change centering. */
    if (orig_lattice != NULL) {
        if (is_equivalent_lattice(tmat, 1, changed_lattice, orig_lattice,
                                  symprec)) {
            mat_multiply_matrix_d3(changed_lattice, changed_lattice, tmat);
            mat_multiply_matrix_d3(change_of_basis, change_of_basis, tmat);
        } else {
            goto cont; /* This is necessary to run through all */
                       /* change_of_basis_ortho. */
        }
    }

    // When num_free_axes=2, axis choices {abc, ba-c}, {cab, -cba}, and {bca,
    // a-cb} give the identical space groups, respectively.
    if (num_free_axes == 2) {
        l = 0;
        for (j = 0; j < 3; j++) {
            if (j == change_of_unique_axis_ortho[axis_choice_index]) {
                continue;
            }
            for (k = 0; k < 3; k++) {
                vec[k] = changed_lattice[k][j];
            }
            norms[l] = mat_norm_squared_d3(vec);
            l++;
        }
        if (norms[0] > norms[1] + ZERO_PREC) {
            goto cont;
        }
    }

    // When num_free_axes=3, axis choices {abc, bca, cab} and {ba-c, a-cb, -cba}
    // give the identical space groups, respectively.
    if (num_free_axes == 3) {
        for (j = 0; j < 3; j++) {
            for (k = 0; k < 3; k++) {
                vec[k] = changed_lattice[k][j];
            }
            norms[j] = mat_norm_squared_d3(vec);
        }
        if ((norms[0] > norms[1] + ZERO_PREC) ||
            (norms[0] > norms[2] + ZERO_PREC)) {
            goto cont;
        }
    }

    // When num_free_axes=6, each axis choice gives a different space group.
    if (num_free_axes == 6) {
        for (j = 0; j < 3; j++) {
            for (k = 0; k < 3; k++) {
                vec[k] = changed_lattice[k][j];
            }
            norms[j] = mat_norm_squared_d3(vec);
        }
        if ((norms[0] > norms[1] + ZERO_PREC) ||
            (norms[1] > norms[2] + ZERO_PREC)) {
            goto cont;
        }
    }

    if ((changed_symmetry = get_conventional_symmetry(
             change_of_basis, PRIMITIVE, symmetry)) == NULL) {
        goto cont;
    }

    is_found =
        hal_match_hall_symbol_db(origin_shift, changed_lattice, hall_number,
                                 changed_centering, changed_symmetry, symprec);
    sym_free_symmetry(changed_symmetry);
    changed_symmetry = NULL;
    if (is_found) {
        mat_copy_matrix_d3(conv_lattice, changed_lattice);
        return 1;
    }

cont:
    return 0;
}

static int match_hall_symbol_db_cubic(double origin_shift[3],
                                      double conv_lattice[3][3],
                                      double const (*orig_lattice)[3],
                                      int const hall_number,
                                      Centering const centering,
                                      Symmetry const *conv_symmetry,
                                      double const symprec) {
    int i;

    /* Special treatment for No. 205 (501) is included in this change of */
    /* basis. To see old code, commit hash 67997654 and change_of_basis_501. */
    if (orig_lattice != NULL) {
        if (mat_get_determinant_d3(orig_lattice) > symprec) {
            for (i = 0; i < 6; i++) {
                if (match_hall_symbol_db_cubic_in_loop(
                        origin_shift, conv_lattice, orig_lattice, i,
                        hall_number, centering, conv_symmetry, symprec)) {
                    return 1;
                }
            }
        }
    }

    for (i = 0; i < 6; i++) {
        if (match_hall_symbol_db_cubic_in_loop(origin_shift, conv_lattice, NULL,
                                               i, hall_number, centering,
                                               conv_symmetry, symprec)) {
            return 1;
        }
    }
    return 0;
}

static int match_hall_symbol_db_cubic_in_loop(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const i, int const hall_number,
    Centering const centering, Symmetry const *conv_symmetry,
    double const symprec) {
    int is_found;
    double changed_lattice[3][3], tmat[3][3], change_of_basis[3][3];
    Symmetry *changed_symmetry;

    changed_symmetry = NULL;

    mat_copy_matrix_d3(change_of_basis, change_of_basis_ortho[i]);
    mat_multiply_matrix_d3(changed_lattice, conv_lattice, change_of_basis);

    if (orig_lattice != NULL) {
        if (is_equivalent_lattice(tmat, 1, changed_lattice, orig_lattice,
                                  symprec)) {
            mat_multiply_matrix_d3(changed_lattice, changed_lattice, tmat);
            mat_multiply_matrix_d3(change_of_basis, change_of_basis, tmat);
        } else {
            goto cont; /* This is necessary to run through all */
                       /* change_of_basis_ortho. */
        }
    }

    if ((changed_symmetry = get_conventional_symmetry(
             change_of_basis, PRIMITIVE, conv_symmetry)) == NULL) {
        goto cont;
    }

    is_found =
        hal_match_hall_symbol_db(origin_shift, changed_lattice, hall_number,
                                 centering, changed_symmetry, symprec);

    sym_free_symmetry(changed_symmetry);
    changed_symmetry = NULL;

    if (is_found) {
        mat_copy_matrix_d3(conv_lattice, changed_lattice);
        return 1;
    }

cont:
    return 0;
}

static int match_hall_symbol_db_rhombo(double origin_shift[3],
                                       double conv_lattice[3][3],
                                       double const (*orig_lattice)[3],
                                       int const hall_number,
                                       Symmetry const *conv_symmetry,
                                       double const symprec) {
    int is_found;

    if (hall_number == 433 || hall_number == 436 || hall_number == 444 ||
        hall_number == 450 || hall_number == 452 || hall_number == 458 ||
        hall_number == 460) { /* Hexagonal lattice */
        is_found = match_hall_symbol_db_change_of_basis_loop(
            origin_shift, conv_lattice, orig_lattice,
            change_of_basis_rhombo_hex, 6, hall_number, R_CENTER, conv_symmetry,
            symprec);
        if (is_found) {
            /* mat_copy_matrix_d3(conv_lattice, changed_lattice); */
            return 1;
        } else {
            return 0;
        }
    } else { /* Rhombohedral (a=b=c) lattice */
        return match_hall_symbol_db_change_of_basis_loop(
            origin_shift, conv_lattice, orig_lattice, change_of_basis_rhombo, 6,
            hall_number, PRIMITIVE, conv_symmetry, symprec);
    }

    return 0;
}

/* HEXA, TETRA, TRICLI and TRIGO but not Rhombohedral a=b=c */
static int match_hall_symbol_db_others(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], int const hall_number,
    Centering const centering, Holohedry const holohedry,
    Symmetry const *conv_symmetry, double const symprec) {
    /* TRICLI: No check. */
    if (holohedry == TRICLI) {
        return hal_match_hall_symbol_db(origin_shift, conv_lattice, hall_number,
                                        centering, conv_symmetry, symprec);
    }

    /* TETRA: Check over 4 fold rotations with/without flipping c-axis */
    if (holohedry == TETRA) {
        return match_hall_symbol_db_change_of_basis_loop(
            origin_shift, conv_lattice, orig_lattice, change_of_basis_C4, 8,
            hall_number, centering, conv_symmetry, symprec);
    }

    /* HEXA and TRIGO but not Rhombohedral: */
    /*    Check over 6 fold rotations with/without flipping c-axis */
    return match_hall_symbol_db_change_of_basis_loop(
        origin_shift, conv_lattice, orig_lattice, change_of_basis_C6, 12,
        hall_number, centering, conv_symmetry, symprec);
}

static int match_hall_symbol_db_change_of_basis_loop(
    double origin_shift[3], double conv_lattice[3][3],
    double const (*orig_lattice)[3], double const (*change_of_basis)[3][3],
    int const num_change_of_basis, int const hall_number,
    Centering const centering, Symmetry const *conv_symmetry,
    double const symprec) {
    int i, is_found;
    double changed_lattice[3][3], tmat[3][3];
    Symmetry *changed_symmetry;
    Centering centering_for_symmetry;

    changed_symmetry = NULL;

    if (centering == R_CENTER) {
        centering_for_symmetry = R_CENTER;
    } else {
        centering_for_symmetry = PRIMITIVE;
    }

    /* Check of similarity of basis vectors to those of input */
    if (orig_lattice != NULL) {
        if (mat_get_determinant_d3(orig_lattice) > symprec) {
            for (i = 0; i < num_change_of_basis; i++) {
                if ((changed_symmetry = get_conventional_symmetry(
                         change_of_basis[i], centering_for_symmetry,
                         conv_symmetry)) == NULL) {
                    continue;
                }
                mat_multiply_matrix_d3(changed_lattice, conv_lattice,
                                       change_of_basis[i]);
                if (is_equivalent_lattice(tmat, 0, changed_lattice,
                                          orig_lattice, symprec)) {
                    /* Here R_CENTER means hP (hexagonal) setting. */
                    is_found = hal_match_hall_symbol_db(
                        origin_shift, changed_lattice, hall_number, centering,
                        changed_symmetry, symprec);
                } else {
                    is_found = 0;
                }
                sym_free_symmetry(changed_symmetry);
                changed_symmetry = NULL;
                if (is_found) {
                    mat_copy_matrix_d3(conv_lattice, changed_lattice);
                    return 1;
                }
            }
        }
    }

    /* No check of similarity of basis vectors to those of input */
    for (i = 0; i < num_change_of_basis; i++) {
        if ((changed_symmetry = get_conventional_symmetry(
                 change_of_basis[i], centering_for_symmetry, conv_symmetry)) ==
            NULL) {
            continue;
        }
        mat_multiply_matrix_d3(changed_lattice, conv_lattice,
                               change_of_basis[i]);
        is_found =
            hal_match_hall_symbol_db(origin_shift, changed_lattice, hall_number,
                                     centering, changed_symmetry, symprec);
        sym_free_symmetry(changed_symmetry);
        changed_symmetry = NULL;
        if (is_found) {
            mat_copy_matrix_d3(conv_lattice, changed_lattice);
            return 1;
        }
    }

    return 0;
}

/* Return NULL if failed */
static Symmetry *get_conventional_symmetry(double const tmat[3][3],
                                           Centering const centering,
                                           Symmetry const *primitive_sym) {
    int i, j, k, multi, size;
    double inv_tmat[3][3], shift[3][3];
    double symmetry_rot_d3[3][3], primitive_sym_rot_d3[3][3];
    Symmetry *symmetry;

    symmetry = NULL;

    size = primitive_sym->size;

    switch (centering) {
        case FACE:
            if ((symmetry = sym_alloc_symmetry(size * 4)) == NULL) {
                return NULL;
            }
            break;
        case R_CENTER:
            if ((symmetry = sym_alloc_symmetry(size * 3)) == NULL) {
                return NULL;
            }
            break;
        case BODY:
        case A_FACE:
        case B_FACE:
        case C_FACE:
            if ((symmetry = sym_alloc_symmetry(size * 2)) == NULL) {
                return NULL;
            }
            break;
        default:
            if ((symmetry = sym_alloc_symmetry(size)) == NULL) {
                return NULL;
            }
            break;
    }

    for (i = 0; i < size; i++) {
        mat_cast_matrix_3i_to_3d(primitive_sym_rot_d3, primitive_sym->rot[i]);

        /* C*S*C^-1: recover conventional cell symmetry operation */
        mat_get_similar_matrix_d3(symmetry_rot_d3, primitive_sym_rot_d3, tmat,
                                  0);
        mat_cast_matrix_3d_to_3i(symmetry->rot[i], symmetry_rot_d3);

        /* translation in conventional cell: C = B^-1*P */
        mat_inverse_matrix_d3(inv_tmat, tmat, 0);
        mat_multiply_matrix_vector_d3(symmetry->trans[i], inv_tmat,
                                      primitive_sym->trans[i]);
    }

    multi = 1;

    if (centering != PRIMITIVE) {
        multi = get_centering_shifts(shift, centering);
        for (i = 0; i < multi - 1; i++) {
            for (j = 0; j < size; j++) {
                mat_copy_matrix_i3(symmetry->rot[(i + 1) * size + j],
                                   symmetry->rot[j]);
                for (k = 0; k < 3; k++) {
                    symmetry->trans[(i + 1) * size + j][k] =
                        symmetry->trans[j][k] + shift[i][k];
                }
            }
        }
    }

    /* This is done in get_origin_shift
    for (i = 0; i < multi; i++) {
      for (j = 0; j < size; j++) {
        for (k = 0; k < 3; k++) {
          symmetry->trans[i * size + j][k] =
            mat_Dmod1(symmetry->trans[i * size + j][k]);
        }
      }
    }
    */

    return symmetry;
}

/* Return CENTERING_ERROR if failed */
static Centering get_centering(double correction_mat[3][3],
                               int const tmat[3][3], Laue const laue) {
    int det;
    double trans_corr_mat[3][3];
    Centering centering;

    mat_copy_matrix_d3(correction_mat, identity);
    det = abs(mat_get_determinant_i3(tmat));
    debug_print("laue class: %d\n", laue);
    debug_print("multiplicity: %d\n", det);

    switch (det) {
        case 1:
            centering = PRIMITIVE;
            break;

        case 2:
            centering = get_base_center(tmat);
            if (centering == A_FACE) {
                if (laue == LAUE2M) {
                    debug_print("Monocli A to C\n");
                    mat_copy_matrix_d3(correction_mat, monocli_a2c);
                } else {
                    mat_copy_matrix_d3(correction_mat, a2c);
                }
                centering = C_FACE;
            }
            if (centering == B_FACE) {
                mat_copy_matrix_d3(correction_mat, b2c);
                centering = C_FACE;
            }
            if (laue == LAUE2M && centering == BODY) {
                debug_print("Monocli I to C\n");
                mat_copy_matrix_d3(correction_mat, monocli_i2c);
                centering = C_FACE;
            }
            break;

        case 3:
            /* hP (a=b) but not hR (a=b=c) */
            centering = R_CENTER;
            mat_multiply_matrix_id3(trans_corr_mat, tmat, rhombo_obverse);
            if (mat_is_int_matrix(trans_corr_mat, INT_PREC)) {
                mat_copy_matrix_d3(correction_mat, rhombo_obverse);
                debug_print("R-center observe setting\n");
                debug_print_matrix_d3(trans_corr_mat);
            }
            mat_multiply_matrix_id3(trans_corr_mat, tmat, rhomb_reverse);
            if (mat_is_int_matrix(trans_corr_mat, INT_PREC)) {
                mat_copy_matrix_d3(correction_mat, rhomb_reverse);
                debug_print("R-center reverse setting\n");
                debug_print_matrix_d3(trans_corr_mat);
            }
            break;

        case 4:
            centering = FACE;
            break;

        default:
            centering = CENTERING_ERROR;
            break;
    }

    return centering;
}

static Centering get_base_center(int const tmat[3][3]) {
    int i;
    Centering centering = PRIMITIVE;

    debug_print("lat_get_base_center\n");

    /* C center */
    for (i = 0; i < 3; i++) {
        if (tmat[i][0] == 0 && tmat[i][1] == 0 && abs(tmat[i][2]) == 1) {
            centering = C_FACE;
            goto end;
        }
    }

    /* A center */
    for (i = 0; i < 3; i++) {
        if (abs(tmat[i][0]) == 1 && tmat[i][1] == 0 && tmat[i][2] == 0) {
            centering = A_FACE;
            goto end;
        }
    }

    /* B center */
    for (i = 0; i < 3; i++) {
        if (tmat[i][0] == 0 && abs(tmat[i][1]) == 1 && tmat[i][2] == 0) {
            centering = B_FACE;
            goto end;
        }
    }

    /* body center */
    if ((abs(tmat[0][0]) + abs(tmat[0][1]) + abs(tmat[0][2]) == 2) &&
        (abs(tmat[1][0]) + abs(tmat[1][1]) + abs(tmat[1][2]) == 2) &&
        (abs(tmat[2][0]) + abs(tmat[2][1]) + abs(tmat[2][2]) == 2)) {
        centering = BODY;
        goto end;
    }

    /* This should not happen. */
    warning_print("spglib: No centring was found.\n");
    return PRIMITIVE;

end:
    return centering;
}

static int get_centering_shifts(double shift[3][3], Centering const centering) {
    int i, j, multi;

    multi = 1;
    for (i = 0; i < 3; i++) {
        for (j = 0; j < 3; j++) {
            shift[i][j] = 0;
        }
    }

    if (centering != PRIMITIVE) {
        if (centering != FACE && centering != R_CENTER) {
            for (i = 0; i < 3; i++) {
                shift[0][i] = 0.5;
            } /* BODY */
            if (centering == A_FACE) {
                shift[0][0] = 0;
            }
            if (centering == B_FACE) {
                shift[0][1] = 0;
            }
            if (centering == C_FACE) {
                shift[0][2] = 0;
            }
            multi = 2;
        }

        if (centering == R_CENTER) {
            shift[0][0] = 2. / 3;
            shift[0][1] = 1. / 3;
            shift[0][2] = 1. / 3;
            shift[1][0] = 1. / 3;
            shift[1][1] = 2. / 3;
            shift[1][2] = 2. / 3;
            multi = 3;
        }

        if (centering == FACE) {
            shift[0][0] = 0;
            shift[0][1] = 0.5;
            shift[0][2] = 0.5;
            shift[1][0] = 0.5;
            shift[1][1] = 0;
            shift[1][2] = 0.5;
            shift[2][0] = 0.5;
            shift[2][1] = 0.5;
            shift[2][2] = 0;
            multi = 4;
        }
    }

    return multi;
}

// @brief Return 1 if `lattice` and `orig_lattice` is equivalent.
//        The equivalence is defined though `mode`.
// @param[out] tmat Isometric transformation s.t. orig_lattice = lattice * tmat
// if exists
// @param[in] mode
//            mode=0: Equivalent if `lattice` and `orig_lattice` are identical
//            as matrices. mode=1: Allow to flip axes when abs(P)=I but P!=I.
//            mode=2: Check equivalence from their metric tensors.
// @param[in] lattice
// @param[in] orig_lattice
// @param[in] symprec
static int is_equivalent_lattice(double tmat[3][3], int const mode,
                                 double const lattice[3][3],
                                 double const orig_lattice[3][3],
                                 double const symprec) {
    int i, j;
    double inv_lat[3][3], tmat_abs[3][3], metric[3][3], orig_metric[3][3];
    int tmat_int[3][3];

    if (mat_Dabs(mat_get_determinant_d3(lattice) -
                 mat_get_determinant_d3(orig_lattice)) > symprec) {
        goto fail;
    }

    if (!mat_inverse_matrix_d3(inv_lat, lattice, symprec)) {
        goto fail;
    }

    // orig_lattice == lattice @ tmat
    mat_multiply_matrix_d3(tmat, inv_lat, orig_lattice);

    switch (mode) {
        case 0: /* Check identity of all elements */
            if (mat_check_identity_matrix_d3(identity, tmat, symprec)) {
                return 1;
            }
            break;

        case 1: /* Check identity of all elements allowing axes flips */
            for (i = 0; i < 3; i++) {
                for (j = 0; j < 3; j++) {
                    tmat_abs[i][j] = mat_Dabs(tmat[i][j]);
                }
            }

            if (mat_check_identity_matrix_d3(identity, tmat_abs, symprec)) {
                return 1;
            }
            break;

        case 2: /* Check metric tensors */
            mat_cast_matrix_3d_to_3i(tmat_int, tmat);
            if (!mat_check_identity_matrix_id3(tmat_int, tmat, symprec)) {
                break;
            }
            if (mat_get_determinant_i3(tmat_int) != 1) {
                break;
            }
            mat_get_metric(orig_metric, orig_lattice);
            mat_get_metric(metric, lattice);
            if (mat_check_identity_matrix_d3(orig_metric, metric, symprec)) {
                return 1;
            }
            break;
    }

fail:
    return 0;
}
