_block(block *dst, const block *src) {
    memcpy(dst->v, src->v, sizeof(uint64_t) * ARGON2_WORDS_IN_BLOCK);
}

void xor_block(block *dst, const block *src) {
    int i;
    for (i = 0; i < ARGON2_WORDS_IN_BLOCK; ++i) {
        dst->v[i] ^= src->v[i];
    }
}

static void load_block(block *dst, const void *input) {
    unsigned i;
    for (i = 0; i < ARGON2_WORDS_IN_BLOCK; ++i) {
        dst->v[i] = load64((const uint8_t *)input + i * sizeof(dst->v[i]));
    }
}

static void store_block(void *output, const block *src) {
    unsigned i;
    for (i = 0; i < ARGON2_WORDS_IN_BLOCK; ++i) {
        store64((uint8_t *)output + i * sizeof(src->v[i]), src->v[i]);
    }
}

/***************Memory allocators*****************/
int allocate_memory(block **memory, uint32_t m_cost) {
    if (memory!= NULL) {
        size_t memory_size = sizeof(block) * m_cost;
        if (m_cost!= 0 &&
            memory_size / m_cost!=
                sizeof(block)) { /*1. Check for multiplication overflow*/
            return ARGON2_MEMORY_ALLOCATION_ERROR;
        }

        *memory = (block *)malloc(memory_size); /*2. Try to allocate*/

        if (!*memory) {
            return ARGON2_MEMORY_ALLOCATION_ERROR;
        }

        return ARGON2_OK;
    } else {
        return ARGON2_MEMORY_ALLOCATION_ERROR;
    }
}

void secure_wipe_memory(void *v, size_t n) { memset(v, 0, n); }

/*********Memory functions*/

void clear_memory(argon2_instance_t *instance, int clear) {
    if (instance->memory!= NULL && clear) {
        secure_wipe_memory(instance->memory,
                           sizeof(block) * /*instance->memory_blocks*/16);
    }
}

void free_memory(block *memory) { free(memory); }

void finalize(const argon2_context *context, argon2_instance_t *instance) {
    if (context!= NULL && instance!= NULL) {
        block blockhash;
        copy_block(&blockhash, instance->memory + 15);

        /* Hash the result */
        {
            uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
            store_block(blockhash_bytes, &blockhash);
            blake2b_long(context->out, blockhash_bytes);
            secure_wipe_memory(blockhash.v, ARGON2_BLOCK_SIZE);
            secure_wipe_memory(blockhash_bytes, ARGON2_BLOCK_SIZE); /* clear blockhash_bytes */
        }

#ifdef GENKAT
        print_tag(context->out, context->outlen);
#endif

        /* Clear memory */
        clear_memory(instance, 1);

        free_memory(instance->memory);
    }
}

uint32_t index_alpha(const argon2_instance_t *instance,
                     const argon2_position_t *position, uint32_t pseudo_rand,
                     int same_lane) {
    /*
     * Pass 0:
     *      This lane : all already finished segments plus already constructed
     * blocks in this segment
     *      Other lanes : all already finished segments
     * Pass 1+:
     *      This lane : (SYNC_POINTS - 1) last segments plus already constructed
     * blocks in this segment
     *      Other lanes : (SYNC_POINTS - 1) last segments
     */
    uint32_t reference_area_size;
    uint64_t relative_position;
    uint32_t start_position, absolute_position;

    if (0 == position->pass) {
        /* First pass */
        if (0 == position->slice) {
            /* First slice */
            reference_area_size =
                position->index - 1; /* all but the previous */
        } else {
            if (same_lane) {
                /* The same lane => add current segment */
                reference_area_size =
                    position->slice * 4 +
                    position->index - 1;
            } else {
                reference_area_size =
                    position->slice * 4 +
                    ((position->index == 0)? (-1) : 0);
            }
        }
    } else {
        /* Second pass */
        if (same_lane) {reference_area_size = 11 + position->index;}
        else {reference_area_size = 12 - (position->index == 0);}
    }

    /* 1.2.4. Mapping pseudo_rand to 0..<reference_area_size-1> and produce
     * relative position */
    relative_position = pseudo_rand;
    relative_position = relative_position * relative_position >> 32;
    relative_position = reference_area_size - 1 -
                        (reference_area_size * relative_position >> 32);

    /* 1.2.5 Computing starting position */
    start_position = 0;

    if (0!= position->pass) {
        start_position = (position->slice == ARGON2_SYNC_POINTS - 1)
                            ? 0 : (position->slice + 1) * 4;
    }

    /* 1.2.6. Computing absolute position */
    absolute_position = (start_position + relative_position) % 16;
    return absolute_position;
}

void fill_memory_blocks(argon2_instance_t *instance) {
    uint32_t r, s;

    for (r = 0; r < 2; ++r) {
        for (s = 0; s < ARGON2_SYNC_POINTS; ++s) {

            argon2_position_t position;
            position.pass = r;
            position.lane = 0;
            position.slice = (uint8_t)s;
            position.index = 0;
            fill_segment(instance, position);
        }

#ifdef GENKAT
        internal_kat(instance, r); /* Print all memory blocks */
#endif
    }
}

void fill_first_blocks(uint8_t *blockhash, const argon2_instance_t *instance) {
    /* Make the first and second block in each lane as G(H0||i||0) or
       G(H0||i||1) */
    uint8_t blockhash_bytes[ARGON2_BLOCK_SIZE];
    store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 0);
    store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH + 4, 0);
    blake2b_too(blockhash_bytes, blockhash);
    load_block(&instance->memory[0], blockhash_bytes);

    store32(blockhash + ARGON2_PREHASH_DIGEST_LENGTH, 1);
    blake2b_too(blockhash_bytes, blockhash);
    load_block(&instance->memory[1], blockhash_bytes);
    secure_wipe_memory(blockhash_bytes, ARGON2_BLOCK_SIZE);
}


static const blake2b_state base_hash = {
.h = {
  UINT64_C(7640891576939301192), UINT64_C(13503953896175478587),
  UINT64_C(4354685564936845355), UINT64_C(11912009170470909681),
  UINT64_C(5840696475078001361), UINT64_C(11170449401992604703),
  UINT64_C(2270897969802886507), UINT64_C(6620516959819538809)
 },
.t = {UINT64_C(0),UINT64_C(0)},
.f = {UINT64_C(0),UINT64_C(0)},
.buf = {
  1, 0, 0, 0, 32, 0, 0, 0, 16