z3-src 503.3.0

Source distribution of the Z3 SMT solver, for use as a build dependency
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
/*++
  Copyright (c) 2017 Microsoft Corporation

  Module Name:

  <name>

  Abstract:

  <abstract>

  Author:
  Nikolaj Bjorner (nbjorner)
  Lev Nachmanson (levnach)

  Revision History:


  --*/
#pragma once
#include <algorithm>
#include <functional>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>

#include "math/lp/bound_analyzer_on_row.h"
#include "math/lp/implied_bound.h"
#include "math/lp/int_solver.h"
#include "math/lp/lar_constraints.h"
#include "math/lp/lar_core_solver.h"
#include "math/lp/lp_bound_propagator.h"
#include "math/lp/lp_types.h"
#include "math/lp/nra_solver.h"
#include "math/lp/numeric_pair.h"
#include "math/lp/random_updater.h"
#include "math/lp/stacked_vector.h"
#include "util/buffer.h"
#include "util/debug.h"
#include "util/stacked_value.h"
#include "util/vector.h"
#include "util/trail.h"

namespace lp {

class int_branch;
class int_solver;
    
class lar_solver : public column_namer {
    struct imp;
    imp* m_imp;

    ////////////////// methods ////////////////////////////////

    bool row_has_a_big_num(unsigned i) const;
    // init region
    void register_new_external_var(unsigned ext_v, bool is_int);
    bool term_is_int(const lar_term* t) const;
    bool term_is_int(const vector<std::pair<mpq, unsigned int>>& coeffs) const;
    void add_non_basic_var_to_core_fields(unsigned ext_j, bool is_int);
    void add_new_var_to_core_fields_for_mpq(bool register_in_basis);
    mpq adjust_bound_for_int(lpvar j, lconstraint_kind&, const mpq&);

    // terms
    bool all_vars_are_registered(const vector<std::pair<mpq, lpvar>>& coeffs);
    bool term_coeffs_are_ok(const vector<std::pair<mpq, lpvar>>& coeffs);
    void add_row_from_term_no_constraint(lar_term* term, unsigned term_ext_index);
    void add_basic_var_to_core_fields();
    bool compare_values(impq const& lhs, lconstraint_kind k, const mpq& rhs);

    void clear_columns_with_changed_bounds();

 public:
    const indexed_uint_set& columns_with_changed_bounds() const;
    void insert_to_columns_with_changed_bounds(unsigned j);
    const u_dependency* crossed_bounds_deps() const;
    u_dependency*& crossed_bounds_deps();

    lpvar crossed_bounds_column() const;
    lpvar& crossed_bounds_column();
        

 private:
    bool validate_bound(lpvar j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);   
    void add_dep_constraints_to_solver(lar_solver& ls, u_dependency* dep);
    void add_bound_negation_to_solver(lar_solver& ls, lpvar j, lconstraint_kind kind, const mpq& right_side);
    void add_constraint_to_validate(lar_solver& ls, constraint_index ci);
    bool m_validate_blocker = false;
    void update_column_type_and_bound_check_on_equal(unsigned j, const impq& right_side, constraint_index ci, unsigned&);
    void update_column_type_and_bound(unsigned j, const impq& right_side, constraint_index ci);
 public:   
    bool validate_blocker() const { return m_validate_blocker; }
    bool & validate_blocker() { return m_validate_blocker; }   
    void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const impq& right_side, u_dependency* dep);
    void update_column_type_and_bound(unsigned j, lconstraint_kind kind, const mpq& right_side, u_dependency* dep);
 private:
    void update_column_type_and_bound_with_ub(lpvar j, lconstraint_kind kind, const impq& right_side, u_dependency* dep);
    void update_column_type_and_bound_with_no_ub(lpvar j, lconstraint_kind kind, const impq& right_side, u_dependency* dep);
    void update_bound_with_ub_lb(lpvar j, lconstraint_kind kind, const impq& right_side, u_dependency* dep);
    void update_bound_with_no_ub_lb(lpvar j, lconstraint_kind kind, const impq& right_side, u_dependency* dep);
    void update_bound_with_ub_no_lb(lpvar j, lconstraint_kind kind, const impq& right_side, u_dependency* dep);
    void update_bound_with_no_ub_no_lb(lpvar j, lconstraint_kind kind, const impq& right_side, u_dependency* dep);
    void remove_non_fixed_from_fixed_var_table();
    constraint_index add_var_bound_on_constraint_for_term(lpvar j, lconstraint_kind kind, const mpq& right_side);
    void set_crossed_bounds_column_and_deps(unsigned j, bool lower_bound, u_dependency* dep);
    unsigned row_of_basic_column(unsigned) const;
    bool sizes_are_correct() const;
    bool implied_bound_is_correctly_explained(implied_bound const& be, const vector<std::pair<mpq, unsigned>>& explanation) const;

