// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package mldsa

import (
	
	
	
	
)

const (
	q        = 8380417    // 2²³ - 2¹³ + 1
	R        = 4294967296 // 2³²
	RR       = 2365951    // R² mod q, aka R in the Montgomery domain
	qNegInv  = 4236238847 // -q⁻¹ mod R (q * qNegInv ≡ -1 mod R)
	one      = 4193792    // R mod q, aka 1 in the Montgomery domain
	minusOne = 4186625    // (q - 1) * R mod q, aka -1 in the Montgomery domain
)

// fieldElement is an element n of ℤ_q in the Montgomery domain, represented as
// an integer x in [0, q) such that x ≡ n * R (mod q) where R = 2³².
type fieldElement uint32

var errUnreducedFieldElement = errors.New("mldsa: unreduced field element")

// fieldToMontgomery checks that a value a is < q, and converts it to
// Montgomery form.
func fieldToMontgomery( uint32) (fieldElement, error) {
	if  >= q {
		return 0, errUnreducedFieldElement
	}
	// a * R² * R⁻¹ ≡ a * R (mod q)
	return fieldMontgomeryMul(fieldElement(), RR), nil
}

// fieldSubToMontgomery converts a difference a - b to Montgomery form.
// a and b must be < q. (This bound can probably be relaxed.)
func fieldSubToMontgomery(,  uint32) fieldElement {
	 :=  -  + q
	return fieldMontgomeryMul(fieldElement(), RR)
}

// fieldFromMontgomery converts a value a in Montgomery form back to
// standard representation.
func fieldFromMontgomery( fieldElement) uint32 {
	// (a * R) * 1 * R⁻¹ ≡ a (mod q)
	return uint32(fieldMontgomeryReduce(uint64()))
}

// fieldCenteredMod returns r mod± q, the value r reduced to the range
// [−(q−1)/2, (q−1)/2].
func fieldCenteredMod( fieldElement) int32 {
	 := int32(fieldFromMontgomery())
	// x <= q / 2 ? x : x - q
	return constantTimeSelectLessOrEqual(, q/2, , -q)
}

// fieldInfinityNorm returns the infinity norm ||r||∞ of r, or the absolute
// value of r centered around 0.
func fieldInfinityNorm( fieldElement) uint32 {
	 := int32(fieldFromMontgomery())
	// x <= q / 2 ? x : |x - q|
	// |x - q| = -(x - q) = q - x because x < q => x - q < 0
	return uint32(constantTimeSelectLessOrEqual(, q/2, , q-))
}

// fieldReduceOnce reduces a value a < 2q.
func fieldReduceOnce( uint32) fieldElement {
	,  := bits.Sub64(uint64(), uint64(q), 0)
	return fieldElement( + *q)
}

// fieldAdd returns a + b mod q.
func fieldAdd(,  fieldElement) fieldElement {
	 := uint32( + )
	return fieldReduceOnce()
}

// fieldSub returns a - b mod q.
func fieldSub(,  fieldElement) fieldElement {
	 := uint32( -  + q)
	return fieldReduceOnce()
}

// fieldMontgomeryMul returns a * b * R⁻¹ mod q.
func fieldMontgomeryMul(,  fieldElement) fieldElement {
	 := uint64() * uint64()
	return fieldMontgomeryReduce()
}

// fieldMontgomeryReduce returns x * R⁻¹ mod q for x < q * R.
func fieldMontgomeryReduce( uint64) fieldElement {
	 := uint32() * qNegInv
	 := ( + uint64()*q) >> 32
	return fieldReduceOnce(uint32())
}

// fieldMontgomeryMulSub returns a * (b - c). This operation is fused to save a
// fieldReduceOnce after the subtraction.
func fieldMontgomeryMulSub(, ,  fieldElement) fieldElement {
	 := uint64() * uint64(-+q)
	return fieldMontgomeryReduce()
}

// fieldMontgomeryAddMul returns a * b + c * d. This operation is fused to save
// a fieldReduceOnce and a fieldReduce.
func fieldMontgomeryAddMul(, , ,  fieldElement) fieldElement {
	 := uint64() * uint64()
	 += uint64() * uint64()
	return fieldMontgomeryReduce()
}

