package guts

import (
	
	
)

// CompressNode compresses a node into a 16-word output.
func ( Node) ( [16]uint32) {
	 := func(, , , , ,  uint32) (uint32, uint32, uint32, uint32) {
		 +=  + 
		 = bits.RotateLeft32(^, -16)
		 += 
		 = bits.RotateLeft32(^, -12)
		 +=  + 
		 = bits.RotateLeft32(^, -8)
		 += 
		 = bits.RotateLeft32(^, -7)
		return , , , 
	}

	// NOTE: we unroll all of the rounds, as well as the permutations that occur
	// between rounds.

	// round 1 (also initializes state)
	// columns
	, , ,  := (.CV[0], .CV[4], IV[0], uint32(.Counter), .Block[0], .Block[1])
	, , ,  := (.CV[1], .CV[5], IV[1], uint32(.Counter>>32), .Block[2], .Block[3])
	, , ,  := (.CV[2], .CV[6], IV[2], .BlockLen, .Block[4], .Block[5])
	, , ,  := (.CV[3], .CV[7], IV[3], .Flags, .Block[6], .Block[7])
	// diagonals
	, , ,  = (, , , , .Block[8], .Block[9])
	, , ,  = (, , , , .Block[10], .Block[11])
	, , ,  = (, , , , .Block[12], .Block[13])
	, , ,  = (, , , , .Block[14], .Block[15])

	// round 2
	, , ,  = (, , , , .Block[2], .Block[6])
	, , ,  = (, , , , .Block[3], .Block[10])
	, , ,  = (, , , , .Block[7], .Block[0])
	, , ,  = (, , , , .Block[4], .Block[13])
	, , ,  = (, , , , .Block[1], .Block[11])
	, , ,  = (, , , , .Block[12], .Block[5])
	, , ,  = (, , , , .Block[9], .Block[14])
	, , ,  = (, , , , .Block[15], .Block[8])

	// round 3
	, , ,  = (, , , , .Block[3], .Block[4])
	, , ,  = (, , , , .Block[10], .Block[12])
	, , ,  = (, , , , .Block[13], .Block[2])
	, , ,  = (, , , , .Block[7], .Block[14])
	, , ,  = (, , , , .Block[6], .Block[5])
	, , ,  = (, , , , .Block[9], .Block[0])
	, , ,  = (, , , , .Block[11], .Block[15])
	, , ,  = (, , , , .Block[8], .Block[1])

	// round 4
	, , ,  = (, , , , .Block[10], .Block[7])
	, , ,  = (, , , , .Block[12], .Block[9])
	, , ,  = (, , , , .Block[14], .Block[3])
	, , ,  = (, , , , .Block[13], .Block[15])
	, , ,  = (, , , , .Block[4], .Block[0])
	, , ,  = (, , , , .Block[11], .Block[2])
	, , ,  = (, , , , .Block[5], .Block[8])
	, , ,  = (, , , , .Block[1], .Block[6])

	// round 5
	, , ,  = (, , , , .Block[12], .Block[13])
	, , ,  = (, , , , .Block[9], .Block[11])
	, , ,  = (, , , , .Block[15], .Block[10])
	, , ,  = (, , , , .Block[14], .Block[8])
	, , ,  = (, , , , .Block[7], .Block[2])
	, , ,  = (, , , , .Block[5], .Block[3])
	, , ,  = (, , , , .Block[0], .Block[1])
	, , ,  = (, , , , .Block[6], .Block[4])

	// round 6
	, , ,  = (, , , , .Block[9], .Block[14])
	, , ,  = (, , , , .Block[11], .Block[5])
	, , ,  = (, , , , .Block[8], .Block[12])
	, , ,  = (, , , , .Block[15], .Block[1])
	, , ,  = (, , , , .Block[13], .Block[3])
	, , ,  = (, , , , .Block[0], .Block[10])
	, , ,  = (, , , , .Block[2], .Block[6])
	, , ,  = (, , , , .Block[4], .Block[7])

	// round 7
	, , ,  = (, , , , .Block[11], .Block[15])
	, , ,  = (, , , , .Block[5], .Block[0])
	, , ,  = (, , , , .Block[1], .Block[9])
	, , ,  = (, , , , .Block[8], .Block[6])
	, , ,  = (, , , , .Block[14], .Block[10])
	, , ,  = (, , , , .Block[2], .Block[12])
	, , ,  = (, , , , .Block[3], .Block[4])
	, , ,  = (, , , , .Block[7], .Block[13])

	// finalization
	return [16]uint32{
		 ^ ,  ^ ,  ^ ,  ^ ,
		 ^ ,  ^ ,  ^ ,  ^ ,
		 ^ .CV[0],  ^ .CV[1],  ^ .CV[2],  ^ .CV[3],
		 ^ .CV[4],  ^ .CV[5],  ^ .CV[6],  ^ .CV[7],
	}
}

// ChainingValue compresses n and returns the first 8 output words.
func ( Node) ( [8]uint32) {
	 := CompressNode()
	copy([:], [:])
	return
}

func compressBufferGeneric( *[MaxSIMD * ChunkSize]byte,  int,  *[8]uint32,  uint64,  uint32) ( Node) {
	if  <= ChunkSize {
		return CompressChunk([:], , , )
	}
	var  [MaxSIMD][8]uint32
	var  uint64
	for  := bytes.NewBuffer([:]); .Len() > 0; ++ {
		[] = ChainingValue(CompressChunk(.Next(ChunkSize), , +, ))
	}
	return mergeSubtrees(&, , , )
}

func compressBlocksGeneric( *[MaxSIMD][64]byte,  Node) {
	for  := range  {
		[] = WordsToBytes(CompressNode())
		.Counter++
	}
}

func mergeSubtreesGeneric( *[MaxSIMD][8]uint32,  uint64,  *[8]uint32,  uint32) Node {
	for  > 2 {
		 :=  / 2
		for  := range [:] {
			[] = ChainingValue(ParentNode([*2], [*2+1], , ))
		}
		if %2 != 0 {
			[] = [*2]
			++
		}
		 = 
	}
	return ParentNode([0], [1], , )
}