/*++
Copyright (c) 2017 Microsoft Corporation

Module Name:

    <name>

Abstract:

    <abstract>

Author:

    Lev Nachmanson (levnach)

Revision History:


--*/
#pragma once
#include "util/vector.h"
#include <string>
#include <utility>
#include "util/lp/lp_core_solver_base.h"
#include <algorithm>
#include "util/lp/indexed_vector.h"
#include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/breakpoint.h"
#include "util/lp/stacked_unordered_set.h"
#include "util/lp/lp_primal_core_solver.h"
#include "util/lp/stacked_vector.h"
#include "util/lp/lar_solution_signature.h"
#include "util/lp/iterator_on_column.h"
#include "util/lp/iterator_on_indexed_vector.h"
#include "util/lp/stacked_value.h"
namespace lp {

class lar_core_solver  {
    // m_sign_of_entering is set to 1 if the entering variable needs
    // to grow and is set to -1  otherwise
    int m_sign_of_entering_delta;
    vector<std::pair<mpq, unsigned>> m_infeasible_linear_combination;
    int m_infeasible_sum_sign; // todo: get rid of this field
    vector<numeric_pair<mpq>> m_right_sides_dummy;
    vector<mpq> m_costs_dummy;
    vector<double> m_d_right_sides_dummy;
    vector<double> m_d_costs_dummy;
public:
    stacked_value<simplex_strategy_enum> m_stacked_simplex_strategy;
    stacked_vector<column_type> m_column_types;
    // r - solver fields, for rational numbers
    vector<numeric_pair<mpq>> m_r_x; // the solution
    stacked_vector<numeric_pair<mpq>> m_r_low_bounds;
    stacked_vector<numeric_pair<mpq>> m_r_upper_bounds;
    static_matrix<mpq, numeric_pair<mpq>> m_r_A;
    stacked_vector<unsigned> m_r_pushed_basis;
    vector<unsigned> m_r_basis;
    vector<unsigned> m_r_nbasis;
    vector<int> m_r_heading;
    stacked_vector<unsigned> m_r_columns_nz;
    stacked_vector<unsigned> m_r_rows_nz;
    
    // d - solver fields, for doubles
    vector<double> m_d_x; // the solution in doubles
    vector<double> m_d_low_bounds;
    vector<double> m_d_upper_bounds;
    static_matrix<double, double> m_d_A;
    stacked_vector<unsigned> m_d_pushed_basis;
    vector<unsigned> m_d_basis;
    vector<unsigned> m_d_nbasis;
    vector<int> m_d_heading;


    lp_primal_core_solver<mpq, numeric_pair<mpq>> m_r_solver; // solver in rational numbers

    lp_primal_core_solver<double, double> m_d_solver; // solver in doubles
    
    lar_core_solver(
                    lp_settings & settings,
                    const column_namer & column_names
                    );

    lp_settings & settings() { return m_r_solver.m_settings;}
    const lp_settings & settings() const { return m_r_solver.m_settings;}
    
    int get_infeasible_sum_sign() const { return m_infeasible_sum_sign;   }

    const vector<std::pair<mpq, unsigned>> & get_infeasibility_info(int & inf_sign) const {
        inf_sign = m_infeasible_sum_sign;
        return m_infeasible_linear_combination;
    }

    void fill_not_improvable_zero_sum_from_inf_row();
    
    column_type get_column_type(unsigned j) { return m_column_types[j];}
    
    void init_costs(bool first_time);

    void init_cost_for_column(unsigned j);

    // returns m_sign_of_alpha_r
    int column_is_out_of_bounds(unsigned j);

    void calculate_pivot_row(unsigned i);

    void print_pivot_row(std::ostream & out, unsigned row_index) const  { // remove later debug !!!!
        for (unsigned j : m_r_solver.m_pivot_row.m_index) {
            if (numeric_traits<mpq>::is_pos(m_r_solver.m_pivot_row.m_data[j]))
                out << "+";
            out << m_r_solver.m_pivot_row.m_data[j] << m_r_solver.column_name(j) << " ";
        }
        
        out << " +" << m_r_solver.column_name(m_r_solver.m_basis[row_index]) << std::endl;
        
        for (unsigned j : m_r_solver.m_pivot_row.m_index) {
            m_r_solver.print_column_bound_info(j, out);
        }
        m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out);
        
    }
    

    void advance_on_sorted_breakpoints(unsigned entering);

