fn flatten_add_or_sub(o_lhs: RcExpr, o_rhs: RcExpr, is_subtract: bool) -> RcExpr {
    let o_span = o_lhs.span.to(o_rhs.span);
    let lhs = flatten_expr(o_lhs);
    let rhs = flatten_expr(o_rhs);

    // Leading coefficients to fold constants into.
    let mut coeff = 0.;
    // Terms -> coefficients present in the expression.
    let mut terms = BTreeMap::<RcExpr, f64>::new();

    let mut args = VecDeque::with_capacity(2);
    args.push_back(lhs);
    args.push_back(rhs);

    // If this is not a subtraction, the first two args are both on the add side.
    let mut args_before_sub = if is_subtract { 1 } else { 2 };

    while let Some(arg) = args.pop_front() {
        let sub_side = args_before_sub <= 0;
        args_before_sub -= 1;

        let arg = unwrap_expr(arg.clone());

        match arg.as_ref() {
            Expr::Const(konst) => {
                if sub_side {
                    coeff -= konst;
                } else {
                    coeff += konst;
                }
            }
            // `flatten` will always normalize add/sub expressions to add, so we only have to
            // handle that.
            Expr::BinaryExpr(BinaryExpr { op, lhs, rhs }) if op == &BinaryOperator::Plus => {
                if sub_side {
                    // 1 - (2 + 3) -> 1 - 2 - 3; add both operands to the sub side.
                    args.push_back(lhs.clone());
                    args.push_back(rhs.clone());
                } else {
                    // 1 + (2 + 3) -> 1 + 2 + 3
                    args.push_front(lhs.clone());
                    args.push_front(rhs.clone());
                    args_before_sub += 2;
                }
            }
            _ => {
                // Otherwise the arg is something we cannot further decompose in an add/sub context
                // (e.g. a variable or an exponentiation), so add it as a term.
                // TODO: see if we can handle other things more granularly
                let entry = terms.entry(arg).or_insert(0.);
                if sub_side {
                    *entry -= 1.;
                } else {
                    *entry += 1.;
                }
            }
        }
    }

    let mut new_args: Vec<RcExpr> = Vec::with_capacity(1 + terms.len());
    if coeff != 0. {
        new_args.push(rc_expr!(Expr::Const(coeff), o_span));
    }
    for (term, coeff) in terms {
        if coeff == 0. {
            // The happiest path :)
            continue;
        } else if (coeff - 1.).abs() < std::f64::EPSILON {
            // coeff == 1
            new_args.push(term.clone());
        } else if (coeff - -1.).abs() < std::f64::EPSILON {
            // coeff == -1
            let neg = UnaryExpr::negate(term.clone());
            new_args.push(rc_expr!(Expr::UnaryExpr(neg), o_span));
        } else {
            let mult = BinaryExpr::mult(rc_expr!(Expr::Const(coeff), o_span), term.clone());
            new_args.push(rc_expr!(Expr::BinaryExpr(mult), o_span));
        }
    }

    match new_args.len() {
        0 => rc_expr!(Expr::Const(0.), o_span),
        1 => new_args.remove(0),
        _ => unflatten_binary_expr(&new_args, BinaryOperator::Plus, UnflattenStrategy::Left),
    }
}
/// Flattens an addition or subtraction, folding constants and like terms as far as possible.
/// The flattened expression is always normalized to an addition.
///
/// ```text
/// 1 + 2x - 3 + x -> -2 + 3x
/// ```
Flattens an addition or subtraction, folding constants and like terms as far as possible.
The flattened expression is always normalized to an addition.


