text: /*
   *  This file is part of the optimized implementation of the Picnic signature scheme.
   *  See the accompanying documentation for complete details.
   *
   *  The code is provided under the MIT license, see LICENSE for
   *  more details.
   *  SPDX-License-Identifier: MIT
   */
  
  #include "compat.h"
  #include "mzd_additional.h"
  
  #if !defined(_MSC_VER)
  #include <stdalign.h>
  #endif
  #include <assert.h>
  #include <stdlib.h>
  #include <string.h>
  
  #if !defined(_MSC_VER) && !defined(static_assert)
  #define static_assert _Static_assert
  #endif
  
  static_assert(((sizeof(mzd_local_t) + 0x1f) & ~0x1f) == 32, "sizeof mzd_local_t not supported");
  
  /* implementation of copy */
  
  void mzd_copy_uint64_128(mzd_local_t* dst, mzd_local_t const* src) {
    const block_t* sblock = CONST_BLOCK(src, 0);
    block_t* dblock       = BLOCK(dst, 0);
  
    for (unsigned int i = 0; i < 2; ++i) {
      dblock->w64[i] = sblock->w64[i];
    }
  }
  
  void mzd_copy_uint64_192(mzd_local_t* dst, mzd_local_t const* src) {
    const block_t* sblock = CONST_BLOCK(src, 0);
    block_t* dblock       = BLOCK(dst, 0);
  
    for (unsigned int i = 0; i < 3; ++i) {
      dblock->w64[i] = sblock->w64[i];
    }
  }
  
  void mzd_copy_uint64_256(mzd_local_t* dst, mzd_local_t const* src) {
    const block_t* sblock = CONST_BLOCK(src, 0);
    block_t* dblock       = BLOCK(dst, 0);
  
    for (unsigned int i = 0; i < 4; ++i) {
      dblock->w64[i] = sblock->w64[i];
    }
  }
  
  /* implementation of mzd_xor and variants */
  
  static void mzd_xor_uint64_block(block_t* rblock, const block_t* fblock, const block_t* sblock,
                                   const unsigned int len) {
    for (unsigned int i = 0; i < len; ++i) {
      rblock->w64[i] = fblock->w64[i] ^ sblock->w64[i];
    }
  }
  
  void mzd_xor_uint64_128(mzd_local_t* res, mzd_local_t const* first, mzd_local_t const* second) {
    mzd_xor_uint64_block(BLOCK(res, 0), CONST_BLOCK(first, 0), CONST_BLOCK(second, 0), 2);
  }
  
  void mzd_xor_uint64_192(mzd_local_t* res, mzd_local_t const* first, mzd_local_t const* second) {
    mzd_xor_uint64_block(BLOCK(res, 0), CONST_BLOCK(first, 0), CONST_BLOCK(second, 0), 3);
  }
  
  void mzd_xor_uint64_256(mzd_local_t* res, mzd_local_t const* first, mzd_local_t const* second) {
    mzd_xor_uint64_block(BLOCK(res, 0), CONST_BLOCK(first, 0), CONST_BLOCK(second, 0), 4);
  }
  
  /* implementation of mzd_and_* and variants */
  
  static inline void mzd_and_uint64_block(block_t* rblock, const block_t* fblock,
                                          const block_t* sblock, const unsigned int len) {
    for (unsigned int i = 0; i < len; ++i) {
      rblock->w64[i] = fblock->w64[i] & sblock->w64[i];
    }
  }
  
  void mzd_and_uint64_128(mzd_local_t* res, mzd_local_t const* first, mzd_local_t const* second) {
    mzd_and_uint64_block(BLOCK(res, 0), CONST_BLOCK(first, 0), CONST_BLOCK(second, 0), 2);
  }
  
  void mzd_and_uint64_192(mzd_local_t* res, mzd_local_t const* first, mzd_local_t const* second) {
    mzd_and_uint64_block(BLOCK(res, 0), CONST_BLOCK(first, 0), CONST_BLOCK(second, 0), 3);
  }
  
  void mzd_and_uint64_256(mzd_local_t* res, mzd_local_t const* first, mzd_local_t const* second) {
    mzd_and_uint64_block(BLOCK(res, 0), CONST_BLOCK(first, 0), CONST_BLOCK(second, 0), 4);
  }
  
  /* shifts and rotations */
  
  void mzd_shift_left_uint64_128(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int right_count = 8 * sizeof(word) - count;
    const block_t* block           = CONST_BLOCK(val, 0);
    block_t* rblock                = BLOCK(res, 0);
  
    rblock->w64[1] = (block->w64[1] << count) | (block->w64[0] >> right_count);
    rblock->w64[0] = block->w64[0] << count;
  }
  
  void mzd_shift_right_uint64_128(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int left_count = 8 * sizeof(word) - count;
    const block_t* block          = CONST_BLOCK(val, 0);
    block_t* rblock               = BLOCK(res, 0);
  
    rblock->w64[0] = (block->w64[0] >> count) | (block->w64[1] << left_count);
    rblock->w64[1] = block->w64[1] >> count;
  }
  
  void mzd_shift_left_uint64_192(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int right_count = 8 * sizeof(word) - count;
    const block_t* block           = CONST_BLOCK(val, 0);
    block_t* rblock                = BLOCK(res, 0);
  
    rblock->w64[2] = (block->w64[2] << count) | (block->w64[1] >> right_count);
    rblock->w64[1] = (block->w64[1] << count) | (block->w64[0] >> right_count);
    rblock->w64[0] = block->w64[0] << count;
  }
  
  void mzd_shift_right_uint64_192(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int left_count = 8 * sizeof(word) - count;
    const block_t* block          = CONST_BLOCK(val, 0);
    block_t* rblock               = BLOCK(res, 0);
  
    rblock->w64[0] = (block->w64[0] >> count) | (block->w64[1] << left_count);
    rblock->w64[1] = (block->w64[1] >> count) | (block->w64[2] << left_count);
    rblock->w64[2] = block->w64[2] >> count;
  }
  
  void mzd_shift_left_uint64_256(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int right_count = 8 * sizeof(word) - count;
    const block_t* block           = CONST_BLOCK(val, 0);
    block_t* rblock                = BLOCK(res, 0);
  
    rblock->w64[3] = (block->w64[3] << count) | (block->w64[2] >> right_count);
    rblock->w64[2] = (block->w64[2] << count) | (block->w64[1] >> right_count);
    rblock->w64[1] = (block->w64[1] << count) | (block->w64[0] >> right_count);
    rblock->w64[0] = block->w64[0] << count;
  }
  
  void mzd_shift_right_uint64_256(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int left_count = 8 * sizeof(word) - count;
    const block_t* block          = CONST_BLOCK(val, 0);
    block_t* rblock               = BLOCK(res, 0);
  
    rblock->w64[0] = (block->w64[0] >> count) | (block->w64[1] << left_count);
    rblock->w64[1] = (block->w64[1] >> count) | (block->w64[2] << left_count);
    rblock->w64[2] = (block->w64[2] >> count) | (block->w64[3] << left_count);
    rblock->w64[3] = block->w64[3] >> count;
  }
  
  #if defined(PICNIC_STATIC)
  void mzd_rotate_left_uint64_128(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int right_count = 8 * sizeof(word) - count;
    const block_t* block           = CONST_BLOCK(val, 0);
    block_t* rblock                = BLOCK(res, 0);
  
    const word tmp = block->w64[1] >> right_count;
    rblock->w64[1] = (block->w64[1] << count) | (block->w64[0] >> right_count);
    rblock->w64[0] = (block->w64[0] << count) | tmp;
  }
  
  void mzd_rotate_right_uint64_128(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int left_count = 8 * sizeof(word) - count;
    const block_t* block          = CONST_BLOCK(val, 0);
    block_t* rblock               = BLOCK(res, 0);
  
    const word tmp = block->w64[0] << left_count;
    rblock->w64[0] = (block->w64[0] >> count) | (block->w64[1] << left_count);
    rblock->w64[1] = (block->w64[1] >> count) | tmp;
  }
  
  void mzd_rotate_left_uint64_192(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int right_count = 8 * sizeof(word) - count;
    const block_t* block           = CONST_BLOCK(val, 0);
    block_t* rblock                = BLOCK(res, 0);
  
    const word tmp = block->w64[2] >> right_count;
    rblock->w64[2] = (block->w64[2] << count) | (block->w64[1] >> right_count);
    rblock->w64[1] = (block->w64[1] << count) | (block->w64[0] >> right_count);
    rblock->w64[0] = (block->w64[0] << count) | tmp;
  }
  
  void mzd_rotate_right_uint64_192(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int left_count = 8 * sizeof(word) - count;
    const block_t* block          = CONST_BLOCK(val, 0);
    block_t* rblock               = BLOCK(res, 0);
  
    const word tmp = block->w64[0] << left_count;
    rblock->w64[0] = (block->w64[0] >> count) | (block->w64[1] << left_count);
    rblock->w64[1] = (block->w64[1] >> count) | (block->w64[2] << left_count);
    rblock->w64[2] = (block->w64[2] >> count) | tmp;
  }
  
  void mzd_rotate_left_uint64_256(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int right_count = 8 * sizeof(word) - count;
    const block_t* block           = CONST_BLOCK(val, 0);
    block_t* rblock                = BLOCK(res, 0);
  
    const word tmp = block->w64[3] >> right_count;
    rblock->w64[3] = (block->w64[3] << count) | (block->w64[2] >> right_count);
    rblock->w64[2] = (block->w64[2] << count) | (block->w64[1] >> right_count);
    rblock->w64[1] = (block->w64[1] << count) | (block->w64[0] >> right_count);
    rblock->w64[0] = (block->w64[0] << count) | tmp;
  }
  
  void mzd_rotate_right_uint64_256(mzd_local_t* res, const mzd_local_t* val, unsigned int count) {
    const unsigned int left_count = 8 * sizeof(word) - count;
    const block_t* block          = CONST_BLOCK(val, 0);
    block_t* rblock               = BLOCK(res, 0);
  
    const word tmp = block->w64[0] << left_count;
    rblock->w64[0] = (block->w64[0] >> count) | (block->w64[1] << left_count);
    rblock->w64[1] = (block->w64[1] >> count) | (block->w64[2] << left_count);
    rblock->w64[2] = (block->w64[2] >> count) | (block->w64[3] << left_count);
    rblock->w64[3] = (block->w64[3] >> count) | tmp;
  }
  #endif
  
  static void clear_uint64_block(block_t* block, const unsigned int idx) {
    for (unsigned int i = 0; i < idx; ++i) {
      block->w64[i] = 0;
    }
  }
  
  static void mzd_xor_mask_uint64_block(block_t* rblock, const block_t* fblock, const word mask,
                                        const unsigned int idx) {
    for (unsigned int i = 0; i < idx; ++i) {
      rblock->w64[i] ^= fblock->w64[i] & mask;
    }
  }
  
  #if 0
  void mzd_addmul_v_uint64_128(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    block_t* cblock       = BLOCK(c, 0);
    const word* vptr      = CONST_BLOCK(v, 0)->w64;
    const block_t* Ablock = CONST_BLOCK(A, 0);
  
    for (unsigned int w = 2; w; --w, ++vptr) {
      word idx = *vptr;
      for (unsigned int i = sizeof(word) * 8; i; i -= 2, idx >>= 2, Ablock += 1) {
        const uint64_t mask1 = -(idx & 1);
        const uint64_t mask2 = -((idx >> 1) & 1);
        cblock->w64[0] ^= (Ablock->w64[0] & mask1) ^ (Ablock->w64[2] & mask2);
        cblock->w64[1] ^= (Ablock->w64[1] & mask1) ^ (Ablock->w64[3] & mask2);
      }
    }
  }
  
  void mzd_mul_v_uint64_128(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    clear_uint64_block(BLOCK(c, 0), 2);
    mzd_addmul_v_uint64_128(c, v, A);
  }
  #endif
  
  void mzd_addmul_v_uint64_129(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    block_t* cblock       = BLOCK(c, 0);
    const word* vptr      = CONST_BLOCK(v, 0)->w64;
    const block_t* Ablock = CONST_BLOCK(A, 0);
  
    {
      word idx            = (*vptr) >> 63;
      const uint64_t mask = -(idx & 1);
      mzd_xor_mask_uint64_block(cblock, Ablock, mask, 3);
      Ablock++;
      vptr++;
    }
  
    for (unsigned int w = 2; w; --w, ++vptr) {
      word idx = *vptr;
      for (unsigned int i = sizeof(word) * 8; i; --i, idx >>= 1, ++Ablock) {
        const uint64_t mask = -(idx & 1);
        mzd_xor_mask_uint64_block(cblock, Ablock, mask, 3);
      }
    }
  }
  
  void mzd_mul_v_uint64_129(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    clear_uint64_block(BLOCK(c, 0), 3);
    mzd_addmul_v_uint64_129(c, v, A);
  }
  
  void mzd_addmul_v_uint64_192(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    block_t* cblock       = BLOCK(c, 0);
    const word* vptr      = CONST_BLOCK(v, 0)->w64;
    const block_t* Ablock = CONST_BLOCK(A, 0);
  
    for (unsigned int w = 3; w; --w, ++vptr) {
      word idx = *vptr;
      for (unsigned int i = sizeof(word) * 8; i; --i, idx >>= 1, ++Ablock) {
        const uint64_t mask = -(idx & 1);
        mzd_xor_mask_uint64_block(cblock, Ablock, mask, 3);
      }
    }
  }
  
  void mzd_mul_v_uint64_192(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    clear_uint64_block(BLOCK(c, 0), 3);
    mzd_addmul_v_uint64_192(c, v, A);
  }
  
  void mzd_addmul_v_uint64_256(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    block_t* cblock       = BLOCK(c, 0);
    const word* vptr      = CONST_BLOCK(v, 0)->w64;
    const block_t* Ablock = CONST_BLOCK(A, 0);
  
    for (unsigned int w = 4; w; --w, ++vptr) {
      word idx = *vptr;
  
      for (unsigned int i = sizeof(word) * 8; i; --i, idx >>= 1, ++Ablock) {
        const uint64_t mask = -(idx & 1);
        mzd_xor_mask_uint64_block(cblock, Ablock, mask, 4);
      }
    }
  }
  
  void mzd_mul_v_uint64_256(mzd_local_t* c, mzd_local_t const* v, mzd_local_t const* A) {
    clear_uint64_block(BLOCK(c, 0), 4);
    mzd_addmul_v_uint64_256(c, v, A);
  }
  
  // no SIMD
  