/***************************************************************************
* Additional implementation of "BIKE: Bit Flipping Key Encapsulation". 
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Written by Nir Drucker and Shay Gueron
* AWS Cryptographic Algorithms Group
* (ndrucker@amazon.com, gueron@amazon.com)
*
* The license is detailed in the file LICENSE.md, and applies to this file.
*
* The optimizations are based on the description developed in the paper: 
* N. Drucker, S. Gueron, 
* "A toolbox for software optimization of QC-MDPC code-based cryptosystems", 
* ePrint (2017).
* The decoder (in decoder/decoder.c) algorithm is the algorithm included in
* the early submission of CAKE (due to N. Sandrier and R Misoczki).
*
* ***************************************************************************/

#include "decode.h"
#include "utilities.h"
#include <string.h>

EXTERNC void compute_counter_of_unsat(OUT uint8_t upc[N_BITS],
                                      IN const uint8_t s[N_BITS],
                                      IN const compressed_idx_dv_t* inv_h0_compressed,
                                      IN const compressed_idx_dv_t* inv_h1_compressed)
{
    uint32_t i=0, j=0, mask[2]={0}, pos[2]={0};
    
    memset(upc, 0, N_BITS);

    for(j = 0; j < FAKE_DV; j++)
    {
        // It is faster (by 5M cycles) to have one loop instead of two.
        mask[0] = inv_h0_compressed->val[j].used;
        mask[1] = inv_h1_compressed->val[j].used;
        pos[0]  = inv_h0_compressed->val[j].val;
        pos[1]  = inv_h1_compressed->val[j].val;
        for(i = 0; i < R_BITS; i++)
        {
            upc[i] += (s[i+pos[0]] & mask[0]);
            upc[R_BITS + i] += (s[i+pos[1]] & mask[1]);
        }
    }
}

EXTERNC void find_error1(IN OUT e_t* e,
                         OUT e_t* black_e,
                         OUT e_t* gray_e,
                         IN const uint8_t* upc,
                         IN const uint32_t black_th,
                         IN const uint32_t gray_th)
{
    uint8_t bit = 1, black_acc = 0, gray_acc = 0;
    uint8_t val = 0, mask = 0;
    uint32_t byte_itr = 0;

    for (uint64_t j = 0; j < N0; j++)
    {
        val = upc[j*R_BITS];
        mask = secure_l32_mask(val, black_th);
        black_acc |= (bit & mask);

        // Update the gray list only if not in the black list
        val &= (~mask);

        mask = secure_l32_mask(val, gray_th);
        gray_acc |= (bit & mask);

        for (int i = R_BITS-1; i > 0; i--)
        {
            if(bit == 0x80) {
                e->raw[byte_itr] ^= black_acc;
                black_e->raw[byte_itr] = black_acc;
                gray_e->raw[byte_itr] = gray_acc;
                byte_itr++;

                bit = 1;
                black_acc = 0;
                gray_acc = 0;
            }
            else {
                bit <<= 1;
            }
            
            val = upc[i + (j*R_BITS)];
            mask = secure_l32_mask(val, black_th);
            black_acc |= (bit & mask);

            // Update the gray list only if not in the black list
            val &= (~mask);
            
            mask = secure_l32_mask(val, gray_th);
            gray_acc |= (bit & mask);
        }
        bit <<= 1;       
    }

    //Final bytes
    e->raw[byte_itr] ^= black_acc;
    black_e->raw[byte_itr] = black_acc;
    gray_e->raw[byte_itr] = gray_acc;
}

EXTERNC void find_error2(IN OUT e_t* e,
                         IN e_t* pos_e,
                         IN const uint8_t* upc,
                         IN const uint32_t threshold)
{
    uint8_t bit = 1;
    uint8_t pos_acc = 0;
    uint32_t byte_itr = 0;

    for (uint64_t j = 0; j < N0; j++)
    {
        uint8_t mask = secure_l32_mask(upc[j*R_BITS], threshold);
        pos_acc |= (bit & mask);

        for (int i = R_BITS-1; i > 0; i--)
        {
            if(bit == 0x80) {
                e->raw[byte_itr] ^= (pos_e->raw[byte_itr] & pos_acc);
                byte_itr++;

                bit = 1;
                pos_acc = 0;
            }
            else {
                bit <<= 1;
            }

            mask = secure_l32_mask(upc[i+(j*R_BITS)], threshold);
            pos_acc |= (bit & mask);
        }
        bit <<= 1;       
    }
    
    //Final byte
    e->raw[byte_itr] ^= (pos_e->raw[byte_itr] & pos_acc);
}
