pub fn count_permutation_constraints<F: Field>(
    sends: &[Interaction<F>],
    receives: &[Interaction<F>],
    batch_size: usize,
    commit_scope: InteractionScope,
) -> usize {
    let mut count = 0;

    let empty = vec![];
    let (scoped_sends, scoped_receives) = scoped_interactions(sends, receives);
    let local_sends = scoped_sends.get(&InteractionScope::Local).unwrap_or(&empty);
    let local_receives = scoped_receives.get(&InteractionScope::Local).unwrap_or(&empty);

    let num_local_interactions = local_sends.len() + local_receives.len();

    if num_local_interactions > 0 {
        let local_permutation_width =
            local_permutation_trace_width(num_local_interactions, batch_size);
        // We loop over (local_permutation_width - 1) iterations with one assert per iteration.
        count += local_permutation_width - 1;

        // One assert that cumulative sum is initialized to `phi_local` on the first row.
        // Two asserts that the cumulative sum is constrained to `phi_next - phi_local` on the
        // transition rows.
        count += 3;
    }

    // If the chip's scope is `InteractionScope::Global`, 14 asserts that
    // the last row's final 14 columns is equal to the global cumulative sum.
    if commit_scope == InteractionScope::Global {
        count += 14;
    }

    count
}
Counts the number of permutation constraints for the given chip.

IMPORTANT: This function must be manually updated if any changes are made to
`eval_permutation_constraints`. The current count includes:
- For local interactions: `local_permutation_trace_width(sends.len() + receives.len(),
batch_size)` + 3 local cumulative sum constraints.
- For global scope: 14 additional constraints for the global cumulative sum.
crates/stark/src/permutation.rs