/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*! \file PHY/LTE_TRANSPORT/ulsch_decoding.c
* \brief Top-level routines for decoding  the ULSCH transport channel from 36.212 V8.6 2009-03
* \author R. Knopp
* \date 2011
* \version 0.1
* \company Eurecom
* \email: knopp@eurecom.fr
* \note
* \warning
*/

//#include "defs.h"

#include "PHY/defs.h"
#include "PHY/extern.h"
#include "PHY/CODING/extern.h"
#include "extern.h"
#include "SCHED/extern.h"
#ifdef OPENAIR2
#include "LAYER2/MAC/defs.h"
#include "LAYER2/MAC/extern.h"
#include "RRC/LITE/extern.h"
#include "PHY_INTERFACE/extern.h"
#endif

#ifdef PHY_ABSTRACTION
#include "UTIL/OCG/OCG.h"
#include "UTIL/OCG/OCG_extern.h"
#endif

#include "UTIL/LOG/vcd_signal_dumper.h"
#include <thread-pool.h>
//#define DEBUG_ULSCH_DECODING

void free_eNB_ulsch(LTE_eNB_ULSCH_t *ulsch)
{

    if (ulsch) {
        for (int i=0; i<8; i++) {
            if (ulsch->harq_processes[i]) {
                if (ulsch->harq_processes[i]->b) {
                    free16(ulsch->harq_processes[i]->b,MAX_ULSCH_PAYLOAD_BYTES);
                    ulsch->harq_processes[i]->b = NULL;
                }
                free16(ulsch->harq_processes[i],sizeof(LTE_UL_eNB_HARQ_t));
                ulsch->harq_processes[i] = NULL;
            }
        }

        free16(ulsch,sizeof(LTE_eNB_ULSCH_t));
        ulsch = NULL;
    }
}

LTE_eNB_ULSCH_t *new_eNB_ulsch(uint8_t max_turbo_iterations,uint8_t N_RB_UL, uint8_t abstraction_flag)
{

    LTE_eNB_ULSCH_t *ulsch;
    uint8_t exit_flag = 0;
    unsigned char bw_scaling;

  switch (N_RB_UL) {
  case 6:
    bw_scaling =16;
    break;

  case 25:
    bw_scaling =4;
    break;

  case 50:
    bw_scaling =2;
    break;

  default:
    bw_scaling =1;
    break;
  }

  ulsch = (LTE_eNB_ULSCH_t *)malloc16(sizeof(LTE_eNB_ULSCH_t));

  if (ulsch) {
    memset(ulsch,0,sizeof(LTE_eNB_ULSCH_t));
    ulsch->max_turbo_iterations = max_turbo_iterations;
        ulsch->Mlimit = 4;

        for (int i=0; i<8; i++) {
            //      printf("new_ue_ulsch: Harq process %d\n",i);
            ulsch->harq_processes[i] = (LTE_UL_eNB_HARQ_t *)malloc16(sizeof(LTE_UL_eNB_HARQ_t));

            if (ulsch->harq_processes[i]) {
                memset(ulsch->harq_processes[i],0,sizeof(LTE_UL_eNB_HARQ_t));
                ulsch->harq_processes[i]->b = (uint8_t*)malloc16(MAX_ULSCH_PAYLOAD_BYTES/bw_scaling);

                if (ulsch->harq_processes[i]->b)
                    memset(ulsch->harq_processes[i]->b,0,MAX_ULSCH_PAYLOAD_BYTES/bw_scaling);
                else
                    exit_flag=3;
            } else {
                exit_flag=1;
            }
        }

        if (exit_flag==0)
            return(ulsch);
    }

  LOG_E(PHY,"new_ue_ulsch: exit_flag = %d\n",exit_flag);
  free_eNB_ulsch(ulsch);

  return(NULL);
}

void clean_eNb_ulsch(LTE_eNB_ULSCH_t *ulsch)
{

  unsigned char i;

  //ulsch = (LTE_eNB_ULSCH_t *)malloc16(sizeof(LTE_eNB_ULSCH_t));
  if (ulsch) {
    ulsch->rnti = 0;

    for (i=0; i<8; i++) {
      if (ulsch->harq_processes[i]) {
        //    ulsch->harq_processes[i]->Ndi = 0;
        ulsch->harq_processes[i]->status = 0;
        //ulsch->harq_processes[i]->phich_active = 0; //this will be done later after transmission of PHICH
        ulsch->harq_processes[i]->phich_ACK = 0;
        ulsch->harq_processes[i]->round = 0;
        ulsch->harq_processes[i]->rar_alloc = 0;
        ulsch->harq_processes[i]->first_rb = 0;
        ulsch->harq_processes[i]->nb_rb = 0;
        ulsch->harq_processes[i]->TBS = 0;
        ulsch->harq_processes[i]->Or1 = 0;
        ulsch->harq_processes[i]->Or2 = 0;
        for ( int j = 0; j < 2; j++ ) {
          ulsch->harq_processes[i]->o_RI[j] = 0;
        }
        ulsch->harq_processes[i]->O_ACK = 0;
        ulsch->harq_processes[i]->srs_active = 0;
        ulsch->harq_processes[i]->rvidx = 0;
        ulsch->harq_processes[i]->Msc_initial = 0;
        ulsch->harq_processes[i]->Nsymb_initial = 0;
      }
    }
    ulsch->beta_offset_cqi_times8 = 0;
    ulsch->beta_offset_ri_times8 = 0;
    ulsch->beta_offset_harqack_times8 = 0;
    ulsch->Msg3_active = 0;
  }
}


uint8_t extract_cqi_crc(uint8_t *cqi,uint8_t CQI_LENGTH)
{

  uint8_t crc;

  crc = cqi[CQI_LENGTH>>3];
  //  printf("crc1: %x, shift %d\n",crc,CQI_LENGTH&0x7);
  crc = (crc<<(CQI_LENGTH&0x7));
  // clear crc bits
  //  ((char *)cqi)[CQI_LENGTH>>3] &= 0xff>>(8-(CQI_LENGTH&0x7));
  //  printf("crc2: %x, cqi0 %x\n",crc,cqi[1+(CQI_LENGTH>>3)]);
  crc |= (cqi[1+(CQI_LENGTH>>3)])>>(8-(CQI_LENGTH&0x7));
  // clear crc bits
  //(((char *)cqi)[1+(CQI_LENGTH>>3)]) = 0;

  //  printf("crc : %x\n",crc);
  return(crc);

}






