The PSE's [zkEVM circuits](https://github.com/privacy-scaling-explorations/zkevm-circuits) have a variety of helpful 'gadgets' for various functionalities. One of these is the [```LTChip```](https://github.com/privacy-scaling-explorations/zkevm-circuits/blob/main/gadgets/src/less_than.rs) gadget, which is used for checking, given inputs ```lhs``` and ```rhs```, whether ```lhs < rhs ``` holds true. It can be used as a helper as a part of various larger circuits, something like this: ```rust struct SomeLargerCircuitConfig<F> { q_enable: Selector, .... .... check: Column<Advice>, lt: LtConfig<F, NUM_BYTES> } ``` Here, ```LTConfig``` is the configuration for the gadget as defined in the zkevm-circuits. But how does it look like and work under the hood and what limitations does it possess? ## Analysing ```LTConfig``` The default ```LTConfig``` is defined in the following manner: ```rust /// Config for the Lt chip. #[derive(Clone, Copy, Debug)] pub struct LtConfig<F, const N_BYTES: usize> { /// Denotes the lt outcome. If lhs < rhs then lt == 1, otherwise lt == 0. pub lt: Column<Advice>, /// Denotes the bytes representation of the difference between lhs and rhs. pub diff: [Column<Advice>; N_BYTES], /// Denotes the range within which each byte should lie. pub u8: Column<Fixed>, /// Denotes the range within which both lhs and rhs lie. pub range: F, } ``` What this means is that the configuration looks something like this: | lt | diff[0] | diff[1] | diff [2] | ..... | diff[N_BYTES-1] | u8| | -------- | -------- | -------- | -------- |-------- | --------- | -------- |1 | ```0xff``` | ``0xff`` | ```0xff```| ....| ```0xff``` | The column contains a lookup table ```u8``` which can be used to perform a range check on the value of each byte, 1 advice column for the ```lt``` status (1 if ```lhs<rhs``` and 0 otherwise) and the 2<sup>N_BYTES</sup> advice columns for the difference. ``` diff[0], diff[1], diff[2] .... diff[N_BYTES-1] ``` denote the respective bytes of the difference of the LHS and RHS. ### How is this difference calculated and hence, how is the circuit constrained? Remember the ``` range ``` field in the ```LTConfig``` struct? This will come into play now. ```range``` is defined as ```NUM_BYTES*8``` (basically the decimal range in which the LHS and RHS will lie) This difference is calculated using the following high level logic: ``` if lhs < rhs: difference = lhs - rhs + range else difference = lhs - rhs ``` What this logic does is that it prevents overflow by adding ```range``` in case ```lhs<rhs```and this comes in handy when constraining the circuit as well. Now, we constrain the "larger" circuit with the following conditions: 1. Check the fact that, given inputs ```lhs``` and ```rhs``` to the circuit, ```lhs - rhs - difference + (range*lt) == 0``` 2. The ```lt``` value is a boolean. ## So what's the issue? We use this gadget in [Summa](https://github.com/summa-dev/summa-solvency), and the main issue here is that this 'horizontal' approach results in a huge number of advice columns. Specifically, for our use case, ```N_BYTES = 31 ``` and hence this results in 31 advice columns for difference in the ```LTChip``` alone. We need to reduce this to 3.