/**
 * Copyright 2013-2023 Software Radio Systems Limited
 *
 * This file is part of srsRAN.
 *
 * srsRAN is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * srsRAN is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * A copy of the GNU Affero General Public License can be found in
 * the LICENSE file in the top-level directory of this distribution
 * and at http://www.gnu.org/licenses/.
 *
 */

#include "srsran/phy/utils/bit.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include "srsran/srsran.h"
#include <assert.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

#define SRSRAN_PDSCH_MIN_TDEC_ITERS 2
#define SRSRAN_PDSCH_MAX_TDEC_ITERS 10

#ifdef LV_HAVE_SSE
#include <immintrin.h>
#endif /* LV_HAVE_SSE */

/* 36.213 Table 8.6.3-1: Mapping of HARQ-ACK offset values and the index signalled by higher layers */
static inline float get_beta_harq_offset(uint32_t idx)
{
  const float beta_harq_offset[16] = {
      2.0, 2.5, 3.125, 4.0, 5.0, 6.250, 8.0, 10.0, 12.625, 15.875, 20.0, 31.0, 50.0, 80.0, 126.0, -1.0};
  float ret = beta_harq_offset[0];

  if (idx < 15) {
    ret = beta_harq_offset[idx];
  } else {
    ERROR("Invalid input %d (min: %d, max: %d)", idx, 0, 14);
  }

  return ret;
}

/* 36.213 Table 8.6.3-2: Mapping of RI offset values and the index signalled by higher layers */
static inline float get_beta_ri_offset(uint32_t idx)
{
  const float beta_ri_offset[16] = {
      1.25, 1.625, 2.0, 2.5, 3.125, 4.0, 5.0, 6.25, 8.0, 10.0, 12.625, 15.875, 20.0, -1.0, -1.0, -1.0};
  float ret = beta_ri_offset[0];

  if (idx < 13) {
    ret = beta_ri_offset[idx];
  } else {
    ERROR("Invalid input %d (min: %d, max: %d)", idx, 0, 12);
  }

  return ret;
}

/* 36.213 Table 8.6.3-3: Mapping of CQI offset values and the index signalled by higher layers */
static inline float get_beta_cqi_offset(uint32_t idx)
{
  const float beta_cqi_offset[16] = {
      -1.0, -1.0, 1.125, 1.25, 1.375, 1.625, 1.750, 2.0, 2.25, 2.5, 2.875, 3.125, 3.5, 4.0, 5.0, 6.25};
  float ret = beta_cqi_offset[2];

  if (idx > 1 && idx < 16) {
    ret = beta_cqi_offset[idx];
  } else {
    ERROR("Invalid input %d (min: %d, max: %d)", idx, 2, 15);
  }

  return ret;
}

float srsran_sch_beta_cqi(uint32_t I_cqi)
{
  if (I_cqi < 16) {
    return get_beta_cqi_offset(I_cqi);
  } else {
    return 0;
  }
}

float srsran_sch_beta_ack(uint32_t I_harq)
{
  if (I_harq < 16) {
    return get_beta_harq_offset(I_harq);
  } else {
    return 0;
  }
}

uint32_t srsran_sch_find_Ioffset_ack(float beta)
{
  for (int i = 0; i < 16; i++) {
    if (get_beta_harq_offset(i) >= beta) {
      return i;
    }
  }
  return 0;
}

uint32_t srsran_sch_find_Ioffset_ri(float beta)
{
  for (int i = 0; i < 16; i++) {
    if (get_beta_ri_offset(i) >= beta) {
      return i;
    }
  }
  return 0;
}

uint32_t srsran_sch_find_Ioffset_cqi(float beta)
{
  for (int i = 0; i < 16; i++) {
    if (get_beta_cqi_offset(i) >= beta) {
      return i;
    }
  }
  return 0;
}

#define SCH_MAX_G_BITS (SRSRAN_MAX_PRB * 12 * 12 * 12)

int srsran_sch_init(srsran_sch_t* q)
{
  int ret = SRSRAN_ERROR_INVALID_INPUTS;
  if (q) {
    ret = SRSRAN_ERROR;
    bzero(q, sizeof(srsran_sch_t));

    if (srsran_crc_init(&q->crc_tb, SRSRAN_LTE_CRC24A, 24)) {
      ERROR("Error initiating CRC");
      goto clean;
    }
    if (srsran_crc_init(&q->crc_cb, SRSRAN_LTE_CRC24B, 24)) {
      ERROR("Error initiating CRC");
      goto clean;
    }

    if (srsran_tcod_init(&q->encoder, SRSRAN_TCOD_MAX_LEN_CB)) {
      ERROR("Error initiating Turbo Coder");
      goto clean;
    }
    if (srsran_tdec_init(&q->decoder, SRSRAN_TCOD_MAX_LEN_CB)) {
      ERROR("Error initiating Turbo Decoder");
      goto clean;
    }

    q->max_iterations = SRSRAN_PDSCH_MAX_TDEC_ITERS;

    srsran_rm_turbo_gentables();

    // Allocate int16 for reception (LLRs)
    q->cb_in = srsran_vec_u8_malloc((SRSRAN_TCOD_MAX_LEN_CB + 8) / 8);
    if (!q->cb_in) {
      goto clean;
    }

    q->parity_bits = srsran_vec_u8_malloc((3 * SRSRAN_TCOD_MAX_LEN_CB + 16) / 8);
    if (!q->parity_bits) {
      goto clean;
    }
    q->temp_g_bits = srsran_vec_u8_malloc(SCH_MAX_G_BITS);
    if (!q->temp_g_bits) {
      goto clean;
    }
    bzero(q->temp_g_bits, SRSRAN_MAX_PRB * 12 * 12 * 12);
    q->ul_interleaver = srsran_vec_u32_malloc(SCH_MAX_G_BITS);
    if (!q->ul_interleaver) {
      goto clean;
    }
    if (srsran_uci_cqi_init(&q->uci_cqi)) {
      goto clean;
    }

    ret = SRSRAN_SUCCESS;
  }
clean:
  if (ret == SRSRAN_ERROR) {
    srsran_sch_free(q);
  }
  return ret;
}

void srsran_sch_free(srsran_sch_t* q)
{
  srsran_rm_turbo_free_tables();

  if (q->cb_in) {
    free(q->cb_in);
  }
  if (q->parity_bits) {
    free(q->parity_bits);
  }
  if (q->temp_g_bits) {
    free(q->temp_g_bits);
  }
  if (q->ul_interleaver) {
    free(q->ul_interleaver);
  }
  srsran_tdec_free(&q->decoder);
  srsran_tcod_free(&q->encoder);
  srsran_uci_cqi_free(&q->uci_cqi);
  bzero(q, sizeof(srsran_sch_t));
}

