// Code generated by bitwrap synth; hand-maintained for now.
// Source schema: ZKPoll Vote:1.0.0, extension: tally-correctness proof.

package prover

import (
	"github.com/consensys/gnark/frontend"
)

// TallyProofMaxReveals is the default batch size — kept for backward
// compatibility with callers that pre-date the 64/256-slot variants.
// New code should reference the size constants on each TallyProofCircuitN
// or use selectTallyCircuitSize.
const TallyProofMaxReveals = 16

// TallyProofMaxChoices is the fixed number of choice bins the circuit
// tallies. Matches the 8-bit VoteChoice range in VoteCastCircuit.
const TallyProofMaxChoices = 8

// TallyProofCircuit16 proves the Petri net `castVote` transition folds the
// revealed (secret, choice) pairs into the claimed tally vector.
// Size-16 batch — the smallest variant. See tally_gen_64.go and
// tally_gen_256.go for larger batches that share the same constraint logic
// via tallyProofConstraints().
//
// Public inputs:
//   - PollID: same scalar used in the voteCast nullifier derivation.
//   - Commitments[i]: mimcHash(secret_i, choice_i), as originally stored
//     when the vote was cast. Inactive slots must be zero.
//   - Nullifiers[i]: mimcHash(secret_i, PollID). Inactive slots must be zero.
//   - Tallies[j]: claimed count of active entries where choice_i == j.
//   - NumReveals: number of active (revealed) entries. Must equal sum(Active)
//     and sum(Tallies).
//
// Private witness:
//   - Secrets[i], Choices[i]: revealed (secret, choice) for active slots.
//   - Active[i]: boolean mask. 1 for real reveals, 0 for padding.
type TallyProofCircuit16 struct {
	PollID      frontend.Variable                 `gnark:",public"`
	NumReveals  frontend.Variable                 `gnark:",public"`
	Commitments [16]frontend.Variable             `gnark:",public"`
	Nullifiers  [16]frontend.Variable             `gnark:",public"`
	Tallies     [TallyProofMaxChoices]frontend.Variable `gnark:",public"`

	Secrets [16]frontend.Variable
	Choices [16]frontend.Variable
	Active  [16]frontend.Variable
}

// TallyProofCircuit is an alias for TallyProofCircuit16 so existing tests
// and integration points that pre-date the sized variants keep compiling.
// New code should reference TallyProofCircuit16 directly.
type TallyProofCircuit = TallyProofCircuit16

// Define delegates to tallyProofConstraints over the struct's slice views.
func (c *TallyProofCircuit16) Define(api frontend.API) error {
	return tallyProofConstraints(api, c.PollID, c.NumReveals,
		c.Commitments[:], c.Nullifiers[:], c.Tallies[:],
		c.Secrets[:], c.Choices[:], c.Active[:])
}

// tallyProofConstraints lays down the shared constraint system for every
// sized TallyProofCircuitN. Keeping a single implementation here prevents
// drift between the 16/64/256 variants — they only differ in array size.
//
// Constraints enforced (for each slot i in [0, len(secrets))):
//  1. Active[i] is boolean.
//  2. If Active[i]==1: Commitments[i] == mimc(Secrets[i], Choices[i]).
//  3. If Active[i]==1: Nullifiers[i] == mimc(Secrets[i], PollID).
//  4. Choices[i] fits in 8 bits.
//  5. For each choice bin j in [0, TallyProofMaxChoices):
//     Tallies[j] == sum_i Active[i] * IsEqual(Choices[i], j).
//  6. sum(Active) == NumReveals.
//  7. sum(Tallies) == NumReveals.
//
// The (diff * active == 0) pattern lets us apply binds only to active
// slots without branching, which would be expensive in a SNARK circuit.
func tallyProofConstraints(
	api frontend.API,
	pollID, numReveals frontend.Variable,
	commitments, nullifiers, tallies []frontend.Variable,
	secrets, choices, active []frontend.Variable,
) error {
	n := len(secrets)

	for i := 0; i < n; i++ {
		api.AssertIsBoolean(active[i])

		expectedCommit := synthMimcHash(api, secrets[i], choices[i])
		commitDiff := api.Sub(commitments[i], expectedCommit)
		api.AssertIsEqual(api.Mul(commitDiff, active[i]), 0)

		expectedNullifier := synthMimcHash(api, secrets[i], pollID)
		nullifierDiff := api.Sub(nullifiers[i], expectedNullifier)
		api.AssertIsEqual(api.Mul(nullifierDiff, active[i]), 0)

		api.ToBinary(choices[i], 8)
	}

	for j := 0; j < len(tallies); j++ {
		var count frontend.Variable = 0
		for i := 0; i < n; i++ {
			eq := api.IsZero(api.Sub(choices[i], j))
			count = api.Add(count, api.Mul(eq, active[i]))
		}
		api.AssertIsEqual(count, tallies[j])
	}

	var activeSum frontend.Variable = 0
	for i := 0; i < n; i++ {
		activeSum = api.Add(activeSum, active[i])
	}
	api.AssertIsEqual(activeSum, numReveals)

	var tallySum frontend.Variable = 0
	for j := 0; j < len(tallies); j++ {
		tallySum = api.Add(tallySum, tallies[j])
	}
	api.AssertIsEqual(tallySum, numReveals)

	return nil
}