int ulsch_decoding_data_2thread0(td_params* tdp) {

  PHY_VARS_eNB *eNB = tdp->eNB;
  int UE_id         = tdp->UE_id;
  int harq_pid      = tdp->harq_pid;
  int llr8_flag     = tdp->llr8_flag;

  unsigned int r,r_offset=0,Kr,Kr_bytes,iind;
  uint8_t crc_type;
  int offset = 0;
  int ret = 1;
  int16_t dummy_w[MAX_NUM_ULSCH_SEGMENTS][3*(6144+64)];
  LTE_eNB_ULSCH_t *ulsch = eNB->ulsch[UE_id];
  LTE_UL_eNB_HARQ_t *ulsch_harq = ulsch->harq_processes[harq_pid];
  int Q_m = ulsch_harq->Qm;
  int G = ulsch_harq->G;
  uint32_t E=0;
  uint32_t Gp,GpmodC,Nl=1;
  uint32_t C = ulsch_harq->C;

    turboDecoder * tc=llr8_flag?
                      phy_threegpplte_turbo_decoder8:
                      phy_threegpplte_turbo_decoder16 ;

  // go through first half of segments to get r_offset
  for (r=0; r<(ulsch_harq->C/2); r++) {

    // Get Turbo interleaver parameters
    if (r<ulsch_harq->Cminus)
      Kr = ulsch_harq->Kminus;
    else
      Kr = ulsch_harq->Kplus;

    Kr_bytes = Kr>>3;

    if (Kr_bytes<=64)
      iind = (Kr_bytes-5);
    else if (Kr_bytes <=128)
      iind = 59 + ((Kr_bytes-64)>>1);
    else if (Kr_bytes <= 256)
      iind = 91 + ((Kr_bytes-128)>>2);
    else if (Kr_bytes <= 768)
      iind = 123 + ((Kr_bytes-256)>>3);
    else {
      AssertFatal(1==0,"ulsch_decoding: Illegal codeword size %d!!!\n",Kr_bytes);
    }

    // This is stolen from rate-matching algorithm to get the value of E
    
    Gp = G/Nl/Q_m;
    GpmodC = Gp%C;
    
    if (r < (C-(GpmodC)))
      E = Nl*Q_m * (Gp/C);
    else
      E = Nl*Q_m * ((GpmodC==0?0:1) + (Gp/C));
    
    r_offset += E;

    if (r==0) {
      offset = Kr_bytes - (ulsch_harq->F>>3) - ((ulsch_harq->C>1)?3:0);
    } else {
      offset += (Kr_bytes- ((ulsch_harq->C>1)?3:0));
    }
  }

  // go through second half of segments
  for (; r<(ulsch_harq->C); r++) {


    //    printf("before subblock deinterleaving c[%d] = %p\n",r,ulsch_harq->c[r]);
    // Get Turbo interleaver parameters
    if (r<ulsch_harq->Cminus)
      Kr = ulsch_harq->Kminus;
    else
      Kr = ulsch_harq->Kplus;

    Kr_bytes = Kr>>3;

    if (Kr_bytes<=64)
      iind = (Kr_bytes-5);
    else if (Kr_bytes <=128)
      iind = 59 + ((Kr_bytes-64)>>1);
    else if (Kr_bytes <= 256)
      iind = 91 + ((Kr_bytes-128)>>2);
    else if (Kr_bytes <= 768)
      iind = 123 + ((Kr_bytes-256)>>3);
    else {
      LOG_E(PHY,"ulsch_decoding: Illegal codeword size %d!!!\n",Kr_bytes);
      return(-1);
    }

#ifdef DEBUG_ULSCH_DECODING
    printf("f1 %d, f2 %d, F %d\n",f1f2mat_old[2*iind],f1f2mat_old[1+(2*iind)],(r==0) ? ulsch_harq->F : 0);
#endif

    memset(&dummy_w[r][0],0,3*(6144+64)*sizeof(short));
    ulsch_harq->RTC[r] = generate_dummy_w(4+(Kr_bytes*8),
                                          (uint8_t*)&dummy_w[r][0],
                                          (r==0) ? ulsch_harq->F : 0);

#ifdef DEBUG_ULSCH_DECODING
    printf("Rate Matching Segment %d (coded bits (G) %d,unpunctured/repeated bits %d, Q_m %d, nb_rb %d, Nl %d)...\n",
        r, G,
        Kr*3,
        Q_m,
        nb_rb,
        ulsch_harq->Nl);
#endif


    if (lte_rate_matching_turbo_rx(ulsch_harq->RTC[r],
                                   G,
                                   ulsch_harq->w[r],
                                   (uint8_t*) &dummy_w[r][0],
                                   ulsch_harq->e+r_offset,
                                   ulsch_harq->C,
                                   NSOFT,
                                   0,   //Uplink
                                   1,
                                   ulsch_harq->rvidx,
                                   (ulsch_harq->round==0)?1:0,  // clear
                                   ulsch_harq->Qm,
                                   1,
                                   r,
                                   &E)==-1) {
      LOG_E(PHY,"ulsch_decoding.c: Problem in rate matching\n");
      return(-1);
    }

    r_offset += E;

        int16_t soft_bits[3*8*6144+12+96] __attribute__((aligned(32)));
        sub_block_deinterleaving_turbo(4+Kr,
                                       soft_bits+96,
                                       ulsch_harq->w[r]);

        if (ulsch_harq->C == 1)
            crc_type = CRC24_A;
        else
            crc_type = CRC24_B;


        uint8_t decoded_bytes[3+768] __attribute__((aligned(32)));
        ret = tc(soft_bits+96,
                 decoded_bytes,
	     Kr,
	     f1f2mat_old[iind*2],
	     f1f2mat_old[(iind*2)+1],
	     ulsch->max_turbo_iterations,//MAX_TURBO_ITERATIONS,
	     crc_type,
	     (r==0) ? ulsch_harq->F : 0,
	     &eNB->ulsch_tc_init_stats,
	     &eNB->ulsch_tc_alpha_stats,
	     &eNB->ulsch_tc_beta_stats,
	     &eNB->ulsch_tc_gamma_stats,
	     &eNB->ulsch_tc_ext_stats,
	     &eNB->ulsch_tc_intl1_stats,
	     &eNB->ulsch_tc_intl2_stats);
    
    // Reassembly of Transport block here

    if (ret != (1+ulsch->max_turbo_iterations)) {
      if (r<ulsch_harq->Cminus)
	Kr = ulsch_harq->Kminus;
      else
	Kr = ulsch_harq->Kplus;
      
      Kr_bytes = Kr>>3;
      
      memcpy(ulsch_harq->b+offset,
                   decoded_bytes,
	     Kr_bytes - ((ulsch_harq->C>1)?3:0));
      offset += (Kr_bytes- ((ulsch_harq->C>1)?3:0));
      
      
    } else {
      break;
    }
    
  }

  return(ret);
}

extern int oai_exit;
void *td_thread(void *param) {
  pthread_setname_np( pthread_self(), "td processing");
  PHY_VARS_eNB *eNB = ((td_params*)param)->eNB;
  eNB_proc_t *proc  = &eNB->proc;

  while (!oai_exit) {

    if (wait_on_condition(&proc->mutex_td,&proc->cond_td,&proc->instance_cnt_td,"td thread")<0) break;  

    ((td_params*)param)->ret = ulsch_decoding_data_2thread0((td_params*)param);

    if (release_thread(&proc->mutex_td,&proc->instance_cnt_td,"td thread")<0) break;

    if (pthread_cond_signal(&proc->cond_td) != 0) {
      printf("[eNB] ERROR pthread_cond_signal for td thread exit\n");
      exit_fun( "ERROR pthread_cond_signal" );
      return(NULL);
    }
  }

  return(NULL);
}