void srsran_sch_set_max_noi(srsran_sch_t* q, uint32_t max_iterations)
{
  if (max_iterations == 0) {
    max_iterations = SRSRAN_PDSCH_MAX_TDEC_ITERS;
  }

  q->max_iterations = max_iterations;
}

float srsran_sch_last_noi(srsran_sch_t* q)
{
  return q->avg_iterations;
}

/* Encode a transport block according to 36.212 5.3.2
 *
 */
static int encode_tb_off(srsran_sch_t*           q,
                         srsran_softbuffer_tx_t* softbuffer,
                         srsran_cbsegm_t*        cb_segm,
                         uint32_t                Qm,
                         uint32_t                rv,
                         uint32_t                nof_e_bits,
                         uint8_t*                data,
                         uint8_t*                e_bits,
                         uint32_t                w_offset)
{
  uint32_t i;
  uint32_t cb_len = 0, rp = 0, wp = 0, rlen = 0, n_e = 0;
  int      ret = SRSRAN_ERROR_INVALID_INPUTS;

  if (q != NULL && e_bits != NULL && cb_segm != NULL && softbuffer != NULL) {
    if (cb_segm->F) {
      ERROR("Error filler bits are not supported. Use standard TBS");
      return SRSRAN_ERROR;
    }

    if (cb_segm->C > softbuffer->max_cb) {
      ERROR("Error number of CB to encode (%d) exceeds soft buffer size (%d CBs)", cb_segm->C, softbuffer->max_cb);
      return SRSRAN_ERROR;
    }

    if (Qm == 0) {
      ERROR("Invalid Qm");
      return SRSRAN_ERROR;
    }

    uint32_t Gp = nof_e_bits / Qm;

    uint32_t gamma = Gp;
    if (cb_segm->C > 0) {
      gamma = Gp % cb_segm->C;
    }

    /* Reset TB CRC */
    srsran_crc_set_init(&q->crc_tb, 0);

    wp = 0;
    rp = 0;
    for (i = 0; i < cb_segm->C; i++) {
      uint32_t cblen_idx;
      /* Get read lengths */
      if (i < cb_segm->C2) {
        cb_len    = cb_segm->K2;
        cblen_idx = cb_segm->K2_idx;
      } else {
        cb_len    = cb_segm->K1;
        cblen_idx = cb_segm->K1_idx;
      }
      if (cb_segm->C > 1) {
        rlen = cb_len - 24;
      } else {
        rlen = cb_len;
      }
      if (i <= cb_segm->C - gamma - 1) {
        n_e = Qm * (Gp / cb_segm->C);
      } else {
        n_e = Qm * ((uint32_t)ceilf((float)Gp / cb_segm->C));
      }

      INFO("CB#%d: cb_len: %d, rlen: %d, wp: %d, rp: %d, E: %d", i, cb_len, rlen, wp, rp, n_e);

      if (data) {
        bool last_cb = false;

        /* Copy data to another buffer, making space for the Codeblock CRC */
        if (i < cb_segm->C - 1) {
          // Copy data
          memcpy(q->cb_in, &data[rp / 8], rlen * sizeof(uint8_t) / 8);
        } else {
          INFO("Last CB, appending parity: %d from %d and 24 to %d", rlen - 24, rp, rlen - 24);

          /* Append Transport Block parity bits to the last CB */
          memcpy(q->cb_in, &data[rp / 8], (rlen - 24) * sizeof(uint8_t) / 8);
          last_cb = true;
        }

        /* Turbo Encoding
         * If Codeblock CRC is required it is given the CRC instance pointer, otherwise CRC pointer shall be NULL
         */
        srsran_tcod_encode_lut(&q->encoder,
                               &q->crc_tb,
                               (cb_segm->C > 1) ? &q->crc_cb : NULL,
                               q->cb_in,
                               q->parity_bits,
                               cblen_idx,
                               last_cb);
      }
      DEBUG("RM cblen_idx=%d, n_e=%d, wp=%d, nof_e_bits=%d", cblen_idx, n_e, wp, nof_e_bits);

      /* Rate matching */
      if (srsran_rm_turbo_tx_lut(softbuffer->buffer_b[i],
                                 q->cb_in,
                                 q->parity_bits,
                                 &e_bits[(wp + w_offset) / 8],
                                 cblen_idx,
                                 n_e,
                                 (wp + w_offset) % 8,
                                 rv)) {
        ERROR("Error in rate matching");
        return SRSRAN_ERROR;
      }

      /* Set read/write pointers */
      rp += rlen;
      wp += n_e;
    }

    INFO("END CB#%d: wp: %d, rp: %d", i, wp, rp);
    ret = SRSRAN_SUCCESS;
  } else {
    ERROR("Invalid parameters: e_bits=%d, cb_segm=%d, softbuffer=%d", e_bits != 0, cb_segm != 0, softbuffer != 0);
  }
  return ret;
}

static int encode_tb(srsran_sch_t*           q,
                     srsran_softbuffer_tx_t* soft_buffer,
                     srsran_cbsegm_t*        cb_segm,
                     uint32_t                Qm,
                     uint32_t                rv,
                     uint32_t                nof_e_bits,
                     uint8_t*                data,
                     uint8_t*                e_bits)
{
  return encode_tb_off(q, soft_buffer, cb_segm, Qm, rv, nof_e_bits, data, e_bits, 0);
}

