use {
    Symbol,
    SymbolType,
    Constraint,
    Variable,
    Expression,
    Term,
    Row,
    AddConstraintError,
    RemoveConstraintError,
    InternalSolverError,
    SuggestValueError,
    AddEditVariableError,
    RemoveEditVariableError,
    RelationalOperator,
    near_zero
};

use ::std::rc::Rc;
use ::std::cell::RefCell;
use ::std::collections::{ HashMap, HashSet };
use ::std::collections::hash_map::Entry;

#[derive(Copy, Clone)]
struct Tag {
    marker: Symbol,
    other: Symbol
}

#[derive(Clone)]
struct EditInfo {
    tag: Tag,
    constraint: Constraint,
    constant: f64
}

/// A constraint solver using the Cassowary algorithm. For proper usage please see the top level crate documentation.
pub struct Solver {
    cns: HashMap<Constraint, Tag>,
    var_data: HashMap<Variable, (f64, Symbol, usize)>,
    var_for_symbol: HashMap<Symbol, Variable>,
    public_changes: Vec<(Variable, f64)>,
    changed: HashSet<Variable>,
    should_clear_changes: bool,
    rows: HashMap<Symbol, Box<Row>>,
    edits: HashMap<Variable, EditInfo>,
    infeasible_rows: Vec<Symbol>, // never contains external symbols
    objective: Rc<RefCell<Row>>,
    artificial: Option<Rc<RefCell<Row>>>,
    id_tick: usize
}

impl Solver {
    /// Construct a new solver.
    pub fn new() -> Solver {
        Solver {
            cns: HashMap::new(),
            var_data: HashMap::new(),
            var_for_symbol: HashMap::new(),
            public_changes: Vec::new(),
            changed: HashSet::new(),
            should_clear_changes: false,
            rows: HashMap::new(),
            edits: HashMap::new(),
            infeasible_rows: Vec::new(),
            objective: Rc::new(RefCell::new(Row::new(0.0))),
            artificial: None,
            id_tick: 1
        }
    }

    pub fn add_constraints<'a, I: IntoIterator<Item = &'a Constraint>>(
        &mut self,
        constraints: I) -> Result<(), AddConstraintError>
    {
        for constraint in constraints {
            try!(self.add_constraint(constraint.clone()));
        }
        Ok(())
    }

    /// Add a constraint to the solver.
    pub fn add_constraint(&mut self, constraint: Constraint) -> Result<(), AddConstraintError> {
        if self.cns.contains_key(&constraint) {
            return Err(AddConstraintError::DuplicateConstraint);
        }

        // Creating a row causes symbols to reserved for the variables
        // in the constraint. If this method exits with an exception,
        // then its possible those variables will linger in the var map.
        // Since its likely that those variables will be used in other
        // constraints and since exceptional conditions are uncommon,
        // i'm not too worried about aggressive cleanup of the var map.
        let (mut row, tag) = self.create_row(&constraint);
        let mut subject = Solver::choose_subject(&row, &tag);

        // If chooseSubject could find a valid entering symbol, one
        // last option is available if the entire row is composed of
        // dummy variables. If the constant of the row is zero, then
        // this represents redundant constraints and the new dummy
        // marker can enter the basis. If the constant is non-zero,
        // then it represents an unsatisfiable constraint.
        if subject.type_() == SymbolType::Invalid && Solver::all_dummies(&row) {
            if !near_zero(row.constant) {
                return Err(AddConstraintError::UnsatisfiableConstraint);
            } else {
                subject = tag.marker;
            }
        }

        // If an entering symbol still isn't found, then the row must
        // be added using an artificial variable. If that fails, then
        // the row represents an unsatisfiable constraint.
        if subject.type_() == SymbolType::Invalid {
            if !try!(self.add_with_artificial_variable(&row)
                     .map_err(|e| AddConstraintError::InternalSolverError(e.0))) {
                return Err(AddConstraintError::UnsatisfiableConstraint);
            }
        } else {
            row.solve_for_symbol(subject);
            self.substitute(subject, &row);
            if subject.type_() == SymbolType::External && row.constant != 0.0 {
                let v = self.var_for_symbol[&subject];
                self.var_changed(v);
            }
            self.rows.insert(subject, row);
        }

        self.cns.insert(constraint, tag);

        // Optimizing after each constraint is added performs less
        // aggregate work due to a smaller average system size. It
        // also ensures the solver remains in a consistent state.
        let objective = self.objective.clone();
        try!(self.optimise(&objective).map_err(|e| AddConstraintError::InternalSolverError(e.0)));
        Ok(())
    }