    template <typename T>
    unsigned calculate_implied_bounds_for_row(unsigned row_index, lp_bound_propagator<T>& bp) {
        if (A_r().m_rows[row_index].size() > settings().max_row_length_for_bound_propagation)
            return 0;
        ++stats().m_bp_rows_analyzed;
        return bound_analyzer_on_row<row_strip<mpq>, lp_bound_propagator<T>>::analyze_row(
            A_r().m_rows[row_index],
            m_zero_rs,
            bp);
    }

    static void clean_popped_elements_for_heap(unsigned n, lpvar_heap& set);
    static void clean_popped_elements(unsigned n, indexed_uint_set& set);
    bool maximize_term_on_tableau(const lar_term& term, impq& term_max);
    bool costs_are_zeros_for_r_solver() const;
    bool reduced_costs_are_zeroes_for_r_solver() const;
    void set_costs_to_zero(const lar_term& term);
    void prepare_costs_for_r_solver(const lar_term& term);
    bool maximize_term_on_feasible_r_solver(lar_term& term, impq& term_max, vector<std::pair<mpq,lpvar>>* max_coeffs);
    u_dependency* get_dependencies_of_maximum(const vector<std::pair<mpq,lpvar>>& max_coeffs);
    void set_upper_bound_witness(lpvar j, u_dependency* ci, impq const& high);
    void set_lower_bound_witness(lpvar j, u_dependency* ci, impq const& low);
    void substitute_terms_in_linear_expression(const vector<std::pair<mpq, lpvar>>& left_side_with_terms,
                                               vector<std::pair<mpq, lpvar>>& left_side) const;

    bool use_tableau_costs() const;
    bool tableau_with_costs() const;
    bool costs_are_used() const;
    void change_basic_columns_dependend_on_a_given_nb_column(unsigned j, const numeric_pair<mpq>& delta);
    void update_x_and_inf_costs_for_column_with_changed_bounds(unsigned j);
    void add_touched_row(unsigned rid);
    void detect_rows_with_changed_bounds_for_column(unsigned j);
    void detect_rows_with_changed_bounds();

    void update_x_and_inf_costs_for_columns_with_changed_bounds_tableau();
    void solve_with_core_solver();
    numeric_pair<mpq> get_basic_var_value_from_row(unsigned i);
    bool all_constrained_variables_are_registered(const vector<std::pair<mpq, lpvar>>& left_side);
    bool all_constraints_hold() const;
    bool constraint_holds(const lar_base_constraint& constr) const;
    static void register_in_map(std::unordered_map<lpvar, mpq>& coeffs, const lar_base_constraint& cn, const mpq& a);
    static void register_monoid_in_map(std::unordered_map<lpvar, mpq>& coeffs, const mpq& a, unsigned j);
    bool the_left_sides_sum_to_zero(const vector<std::pair<mpq, unsigned>>& evidence) const;
    bool explanation_is_correct(explanation&) const;
    bool inf_explanation_is_correct() const;
    mpq sum_of_right_sides_of_explanation(explanation&) const;
    mpq get_left_side_val(const lar_base_constraint& cns, const std::unordered_map<lpvar, mpq>& var_map) const;
    void fill_var_set_for_random_update(unsigned sz, lpvar const* vars, vector<unsigned>& column_list);
    bool column_represents_row_in_tableau(unsigned j);
    void make_sure_that_the_bottom_right_elem_not_zero_in_tableau(unsigned i, unsigned j);
    void remove_last_row_and_column_from_tableau(unsigned j);
    void remove_last_column_from_A();