bool decode_tb_cb(srsran_sch_t*           q,
                  srsran_softbuffer_rx_t* softbuffer,
                  srsran_cbsegm_t*        cb_segm,
                  uint32_t                Qm,
                  uint32_t                rv,
                  uint32_t                nof_e_bits,
                  void*                   e_bits,
                  uint8_t*                data)
{
  int8_t*  e_bits_b = e_bits;
  int16_t* e_bits_s = e_bits;

  if (cb_segm->C > SRSRAN_MAX_CODEBLOCKS) {
    ERROR("Error SRSRAN_MAX_CODEBLOCKS=%d", SRSRAN_MAX_CODEBLOCKS);
    return false;
  }

  q->avg_iterations = 0;

  for (int cb_idx = 0; cb_idx < cb_segm->C; cb_idx++) {
    /* Do not process blocks with CRC Ok */
    if (softbuffer->cb_crc[cb_idx] == false) {
      uint32_t cb_len     = cb_idx < cb_segm->C1 ? cb_segm->K1 : cb_segm->K2;
      uint32_t cb_len_idx = cb_idx < cb_segm->C1 ? cb_segm->K1_idx : cb_segm->K2_idx;

      uint32_t rlen  = cb_segm->C == 1 ? cb_len : (cb_len - 24);
      uint32_t Gp    = nof_e_bits / Qm;
      uint32_t gamma = cb_segm->C > 0 ? Gp % cb_segm->C : Gp;
      uint32_t n_e   = Qm * (Gp / cb_segm->C);

      uint32_t rp   = cb_idx * n_e;
      uint32_t n_e2 = n_e;

      if (cb_idx > cb_segm->C - gamma) {
        n_e2 = n_e + Qm;
        rp   = (cb_segm->C - gamma) * n_e + (cb_idx - (cb_segm->C - gamma)) * n_e2;
      }

      if (q->llr_is_8bit) {
        if (srsran_rm_turbo_rx_lut_8bit(&e_bits_b[rp], (int8_t*)softbuffer->buffer_f[cb_idx], n_e2, cb_len_idx, rv)) {
          ERROR("Error in rate matching");
          return SRSRAN_ERROR;
        }
      } else {
        if (srsran_rm_turbo_rx_lut(&e_bits_s[rp], softbuffer->buffer_f[cb_idx], n_e2, cb_len_idx, rv)) {
          ERROR("Error in rate matching");
          return SRSRAN_ERROR;
        }
      }

      srsran_tdec_new_cb(&q->decoder, cb_len);

      // Run iterations and use CRC for early stopping
      bool     early_stop = false;
      uint32_t cb_noi     = 0;
      do {
        if (q->llr_is_8bit) {
          srsran_tdec_iteration_8bit(&q->decoder, (int8_t*)softbuffer->buffer_f[cb_idx], &data[cb_idx * rlen / 8]);
        } else {
          srsran_tdec_iteration(&q->decoder, softbuffer->buffer_f[cb_idx], &data[cb_idx * rlen / 8]);
        }
        q->avg_iterations++;
        cb_noi++;

        uint32_t      len_crc;
        srsran_crc_t* crc_ptr;

        if (cb_segm->C > 1) {
          len_crc = cb_len;
          crc_ptr = &q->crc_cb;
        } else {
          len_crc = cb_segm->tbs + 24;
          crc_ptr = &q->crc_tb;
        }

        // CRC is OK and ran the minimum number of iterations
        if (!srsran_crc_checksum_byte(crc_ptr, &data[cb_idx * rlen / 8], len_crc) &&
            (cb_noi >= SRSRAN_PDSCH_MIN_TDEC_ITERS)) {
          softbuffer->cb_crc[cb_idx] = true;
          early_stop                 = true;

          // CRC is error and exceeded maximum iterations for this CB.
          // Early stop the whole transport block.
        }

      } while (cb_noi < q->max_iterations && !early_stop);

      INFO("CB %d: rp=%d, n_e=%d, cb_len=%d, CRC=%s, rlen=%d, iterations=%d/%d",
           cb_idx,
           rp,
           n_e2,
           cb_len,
           early_stop ? "OK" : "KO",
           rlen,
           cb_noi,
           q->max_iterations);

    } else {
      // Copy decoded data from previous transmissions
      uint32_t cb_len = cb_idx < cb_segm->C1 ? cb_segm->K1 : cb_segm->K2;
      uint32_t rlen   = cb_segm->C == 1 ? cb_len : (cb_len - 24);
      memcpy(&data[cb_idx * rlen / 8], softbuffer->data[cb_idx], rlen / 8 * sizeof(uint8_t));
    }
  }

  softbuffer->tb_crc = true;
  for (int i = 0; i < cb_segm->C && softbuffer->tb_crc; i++) {
    /* If one CB failed return false */
    softbuffer->tb_crc = softbuffer->cb_crc[i];
  }
  // If TB CRC failed, save correct CB for next retransmission
  if (!softbuffer->tb_crc) {
    for (int i = 0; i < cb_segm->C; i++) {
      if (softbuffer->cb_crc[i]) {
        uint32_t cb_len = i < cb_segm->C1 ? cb_segm->K1 : cb_segm->K2;
        uint32_t rlen   = cb_segm->C == 1 ? cb_len : (cb_len - 24);
        memcpy(softbuffer->data[i], &data[i * rlen / 8], rlen / 8 * sizeof(uint8_t));
      }
    }
  }

  q->avg_iterations /= (float)cb_segm->C;
  return softbuffer->tb_crc;
}

/**
 * Decode a transport block according to 36.212 5.3.2
 *
 * @param[in] q
 * @param[inout] softbuffer Initialized softbuffer
 * @param[in] cb_segm Code block segmentation parameters
 * @param[in] e_bits Input transport block
 * @param[in] Qm Modulation type
 * @param[in] rv Redundancy Version. Indicates which part of FEC bits is in input buffer
 * @param[out] softbuffer Initialized output softbuffer
 * @param[out] data Decoded transport block
 * @return negative if error in parameters or CRC error in decoding
 */
static int decode_tb(srsran_sch_t*           q,
                     srsran_softbuffer_rx_t* softbuffer,
                     srsran_cbsegm_t*        cb_segm,
                     uint32_t                Qm,
                     uint32_t                rv,
                     uint32_t                nof_e_bits,
                     int16_t*                e_bits,
                     uint8_t*                data)
{
  // Check inputs
  if (q == NULL || data == NULL || softbuffer == NULL || e_bits == NULL || cb_segm == NULL || Qm == 0) {
    ERROR("Missing inputs: data=%d, softbuffer=%d, e_bits=%d, cb_segm=%d Qm=%d",
          data != 0,
          softbuffer != 0,
          e_bits != 0,
          cb_segm != 0,
          Qm);
    return SRSRAN_ERROR_INVALID_INPUTS;
  }

  // Check segmentation is valid
  if (cb_segm->tbs == 0 || cb_segm->C == 0) {
    return SRSRAN_SUCCESS;
  }

  if (cb_segm->F) {
    fprintf(stderr, "Error filler bits are not supported. Use standard TBS\n");
    return SRSRAN_ERROR_INVALID_INPUTS;
  }

  if (cb_segm->C > softbuffer->max_cb) {
    fprintf(stderr,
            "Error number of CB to decode (%d) exceeds soft buffer size (%d CBs)\n",
            cb_segm->C,
            softbuffer->max_cb);
    return SRSRAN_ERROR_INVALID_INPUTS;
  }

  // Process Codeblocks
  bool cb_crc_ok = decode_tb_cb(q, softbuffer, cb_segm, Qm, rv, nof_e_bits, e_bits, data);

  // If any of the CBs CRC is KO
  if (!cb_crc_ok) {
    INFO("Error in CB parity");
    return SRSRAN_ERROR;
  }

  // One CB CRC OK, means TB CRC is OK.
  if (cb_segm->C == 1) {
    INFO("TB decoded OK");
    return SRSRAN_SUCCESS;
  }

  // Check TB CRC for whole TB
  if (srsran_crc_match_byte(&q->crc_tb, data, cb_segm->tbs)) {
    INFO("TB decoded OK");
    return SRSRAN_SUCCESS;
  }

  // TB CRC check failed, as at least one CB had a false alarm, reset all CB CRC flags in the softbuffer
  srsran_softbuffer_rx_reset_cb_crc(softbuffer, cb_segm->C);

  INFO("Error in TB parity");
  return SRSRAN_ERROR;
}