int ulsch_decoding_data_2thread(PHY_VARS_eNB *eNB,int UE_id,int harq_pid,int llr8_flag) {

  eNB_proc_t *proc = &eNB->proc;
  unsigned int r,r_offset=0,Kr,Kr_bytes,iind;
  uint8_t crc_type;
  int offset = 0;
  int ret = 1;
  int16_t dummy_w[MAX_NUM_ULSCH_SEGMENTS][3*(6144+64)];
  LTE_eNB_ULSCH_t *ulsch = eNB->ulsch[UE_id];
  LTE_UL_eNB_HARQ_t *ulsch_harq = ulsch->harq_processes[harq_pid];

  int G = ulsch_harq->G;
  unsigned int E;
  int Cby2;

    struct timespec wait;

    wait.tv_sec=0;
    wait.tv_nsec=5000000L;
    turboDecoder * tc=llr8_flag?
                      phy_threegpplte_turbo_decoder8:
                      phy_threegpplte_turbo_decoder16 ;

  if (ulsch_harq->C>1) { // wakeup worker if more than 1 segment
    if (pthread_mutex_timedlock(&proc->mutex_td,&wait) != 0) {
      printf("[eNB] ERROR pthread_mutex_lock for TD thread (IC %d)\n", proc->instance_cnt_td);
      exit_fun( "error locking mutex_fep" );
      return -1;
    }
    
    if (proc->instance_cnt_td==0) {
      printf("[eNB] TD thread busy\n");
      exit_fun("TD thread busy");
      pthread_mutex_unlock( &proc->mutex_td );
      return -1;
    }
    
    ++proc->instance_cnt_td;
    
    proc->tdp.eNB       = eNB;
    proc->tdp.UE_id     = UE_id;
    proc->tdp.harq_pid  = harq_pid;
    proc->tdp.llr8_flag = llr8_flag;
    
    
    // wakeup worker to do second half segments 
    if (pthread_cond_signal(&proc->cond_td) != 0) {
      printf("[eNB] ERROR pthread_cond_signal for td thread exit\n");
      exit_fun( "ERROR pthread_cond_signal" );
      return (1+ulsch->max_turbo_iterations);
    }

    pthread_mutex_unlock( &proc->mutex_td );
    Cby2 = ulsch_harq->C/2;
  }
  else {
    Cby2 = 1;
  }

  // go through first half of segments in main thread
  for (r=0; r<Cby2; r++) {

    //    printf("before subblock deinterleaving c[%d] = %p\n",r,ulsch_harq->c[r]);
    // Get Turbo interleaver parameters
    if (r<ulsch_harq->Cminus)
      Kr = ulsch_harq->Kminus;
    else
      Kr = ulsch_harq->Kplus;

    Kr_bytes = Kr>>3;

    if (Kr_bytes<=64)
      iind = (Kr_bytes-5);
    else if (Kr_bytes <=128)
      iind = 59 + ((Kr_bytes-64)>>1);
    else if (Kr_bytes <= 256)
      iind = 91 + ((Kr_bytes-128)>>2);
    else if (Kr_bytes <= 768)
      iind = 123 + ((Kr_bytes-256)>>3);
    else {
      LOG_E(PHY,"ulsch_decoding: Illegal codeword size %d!!!\n",Kr_bytes);
      return(-1);
    }

#ifdef DEBUG_ULSCH_DECODING
    printf("f1 %d, f2 %d, F %d\n",f1f2mat_old[2*iind],f1f2mat_old[1+(2*iind)],(r==0) ? ulsch_harq->F : 0);
#endif

    memset(&dummy_w[r][0],0,3*(6144+64)*sizeof(short));
    ulsch_harq->RTC[r] = generate_dummy_w(4+(Kr_bytes*8),
                                          (uint8_t*)&dummy_w[r][0],
                                          (r==0) ? ulsch_harq->F : 0);

#ifdef DEBUG_ULSCH_DECODING
    printf("Rate Matching Segment %d (coded bits (G) %d,unpunctured/repeated bits %d, Q_m %d, nb_rb %d, Nl %d)...\n",
        r, G,
        Kr*3,
        Q_m,
        nb_rb,
        ulsch_harq->Nl);
#endif

    start_meas(&eNB->ulsch_rate_unmatching_stats);

    if (lte_rate_matching_turbo_rx(ulsch_harq->RTC[r],
                                   G,
                                   ulsch_harq->w[r],
                                   (uint8_t*) &dummy_w[r][0],
                                   ulsch_harq->e+r_offset,
                                   ulsch_harq->C,
                                   NSOFT,
                                   0,   //Uplink
                                   1,
                                   ulsch_harq->rvidx,
                                   (ulsch_harq->round==0)?1:0,  // clear
                                   ulsch_harq->Qm,
                                   1,
                                   r,
                                   &E)==-1) {
      LOG_E(PHY,"ulsch_decoding.c: Problem in rate matching\n");
      return(-1);
    }

    stop_meas(&eNB->ulsch_rate_unmatching_stats);
        r_offset += E;

        int16_t soft_bits[3*8*6144+12+96] __attribute__((aligned(32)));
        start_meas(&eNB->ulsch_deinterleaving_stats);
        sub_block_deinterleaving_turbo(4+Kr,
                                       soft_bits+96,
                                       ulsch_harq->w[r]);
        stop_meas(&eNB->ulsch_deinterleaving_stats);

        if (ulsch_harq->C == 1)
            crc_type = CRC24_A;
        else
            crc_type = CRC24_B;

        start_meas(&eNB->ulsch_turbo_decoding_stats);

        uint8_t decoded_bytes[3+768] __attribute__((aligned(32)));
        ret = tc(soft_bits+96,
                 decoded_bytes,
                 Kr,
                 f1f2mat_old[iind*2],
                 f1f2mat_old[(iind*2)+1],
                 ulsch->max_turbo_iterations,//MAX_TURBO_ITERATIONS,
                 crc_type,
                 (r==0) ? ulsch_harq->F : 0,
                 &eNB->ulsch_tc_init_stats,
                 &eNB->ulsch_tc_alpha_stats,
                 &eNB->ulsch_tc_beta_stats,
                 &eNB->ulsch_tc_gamma_stats,
                 &eNB->ulsch_tc_ext_stats,
                 &eNB->ulsch_tc_intl1_stats,
                 &eNB->ulsch_tc_intl2_stats);

        // Reassembly of Transport block here

        if (ret != (1+ulsch->max_turbo_iterations)) {
            if (r<ulsch_harq->Cminus)
                Kr = ulsch_harq->Kminus;
            else
                Kr = ulsch_harq->Kplus;

            Kr_bytes = Kr>>3;

            if (r==0) {
                memcpy(ulsch_harq->b,
                       decoded_bytes+(ulsch_harq->F>>3),
                       Kr_bytes - (ulsch_harq->F>>3) - ((ulsch_harq->C>1)?3:0));
	offset = Kr_bytes - (ulsch_harq->F>>3) - ((ulsch_harq->C>1)?3:0);
      } else {
	memcpy(ulsch_harq->b+offset,
                       decoded_bytes,
	       Kr_bytes - ((ulsch_harq->C>1)?3:0));
	offset += (Kr_bytes- ((ulsch_harq->C>1)?3:0));
      }
      
    } else {
      break;
    }
    stop_meas(&eNB->ulsch_turbo_decoding_stats);    
  }

   // wait for worker to finish

  wait_on_busy_condition(&proc->mutex_td,&proc->cond_td,&proc->instance_cnt_td,"td thread");  

  return( (ret>proc->tdp.ret) ? ret : proc->tdp.ret );
}

request_t * ulsch_decoding_data(PHY_VARS_eNB *eNB,int UE_id,int harq_pid,int llr8_flag, int frame, int subframe) {
    request_t * req=NULL;
    unsigned int r_offset=0;
    int offset = 0;
    LTE_UL_eNB_HARQ_t *ulsch_harq = eNB->ulsch[UE_id]->harq_processes[harq_pid];
    int G = ulsch_harq->G;
    unsigned int E;
    turboDecoder * td=llr8_flag?
                      phy_threegpplte_turbo_decoder8:
                      phy_threegpplte_turbo_decoder16 ;

    for (int r=0; r<ulsch_harq->C; r++) {
        // Get Turbo interleaver parameters
        int Kr=r<ulsch_harq->Cminus?ulsch_harq->Kminus:ulsch_harq->Kplus;
        int Kr_bytes = Kr>>3;
        int16_t dummy_w[MAX_NUM_ULSCH_SEGMENTS][3*(6144+64)];
        memset(dummy_w, 0, sizeof(dummy_w));
        ulsch_harq->RTC[r] = generate_dummy_w(4+(Kr_bytes*8),
                                              (uint8_t*)&dummy_w[r][0],
                                              (r==0) ? ulsch_harq->F : 0);
        start_meas(&eNB->ulsch_rate_unmatching_stats);
        if (lte_rate_matching_turbo_rx(ulsch_harq->RTC[r],
                                       G,
                                       ulsch_harq->w[r],
                                       (uint8_t*) &dummy_w[r][0],
                                       ulsch_harq->e+r_offset,
                                       ulsch_harq->C,
                                       NSOFT,
                                       0,   //Uplink
                                       1,
                                       ulsch_harq->rvidx,
                                       (ulsch_harq->round==0)?1:0,  // clear
                                       ulsch_harq->Qm,
                                       1,
                                       r,
                                       &E)==-1) {
            LOG_E(PHY,"ulsch_decoding.c: Problem in rate matching\n");
            return NULL;
        }

        stop_meas(&eNB->ulsch_rate_unmatching_stats);
        r_offset += E;
        start_meas(&eNB->ulsch_deinterleaving_stats);
        req=createRequest(DECODE,sizeof(turboDecode_t));
        union turboReqUnion id= {.s={eNB->ulsch[UE_id]->rnti,frame,subframe,r,0}};
        req->id= id.p;
        turboDecode_t * rdata=(turboDecode_t *) req->data;
        sub_block_deinterleaving_turbo(4+Kr,
                                       rdata->soft_bits+96,
                                       ulsch_harq->w[r]);
        stop_meas(&eNB->ulsch_deinterleaving_stats);
        start_meas(&eNB->ulsch_turbo_decoding_stats);
        if (Kr_bytes<=64)
            rdata->iind = (Kr_bytes-5);
        else if (Kr_bytes <=128)
            rdata->iind = 59 + ((Kr_bytes-64)>>1);
        else if (Kr_bytes <= 256)
            rdata->iind = 91 + ((Kr_bytes-128)>>2);
        else if (Kr_bytes <= 768)
            rdata->iind = 123 + ((Kr_bytes-256)>>3);
        else {
            LOG_E(PHY,"ulsch_decoding: Illegal codeword size %d!!!\n",Kr_bytes);
            return NULL;
        }

        rdata->frame=frame;
        rdata->subframe=subframe;
        rdata->UEid=UE_id;
        rdata->harq_pid=harq_pid;
        rdata->Kr=Kr;
        rdata->maxIterations=eNB->ulsch[UE_id]->max_turbo_iterations;
        rdata->ulsch_harq=ulsch_harq;
        rdata->eNB=eNB;
        rdata->nbSegments=ulsch_harq->C;
        rdata->segment_r=r;
        rdata->Fbits=(r==0) ? ulsch_harq->F : 0;
        rdata->offset=offset;
        rdata->function=td;
        int Fbytes=rdata->Fbits>>3;
        int blockSize=Kr_bytes - Fbytes - (rdata->nbSegments>1?3:0);
        if ( eNB->proc.threadPool.activated) {
            add_request(req, &eNB->proc.threadPool);
            req=NULL;
        } else {
            req->creationTime=req->startProcessingTime=rdtsc();
            rdata->decodeIterations = td( rdata->soft_bits+96,
                                          rdata->decoded_bytes,
                                          rdata->Kr,
                                          f1f2mat_old[rdata->iind*2],
                                          f1f2mat_old[(rdata->iind*2)+1],
                                          rdata->maxIterations,
                                          rdata->nbSegments == 1 ? CRC24_A: CRC24_B,
                                          rdata->Fbits,
                                          &eNB->ulsch_tc_init_stats,
                                          &eNB->ulsch_tc_alpha_stats,
                                          &eNB->ulsch_tc_beta_stats,
                                          &eNB->ulsch_tc_gamma_stats,
                                          &eNB->ulsch_tc_ext_stats,
                                          &eNB->ulsch_tc_intl1_stats,
                                          &eNB->ulsch_tc_intl2_stats);

            stop_meas(&eNB->ulsch_turbo_decoding_stats);
            req->returnTime=req->endProcessingTime=rdtsc();
            req->decodeIterations=rdata->decodeIterations;
            req->coreId=0;
            req->processedBy[0]=0;
	    req->next=eNB->proc.threadPool.doneRequests;
	    eNB->proc.threadPool.doneRequests=req;
	    
            if (rdata->decodeIterations > eNB->ulsch[UE_id]->max_turbo_iterations ) 
                // Entire TPU need retransmission
                break;
        }
        offset += blockSize;
    }
    return NULL;
}