    /// Remove a constraint from the solver.
    pub fn remove_constraint(&mut self, constraint: &Constraint) -> Result<(), RemoveConstraintError> {
        let tag = try!(self.cns.remove(constraint).ok_or(RemoveConstraintError::UnknownConstraint));

        // Remove the error effects from the objective function
        // *before* pivoting, or substitutions into the objective
        // will lead to incorrect solver results.
        self.remove_constraint_effects(constraint, &tag);

        // If the marker is basic, simply drop the row. Otherwise,
        // pivot the marker into the basis and then drop the row.
        if let None = self.rows.remove(&tag.marker) {
            let (leaving, mut row) = try!(self.get_marker_leaving_row(tag.marker)
                                     .ok_or(
                                         RemoveConstraintError::InternalSolverError(
                                             "Failed to find leaving row.")));
            row.solve_for_symbols(leaving, tag.marker);
            self.substitute(tag.marker, &row);
        }

        // Optimizing after each constraint is removed ensures that the
        // solver remains consistent. It makes the solver api easier to
        // use at a small tradeoff for speed.
        let objective = self.objective.clone();
        try!(self.optimise(&objective).map_err(|e| RemoveConstraintError::InternalSolverError(e.0)));

        // Check for and decrease the reference count for variables referenced by the constraint
        // If the reference count is zero remove the variable from the variable map
        for term in &constraint.expr().terms {
            if !near_zero(term.coefficient) {
                let mut should_remove = false;
                if let Some(&mut (_, _, ref mut count)) = self.var_data.get_mut(&term.variable) {
                    *count -= 1;
                    should_remove = *count == 0;
                }
                if should_remove {
                    self.var_for_symbol.remove(&self.var_data[&term.variable].1);
                    self.var_data.remove(&term.variable);
                }
            }
        }
        Ok(())
    }

    /// Test whether a constraint has been added to the solver.
    pub fn has_constraint(&self, constraint: &Constraint) -> bool {
        self.cns.contains_key(constraint)
    }

    /// Add an edit variable to the solver.
    ///
    /// This method should be called before the `suggest_value` method is
    /// used to supply a suggested value for the given edit variable.
    pub fn add_edit_variable(&mut self, v: Variable, strength: f64) -> Result<(), AddEditVariableError> {
        if self.edits.contains_key(&v) {
            return Err(AddEditVariableError::DuplicateEditVariable);
        }
        let strength = ::strength::clip(strength);
        if strength == ::strength::REQUIRED {
            return Err(AddEditVariableError::BadRequiredStrength);
        }
        let cn = Constraint::new(Expression::from_term(Term::new(v.clone(), 1.0)),
                                 RelationalOperator::Equal,
                                 strength);
        self.add_constraint(cn.clone()).unwrap();
        self.edits.insert(v.clone(), EditInfo {
            tag: self.cns[&cn].clone(),
            constraint: cn,
            constant: 0.0
        });
        Ok(())
    }

    /// Remove an edit variable from the solver.
    pub fn remove_edit_variable(&mut self, v: Variable) -> Result<(), RemoveEditVariableError> {
        if let Some(constraint) = self.edits.remove(&v).map(|e| e.constraint) {
            try!(self.remove_constraint(&constraint)
                 .map_err(|e| match e {
                     RemoveConstraintError::UnknownConstraint =>
                         RemoveEditVariableError::InternalSolverError("Edit constraint not in system"),
                     RemoveConstraintError::InternalSolverError(s) =>
                         RemoveEditVariableError::InternalSolverError(s)
                 }));
            Ok(())
        } else {
            Err(RemoveEditVariableError::UnknownEditVariable)
        }
    }