    void remove_last_column_from_basis_tableau(unsigned j);
    void remove_last_column_from_tableau();
    void clean_inf_heap_of_r_solver_after_pop();
    inline bool column_value_is_integer(unsigned j) const { return get_column_value(j).is_int(); }
    bool model_is_int_feasible() const;

    bool bound_is_integer_for_integer_column(unsigned j, const mpq& right_side) const;
    const lar_core_solver& get_core_solver() const;
    lar_core_solver& get_core_solver();
    lpvar to_column(unsigned ext_j) const;
    void fix_terms_with_rounded_columns();
    bool remove_from_basis(unsigned);
    lp_status adjust_integer_columns(lar_term const& term, impq const& prev_value, impq& term_max);
    lar_term get_term_to_maximize(unsigned ext_j) const;
    bool sum_first_coords(const lar_term& t, mpq& val) const;
    void register_normalized_term(const lar_term&, lpvar);
    void deregister_normalized_term(const lar_term&);

public:
    u_dependency* find_improved_bound(lpvar j, bool is_lower, mpq& bound);

    std::ostream& print_explanation(
        std::ostream& out, const explanation& exp, 
        std::function<std::string(lpvar)> var_str = [](lpvar j) { return std::string("j") + T_to_string(j); }) const;
    // this function just looks at the status
    bool is_feasible() const;

    const map<mpq, unsigned, obj_hash<mpq>, default_eq<mpq>>& fixed_var_table_int() const;
    const map<mpq, unsigned, obj_hash<mpq>, default_eq<mpq>>& fixed_var_table_real() const;

    bool find_in_fixed_tables(const rational& mpq, bool is_int, unsigned& j) const {
        return is_int ? fixed_var_table_int().find(mpq, j) : fixed_var_table_real().find(mpq, j);
    }

    template <typename T>
    void remove_non_fixed_from_table(T&);

    bool inside_bounds(lpvar, const impq&) const;

    void set_column_value(unsigned j, const impq& v);
    
    inline void set_column_value_test(unsigned j, const impq& v) {
        set_column_value(j, v);
    }

    // fix_int_cols: after maximizing try to move the integer columns to integer values;
    // pass false to keep the optimal (possibly fractional) vertex intact, e.g., for the largest cube test
    // The optional dependencies certify term_max only when the returned status is OPTIMAL.
    lp_status maximize_term(unsigned j_or_term, impq& term_max, bool fix_int_cols,
                            u_dependency** upper_bound_dependencies = nullptr);

    core_solver_pretty_printer<lp::mpq, lp::impq> pp(std::ostream& out) const;
    
    void get_infeasibility_explanation(explanation&) const;


    std::function<void(lpvar)> m_fixed_var_eh;
    template <typename T>
    u_dependency* explain_implied_bound(const implied_bound& ib, lp_bound_propagator<T>& bp) {
        ib.explain_weighted([&](mpq const& coeff, u_dependency* dep) {
            if (dep_manager().proofs_enabled()) {
                explanation ex;
                explain_evidence(dep, coeff, ex);
                for (auto ev : ex)
                    bp.consume(ev.coeff(), ev.ci());
            }
            else
                for (auto ci : flatten(dep))
                    bp.consume(coeff, ci);
        });
        return dep_manager().proofs_enabled() ? ib.explain_implied() : nullptr;
    }

    void set_value_for_nbasic_column(unsigned j, const impq& new_val);

    void remove_fixed_vars_from_base();
    /**
     * \brief set j to basic (if not already basic)
     * return the rest of the row as t comprising of non-fixed variables and coeff as sum of fixed variables.
     * return false if j has no rows.
     */

    struct solution {
        unsigned j;
        lar_term t;
    };

