/////////////////////////////////////////////////////////////////////////////
// This C file has been created automatically. Do not edit!!!
/////////////////////////////////////////////////////////////////////////////

/** @file involutions.c
  File ``involutions.c`` contains  functions for transforming
  involutions of the  subgroup \f$G_{x0}\f$ 
  (of structure \f$2^{1+24}.\mbox{Co}_1\f$)  of the monster.

  We try to transform such involutions to a standard form 
  via conjugation by elements of the monster group. 
*/


/*************************************************************************
** External references 
*************************************************************************/

/// @cond DO_NOT_DOCUMENT 
#include <string.h>
#include "mat24_functions.h"
#define MMGROUP_GENERATORS_INTERN
#include "mmgroup_generators.h"
#define CLIFFORD12_INTERN
#include "clifford12.h"
/// @endcond  


// %%EXPORT_KWD CLIFFORD12_API


// %%GEN ch
#ifdef __cplusplus
extern "C" {
#endif
// %%GEN c


//  %%GEN h
//  %%GEN c





/*************************************************************************
*** Support for debugging
*************************************************************************/
/// @cond DO_NOT_DOCUMENT 

// If ERROR_POOL is defined then function xsp2co1_involution_error_pool()
// can read data from an "error pool" that contains debug information
// for certain functions after calling them.
// #define ERROR_POOL

#ifdef ERROR_POOL
// Number of entries of type uit64_t in the error pool
#define LEN_ERROR_POOL 64
static uint64_t error_pool[LEN_ERROR_POOL];
#endif

/**
@brief Used for debugging only
*/
// %%EXPORT px
CLIFFORD12_API
uint32_t xsp2co1_involution_error_pool(uint64_t *dest, uint32_t length)
{
  #ifdef ERROR_POOL
    uint32_t i;
    if (length > LEN_ERROR_POOL) length = LEN_ERROR_POOL;
    for (i = 0; i < length; ++i) dest[i] = error_pool[i];   
    return length;
  #else
    return 0;
  #endif
}




/// @endcond 

/*************************************************************************
*** Auxiliary functions
*************************************************************************/

/// @cond DO_NOT_DOCUMENT 


/**
  @brief Return 0 if the square of a 24 times 24 matrix is zero

  Here ``m`` is a 24 times 24 bit matrix. The function returns 0 
  if the square of ``m`` is zero and a nonzero value otherwise.

*/
static inline uint64_t square_mat24_nonzero(uint64_t *m)
{
    uint_fast32_t i, j; 
    uint64_t mi, mo, result;
    result = 0;
    for (i = 0; i < 24; ++i) {
         mi = m[i]; mo = 0;
         for (j = 0; j < 24; ++j) {
             mo ^= (0 - ((mi >> j) & ONE)) & m[j];
         }
         result |= mo;
    }
    return result & 0xffffff;
}



/**
  @brief Return type of Leech lattice vector mod2

  Here ``v`` is a vector in the Leech lattice mod 2. The function
  returns the type of ``v`` modulo 2.

*/
static inline uint64_t leech_type_mod2(uint64_t v)
{
    uint64_t x = v;
    x &= (x >> 12);
    mat24_def_parity12(x);
    return x;
}

/// @endcond 





/*************************************************************************
*** Invariants of an invloution in G_x0 / Q_x0
*************************************************************************/



/**
   @brief Compute invariant spaces for an involution in \f$G_{x0}\f$

   Let \f$g\f$ be the element of the group \f$G_{x0}\f$ stored in
   the array given by parameter ``elem``. Let \f$\Lambda_2\f$ be
   the Leech lattice mod 2, with vectors in \f$\Lambda_2\f$ coded
   in **Leech lattice encoding** as usual. Conjugation by \f$g\f$
   is a linear operation on  \f$\Lambda_2\f$, since the vectors
   in \f$\Lambda_2\f$ correspond to the elements of the normal
   subgroup \f$Q_{x0}\f$ of structure \f$2^{1+24}\f$ (modulo the
   centre of \f$G_{x0}\f$). Let \f$A = A(g)\f$ be
   the \f$24 \times 24\f$ bit matrix that performs this operation
   on \f$\Lambda_2\f$ by right multiplication. Put \f$A_1 = A - 1\f$,
   and let \f$I_1\f$ be the image of matrix  \f$A_1\f$.

   In this function we require that the image of \f$g\f$ in the
   factor group  \f$\mbox{Co}_1\f$ of  \f$G_{x0}\f$ has order
   1 or 2; othereise the function fails. That condition is
   equivalent to \f$A^2 = 1\f$, and also to \f$A_1^2 = 0\f$. If
   this is the case then we have:

   \f$(\ker A_1)^\perp = I_1  \subset \ker A_1 = (I_1)^\perp\f$.

   Any element \f$v \in \ker A_1\f$ is invariant under \f$g\f$, and
   so the corresponding element in \f$Q_{x0}\f$ is invariant up to
   sign. The elements of \f$Q_{x0}\f$ invariant under \f$g\f$ (modulo
   the center of \f$Q_{x0}\f$) form a subspace \f$(\ker A_1)^+\f$
   of \f$\ker A_1\f$ of codimension \f$0\f$ or \f$1\f$.
   Let \f$(I_1)^+\f$ be the orthogonal complement
   of \f$(\ker A_1)^+\f$. Then \f$I_1\f$ has the same codimension
   in \f$(I_1)^+\f$. The purpose of this function is to compute
   a basis of the smaller of the two spaces \f$(I_1)^+\f$
   or \f$\ker A_1\f$.

   We compute an output matrix in the array ``invar`` and return the
   number ``k`` of rows of that matrix in case of success.
   We use the following column bits of the output matrix.
    
   23,...,0:   Basis vector \f$v_i\f$ of \f$I_1\f$ or \f$(I_1)^+\f$

   55,...,32:  Preimage (under \f$A_1\f$) of basis vector \f$v_i\f$,
               undefined if \f$v_i \notin I_1\f$
   
   27:         Here a nonzero bit in row 0 indicates an error.
   

   In bits 24,...,26  of the output matrix we return the following
   linear forms on the space spanned by basis vectors:

   Case 1:  \f$I_1 = (I_1)^+\f$ or \f$I_1 = \ker A_1 \f$

   Then we return a basis of \f$I_1\f$, and we 
   have \f$k = \dim I_1 = \dim (I_1)^+ \in \{0, 8, 12\}\f$
   
   Bit 26:  0

   Bit 25:  type of basis vector (modulo 2)

   Bit 24:  sign of basis vector in \f$I_1\f$

   Case 2:  \f$I_1 \neq (I_1)^+\f$ and  \f$I_1 \neq \ker A_1 \f$

   Then we return a basis of \f$(I_1)^+\f$, and we
   have \f$k - 1 = \dim (I_1)^+ - 1 = \dim I_1 \in \{0, 8\}\f$.

   Bit 26:  0 if and only if the basis vector  is in \f$I_1\f$

   Bit 25:  0

   Bit 24:  sign of the basis vector if the vector is in \f$I_1\f$,
            and type of the basis vector (mod 2) otherwise


   Parameter ``invar`` must be an array of length 12. Zero lines
   are appeded to that array so that its length will be 12.

   The function returns the dimension ``k`` of the computed basis,
   and a negative value in case of error. The return value
   ERR_QSTATE12_GX0_BAD_ELEM means that that the image of \f$g\f$
   in \f$\mbox{Co}_1\f$ has order greater than \f$2\f$.

   Bits 26,...,0  of the output matrix are echelonized in a special
   way.  Here the columns are processed in the order: 

   26, 25, 24, 11, 22, 21, ..., 13, 12, 10, 9, ..., 1, 0, 23.

   One of the advantages of this echelonization is that the
   vector \f$\Omega\f$ (encoded as 0x800000) will occur in the
   basis if it is in the subspace, and that there are many even
   vectors (i.e. vectors orthogonal to \f$\Omega\f$) in the basis.
   Also, bits 26, 25, 24 may be nonzero at most in the first two
   columns of the output matrix.
*/
// %%EXPORT px
CLIFFORD12_API
int32_t xsp2co1_involution_invariants(uint64_t *elem, uint64_t *invar)
{
    uint64_t data[40], *pa = data + 16;
    uint64_t t0, t1;
    uint_fast32_t i, n;
    int_fast32_t status;

    // Initialize output with zeros and an error bit in row 0
    invar[0] = 0x8000000ULL;
    for (i = 1; i < 12; ++i) invar[i] = 0;

    // Let `pa` be the 24 times 24 unit matrix 1
    for (i = 0; i < 24; ++i) pa[i] = ONE << i;

    // Conjugate row vectors of unit matrix pa with element
    // and store the the matrix A of the conjugated
    // row vectors in pa.
    status = xsp2co1_xspecial_conjugate(elem, 24, pa, 0);
    if (status < 0) return status;

    // Next we store two matrices PAH, PAL in pa, with PAL
    // in the lower 32 columns and PAH in the upper 32 columns
    // Put PAH = 1, PAL = A_1, where A_1 = A - 1 
    for (i = 0; i < 24; ++i) {
         pa[i] &=  0xffffffULL;
         pa[i] ^= 0x100000001ULL  << i;
    }

    // The group element `elem` is an involution (modulo the group  
    // Co_1) iff we have A_1**2 == 0. Otherwise we abort with an error.
    if (square_mat24_nonzero(pa)) return ERR_QSTATE12_GX0_BAD_ELEM; 

    // Echelonize PAL. So we left multiply both, PAH and PAL with a 
    // nonsingular matrix T auch that T * A_1 is echelonized. Then
    // PAH will contain T and PAL will contain  T * A_1.
    n = bitmatrix64_echelon_h(pa, 24, 24, 24);
    // Now the upper n rows of PAL contain the image I_1.
    // The upper n rows of PAH contain preimages of the rows of PAL.
    // The lower 24 - n rows of PAH contain the kernel \ker A_1.
    // The lower 24 - n rows of PAL are zero.

    #ifdef ERROR_POOL
    for (i = 0; i < 24; ++i) error_pool[i] = pa[i];
    error_pool[24] = n;
    for (i = 25; i < LEN_ERROR_POOL; ++i) error_pool[i] =0;
    #endif

    // Deal with the A_1 == 0, i.e. `elem` is in O_2(G_{x0}) or,
    // equivalently, `elem` is neutral modulo Co_1.
    if (n == 0) {
        invar[0]  = xsp2co1_xspecial_vector(elem) & 0xffffffULL;
        if (invar[0] == 0)  return 0;
        invar[0] |= (leech_type_mod2(invar[0]) << 24) | 0x4000000ULL;
        return 1;
    }
      
    if (n == 8) {
        // Deal with a 2A involution in Co_1

        // Move \ker A_1 to PAL and compute signs for \ker A_1
        bitmatrix64_rot_bits(pa + 8, 16, 32, 64, 0);
        status = xsp2co1_xspecial_conjugate(elem, 16, pa + 8, 1);
        if (status < 0) return status;

        // Make sure that at most the first row of \ker A_1
        // has negative sign
        bitmatrix64_echelon_h(pa + 8, 16, 25, 1);

        // Copy the image I_1 to output rows t1,...,t1+7, with
        // t1 = 0 if all entries of  \ker A_1 have positive sign
        // and t1 = 1 otherwise.
        t1 = (pa[8] >> 24) & 1;
        for (i = 0; i < 8; ++i) invar[t1 + i] = pa[i];

        #ifdef ERROR_POOL
        for (i = 0; i < 16; ++i) error_pool[i+25] = pa[i+8];
        #endif

        // Skip the following steps for n == 8 if t1 is 0.
        if (t1 == 0) goto final_echelonize;

        // Now the first row vector of the matrix PAL[8..23] 
        // representing \ker A_1 has negative sign and the 
        // other row vectors of that matrix have positive sign.


        // Comupute the orthogonal complement (I_1)^+  of the 
        // positive part (\ker A_1)^+ of \ker A. We also compute the
        // orthogonal complement of \ker A_1 (which is I_1) in
        // such a way that we can find an vector in (I_1)^+
        // that is not in I_1. 
        leech2_matrix_orthogonal(pa + 8, data, 16);
        // Now we have computed a basis V of the Leech lattice mod 2
        // (in the array ``data``) such that V[16],...V[23] spans
        // the orthogonal complement I_1 of \ker A_1. By
        // definition of function ``leech2_matrix_orthogonal`` 
        // the vector v0 = V[0] is orthognal to (\ker A_1)^+, 
        // but not to vector \ker A_1. 

        // Thus v0 is in  (I_1)^+ but not in I_1.
        // Copy the v0 to the output row 0.
        invar[0] = data[0] & 0xffffffULL;

        // Set bit 26 of output row 0 to 1.
        invar[0] |= 0x4000000ULL;

        // Set bit 24 of output row 0 to t0, with t0 = type(v0) (mod 2).
        t0 = leech_type_mod2(invar[0]);
        invar[0] |= t0 << 24;

        // Set bit 24 of the other output rows i = 1,...8 to t[i] with
        // t[i] = t0 + type(v0 + x[i])   (mod 2) .
        // Here x[i] is the output vector in row i.
        for (i = 1; i < 9; ++i) {
            t1 = t0 ^ leech_type_mod2(invar[0] ^ invar[i]);
            invar[i] |= t1 << 24; 
        }

        // Adjust number of output rows to 9.
        n = 9;
    } else if (n == 12) {
        // Deal with a 2B or 2C involution in Co_1

        // Copy the image the I_1 to the array ``data`` and 
        // store the signs of these images in bit 24.
        for (i = 0; i < 12; ++i) data[i] = pa[i];
        status = xsp2co1_xspecial_conjugate(elem, 16, data, 1);
        if (status < 0) return status;

        // Copy vectors pa[0,...,11] (containing I_1 and preimages
        // of these images) to output vector bits 23...0 and 55...32 
        // Copy signs of images to output vector bit 24
        // Copy type(output vector) modulo 2 to output vector bit 25.
        for (i = 0; i < 12; ++ i) {
             invar[i] = (pa[i] & 0xffffff00ffffffULL)
                     | (data[i] & 0x1000000ULL)
                     | (leech_type_mod2(pa[i]) << 25);
        }
    } else {
        // Report failure
        return ERR_QSTATE12_REP_GX0;
    }


final_echelonize:
    #ifdef ERROR_POOL
    for (i = 0; i < 12; ++i) error_pool[i+48] = invar[i];
    error_pool[48+12] = n;
    #endif
    // Echelonize the final result as documented above.
    bitmatrix64_xch_bits(invar, n, 12, 0x800);
    bitmatrix64_rot_bits(invar, n, 1, 12,0);
    bitmatrix64_echelon_h(invar, n, 27, 27);
    bitmatrix64_rot_bits(invar, n, 11, 12,0);
    bitmatrix64_xch_bits(invar, n, 12, 0x800);

    // Zero preimage in row 0 if bit 26 in row 0 is set.
    invar[0] &= ((invar[0] & 0x4000000ULL) << 2) - 1;
    return n;
    
}





/*************************************************************************
*** Application of invariants of an invloution in G_x0 / Q_x0
*************************************************************************/

/**
   @brief Compute some orthogonal complement for involution invariants

   Let \f$g\f$ be an element of the group \f$G_{x0}\f$ such
   that the image of \f$g\f$ in \f$\mbox{Co}_1\f$ has order 1
   or 2. For that element \f$g\f$, let \f$A, A_1\f$,
   and \f$I_1\f$ be as in function ``xsp2co1_involution_invariants``.

   In this function the input parameter ``invar`` must be equal to
   the  output ``invar`` of function ``xsp2co1_involution_invariants``
   applied to the element \f$g\f$.

   There is a nondegenerate bilinear
   form \f$ \langle \langle .,. \rangle \rangle\f$ on  \f$I_1\f$
   given by

   \f$\langle \langle x,y \rangle \rangle = \langle \pi(x),y \rangle\f$,

   where \f$\pi(x)\f$ is any preimage of \f$x\f$ under \f$A_1\f$,
   and \f$\langle .,. \rangle\f$ is the scalar product on the
   Leech lattice modulo 2. The
   form \f$\langle \langle .,. \rangle \rangle \f$ is also called
   the **Wall parametrization**, see [Wal63]. If  the image
   of \f$g\f$ in \f$\mbox{Co}_1\f$ has order at most two then
   the Wall parametrization is a symmetric bilinear form.

   The function computes the orthogonal complement \f$v\f$ of a
   linear form \f$l\f$ on the Leech lattice modulo 2 under the Wall
   parametrization. Then \f$v\f$ is a vector in the Leech lattice
   modulo two. If parameter ``col`` is 0 or 1 then we let
   \f$l\f$ be the linear form in column ``col + 25`` of
   matrix ``invar``.

   The function returns \f$v\f$ in case of success and a negative
   value in case of failure.

   [Wal63] G. E. Wall. On the conjugacy classes in the unitary,
   symplectic and orthogonal groups.
   J. Australian Math. Soc. 3, pp 1–63, 1963.
*/
// %%EXPORT px
CLIFFORD12_API
int32_t xsp2co1_involution_orthogonal(uint64_t *invar, uint32_t col)
{
   uint64_t M[12], T[24], v, *pA;
   int32_t n, i;

   // Select column `c` of input matrix a;
   // abort if ``col`` or ``invar`` is erroneous.
   if (col > 1) return ERR_QSTATE12_PARAM;
   if (invar[0] & 0x8000000) return ERR_QSTATE12_GX0_BAD_ELEM;
   col += 24;

   // Extract relevant rows of matrix a of invariants
   n = 12;
   while (n > 0 && invar[n-1] == 0) --n;
   pA = invar;
   if (pA[0] & 0x4000000) { 
       ++pA; --n;
   }
   if (n == 0) return 0;
   // Now the relevant rows of a are pA[j], 0 <= j < n;
   // Let A be the relevant part of the image \f$I_1\f$ in a
   // and P be the relevant part of the preimage of A
   // A is in columns 23,...,0 and P is in columns 55,...,32.

   // Store column `c` of input matrix a in bit vector v.
   // Return 0 if that column is zero
   v = 0;
   for (i = 0; i < n; ++i) v |= ((pA[i] >> col) & ONE) << i; 
   if (v == 0) return 0;

   // Put M = (A, P), with columns used as above.
   for (i = 0; i < n; ++i) M[i] = pA[i];
   bitmatrix64_rot_bits(M, n, 32, 64, 0);
   
   // Put T = P^T (P^T is the transposed of P)
   bitmatrix64_t(M, n, 24, T);

   // Put M = (?, A * Q), where Q is the scalar poduct in the Leech e
   // lattice  mod 2; i.e. exchange column i of A with column i + 12.
   bitmatrix64_rot_bits(M, n, 32, 64, 0); // M = (?, A)
   bitmatrix64_rot_bits(M, n, 12, 24, 0); // M = (?, A * Q)

   // Put M = A * Q * P^T
   bitmatrix64_mul(M, T, n, 24, M);

   // Put M = J, with J = ( A * Q * P^T) ** (-1);
   // abort if that inverse dose not exist.
   if (bitmatrix64_inv(M, n) < 0) return ERR_QSTATE12_REP_GX0;

   // Put v = c * J
   bitmatrix64_mul(&v, M, 1, n, &v);

   // Put v = c * J * A
   bitmatrix64_mul(&v, pA, 1, 24, &v);
   v &= 0xffffffULL;
   
   // Now v is in the space spanned by A and we have
   // v * Q * P^T = c. So v is the result
   return (int32_t) v;
}                




/*************************************************************************
*** Find type-4 vector in involution invariants
*************************************************************************/
/// @cond DO_NOT_DOCUMENT 


/** @brief  List all vectors in an affine space.

    Let ``a``  = \f$a_0,...,a_{k-1}\f$ be a matrix of bit vectors,
    and let \f$a_a\f$  be the bit vector stored in ``aa``. Let 

    \f$b_m = a_{a} + \sum_{i = 0}^{k-1} \cdot m_i a_{k-1-i}\f$,

    where \f$m_{k-1},\ldots,m_0\f$ is the binary representation
    of \f$m\f$.
    
    We write \f$b_m\f$ to ``b[m]`` for \f$0 \leq m < 2^k\f$.
*/
static inline void expand_affine(
    uint64_t *a, uint32_t k, uint64_t aa, uint32_t *b
)
{
    uint_fast32_t exp, i, j, v;
    b[0] = (uint32_t)(aa);
    exp = 1;
    for (i = 0; i < k; ++i) {
        v = (uint32_t)(a[k-i-1]);
        for (j = 0; j < exp; ++j) b[exp + j] = v ^ b[j];
        exp <<= 1;
    }
}
/// @endcond 



/// @cond DO_NOT_DOCUMENT 

/** Find a **nice** type-2 or type-4 vector in subset of Leech lattice mod 2

    Here ``a`` is a sequence of two concatenated arrays ``a0``
    and ``a1`` of elements of the Leech lattice mod 2
    of length ``n0`` and ``n1`` respectively. ``a0`` and ``a1``
    are input arrays.

    The function runs through all vectors ``a0[i0] + a1[i1]``
    with ``0 <= i0 < n0`` and ``0 <= i1 < n1``. Let ``V`` be
    the set of these vectors. All vectors in the set ``V`` must
    be of Leech lattice type 0, 2, or 4; otherwise the function
    computes garbage.

    The function returns a type-4 vector in ``V`` if such a vector 
    exists; otherwise it returns zero.

    The function tries to find a **nice** type-4 vector, provided
    that the input has been echelonized properly (as e.g. in
    function ``xsp2co1_involution_invariants``) and expanded
    properly as e.g. in function ``uint32_t expand_affine``.

    Parameter ``guide`` should usually be zero. If ``guide`` is a
    type-4 vector in the set ``V`` then the function
    returns ``guide``. Otherwise parameter ``guide`` is ignored.

    Array ``a`` is destroyed.
*/
static uint32_t 
sub_find_type(uint32_t *a, uint32_t n0, uint32_t n1, uint32_t guide)
{  
    uint32_t v;                   // The current vector being analysed
    uint32_t v_out = 0;           // 0 or a saved type-4 vector
    uint32_t i0, i1, n2 = n0 + n1;

    for (i0 = 0; i0 < n2; ++i0) a[i0] &= 0xffffffUL;
    if (gen_leech2_type(guide) == 4) {
        guide &= 0xffffffUL;
        for (i0 = 0; i0 < n0; ++i0) {
            v = a[i0] ^ guide;
            for (i1 = n0; i1 < n0 + n1; ++i1) {
                 if (v == a[i1]) return a[i0] ^ a[i1];
            }
        }
    }

    for (i0 = 0; i0 < n0; ++i0) {
        for (i1 = n0; i1 < n2; ++i1) {
            // let v be the vector in the Leech lattice mod 2
            // to be analysed. v must be of type 0, 2, or 4.
            v = a[i0] ^ a[i1];
            // Continue if v is not of the type that we are looking for.
            if (gen_leech2_type2(v) > 0) continue;
            
            // Here we look for a vector of type 4 ...
            
            // Deal with an odd cocode word.
            // Return the pre-stored even vector v_out of type 4 
            // if present and return v otherwise.
            if (v & 0x800UL) return v_out ? v_out : v;
                         
            // Deal with Golay code word 0 or Omega and even cocode word.
            if ((v & 0x7ff000UL) == 0) {
                if (v == 0) continue;  // Zero vector is not of type 4   
                return v;              // Otherwise return v
            }
            
            // Return v if its Golay code word is an octad.
            if (mat24_def_not_nonstrict_octad(v >> 12) == 0) return v;

            // Special case: Golay code word is a dodecad.
            // Here we'll wait for something better to come.
            // Save v in v_out if we have not yet done so.
            if (v_out == 0) v_out = v;
        }
    }
    // When searching for type 4 and a dodecad has been found then return
    // that dodecad. Otherwise return the number of type-2 vectors found.
    return v_out;
}

/// @endcond 



/**
   Find type-4 vector in a space computed by ``xsp2co1_involution_invariants``. 

   Let \f$g\f$ be the element of the group \f$G_{x0}\f$,
   and for that element \f$g\f$
   let \f$A, A_1, I_1\f$, and \f$(I_1)^+\f$
   be as in function ``xsp2co1_involution_invariants``. 

   Here input parameter ``invar`` must be the output ``invar`` 
   of function ``xsp2co1_involution_invariants`` applied to the
   element \f$g\f$. This function is successful in case
   \f$\dim I_1 = 8\f$ only.

   We return a type-4 vector the space \f$I_1\f$. If no such 
   vector exists then we return 0.

   Parameter ``guide`` should usually be zero. If ``guide`` is a
   type-4 vector in the Leech lattice mod 2 satisfying the
   assumptions the return value ``v``then the function
   returns ``v = guide``. Otherwise parameter ``guide`` is ignored.
*/
// %%EXPORT px
CLIFFORD12_API
int32_t xsp2co1_involution_find_type4(uint64_t *invar, uint32_t guide)
{
   uint64_t *pInv;
   uint32_t a[48], n, n0, n1, e0, e1;

   // Abort if ``invar`` is erroneous.
   if (invar[0] & 0x8000000UL) return -901;

   // Delete tailing zero rows of matrix ``invar``
   n = 12;
   while (n > 0 && (invar[n-1] & 0xffffffUL) == 0) --n;

   // Put pInv = invar, and delete leading rows of matrix ``pInv`` 
   // which are not in \f$I_1\f$. 
   pInv = invar;
   while (n > 0 && (pInv[0] & 0xf000000UL) != 0) { 
       ++pInv; --n;
   }
   // Abort if the dimension n of pInv is > 8.
   if (n > 8) return -1000 - n;
   // Now the relevant space V is generated py pInv[i], 0 <= i < 8.
 
   // Let V_1 =  V + coset * invar[0]. Store vectors in 
   // A0 = (a[0],..,a[15]) and A1 = (a[16],..,a[31]) such that
   // V_1 = {x0 + x1, x0 \in A0, x1 \in A1}. 
   n1 = n < 4 ? n : 4; n0 = n - n1;
   e0 = 1 << n0; e1 = 1 << n1;
   expand_affine(pInv, n0, 0, a);
   expand_affine(pInv + n0, n1, 0, a + e0);
 
   // If coset == 0 then return a type-4 vector in V_1.
   // Otherwise store the type-2 vectors in V_1 in the
   // array A2 =  (a[32],..,a[47]).
   return sub_find_type(a, e0, e1, guide); 
}


/// @cond DO_NOT_DOCUMENT 
/** @brief Find type-4 vector in space computed by ``xsp2co1_involution_invariants``. 

   Let \f$g\f$ be the element of the group \f$G_{x0}\f$,
   and for that element \f$g\f$
   let \f$A, A_1, I_1\f$, and \f$(I_1)^+\f$
   be as in function ``xsp2co1_involution_invariants``. 

   Here input parameter ``invar`` must be the output ``invar`` 
   of function ``xsp2co1_involution_invariants`` applied to the
   element \f$g\f$. This function is successful in case
   \f$\dim I_1 = 8, \dim I_1^+ = 9\f$ only.

   We return a type-4 vector in the space that can bw written as a sum
   of two type-2 vectors in \f$I_1^+ \setminus I_1\f$. If no such 
   vector exists then we return 0.

   Parameter ``guide`` is as in function ``sub_find_type``
*/
static int32_t xsp2co1_involution_find_coset8(uint64_t *invar, uint32_t guide)
{
   uint32_t a0[16], a1[16], a2[16], i0, i1, i2 = 0, v;

   // Abort if ``invar`` is erroneous.
   if (invar[0] & 0x8000000UL) return -901;
   if ((invar[0] & 0xf000000UL) == 0) return -951;
   if (invar[1] & 0xf000000UL) return -952;
   if (invar[9] & 0xffffffUL) return -953;

   expand_affine(invar + 1, 4, invar[0], a0);
   expand_affine(invar + 5, 4, 0, a1);
   for (i0 = 0; i0 < 16; ++i0)  {
       for (i1 = 0; i1 < 16; ++i1) {
           v = a0[i0] ^ a1[i1];
           if (gen_leech2_type2(v) && i2 < 16) a2[i2++] = v;
       }
   }
   i2 -= i2 > 0;
   return sub_find_type(a2, 1, i2, guide);
}




/// @endcond 



/** @brief Try to simplify an element in \f$G_{x0}\f$ via conjugation

   Let \f$g\f$ be the element of the group \f$G_{x0}\f$ stored
   in the array given by parameter ``elem``. In this function
   we require that the image of \f$g\f$ in the factor
   group \f$\mbox{Co}_1\f$ of  \f$G_{x0}\f$ has order 1 or 2;
   otherwise the function fails.

   Let \f$\Lambda_2\f$ be the Leech lattice mod 2, with vectors 
   in \f$\Lambda_2\f$ coded in **Leech lattice encoding** as 
   usual. Let \f$\Omega\f$ be the standard frame in \f$\Lambda_2\f$.

   Then the function tries to find a vector  \f$v \in \Lambda_2\f$
   with the following property:

   For any \f$h \in G_{x0}\f$ with \f$v \cdot h = \Omega\f$ we have
   \f$h^{-1} g h \in  N_{x0}\f$.

   The function returns \f$v\f$ in case of success and a negative
   value in case of an error. It returns ``ERR_QSTATE12_GX0_BAD_ELEM``
   if no suitable vector \f$v\f$ has been found.

   The function succeeds if the following two conditions hold:

    -  \f$g\f$ is in class 1A, 2A, 2B or 4A of the monster group

    -  \f$g^2\f$ is in subgroup \f$Q_{x0}\f$ of \f$G_{x0}\f$.

   In these two cases there is also a power \f$\tau^e\f$ of the
   triality element \f$\tau\f$  with

   \f$\tau^{-e} h^{-1} g h \tau^e \in  Q_{x0}\f$.

   Caution:

   The last statement has been checked for classes 1A, 2A and 2B only!


   Parameter ``guide`` should usually be zero. If ``guide`` is a
   type-4 vector in the Leech lattice mod 2 satisfying the
   assumptions the return value ``v``then the function
   returns ``v = guide``. Otherwise parameter ``guide`` is ignored.
   It is also ignored in case  \f$g \in Q_{x0}\f$.
*/
// %%EXPORT px
CLIFFORD12_API
int32_t xsp2co1_elem_find_type4(uint64_t *elem, uint32_t guide)
{
    uint64_t invar[12];   
    int_fast32_t v, t, n;

    // Nothing to do if \f$g \in Q_{x0}\f$:
    // The we return \f$\Omega\f$.
    if (xsp2co1_xspecial_vector(elem) >= 0) return 0x800000;

    // Compute involution invariants; abort if this fails.
    // Let \f$A, A_1, I_1, (I_1^+)\f$, and \f$(\ker A_1^+)\f$ 
    // be as in  function ``xsp2co1_involution_invariants``.
    n = xsp2co1_involution_invariants(elem, invar);
    if (n < 0) return n;
    // Now ``invar`` essentially contains a basis of one of the
    // spaces \f$A\f$ or \f$(I_1^+)\f$ of dimension n

    switch (n) {
        case 8:
            // Then \f$(I_1^+) = I_1\f$ has dimension 8. Here
            // any type-4 vector in \f$I_1\f$ will do.
            return xsp2co1_involution_find_type4(invar, guide);
        case 9:
            // Then the proper subspace \f$I_1\f$ of \f$(I_1^+)\f$ 
            // has dimension 8. Let \f$l\f$ be the linear form
            // on \f$I_1\f$ with \f$l(u) = 0\f$ 
            // iff \f$u \in (\ker A_1^+)\f$. Let \f$v \in I_1\f$
            // be the orthogonal complement of  \f$l\f$ under
            // Wall parametrization
            v = xsp2co1_involution_orthogonal(invar, 0);
            // Let t be the type the vector \f$v\f$ in the
            // Leech lattice modulo 2. Then t is 0, 2 or 4.
            t = gen_leech2_type(v);
            // In case t == 4 we simply return v.
            if (t == 4) return v;
            // In case t = 0 try to$A, A_1, I find a sum of two type-2
            // vectors in \f$(I_1^+) \setminus I_1\f$
            if (t == 0) {
                 v = xsp2co1_involution_find_coset8(invar, guide);
                 if (v > 0) return v;
            } 
            // If this fails (or we have t == 2) then return a
            // vector in \f$I_1\f$ orthogonal to  v.
            return xsp2co1_involution_find_type4(invar, guide);
        case 12:
            // Then \f$I_1\f$ has dimension 12, and the type
            // function on the Leech lattice mod 2 is a linear 
            // form \f$l\f$ on  \f$I_1\f$. If the orthogonal
            // complement \f$v\f$ of \f$l\f$ in \f$I_1\f$ 
            // (with respect to the Wall parametrization) has
            // type 4, we return f$v\f$; otherwise we fail.
            v = xsp2co1_involution_orthogonal(invar, 1);
            t = gen_leech2_type(v);
            if (t != 4) return  ERR_QSTATE12_GX0_BAD_ELEM; 
            return v;
        default:    
            // Any other case is illegal; so we fail.
            return ERR_QSTATE12_GX0_BAD_ELEM;
    }
}


/** @brief Try to map an element of \f$G_{x0}\f$ to \f$Q_{x0}\f$

   Let \f$g\f$ be the element of the group \f$G_{x0}\f$ stored in
   the array given by parameter ``elem``. The function tries to
   find an element \f$h\f$ in the monster group with
   \f$h^{-1} g h = q \in Q_{x0}\f$.

   The function succeeds if the following two conditions hold:

    -  \f$g\f$ is in class 1A, 2A, 2B or 4A of the monster group

    -  \f$g^2\f$ is in subgroup \f$Q_{x0}\f$ of \f$G_{x0}\f$.

   The function stores  \f$h\f$ in the output array ``a`` as a
   word of generators of the monster group. Array ``a``  must
   be of length 7 . The function returns \f$q\f$ in bits
   ``24,...,0`` of the return value, and number of atoms in
   the array ``a`` in bits ``27, 26, 25`` of the return value
   The data in the array ``a`` are padded with zeros.

   The function returns a negative value in case of failure.
   It returns ``ERR_QSTATE12_GX0_BAD_ELEM`` if no suitable
   element \f$h\f$ can be found.   
*/
// %%EXPORT px
CLIFFORD12_API
int32_t xsp2co1_elem_conj_G_x0_to_Q_x0(uint64_t *elem, uint32_t *a)
{
   uint64_t elem1[26];
   uint32_t e_N[5];
   int32_t v, len, res;

   // Let \f$\Lambda_2\f$ be the Leech lattice modulo 2.
   // find a vector \f$v_1 \in \Lambda_2\f$ with the following property:

   // For any \f$a_1 \in G_{x0}\f$ with \f$v_1 \cdot a_1 = \Omega\f$ we
   // have \f$a_1^{-1} g a_1 \in  N_{x0}\f$. Abort if no such \f$a_1\f$
   // exists.

   v = xsp2co1_elem_find_type4(elem, 0);
   if (v < 0) return v;

   // Compute \f$a_1\f$ from \f$v_1\f$ and store \f$a_1\f$ as a word of
   // generators of the monster of length ``len`` in the array ``a``. 
   len = gen_leech2_reduce_type4(v, a);
   if (len < 0) return ERR_QSTATE12_GX0_BAD_ELEM;

   // Put ``elem1`` = \f$g_1 = a_1^{-1} g a_1 \in  N_{x0}\f$.
   xsp2co1_copy_elem(elem, elem1);
   res = xsp2co1_conjugate_elem(elem1, a, len);
   if (res < 0) return res;

   // Copy \f$g_1\f$ from ``elem1`` (representing an element 
   // of \f$G_{x0}\f$) to ``e_N`` (representing an element 
   // of \f$N_{0}\f$). Abort if 
   // \f$g_1 \notin G_{x0} \cap N_{0} = G_{x0}\f$.
   res = xsp2co1_elem_to_N0(elem1, e_N);
   if (res < 0) return res - 10000;

   // Find a power \f$a_2 = \tau^e\f$ of the triality element \f$\tau\f$
   // such that \f$g_2 = a_2^{-1} g_1 a_2 \in  Q_{x0}\f$. Abort if this
   // fails.
   v = mm_group_n_conj_to_Q_x0(e_N); 
   if (v < 0) return ERR_QSTATE12_GX0_BAD_ELEM - 20000;
   // \f$a_2\f$ is in bits 24,...,0 of \f$v\f$ and \f$-e\f$ in bits 26,25.
   // Store \f$a_1 a_2\f$ in the array ``a`` and put \f$v = g_2\f$,
   // with   \f$g_2\f$ in Leech lattice encoding.
   if (v & 0x6000000UL)  a[len++] = 0xd0000000UL + ((v >> 25) & 3);

   // Return the pair (len, v) as documented above.
   v &= 0x1ffffff; 
   return v | ((len & 7) << 25);
}


/** 
   Map an involution in \f$G_{x0}\f$ to a standard form.

   Let \f$g\f$ be an involution of the group \f$G_{x0}\f$ stored in
   the array given by parameter ``elem``. 

   The function computes an element \f$a\f$ in the monster such
   that \f$h = a^{-1} g a\f$ is one of the following elements of 
   the subgroup \f$Q_{x0}\f$ of \f$G_{x0}\f$:

   If \f$g = 1\f$ then \f$h = a = 1\f$.

   If \f$g\f$ is a 2A involution then \f$h\f$ is the 
   involution in \f$Q_{x0}\f$ corresponding to the Golay cocode
   word with entries \f$2,3\f$ being set.

   If \f$g\f$ is a 2B involution then  \f$h\f$ is the central
   involution \f$z\f$ in \f$Q_{x0}\f$.

   The element \f$a\f$ is stored in the array ``a`` as a word of 
   generators of  the monster group. In case of success the 
   function returns  ``0x100 * I + len(a)``, where ``len(a)`` 
   is the length of the array ``a``. We put ``I = 0`` 
   if \f$g = 1\f$. We put ``I = 1, 2`` if \f$g\f$ is a 2A or
   2B involution, respectively.

   The function returns ``ERR_QSTATE12_GX0_BAD_ELEM`` if \f$g\f$
   is not an involution.

   The array ``a`` must have length at least \f$14\f$.
*/
// %%EXPORT px
CLIFFORD12_API
int32_t xsp2co1_elem_conjugate_involution(uint64_t *elem, uint32_t *a)
{
   int32_t v, len, l1;

   // Find a \f$g_1 \in Q_{x0}\f$  and an element \f$a_1\f$ in
   // the monster with \f$a_1^{-1} g a_1 = g_1\f$. Store  \f$g_1\f$
   // in ``v`` and \f$a_1\f$ in the buffer ``a`` as a word of generators
   // generators of the monster of length ``len``. Abort if this fails.
   v = xsp2co1_elem_conj_G_x0_to_Q_x0(elem, a);
   if (v < 0) return v;
   len = v >> 25; v &= 0x1ffffffUL; 

   // If v = 0 then we are done, returning class 1A
   if (v == 0) return 0;

   // If v is the central involution we are done, returning class 2B 
   if (v == 0x1000000UL) return 0x200 + len;

   // Now \f$g_1 \in Q_{x0}\f$ is stored in ``v`` in Leech lattice
   // encoding. We will be successful if the vector in \f$\Lambda_2\f$
   // corresponding to \f$g_2\f$ is of type 2 or 4.

   switch(gen_leech2_type(v)) {
       case 2:
           // Deal with a type-2 vector
           l1 = gen_leech2_reduce_type2(v, a + len);
           if (l1 < 0) return ERR_QSTATE12_LEECH_OP;
           v = gen_leech2_op_word(v, a + len, l1);
           len += l1;
           if (v & 0x1000000) {
               a[len] = 0xb0000200;  
                   // operation x_d such that d has odd scalar
                   // product with cocode word [2,3]
               v = gen_leech2_op_atom(v, a[len++]);
           }
           if (v != 0x200) return -0x4000000 + (v & 0x1ffffff);
                // internal error
           return 0x100 + len;
       case 4:
           // Deal with a type-4  vector later below
           break;
       default:
           // Any other vector type leads to failure
           return ERR_QSTATE12_GX0_BAD_ELEM;
   }

   // Here we deal with a type-4 vector, i.e. a 2B invulution

   // Compute \f$a_2 \in \G_{x0}\f$ from \f$g_1\f$ such that 
   // \f$a_2^{-1} g1 a_2 = \pm \Omega\f$, with 
   // \f$\Omega \in Q_{x0}\f$ the (positive) element corresponding
   // to the standard frame of the Leech lattice.
   l1 = gen_leech2_reduce_type4(v, a + len);
   if (l1 < 0) return ERR_QSTATE12_GX0_BAD_ELEM;

   // Next we compute \f$g_2 = a_2^{-1} g1 a_2\f$ in the vector ``v``.
   v = gen_leech2_op_word(v, a + len, l1);
   // Internal error if result differs from \f$ \pm \Omega\f$.
   if ((v & ~0x1000000UL) != 0x800000) return ERR_QSTATE12_LEECH_OP;

   // Append  \f$a_2\f$ (of length ``l1``) to the 
   // word \f$a_1\f$ in the array ``a`` (of length ``len``).
   len += l1; 

   // Finally, transform \f$g_3 = x_{\pm \Omega}\f$ to the central
   // element  \f$x_{-1}\f$. We have to conjugate \f$x_{\Omega}\f$ 
   // with \f$\tau\f$ and \f$x_{-\Omega}\f$ with \f$\tau^2\f$.
   // So we append the approprate power of  \f$\tau\f$ to ``a``.
   a[len++] = 0xd0000002 - ((v >> 24) & 1);

   // Return the length of the word in the array ``a``.
   return 0x200 + len;
}



//  %%GEN h
//  %%GEN c


// %%GEN ch
#ifdef __cplusplus
}
#endif