    /// Test whether an edit variable has been added to the solver.
    pub fn has_edit_variable(&self, v: &Variable) -> bool {
        self.edits.contains_key(v)
    }

    /// Suggest a value for the given edit variable.
    ///
    /// This method should be used after an edit variable has been added to
    /// the solver in order to suggest the value for that variable.
    pub fn suggest_value(&mut self, variable: Variable, value: f64) -> Result<(), SuggestValueError> {
        let (info_tag_marker, info_tag_other, delta) = {
            let info = try!(self.edits.get_mut(&variable).ok_or(SuggestValueError::UnknownEditVariable));
            let delta = value - info.constant;
            info.constant = value;
            (info.tag.marker, info.tag.other, delta)
        };
        // tag.marker and tag.other are never external symbols

        // The nice version of the following code runs into non-lexical borrow issues.
        // Ideally the `if row...` code would be in the body of the if. Pretend that it is.
        {
            let infeasible_rows = &mut self.infeasible_rows;
            if self.rows.get_mut(&info_tag_marker)
                .map(|row|
                     if row.add(-delta) < 0.0 {
                         infeasible_rows.push(info_tag_marker);
                     }).is_some()
            {

            } else if self.rows.get_mut(&info_tag_other)
                .map(|row|
                     if row.add(delta) < 0.0 {
                         infeasible_rows.push(info_tag_other);
                     }).is_some()
            {

            } else {
                for (symbol, row) in &mut self.rows {
                    let coeff = row.coefficient_for(info_tag_marker);
                    let diff = delta * coeff;
                    if diff != 0.0 && symbol.type_() == SymbolType::External {
                        let v = self.var_for_symbol[symbol];
                        // inline var_changed - borrow checker workaround
                        if self.should_clear_changes {
                            self.changed.clear();
                            self.should_clear_changes = false;
                        }
                        self.changed.insert(v);
                    }
                    if coeff != 0.0 &&
                        row.add(diff) < 0.0 &&
                        symbol.type_() != SymbolType::External
                    {
                        infeasible_rows.push(*symbol);
                    }
                }
            }
        }
        try!(self.dual_optimise().map_err(|e| SuggestValueError::InternalSolverError(e.0)));
        return Ok(());
    }

    fn var_changed(&mut self, v: Variable) {
        if self.should_clear_changes {
            self.changed.clear();
            self.should_clear_changes = false;
        }
        self.changed.insert(v);
    }

    /// Fetches all changes to the values of variables since the last call to this function.
    ///
    /// The list of changes returned is not in a specific order. Each change comprises the variable changed and
    /// the new value of that variable.
    pub fn fetch_changes(&mut self) -> &[(Variable, f64)] {
        if self.should_clear_changes {
            self.changed.clear();
            self.should_clear_changes = false;
        } else {
            self.should_clear_changes = true;
        }
        self.public_changes.clear();
        for &v in &self.changed {
            if let Some(var_data) = self.var_data.get_mut(&v) {
                let new_value = self.rows.get(&var_data.1).map(|r| r.constant).unwrap_or(0.0);
                let old_value = var_data.0;
                if old_value != new_value {
                    self.public_changes.push((v, new_value));
                    var_data.0 = new_value;
                }
            }
        }
        &self.public_changes
    }

    /// Reset the solver to the empty starting condition.
    ///
    /// This method resets the internal solver state to the empty starting
    /// condition, as if no constraints or edit variables have been added.
    /// This can be faster than deleting the solver and creating a new one
    /// when the entire system must change, since it can avoid unnecessary
    /// heap (de)allocations.
    pub fn reset(&mut self) {
        self.rows.clear();
        self.cns.clear();
        self.var_data.clear();
        self.var_for_symbol.clear();
        self.changed.clear();
        self.should_clear_changes = false;
        self.edits.clear();
        self.infeasible_rows.clear();
        *self.objective.borrow_mut() = Row::new(0.0);
        self.artificial = None;
        self.id_tick = 1;
    }