int srsran_dlsch_decode(srsran_sch_t* q, srsran_pdsch_cfg_t* cfg, int16_t* e_bits, uint8_t* data)
{
  return srsran_dlsch_decode2(q, cfg, e_bits, data, 0, 1);
}

int srsran_dlsch_decode2(srsran_sch_t*       q,
                         srsran_pdsch_cfg_t* cfg,
                         int16_t*            e_bits,
                         uint8_t*            data,
                         int                 tb_idx,
                         uint32_t            nof_layers)
{
  uint32_t Nl = 1;

  if (nof_layers != cfg->grant.nof_tb) {
    Nl = 2;
  }
  // Prepare cbsegm
  srsran_cbsegm_t cb_segm;
  if (srsran_cbsegm(&cb_segm, (uint32_t)cfg->grant.tb[tb_idx].tbs)) {
    ERROR("Error computing Codeword (%d) segmentation for TBS=%d", tb_idx, cfg->grant.tb[tb_idx].tbs);
    return SRSRAN_ERROR;
  }

  uint32_t Qm = srsran_mod_bits_x_symbol(cfg->grant.tb[tb_idx].mod);

  return decode_tb(q,
                   cfg->softbuffers.rx[tb_idx],
                   &cb_segm,
                   Qm * Nl,
                   cfg->grant.tb[tb_idx].rv,
                   cfg->grant.tb[tb_idx].nof_bits,
                   e_bits,
                   data);
}

/**
 * Encode transport block. Segments into code blocks, adds channel coding, and does rate matching.
 *
 * @param[in] q Initialized
 * @param[in] cfg Encoding parameters
 * @param[inout] softbuffer Initialized softbuffer
 * @param[in] data Byte array of data. Size is implicit in cb_segm
 * @param e_bits
 * @return Error code
 */
int srsran_dlsch_encode(srsran_sch_t* q, srsran_pdsch_cfg_t* cfg, uint8_t* data, uint8_t* e_bits)
{
  return srsran_dlsch_encode2(q, cfg, data, e_bits, 0, 1);
}

int srsran_dlsch_encode2(srsran_sch_t*       q,
                         srsran_pdsch_cfg_t* cfg,
                         uint8_t*            data,
                         uint8_t*            e_bits,
                         int                 tb_idx,
                         uint32_t            nof_layers)
{
  uint32_t Nl = 1;

  if (nof_layers != cfg->grant.nof_tb) {
    Nl = 2;
  }

  // Prepare cbsegm
  srsran_cbsegm_t cb_segm;
  if (srsran_cbsegm(&cb_segm, (uint32_t)cfg->grant.tb[tb_idx].tbs)) {
    ERROR("Error computing Codeword (%d) segmentation for TBS=%d", tb_idx, cfg->grant.tb[tb_idx].tbs);
    return SRSRAN_ERROR;
  }

  uint32_t Qm = srsran_mod_bits_x_symbol(cfg->grant.tb[tb_idx].mod);

  return encode_tb(q,
                   cfg->softbuffers.tx[tb_idx],
                   &cb_segm,
                   Qm * Nl,
                   cfg->grant.tb[tb_idx].rv,
                   cfg->grant.tb[tb_idx].nof_bits,
                   data,
                   e_bits);
}

/* Compute the interleaving function on-the-fly, because it depends on number of RI bits
 * Profiling show that the computation of this matrix is neglegible.
 */
static void ulsch_interleave_gen(uint32_t  H_prime_total,
                                 uint32_t  N_pusch_symbs,
                                 uint32_t  Qm,
                                 uint8_t*  ri_present,
                                 uint32_t* interleaver_lut)
{
  uint32_t rows = H_prime_total / N_pusch_symbs;
  uint32_t cols = N_pusch_symbs;
  uint32_t idx  = 0;
  for (uint32_t j = 0; j < rows; j++) {
    for (uint32_t i = 0; i < cols; i++) {
      for (uint32_t k = 0; k < Qm; k++) {
        if (ri_present && ri_present[j * Qm + i * rows * Qm + k]) {
          interleaver_lut[j * Qm + i * rows * Qm + k] = 0;
        } else {
          interleaver_lut[j * Qm + i * rows * Qm + k] = idx;
          idx++;
        }
      }
    }
  }
}

static void ulsch_interleave_qm2(const uint8_t* g_bits,
                                 uint32_t       rows,
                                 uint32_t       cols,
                                 uint8_t*       q_bits,
                                 uint32_t       ri_min_row,
                                 const uint8_t* ri_present)
{
  uint32_t bit_read_idx = 0;

  for (uint32_t j = 0; j < ri_min_row; j++) {
    for (uint32_t i = 0; i < cols; i++) {
      uint32_t k = (i * rows + j) * 2;

      uint32_t read_byte_idx  = bit_read_idx / 8;
      uint32_t read_bit_idx   = bit_read_idx % 8;
      uint32_t write_byte_idx = k / 8;
      uint32_t write_bit_idx  = k % 8;
      uint8_t  w              = (g_bits[read_byte_idx] >> (6 - read_bit_idx)) & (uint8_t)0x03;
      q_bits[write_byte_idx] |= w << (6 - write_bit_idx);

      bit_read_idx += 2;
    }
  }

  for (uint32_t j = ri_min_row; j < rows; j++) {
    for (uint32_t i = 0; i < cols; i++) {
      uint32_t k = (i * rows + j) * 2;

      if (ri_present[k]) {
        /* do nothing */
      } else {
        uint32_t read_byte_idx  = bit_read_idx / 8;
        uint32_t read_bit_idx   = bit_read_idx % 8;
        uint32_t write_byte_idx = k / 8;
        uint32_t write_bit_idx  = k % 8;
        uint8_t  w              = (g_bits[read_byte_idx] >> (6 - read_bit_idx)) & (uint8_t)0x03;
        q_bits[write_byte_idx] |= w << (6 - write_bit_idx);

        bit_read_idx += 2;
      }
    }
  }
}