const n = 256

// ringElement is a polynomial, an element of R_q.
type ringElement [n]fieldElement

// polyAdd adds two ringElements or nttElements.
func polyAdd[ ~[n]fieldElement](,  ) ( ) {
	for  := range  {
		[] = fieldAdd([], [])
	}
	return 
}

// polySub subtracts two ringElements or nttElements.
func polySub[ ~[n]fieldElement](,  ) ( ) {
	for  := range  {
		[] = fieldSub([], [])
	}
	return 
}

// nttElement is an NTT representation, an element of T_q.
type nttElement [n]fieldElement

// zetas are the values ζ^BitRev₈(k) mod q for each index k, converted to the
// Montgomery domain.
var zetas = [256]fieldElement{4193792, 25847, 5771523, 7861508, 237124, 7602457, 7504169, 466468, 1826347, 2353451, 8021166, 6288512, 3119733, 5495562, 3111497, 2680103, 2725464, 1024112, 7300517, 3585928, 7830929, 7260833, 2619752, 6271868, 6262231, 4520680, 6980856, 5102745, 1757237, 8360995, 4010497, 280005, 2706023, 95776, 3077325, 3530437, 6718724, 4788269, 5842901, 3915439, 4519302, 5336701, 3574422, 5512770, 3539968, 8079950, 2348700, 7841118, 6681150, 6736599, 3505694, 4558682, 3507263, 6239768, 6779997, 3699596, 811944, 531354, 954230, 3881043, 3900724, 5823537, 2071892, 5582638, 4450022, 6851714, 4702672, 5339162, 6927966, 3475950, 2176455, 6795196, 7122806, 1939314, 4296819, 7380215, 5190273, 5223087, 4747489, 126922, 3412210, 7396998, 2147896, 2715295, 5412772, 4686924, 7969390, 5903370, 7709315, 7151892, 8357436, 7072248, 7998430, 1349076, 1852771, 6949987, 5037034, 264944, 508951, 3097992, 44288, 7280319, 904516, 3958618, 4656075, 8371839, 1653064, 5130689, 2389356, 8169440, 759969, 7063561, 189548, 4827145, 3159746, 6529015, 5971092, 8202977, 1315589, 1341330, 1285669, 6795489, 7567685, 6940675, 5361315, 4499357, 4751448, 3839961, 2091667, 3407706, 2316500, 3817976, 5037939, 2244091, 5933984, 4817955, 266997, 2434439, 7144689, 3513181, 4860065, 4621053, 7183191, 5187039, 900702, 1859098, 909542, 819034, 495491, 6767243, 8337157, 7857917, 7725090, 5257975, 2031748, 3207046, 4823422, 7855319, 7611795, 4784579, 342297, 286988, 5942594, 4108315, 3437287, 5038140, 1735879, 203044, 2842341, 2691481, 5790267, 1265009, 4055324, 1247620, 2486353, 1595974, 4613401, 1250494, 2635921, 4832145, 5386378, 1869119, 1903435, 7329447, 7047359, 1237275, 5062207, 6950192, 7929317, 1312455, 3306115, 6417775, 7100756, 1917081, 5834105, 7005614, 1500165, 777191, 2235880, 3406031, 7838005, 5548557, 6709241, 6533464, 5796124, 4656147, 594136, 4603424, 6366809, 2432395, 2454455, 8215696, 1957272, 3369112, 185531, 7173032, 5196991, 162844, 1616392, 3014001, 810149, 1652634, 4686184, 6581310, 5341501, 3523897, 3866901, 269760, 2213111, 7404533, 1717735, 472078, 7953734, 1723600, 6577327, 1910376, 6712985, 7276084, 8119771, 4546524, 5441381, 6144432, 7959518, 6094090, 183443, 7403526, 1612842, 4834730, 7826001, 3919660, 8332111, 7018208, 3937738, 1400424, 7534263, 1976782}