    /// Get the symbol for the given variable.
    ///
    /// If a symbol does not exist for the variable, one will be created.
    fn get_var_symbol(&mut self, v: Variable) -> Symbol {
        let id_tick = &mut self.id_tick;
        let var_for_symbol = &mut self.var_for_symbol;
        let value = self.var_data.entry(v).or_insert_with(|| {
            let s = Symbol(*id_tick, SymbolType::External);
            var_for_symbol.insert(s, v);
            *id_tick += 1;
            (::std::f64::NAN, s, 0)
        });
        value.2 += 1;
        value.1
    }

    /// Create a new Row object for the given constraint.
    ///
    /// The terms in the constraint will be converted to cells in the row.
    /// Any term in the constraint with a coefficient of zero is ignored.
    /// This method uses the `getVarSymbol` method to get the symbol for
    /// the variables added to the row. If the symbol for a given cell
    /// variable is basic, the cell variable will be substituted with the
    /// basic row.
    ///
    /// The necessary slack and error variables will be added to the row.
    /// If the constant for the row is negative, the sign for the row
    /// will be inverted so the constant becomes positive.
    ///
    /// The tag will be updated with the marker and error symbols to use
    /// for tracking the movement of the constraint in the tableau.
    fn create_row(&mut self, constraint: &Constraint) -> (Box<Row>, Tag) {
        let expr = constraint.expr();
        let mut row = Row::new(expr.constant);
        // Substitute the current basic variables into the row.
        for term in &expr.terms {
            if !near_zero(term.coefficient) {
                let symbol = self.get_var_symbol(term.variable);
                if let Some(other_row) = self.rows.get(&symbol) {
                    row.insert_row(other_row, term.coefficient);
                } else {
                    row.insert_symbol(symbol, term.coefficient);
                }
            }
        }

        let mut objective = self.objective.borrow_mut();

        // Add the necessary slack, error, and dummy variables.
        let tag = match constraint.op() {
            RelationalOperator::GreaterOrEqual |
            RelationalOperator::LessOrEqual => {
                let coeff = if constraint.op() == RelationalOperator::LessOrEqual {
                    1.0
                } else {
                    -1.0
                };
                let slack = Symbol(self.id_tick, SymbolType::Slack);
                self.id_tick += 1;
                row.insert_symbol(slack, coeff);
                if constraint.strength() < ::strength::REQUIRED {
                    let error = Symbol(self.id_tick, SymbolType::Error);
                    self.id_tick += 1;
                    row.insert_symbol(error, -coeff);
                    objective.insert_symbol(error, constraint.strength());
                    Tag {
                        marker: slack,
                        other: error
                    }
                } else {
                    Tag {
                        marker: slack,
                        other: Symbol::invalid()
                    }
                }
            }
            RelationalOperator::Equal => {
                if constraint.strength() < ::strength::REQUIRED {
                    let errplus = Symbol(self.id_tick, SymbolType::Error);
                    self.id_tick += 1;
                    let errminus = Symbol(self.id_tick, SymbolType::Error);
                    self.id_tick += 1;
                    row.insert_symbol(errplus, -1.0); // v = eplus - eminus
                    row.insert_symbol(errminus, 1.0); // v - eplus + eminus = 0
                    objective.insert_symbol(errplus, constraint.strength());
                    objective.insert_symbol(errminus, constraint.strength());
                    Tag {
                        marker: errplus,
                        other: errminus
                    }
                } else {
                    let dummy = Symbol(self.id_tick, SymbolType::Dummy);
                    self.id_tick += 1;
                    row.insert_symbol(dummy, 1.0);
                    Tag {
                        marker: dummy,
                        other: Symbol::invalid()
                    }
                }
            }
        };

        // Ensure the row has a positive constant.
        if row.constant < 0.0 {
            row.reverse_sign();
        }
        (Box::new(row), tag)
    }