static void ulsch_interleave_qm4(uint8_t*       g_bits,
                                 uint32_t       rows,
                                 uint32_t       cols,
                                 uint8_t*       q_bits,
                                 uint32_t       ri_min_row,
                                 const uint8_t* ri_present)
{
  uint32_t bit_read_idx = 0;

  for (uint32_t j = 0; j < ri_min_row; j++) {
    int32_t i = 0;

#ifdef DOES_NOT_WORK
#ifndef HAVE_NEON
    __m128i  _counter = _mm_slli_epi32(_mm_add_epi32(_mm_mullo_epi32(_counter0, _rows), _mm_set1_epi32(j)), 2);
    uint8_t* _g_bits  = &g_bits[bit_read_idx / 8];

    /* First bits are aligned to byte */
    if (0 == (bit_read_idx & 0x3)) {
      for (; i < (cols - 3); i += 4) {
        uint8_t w1 = *(_g_bits++);
        uint8_t w2 = *(_g_bits++);

        __m128i _write_byte_idx = _mm_srli_epi32(_counter, 3);
        __m128i _write_bit_idx  = _mm_and_si128(_counter, _7);
        __m128i _write_shift    = _mm_sub_epi32(_4, _write_bit_idx);

        q_bits[_mm_extract_epi32(_write_byte_idx, 0)] |= (w1 >> 0x4) << _mm_extract_epi32(_write_shift, 0);
        q_bits[_mm_extract_epi32(_write_byte_idx, 1)] |= (w1 & 0xf) << _mm_extract_epi32(_write_shift, 1);
        q_bits[_mm_extract_epi32(_write_byte_idx, 2)] |= (w2 >> 0x4) << _mm_extract_epi32(_write_shift, 2);
        q_bits[_mm_extract_epi32(_write_byte_idx, 3)] |= (w2 & 0xf) << _mm_extract_epi32(_write_shift, 3);
        _counter = _mm_add_epi32(_counter, _inc);
      }
    } else {
      for (; i < (cols - 3); i += 4) {
        __m128i _write_byte_idx = _mm_srli_epi32(_counter, 3);
        __m128i _write_bit_idx  = _mm_and_si128(_counter, _7);
        __m128i _write_shift    = _mm_sub_epi32(_4, _write_bit_idx);

        uint8_t w1 = *(_g_bits);
        uint8_t w2 = *(_g_bits++);
        uint8_t w3 = *(_g_bits++);
        q_bits[_mm_extract_epi32(_write_byte_idx, 0)] |= (w1 & 0xf) << _mm_extract_epi32(_write_shift, 0);
        q_bits[_mm_extract_epi32(_write_byte_idx, 1)] |= (w2 >> 0x4) << _mm_extract_epi32(_write_shift, 1);
        q_bits[_mm_extract_epi32(_write_byte_idx, 2)] |= (w2 & 0xf) << _mm_extract_epi32(_write_shift, 2);
        q_bits[_mm_extract_epi32(_write_byte_idx, 3)] |= (w3 >> 0x4) << _mm_extract_epi32(_write_shift, 3);

        _counter = _mm_add_epi32(_counter, _inc);
      }
    }
    bit_read_idx += i * 4;
#endif /* HAVE_NEON */
#endif /* LV_HAVE_SSE */

    /* Spare bits */
    for (; i < cols; i++) {
      uint32_t k = (i * rows + j) * 4;

      uint32_t read_byte_idx  = bit_read_idx / 8;
      uint32_t read_bit_idx   = bit_read_idx % 8;
      uint32_t write_byte_idx = k / 8;
      uint32_t write_bit_idx  = k % 8;
      uint8_t  w              = (g_bits[read_byte_idx] >> (4 - read_bit_idx)) & (uint8_t)0x0f;
      q_bits[write_byte_idx] |= w << (4 - write_bit_idx);

      bit_read_idx += 4;
    }
  }

  /* Do rows containing RI */
  for (uint32_t j = ri_min_row; j < rows; j++) {
    for (uint32_t i = 0; i < cols; i++) {
      uint32_t k = (i * rows + j) * 4;

      if (ri_present[k]) {
        /* do nothing */
      } else {
        uint32_t read_byte_idx  = bit_read_idx / 8;
        uint32_t read_bit_idx   = bit_read_idx % 8;
        uint32_t write_byte_idx = k / 8;
        uint32_t write_bit_idx  = k % 8;
        uint8_t  w              = (g_bits[read_byte_idx] >> (4 - read_bit_idx)) & (uint8_t)0x0f;
        q_bits[write_byte_idx] |= w << (4 - write_bit_idx);

        bit_read_idx += 4;
      }
    }
  }
}

