Depends on #2151.
This PR solves all performance issues with `std::fingerprint`. It now
has two functions:
- `fingerprint()`: Is similar to a pre-#1985 version of `fingerprint()`.
It computes the fingerprint in a recursive way. It should *not* be used
to generate expressions, because they would be exponentially large. To
prevent that, I changed the type so that it can only be applied to `fe`.
- `fingerprint_inter()`: Is the analog for expressions. It stores
intermediate results in intermediate columns. Because of that, it needs
to be a `constr` function. It generates $O(n)$ intermediate columns of
constant size.
I also added a test for the `fingerprint_inter` function and changed all
protocols (bus, permutation, lookup) to use `fingerprint_inter` to
generate constraints.