    /// Choose the subject for solving for the row.
    ///
    /// This method will choose the best subject for using as the solve
    /// target for the row. An invalid symbol will be returned if there
    /// is no valid target.
    ///
    /// The symbols are chosen according to the following precedence:
    ///
    /// 1) The first symbol representing an external variable.
    /// 2) A negative slack or error tag variable.
    ///
    /// If a subject cannot be found, an invalid symbol will be returned.
    fn choose_subject(row: &Row, tag: &Tag) -> Symbol {
        for s in row.cells.keys() {
            if s.type_() == SymbolType::External {
                return *s
            }
        }
        if tag.marker.type_() == SymbolType::Slack || tag.marker.type_() == SymbolType::Error {
            if row.coefficient_for(tag.marker) < 0.0 {
                return tag.marker;
            }
        }
        if tag.other.type_() == SymbolType::Slack || tag.other.type_() == SymbolType::Error {
            if row.coefficient_for(tag.other) < 0.0 {
                return tag.other;
            }
        }
        Symbol::invalid()
    }

    /// Add the row to the tableau using an artificial variable.
    ///
    /// This will return false if the constraint cannot be satisfied.
    fn add_with_artificial_variable(&mut self, row: &Row) -> Result<bool, InternalSolverError> {
        // Create and add the artificial variable to the tableau
        let art = Symbol(self.id_tick, SymbolType::Slack);
        self.id_tick += 1;
        self.rows.insert(art, Box::new(row.clone()));
        self.artificial = Some(Rc::new(RefCell::new(row.clone())));

        // Optimize the artificial objective. This is successful
        // only if the artificial objective is optimized to zero.
        let artificial = self.artificial.as_ref().unwrap().clone();
        try!(self.optimise(&artificial));
        let success = near_zero(artificial.borrow().constant);
        self.artificial = None;

        // If the artificial variable is basic, pivot the row so that
        // it becomes basic. If the row is constant, exit early.
        if let Some(mut row) = self.rows.remove(&art) {
            if row.cells.is_empty() {
                return Ok(success);
            }
            let entering = Solver::any_pivotable_symbol(&row); // never External
            if entering.type_() == SymbolType::Invalid {
                return Ok(false); // unsatisfiable (will this ever happen?)
            }
            row.solve_for_symbols(art, entering);
            self.substitute(entering, &row);
            self.rows.insert(entering, row);
        }

        // Remove the artificial row from the tableau
        for (_, row) in &mut self.rows {
            row.remove(art);
        }
        self.objective.borrow_mut().remove(art);
        Ok(success)
    }

    /// Substitute the parametric symbol with the given row.
    ///
    /// This method will substitute all instances of the parametric symbol
    /// in the tableau and the objective function with the given row.
    fn substitute(&mut self, symbol: Symbol, row: &Row) {
        for (&other_symbol, other_row) in &mut self.rows {
            let constant_changed = other_row.substitute(symbol, row);
            if other_symbol.type_() == SymbolType::External && constant_changed {
                let v = self.var_for_symbol[&other_symbol];
                // inline var_changed
                if self.should_clear_changes {
                    self.changed.clear();
                    self.should_clear_changes = false;
                }
                self.changed.insert(v);
            }
            if other_symbol.type_() != SymbolType::External && other_row.constant < 0.0 {
                self.infeasible_rows.push(other_symbol);
            }
        }
        self.objective.borrow_mut().substitute(symbol, row);
        if let Some(artificial) = self.artificial.as_ref() {
            artificial.borrow_mut().substitute(symbol, row);
        }
    }