static inline unsigned int lte_gold_unscram(unsigned int *x1, unsigned int *x2, unsigned char reset) __attribute__((always_inline));
static inline unsigned int lte_gold_unscram(unsigned int *x1, unsigned int *x2, unsigned char reset)
{
  int n;

  if (reset) {
    *x1 = 1+ (1<<31);
    *x2=*x2 ^ ((*x2 ^ (*x2>>1) ^ (*x2>>2) ^ (*x2>>3))<<31);

    // skip first 50 double words (1600 bits)
    //      printf("n=0 : x1 %x, x2 %x\n",x1,x2);
    for (n=1; n<50; n++) {
      *x1 = (*x1>>1) ^ (*x1>>4);
      *x1 = *x1 ^ (*x1<<31) ^ (*x1<<28);
      *x2 = (*x2>>1) ^ (*x2>>2) ^ (*x2>>3) ^ (*x2>>4);
      *x2 = *x2 ^ (*x2<<31) ^ (*x2<<30) ^ (*x2<<29) ^ (*x2<<28);
    }
  }

  *x1 = (*x1>>1) ^ (*x1>>4);
  *x1 = *x1 ^ (*x1<<31) ^ (*x1<<28);
  *x2 = (*x2>>1) ^ (*x2>>2) ^ (*x2>>3) ^ (*x2>>4);
  *x2 = *x2 ^ (*x2<<31) ^ (*x2<<30) ^ (*x2<<29) ^ (*x2<<28);
  return(*x1^*x2);
  //  printf("n=%d : c %x\n",n,x1^x2);

}

request_t*  ulsch_decoding(PHY_VARS_eNB *eNB,
                           eNB_rxtx_proc_t *proc,
                           uint8_t UE_id,
                           uint8_t control_only_flag,
                           uint8_t Nbundled,
                           uint8_t llr8_flag,
                           int frame,
                           int subframe)
{


    int16_t *ulsch_llr = eNB->pusch_vars[UE_id]->llr;
    LTE_DL_FRAME_PARMS *frame_parms = &eNB->frame_parms;
    LTE_eNB_ULSCH_t *ulsch = eNB->ulsch[UE_id];
    uint8_t harq_pid;
    unsigned short nb_rb;
    unsigned int A;
    uint8_t Q_m;
    unsigned int i,i2,q,j,j2;
    int iprime;

    //  uint8_t dummy_channel_output[(3*8*block_length)+12];
    int r,Kr;

    uint8_t *columnset;
    unsigned int sumKr=0;
    unsigned int Qprime,L,G,Q_CQI,Q_RI,H,Hprime,Hpp,Cmux,Rmux_prime,O_RCC;
    unsigned int Qprime_ACK,Qprime_RI,len_ACK=0,len_RI=0;
    //  uint8_t q_ACK[MAX_ACK_PAYLOAD],q_RI[MAX_RI_PAYLOAD];
    int metric,metric_new;
    uint32_t x1, x2, s=0;
    int16_t ys,c;
    uint32_t wACK_idx;
    uint8_t dummy_w_cc[3*(MAX_CQI_BITS+8+32)];
    int16_t y[6*14*1200] __attribute__((aligned(32)));
    uint8_t ytag[14*1200];
    //  uint8_t ytag2[6*14*1200],*ytag2_ptr;
    int16_t cseq[6*14*1200] __attribute__((aligned(32)));
    int off;

    LTE_UL_eNB_HARQ_t *ulsch_harq;



    harq_pid = subframe2harq_pid(frame_parms,proc->frame_rx,subframe);

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_ULSCH_DECODING0+harq_pid,1);

  // x1 is set in lte_gold_generic
  x2 = ((uint32_t)ulsch->rnti<<14) + ((uint32_t)subframe<<9) + frame_parms->Nid_cell; //this is c_init in 36.211 Sec 6.3.1
  ulsch_harq = ulsch->harq_processes[harq_pid];

    AssertFatal(harq_pid<8,
              "FATAL ERROR: illegal harq_pid, returning\n");

  AssertFatal(ulsch_harq->Nsymb_pusch != 0,
              "FATAL ERROR: harq_pid %d, Nsymb 0!\n",harq_pid);


  nb_rb = ulsch_harq->nb_rb;

  A = ulsch_harq->TBS;


  Q_m = ulsch_harq->Qm;
  G = nb_rb * (12 * Q_m) * ulsch_harq->Nsymb_pusch;


  //#ifdef DEBUG_ULSCH_DECODING
  LOG_D(PHY,"[PUSCH %d] Frame %d, Subframe %d: ulsch_decoding (Nid_cell %d, rnti %x, x2 %x): A %d, round %d, RV %d, O_r1 %d, O_RI %d, O_ACK %d, G %d, Q_m %d Nsymb_pusch %d nb_rb %d\n",
      harq_pid,
	proc->frame_rx,subframe,
	frame_parms->Nid_cell,ulsch->rnti,x2,
	A,
	ulsch_harq->round,
	ulsch_harq->rvidx,
	ulsch_harq->Or1,
	ulsch_harq->O_RI,
	ulsch_harq->O_ACK,
	G,
        ulsch_harq->Qm,
        ulsch_harq->Nsymb_pusch,
        nb_rb);
	
  //#endif

  if (ulsch_harq->round == 0) {
    // This is a new packet, so compute quantities regarding segmentation
    ulsch_harq->B = A+24;
    lte_segmentation(NULL,
                     NULL,
                     ulsch_harq->B,
                     &ulsch_harq->C,
                     &ulsch_harq->Cplus,
                     &ulsch_harq->Cminus,
                     &ulsch_harq->Kplus,
                     &ulsch_harq->Kminus,
                     &ulsch_harq->F);
    //  CLEAR LLR's HERE for first packet in process
  }

  //  printf("after segmentation c[%d] = %p\n",0,ulsch_harq->c[0]);

  sumKr = 0;

  for (r=0; r<ulsch_harq->C; r++) {
    if (r<ulsch_harq->Cminus)
      Kr = ulsch_harq->Kminus;
    else
      Kr = ulsch_harq->Kplus;

    sumKr += Kr;
  }

  AssertFatal(sumKr>0,
	      "[eNB] ulsch_decoding.c: FATAL sumKr is 0! (Nid_cell %d, rnti %x, x2 %x): harq_pid %d round %d, RV %d, O_RI %d, O_ACK %d, G %d, subframe %d\n",
	      frame_parms->Nid_cell,ulsch->rnti,x2,
	      harq_pid,
	      ulsch_harq->round,
	      ulsch_harq->rvidx,
	      ulsch_harq->O_RI,
	      ulsch_harq->O_ACK,
	      G,
	      subframe);


  // Compute Q_ri
  Qprime = ulsch_harq->O_RI*ulsch_harq->Msc_initial*ulsch_harq->Nsymb_initial * ulsch->beta_offset_ri_times8;

  if (Qprime > 0 ) {
    if ((Qprime % (8*sumKr)) > 0)
      Qprime = 1+(Qprime/(8*sumKr));
    else
      Qprime = Qprime/(8*sumKr);

    if (Qprime > 4*nb_rb * 12)
      Qprime = 4*nb_rb * 12;
  }

  Q_RI = Q_m*Qprime;
  Qprime_RI = Qprime;


  // Compute Q_ack

  Qprime = ulsch_harq->O_ACK*ulsch_harq->Msc_initial*ulsch_harq->Nsymb_initial * ulsch->beta_offset_harqack_times8;

  if (Qprime > 0) {
    if ((Qprime % (8*sumKr)) > 0)
      Qprime = 1+(Qprime/(8*sumKr));
    else
      Qprime = Qprime/(8*sumKr);

    if (Qprime > (4*nb_rb * 12))
      Qprime = 4*nb_rb * 12;
  }

  //  Q_ACK = Qprime * Q_m;
  Qprime_ACK = Qprime;
#ifdef DEBUG_ULSCH_DECODING
  printf("ulsch_decoding.c: Qprime_ACK %d, Msc_initial %d, Nsymb_initial %d, sumKr %d\n",
      Qprime_ACK,ulsch_harq->Msc_initial,ulsch_harq->Nsymb_initial,sumKr);