    lpvar add_named_var(unsigned ext_j, bool is_integer, const std::string&);
    void solve_for(unsigned_vector const& js, vector<solution>& sol);
    void check_fixed(unsigned j);
    void solve_for(unsigned j, uint_set& tabu, vector<solution>& sol);

    unsigned get_base_column_in_row(unsigned row_index) const;
#ifdef Z3DEBUG
    bool fixed_base_removed_correctly() const;
#endif
    constraint_index mk_var_bound(lpvar j, lconstraint_kind kind, const mpq& right_side);
    constraint_index mk_var_bound(lpvar j, lconstraint_kind kind, const mpq& right_side, const mpq& eps);
    void activate_check_on_equal(constraint_index, lpvar&);
    void activate(constraint_index);
    // true if activating constraint ci cannot change the bounds of its column
    bool constraint_is_redundant(constraint_index ci) const;
    void random_update(unsigned sz, lpvar const* vars);
    void add_column_rows_to_touched_rows(lpvar j);
    const indexed_uint_set & touched_rows() const;
    indexed_uint_set & touched_rows();
    unsigned_vector& row_bounds_to_replay();
    template <typename T>
    void propagate_bounds_for_touched_rows(lp_bound_propagator<T>& bp) {
        if (settings().propagate_eqs()) {
            if (settings().random_next() % 10 == 0) 
                remove_fixed_vars_from_base();
            bp.clear_for_eq();
            for (unsigned i : touched_rows()) {
                unsigned offset_eqs = stats().m_offset_eqs;
                bp.cheap_eq_on_nbase(i);
                if (settings().get_cancel_flag())
                    return;
                if (stats().m_offset_eqs > offset_eqs)
                    row_bounds_to_replay().push_back(i);
            }
        }
        for (unsigned i : touched_rows()) {
            calculate_implied_bounds_for_row(i, bp);
            if (settings().get_cancel_flag())
                return;
        }
        touched_rows().reset();
    }
    void collect_more_rows_for_lp_propagation();
    template <typename T>
    void check_missed_propagations(lp_bound_propagator<T>& bp) {
        for (unsigned i = 0; i < A_r().row_count(); ++i)
            if (!touched_rows().contains(i))
                if (0 < calculate_implied_bounds_for_row(i, bp)) {
                    verbose_stream() << i << ": " << get_row(i) << "\n";
                }
    }

    
    const impq m_zero_rs; // the right-hand side of every tableau row
    public:
    std::function<void (const lar_term*)> m_add_term_callback;
    std::function<void (unsigned)> m_update_column_bound_callback;  
    bool external_is_used(unsigned) const;
    void pop(unsigned k);
    trail_stack& trail();
    bool compare_values(lpvar j, lconstraint_kind kind, const mpq& right_side);
    lpvar add_term(const vector<std::pair<mpq, lpvar>>& coeffs, unsigned ext_i);
    void register_existing_terms();
    class scoped_auxiliary {
        lar_solver& s;
    public:
        scoped_auxiliary(lar_solver& s);
        ~scoped_auxiliary();
    };
    constraint_index add_var_bound(lpvar, lconstraint_kind, const mpq&);
    constraint_index add_var_bound_check_on_equal(lpvar, lconstraint_kind, const mpq&, lpvar&);

    lpvar add_var(unsigned ext_j, bool is_integer);
    void set_cut_strategy(unsigned cut_frequency);
    inline unsigned column_count() const { return A_r().column_count(); }
    lpvar local_to_external(lpvar idx) const; 
    bool column_associated_with_row(lpvar j) const;
    unsigned row_count() const { return A_r().row_count(); }
    bool var_is_registered(lpvar vj) const;
    void clear_inf_heap() {
        get_core_solver().m_r_solver.inf_heap().clear();
    }

    void pivot(int entering, int leaving) {
        get_core_solver().pivot(entering, leaving);
    }

    indexed_uint_set& basic_columns_with_changed_cost();