static void ulsch_interleave_qm6(const uint8_t* g_bits,
                                 uint32_t       rows,
                                 uint32_t       cols,
                                 uint8_t*       q_bits,
                                 uint32_t       ri_min_row,
                                 const uint8_t* ri_present)
{
  uint32_t bit_read_idx = 0;

  for (uint32_t j = 0; j < ri_min_row; j++) {
    for (uint32_t i = 0; i < cols; i++) {
      uint32_t k = (i * rows + j) * 6;

      uint32_t read_byte_idx  = bit_read_idx / 8;
      uint32_t read_bit_idx   = bit_read_idx % 8;
      uint32_t write_byte_idx = k / 8;
      uint32_t write_bit_idx  = k % 8;
      uint8_t  w;

      switch (read_bit_idx) {
        case 0:
          w = g_bits[read_byte_idx] >> 2;
          break;
        case 2:
          w = g_bits[read_byte_idx] & (uint8_t)0x3f;
          break;
        case 4:
          w = ((g_bits[read_byte_idx] << 2) | (g_bits[read_byte_idx + 1] >> 6)) & (uint8_t)0x3f;
          break;
        case 6:
          w = ((g_bits[read_byte_idx] << 4) | (g_bits[read_byte_idx + 1] >> 4)) & (uint8_t)0x3f;
          break;
        default:
          w = 0;
      }

      switch (write_bit_idx) {
        case 0:
          q_bits[write_byte_idx] |= w << 2;
          break;
        case 2:
          q_bits[write_byte_idx] |= w;
          break;
        case 4:
          q_bits[write_byte_idx] |= w >> 2;
          q_bits[write_byte_idx + 1] |= w << 6;
          break;
        case 6:
          q_bits[write_byte_idx] |= w >> 4;
          q_bits[write_byte_idx + 1] |= w << 4;
          break;
        default:
          /* Do nothing */;
      }

      bit_read_idx += 6;
    }
  }

  for (uint32_t j = ri_min_row; j < rows; j++) {
    for (uint32_t i = 0; i < cols; i++) {
      uint32_t k = (i * rows + j) * 6;

      if (ri_present[k]) {
        /* do nothing */
      } else {
        uint32_t read_byte_idx  = bit_read_idx / 8;
        uint32_t read_bit_idx   = bit_read_idx % 8;
        uint32_t write_byte_idx = k / 8;
        uint32_t write_bit_idx  = k % 8;
        uint8_t  w;

        switch (read_bit_idx) {
          case 0:
            w = g_bits[read_byte_idx] >> 2;
            break;
          case 2:
            w = g_bits[read_byte_idx] & (uint8_t)0x3f;
            break;
          case 4:
            w = ((g_bits[read_byte_idx] << 2) | (g_bits[read_byte_idx + 1] >> 6)) & (uint8_t)0x3f;
            break;
          case 6:
            w = ((g_bits[read_byte_idx] << 4) | (g_bits[read_byte_idx + 1] >> 4)) & (uint8_t)0x3f;
            break;
          default:
            w = 0;
        }

        switch (write_bit_idx) {
          case 0:
            q_bits[write_byte_idx] |= w << 2;
            break;
          case 2:
            q_bits[write_byte_idx] |= w;
            break;
          case 4:
            q_bits[write_byte_idx] |= w >> 2;
            q_bits[write_byte_idx + 1] |= w << 6;
            break;
          case 6:
            q_bits[write_byte_idx] |= w >> 4;
            q_bits[write_byte_idx + 1] |= w << 4;
            break;
          default:
            /* Do nothing */;
        }

        bit_read_idx += 6;
      }
    }
  }
}

/* UL-SCH channel interleaver according to 5.2.2.8 of 36.212 */
static void ulsch_interleave(uint8_t*          g_bits,
                             uint32_t          Qm,
                             uint32_t          H_prime_total,
                             uint32_t          N_pusch_symbs,
                             uint8_t*          q_bits,
                             srsran_uci_bit_t* ri_bits,
                             uint32_t          nof_ri_bits,
                             uint8_t*          ri_present)
{
  if (N_pusch_symbs == 0 || Qm == 0 || H_prime_total == 0 || H_prime_total < N_pusch_symbs) {
    ERROR("Invalid input: N_pusch_symbs=%d, Qm=%d, H_prime_total=%d, N_pusch_symbs=%d",
          N_pusch_symbs,
          Qm,
          H_prime_total,
          N_pusch_symbs);
    return;
  }

  const uint32_t nof_bits   = H_prime_total * Qm;
  uint32_t       rows       = H_prime_total / N_pusch_symbs;
  uint32_t       cols       = N_pusch_symbs;
  uint32_t       ri_min_row = rows;

  // Prepare ri_bits for fast search using temp_buffer
  if (nof_ri_bits > 0) {
    for (uint32_t i = 0; i < nof_ri_bits; i++) {
      uint32_t ri_row = (ri_bits[i].position / Qm) % rows;

      if (ri_row < ri_min_row) {
        ri_min_row = ri_row;
      }

      ri_present[ri_bits[i].position] = 1;
    }
  }

  bzero(q_bits, nof_bits / 8);
  switch (Qm) {
    case 2:
      ulsch_interleave_qm2(g_bits, rows, cols, q_bits, ri_min_row, ri_present);
      break;
    case 4:
      ulsch_interleave_qm4(g_bits, rows, cols, q_bits, ri_min_row, ri_present);
      break;
    case 6:
      ulsch_interleave_qm6(g_bits, rows, cols, q_bits, ri_min_row, ri_present);
      break;
    default:
      /* This line should never be reached */
      ERROR("Wrong Qm (%d)", Qm);
  }

  // Reset temp_buffer because will be reused next time
  if (nof_ri_bits > 0) {
    for (uint32_t i = 0; i < nof_ri_bits; i++) {
      ri_present[ri_bits[i].position] = 0;
    }
  }
}

/* UL-SCH channel deinterleaver according to 5.2.2.8 of 36.212 */
void ulsch_deinterleave(int16_t*          q_bits,
                        uint32_t          Qm,
                        uint32_t          H_prime_total,
                        uint32_t          N_pusch_symbs,
                        int16_t*          g_bits,
                        srsran_uci_bit_t* ri_bits,
                        uint32_t          nof_ri_bits,
                        uint8_t*          ri_present,
                        uint32_t*         inteleaver_lut)
{
  // Prepare ri_bits for fast search using temp_buffer
  if (nof_ri_bits > 0) {
    for (uint32_t i = 0; i < nof_ri_bits; i++) {
      ri_present[ri_bits[i].position] = 1;
    }
  }

  // Generate interleaver table and interleave samples
  ulsch_interleave_gen(H_prime_total, N_pusch_symbs, Qm, ri_present, inteleaver_lut);
  srsran_vec_lut_sis(q_bits, inteleaver_lut, g_bits, H_prime_total * Qm);

  // Reset temp_buffer because will be reused next time
  if (nof_ri_bits > 0) {
    for (uint32_t i = 0; i < nof_ri_bits; i++) {
      ri_present[ri_bits[i].position] = 0;
    }
  }
}