Flattens an addition or subtraction folding constants and like terms as far as possible . The flattened expression is always normalized to an addition .
fn flatten_add_or_sub(o_lhs: RcExpr, o_rhs: RcExpr, is_subtract: bool) -> RcExpr {
    let o_span = o_lhs.span.to(o_rhs.span);
    let lhs = flatten_expr(o_lhs);
    let rhs = flatten_expr(o_rhs);
    // Leading coefficients to fold constants into.
    let mut coeff = 0.;
    // Terms -> coefficients present in the expression.
    let mut terms = BTreeMap::<RcExpr, f64>::new();
    let mut args = VecDeque::with_capacity(2);
    args.push_back(lhs);
    args.push_back(rhs);
    // If this is not a subtraction, the first two args are both on the add side.
    let mut args_before_sub = if is_subtract { 1 } else { 2 };
    while let Some(arg) = args.pop_front() {
        let sub_side = args_before_sub <= 0;
        args_before_sub -= 1;
        let arg = unwrap_expr(arg.clone());
        match arg.as_ref() {
            Expr::Const(konst) => {
                if sub_side {
                    coeff -= konst;
                } else {
                    coeff += konst;
                }
            }
            // `flatten` will always normalize add/sub expressions to add, so we only have to
            // handle that.
            Expr::BinaryExpr(BinaryExpr { op, lhs, rhs }) if op == &BinaryOperator::Plus => {
                if sub_side {
                    // 1 - (2 + 3) -> 1 - 2 - 3; add both operands to the sub side.
                    args.push_back(lhs.clone());
                    args.push_back(rhs.clone());
                } else {
                    // 1 + (2 + 3) -> 1 + 2 + 3
                    args.push_front(lhs.clone());
                    args.push_front(rhs.clone());
                    args_before_sub += 2;
                }
            }
            _ => {
                // Otherwise the arg is something we cannot further decompose in an add/sub context
                // (e.g. a variable or an exponentiation), so add it as a term.
                // TODO: see if we can handle other things more granularly
                let entry = terms.entry(arg).or_insert(0.);
                if sub_side {
                    *entry -= 1.;
                } else {
                    *entry += 1.;
                }
            }
        }
    }
    let mut new_args: Vec<RcExpr> = Vec::with_capacity(1 + terms.len());
    if coeff != 0. {
        new_args.push(rc_expr!(Expr::Const(coeff), o_span));
    }
    for (term, coeff) in terms {
        if coeff == 0. {
            // The happiest path :)
            continue;
        } else if (coeff - 1.).abs() < std::f64::EPSILON {
            // coeff == 1
            new_args.push(term.clone());
        } else if (coeff - -1.).abs() < std::f64::EPSILON {
            // coeff == -1
            let neg = UnaryExpr::negate(term.clone());
            new_args.push(rc_expr!(Expr::UnaryExpr(neg), o_span));
        } else {
            let mult = BinaryExpr::mult(rc_expr!(Expr::Const(coeff), o_span), term.clone());
            new_args.push(rc_expr!(Expr::BinaryExpr(mult), o_span));
        }
    }
    match new_args.len() {
        0 => rc_expr!(Expr::Const(0.), o_span),
        1 => new_args.remove(0),
        _ => unflatten_binary_expr(&new_args, BinaryOperator::Plus, UnflattenStrategy::Left),
    }
}
fn flatten_add_or_sub ( o_lhs : RcExpr , o_rhs : RcExpr , is_subtract : bool ) -> RcExpr { let o_span = o_lhs . span . to ( o_rhs . span ) ; let lhs = flatten_expr ( o_lhs ) ; let rhs = flatten_expr ( o_rhs ) ; // Leading coefficients to fold constants into. let mut coeff = 0. ; // Terms -> coefficients present in the expression. let mut terms = BTreeMap :: < RcExpr , f64 > :: new ( ) ; let mut args = VecDeque :: with_capacity ( 2 ) ; args . push_back ( lhs ) ; args . push_back ( rhs ) ; // If this is not a subtraction, the first two args are both on the add side. let mut args_before_sub = if is_subtract { 1 } else { 2 } ; while let Some ( arg ) = args . pop_front ( ) { let sub_side = args_before_sub <= 0 ; args_before_sub -= 1 ; let arg = unwrap_expr ( arg . clone ( ) ) ; match arg . as_ref ( ) { Expr :: Const ( konst ) => { if sub_side { coeff -= konst ; } else { coeff += konst ; } } // `flatten` will always normalize add/sub expressions to add, so we only have to // handle that. Expr :: BinaryExpr ( BinaryExpr { op , lhs , rhs } ) if op == & BinaryOperator :: Plus => { if sub_side { // 1 - (2 + 3) -> 1 - 2 - 3; add both operands to the sub side. args . push_back ( lhs . clone ( ) ) ; args . push_back ( rhs . clone ( ) ) ; } else { // 1 + (2 + 3) -> 1 + 2 + 3 args . push_front ( lhs . clone ( ) ) ; args . push_front ( rhs . clone ( ) ) ; args_before_sub += 2 ; } } _ => { // Otherwise the arg is something we cannot further decompose in an add/sub context // (e.g. a variable or an exponentiation), so add it as a term. // TODO: see if we can handle other things more granularly let entry = terms . entry ( arg ) . or_insert ( 0. ) ; if sub_side { * entry -= 1. ; } else { * entry += 1. ; } } } } let mut new_args : Vec < RcExpr > = Vec :: with_capacity ( 1 + terms . len ( ) ) ; if coeff != 0. { new_args . push ( rc_expr ! ( Expr Const ( coeff ) o_span ) ) ; } for ( term , coeff ) in terms { if coeff == 0. { // The happiest path :) continue ; } else if ( coeff - 1. ) . abs ( ) < std :: f64 :: EPSILON { // coeff == 1 new_args . push ( term . clone ( ) ) ; } else if ( coeff - - 1. ) . abs ( ) < std :: f64 :: EPSILON { // coeff == -1 let neg = UnaryExpr :: negate ( term . clone ( ) ) ; new_args . push ( rc_expr ! ( Expr UnaryExpr ( neg ) o_span ) ) ; } else { let mult = BinaryExpr :: mult ( rc_expr ! ( Expr Const ( coeff ) o_span ) , term . clone ( ) ) ; new_args . push ( rc_expr ! ( Expr BinaryExpr ( mult ) o_span ) ) ; } } match new_args . len ( ) { 0 => rc_expr ! ( Expr Const ( 0. ) o_span ) , 1 => new_args . remove ( 0 ) , _ => unflatten_binary_expr ( & new_args , BinaryOperator :: Plus , UnflattenStrategy :: Left ) , } }
Flattens an addition or subtraction, folding constants and like terms as far as possible.
Flattens an addition or subtraction folding constants and like terms as far as possible .
o_lhs
RcExpr
o_rhs
RcExpr
is_subtract
bool
o_lhs
RcExpr
o_rhs
RcExpr
is_subtract
bool