// error_correction.go - NewHope key exchange error correction.
//
// To the extent possible under law, Yawning Angel has waived all copyright
// and related or neighboring rights to newhope, using the Creative
// Commons "CC0" public domain dedication. See LICENSE or
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.

package newhope

import "blitter.com/go/chacha20"

func abs(v int32) int32 {
	mask := v >> 31
	return (v ^ mask) - mask
}

func f(v0, v1 *int32, x int32) int32 {
	// The`ref` code uses uint32 for x, but none of the values ever get large
	// enough for that, and that would be cast-tastic due to Go being Go.

	// Next 6 lines compute t = x/PARAM_Q
	b := x * 2730
	t := b >> 25
	b = x - t*paramQ
	b = (paramQ - 1) - b
	b >>= 31
	t -= b

	r := t & 1
	xit := t >> 1
	*v0 = xit + r // v0 = round(x/(2*PARAM_Q))

	t--
	r = t & 1
	*v1 = (t >> 1) + r

	return abs(x - ((*v0) * 2 * paramQ))
}

func g(x int32) int32 {
	// Next 6 lines compute t = x/(4 *PARAMQ)
	b := x * 2730
	t := b >> 27
	b = x - t*(paramQ*4)
	b = (paramQ * 4) - b
	b >>= 31
	t -= b

	c := t & 1
	t = (t >> 1) + c // t = round(x/(8*PARAM_Q))

	t *= 8 * paramQ

	return abs(t - x)
}

func llDecode(xi0, xi1, xi2, xi3 int32) int16 {
	t := g(xi0)
	t += g(xi1)
	t += g(xi2)
	t += g(xi3)

	t -= 8 * paramQ
	t >>= 31
	return int16(t & 1)
}

func (c *poly) helpRec(v *poly, seed *[SeedBytes]byte, nonce byte) {
	var v0, v1, vTmp [4]int32
	var k int32
	var rand [32]byte
	var n [8]byte

	n[7] = nonce

	stream, err := chacha20.New(seed[:], n[:])
	if err != nil {
		panic(err)
	}
	stream.KeyStream(rand[:])
	stream.Reset()
	defer memwipe(rand[:])

	for i := uint(0); i < 256; i++ {
		rBit := int32((rand[i>>3] >> (i & 7)) & 1)

		vTmp[0], vTmp[1], vTmp[2], vTmp[3] = int32(v.coeffs[i]), int32(v.coeffs[256+i]), int32(v.coeffs[512+i]), int32(v.coeffs[768+i])

		// newhope-20151209 - New version of the reconciliation.
		k = f(&v0[0], &v1[0], 8*vTmp[0]+4*rBit)
		k += f(&v0[1], &v1[1], 8*vTmp[1]+4*rBit)
		k += f(&v0[2], &v1[2], 8*vTmp[2]+4*rBit)
		k += f(&v0[3], &v1[3], 8*vTmp[3]+4*rBit)

		k = (2*paramQ - 1 - k) >> 31

		vTmp[0] = ((^k) & v0[0]) ^ (k & v1[0])
		vTmp[1] = ((^k) & v0[1]) ^ (k & v1[1])
		vTmp[2] = ((^k) & v0[2]) ^ (k & v1[2])
		vTmp[3] = ((^k) & v0[3]) ^ (k & v1[3])

		c.coeffs[0+i] = uint16((vTmp[0] - vTmp[3]) & 3)
		c.coeffs[256+i] = uint16((vTmp[1] - vTmp[3]) & 3)
		c.coeffs[512+i] = uint16((vTmp[2] - vTmp[3]) & 3)
		c.coeffs[768+i] = uint16((-k + 2*vTmp[3]) & 3)
	}

	for i := range vTmp {
		vTmp[i] = 0
	}
}

func rec(key *[32]byte, v, c *poly) {
	var tmp, vTmp, cTmp [4]int32
	for i := range key {
		key[i] = 0
	}

	for i := uint(0); i < 256; i++ {
		vTmp[0], vTmp[1], vTmp[2], vTmp[3] = int32(v.coeffs[i]), int32(v.coeffs[256+i]), int32(v.coeffs[512+i]), int32(v.coeffs[768+i])
		cTmp[0], cTmp[1], cTmp[2], cTmp[3] = int32(c.coeffs[i]), int32(c.coeffs[256+i]), int32(c.coeffs[512+i]), int32(c.coeffs[768+i])
		tmp[0] = 16*paramQ + 8*vTmp[0] - paramQ*(2*cTmp[0]+cTmp[3])
		tmp[1] = 16*paramQ + 8*vTmp[1] - paramQ*(2*cTmp[1]+cTmp[3])
		tmp[2] = 16*paramQ + 8*vTmp[2] - paramQ*(2*cTmp[2]+cTmp[3])
		tmp[3] = 16*paramQ + 8*vTmp[3] - paramQ*(cTmp[3])

		key[i>>3] |= byte(llDecode(tmp[0], tmp[1], tmp[2], tmp[3]) << (i & 7))
	}

	for i := 0; i < 4; i++ {
		tmp[i] = 0
		vTmp[i] = 0
		cTmp[i] = 0
	}
}
