message: Add equivalence_classes_to_hashtbl method
diff: use crate::circuits::gate::{CircuitGate, GateType};
  use crate::circuits::wires::{Wire, COLUMNS, PERMUTS};
  use ark_ff::FftField;
  -use std::collections::HashMap;
  +use std::collections::{HashMap, HashSet};
  /** A gate interface, parameterized by a field. */
  trait GateVector<Field: FftField> {
  @@ -13,6 +13,7 @@ trait GateVector<Field: FftField> {
  /** A row indexing in a constraint system.
  Either a public input row, or a non-public input row that starts at index 0.
  */
  +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
  enum Row {
  PublicInput(usize),
  AfterPublicInput(usize),
  @@ -30,6 +31,7 @@ impl Row {
  /* TODO: rename module Position to Permutation/Wiring? */
  /** A position represents the position of a cell in the constraint system.
  A position is a row and a column. */
  +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
  struct Position<Row> {
  row: Row,
  col: usize,
  @@ -208,10 +210,10 @@ enum PlonkConstraint<Var, Field> {
  }
  /* TODO: This is a Unique_id in OCaml. */
  -#[derive(Clone)]
  +#[derive(Clone, PartialEq, Eq, Hash)]
  struct InternalVar(usize);
  -#[derive(Clone)]
  +#[derive(Clone, PartialEq, Eq, Hash)]
  enum V {
  /** An external variable (generated by snarky, via [exists]). */
  External(usize),
  @@ -270,3 +272,32 @@ struct SnarkyConstraintSystem<Field, RustGates> {
  */
  union_finds: disjoint_set::DisjointSet<V>,
  }
  +
  +impl<Field: FftField, Gates: GateVector<Field>> SnarkyConstraintSystem<Field, Gates> {
  + /** Converts the set of permutations (equivalence_classes) to
  + a hash table that maps each position to the next one.
  + For example, if one of the equivalence class is [pos1, pos3, pos7],
  + the function will return a hashtable that maps pos1 to pos3,
  + pos3 to pos7, and pos7 to pos1.
  + */
  + fn equivalence_classes_to_hashtbl(self: &mut Self) -> HashMap<Position<Row>, Position<Row>> {
  + let mut equivalence_classes: HashMap<usize, HashSet<Position<Row>>> = HashMap::new();
  + for (key, data) in self.equivalence_classes.iter() {
  + let u = self.union_finds.find(key.clone()).unwrap();
  + let entry = equivalence_classes.entry(u).or_insert(HashSet::new());
  + for position in data.iter() {
  + entry.insert(position.clone());
  + }
  + }
  + let mut res: HashMap<Position<Row>, Position<Row>> = HashMap::new();
  + for data in equivalence_classes.into_values() {
  + let mut data: Vec<_> = data.into_iter().collect();
  + /* HashSet uses an unstable order, so sort to avoid dealing with that. */
  + data.sort();
  + for (i, j) in (0..data.len()).zip((1..data.len()).chain(0..=0)) {
  + res.insert(data[i].clone(), data[j].clone());
  + }
  + }
  + res
  + }
  +}
  