static int uci_decode_ri_ack(srsran_sch_t*       q,
                             srsran_pusch_cfg_t* cfg,
                             int16_t*            q_bits,
                             uint8_t*            c_seq,
                             srsran_uci_value_t* uci_data)
{
  int ret = 0;

  uint32_t Q_prime_ack = 0;
  uint32_t Q_prime_ri  = 0;

  uint32_t nb_q = cfg->grant.tb.nof_bits;
  uint32_t Qm   = srsran_mod_bits_x_symbol(cfg->grant.tb.mod);

  if (Qm == 0) {
    ERROR("Invalid modulation %s", srsran_mod_string(cfg->grant.tb.mod));
    return SRSRAN_ERROR;
  }

  // If there is RI and CQI, assume RI = 1 for the purpose of RI/ACK decoding (3GPP 36.212 Clause 5.2.4.1. )
  if (cfg->uci_cfg.cqi.data_enable) {
    if (cfg->uci_cfg.cqi.type == SRSRAN_CQI_TYPE_SUBBAND_HL && cfg->uci_cfg.cqi.ri_len) {
      cfg->uci_cfg.cqi.rank_is_not_one = false;
    }
  }

  uint32_t cqi_len = srsran_cqi_size(&cfg->uci_cfg.cqi);

  // Deinterleave and decode HARQ bits
  if (srsran_uci_cfg_total_ack(&cfg->uci_cfg) > 0) {
    float beta = get_beta_harq_offset(cfg->uci_offset.I_offset_ack);
    if (cfg->grant.tb.tbs == 0) {
      float beta_cqi = get_beta_cqi_offset(cfg->uci_offset.I_offset_cqi);
      if (!isnormal(beta_cqi)) {
        ERROR("Invalid beta_cqi (%d, %f)", cfg->uci_offset.I_offset_cqi, beta_cqi);
        return SRSRAN_ERROR;
      }
      beta /= beta_cqi;
    }
    ret = srsran_uci_decode_ack_ri(cfg,
                                   q_bits,
                                   c_seq,
                                   beta,
                                   nb_q / Qm,
                                   cqi_len,
                                   q->ack_ri_bits,
                                   uci_data->ack.ack_value,
                                   &uci_data->ack.valid,
                                   srsran_uci_cfg_total_ack(&cfg->uci_cfg),
                                   false);
    if (ret < 0) {
      return ret;
    }
    Q_prime_ack = (uint32_t)ret;

    // Set zeros to HARQ bits
    for (uint32_t i = 0; i < Q_prime_ack * Qm; i++) {
      q_bits[q->ack_ri_bits[i].position] = 0;
    }
  }

  // Deinterleave and decode RI bits
  if (cfg->uci_cfg.cqi.ri_len > 0) {
    float beta = get_beta_ri_offset(cfg->uci_offset.I_offset_ri);
    if (cfg->grant.tb.tbs == 0) {
      float beta_cqi = get_beta_cqi_offset(cfg->uci_offset.I_offset_cqi);
      if (!isnormal(beta_cqi)) {
        ERROR("Invalid beta_cqi (%d, %f)", cfg->uci_offset.I_offset_cqi, beta_cqi);
        return SRSRAN_ERROR;
      }
      beta /= beta_cqi;
    }
    ret = srsran_uci_decode_ack_ri(cfg,
                                   q_bits,
                                   c_seq,
                                   beta,
                                   nb_q / Qm,
                                   cqi_len,
                                   q->ack_ri_bits,
                                   &uci_data->ri,
                                   NULL,
                                   cfg->uci_cfg.cqi.ri_len,
                                   true);
    if (ret < 0) {
      return ret;
    }
    Q_prime_ri = (uint32_t)ret;
  }

  // Now set correct RI
  if (cfg->uci_cfg.cqi.data_enable) {
    if (cfg->uci_cfg.cqi.type == SRSRAN_CQI_TYPE_SUBBAND_HL && cfg->uci_cfg.cqi.ri_len) {
      cfg->uci_cfg.cqi.rank_is_not_one = uci_data->ri > 0;
    }
  }

  return Q_prime_ri;
}

int srsran_ulsch_decode(srsran_sch_t*       q,
                        srsran_pusch_cfg_t* cfg,
                        int16_t*            q_bits,
                        int16_t*            g_bits,
                        uint8_t*            c_seq,
                        uint8_t*            data,
                        srsran_uci_value_t* uci_data)
{
  int ret = SRSRAN_ERROR_INVALID_INPUTS;

  // Prepare cbsegm
  srsran_cbsegm_t cb_segm;
  if (srsran_cbsegm(&cb_segm, (uint32_t)cfg->grant.tb.tbs)) {
    ERROR("Error computing segmentation for TBS=%d", cfg->grant.tb.tbs);
    return SRSRAN_ERROR;
  }

  uint32_t nb_q = cfg->grant.tb.nof_bits;
  uint32_t Qm   = srsran_mod_bits_x_symbol(cfg->grant.tb.mod);

  cfg->K_segm = cb_segm.C1 * cb_segm.K1 + cb_segm.C2 * cb_segm.K2;

  // Decode RI/HARQ values
  if ((ret = uci_decode_ri_ack(q, cfg, q_bits, c_seq, uci_data)) < 0) {
    ERROR("Error decoding RI/HARQ bits");
    return SRSRAN_ERROR;
  }

  uint32_t Q_prime_ri = (uint32_t)ret;

  // Deinterleave data and CQI in ULSCH
  ulsch_deinterleave(q_bits,
                     Qm,
                     nb_q / Qm,
                     cfg->grant.nof_symb,
                     g_bits,
                     q->ack_ri_bits,
                     Q_prime_ri * Qm,
                     q->temp_g_bits,
                     q->ul_interleaver);

  // Decode CQI (multiplexed at the front of ULSCH)
  uint32_t Q_prime_cqi = 0;
  uint32_t e_offset    = 0;
  if (cfg->uci_cfg.cqi.data_enable) {
    uint32_t cqi_len = srsran_cqi_size(&cfg->uci_cfg.cqi);
    uint8_t  cqi_buff[SRSRAN_CQI_MAX_BITS];
    ZERO_OBJECT(cqi_buff);
    ret = srsran_uci_decode_cqi_pusch(&q->uci_cqi,
                                      cfg,
                                      g_bits,
                                      get_beta_cqi_offset(cfg->uci_offset.I_offset_cqi),
                                      Q_prime_ri,
                                      cqi_len,
                                      cqi_buff,
                                      &uci_data->cqi.data_crc);
    if (ret < 0) {
      return ret;
    }
    srsran_cqi_value_unpack(&cfg->uci_cfg.cqi, cqi_buff, &uci_data->cqi);
    Q_prime_cqi = (uint32_t)ret;
  }

  e_offset += Q_prime_cqi * Qm;

  // Decode ULSCH
  if (cb_segm.tbs > 0) {
    uint32_t G = nb_q / Qm - Q_prime_ri - Q_prime_cqi;
    ret        = decode_tb(q, cfg->softbuffers.rx, &cb_segm, Qm, cfg->grant.tb.rv, G * Qm, &g_bits[e_offset], data);
  }
  return ret;
}