// ntt maps a ringElement to its nttElement representation.
//
// It implements NTT, according to FIPS 203, Algorithm 9.
func ntt( ringElement) nttElement {
	var  uint8

	for  := 128;  >= 8;  /= 2 {
		for  := 0;  < 256;  += 2 *  {
			++
			 := zetas[]

			// Bounds check elimination hint.
			,  := [:+], [+:++]
			for  := 0;  < ;  += 2 {
				 := fieldMontgomeryMul(, [])
				[] = fieldSub([], )
				[] = fieldAdd([], )

				// Unroll by 2 for performance.
				 = fieldMontgomeryMul(, [+1])
				[+1] = fieldSub([+1], )
				[+1] = fieldAdd([+1], )
			}
		}
	}

	// Unroll len = 4, 2, and 1.
	for  := 0;  < 256;  += 8 {
		++
		 := zetas[]

		 := fieldMontgomeryMul(, [+4])
		[+4] = fieldSub([], )
		[] = fieldAdd([], )

		 = fieldMontgomeryMul(, [+5])
		[+5] = fieldSub([+1], )
		[+1] = fieldAdd([+1], )

		 = fieldMontgomeryMul(, [+6])
		[+6] = fieldSub([+2], )
		[+2] = fieldAdd([+2], )

		 = fieldMontgomeryMul(, [+7])
		[+7] = fieldSub([+3], )
		[+3] = fieldAdd([+3], )
	}
	for  := 0;  < 256;  += 4 {
		++
		 := zetas[]

		 := fieldMontgomeryMul(, [+2])
		[+2] = fieldSub([], )
		[] = fieldAdd([], )

		 = fieldMontgomeryMul(, [+3])
		[+3] = fieldSub([+1], )
		[+1] = fieldAdd([+1], )
	}
	for  := 0;  < 256;  += 2 {
		++
		 := zetas[]

		 := fieldMontgomeryMul(, [+1])
		[+1] = fieldSub([], )
		[] = fieldAdd([], )
	}

	return nttElement()
}

// inverseNTT maps a nttElement back to the ringElement it represents.
//
// It implements NTT⁻¹, according to FIPS 203, Algorithm 10.
func inverseNTT( nttElement) ringElement {
	var  uint8 = 255

	// Unroll len = 1, 2, and 4.
	for  := 0;  < 256;  += 2 {
		 := zetas[]
		--

		 := []
		[] = fieldAdd(, [+1])
		[+1] = fieldMontgomeryMulSub(, [+1], )
	}
	for  := 0;  < 256;  += 4 {
		 := zetas[]
		--

		 := []
		[] = fieldAdd(, [+2])
		[+2] = fieldMontgomeryMulSub(, [+2], )

		 = [+1]
		[+1] = fieldAdd(, [+3])
		[+3] = fieldMontgomeryMulSub(, [+3], )
	}
	for  := 0;  < 256;  += 8 {
		 := zetas[]
		--

		 := []
		[] = fieldAdd(, [+4])
		[+4] = fieldMontgomeryMulSub(, [+4], )

		 = [+1]
		[+1] = fieldAdd(, [+5])
		[+5] = fieldMontgomeryMulSub(, [+5], )

		 = [+2]
		[+2] = fieldAdd(, [+6])
		[+6] = fieldMontgomeryMulSub(, [+6], )

		 = [+3]
		[+3] = fieldAdd(, [+7])
		[+7] = fieldMontgomeryMulSub(, [+7], )
	}

	for  := 8;  < 256;  *= 2 {
		for  := 0;  < 256;  += 2 *  {
			 := zetas[]
			--

			// Bounds check elimination hint.
			,  := [:+], [+:++]
			for  := 0;  < ;  += 2 {
				 := []
				[] = fieldAdd(, [])
				// -z * (t - flen[j]) = z * (flen[j] - t)
				[] = fieldMontgomeryMulSub(, [], )

				// Unroll by 2 for performance.
				 = [+1]
				[+1] = fieldAdd(, [+1])
				[+1] = fieldMontgomeryMulSub(, [+1], )
			}
		}
	}

	for  := range  {
		[] = fieldMontgomeryMul([], 16382) // 16382 = 256⁻¹ * R mod q
	}
	return ringElement()
}

// nttMul multiplies two nttElements.
func nttMul(,  nttElement) ( nttElement) {
	for  := range  {
		[] = fieldMontgomeryMul([], [])
	}
	return 
}