#endif

  // Compute Q_cqi
  if (ulsch_harq->Or1 < 12)
    L=0;
  else
    L=8;

  // NOTE: we have to handle the case where we have a very small number of bits (condition on pg. 26 36.212)
  if (ulsch_harq->Or1 > 0)
    Qprime = (ulsch_harq->Or1 + L) * ulsch_harq->Msc_initial*ulsch_harq->Nsymb_initial * ulsch->beta_offset_cqi_times8;
  else
    Qprime=0;

  if (Qprime > 0) {  // check if ceiling is larger than floor in Q' expression
    if ((Qprime % (8*sumKr)) > 0)
      Qprime = 1+(Qprime/(8*sumKr));
    else
      Qprime = Qprime/(8*sumKr);
  }

  G = nb_rb * (12 * Q_m) * (ulsch_harq->Nsymb_pusch);



  Q_CQI = Q_m * Qprime;
#ifdef DEBUG_ULSCH_DECODING
  printf("ulsch_decoding: G %d, Q_RI %d, Q_CQI %d (L %d, Or1 %d) O_ACK %d\n",G,Q_RI,Q_CQI,L,ulsch_harq->Or1,ulsch_harq->O_ACK);
#endif

  G = G - Q_RI - Q_CQI;
  ulsch_harq->G = G;

  AssertFatal((int)G > 0,
              "FATAL: ulsch_decoding.c G < 0 (%d) : Q_RI %d, Q_CQI %d\n",G,Q_RI,Q_CQI);

  H = G + Q_CQI;
  Hprime = H/Q_m;


  // Demultiplexing/Deinterleaving of PUSCH/ACK/RI/CQI
  start_meas(&eNB->ulsch_demultiplexing_stats);
  Hpp = Hprime + Qprime_RI;

  Cmux       = ulsch_harq->Nsymb_pusch;
  Rmux_prime = Hpp/Cmux;

  // Clear "tag" interleaving matrix to allow for CQI/DATA identification
  memset(ytag,0,Cmux*Rmux_prime);



  i=0;
  memset(y,LTE_NULL,Q_m*Hpp);

  // read in buffer and unscramble llrs for everything but placeholder bits
  // llrs stored per symbol correspond to columns of interleaving matrix


  s = lte_gold_unscram(&x1, &x2, 1);
  i2=0;

  for (i=0; i<((Hpp*Q_m)>>5); i++) {
    /*
    for (j=0; j<32; j++) {
      cseq[i2++] = (int16_t)((((s>>j)&1)<<1)-1);
    }
    */
#if defined(__x86_64__) || defined(__i386__)
#ifndef __AVX2__
    ((__m128i*)cseq)[i2++] = ((__m128i*)unscrambling_lut)[(s&65535)<<1];
    ((__m128i*)cseq)[i2++] = ((__m128i*)unscrambling_lut)[1+((s&65535)<<1)];
    s>>=16;
    ((__m128i*)cseq)[i2++] = ((__m128i*)unscrambling_lut)[(s&65535)<<1];
    ((__m128i*)cseq)[i2++] = ((__m128i*)unscrambling_lut)[1+((s&65535)<<1)];
#else
    ((__m256i*)cseq)[i2++] = ((__m256i*)unscrambling_lut)[s&65535];
    ((__m256i*)cseq)[i2++] = ((__m256i*)unscrambling_lut)[(s>>16)&65535];
#endif
#elif defined(__arm__)
    ((int16x8_t*)cseq)[i2++] = ((int16x8_t*)unscrambling_lut)[(s&65535)<<1];
    ((int16x8_t*)cseq)[i2++] = ((int16x8_t*)unscrambling_lut)[1+((s&65535)<<1)];
    s>>=16;
    ((int16x8_t*)cseq)[i2++] = ((int16x8_t*)unscrambling_lut)[(s&65535)<<1];
    ((int16x8_t*)cseq)[i2++] = ((int16x8_t*)unscrambling_lut)[1+((s&65535)<<1)];
#endif
    s = lte_gold_unscram(&x1, &x2, 0);
  }


  //  printf("after unscrambling c[%d] = %p\n",0,ulsch_harq->c[0]);

  if (frame_parms->Ncp == 0)
    columnset = cs_ri_normal;
  else
    columnset = cs_ri_extended;

  j=0;

  for (i=0; i<Qprime_RI; i++) {
    r = Rmux_prime - 1 - (i>>2);
    /*
    for (q=0;q<Q_m;q++)
      ytag2[q+(Q_m*((r*Cmux) + columnset[j]))]  = q_RI[(q+(Q_m*i))%len_RI];
    */
    off =((Rmux_prime*Q_m*columnset[j])+(r*Q_m));
    cseq[off+1] = cseq[off];  // PUSCH_y

    for (q=2; q<Q_m; q++)
      cseq[off+q] = -1;    // PUSCH_x

    j=(j+3)&3;

  }

  //  printf("after RI c[%d] = %p\n",0,ulsch_harq->c[0]);

  // HARQ-ACK Bits (Note these overwrite some bits)
  if (frame_parms->Ncp == 0)
    columnset = cs_ack_normal;
  else
    columnset = cs_ack_extended;

  j=0;

  for (i=0; i<Qprime_ACK; i++) {
    r = Rmux_prime - 1 - (i>>2);
    off =((Rmux_prime*Q_m*columnset[j])+(r*Q_m));

    if (ulsch_harq->O_ACK == 1) {
      if (ulsch->bundling==0)
        cseq[off+1] = cseq[off];  // PUSCH_y

      for (q=2; q<Q_m; q++)
        cseq[off+q] = -1;    // PUSCH_x
    } else if (ulsch_harq->O_ACK == 2) {
      for (q=2; q<Q_m; q++)
        cseq[off+q] = -1;    // PUSCH_x
    }

#ifdef DEBUG_ULSCH_DECODING
    printf("ulsch_decoding.c: ACK i %d, r %d, j %d, ColumnSet[j] %d\n",i,r,j,columnset[j]);
#endif
    j=(j+3)&3;
  }



  i=0;

  switch (Q_m) {
  case 2:
    for (j=0; j<Cmux; j++) {
      i2=j<<1;

      for (r=0; r<Rmux_prime; r++) {
        c = cseq[i];
        //  printf("ulsch %d: %d * ",i,c);
        y[i2++] = c*ulsch_llr[i++];
        //  printf("%d\n",ulsch_llr[i-1]);
        c = cseq[i];
        //  printf("ulsch %d: %d * ",i,c);
        y[i2] = c*ulsch_llr[i++];
        //  printf("%d\n",ulsch_llr[i-1]);
        i2=(i2+(Cmux<<1)-1);
      }
    }

    break;

  case 4:
    for (j=0; j<Cmux; j++) {
      i2=j<<2;

      for (r=0; r<Rmux_prime; r++) {
	/*
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2] = c*ulsch_llr[i++];
        i2=(i2+(Cmux<<2)-3);
                */
                // slightly more optimized version (equivalent to above) for 16QAM to improve computational performance
                *(__m64 *)&y[i2] = _mm_sign_pi16(*(__m64*)&ulsch_llr[i],*(__m64*)&cseq[i]);
                i+=4;
                i2+=(Cmux<<2);


      }
    }

    break;

  case 6:
    for (j=0; j<Cmux; j++) {
      i2=j*6;

      for (r=0; r<Rmux_prime; r++) {
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2++] = c*ulsch_llr[i++];
        c = cseq[i];
        y[i2] = c*ulsch_llr[i++];
        i2=(i2+(Cmux*6)-5);
      }
    }

    break;
  }




  if (i!=(H+Q_RI))
    LOG_D(PHY,"ulsch_decoding.c: Error in input buffer length (j %d, H+Q_RI %d)\n",i,H+Q_RI);

  // HARQ-ACK Bits (LLRs are nulled in overwritten bits after copying HARQ-ACK LLR)

  if (frame_parms->Ncp == 0)
    columnset = cs_ack_normal;
  else
    columnset = cs_ack_extended;

  j=0;

  if (ulsch_harq->O_ACK == 1) {
    switch (Q_m) {
    case 2:
      len_ACK = 2;
      break;

    case 4:
      len_ACK = 4;
      break;

    case 6:
      len_ACK = 6;
      break;
    }
  }

  if (ulsch_harq->O_ACK == 2) {
    switch (Q_m) {
    case 2:
      len_ACK = 6;
      break;

    case 4:
      len_ACK = 12;
      break;

    case 6:
      len_ACK = 18;
      break;
    }
  }

    if (ulsch_harq->O_ACK > 2) {
        LOG_E(PHY,"ulsch_decoding: FATAL, ACK cannot be more than 2 bits yet\n");
        return NULL;
    }

    for (i=0; i<len_ACK; i++)
        ulsch_harq->q_ACK[i] = 0;


    for (i=0; i<Qprime_ACK; i++) {
        r = Rmux_prime -1 - (i>>2);

        for (q=0; q<Q_m; q++) {
            if (y[q+(Q_m*((r*Cmux) + columnset[j]))]!=0)
                ulsch_harq->q_ACK[(q+(Q_m*i))%len_ACK] += y[q+(Q_m*((r*Cmux) + columnset[j]))];
            y[q+(Q_m*((r*Cmux) + columnset[j]))]=0;  // NULL LLRs in ACK positions
        }

        j=(j+3)&3;
    }

    //  printf("after ACKNAK c[%d] = %p\n",0,ulsch_harq->c[0]);

    // RI BITS

    if (ulsch_harq->O_RI == 1) {
        switch (Q_m) {
        case 2:
            len_RI=2;
            break;

        case 4:
            len_RI=4;
            break;

        case 6:
            len_RI=6;
            break;
        }
    }

    if (ulsch_harq->O_RI > 1) {
        LOG_E(PHY,"ulsch_decoding: FATAL, RI cannot be more than 1 bit yet\n");
        return NULL;
    }

  for (i=0; i<len_RI; i++)
    ulsch_harq->q_RI[i] = 0;

  if (frame_parms->Ncp == 0)
    columnset = cs_ri_normal;
  else
    columnset = cs_ri_extended;

  j=0;

  for (i=0; i<Qprime_RI; i++) {
    r = Rmux_prime -1 - (i>>2);

    for (q=0; q<Q_m; q++)
      ulsch_harq->q_RI[(q+(Q_m*i))%len_RI] += y[q+(Q_m*((r*Cmux) + columnset[j]))];

    ytag[(r*Cmux) + columnset[j]] = LTE_NULL;
    j=(j+3)&3;
  }

  //  printf("after RI2 c[%d] = %p\n",0,ulsch_harq->c[0]);

  // CQI and Data bits
  j=0;
  j2=0;

  //  r=0;
  if (Q_RI>0) {
    for (i=0; i<(Q_CQI/Q_m); i++) {
      
      while (ytag[j]==LTE_NULL) {
	j++;
	j2+=Q_m;
      }
      
      for (q=0; q<Q_m; q++) {
	//      ys = y[q+(Q_m*((r*Cmux)+j))];
	ys = y[q+j2];
	
	if (ys>127)
	  ulsch_harq->q[q+(Q_m*i)] = 127;
	else if (ys<-128)
	  ulsch_harq->q[q+(Q_m*i)] = -128;
	else
	  ulsch_harq->q[q+(Q_m*i)] = ys;
      }
      
      j2+=Q_m;
    }
    
        
    switch (Q_m) {
    case 2:
      for (iprime=0; iprime<G;) {
	while (ytag[j]==LTE_NULL) {
	  j++;
	  j2+=2;
	}
	
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	
      }
      
      
      break;
      
    case 4:
      for (iprime=0; iprime<G;) {
	while (ytag[j]==LTE_NULL) {
	  j++;
	  j2+=4;
	}
	
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	
      }
      
      break;
      
    case 6:
      for (iprime=0; iprime<G;) {
	while (ytag[j]==LTE_NULL) {
	  j++;
	  j2+=6;
	}
	
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	ulsch_harq->e[iprime++] = y[j2++];
	
      }
      
      break;

    }
    

  } // Q_RI>0
  else {

    for (i=0; i<(Q_CQI/Q_m); i++) {
      
      for (q=0; q<Q_m; q++) {
	ys = y[q+j2];
	if (ys>127)
	  ulsch_harq->q[q+(Q_m*i)] = 127;
	else if (ys<-128)
	  ulsch_harq->q[q+(Q_m*i)] = -128;
	else
	  ulsch_harq->q[q+(Q_m*i)] = ys;
      }
      
      j2+=Q_m;
    }
    /* To be improved according to alignment of j2
#if defined(__x86_64__)||defined(__i386__)
#ifndef __AVX2__
    for (iprime=0; iprime<G;iprime+=8,j2+=8)
      *((__m128i *)&ulsch_harq->e[iprime]) = *((__m128i *)&y[j2]);
#else
    for (iprime=0; iprime<G;iprime+=16,j2+=16)
      *((__m256i *)&ulsch_harq->e[iprime]) = *((__m256i *)&y[j2]);
#endif
#elif defined(__arm__)
    for (iprime=0; iprime<G;iprime+=8,j2+=8)
      *((int16x8_t *)&ulsch_harq->e[iprime]) = *((int16x8_t *)&y[j2]);
#endif
    */
    int16_t *yp,*ep;
    for (iprime=0,yp=&y[j2],ep=&ulsch_harq->e[0]; 
	 iprime<G;
	 iprime+=8,j2+=8,ep+=8,yp+=8) {
      ep[0] = yp[0];
      ep[1] = yp[1];
      ep[2] = yp[2];
      ep[3] = yp[3];
      ep[4] = yp[4];
      ep[5] = yp[5];
      ep[6] = yp[6];
      ep[7] = yp[7];
    }
  }
    
   
  stop_meas(&eNB->ulsch_demultiplexing_stats);

  //  printf("after ACKNAK2 c[%d] = %p (iprime %d, G %d)\n",0,ulsch_harq->c[0],iprime,G);

  // Do CQI/RI/HARQ-ACK Decoding first and pass to MAC

  // HARQ-ACK
  wACK_idx = (ulsch->bundling==0) ? 4 : ((Nbundled-1)&3);

  if (ulsch_harq->O_ACK == 1) {
    ulsch_harq->q_ACK[0] *= wACK_RX[wACK_idx][0];
    ulsch_harq->q_ACK[0] += (ulsch->bundling==0) ? ulsch_harq->q_ACK[1]*wACK_RX[wACK_idx][0] : ulsch_harq->q_ACK[1]*wACK_RX[wACK_idx][1];

    if (ulsch_harq->q_ACK[0] < 0)
      ulsch_harq->o_ACK[0] = 0;
    else
      ulsch_harq->o_ACK[0] = 1;
  }

  if (ulsch_harq->O_ACK == 2) {
    switch (Q_m) {

    case 2:
      ulsch_harq->q_ACK[0] = ulsch_harq->q_ACK[0]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[3]*wACK_RX[wACK_idx][1];
      ulsch_harq->q_ACK[1] = ulsch_harq->q_ACK[1]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[4]*wACK_RX[wACK_idx][1];
      ulsch_harq->q_ACK[2] = ulsch_harq->q_ACK[2]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[5]*wACK_RX[wACK_idx][1];
      break;
    case 4:
      ulsch_harq->q_ACK[0] = ulsch_harq->q_ACK[0]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[5]*wACK_RX[wACK_idx][1];
      ulsch_harq->q_ACK[1] = ulsch_harq->q_ACK[1]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[8]*wACK_RX[wACK_idx][1];
      ulsch_harq->q_ACK[2] = ulsch_harq->q_ACK[4]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[9]*wACK_RX[wACK_idx][1];
      break;
    case 6:
      ulsch_harq->q_ACK[0] =  ulsch_harq->q_ACK[0]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[7]*wACK_RX[wACK_idx][1];
      ulsch_harq->q_ACK[1] =  ulsch_harq->q_ACK[1]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[12]*wACK_RX[wACK_idx][1];
      ulsch_harq->q_ACK[2] =  ulsch_harq->q_ACK[6]*wACK_RX[wACK_idx][0] + ulsch_harq->q_ACK[13]*wACK_RX[wACK_idx][1];
      break;
    }

    ulsch_harq->o_ACK[0] = 1;
    ulsch_harq->o_ACK[1] = 1;
    metric     = ulsch_harq->q_ACK[0]+ulsch_harq->q_ACK[1]-ulsch_harq->q_ACK[2];
    metric_new = -ulsch_harq->q_ACK[0]+ulsch_harq->q_ACK[1]+ulsch_harq->q_ACK[2];

    if (metric_new > metric) {
      ulsch_harq->o_ACK[0]=0;
      ulsch_harq->o_ACK[1]=1;
      metric = metric_new;
    }

    metric_new = ulsch_harq->q_ACK[0]-ulsch_harq->q_ACK[1]+ulsch_harq->q_ACK[2];


    if (metric_new > metric) {
      ulsch_harq->o_ACK[0] = 1;
      ulsch_harq->o_ACK[1] = 0;
      metric = metric_new;
    }

    metric_new = -ulsch_harq->q_ACK[0]-ulsch_harq->q_ACK[1]-ulsch_harq->q_ACK[2];

    if (metric_new > metric) {
      ulsch_harq->o_ACK[0] = 0;
      ulsch_harq->o_ACK[1] = 0;
      metric = metric_new;
    }
  }

  // RI

  // rank 1
  if ((ulsch_harq->O_RI == 1) && (Qprime_RI > 0)) {
    ulsch_harq->o_RI[0] = ((ulsch_harq->q_RI[0] + ulsch_harq->q_RI[Q_m/2]) > 0) ? 0 : 1;
  }

  // CQI

  //  printf("before cqi c[%d] = %p\n",0,ulsch_harq->c[0]);
  ulsch_harq->cqi_crc_status = 0;
  if (Q_CQI>0) {
    memset((void *)&dummy_w_cc[0],0,3*(ulsch_harq->Or1+8+32));

    O_RCC = generate_dummy_w_cc(ulsch_harq->Or1+8,
                                &dummy_w_cc[0]);


    lte_rate_matching_cc_rx(O_RCC,
                            Q_CQI,
                            ulsch_harq->o_w,
                            dummy_w_cc,
                            ulsch_harq->q);

    sub_block_deinterleaving_cc((unsigned int)(ulsch_harq->Or1+8),
                                &ulsch_harq->o_d[96],
                                &ulsch_harq->o_w[0]);

    memset(ulsch_harq->o,0,(7+8+ulsch_harq->Or1) / 8);
    phy_viterbi_lte_sse2(ulsch_harq->o_d+96,ulsch_harq->o,8+ulsch_harq->Or1);

    if (extract_cqi_crc(ulsch_harq->o,ulsch_harq->Or1) == (crc8(ulsch_harq->o,ulsch_harq->Or1)>>24))
      ulsch_harq->cqi_crc_status = 1;

#ifdef DEBUG_ULSCH_DECODING
    printf("ulsch_decoding: Or1=%d\n",ulsch_harq->Or1);

    for (i=0; i<1+((8+ulsch_harq->Or1)/8); i++)
      printf("ulsch_decoding: O[%d] %d\n",i,ulsch_harq->o[i]);

    if (ulsch_harq->cqi_crc_status == 1)
      printf("RX CQI CRC OK (%x)\n",extract_cqi_crc(ulsch_harq->o,ulsch_harq->Or1));
    else
      printf("RX CQI CRC NOT OK (%x)\n",extract_cqi_crc(ulsch_harq->o,ulsch_harq->Or1));

#endif
  }

  LOG_D(PHY,"frame %d subframe %d O_ACK:%d o_ACK[]=%d:%d:%d:%d\n",frame,subframe,ulsch_harq->O_ACK,ulsch_harq->o_ACK[0],ulsch_harq->o_ACK[1],ulsch_harq->o_ACK[2],ulsch_harq->o_ACK[3]);

  // Do ULSCH Decoding for data portion

    request_t * ret = eNB->td(eNB,UE_id,harq_pid,llr8_flag, frame, subframe);

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_PHY_ENB_ULSCH_DECODING0+harq_pid,0);

  return(ret);
}