    /// Optimize the system for the given objective function.
    ///
    /// This method performs iterations of Phase 2 of the simplex method
    /// until the objective function reaches a minimum.
    fn optimise(&mut self, objective: &RefCell<Row>) -> Result<(), InternalSolverError> {
        loop {
            let entering = Solver::get_entering_symbol(&objective.borrow());
            if entering.type_() == SymbolType::Invalid {
                return Ok(());
            }
            let (leaving, mut row) = try!(self.get_leaving_row(entering)
                             .ok_or(InternalSolverError("The objective is unbounded")));
            // pivot the entering symbol into the basis
            row.solve_for_symbols(leaving, entering);
            self.substitute(entering, &row);
            if entering.type_() == SymbolType::External && row.constant != 0.0 {
                let v = self.var_for_symbol[&entering];
                self.var_changed(v);
            }
            self.rows.insert(entering, row);
        }
    }

    /// Optimize the system using the dual of the simplex method.
    ///
    /// The current state of the system should be such that the objective
    /// function is optimal, but not feasible. This method will perform
    /// an iteration of the dual simplex method to make the solution both
    /// optimal and feasible.
    fn dual_optimise(&mut self) -> Result<(), InternalSolverError> {
        while !self.infeasible_rows.is_empty() {
            let leaving = self.infeasible_rows.pop().unwrap();

            let row = if let Entry::Occupied(entry) = self.rows.entry(leaving) {
                if entry.get().constant < 0.0 {
                    Some(entry.remove())
                } else {
                    None
                }
            } else {
                None
            };
            if let Some(mut row) = row {
                let entering = self.get_dual_entering_symbol(&row);
                if entering.type_() == SymbolType::Invalid {
                    return Err(InternalSolverError("Dual optimise failed."));
                }
                // pivot the entering symbol into the basis
                row.solve_for_symbols(leaving, entering);
                self.substitute(entering, &row);
                if entering.type_() == SymbolType::External && row.constant != 0.0 {
                    let v = self.var_for_symbol[&entering];
                    self.var_changed(v);
                }
                self.rows.insert(entering, row);
            }
        }
        Ok(())
    }

    /// Compute the entering variable for a pivot operation.
    ///
    /// This method will return first symbol in the objective function which
    /// is non-dummy and has a coefficient less than zero. If no symbol meets
    /// the criteria, it means the objective function is at a minimum, and an
    /// invalid symbol is returned.
    /// Could return an External symbol
    fn get_entering_symbol(objective: &Row) -> Symbol {
        for (symbol, value) in &objective.cells {
            if symbol.type_() != SymbolType::Dummy && *value < 0.0 {
                return *symbol;
            }
        }
        Symbol::invalid()
    }

    /// Compute the entering symbol for the dual optimize operation.
    ///
    /// This method will return the symbol in the row which has a positive
    /// coefficient and yields the minimum ratio for its respective symbol
    /// in the objective function. The provided row *must* be infeasible.
    /// If no symbol is found which meats the criteria, an invalid symbol
    /// is returned.
    /// Could return an External symbol
    fn get_dual_entering_symbol(&self, row: &Row) -> Symbol {
        let mut entering = Symbol::invalid();
        let mut ratio = ::std::f64::INFINITY;
        let objective = self.objective.borrow();
        for (symbol, value) in &row.cells {
            if *value > 0.0 && symbol.type_() != SymbolType::Dummy {
                let coeff = objective.coefficient_for(*symbol);
                let r = coeff / *value;
                if r < ratio {
                    ratio = r;
                    entering = *symbol;
                }
            }
        }
        entering
    }

    /// Get the first Slack or Error symbol in the row.
    ///
    /// If no such symbol is present, and Invalid symbol will be returned.
    /// Never returns an External symbol
    fn any_pivotable_symbol(row: &Row) -> Symbol {
        for symbol in row.cells.keys() {
            if symbol.type_() == SymbolType::Slack || symbol.type_() == SymbolType::Error {
                return *symbol;
            }
        }
        Symbol::invalid()
    }