// sampleNTT samples an nttElement uniformly at random from the seed rho and the
// indices s and r. It implements Step 3 of ExpandA, RejNTTPoly, and
// CoeffFromThreeBytes from FIPS 204, passing in ρ, s, and r instead of ρ'.
func sampleNTT( []byte, ,  byte) nttElement {
	 := sha3.NewShake128()
	.Write()
	.Write([]byte{, })

	var  nttElement
	var  int         // index into a
	var  [168]byte // buffered reads from B, matching the rate of SHAKE-128
	 := len()   // index into buf, starts in a "buffer fully consumed" state
	for  < n {
		if  >= len() {
			.Read([:])
			 = 0
		}
		 := uint32([]) | uint32([+1])<<8 | uint32([+2])<<16
		 += 3
		,  := fieldToMontgomery( & 0b01111111_11111111_11111111) // 23 bits
		if  != nil {
			continue
		}
		[] = 
		++
	}
	return 
}

// sampleBoundedPoly samples a ringElement with coefficients in [−η, η] from the
// seed rho and the index r. It implements RejBoundedPoly and CoeffFromHalfByte
// from FIPS 204, passing in ρ and r separately from ExpandS.
func sampleBoundedPoly( []byte,  byte,  parameters) ringElement {
	 := sha3.NewShake256()
	.Write()
	.Write([]byte{, 0}) // IntegerToBytes(r, 2)

	var  ringElement
	var  int
	var  [136]byte // buffered reads from H, matching the rate of SHAKE-256
	 := len()   // index into buf, starts in a "buffer fully consumed" state
	for {
		if  >= len() {
			.Read([:])
			 = 0
		}
		 := [] & 0x0F
		 := [] >> 4
		++
		,  := coeffFromHalfByte(, )
		if  {
			[] = 
			++
		}
		if  >= len() {
			break
		}
		,  = coeffFromHalfByte(, )
		if  {
			[] = 
			++
		}
		if  >= len() {
			break
		}
	}
	return 
}

// sampleInBall samples a ringElement with coefficients in {−1, 0, 1}, and τ
// non-zero coefficients. It is not constant-time.
func sampleInBall( []byte,  parameters) ringElement {
	 := sha3.NewShake256()
	.Write()
	 := make([]byte, 8)
	.Read()

	var  ringElement
	for  := 256 - .τ;  < 256; ++ {
		 := make([]byte, 1)
		.Read()
		for [0] > byte() {
			.Read()
		}
		[] = [[0]]
		// c[j] = (−1) ^ h[i+τ−256], where h are the bits in s in little-endian.
		// That is, -1⁰ = 1 if the bit is 0, -1¹ = -1 if it is 1.
		 :=  + .τ - 256
		 := ([/8] >> ( % 8)) & 1
		if  == 0 {
			[[0]] = one
		} else {
			[[0]] = minusOne
		}
	}

	return 
}

// coeffFromHalfByte implements CoeffFromHalfByte from FIPS 204.
//
// It maps a value in [0, 15] to a coefficient in [−η, η]
func coeffFromHalfByte( byte,  parameters) (fieldElement, bool) {
	if  > 15 {
		panic("internal error: half-byte out of range")
	}
	switch .η {
	case 2:
		// Return z = 2 − (b mod 5), which maps from
		//
		//     b = ( 14, 13, 12, 11, 10,  9,  8,  7,  6,  5,  4,  3,  2,  1,  0 )
		//
		// to
		//
		//   b%5 = (  4,  3,  2,  1,  0,  4,  3,  2,  1,  0,  4,  3,  2,  1,  0 )
		//
		// to
		//
		//     z = ( -2, -1,  0,  1,  2, -2, -1,  0,  1,  2, -2, -1,  0,  1,  2 )
		//
		if  > 14 {
			return 0, false
		}
		// Calculate b % 5 with Barrett reduction, to avoid a potentially
		// variable-time division.
		const  = 0x3334 // ⌈2¹⁶ / 5⌉
		const  = 16          // log₂(2¹⁶)
		 := (uint32() * ) >> 
		 := uint32() - *5
		return fieldSubToMontgomery(2, ), true
	case 4:
		// Return z = 4 − b, which maps from
		//
		//   b = (  8,  7,  6,  5,  4,  3,  2,  1,  0 )
		//
		// to
		//
		//   z = ( −4, -3, -2, -1,  0,  1,  2,  3,  4 )
		//
		if  > 8 {
			return 0, false
		}
		return fieldSubToMontgomery(4, uint32()), true
	default:
		panic("internal error: unsupported η")
	}
}