    void change_slope_on_breakpoint(unsigned entering, breakpoint<numeric_pair<mpq>> * b, mpq & slope_at_entering);

    bool row_is_infeasible(unsigned row);

    bool row_is_evidence(unsigned row);

    bool find_evidence_row();

    void prefix_r();

    void prefix_d();

    unsigned m_m() const {
        return m_r_A.row_count();
    }

    unsigned m_n() const {
        return m_r_A.column_count();
    }
    
    bool is_tiny() const { return this->m_m() < 10 && this->m_n() < 20; }

    bool is_empty() const { return this->m_m() == 0 && this->m_n() == 0; }

    template <typename L>
    int get_sign(const L & v) {
        return v > zero_of_type<L>() ? 1 : (v < zero_of_type<L>() ? -1 : 0);
    }



    void fill_evidence(unsigned row);



    void solve();

    bool low_bounds_are_set() const { return true; }

    const indexed_vector<mpq> & get_pivot_row() const {
        return m_r_solver.m_pivot_row;
    }
    
    void fill_not_improvable_zero_sum();

    void pop_basis(unsigned k) {
        if (!settings().use_tableau()) {
            m_r_pushed_basis.pop(k);        
            m_r_basis = m_r_pushed_basis();
            m_r_solver.init_basis_heading_and_non_basic_columns_vector();
            m_d_pushed_basis.pop(k);
            m_d_basis = m_d_pushed_basis();
            m_d_solver.init_basis_heading_and_non_basic_columns_vector();
        } else {
            m_d_basis = m_r_basis;
            m_d_nbasis = m_r_nbasis;
            m_d_heading = m_r_heading;
        }
    }

    void push() {
        SASSERT(m_r_solver.basis_heading_is_correct());
        SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
        SASSERT(m_column_types.size() == m_r_A.column_count());
        m_stacked_simplex_strategy = settings().simplex_strategy();
        m_stacked_simplex_strategy.push();
        m_column_types.push();
        // rational
        if (!settings().use_tableau()) 
            m_r_A.push();
        m_r_low_bounds.push();
        m_r_upper_bounds.push();
        if (!settings().use_tableau()) {
            push_vector(m_r_pushed_basis, m_r_basis);
            push_vector(m_r_columns_nz, m_r_solver.m_columns_nz);
            push_vector(m_r_rows_nz, m_r_solver.m_rows_nz);
        }
        
        m_d_A.push();
        if (!settings().use_tableau())
            push_vector(m_d_pushed_basis, m_d_basis);
    }

    template <typename K> 
    void push_vector(stacked_vector<K> & pushed_vector, const vector<K> & vector) {
        SASSERT(pushed_vector.size() <= vector.size());
        for (unsigned i = 0; i < vector.size();i++) {
            if (i == pushed_vector.size()) {
                pushed_vector.push_back(vector[i]);
            } else {
                pushed_vector[i] = vector[i];
            }
        }
        pushed_vector.push();
    }