    /// Compute the row which holds the exit symbol for a pivot.
    ///
    /// This method will return an iterator to the row in the row map
    /// which holds the exit symbol. If no appropriate exit symbol is
    /// found, the end() iterator will be returned. This indicates that
    /// the objective function is unbounded.
    /// Never returns a row for an External symbol
    fn get_leaving_row(&mut self, entering: Symbol) -> Option<(Symbol, Box<Row>)> {
        let mut ratio = ::std::f64::INFINITY;
        let mut found = None;
        for (symbol, row) in &self.rows {
            if symbol.type_() != SymbolType::External {
                let temp = row.coefficient_for(entering);
                if temp < 0.0 {
                    let temp_ratio = -row.constant / temp;
                    if temp_ratio < ratio {
                        ratio = temp_ratio;
                        found = Some(*symbol);
                    }
                }
            }
        }
        found.map(|s| (s, self.rows.remove(&s).unwrap()))
    }

    /// Compute the leaving row for a marker variable.
    ///
    /// This method will return an iterator to the row in the row map
    /// which holds the given marker variable. The row will be chosen
    /// according to the following precedence:
    ///
    /// 1) The row with a restricted basic varible and a negative coefficient
    ///    for the marker with the smallest ratio of -constant / coefficient.
    ///
    /// 2) The row with a restricted basic variable and the smallest ratio
    ///    of constant / coefficient.
    ///
    /// 3) The last unrestricted row which contains the marker.
    ///
    /// If the marker does not exist in any row, the row map end() iterator
    /// will be returned. This indicates an internal solver error since
    /// the marker *should* exist somewhere in the tableau.
    fn get_marker_leaving_row(&mut self, marker: Symbol) -> Option<(Symbol, Box<Row>)> {
        let mut r1 = ::std::f64::INFINITY;
        let mut r2 = r1;
        let mut first = None;
        let mut second = None;
        let mut third = None;
        for (symbol, row) in &self.rows {
            let c = row.coefficient_for(marker);
            if c == 0.0 {
                continue;
            }
            if symbol.type_() == SymbolType::External {
                third = Some(*symbol);
            } else if c < 0.0 {
                let r = -row.constant / c;
                if r < r1 {
                    r1 = r;
                    first = Some(*symbol);
                }
            } else {
                let r = row.constant / c;
                if r < r2 {
                    r2 = r;
                    second = Some(*symbol);
                }
            }
        }
        first
            .or(second)
            .or(third)
            .and_then(|s| {
                if s.type_() == SymbolType::External && self.rows[&s].constant != 0.0 {
                    let v = self.var_for_symbol[&s];
                    self.var_changed(v);
                }
                self.rows
                    .remove(&s)
                    .map(|r| (s, r))
            })
    }

    /// Remove the effects of a constraint on the objective function.
    fn remove_constraint_effects(&mut self, cn: &Constraint, tag: &Tag) {
        if tag.marker.type_() == SymbolType::Error {
            self.remove_marker_effects(tag.marker, cn.strength());
        } else if tag.other.type_() == SymbolType::Error {
            self.remove_marker_effects(tag.other, cn.strength());
        }
    }

    /// Remove the effects of an error marker on the objective function.
    fn remove_marker_effects(&mut self, marker: Symbol, strength: f64) {
        if let Some(row) = self.rows.get(&marker) {
            self.objective.borrow_mut().insert_row(row, -strength);
        } else {
            self.objective.borrow_mut().insert_symbol(marker, -strength);
        }
    }

    /// Test whether a row is composed of all dummy variables.
    fn all_dummies(row: &Row) -> bool {
        for symbol in row.cells.keys() {
            if symbol.type_() != SymbolType::Dummy {
                return false;
            }
        }
        true
    }

    /// Get the stored value for a variable.
    ///
    /// Normally values should be retrieved and updated using `fetch_changes`, but
    /// this method can be used for debugging or testing.
    pub fn get_value(&self, v: Variable) -> f64 {
        self.var_data.get(&v).and_then(|s| {
            self.rows.get(&s.1).map(|r| r.constant)
        }).unwrap_or(0.0)
    }
}