// power2Round implements Power2Round from FIPS 204.
//
// It separates the bottom d = 13 bits of each 23-bit coefficient, rounding the
// high part based on the low part, and correcting the low part accordingly.
func power2Round( fieldElement) ( uint16,  fieldElement) {
	 := fieldFromMontgomery()
	// Add 2¹² - 1 to round up r1 by one if r0 > 2¹².
	// r is at most 2²³ - 2¹³ + 1, so rr + (2¹² - 1) won't overflow 23 bits.
	 :=  + 1<<12 - 1
	 >>= 13
	// r1 <= 2¹⁰ - 1
	// r1 * 2¹³ <= (2¹⁰ - 1) * 2¹³ = 2²³ - 2¹³ < q
	 := fieldSubToMontgomery(, <<13)
	return uint16(), 
}

// highBits implements HighBits from FIPS 204.
func highBits( ringElement,  parameters) [n]byte {
	var  [n]byte
	switch .γ2 {
	case 32:
		for  := range n {
			[] = highBits32(fieldFromMontgomery([]))
		}
	case 88:
		for  := range n {
			[] = highBits88(fieldFromMontgomery([]))
		}
	default:
		panic("mldsa: internal error: unsupported γ2")
	}
	return 
}

// useHint implements UseHint from FIPS 204.
//
// It is not constant-time.
func useHint( ringElement,  [n]byte,  parameters) [n]byte {
	var  [n]byte
	switch .γ2 {
	case 32:
		for  := range n {
			[] = useHint32([], [])
		}
	case 88:
		for  := range n {
			[] = useHint88([], [])
		}
	default:
		panic("mldsa: internal error: unsupported γ2")
	}
	return 
}

// makeHint implements MakeHint from FIPS 204.
func makeHint(, ,  ringElement,  parameters) ( [n]byte,  int) {
	switch .γ2 {
	case 32:
		for  := range n {
			[] = makeHint32([], [], [])
			 += int([])
		}
	case 88:
		for  := range n {
			[] = makeHint88([], [], [])
			 += int([])
		}
	default:
		panic("mldsa: internal error: unsupported γ2")
	}
	return , 
}

// highBits32 implements HighBits from FIPS 204 for γ2 = (q - 1) / 32.
func highBits32( uint32) byte {
	// The implementation is based on the reference implementation and on
	// BoringSSL. There are exhaustive tests in TestDecompose that compare it to
	// a straightforward implementation of Decompose from the spec, so for our
	// purposes it only has to work and be constant-time.
	 := ( + 127) >> 7
	 = (*1025 + (1 << 21)) >> 22
	 &= 0b1111
	return byte()
}

// decompose32 implements Decompose from FIPS 204 for γ2 = (q - 1) / 32.
//
// r1 is in [0, 15].
func decompose32( fieldElement) ( byte,  int32) {
	 := fieldFromMontgomery()
	 = highBits32()

	// r - r1 * (2 * γ2) mod± q
	 = int32() - int32()*2*(q-1)/32
	 = constantTimeSelectLessOrEqual(q/2+1, , -q, )

	return , 
}

// useHint32 implements UseHint from FIPS 204 for γ2 = (q - 1) / 32.
func useHint32( fieldElement,  byte) byte {
	const  = 16 // (q − 1) / (2 * γ2)
	,  := decompose32()
	if  == 1 {
		if  > 0 {
			 = ( + 1) % 
		} else {
			// Underflow is safe, because it operates modulo 256 (since the type
			// is byte), which is a multiple of m.
			 = ( - 1) % 
		}
	}
	return 
}

// makeHint32 implements MakeHint from FIPS 204 for γ2 = (q - 1) / 32.
func makeHint32(, ,  fieldElement) byte {
	// v1 = HighBits(r + z) = HighBits(w - cs2 + ct0 - ct0) = HighBits(w - cs2)
	 := fieldSub(, )
	 := highBits32(fieldFromMontgomery())
	// r1 = HighBits(r) = HighBits(w - cs2 + ct0)
	 := highBits32(fieldFromMontgomery(fieldAdd(, )))

	return byte(constanttime.ByteEq(, ) ^ 1)
}