    template <typename ChangeReport>
    void change_basic_columns_dependend_on_a_given_nb_column_report(unsigned j,
                                                                    const numeric_pair<mpq>& delta,
                                                                    const ChangeReport& after) {
        for (const auto& c : A_r().m_columns[j]) {
            unsigned bj = get_core_solver().m_r_basis[c.var()];
            if (tableau_with_costs())
                basic_columns_with_changed_cost().insert(bj);
            get_core_solver().m_r_solver.add_delta_to_x_and_track_feasibility(bj, -A_r().get_val(c) * delta);
            after(bj);
            TRACE(change_x_del,
                  tout << "changed basis column " << bj << ", it is " << (get_core_solver().m_r_solver.column_is_feasible(bj) ? "feas" : "inf") << std::endl;);
        }
    }

    template <typename ChangeReport>
    void set_value_for_nbasic_column_report(unsigned j,
                                            const impq& new_val,
                                            const ChangeReport& after) {
        SASSERT(!is_base(j));
        auto& x = get_core_solver().r_x(j);
        auto delta = new_val - x;
        x = new_val;
        after(j);
        change_basic_columns_dependend_on_a_given_nb_column_report(j, delta, after);
    }

    template <typename Blocker, typename ChangeReport>
    bool try_to_patch(lpvar j, const mpq& val,
                      const Blocker& is_blocked,
                      const ChangeReport& change_report) {
        if (is_base(j))  {
            TRACE(nla_solver, get_int_solver()->display_row_info(tout, row_of_basic_column(j)) << "\n";);
            if (!remove_from_basis(j))
               return false;
        }

        impq ival(val);
        if (is_blocked(j, ival))
            return false;
        TRACE(nla_solver, tout << "j" << j << " not blocked\n";);
        impq delta = get_column_value(j) - ival;
        for (auto c : A_r().column(j)) {
            unsigned row_index = c.var();
            const mpq& a = c.coeff();
            unsigned rj = get_core_solver().m_r_basis[row_index];
            impq rj_new_val = a * delta + get_column_value(rj);
            // if (column_is_int(rj) && !rj_new_val.is_int())
            //     return false;
            if (is_blocked(rj, rj_new_val))
                return false;
        }

        set_value_for_nbasic_column_report(j, ival, change_report);
        return true;
    }
    inline bool column_has_upper_bound(unsigned j) const {
        return get_core_solver().m_r_solver.column_has_upper_bound(j);
    }

    inline bool column_has_lower_bound(unsigned j) const {
        return get_core_solver().m_r_solver.column_has_lower_bound(j);
    }

    svector<constraint_index> const& flatten(u_dependency* d);
    void push_explanation(u_dependency* d, explanation& ex) {
        for (auto ci : flatten(d))
            ex.push_back(ci);
    }    
    
    const evidence_manager& dep_manager() const;
    evidence_manager& dep_manager();

    void explain_evidence(u_dependency* dep, mpq const& coefficient, explanation& ex) const;

    u_dependency* explain_integer_infeasibility(lar_term const& row, unsigned_vector const& boxed = {});

    u_dependency* get_column_upper_bound_witness(unsigned j) const;
    
    const impq& get_upper_bound(lpvar j) const;
    const impq& get_lower_bound(lpvar j) const;
    mpq bound_span_x(lpvar j) const;
    bool has_lower_bound(lpvar var, u_dependency*& ci, mpq& value, bool& is_strict) const;
    bool has_upper_bound(lpvar var, u_dependency*& ci, mpq& value, bool& is_strict) const;
    bool has_bound_of_type(lpvar var, u_dependency*& ci, mpq& value, bool& is_strict, bool is_upper) const;
  
    bool has_value(lpvar var, mpq& value) const;
    bool fetch_normalized_term_column(const lar_term& t, std::pair<mpq, lpvar>&) const;
    bool column_is_fixed(unsigned j) const;
    bool column_is_free(unsigned j) const;
    bool column_is_feasible(unsigned j) const;
    lp_settings& settings();
    lp_settings const& settings() const;
    statistics& stats();

