/*********************************************************************
  Blosc - Blocked Shuffling and Compression Library

  Copyright (c) 2021  Blosc Development Team <blosc@blosc.org>
  https://blosc.org
  License: BSD 3-Clause (see LICENSE.txt)

  See LICENSE.txt for details about copyright and rights to use.
**********************************************************************/

/*
 * Bitshuffle - Filter for improving compression of typed binary data.
 *
 * Author: Kiyoshi Masui <kiyo@physics.ubc.ca>
 * Website: http://www.github.com/kiyo-masui/bitshuffle
 * Created: 2014
 *
 */

/* Generic (non-hardware-accelerated) shuffle/unshuffle routines.
   These are used when hardware-accelerated functions aren't available
   for a particular platform; they are also used by the hardware-
   accelerated functions to handle any remaining elements in a block
   which isn't a multiple of the hardware's vector size. */

#ifndef BLOSC_BITSHUFFLE_GENERIC_H
#define BLOSC_BITSHUFFLE_GENERIC_H

#include "blosc2/blosc2-common.h"

#include <stddef.h>
#include <stdint.h>

// Macros.
#define CHECK_MULT_EIGHT(n) if (n % 8) return -80;
#define MAX(X,Y) ((X) > (Y) ? (X) : (Y))

#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
#define CHECK_ERR(count)                  \
        do {                              \
          if ((count) < 0)                \
            return count;                 \
         } while (0)

#define CHECK_ERR_FREE(count, buf) if (count < 0) { free(buf); return count; }

/* ---- Worker code not requiring special instruction sets. ----
 *
 * The following code does not use any x86 specific vectorized instructions
 * and should compile on any machine
 *
 */

/* Transpose 8x8 bit array packed into a single quadword *x*.
 * *t* is workspace. */
#define TRANS_BIT_8X8(x, t) {                                               \
        t = (x ^ (x >> 7)) & 0x00AA00AA00AA00AALL;                          \
        x = x ^ t ^ (t << 7);                                               \
        t = (x ^ (x >> 14)) & 0x0000CCCC0000CCCCLL;                         \
        x = x ^ t ^ (t << 14);                                              \
        t = (x ^ (x >> 28)) & 0x00000000F0F0F0F0LL;                         \
        x = x ^ t ^ (t << 28);                                              \
    }

/* Transpose 8x8 bit array along the diagonal from upper right
   to lower left */
#define TRANS_BIT_8X8_BE(x, t) {                                            \
        t = (x ^ (x >> 9)) & 0x0055005500550055LL;                          \
        x = x ^ t ^ (t << 9);                                               \
        t = (x ^ (x >> 18)) & 0x0000333300003333LL;                         \
        x = x ^ t ^ (t << 18);                                              \
        t = (x ^ (x >> 36)) & 0x000000000F0F0F0FLL;                         \
        x = x ^ t ^ (t << 36);                                              \
    }

/* Transpose of an array of arbitrarily typed elements. */
#define TRANS_ELEM_TYPE(in, out, lda, ldb, type_t) {                        \
        size_t ii, jj, kk;                                                  \
        const type_t* in_type = (const type_t*) in;                                 \
        type_t* out_type = (type_t*) out;                                   \
        for(ii = 0; ii + 7 < lda; ii += 8) {                                \
            for(jj = 0; jj < ldb; jj++) {                                   \
                for(kk = 0; kk < 8; kk++) {                                 \
                    out_type[jj*lda + ii + kk] =                            \
                        in_type[ii*ldb + kk * ldb + jj];                    \
                }                                                           \
            }                                                               \
        }                                                                   \
        for(ii = lda - lda % 8; ii < lda; ii ++) {                          \
            for(jj = 0; jj < ldb; jj++) {                                   \
                out_type[jj*lda + ii] = in_type[ii*ldb + jj];                            \
            }                                                               \
        }                                                                   \
    }



/* Memory copy with bshuf call signature. For testing and profiling. */
BLOSC_NO_EXPORT int64_t
bshuf_copy(const void* in, void* out, const size_t size, const size_t elem_size);

/* Private functions */
BLOSC_NO_EXPORT int64_t
bshuf_trans_byte_elem_remainder(const void* in, void* out, const size_t size,
                                const size_t elem_size, const size_t start);

BLOSC_NO_EXPORT int64_t
bshuf_trans_byte_elem_scal(const void* in, void* out, const size_t size,
                           const size_t elem_size);

BLOSC_NO_EXPORT int64_t
bshuf_trans_bit_byte_remainder(const void* in, void* out, const size_t size,
                               const size_t elem_size, const size_t start_byte);

BLOSC_NO_EXPORT int64_t
bshuf_trans_elem(const void* in, void* out, const size_t lda,
                 const size_t ldb, const size_t elem_size);

BLOSC_NO_EXPORT int64_t
bshuf_trans_bitrow_eight(const void* in, void* out, const size_t size,
                         const size_t elem_size);

BLOSC_NO_EXPORT int64_t
bshuf_shuffle_bit_eightelem_scal(const void* in, void* out,
                                 const size_t size, const size_t elem_size);

BLOSC_NO_EXPORT int64_t
bshuf_trans_byte_bitrow_scal(const void* in, void* out, const size_t size,
                             const size_t elem_size);

/* Bitshuffle the data.
 *
 * Transpose the bits within elements.
 *
 * Parameters
 * ----------
 *  in : input buffer, must be of size * elem_size bytes
 *  out : output buffer, must be of size * elem_size bytes
 *  size : number of elements in input
 *  elem_size : element size of typed data
 *  tmp_buffer : temporary buffer with the same `size` than `in` and `out`
 *
 * Returns
 * -------
 *  nothing -- this cannot fail
 *
 */

BLOSC_NO_EXPORT int64_t
bshuf_trans_bit_elem_scal(const void* in, void* out, const size_t size,
                          const size_t elem_size);

/* Unshuffle bitshuffled data.
 *
 * Untranspose the bits within elements.
 *
 * To properly unshuffle bitshuffled data, *size* and *elem_size* must
 * match the parameters used to shuffle the data.
 *
 * Parameters
 * ----------
 *  in : input buffer, must be of size * elem_size bytes
 *  out : output buffer, must be of size * elem_size bytes
 *  size : number of elements in input
 *  elem_size : element size of typed data
 *  tmp_buffer : temporary buffer with the same `size` than `in` and `out`
 *
 * Returns
 * -------
 *  nothing -- this cannot fail
 *
 */

BLOSC_NO_EXPORT int64_t
bshuf_untrans_bit_elem_scal(const void* in, void* out, const size_t size,
                            const size_t elem_size);

#endif /* BLOSC_BITSHUFFLE_GENERIC_H */