// highBits88 implements HighBits from FIPS 204 for γ2 = (q - 1) / 88.
func highBits88( uint32) byte {
	// Like highBits32, this is exhaustively tested in TestDecompose.
	 := ( + 127) >> 7
	 = (*11275 + (1 << 23)) >> 24
	 = constantTimeSelectEqual(, 44, 0, )
	return byte()
}

// decompose88 implements Decompose from FIPS 204 for γ2 = (q - 1) / 88.
//
// r1 is in [0, 43].
func decompose88( fieldElement) ( byte,  int32) {
	 := fieldFromMontgomery()
	 = highBits88()

	// r - r1 * (2 * γ2) mod± q
	 = int32() - int32()*2*(q-1)/88
	 = constantTimeSelectLessOrEqual(q/2+1, , -q, )

	return , 
}

// useHint88 implements UseHint from FIPS 204 for γ2 = (q - 1) / 88.
func useHint88( fieldElement,  byte) byte {
	const  = 44 // (q − 1) / (2 * γ2)
	,  := decompose88()
	if  == 1 {
		if  > 0 {
			// (r1 + 1) mod m, for r1 in [0, m-1]
			if  == -1 {
				 = 0
			} else {
				++
			}
		} else {
			// (r1 - 1) % m, for r1 in [0, m-1]
			if  == 0 {
				 =  - 1
			} else {
				--
			}
		}
	}
	return 
}

// makeHint88 implements MakeHint from FIPS 204 for γ2 = (q - 1) / 88.
func makeHint88(, ,  fieldElement) byte {
	// Same as makeHint32 above.
	 := fieldSub(, )
	 := highBits88(fieldFromMontgomery())
	 := highBits88(fieldFromMontgomery(fieldAdd(, )))
	return byte(constanttime.ByteEq(, ) ^ 1)
}

// bitPack implements BitPack(r mod± q, γ₁-1, γ₁), which packs the centered
// coefficients of r into little-endian γ1+1-bit chunks. It appends to buf.
//
// It must only be applied to r with coefficients in [−γ₁+1, γ₁], as
// guaranteed by the rejection conditions in Sign.
func bitPack( []byte,  ringElement,  parameters) []byte {
	switch .γ1 {
	case 17:
		return bitPack18(, )
	case 19:
		return bitPack20(, )
	default:
		panic("mldsa: internal error: unsupported γ1")
	}
}

// bitPack18 implements BitPack(r mod± q, 2¹⁷-1, 2¹⁷), which packs the centered
// coefficients of r into little-endian 18-bit chunks. It appends to buf.
//
// It must only be applied to r with coefficients in [−2¹⁷+1, 2¹⁷], as
// guaranteed by the rejection conditions in Sign.
func bitPack18( []byte,  ringElement) []byte {
	,  := sliceForAppend(, 18*n/8)
	const  = 1 << 17
	for  := 0;  < n;  += 4 {
		// b - [−2¹⁷+1, 2¹⁷] = [0, 2²⁸-1]
		 :=  - fieldCenteredMod([])
		[0] = byte( << 0)
		[1] = byte( >> 8)
		[2] = byte( >> 16)
		 :=  - fieldCenteredMod([+1])
		[2] |= byte( << 2)
		[3] = byte( >> 6)
		[4] = byte( >> 14)
		 :=  - fieldCenteredMod([+2])
		[4] |= byte( << 4)
		[5] = byte( >> 4)
		[6] = byte( >> 12)
		 :=  - fieldCenteredMod([+3])
		[6] |= byte( << 6)
		[7] = byte( >> 2)
		[8] = byte( >> 10)
		 = [4*18/8:]
	}
	return 
}