    void backup_x() { get_core_solver().backup_x(); }
    void restore_x() {
        auto& cs = get_core_solver();
        unsigned backup_sz = cs.backup_x_size();
        unsigned current_sz = cs.m_n();
        CTRACE(lar_solver_restore, backup_sz != current_sz,
               tout << "restore_x: backup_sz=" << backup_sz
               << " current_sz=" << current_sz << "\n";);
        cs.restore_x();
        if (backup_sz < current_sz) {
            // New columns were added after backup.
            // Recalculate basic variable values from non-basic ones
            // to restore the Ax=0 tableau invariant, then snap
            // non-basic columns to their bounds and find a feasible solution.
            for (unsigned i = 0; i < A_r().row_count(); i++)
                set_column_value(r_basis()[i], get_basic_var_value_from_row(i));
            move_non_basic_columns_to_bounds();
        }
        else {
            SASSERT(ax_is_correct());
            SASSERT(cs.m_r_solver.calc_current_x_is_feasible_include_non_basis());
        }
    }

    void updt_params(params_ref const& p);
    column_type get_column_type(unsigned j) const { return get_core_solver().m_column_types()[j]; }
    const vector<column_type>&  get_column_types() const { return get_core_solver().m_column_types(); }
    std::ostream& print_terms(std::ostream& out) const;
    std::ostream& print_term(lar_term const& term, std::ostream& out) const;
    static std::ostream& print_term_as_indices(lar_term const& term, std::ostream& out);
    std::ostream& print_constraint_indices_only(const lar_base_constraint* c, std::ostream& out) const;
    std::ostream& print_implied_bound(const implied_bound& be, std::ostream& out) const;
    std::ostream& print_values(std::ostream& out) const;
    std::ostream& display(std::ostream& out) const;
    std::ostream& display_constraint(std::ostream& out, constraint_index ci) const;
    bool init_model() const;
    mpq from_model_in_impq_to_mpq(const impq& v) const;
    mpq get_value(lpvar j) const;
    void get_model(std::unordered_map<lpvar, mpq>& variable_values) const;
    void get_rid_of_inf_eps();
    void get_model_do_not_care_about_diff_vars(std::unordered_map<lpvar, mpq>& variable_values) const;
    void get_model_do_not_care_about_diff_vars(std::unordered_map<lpvar, mpq>& variable_values, const mpq& delta) const;
    std::string get_variable_name(lpvar vi) const override;
    void set_variable_name(lpvar vi, const std::string&);
    unsigned number_of_vars() const;
    inline bool is_base(unsigned j) const { return get_core_solver().m_r_heading[j] >= 0; }
    inline const impq& column_lower_bound(unsigned j) const {
        return get_core_solver().lower_bound(j);
    }

    inline const impq& column_upper_bound(unsigned j) const {
        return get_core_solver().upper_bound(j);
    }

    inline bool column_is_bounded(unsigned j) const {
        return get_core_solver().column_is_bounded(j);
    }

    bool check_feasible() const {
        return get_core_solver().m_r_solver.calc_current_x_is_feasible_include_non_basis();
    }

    bool are_equal(lpvar j, lpvar k);
    std::pair<constraint_index, constraint_index> add_equality(lpvar j, lpvar k);

    u_dependency* get_bound_constraint_witnesses_for_column(unsigned j);
    
    template <typename T>
    u_dependency* get_bound_constraint_witnesses_for_columns(const T& collection) {
        u_dependency* dep = nullptr;
        for (auto j : collection) {
            u_dependency* d = get_bound_constraint_witnesses_for_column(j);
            dep = dep_manager().mk_join(dep, d);
        }
        return dep;
    }

    std::ostream& print_expl(std::ostream& out, const explanation& exp) const {
        for (auto p : exp)
            constraints().display(
                out, [this](lpvar j) { return get_variable_name(j); }, p.ci());
        return out;
    }