#ifdef PHY_ABSTRACTION

#ifdef PHY_ABSTRACTION_UL
int ulsch_abstraction(double* sinr_dB, uint8_t TM, uint8_t mcs,uint16_t nrb, uint16_t frb)
{

  int index,ii;
  double sinr_eff = 0;
  int rb_count = 0;
  int offset;
  double bler = 0;
  TM = TM-1;
  sinr_eff = sinr_dB[frb]; //the single sinr_eff value we calculated with MMSE FDE formula in init_snr_up function


  sinr_eff *= 10;
  sinr_eff = floor(sinr_eff);
  sinr_eff /= 10;

  LOG_D(PHY,"[ABSTRACTION] sinr_eff after rounding = %f\n",sinr_eff);

  for (index = 0; index < 16; index++) {
    if(index == 0) {
      if (sinr_eff < sinr_bler_map_up[mcs][0][index]) {
        bler = 1;
        break;
      }
    }

    if (sinr_eff == sinr_bler_map_up[mcs][0][index]) {
      bler = sinr_bler_map_up[mcs][1][index];
    }
  }

#ifdef USER_MODE // need to be adapted for the emulation in the kernel space 

  if (uniformrandom() < bler) {
    LOG_I(OCM,"abstraction_decoding failed (mcs=%d, sinr_eff=%f, bler=%f)\n",mcs,sinr_eff,bler);
    return(0);
  } else {
    LOG_I(OCM,"abstraction_decoding successful (mcs=%d, sinr_eff=%f, bler=%f)\n",mcs,sinr_eff,bler);
    return(1);
  }

#endif
}