// bitPack20 implements BitPack(r mod± q, 2¹⁹-1, 2¹⁹), which packs the centered
// coefficients of r into little-endian 20-bit chunks. It appends to buf.
//
// It must only be applied to r with coefficients in [−2¹⁹+1, 2¹⁹], as
// guaranteed by the rejection conditions in Sign.
func bitPack20( []byte,  ringElement) []byte {
	,  := sliceForAppend(, 20*n/8)
	const  = 1 << 19
	for  := 0;  < n;  += 2 {
		// b - [−2¹⁹+1, 2¹⁹] = [0, 2²⁰-1]
		 :=  - fieldCenteredMod([])
		[0] = byte( << 0)
		[1] = byte( >> 8)
		[2] = byte( >> 16)
		 :=  - fieldCenteredMod([+1])
		[2] |= byte( << 4)
		[3] = byte( >> 4)
		[4] = byte( >> 12)
		 = [2*20/8:]
	}
	return 
}

// bitUnpack implements BitUnpack(v, 2^γ1-1, 2^γ1), which unpacks each γ1+1 bits
// in little-endian into a coefficient in [-2^γ1+1, 2^γ1].
func bitUnpack( []byte,  parameters) ringElement {
	switch .γ1 {
	case 17:
		return bitUnpack18()
	case 19:
		return bitUnpack20()
	default:
		panic("mldsa: internal error: unsupported γ1")
	}
}

// bitUnpack18 implements BitUnpack(v, 2¹⁷-1, 2¹⁷), which unpacks each 18 bits
// in little-endian into a coefficient in [-2¹⁷+1, 2¹⁷].
func bitUnpack18( []byte) ringElement {
	if len() != 18*n/8 {
		panic("mldsa: internal error: invalid bitUnpack18 input length")
	}
	const  = 1 << 17
	const  = 1<<18 - 1
	var  ringElement
	for  := 0;  < n;  += 4 {
		 := uint32([0]) | uint32([1])<<8 | uint32([2])<<16
		[+0] = fieldSubToMontgomery(, &)
		 := uint32([2])>>2 | uint32([3])<<6 | uint32([4])<<14
		[+1] = fieldSubToMontgomery(, &)
		 := uint32([4])>>4 | uint32([5])<<4 | uint32([6])<<12
		[+2] = fieldSubToMontgomery(, &)
		 := uint32([6])>>6 | uint32([7])<<2 | uint32([8])<<10
		[+3] = fieldSubToMontgomery(, &)
		 = [4*18/8:]
	}
	return 
}

// bitUnpack20 implements BitUnpack(v, 2¹⁹-1, 2¹⁹), which unpacks each 20 bits
// in little-endian into a coefficient in [-2¹⁹+1, 2¹⁹].
func bitUnpack20( []byte) ringElement {
	if len() != 20*n/8 {
		panic("mldsa: internal error: invalid bitUnpack20 input length")
	}
	const  = 1 << 19
	const  = 1<<20 - 1
	var  ringElement
	for  := 0;  < n;  += 2 {
		 := uint32([0]) | uint32([1])<<8 | uint32([2])<<16
		[+0] = fieldSubToMontgomery(, &)
		 := uint32([2])>>4 | uint32([3])<<4 | uint32([4])<<12
		[+1] = fieldSubToMontgomery(, &)
		 = [2*20/8:]
	}
	return 
}

// sliceForAppend takes a slice and a requested number of bytes. It returns a
// slice with the contents of the given slice followed by that many bytes and a
// second slice that aliases into it and contains only the extra bytes. If the
// original slice has sufficient capacity then no allocation is performed.
func sliceForAppend( []byte,  int) (,  []byte) {
	if  := len() + ; cap() >=  {
		 = [:]
	} else {
		 = make([]byte, )
		copy(, )
	}
	 = [len():]
	return
}

// constantTimeSelectLessOrEqual returns yes if a <= b, no otherwise, in constant time.
func constantTimeSelectLessOrEqual(, , ,  int32) int32 {
	return int32(constanttime.Select(constanttime.LessOrEq(int(), int()), int(), int()))
}

// constantTimeSelectEqual returns yes if a == b, no otherwise, in constant time.
func constantTimeSelectEqual(, , ,  uint32) uint32 {
	return uint32(constanttime.Select(constanttime.Eq(int32(), int32()), int(), int()))
}

// constantTimeAbs returns the absolute value of x in constant time.
func constantTimeAbs( int32) uint32 {
	return uint32(constantTimeSelectLessOrEqual(0, , , -))
}