/// - `new_expression`: The updated [`Expression`] that replaces the original one after applying the rule.
/// - `new_top`: An additional top-level [`Expression`] constraint that should be added to the model. If no top-level
/// - `symbols`: A [`SymbolTable`] containing any new symbol definitions or modifications to be added to the model's
/// - [`Reduction::new`]: Creates a reduction with a new expression, top-level constraint, and symbol modifications.
/// - [`Reduction::pure`]: Creates a reduction with only a new expression and no side-effects on the symbol table or constraints.
/// - [`Reduction::with_symbols`]: Creates a reduction with a new expression and symbol table modifications, but no top-level constraint.
/// - [`Reduction::with_top`]: Creates a reduction with a new expression and a top-level constraint, but no symbol table modifications.
/// The `apply` method allows for applying the changes represented by the `Reduction` to a [`Model`].
/// - [`ApplicationResult`]: Represents the result of applying a rule, which may either be a `Reduction` or an `ApplicationError`.
pub fn new(new_expression: Expression, new_top: Expression, symbols: SymbolTable) -> Self {
* - `rule_sets` A list of rule set names and priorities that this rule is a part of. This is used to populate rulesets at runtime.
pub rule_sets: &'a [(&'a str, u16)], // (name, priority). At runtime, we add the rule to rulesets