int srsran_ulsch_encode(srsran_sch_t*       q,
                        srsran_pusch_cfg_t* cfg,
                        uint8_t*            data,
                        srsran_uci_value_t* uci_data,
                        uint8_t*            g_bits,
                        uint8_t*            q_bits)
{
  int ret;

  uint32_t e_offset    = 0;
  uint32_t Q_prime_cqi = 0;
  uint32_t Q_prime_ack = 0;
  uint32_t Q_prime_ri  = 0;

  uint32_t nb_q = cfg->grant.tb.nof_bits;
  uint32_t Qm   = srsran_mod_bits_x_symbol(cfg->grant.tb.mod);

  if (Qm == 0) {
    ERROR("Invalid input");
    return SRSRAN_ERROR_INVALID_INPUTS;
  }

  if (cfg->grant.nof_symb == 0) {
    ERROR("Invalid input");
    return SRSRAN_ERROR_INVALID_INPUTS;
  }

  // Prepare cbsegm
  srsran_cbsegm_t cb_segm;
  if (srsran_cbsegm(&cb_segm, (uint32_t)cfg->grant.tb.tbs)) {
    ERROR("Error computing segmentation for TBS=%d", cfg->grant.tb.tbs);
    return SRSRAN_ERROR;
  }

  cfg->K_segm = cb_segm.C1 * cb_segm.K1 + cb_segm.C2 * cb_segm.K2;

  int     uci_cqi_len = 0;
  uint8_t cqi_buff[SRSRAN_CQI_MAX_BITS];
  ZERO_OBJECT(cqi_buff);

  if (cfg->uci_cfg.cqi.data_enable) {
    uci_cqi_len = (uint32_t)srsran_cqi_value_pack(&cfg->uci_cfg.cqi, &uci_data->cqi, cqi_buff);
    if (uci_cqi_len < 0) {
      ERROR("Error encoding CQI bits");
      return SRSRAN_ERROR;
    }
  }

  if (cfg->uci_cfg.cqi.ri_len > 0) {
    float beta = get_beta_ri_offset(cfg->uci_offset.I_offset_ri);
    if (cb_segm.tbs == 0) {
      beta /= get_beta_cqi_offset(cfg->uci_offset.I_offset_cqi);
    }
    uint8_t ri[2] = {uci_data->ri, 0};
    ret           = srsran_uci_encode_ack_ri(cfg,
                                   ri,
                                   cfg->uci_cfg.cqi.ri_len,
                                   (uint32_t)uci_cqi_len,
                                   beta,
                                   nb_q / Qm,
                                   true,
                                   cfg->uci_cfg.ack[0].N_bundle,
                                   q->ack_ri_bits);
    if (ret < 0) {
      return ret;
    }
    Q_prime_ri = (uint32_t)ret;
  }

  // Encode CQI
  if (uci_cqi_len > 0) {
    ret = srsran_uci_encode_cqi_pusch(&q->uci_cqi,
                                      cfg,
                                      cqi_buff,
                                      (uint32_t)uci_cqi_len,
                                      get_beta_cqi_offset(cfg->uci_offset.I_offset_cqi),
                                      Q_prime_ri,
                                      q->temp_g_bits);
    if (ret < 0) {
      return ret;
    }
    Q_prime_cqi = (uint32_t)ret;

    srsran_bit_pack_vector(q->temp_g_bits, g_bits, Q_prime_cqi * Qm);

    // Reset the buffer because will be reused in ulsch_interleave
    srsran_vec_u8_zero(q->temp_g_bits, Q_prime_cqi * Qm);
  }

  e_offset += Q_prime_cqi * Qm;

  // Encode UL-SCH
  if (cb_segm.tbs > 0) {
    uint32_t G = nb_q / Qm - Q_prime_ri - Q_prime_cqi;
    ret        = encode_tb_off(
        q, cfg->softbuffers.tx, &cb_segm, Qm, cfg->grant.tb.rv, G * Qm, data, &g_bits[e_offset / 8], e_offset % 8);
    if (ret) {
      return ret;
    }
  }

  // Interleave UL-SCH (and RI and CQI)
  ulsch_interleave(g_bits, Qm, nb_q / Qm, cfg->grant.nof_symb, q_bits, q->ack_ri_bits, Q_prime_ri * Qm, q->temp_g_bits);

  // Encode (and interleave) ACK
  if (srsran_uci_cfg_total_ack(&cfg->uci_cfg) > 0) {
    float beta = get_beta_harq_offset(cfg->uci_offset.I_offset_ack);
    if (cb_segm.tbs == 0) {
      beta /= get_beta_cqi_offset(cfg->uci_offset.I_offset_cqi);
    }
    ret = srsran_uci_encode_ack_ri(cfg,
                                   uci_data->ack.ack_value,
                                   srsran_uci_cfg_total_ack(&cfg->uci_cfg),
                                   (uint32_t)uci_cqi_len,
                                   beta,
                                   nb_q / Qm,
                                   false,
                                   cfg->uci_cfg.ack[0].N_bundle,
                                   &q->ack_ri_bits[Q_prime_ri * Qm]);
    if (ret < 0) {
      return ret;
    }
    Q_prime_ack = (uint32_t)ret;
  }

  uint32_t nof_ri_ack_bits = (Q_prime_ack + Q_prime_ri) * Qm;

  for (uint32_t i = 0; i < nof_ri_ack_bits; i++) {
    uint32_t p = q->ack_ri_bits[i].position;
    if (p < nb_q) {
      if (q->ack_ri_bits[i].type == UCI_BIT_1) {
        q_bits[p / 8] |= (1U << (7 - p % 8));
      } else {
        q_bits[p / 8] &= ~(1U << (7 - p % 8));
      }
    } else {
      ERROR("Invalid RI/ACK bit %d/%d, position %d. Max bits=%d, Qm=%d", i, nof_ri_ack_bits, p, nb_q, Qm);
    }
  }

  INFO("Q_prime_ack=%d, Q_prime_cqi=%d, Q_prime_ri=%d", Q_prime_ack, Q_prime_cqi, Q_prime_ri);

  return nof_ri_ack_bits;
}

void srsran_sl_ulsch_interleave(uint8_t* g_bits,
                                uint32_t Qm,
                                uint32_t H_prime_total,
                                uint32_t N_pusch_symbs,
                                uint8_t* q_bits)
{
  ulsch_interleave(g_bits, Qm, H_prime_total, N_pusch_symbs, q_bits, NULL, 0, false);
}

void srsran_sl_ulsch_deinterleave(int16_t*  q_bits,
                                  uint32_t  Qm,
                                  uint32_t  H_prime_total,
                                  uint32_t  N_pusch_symbs,
                                  int16_t*  g_bits,
                                  uint32_t* inteleaver_lut)
{
  ulsch_deinterleave(q_bits, Qm, H_prime_total, N_pusch_symbs, g_bits, NULL, 0, NULL, inteleaver_lut);
}