    void pop_markowitz_counts(unsigned k) {
        m_r_columns_nz.pop(k);
        m_r_rows_nz.pop(k);
        m_r_solver.m_columns_nz.resize(m_r_columns_nz.size());
        m_r_solver.m_rows_nz.resize(m_r_rows_nz.size());
        for (unsigned i = 0; i < m_r_columns_nz.size(); i++)
            m_r_solver.m_columns_nz[i] = m_r_columns_nz[i];
        for (unsigned i = 0; i < m_r_rows_nz.size(); i++)
            m_r_solver.m_rows_nz[i] = m_r_rows_nz[i];
    }

    
    void pop(unsigned k) {
        // rationals
        if (!settings().use_tableau()) 
            m_r_A.pop(k);
        m_r_low_bounds.pop(k);
        m_r_upper_bounds.pop(k);
        m_column_types.pop(k);
        
        if (m_r_solver.m_factorization != nullptr) {
            delete m_r_solver.m_factorization;
            m_r_solver.m_factorization = nullptr;
        }
        m_r_x.resize(m_r_A.column_count());
        m_r_solver.m_costs.resize(m_r_A.column_count());
        m_r_solver.m_d.resize(m_r_A.column_count());
        if(!settings().use_tableau())
            pop_markowitz_counts(k);
        m_d_A.pop(k);
        if (m_d_solver.m_factorization != nullptr) {
            delete m_d_solver.m_factorization;
            m_d_solver.m_factorization = nullptr;
        }
        
        m_d_x.resize(m_d_A.column_count());
        pop_basis(k);
        m_stacked_simplex_strategy.pop(k);
        settings().simplex_strategy() = m_stacked_simplex_strategy;
        SASSERT(m_r_solver.basis_heading_is_correct());
        SASSERT(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
    }

    bool need_to_presolve_with_double_solver() const {
        return settings().simplex_strategy() == simplex_strategy_enum::lu;
    }

    template <typename L>
    bool is_zero_vector(const vector<L> & b) {
        for (const L & m: b)
            if (!is_zero(m)) return false;
        return true;
    }


    bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair<mpq> & delta) {
        auto & x = m_r_x[j];
        switch (pos_type) {
        case at_low_bound:
            if (x == m_r_solver.m_low_bounds[j])
                return false;
            delta = m_r_solver.m_low_bounds[j] - x;
            m_r_solver.m_x[j] = m_r_solver.m_low_bounds[j];
            break;
        case at_fixed:
        case at_upper_bound:
            if (x == m_r_solver.m_upper_bounds[j])
                return false;
            delta = m_r_solver.m_upper_bounds[j] - x;
            x = m_r_solver.m_upper_bounds[j];
            break;
        case free_of_bounds: {
            return false;
        }
        case not_at_bound:
            switch (m_column_types[j]) {
            case column_type::free_column:
                return false;
            case column_type::upper_bound:
                delta = m_r_solver.m_upper_bounds[j] - x;
                x = m_r_solver.m_upper_bounds[j];
                break;
            case column_type::low_bound:
                delta = m_r_solver.m_low_bounds[j] - x;
                x = m_r_solver.m_low_bounds[j];
                break;
            case column_type::boxed:
                if (x > m_r_solver.m_upper_bounds[j]) {
                    delta = m_r_solver.m_upper_bounds[j] - x;
                    x += m_r_solver.m_upper_bounds[j];
                } else {
                    delta = m_r_solver.m_low_bounds[j] - x;
                    x = m_r_solver.m_low_bounds[j];
                }
                break;
            case column_type::fixed:
                delta = m_r_solver.m_low_bounds[j] - x;
                x = m_r_solver.m_low_bounds[j];
                break;

            default:
                SASSERT(false);
            }
            break;
        default:
            SASSERT(false);
        }
        m_r_solver.remove_column_from_inf_set(j);
        return true;
    }

    
    