int ulsch_abstraction_MIESM(double* sinr_dB,uint8_t TM, uint8_t mcs,uint16_t nrb, uint16_t frb)
{
  int index;
  double sinr_eff = 0;
  double sinr_db1 = 0;
  double sinr_db2 = 0;
  double SI=0;
  double RBIR=0;
  int rb_count = 0;
  int offset, M=0;
  double bler = 0;
  int start,middle,end;
  TM = TM-1;

  for (offset = frb; offset <= (frb + nrb -1); offset++) {

    rb_count++;

    //we need to do the table lookups here for the mutual information corresponding to the certain sinr_dB.

    sinr_db1 = sinr_dB[offset*2];
    sinr_db2 = sinr_dB[offset*2+1];

    printf("sinr_db1=%f\n,sinr_db2=%f\n",sinr_db1,sinr_db2);

    //rounding up for the table lookup
    sinr_db1 *= 10;
    sinr_db2 *= 10;

    sinr_db1 = floor(sinr_db1);
    sinr_db2 = floor(sinr_db2);

    if ((int)sinr_db1%2) {
      sinr_db1 += 1;
    }

    if ((int)sinr_db2%2) {
      sinr_db2 += 1;
    }

    sinr_db1 /= 10;
    sinr_db2 /= 10;

    if(mcs<10) {
      //for sinr_db1
      for (index = 0; index < 162; index++) {
        if (sinr_db1 < MI_map_4qam[0][0]) {
          SI += (MI_map_4qam[1][0]/beta1_dlsch_MI[TM][mcs]);
          M +=2;
          break;
        }

        if (sinr_db1 > MI_map_4qam[0][161]) {
          SI += (MI_map_4qam[1][161]/beta1_dlsch_MI[TM][mcs]);
          M +=2;
          break;
        }

        if (sinr_db1 == MI_map_4qam[0][index]) {
          SI += (MI_map_4qam[1][index]/beta1_dlsch_MI[TM][mcs]);
          M +=2;
          break;
        }
      }

      //for sinr_db2
      for (index = 0; index < 162; index++) {
        if (sinr_db2 < MI_map_4qam[0][0]) {
          SI += (MI_map_4qam[1][0]/beta1_dlsch_MI[TM][mcs]);
          M +=2;
          break;
        }

        if (sinr_db2 > MI_map_4qam[0][161]) {
          SI += (MI_map_4qam[1][161]/beta1_dlsch_MI[TM][mcs]);
          M +=2;
          break;
        }

        if (sinr_db2 == MI_map_4qam[0][index]) {
          SI += (MI_map_4qam[1][index]/beta1_dlsch_MI[TM][mcs]);
          M +=2;
          break;
        }
      }

    } else if(mcs>9 && mcs<17) {
      //for sinr_db1
      for (index = 0; index < 197; index++) {
        if (sinr_db1 < MI_map_16qam[0][0]) {
          SI += (MI_map_16qam[1][0]/beta1_dlsch_MI[TM][mcs]);
          M +=4;
          break;
        }

        if (sinr_db1 > MI_map_16qam[0][196]) {
          SI += (MI_map_16qam[1][196]/beta1_dlsch_MI[TM][mcs]);
          M +=4;
          break;
        }

        if (sinr_db1 == MI_map_16qam[0][index]) {
          SI += (MI_map_16qam[1][index]/beta1_dlsch_MI[TM][mcs]);
          M +=4;
          break;
        }
      }

      //for sinr_db2
      for (index = 0; index < 197; index++) {
        if (sinr_db2 < MI_map_16qam[0][0]) {
          SI += (MI_map_16qam[1][0]/beta1_dlsch_MI[TM][mcs]);
          M +=4;
          break;
        }

        if (sinr_db2 > MI_map_16qam[0][196]) {
          SI += (MI_map_16qam[1][196]/beta1_dlsch_MI[TM][mcs]);
          M +=4;
          break;
        }

        if (sinr_db2 == MI_map_16qam[0][index]) {
          SI += (MI_map_16qam[1][index]/beta1_dlsch_MI[TM][mcs]);
          M +=4;
          break;
        }
      }

    } else if(mcs>16 && mcs<22) {
      //for sinr_db1
      for (index = 0; index < 227; index++) {
        if (sinr_db1 < MI_map_64qam[0][0]) {
          SI += (MI_map_64qam[1][0]/beta1_dlsch_MI[TM][mcs]);
          M +=6;
          break;
        }

        if (sinr_db1 > MI_map_64qam[0][226]) {
          SI += (MI_map_64qam[1][226]/beta1_dlsch_MI[TM][mcs]);
          M +=6;
          break;
        }

        if (sinr_db1 == MI_map_64qam[0][index]) {
          SI += (MI_map_64qam[1][index]/beta1_dlsch_MI[TM][mcs]);
          M +=6;
          break;
        }
      }

      //for sinr_db2
      for (index = 0; index < 227; index++) {
        if (sinr_db2 < MI_map_64qam[0][0]) {
          SI += (MI_map_64qam[1][0]/beta1_dlsch_MI[TM][mcs]);
          M +=6;
          break;
        }

        if (sinr_db2 > MI_map_64qam[0][226]) {
          SI += (MI_map_64qam[1][226]/beta1_dlsch_MI[TM][mcs]);
          M +=6;
          break;
        }

        if (sinr_db2 == MI_map_64qam[0][index]) {
          SI += (MI_map_64qam[1][index]/beta1_dlsch_MI[TM][mcs]);
          M +=6;
          break;
        }
      }
    }
  }

  // }

  RBIR = SI/M;

  //Now RBIR->SINR_effective Mapping
  //binary search method is performed here
  if(mcs<10) {
    start = 0;
    end = 161;
    middle = end/2;

    if (RBIR <= MI_map_4qam[2][start]) {
      sinr_eff =  MI_map_4qam[0][start];
    } else {
      if (RBIR >= MI_map_4qam[2][end])
        sinr_eff =  MI_map_4qam[0][end];
      else {
        //while((end-start > 1) && (RBIR >= MI_map_4qam[2]))
        if (RBIR < MI_map_4qam[2][middle]) {
          end = middle;
          middle = end/2;
        } else {
          start = middle;
          middle = (end-middle)/2;
        }
      }

      for (; end>start; end--) {
        if ((RBIR < MI_map_4qam[2][end]) && (RBIR >  MI_map_4qam[2][end-2])) {
          sinr_eff = MI_map_4qam[0][end-1];
          break;
        }
      }
    }

    sinr_eff = sinr_eff * beta2_dlsch_MI[TM][mcs];
  }



  else if (mcs>9 && mcs<17) {

    start = 0;
    end = 196;
    middle = end/2;

    if (RBIR <= MI_map_16qam[2][start]) {
      sinr_eff =  MI_map_16qam[0][start];
    } else {
      if (RBIR >= MI_map_16qam[2][end])
        sinr_eff =  MI_map_16qam[0][end];
      else {
        //while((end-start > 1) && (RBIR >= MI_map_4qam[2]))
        if (RBIR < MI_map_16qam[2][middle]) {
          end = middle;
          middle = end/2;
        } else {
          start = middle;
          middle = (end-middle)/2;
        }
      }

      for (; end>start; end--) {
        if ((RBIR < MI_map_16qam[2][end]) && (RBIR >  MI_map_16qam[2][end-2])) {
          sinr_eff = MI_map_16qam[0][end-1];
          break;
        }
      }
    }

    sinr_eff = sinr_eff * beta2_dlsch_MI[TM][mcs];
  } else if (mcs>16) {
    start = 0;
    end = 226;
    middle = end/2;

    if (RBIR <= MI_map_64qam[2][start]) {
      sinr_eff =  MI_map_64qam[0][start];
    } else {
      if (RBIR >= MI_map_64qam[2][end])
        sinr_eff =  MI_map_64qam[0][end];
      else {
        //while((end-start > 1) && (RBIR >= MI_map_4qam[2]))
        if (RBIR < MI_map_64qam[2][middle]) {
          end = middle;
          middle = end/2;
        } else {
          start = middle;
          middle = (end-middle)/2;
        }
      }

      for (; end>start; end--) {
        if ((RBIR < MI_map_64qam[2][end]) && (RBIR >  MI_map_64qam[2][end-2])) {
          sinr_eff = MI_map_64qam[0][end-1];
          break;
        }
      }
    }

    sinr_eff = sinr_eff * beta2_dlsch_MI[TM][mcs];
  }

  printf("SINR_Eff = %e\n",sinr_eff);

  sinr_eff *= 10;
  sinr_eff = floor(sinr_eff);
  // if ((int)sinr_eff%2) {
  //   sinr_eff += 1;
  // }
  sinr_eff /= 10;
  printf("sinr_eff after rounding = %f\n",sinr_eff);

  for (index = 0; index < 16; index++) {
    if(index == 0) {
      if (sinr_eff < sinr_bler_map_up[mcs][0][index]) {
        bler = 1;
        break;
      }
    }

    if (sinr_eff == sinr_bler_map_up[mcs][0][index]) {
      bler = sinr_bler_map_up[mcs][1][index];
    }
  }

#ifdef USER_MODE // need to be adapted for the emulation in the kernel space 

  if (uniformrandom() < bler) {
    printf("abstraction_decoding failed (mcs=%d, sinr_eff=%f, bler=%f)\n",mcs,sinr_eff,bler);
    return(0);
  } else {
    printf("abstraction_decoding successful (mcs=%d, sinr_eff=%f, bler=%f)\n",mcs,sinr_eff,bler);
    return(1);
  }

#endif

}

