//! can end up comparing a one-hot variable with a bit-vector one. The encoding-specific rules all
/// never reaches this rule. Remaining operands may have different encodings, use an encoding with
/// no rule for the operation, or include Boolean indicators needing an actual-value circuit view.
#[register_rule("SAT", 4000, [Eq, Neq, Lt, Gt, Leq, Geq, AllDiff, AllDifferentExcept, Table, NegativeTable, SatElement, SatObjective, Sum, Product, Min, Max, Abs, Neg, SafeDiv, SafeMod, SafePow])]
&& !matches!(&input, Expr::SATInt(_, encoding, _, _) if needs_actual_value_circuit(encoding)))
/// `unwrap_list` only recognises the implied `int(1..)` index domain, but a matrix rebuilt from its
/// components keeps the one it was declared with. These rules look through a child to reach the
fn to_log(expr: Expr, decisions: &mut Vec<SatEncodingDecision>, symbols: &mut SymbolTable) -> Expr {
let mut decoded = add_unsigned_constant(&bits, i64::from(low), width, decisions, symbols);
/// `x = low + i` exactly when `x >= low + i` holds and `x >= low + i + 1` does not; past the top
/// The sign-and-magnitude bits of a constant: its magnitude, least significant first, then the sign.
/// The magnitude is inverted and incremented when the sign is set: with the sign as the carry in,
/// bit `i` is `(m_i xor s) xor carry_i` and the carry on is `(m_i xor s) and carry_i`. Bits past
/// Constants have no representation of their own to follow, and encoding one eagerly would mean
/// guessing: the same `2` belongs in a bit vector next to a log-encoded variable and in a one-hot
/// vector next to a direct-encoded one. Deferring until the operation is known keeps the encodings
#[register_rule("SAT", 9400, [Eq, Neq, Lt, Gt, Leq, Geq, Sum, Product, Min, Max, SafeDiv, SafeMod, SafePow])]
Expr::Atomic(_, Atom::Literal(Literal::Int(value))) => sat_int_literal(&encoding, value),