    void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) {
        SASSERT(m_r_solver.inf_set_is_correct());
        for (auto &t : signature) {
            unsigned j = t.first;
            if (m_r_heading[j] >= 0)
                continue;
            auto pos_type = t.second;
            numeric_pair<mpq> delta;
            if (!update_xj_and_get_delta(j, pos_type, delta))
                continue;
            for (const auto & cc : m_r_solver.m_A.m_columns[j]){
                unsigned i = cc.m_i;
                unsigned jb = m_r_solver.m_basis[i];
                m_r_solver.m_x[jb] -= delta * m_r_solver.m_A.get_val(cc);
                m_r_solver.update_column_in_inf_set(jb);
            }
            SASSERT(m_r_solver.A_mult_x_is_off() == false);
        }
        SASSERT(m_r_solver.inf_set_is_correct());
    }

    
    template <typename L, typename K>
    void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver<L,K> & s) {
        for (auto &t : signature) {
            unsigned j = t.first;
            SASSERT(m_r_heading[j] < 0);
            auto pos_type = t.second;
            switch (pos_type) {
            case at_low_bound:
                s.m_x[j] = s.m_low_bounds[j];
                break;
            case at_fixed:
            case at_upper_bound:
                s.m_x[j] = s.m_upper_bounds[j];
                break;
            case free_of_bounds: {
                s.m_x[j] = zero_of_type<K>();
                continue;
            }
            case not_at_bound:
                  switch (m_column_types[j]) {
                  case column_type::free_column:
                      SASSERT(false); // unreachable
                  case column_type::upper_bound:
                      s.m_x[j] = s.m_upper_bounds[j];
                      break;
                  case column_type::low_bound:
                      s.m_x[j] = s.m_low_bounds[j];
                      break;
                  case column_type::boxed:
                      if (settings().random_next() % 2) {
                          s.m_x[j] = s.m_low_bounds[j];
                      } else {
                          s.m_x[j] = s.m_upper_bounds[j];
                      }
                      break;
                  case column_type::fixed:
                      s.m_x[j] = s.m_low_bounds[j];
                      break;
                  default:
                      SASSERT(false);
                  }
                  break;
            default:
                SASSERT(false);
            }
        }

        SASSERT(is_zero_vector(s.m_b));
        s.solve_Ax_eq_b();
    }

    template <typename L, typename K> 
    void catch_up_in_lu_in_reverse(const vector<unsigned> & trace_of_basis_change, lp_primal_core_solver<L,K> & cs) {
        // recover the previous working basis
        for (unsigned i = trace_of_basis_change.size(); i > 0;  i-= 2) {
            unsigned entering = trace_of_basis_change[i-1];
            unsigned leaving = trace_of_basis_change[i-2];
            cs.change_basis_unconditionally(entering, leaving);
        }
        cs.init_lu();
    }

    //basis_heading is the basis heading of the solver owning trace_of_basis_change
    // here we compact the trace as we go to avoid unnecessary column changes
    template <typename L, typename K> 
    void catch_up_in_lu(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading, lp_primal_core_solver<L,K> & cs) {
        if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) {
            for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
                unsigned entering = trace_of_basis_change[i];
                unsigned leaving = trace_of_basis_change[i+1];
                cs.change_basis_unconditionally(entering, leaving);
            }
            if (cs.m_factorization != nullptr)
                delete cs.m_factorization;
            cs.m_factorization = nullptr;
        } else {
            indexed_vector<L> w(cs.m_A.row_count());
            // the queues of delayed indices
            std::queue<unsigned> entr_q, leav_q;
            auto * l = cs.m_factorization;
            SASSERT(l->get_status() == LU_status::OK);
            for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
                unsigned entering = trace_of_basis_change[i];
                unsigned leaving = trace_of_basis_change[i+1];
                bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0;
                bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0;
                if (!good_e && !good_l) continue;
                if (good_e && !good_l) {
                    while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0)
                        leav_q.pop();
                    if (!leav_q.empty()) {
                        leaving = leav_q.front();
                        leav_q.pop();
                    } else {
                        entr_q.push(entering);
                        continue;
                    }
                } else if (!good_e && good_l) {
                    while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0)
                        entr_q.pop();
                    if (!entr_q.empty()) {
                        entering = entr_q.front();
                        entr_q.pop();
                    } else {
                        leav_q.push(leaving);
                        continue;
                    }
                }
                SASSERT(cs.m_basis_heading[entering] < 0);
                SASSERT(cs.m_basis_heading[leaving] >= 0);
                if (l->get_status() == LU_status::OK) {
                    l->prepare_entering(entering, w); // to init vector w
                    l->replace_column(zero_of_type<L>(), w, cs.m_basis_heading[leaving]);
                }
                cs.change_basis_unconditionally(entering, leaving);
            }
            if (l->get_status() != LU_status::OK) {
                delete l;
                cs.m_factorization = nullptr;
            }
        }
        if (cs.m_factorization == nullptr) {
            if (numeric_traits<L>::precise())
                init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings());
        }
    }

    bool no_r_lu() const {
        return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated;
    }

    void solve_on_signature_tableau(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) {
        r_basis_is_OK();
        SASSERT(settings().use_tableau());
        bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading);

        if (!r) { // it is the case where m_d_solver gives a degenerated basis
            prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially
            m_r_solver.find_feasible_solution();
            m_d_basis = m_r_basis;
            m_d_heading = m_r_heading;
            m_d_nbasis = m_r_nbasis;
            delete m_d_solver.m_factorization;
            m_d_solver.m_factorization = nullptr;
        } else {
            prepare_solver_x_with_signature_tableau(signature);
            m_r_solver.start_tracing_basis_changes();
            m_r_solver.find_feasible_solution();
            if (settings().get_cancel_flag())
                return;
            m_r_solver.stop_tracing_basis_changes();
            // and now catch up in the double solver
            SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
            catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver);
        }
        SASSERT(r_basis_is_OK());
    }

    bool adjust_x_of_column(unsigned j) {
        /*
        if (m_r_solver.m_basis_heading[j] >= 0) {
            return false;
        }

        if (m_r_solver.column_is_feasible(j)) {
            return false;
        }

        m_r_solver.snap_column_to_bound_tableau(j);
        SASSERT(m_r_solver.column_is_feasible(j));
        m_r_solver.m_inf_set.erase(j);
        */
        SASSERT(false);
        return true;
    }

    
    bool catch_up_in_lu_tableau(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading) {
        SASSERT(r_basis_is_OK());
        // the queues of delayed indices
        std::queue<unsigned> entr_q, leav_q;
        for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
            unsigned entering = trace_of_basis_change[i];
            unsigned leaving = trace_of_basis_change[i+1];
            bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0;
            bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0;
            if (!good_e && !good_l) continue;
            if (good_e && !good_l) {
                while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0)
                    leav_q.pop();
                if (!leav_q.empty()) {
                    leaving = leav_q.front();
                    leav_q.pop();
                } else {
                    entr_q.push(entering);
                    continue;
                }
            } else if (!good_e && good_l) {
                while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0)
                    entr_q.pop();
                if (!entr_q.empty()) {
                    entering = entr_q.front();
                    entr_q.pop();
                } else {
                    leav_q.push(leaving);
                    continue;
                }
            }
            SASSERT(m_r_solver.m_basis_heading[entering] < 0);
            SASSERT(m_r_solver.m_basis_heading[leaving] >= 0);
            m_r_solver.change_basis_unconditionally(entering, leaving);
            if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) {
                // unroll the last step
                m_r_solver.change_basis_unconditionally(leaving, entering);
#ifdef Z3DEBUG
                bool t =
#endif
                    m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]);