#endif

uint32_t ulsch_decoding_emul(PHY_VARS_eNB *eNB, eNB_rxtx_proc_t *proc,
                             uint8_t UE_index,
                             uint16_t *crnti)
{

  uint8_t UE_id;
  uint16_t rnti;
  int subframe = proc->subframe_rx;
  uint8_t harq_pid;
  uint8_t CC_id = eNB->CC_id;

  harq_pid = subframe2harq_pid(&eNB->frame_parms,proc->frame_rx,subframe);

  rnti = eNB->ulsch[UE_index]->rnti;
#ifdef DEBUG_PHY
  LOG_D(PHY,"[eNB %d] ulsch_decoding_emul : subframe %d UE_index %d harq_pid %d rnti %x\n",eNB->Mod_id,subframe,UE_index,harq_pid,rnti);
#endif

  for (UE_id=0; UE_id<NB_UE_INST; UE_id++) {
    if (rnti == PHY_vars_UE_g[UE_id][CC_id]->pdcch_vars[PHY_vars_UE_g[UE_id][CC_id]->current_thread_id[subframe]][0]->crnti)
      break;

  }

  if (UE_id==NB_UE_INST) {
    LOG_W(PHY,"[eNB %d] ulsch_decoding_emul: FATAL, didn't find UE with rnti %x (UE index %d)\n",
          eNB->Mod_id, rnti, UE_index);
    return(1+eNB->ulsch[UE_id]->max_turbo_iterations);
  } else {
    LOG_D(PHY,"[eNB %d] Found UE with rnti %x => UE_id %d\n",eNB->Mod_id, rnti, UE_id);
  }

  if (PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->harq_processes[harq_pid]->status == CBA_ACTIVE) {
    *crnti = rnti;
    PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->harq_processes[harq_pid]->status=IDLE;
  } else
    *crnti = 0x0;

  if (1) {
    LOG_D(PHY,"ulsch_decoding_emul abstraction successful\n");

    memcpy(eNB->ulsch[UE_index]->harq_processes[harq_pid]->b,
           PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->harq_processes[harq_pid]->b,
           eNB->ulsch[UE_index]->harq_processes[harq_pid]->TBS>>3);

    // get local ue's ack
    if ((UE_index >= oai_emulation.info.first_ue_local) ||(UE_index <(oai_emulation.info.first_ue_local+oai_emulation.info.nb_ue_local))) {
      get_ack(&eNB->frame_parms,
              PHY_vars_UE_g[UE_id][CC_id]->dlsch[0][0][0]->harq_ack,
              proc->subframe_tx,
              proc->subframe_rx,
              eNB->ulsch[UE_index]->harq_processes[harq_pid]->o_ACK,0);
    } else { // get remote UEs' ack
      eNB->ulsch[UE_index]->harq_processes[harq_pid]->o_ACK[0] = PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->o_ACK[0];
      eNB->ulsch[UE_index]->harq_processes[harq_pid]->o_ACK[1] = PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->o_ACK[1];
    }

    // Do abstraction of PUSCH feedback
#ifdef DEBUG_PHY
    LOG_D(PHY,"[eNB %d][EMUL] ue index %d UE_id %d: subframe %d : o_ACK (%d %d), cqi (val %d, len %d)\n",
          eNB->Mod_id,UE_index, UE_id, subframe,eNB->ulsch[UE_index]->harq_processes[harq_pid]->o_ACK[0],
          eNB->ulsch[UE_index]->harq_processes[harq_pid]->o_ACK[1],
          ((HLC_subband_cqi_rank1_2A_5MHz *)PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->o)->cqi1,
          PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->O);
#endif

    eNB->ulsch[UE_index]->harq_processes[harq_pid]->Or1 = PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->O;
    eNB->ulsch[UE_index]->harq_processes[harq_pid]->Or2 = PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->O;

    eNB->ulsch[UE_index]->harq_processes[harq_pid]->uci_format = PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->uci_format;
    memcpy(eNB->ulsch[UE_index]->harq_processes[harq_pid]->o,PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->o,MAX_CQI_BYTES);
    memcpy(eNB->ulsch[UE_index]->harq_processes[harq_pid]->o_RI,PHY_vars_UE_g[UE_id][CC_id]->ulsch[0]->o_RI,2);

    eNB->ulsch[UE_index]->harq_processes[harq_pid]->cqi_crc_status = 1;

    return(1);
  } else {
    LOG_W(PHY,"[eNB %d] ulsch_decoding_emul abstraction failed for UE %d\n",eNB->Mod_id,UE_index);

    eNB->ulsch[UE_index]->harq_processes[harq_pid]->cqi_crc_status = 0;

    // retransmission
    return(1+eNB->ulsch[UE_index]->max_turbo_iterations);
  }

}
#endif