    void explain_fixed_column(unsigned j, explanation& ex);
    void explain_fixed_in_row(unsigned row, explanation& ex);
    u_dependency* join_deps(u_dependency* a, u_dependency *b) { return dep_manager().mk_join(a, b); }
    const constraint_set & constraints() const;
    void push();
    void pop();
    unsigned get_scope_level() const;

    u_dependency* get_column_lower_bound_witness(unsigned j) const;
    
    bool column_has_term(lpvar j) const;

    std::ostream& print_column_info(unsigned j, std::ostream& out, bool print_expl = false) const;

    std::ostream& display_column_explanation(std::ostream& out, unsigned j) const;
    
    void subst_known_terms(lar_term*);

    std::ostream& print_column_bound_info(unsigned j, std::ostream& out) const {
        return get_core_solver().m_r_solver.print_column_bound_info(j, out);
    }

    bool has_int_var() const;

    inline bool has_inf_int() const {
        for (unsigned j = 0; j < column_count(); ++j) {
            if (column_is_int(j) && !column_value_is_int(j))
                return true;
        }
        return false;
    }

    const vector<lar_term*>& terms() const;
    
    void set_int_solver(int_solver* int_slv);
    int_solver* get_int_solver();
    const int_solver* get_int_solver() const;
    const lar_term& get_term(lpvar j) const;
    lp_status find_feasible_solution();
    void move_non_basic_columns_to_bounds();
    bool move_non_basic_column_to_bounds(unsigned j);
    bool move_lpvar_to_value(lpvar j, mpq const& value);
    inline bool r_basis_has_inf_int() const {
        for (unsigned j : r_basis()) {
            if (column_is_int(j) && !column_value_is_int(j))
                return true;
        }
        return false;
    }
    void round_to_integer_solution();
    inline const row_strip<mpq>& get_row(unsigned i) const { return A_r().m_rows[i]; }
    inline const row_strip<mpq>& basic2row(unsigned i) const { return A_r().m_rows[row_of_basic_column(i)]; }
    inline const column_strip& get_column(unsigned i) const { return A_r().m_columns[i]; }
    bool row_is_correct(unsigned i) const;
    bool ax_is_correct() const;
    bool get_equality_and_right_side_for_term_on_current_x(lpvar j, mpq& rs, u_dependency*& ci, bool& upper_bound) const;
    bool var_is_int(lpvar v) const;
    inline const std_vector<int>& r_heading() const { return get_core_solver().m_r_heading; }
    inline const vector<unsigned>& r_basis() const { return get_core_solver().r_basis(); }
    inline const vector<unsigned>& r_nbasis() const { return get_core_solver().r_nbasis(); }
    inline bool column_is_real(unsigned j) const { return !column_is_int(j); }
    lp_status get_status() const;
    bool has_changed_columns() const;
    void set_status(lp_status s);
    lp_status solve();
    void fill_explanation_from_crossed_bounds_column(explanation& evidence) const;
    bool tighten_term_bounds_by_delta(lpvar j, const impq&);
    explicit lar_solver(bool proofs = false);
    void track_touched_rows(bool v);
    bool touched_rows_are_tracked() const;
    ~lar_solver() override;
    const vector<impq>& r_x() const { return get_core_solver().r_x(); }
    bool column_is_int(unsigned j) const;
    inline bool column_value_is_int(unsigned j) const { return get_core_solver().r_x(j).is_int(); }
    inline static_matrix<mpq, impq>& A_r() { return get_core_solver().m_r_A; }
    inline const static_matrix<mpq, impq>& A_r() const { return get_core_solver().m_r_A; }
    // columns
    const impq& get_column_value(lpvar j) const { return get_core_solver().r_x(j); }
    lpvar external_to_local(unsigned j) const;
    unsigned usage_in_terms(lpvar j) const;

    std::string get_bounds_string(unsigned j) const;
    void write_bound_lemma(unsigned j, bool is_low, const std::string & location, std::ostream & out) const;

    std::function<void (const indexed_uint_set& columns_with_changed_bound)> m_find_monics_with_changed_bounds_func = nullptr;
    friend int_solver;
    friend int_branch;
    friend imp;

};
}  // namespace lp