#ifdef Z3DEBUG
                SASSERT(t);
#endif 
                return false;
            }
        }
        SASSERT(r_basis_is_OK());
        return true;
    }


    bool r_basis_is_OK() const {
#ifdef Z3DEBUG
        if (!m_r_solver.m_settings.use_tableau())
            return true;
        for (unsigned j : m_r_solver.m_basis) {
            SASSERT(m_r_solver.m_A.m_columns[j].size() == 1);
            SASSERT(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type<mpq>());
        }
        for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) {
            if (m_r_solver.m_basis_heading[j] >= 0) continue;
            if (m_r_solver.m_column_types[j] == column_type::fixed) continue;
            SASSERT(static_cast<unsigned>(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size());
            SASSERT( m_r_solver.m_basis_heading[j] <= -1);
        }
#endif
        return true;
    }
    
    void solve_on_signature(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) {
        SASSERT(!settings().use_tableau());
        if (m_r_solver.m_factorization == nullptr) {
            for (unsigned j = 0; j < changes_of_basis.size(); j+=2) {
                unsigned entering = changes_of_basis[j];
                unsigned leaving = changes_of_basis[j + 1];
                m_r_solver.change_basis_unconditionally(entering, leaving);
            }
            init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings());
        } else {
            catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver);
        }

        if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back
            //            std::cout << "no_r_lu" << std::endl;
            catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver);
            m_r_solver.find_feasible_solution();
            m_d_basis = m_r_basis;
            m_d_heading = m_r_heading;
            m_d_nbasis = m_r_nbasis;
            delete m_d_solver.m_factorization;
            m_d_solver.m_factorization = nullptr;
        } else {
            prepare_solver_x_with_signature(signature, m_r_solver);
            m_r_solver.start_tracing_basis_changes();
            m_r_solver.find_feasible_solution();
            if (settings().get_cancel_flag())
                return;
            m_r_solver.stop_tracing_basis_changes();
            // and now catch up in the double solver
            SASSERT(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
            catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver);
        }
    }

    void create_double_matrix(static_matrix<double, double> & A) {
        for (unsigned i = 0; i < m_r_A.row_count(); i++) {
            auto & row = m_r_A.m_rows[i];
            for (row_cell<mpq> & c : row) {
                A.set(i, c.m_j, c.get_val().get_double());
            }
        }
    }

    void fill_basis_d(
                      vector<unsigned>& basis_d,
                      vector<int>& heading_d,
                      vector<unsigned>& nbasis_d){
        basis_d = m_r_basis;
        heading_d = m_r_heading;
        nbasis_d = m_r_nbasis;
    }

    template <typename L, typename K>
    void extract_signature_from_lp_core_solver(const lp_primal_core_solver<L, K> & solver, lar_solution_signature & signature) {
        signature.clear();
        SASSERT(signature.size() == 0);
        for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) {
            if (solver.m_basis_heading[j] < 0) {
                signature[j] = solver.get_non_basic_column_value_position(j);
            }
        }
    }

    void get_bounds_for_double_solver() {
        unsigned n = m_n();
        m_d_low_bounds.resize(n);
        m_d_upper_bounds.resize(n);
        double delta = find_delta_for_strict_boxed_bounds().get_double();
        if (delta > 0.000001)
            delta = 0.000001;
        for (unsigned j = 0; j < n; j++) {
            if (low_bound_is_set(j)) {
                const auto & lb = m_r_solver.m_low_bounds[j];
                m_d_low_bounds[j] = lb.x.get_double() + delta * lb.y.get_double();
            }
            if (upper_bound_is_set(j)) {
                const auto & ub = m_r_solver.m_upper_bounds[j];
                m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double();
                SASSERT(!low_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_low_bounds[j]));
            }
        }
    }

    void scale_problem_for_doubles(
                        static_matrix<double, double>& A,        
                        vector<double> & low_bounds,
                        vector<double> & upper_bounds) {
        vector<double> column_scale_vector;
        vector<double> right_side_vector(A.column_count());
        settings().reps_in_scaler = 5;
        scaler<double, double > scaler(right_side_vector,
                                       A,
                                       settings().scaling_minimum,
                                       settings().scaling_maximum,
                                       column_scale_vector,
                                       settings());
        if (! scaler.scale()) {
            // the scale did not succeed, unscaling
            A.clear();
            create_double_matrix(A);
        } else {
            for (unsigned j = 0; j < A.column_count(); j++) {
                if (m_r_solver.column_has_upper_bound(j)) {
                    upper_bounds[j] /= column_scale_vector[j];
                }
                if (m_r_solver.column_has_low_bound(j)) {
                    low_bounds[j] /= column_scale_vector[j];
                }
            }
        }
        
    }
    // returns the trace of basis changes
    vector<unsigned> find_solution_signature_with_doubles(lar_solution_signature & signature) {
        if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) {
            vector<unsigned> ret;
            return ret;
        }
        get_bounds_for_double_solver();

        extract_signature_from_lp_core_solver(m_r_solver, signature);
        prepare_solver_x_with_signature(signature, m_d_solver);
        m_d_solver.start_tracing_basis_changes();
        m_d_solver.find_feasible_solution();
        if (settings().get_cancel_flag())
            return vector<unsigned>();
            
        m_d_solver.stop_tracing_basis_changes();
        extract_signature_from_lp_core_solver(m_d_solver, signature);
        return m_d_solver.m_trace_of_basis_change_vector;
    }


    bool low_bound_is_set(unsigned j) const {
        switch (m_column_types[j]) {
        case column_type::free_column:
        case column_type::upper_bound:
            return false;
        case column_type::low_bound:
        case column_type::boxed:
        case column_type::fixed:
            return true;
        default:
            SASSERT(false);
        }
        return false;
    }
    
    bool upper_bound_is_set(unsigned j) const {
        switch (m_column_types[j]) {
        case column_type::free_column:
        case column_type::low_bound:
            return false;
        case column_type::upper_bound:
        case column_type::boxed:
        case column_type::fixed:
            return true;
        default:
            SASSERT(false);
        }
        return false;
    }

    void update_delta(mpq& delta, numeric_pair<mpq> const& l, numeric_pair<mpq> const& u) const {
        SASSERT(l <= u);
        if (l.x < u.x && l.y > u.y) {
            mpq delta1 = (u.x - l.x) / (l.y - u.y);
            if (delta1 < delta) {
                delta = delta1;
            }
        }
        SASSERT(l.x + delta * l.y <= u.x + delta * u.y);
    }


    mpq find_delta_for_strict_boxed_bounds() const{
        mpq delta = numeric_traits<mpq>::one();
        for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
            if (m_column_types()[j] != column_type::boxed)
                continue;
            update_delta(delta, m_r_low_bounds[j], m_r_upper_bounds[j]);

        }
        return delta;
    }

    
    mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{
        mpq delta = initial_delta;
        for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
            if (low_bound_is_set(j))
                update_delta(delta, m_r_low_bounds[j], m_r_x[j]);
            if (upper_bound_is_set(j))
                update_delta(delta, m_r_x[j], m_r_upper_bounds[j]);
        }
        return delta;
    }

    void init_column_row_nz_for_r_solver() {
        m_r_solver.init_column_row_non_zeroes();
    }

    linear_combination_iterator<mpq> * get_column_iterator(unsigned j) {
        if (settings().use_tableau()) {
            return new iterator_on_column<mpq, numeric_pair<mpq>>(m_r_solver.m_A.m_columns[j], m_r_solver.m_A);
        } else {
            m_r_solver.solve_Bd(j);
            return new iterator_on_indexed_vector<mpq>(m_r_solver.m_ed);
        }
    }
    
};
}
