z3-src 416.0.2

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
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282

/*++
Copyright (c) 2006 Microsoft Corporation

Module Name:

    theory_arith.h

Abstract:

    <abstract>

Author:

    Leonardo de Moura (leonardo) 2008-04-21.

Revision History:

--*/
#pragma once

#include "util/map.h"
#include "util/heap.h"
#include "util/nat_set.h"
#include "util/inf_rational.h"
#include "util/s_integer.h"
#include "util/inf_s_integer.h"
#include "util/obj_pair_hashtable.h"
#include "util/uint_set.h"
#include "ast/arith_decl_plugin.h"
#include "model/numeral_factory.h"
#include "smt/smt_theory.h"
#include "params/theory_arith_params.h"
#include "smt/arith_eq_adapter.h"
#include "smt/smt_context.h"
#include "smt/old_interval.h"
#include "smt/arith_eq_solver.h"
#include "smt/theory_opt.h"
#include "math/grobner/grobner.h"

namespace smt {
    
    struct theory_arith_stats {
        unsigned m_conflicts, m_add_rows, m_pivots, m_diseq_cs, m_gomory_cuts, m_branches, m_gcd_tests, m_gcd_conflicts, m_patches, m_patches_succ;
        unsigned m_assert_lower, m_assert_upper, m_assert_diseq, m_core2th_eqs, m_core2th_diseqs;
        unsigned m_th2core_eqs, m_th2core_diseqs, m_bound_props, m_offset_eqs, m_fixed_eqs, m_offline_eqs;
        unsigned m_max_min; 
        unsigned m_assume_eqs;
        unsigned m_gb_simplify, m_gb_superpose, m_gb_compute_basis, m_gb_num_processed;
        unsigned m_nl_branching, m_nl_linear, m_nl_bounds, m_nl_cross_nested;
        unsigned m_branch_infeasible_int, m_branch_infeasible_var;
        unsigned m_tableau_max_rows, m_tableau_max_columns;

        void reset() { memset(this, 0, sizeof(theory_arith_stats)); }
        theory_arith_stats() { reset(); }
    };


    /**
       - There are 3 kinds of variables in the tableau: base, quasi-base,
       and non-base
       
       - Each base var and quasi-base var v owns a row R(v). 
       
       - If v is a base var, then R(v) contains v and other non-base variables.
       
       - If v is a quasi-base var, then R(v) contains v and other base and
       non-base variables.
       
       - Each quasi-base var occurs only once in the tableau (i.e., it
       occurs in R(v)).
       
       - A quasi-base var does not have upper&lower bounds and distinct set.
       
       - A quasi-base var v can be transformed into a base var by
       eliminating the base vars v' in R(v).  This can be accomplished by
       adding -c * R(v') where c is the coefficient of v' in R(v).
       
       - A column is used to store the occurrences of a non-base var v' in
       rows R(v), where v is a base variable.
       
       - An implied bound stores the linear equation that implied it.
       
    */

    template<typename Ext>
    class theory_arith : public theory, public theory_opt, private Ext {
    public:
        typedef typename Ext::numeral     numeral;
        typedef typename Ext::inf_numeral inf_numeral;
        typedef vector<numeral> numeral_vector; 
        typedef map<rational, theory_var, obj_hash<rational>, default_eq<rational> > rational2var;

        static const int    dead_row_id = -1;
    protected:
        bool proofs_enabled() const { return get_manager().proofs_enabled(); }
        bool coeffs_enabled() const { return proofs_enabled() || m_bound_watch != null_bool_var; }
        
        struct linear_monomial {
            numeral     m_coeff;
            theory_var  m_var;
            linear_monomial():m_var(null_theory_var) {}
            linear_monomial(numeral const & c, theory_var v):m_coeff(c), m_var(v) {}
        };

        /**
           \brief A row_entry is:  m_var*m_coeff

           m_col_idx points to the place in the
           column where the variable occurs.
        */
        struct row_entry {
            numeral     m_coeff;
            theory_var  m_var = 0;
            union {
                int       m_col_idx;
                int       m_next_free_row_entry_idx;
            };
            
            row_entry(): m_col_idx(0) {}
            row_entry(numeral const & c, theory_var v): m_coeff(c), m_var(v), m_col_idx(0) {}
            bool is_dead() const { return m_var == null_theory_var; }
        };

        /**
           \brief A column entry points to the row and the row_entry within the row 
           that has a non-zero coefficient on the variable associated
           with the column entry.
        */
        struct col_entry {
            int m_row_id = 0;
            union {
                int m_row_idx;
                int m_next_free_row_entry_idx;
            };
            
            col_entry(int r, int i): m_row_id(r), m_row_idx(i) {}
            col_entry(): m_row_idx(0) {}
            bool is_dead() const { return m_row_id == dead_row_id; }
        };
     
        struct column;

        /**
           \brief A row contains a base variable and set of
           row_entries. The base variable must occur in the set of
           row_entries with coefficient 1.
        */
        struct row {
            vector<row_entry> m_entries;
            unsigned          m_size;      // the real size, m_entries contains dead row_entries.
            int               m_base_var;
            int               m_first_free_idx; // first available position.
            row();
            unsigned size() const { return m_size; }
            unsigned num_entries() const { return m_entries.size(); }
            void reset();
            row_entry & operator[](unsigned idx) { return m_entries[idx]; }
            row_entry const & operator[](unsigned idx) const { return m_entries[idx]; }
            typename vector<row_entry>::iterator begin_entries() { return m_entries.begin(); }
            typename vector<row_entry>::const_iterator begin_entries() const { return m_entries.begin(); }
            typename vector<row_entry>::iterator end_entries() { return m_entries.end(); }
            typename vector<row_entry>::const_iterator end_entries() const { return m_entries.end(); }

            typename vector<row_entry>::iterator begin() { return m_entries.begin(); }
            typename vector<row_entry>::const_iterator begin() const { return m_entries.begin(); }
            typename vector<row_entry>::iterator end() { return m_entries.end(); }
            typename vector<row_entry>::const_iterator end() const { return m_entries.end(); }

            row_entry & add_row_entry(int & pos_idx);
            void del_row_entry(unsigned idx);
            void compress(vector<column> & cols); 
            void compress_if_needed(vector<column> & cols);
            void save_var_pos(svector<int> & result_map) const;
            void reset_var_pos(svector<int> & result_map) const;
            theory_var get_base_var() const { return m_base_var; }
#ifdef Z3DEBUG
            bool is_coeff_of(theory_var v, numeral const & expected) const;
#endif
            void display(std::ostream & out) const;
            numeral get_denominators_lcm() const;
            int get_idx_of(theory_var v) const;
        };
        
        /**
           \brief A column stores in which rows a variable occurs.
           The column may have free/dead entries. The field m_first_free_idx
           is a reference to the first free/dead entry.
        */
        struct column {
            svector<col_entry> m_entries;
            unsigned           m_size = 0; 
            int                m_first_free_idx = -1;
            
            unsigned size() const { return m_size; }
            unsigned num_entries() const { return m_entries.size(); }
            void reset();
            void compress(vector<row> & rows);
            void compress_if_needed(vector<row> & rows);
            void compress_singleton(vector<row> & rows, unsigned singleton_pos);
            col_entry const * get_first_col_entry() const;
            col_entry & operator[](unsigned idx) { return m_entries[idx]; }
            col_entry const & operator[](unsigned idx) const { return m_entries[idx]; }
            typename svector<col_entry>::iterator begin_entries() { return m_entries.begin(); }
            typename svector<col_entry>::const_iterator begin_entries() const { return m_entries.begin(); }
            typename svector<col_entry>::iterator end_entries() { return m_entries.end(); }
            typename svector<col_entry>::const_iterator end_entries() const { return m_entries.end(); }
            typename svector<col_entry>::iterator begin() { return m_entries.begin(); }
            typename svector<col_entry>::const_iterator begin() const { return m_entries.begin(); }
            typename svector<col_entry>::iterator end() { return m_entries.end(); }
            typename svector<col_entry>::const_iterator end() const { return m_entries.end(); }
            col_entry & add_col_entry(int & pos_idx);
            void del_col_entry(unsigned idx);
        };
        
        enum bound_kind {
            B_LOWER,
            B_UPPER
        };

        friend std::ostream & operator<<(std::ostream & out, bound_kind k) {
            switch (k) {
            case B_LOWER: out << ">="; break;
            case B_UPPER: out << "<="; break;
            }
            return out;
        }

        typedef svector<enode_pair> eq_vector;

        // keep track of coefficients used for bounds for proof generation.
        class antecedents_t {
            literal_vector    m_lits;
            eq_vector         m_eqs;
            vector<numeral>   m_lit_coeffs;
            vector<numeral>   m_eq_coeffs;
            vector<parameter> m_params;
            bool m_init = false;

            bool empty() const { 
                return m_eq_coeffs.empty() && m_lit_coeffs.empty(); 
            }

            void init();

        public:
            void reset();
            literal_vector const& lits() const { return m_lits; }
            eq_vector const& eqs() const { return m_eqs; }
            void push_lit(literal l, numeral const& r, bool proofs_enabled);
            void push_eq(enode_pair const& p, numeral const& r, bool proofs_enabled);
            void append(unsigned sz, literal const* ls) { m_lits.append(sz, ls); }
            void append(unsigned sz, enode_pair const* ps) { m_eqs.append(sz, ps); }
            unsigned num_params() const { return empty()?0:m_eq_coeffs.size() + m_lit_coeffs.size() + 1; }
            numeral const* lit_coeffs() const { return m_lit_coeffs.data(); }
            numeral const* eq_coeffs() const { return m_eq_coeffs.data(); }
            parameter* params(char const* name);
            std::ostream& display(theory_arith& th, std::ostream& out) const;
        };

        class antecedents {
            theory_arith& th;
            antecedents_t&  a;
        public:
            antecedents(theory_arith& th);
            ~antecedents();  
            literal_vector const& lits() const { return a.lits(); }
            eq_vector const& eqs() const { return a.eqs(); }
            void push_lit(literal l, numeral const& r, bool e) { a.push_lit(l, r, e); }
            void push_eq(enode_pair const& p, numeral const& r, bool e) { a.push_eq(p, r, e); }
            void append(unsigned sz, literal const* ls) { a.append(sz, ls); }
            void append(unsigned sz, enode_pair const* ps) { a.append(sz, ps); }
            unsigned num_params() const { return a.num_params(); }
            numeral const* lit_coeffs() const { return a.lit_coeffs(); }
            numeral const* eq_coeffs() const { return a.eq_coeffs(); }
            parameter* params(char const* name) { return a.params(name); }
            std::ostream& display(std::ostream& out) const { return a.display(th, out); }
        };

        class gomory_cut_justification;

        class bound { 
        protected:
            theory_var  m_var;
            inf_numeral m_value;
            unsigned    m_bound_kind:1; 
            unsigned    m_atom:1;
        public:
            bound(theory_var v, inf_numeral const & val, bound_kind k, bool a):
                m_var(v),
                m_value(val),
                m_bound_kind(k),
                m_atom(a) {
            }
            virtual ~bound() = default;
            theory_var get_var() const { return m_var; }
            bound_kind get_bound_kind() const { return static_cast<bound_kind>(m_bound_kind); }
            bool is_atom() const { return m_atom; }
            inf_numeral const & get_value() const { return m_value; }
            virtual bool has_justification() const { return false; }
            virtual void push_justification(antecedents& antecedents, numeral const& coeff, bool proofs_enabled) {}
            virtual std::ostream& display(theory_arith const& th, std::ostream& out) const;
        };


        enum atom_kind {
            A_LOWER,
            A_UPPER
        };

        class atom : public bound {
        protected:
            bool_var    m_bvar;
            inf_numeral m_k;
            unsigned    m_atom_kind:2;   // atom kind
            unsigned    m_is_true:1;     // cache: true if the atom was assigned to true.
        public:
            atom(bool_var bv, theory_var v, inf_numeral const & k, atom_kind kind);
            atom_kind get_atom_kind() const { return static_cast<atom_kind>(m_atom_kind); }
            inline inf_numeral const & get_k() const { return m_k; }
            bool_var get_bool_var() const { return m_bvar; }
            bool is_true() const { return m_is_true; }
            void assign_eh(bool is_true, inf_numeral const & epsilon);
            bool has_justification() const override { return true; }
            void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override {
                a.push_lit(literal(get_bool_var(), !m_is_true), coeff, proofs_enabled); 
            }
            std::ostream& display(theory_arith const& th, std::ostream& out) const override;
        };

        class eq_bound : public bound { 
            enode * m_lhs;
            enode * m_rhs;
        public:
            eq_bound(theory_var v, inf_numeral const & val, bound_kind k, enode * lhs, enode * rhs):
                bound(v, val, k, false),
                m_lhs(lhs),
                m_rhs(rhs) {
                SASSERT(m_lhs->get_root() == m_rhs->get_root());
            }
            bool has_justification() const override { return true; }
            void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override {
                SASSERT(m_lhs->get_root() == m_rhs->get_root());
                a.push_eq(enode_pair(m_lhs, m_rhs), coeff, proofs_enabled); 
            }
            std::ostream& display(theory_arith const& th, std::ostream& out) const override;
        };

        class derived_bound : public bound {
        protected:
            literal_vector m_lits;
            eq_vector      m_eqs;
            friend class theory_arith;
        public:
            derived_bound(theory_var v, inf_numeral const & val, bound_kind k):bound(v, val, k, false) {}
            literal_vector const& lits() const { return m_lits; }
            eq_vector const& eqs() const { return m_eqs; }
            bool has_justification() const override { return true; }
            void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override;
            virtual void push_lit(literal l, numeral const&) { m_lits.push_back(l); }
            virtual void push_eq(enode_pair const& p, numeral const&) { m_eqs.push_back(p); }
            std::ostream& display(theory_arith const& th, std::ostream& out) const override;
            
        };
    
        class justified_derived_bound : public derived_bound {
            vector<numeral>  m_lit_coeffs;
            vector<numeral>  m_eq_coeffs;
            friend class theory_arith;
        public:
            justified_derived_bound(theory_var v, inf_numeral const & val, bound_kind k):derived_bound(v, val, k) {}
            bool has_justification() const override { return true; }
            void push_justification(antecedents& a, numeral const& coeff, bool proofs_enabled) override;
            void push_lit(literal l, numeral const& coeff) override;

            void push_eq(enode_pair const& p, numeral const& coeff) override;
        };
   
        typedef int_hashtable<int_hash, default_eq<int> > literal_idx_set;
        typedef obj_pair_hashtable<enode, enode> eq_set;
        literal_vector   m_tmp_acc_lits;
        eq_vector        m_tmp_acc_eqs;
        literal_idx_set  m_tmp_lit_set;
        eq_set           m_tmp_eq_set;
        void accumulate_justification(bound & b, derived_bound & target, numeral const& coeff, literal_idx_set & lits, eq_set & eqs);
        inf_numeral normalize_bound(theory_var v, inf_numeral const & k, bound_kind kind);
        void mk_bound_from_row(theory_var v, inf_numeral const & coeff, bound_kind k, row const & r);

        typedef ptr_vector<atom> atoms;
// #define SPARSE_MAP

#ifdef SPARSE_MAP
        typedef u_map<atom *>    bool_var2atom;
#else
        typedef ptr_vector<atom> bool_var2atom;
#endif 
        struct theory_var_lt {
            bool operator()(theory_var v1, theory_var v2) const { return v1 < v2; }
        };

        typedef heap<theory_var_lt> var_heap;
        
        enum var_kind {
            NON_BASE,
            BASE,
            QUASI_BASE
        };

        struct var_data {
            unsigned     m_row_id:28; // row owned by the variable, irrelevant if kind() == NON_BASE
            unsigned     m_kind:2; 
            unsigned     m_is_int:1;
            unsigned     m_nl_propagated:1;
            var_data(bool is_int = false):m_row_id(0), m_kind(NON_BASE), m_is_int(is_int), m_nl_propagated(false) {}
            var_kind kind() const { return static_cast<var_kind>(m_kind); }
        };

        class bound_trail {
            theory_var  m_var;
            bound *     m_old_bound;
        public:
            bound_trail(theory_var v, bound * b, bool is_upper):
                m_var(v << 1 | static_cast<int>(is_upper)),
                m_old_bound(b) {
            }

            bool is_upper() const { return (m_var & 1) == 1; }
            theory_var get_var() const { return m_var >> 1; }
            bound * get_old_bound() const { return m_old_bound; }
        };


        theory_arith_stats      m_stats;
        theory_arith_params &   m_params;
        arith_util              m_util;
        arith_eq_solver         m_arith_eq_solver;
        ptr_vector<app>         m_underspecified_ops;
        ptr_vector<app>         m_unsupported_ops;
        arith_eq_adapter        m_arith_eq_adapter;
        vector<row>             m_rows;
        svector<unsigned>       m_dead_rows;
        vector<column>          m_columns;          // per var
        svector<var_data>       m_data;             // per var
        vector<inf_numeral>     m_value;            // per var, the current assignment for the variable.
        vector<inf_numeral>     m_old_value;        // per var, the old assignment for the variable.
        ptr_vector<bound>       m_bounds[2];        // per var, lower bound & upper_bound
        vector<atoms>           m_var_occs;         // per var, atoms that contain a variable
        svector<unsigned>       m_unassigned_atoms; // per var, the number of unassigned atoms that contain a variable.
        bool_var2atom           m_bool_var2atom;    // map bool_var -> atom
        svector<int>            m_var_pos;          // temporary array used in add_rows
        atoms                   m_atoms;            // set of theory atoms
        ptr_vector<bound>       m_asserted_bounds;  // set of asserted bounds
        unsigned                m_asserted_qhead = 0;   
        ptr_vector<atom>        m_new_atoms;        // new bound atoms that have yet to be internalized.
        svector<theory_var>     m_nl_monomials;     // non linear monomials
        svector<theory_var>     m_nl_propagated;    // non linear monomials that became linear
        v_dependency_manager    m_dep_manager;      // for tracking bounds during non-linear reasoning

        vector<uint_set>        m_row_vars;         // variables in a given row. Used during internalization to detect repeated variables.
        unsigned                m_row_vars_top = 0;

        var_heap                m_to_patch;         // heap containing all variables v s.t. m_value[v] does not satisfy bounds of v.
        nat_set                 m_left_basis;       // temporary: set of variables that already left the basis in make_feasible
        bool                    m_blands_rule = false;

        svector<unsigned>       m_update_trail_stack;    // temporary trail stack used to restore the last feasible assignment.
        nat_set                 m_in_update_trail_stack; // set of variables in m_update_trail_stack

        svector<unsigned>       m_to_check;    // rows that should be checked for theory propagation
        nat_set                 m_in_to_check; // set of rows in m_to_check. 
        
        inf_numeral             m_tmp;
        random_gen              m_random;
        unsigned                m_num_conflicts = 0;

        unsigned                m_branch_cut_counter = 0;
        bool                    m_eager_gcd; // true if gcd should be applied at every add_row
        unsigned                m_final_check_idx = 0;


        // backtracking
        svector<bound_trail>    m_bound_trail;
        svector<unsigned>       m_unassigned_atoms_trail;
        ptr_vector<bound>       m_bounds_to_delete;
        struct scope {
            unsigned      m_atoms_lim;
            unsigned      m_bound_trail_lim;
            unsigned      m_unassigned_atoms_trail_lim;
            unsigned      m_asserted_bounds_lim;
            unsigned      m_asserted_qhead_old;
            unsigned      m_bounds_to_delete_lim;
            unsigned      m_nl_monomials_lim;
            unsigned      m_nl_propagated_lim;
        };

        svector<scope>          m_scopes;
        literal_vector          m_tmp_literal_vector2;
        antecedents_t           m_antecedents[3];
        unsigned                m_antecedents_index;

        struct var_value_hash;
        friend struct var_value_hash;
        struct var_value_hash {
            theory_arith & m_th;
            var_value_hash(theory_arith & th):m_th(th) {}
            unsigned operator()(theory_var v) const { return m_th.get_value(v).hash(); }
        };

        struct var_value_eq;
        friend struct var_value_eq;
        struct var_value_eq {
            theory_arith & m_th;
            var_value_eq(theory_arith & th):m_th(th) {}
            bool operator()(theory_var v1, theory_var v2) const { return m_th.get_value(v1) == m_th.get_value(v2) && m_th.is_int_src(v1) == m_th.is_int_src(v2); }
        };

        typedef int_hashtable<var_value_hash, var_value_eq> var_value_table;
        var_value_table             m_var_value_table;

        theory_var mk_var(enode * n) override;

        void found_unsupported_op(app * n);
        void found_underspecified_op(app * n);

        bool has_var(expr * v) const { return get_context().e_internalized(v) && get_context().get_enode(v)->get_th_var(get_id()) != null_theory_var; }
        theory_var expr2var(expr * v) const { SASSERT(get_context().e_internalized(v)); return get_context().get_enode(v)->get_th_var(get_id()); }
        expr * var2expr(theory_var v) const { return get_enode(v)->get_expr(); }
        bool reflection_enabled() const;
        bool reflect(app * n) const;
        unsigned lazy_pivoting_lvl() const { return m_params.m_arith_lazy_pivoting_lvl; }
        bool propagate_eqs() const { return m_params.m_arith_propagate_eqs && m_num_conflicts < m_params.m_arith_propagation_threshold; }
        bool propagate_diseqs() const { return false; }
        bool random_initial_value() const { return m_params.m_arith_random_initial_value; }
        int random_lower() const { return m_params.m_arith_random_lower; }
        int random_upper() const { return m_params.m_arith_random_upper; }
        unsigned blands_rule_threshold() const { return m_params.m_arith_blands_rule_threshold; }
        bound_prop_mode propagation_mode() const { return m_num_conflicts < m_params.m_arith_propagation_threshold ? m_params.m_arith_bound_prop : bound_prop_mode::BP_NONE; }
        bool adaptive() const { return m_params.m_arith_adaptive; }
        double adaptive_assertion_threshold() const { return m_params.m_arith_adaptive_assertion_threshold; }
        unsigned max_lemma_size() const { return m_params.m_arith_max_lemma_size; }
        unsigned small_lemma_size() const { return m_params.m_arith_small_lemma_size; }
        bool relax_bounds() const { return m_params.m_arith_stronger_lemmas; }
        bool skip_big_coeffs() const { return m_params.m_arith_skip_rows_with_big_coeffs; }
        bool process_atoms() const;
        unsigned get_num_conflicts() const { return m_num_conflicts; }
        var_kind get_var_kind(theory_var v) const { return m_data[v].kind(); }
        bool is_base(theory_var v) const { return v != null_theory_var && get_var_kind(v) == BASE; }
        bool is_quasi_base(theory_var v) const { return v != null_theory_var && get_var_kind(v) == QUASI_BASE; }
        bool is_non_base(theory_var v) const { return v != null_theory_var && get_var_kind(v) == NON_BASE; }
        void set_var_kind(theory_var v, var_kind k) { m_data[v].m_kind = k; }
        unsigned get_var_row(theory_var v) const { SASSERT(!is_non_base(v)); return m_data[v].m_row_id; }
        void set_var_row(theory_var v, unsigned r_id) { m_data[v].m_row_id = r_id; }
        ptr_vector<expr> m_todo;
        bool is_int_expr(expr* e);
        bool is_int(theory_var v) const { return m_data[v].m_is_int; }
        bool is_int_src(theory_var v) const { return m_util.is_int(var2expr(v)); }
        bool is_real(theory_var v) const { return !is_int(v); }
        bool is_real_src(theory_var v) const { return !is_int_src(v); }
        bool get_implied_old_value(theory_var v, inf_numeral & r) const;
        inf_numeral const & get_implied_value(theory_var v) const;
        inf_numeral const & get_quasi_base_value(theory_var v) const { return get_implied_value(v); }
        inf_numeral const & get_value(theory_var v) const { return is_quasi_base(v) ? get_quasi_base_value(v) : m_value[v]; }
        bound * get_bound(theory_var v, bool upper) const { return m_bounds[static_cast<unsigned>(upper)][v]; }
        bound * lower(theory_var v) const { return m_bounds[0][v]; }
        bound * upper(theory_var v) const { return m_bounds[1][v]; }
        inf_numeral const & lower_bound(theory_var v) const { SASSERT(lower(v) != 0); return lower(v)->get_value(); }
        inf_numeral const & upper_bound(theory_var v) const { SASSERT(upper(v) != 0); return upper(v)->get_value(); }
        bool below_lower(theory_var v) const { bound * l = lower(v); return l != nullptr && get_value(v) < l->get_value(); }
        bool above_upper(theory_var v) const { bound * u = upper(v); return u != nullptr && get_value(v) > u->get_value(); }
        bool below_upper(theory_var v) const { bound * u = upper(v); return u == nullptr || get_value(v) < u->get_value(); }
        bool above_lower(theory_var v) const { bound * l = lower(v); return l == nullptr || get_value(v) > l->get_value(); }
        bool at_bound(theory_var v) const;
        bool at_lower(theory_var v) const { bound * l = lower(v); return l != nullptr && get_value(v) == l->get_value(); }
        bool at_upper(theory_var v) const { bound * u = upper(v); return u != nullptr && get_value(v) == u->get_value(); }
        bool is_free(theory_var v) const { return lower(v) == nullptr && upper(v) == nullptr; }
        bool is_non_free(theory_var v) const { return lower(v) != nullptr || upper(v) != nullptr; }
        bool is_bounded(theory_var v) const { return lower(v) != nullptr && upper(v) != nullptr; }
        bool is_free(expr * n) const { 
            SASSERT(get_context().e_internalized(n) && get_context().get_enode(n)->get_th_var(get_id()) != null_theory_var);
            return is_free(get_context().get_enode(n)->get_th_var(get_id())); 
        }
        bool is_fixed(theory_var v) const;
        void set_bound_core(theory_var v, bound * new_bound, bool upper) { m_bounds[static_cast<unsigned>(upper)][v] = new_bound; }
        void restore_bound(theory_var v, bound * new_bound, bool upper) { set_bound_core(v, new_bound, upper); }
        void restore_nl_propagated_flag(unsigned old_trail_size);
        void set_bound(bound * new_bound, bool upper);
        inf_numeral const & get_epsilon(theory_var v) const { return is_real(v) ? this->m_real_epsilon : this->m_int_epsilon; }
        bool enable_cgc_for(app * n) const;
        enode * mk_enode(app * n);
        void mk_enode_if_reflect(app * n);
        template<bool invert>
        void add_row_entry(unsigned r_id, numeral const & coeff, theory_var v);
        uint_set& row_vars();
        class scoped_row_vars;

        void check_app(expr* e, expr* n);
        void internalize_internal_monomial(app * m, unsigned r_id);
        theory_var internalize_add(app * n);
        theory_var internalize_sub(app * n);
        theory_var internalize_mul_core(app * m);
        theory_var internalize_mul(app * m);
        theory_var internalize_div(app * n);
        theory_var mk_binary_op(app * n);
        theory_var internalize_idiv(app * n);
        theory_var internalize_mod(app * n);
        theory_var internalize_rem(app * n);
        theory_var internalize_to_real(app * n);
        theory_var internalize_to_int(app * n);
        void internalize_is_int(app * n);
        theory_var internalize_numeral(app * n);
        theory_var internalize_numeral(app * n, numeral const& val);
        theory_var internalize_term_core(app * n);
        void mk_axiom(expr * n1, expr * n2, bool simplify_conseq = true);
        void mk_idiv_mod_axioms(expr * dividend, expr * divisor);
        void mk_div_axiom(expr * dividend, expr * divisor);
        void mk_rem_axiom(expr * dividend, expr * divisor);
        void mk_to_int_axiom(app* to_int);
        void mk_is_int_axiom(app* is_int);

        unsigned mk_row();
        void init_row(unsigned r_id);
        void collect_vars(unsigned r_id, var_kind k, buffer<linear_monomial> & result);
        void normalize_quasi_base_row(unsigned r_id);
        void quasi_base_row2base_row(unsigned r_id);
        void normalize_base_row(unsigned r_id);
        void mk_clause(literal l1, literal l2, unsigned num_params, parameter * params);
        void mk_clause(literal l1, literal l2, literal l3, unsigned num_params, parameter * params);
        void mk_bound_axioms(atom * a);
        void mk_bound_axiom(atom* a1, atom* a2);
        void flush_bound_axioms();
        typename atoms::iterator next_sup(atom* a1, atom_kind kind, 
                                          typename atoms::iterator it, 
                                          typename atoms::iterator end,
                                          bool& found_compatible);
        typename atoms::iterator next_inf(atom* a1, atom_kind kind, 
                                          typename atoms::iterator it, 
                                          typename atoms::iterator end,
                                          bool& found_compatible);
        typename atoms::iterator first(atom_kind kind, 
                                       typename atoms::iterator it, 
                                       typename atoms::iterator end);
        struct compare_atoms {
            bool operator()(atom* a1, atom* a2) const { return a1->get_k() < a2->get_k(); }
        };
        bool default_internalizer() const override { return false; }
        bool internalize_atom(app * n, bool gate_ctx) override;
        bool internalize_term(app * term) override;
        void internalize_eq_eh(app * atom, bool_var v) override;
        void apply_sort_cnstr(enode * n, sort * s) override;
        
        void assign_eh(bool_var v, bool is_true) override;
        void new_eq_eh(theory_var v1, theory_var v2) override;
        bool use_diseqs() const override;
        void new_diseq_eh(theory_var v1, theory_var v2) override;

        void push_scope_eh() override;
        void pop_scope_eh(unsigned num_scopes) override;

        void relevant_eh(app * n) override;
        
        void restart_eh() override;
        void init_search_eh() override;
        void initialize_value(expr* var, expr* value) override;
        /**
           \brief True if the assignment may be changed during final
           check.  assume_eqs, check_int_feasibility,
           process_non_linear may change the current assignment to
           satisfy their respective constraints.  However, when they
           do that the may create inconsistencies in the other
           modules. I use m_liberal_final_check to avoid infinite
           loops where the modules keep changing the assignment and no
           progress is made. If m_liberal_final_check is set to false,
           these modules will avoid mutating the assignment to satisfy
           constraints.

           See also m_changed_assignment flag.
        */
        bool    m_liberal_final_check = true; 
        final_check_status final_check_core();
        final_check_status final_check_eh(unsigned) override;
        
        bool can_propagate() override;
        void propagate() override;
        bool propagate_core();
        void failed();

        
        void flush_eh() override;
        void reset_eh() override;
        
        // -----------------------------------
        //
        // bool_var -> atom mapping
        //
        // -----------------------------------
        void insert_bv2a(bool_var bv, atom * a) {
#ifdef SPARSE_MAP
            m_bool_var2atom.insert(bv, a);
#else
            m_bool_var2atom.setx(bv, a, 0);
#endif
        }
        
        void erase_bv2a(bool_var bv) {
#ifdef SPARSE_MAP
            m_bool_var2atom.erase(bv);
#else
            m_bool_var2atom[bv] = 0;
#endif
        }

        atom * get_bv2a(bool_var bv) {
#ifdef SPARSE_MAP
            atom * a;
            m_bool_var2atom.find(bv, a);
            return a;
#else
            return m_bool_var2atom.get(bv, 0);
#endif
        }


        // -----------------------------------
        //
        // Add Row
        //
        // -----------------------------------
        void add_row(unsigned r1, const numeral & coeff, unsigned r2, bool apply_gcd_test);
        void add_rows(unsigned r1, unsigned sz, linear_monomial * a_xs);

        // -----------------------------------
        //
        // Assignment management
        //
        // -----------------------------------
        bool m_changed_assignment = false; //!< auxiliary variable set to true when the assignment is changed.
        void save_value(theory_var v);
        void discard_update_trail();
        void restore_assignment();
        void update_value_core(theory_var v, inf_numeral const & delta);
        void update_value(theory_var v, inf_numeral const & delta);
        void set_value(theory_var v, const inf_numeral & new_val) { update_value(v, new_val - m_value[v]); }

        // -----------------------------------
        //
        // Pivoting
        //
        // -----------------------------------
        template<bool Lazy>
        void pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, bool apply_gcd_test);
        template<bool Lazy>
        void eliminate(theory_var x_i, bool apply_gcd_test);
        void update_and_pivot(theory_var x_i, theory_var x_j, numeral const & a_ij, inf_numeral const & x_i_new_val);
        int get_num_non_free_dep_vars(theory_var v, int best_so_far);
        theory_var select_blands_pivot_core(theory_var x_i, bool is_below, numeral & out_a_ij);
        template<bool is_below>
        theory_var select_pivot_core(theory_var x_i, numeral & out_a_ij);
        theory_var select_pivot(theory_var x_i, bool is_below, numeral & out_a_ij);

        // -----------------------------------
        //
        // Make feasible
        //
        // -----------------------------------
        bool make_var_feasible(theory_var x_i);
        theory_var select_var_to_fix();
        theory_var select_lg_error_var(bool least);
        theory_var select_greatest_error_var() { return select_lg_error_var(false); }
        theory_var select_least_error_var() { return select_lg_error_var(true); }
        theory_var select_smallest_var();
        bool make_feasible();
        void sign_row_conflict(theory_var x_i, bool is_below);

        // -----------------------------------
        //
        // Assert bound
        //
        // -----------------------------------
        bool assert_lower(bound * b);
        bool assert_upper(bound * b);
        bool assert_bound(bound * b);
        void sign_bound_conflict(bound * b1, bound * b2);

        // -----------------------------------
        //
        // Bound propagation
        //
        // -----------------------------------
        void mark_row_for_bound_prop(unsigned r1);
        void add_column_rows_to_touched_rows(theory_var v);
        void is_row_useful_for_bound_prop(row const & r, int & lower_idx, int & upper_idx) const;
        unsigned imply_bound_for_monomial(row const & r, int idx, bool lower);
        unsigned imply_bound_for_all_monomials(row const & r, bool lower);
        void explain_bound(row const & r, int idx, bool lower, inf_numeral & delta, 
                           antecedents & antecedents);
        unsigned mk_implied_bound(row const & r, unsigned idx, bool lower, theory_var v, bound_kind kind, inf_numeral const & k);
        void assign_bound_literal(literal l, row const & r, unsigned idx, bool lower, inf_numeral & delta);
        void propagate_bounds();

        // -----------------------------------
        //
        // Freedom intervals
        //
        // -----------------------------------
        bool get_freedom_interval(theory_var x_j, bool & inf_l, inf_numeral & l, bool & inf_u, inf_numeral & u, numeral & m);

        // -----------------------------------
        //
        // Implied eqs
        //
        // -----------------------------------
        bool try_to_imply_eq(theory_var v1, theory_var v2);

        // -----------------------------------
        //
        // Assume eqs with randomization
        //
        // -----------------------------------
        typedef int_hashtable<int_hash, default_eq<int> > var_set;
        var_set         m_tmp_var_set;
        var_set         m_tmp_var_set2;
        svector<std::pair<theory_var, theory_var> >       m_assume_eq_candidates;
        unsigned                                          m_assume_eq_head = 0;
        bool random_update(theory_var v);
        void mutate_assignment();
        bool assume_eqs();
        bool delayed_assume_eqs();

        // -----------------------------------
        //
        // Integrality
        //
        // -----------------------------------
        void move_non_base_vars_to_bounds();
        bool has_infeasible_int_var();
        theory_var find_infeasible_int_base_var();
        theory_var find_bounded_infeasible_int_base_var();
        void branch_infeasible_int_var(theory_var v);
        bool branch_infeasible_int_equality();
        bool constrain_free_vars(row const & r);
        bool is_gomory_cut_target(row const & r);
        bool mk_gomory_cut(row const & r);
        bool gcd_test(row const & r);
        bool ext_gcd_test(row const & r, numeral const & least_coeff, numeral const & lcm_den, numeral const & consts);
        bool gcd_test();
        void mk_polynomial_ge(unsigned num_args, row_entry const * args, rational const& k, expr_ref & result);
        bool max_min_infeasible_int_vars();
        void patch_int_infeasible_vars();
        void fix_non_base_vars();
//        unsynch_mpq_manager m_es_num_manager; // manager for euclidean solver.
//        struct euclidean_solver_bridge;
//        bool apply_euclidean_solver();
        final_check_status check_int_feasibility();

        // -----------------------------------
        //
        // Eq propagation
        //
        // -----------------------------------
        typedef std::pair<numeral, bool> value_sort_pair;
        typedef pair_hash<obj_hash<numeral>, bool_hash> value_sort_pair_hash;
        typedef map<value_sort_pair, theory_var, value_sort_pair_hash, default_eq<value_sort_pair> > value2var;
        value2var               m_fixed_var_table;
        
        typedef std::pair<theory_var, numeral> var_offset;
        typedef pair_hash<int_hash, obj_hash<numeral> > var_offset_hash;
        typedef map<var_offset, int, var_offset_hash, default_eq<var_offset> > var_offset2row_id;
        var_offset2row_id       m_var_offset2row_id;

        bool is_equal(theory_var x, theory_var y) const { return get_enode(x)->get_root() == get_enode(y)->get_root(); }
        void fixed_var_eh(theory_var v);
        bool is_offset_row(row const & r, theory_var & x, theory_var & y, numeral & k) const;
        void propagate_cheap_eq(unsigned rid);
        void propagate_eq_to_core(theory_var x, theory_var y, antecedents& antecedents);

        bool is_shared(theory_var v) const override;

        // -----------------------------------
        //
        // Justification
        //
        // -----------------------------------
        void set_conflict(unsigned num_literals, literal const * lits, unsigned num_eqs, enode_pair const * eqs, antecedents& antecedents, char const* proof_rule);
        void set_conflict(antecedents const& ante, antecedents& bounds, char const* proof_rule);
        void set_conflict(derived_bound const& ante, antecedents& bounds, char const* proof_rule);
        void collect_fixed_var_justifications(row const & r, antecedents& antecedents) const;
        
        // -----------------------------------
        //
        // Backtracking
        //
        // -----------------------------------
        void push_bound_trail(theory_var v, bound * old_bound, bool is_upper) { m_bound_trail.push_back(bound_trail(v, old_bound, is_upper)); }
        void push_dec_unassigned_atoms_trail(theory_var v) { m_unassigned_atoms_trail.push_back(v); }
        void restore_bounds(unsigned old_trail_size);
        void restore_unassigned_atoms(unsigned old_trail_size);
        void del_atoms(unsigned old_size);
        void del_bounds(unsigned old_size);
        void del_vars(unsigned old_num_vars);
        void del_row(unsigned r_id);

        // -----------------------------------
        //
        // Auxiliary methods
        //
        // -----------------------------------
        col_entry const * get_a_base_row_that_contains(theory_var v);
        bool all_coeff_int(row const & r) const;
        col_entry const * get_row_for_eliminating(theory_var v) const;
        void move_unconstrained_to_base();
        void elim_quasi_base_rows();
        void remove_fixed_vars_from_base();
        void try_to_minimize_rational_coeffs();

        /**
           \brief See comment in theory::mk_eq_atom
        */
        app * mk_eq_atom(expr * lhs, expr * rhs) override { return m_util.mk_eq(lhs, rhs); }

        // -----------------------------------
        //
        // Maximization/Minimization 
        //
        // -----------------------------------
        row               m_tmp_row;

        void add_tmp_row(row & r1, numeral const & coeff, row const & r2);
        bool is_safe_to_leave(theory_var x, bool inc, bool& has_int, bool& is_shared);
        template<bool invert>
        void add_tmp_row_entry(row & r, numeral const & coeff, theory_var v);
        enum max_min_t { UNBOUNDED, AT_BOUND, OPTIMIZED, BEST_EFFORT};
        max_min_t max_min(theory_var v, bool max, bool maintain_integrality, bool& has_shared);
        bool has_interface_equality(theory_var v);
        bool max_min(svector<theory_var> const & vars);

        max_min_t max_min(row& r, bool max, bool maintain_integrality, bool& has_shared);
        bool unbounded_gain(inf_numeral const & max_gain) const;
        bool safe_gain(inf_numeral const& min_gain, inf_numeral const & max_gain) const;
        void normalize_gain(numeral const& divisor, inf_numeral & max_gain) const;
        void init_gains(theory_var x, bool inc, inf_numeral& min_gain, inf_numeral& max_gain);
        bool update_gains(bool inc, theory_var x_i, numeral const& a_ij, 
                          inf_numeral& min_gain, inf_numeral& max_gain);
        bool move_to_bound(theory_var x_i, bool inc, unsigned& best_efforts, bool& has_shared);
        bool pick_var_to_leave(
            theory_var x_j, bool inc, numeral & a_ij, 
            inf_numeral& min_gain, inf_numeral& max_gain, 
            bool& shared, theory_var& x_i);

        // -----------------------------------
        //
        // Non linear
        //
        // -----------------------------------
        typedef int_hashtable<int_hash, default_eq<int> > row_set;
        bool            m_model_depends_on_computed_epsilon = false;
        unsigned        m_nl_rounds = 0;
        bool            m_nl_gb_exhausted = false;
        unsigned        m_nl_strategy_idx = 0; // for fairness
        expr_ref_vector m_nl_new_exprs;
        typedef obj_map<expr, unsigned> var2num_occs;
        var2num_occs m_var2num_occs;
        typedef std::pair<expr *, unsigned> var_num_occs;
        struct var_num_occs_lt { bool operator()(var_num_occs const & vn1, var_num_occs const & vn2) const { return vn1.second > vn2.second; } };

        /**
           \brief A monomial is 'pure' if does not have a numeric coefficient.
        */
        bool is_pure_monomial(expr * m) const;
        bool is_pure_monomial(theory_var v) const { return is_pure_monomial(get_enode(v)->get_expr()); }
        void mark_var(theory_var v, svector<theory_var> & vars, var_set & already_found);
        void mark_dependents(theory_var v, svector<theory_var> & vars, var_set & already_found, row_set & already_visited_rows);
        void get_non_linear_cluster(svector<theory_var> & vars);

        typedef std::pair<expr *, unsigned> var_power_pair;
        typedef std::pair<unsigned, var_power_pair> n_var_power_pair;
        n_var_power_pair analyze_monomial(expr * m) const;
        
        rational decompose_monomial(expr* m, buffer<var_power_pair>& vp) const;

        void display_monomial(std::ostream & out, expr * m) const;
        bool propagate_nl_upward(expr * m);
        bool propagate_nl_downward(expr * m, var_power_pair const& p);
        interval mk_interval_for(theory_var v);
        interval mk_interval_for(expr * n);
        void mul_bound_of(expr * var, unsigned power, interval & target);
        interval evaluate_as_interval(expr * n);
        void dependency2new_bound(v_dependency * dep, derived_bound& new_bound);
        void mk_derived_nl_bound(theory_var v, inf_numeral const & coeff, bound_kind k, v_dependency * dep);
        bool update_bounds_using_interval(theory_var v, interval const & i);
        bool update_bounds_using_interval(expr * n, interval const & i);
        bool propagate_nl_bounds(expr * m);
        bool propagate_nl_bounds();
        bool is_problematic_non_linear_row(row const & r);
        bool is_mixed_real_integer(row const & r) const;
        bool is_integer(row const & r) const;
        typedef std::pair<rational, expr *> coeff_expr; 
        bool get_polynomial_info(buffer<coeff_expr> const & p, sbuffer<var_num_occs> & vars);
        expr_ref p2expr(buffer<coeff_expr> & p);
        expr * power(expr * var, unsigned power);
        expr * mk_nary_mul(unsigned sz, expr * const * args, bool is_int);
        expr * mk_nary_add(unsigned sz, expr * const * args, bool is_int);
        expr * mk_nary_add(unsigned sz, expr * const * args);
        void display_nested_form(std::ostream & out, expr * p);
        unsigned get_degree_of(expr * m, expr * var);
        unsigned get_min_degree(buffer<coeff_expr> & p, expr * var);
        expr * factor(expr * m, expr * var, unsigned d);
        bool in_monovariate_monomials(buffer<coeff_expr> & p, expr * var, unsigned & i1, rational & c1, unsigned & n1, unsigned & i2, rational & c2, unsigned & n2);
        expr_ref horner(unsigned depth, buffer<coeff_expr> & p, expr * var);
        expr_ref cross_nested(unsigned depth, buffer<coeff_expr> & p, expr * var);
        bool is_cross_nested_consistent(buffer<coeff_expr> & p);
        bool is_cross_nested_consistent(row const & r);
        bool is_cross_nested_consistent(svector<theory_var> const & nl_cluster);
        rational get_value(theory_var v, bool & computed_epsilon);
        bool check_monomial_assignment(theory_var v, bool & computed_epsilon);
        bool check_monomial_assignments();
        theory_var find_nl_var_for_branching();
        bool branch_nl_int_var(theory_var v);
        bool is_monomial_linear(expr * m) const;
        numeral get_monomial_fixed_var_product(expr * m) const;
        expr * get_monomial_non_fixed_var(expr * m) const;
        bool propagate_linear_monomial(theory_var v);
        bool propagate_linear_monomials();
        grobner::monomial * mk_gb_monomial(rational const & coeff, expr * m, grobner & gb, v_dependency * & dep, var_set & already_found);
        void add_monomial_def_to_gb(theory_var v, grobner & gb);
        void add_row_to_gb(row const & r, grobner & gb);
        void init_grobner_var_order(svector<theory_var> const & nl_cluster, grobner & gb);
        void init_grobner(svector<theory_var> const & nl_cluster, grobner & gb);
        interval mk_interval_for(grobner::monomial const * m);
        void set_conflict(v_dependency * d);
        bool is_inconsistent(interval const & I, unsigned num_monomials, grobner::monomial * const * monomials, v_dependency * dep);
        bool is_inconsistent(grobner::equation const * eq, grobner & gb);
        bool is_inconsistent2(grobner::equation const * eq, grobner & gb);
        expr * monomial2expr(grobner::monomial const * m, bool is_int);
        bool internalize_gb_eq(grobner::equation const * eq);
        enum gb_result { GB_PROGRESS, GB_NEW_EQ, GB_FAIL };
        gb_result compute_grobner(svector<theory_var> const & nl_cluster);
        bool compute_basis_loop(grobner & gb);
        void compute_basis(grobner&, bool&);
        void update_statistics(grobner&);
        void set_gb_exhausted();
        bool get_gb_eqs_and_look_for_conflict(ptr_vector<grobner::equation>& eqs, grobner&);
        bool scan_for_linear(ptr_vector<grobner::equation>& eqs, grobner&);
        bool try_to_modify_eqs(ptr_vector<grobner::equation>& eqs, grobner&, unsigned &);
        bool max_min_nl_vars();
        final_check_status process_non_linear();
        

        // -----------------------------------
        //
        // Constructor 
        //
        // -----------------------------------
    public:
        theory_arith(context& ctx);
        
        theory * mk_fresh(context * new_ctx) override;

        void setup() override;

        lbool get_phase(bool_var v) override;

        char const * get_name() const override { return "arithmetic"; }

        // -----------------------------------
        //
        // Model generation
        //
        // -----------------------------------
        arith_factory *    m_factory;
        numeral            m_epsilon;
        void update_epsilon(const inf_numeral & l, const inf_numeral & u);
        void compute_epsilon();
        void refine_epsilon();

        void init_model(model_generator & m) override;
        model_value_proc * mk_value(enode * n, model_generator & mg) override;

        // -----------------------------------
        //
        // Model checker
        //
        // -----------------------------------
        bool get_value(enode * n, expr_ref & r) override;
        bool include_func_interp(func_decl* f) override;

        bool get_lower(enode* n, expr_ref& r);
        bool get_upper(enode* n, expr_ref& r);
        bool get_lower(enode* n, rational& r, bool &is_strict);
        bool get_upper(enode* n, rational& r, bool &is_strict);
        bool to_expr(inf_numeral const& val, bool is_int, expr_ref& r);


        // -----------------------------------
        //
        // Optimization
        //
        // -----------------------------------
        expr_ref mk_ge(generic_model_converter& fm, theory_var v, inf_numeral const& val);
        inf_eps_rational<inf_rational> maximize(theory_var v, expr_ref& blocker, bool& has_shared) override;
        inf_eps_rational<inf_rational> value(theory_var v) override;
        theory_var add_objective(app* term) override;
        void enable_record_conflict(expr* bound);
        void record_conflict(unsigned num_lits, literal const * lits, 
                          unsigned num_eqs, enode_pair const * eqs,
                          unsigned num_params, parameter* params);
        inf_eps_rational<inf_rational> conflict_minimize();


    private:
        virtual expr_ref mk_gt(theory_var v);

        bool_var m_bound_watch;
        inf_eps_rational<inf_rational> m_upper_bound;
        bool get_theory_vars(expr * n, uint_set & vars);
    public:
        // -----------------------------------
        //
        // Pretty Printing
        //
        // -----------------------------------
    public:
        void collect_statistics(::statistics & st) const override;
        void display(std::ostream & out) const override;
    protected:
        void display_row(std::ostream & out, unsigned r_id, bool compact = true) const;
        void display_row(std::ostream & out, row const & r, bool compact = true) const;
        void display_rows(std::ostream & out, bool compact = true) const;
        void display_row_info(std::ostream & out, unsigned r_id) const;
        void display_row_info(std::ostream & out, row const & r) const;
        bool is_one_minus_one_row(row const & r) const;
        void display_row_shape(std::ostream & out, row const & r) const;
        void display_rows_shape(std::ostream & out) const;
        void display_rows_stats(std::ostream & out) const;
        void display_rows_bignums(std::ostream & out) const;
        void display_simplified_row(std::ostream & out, row const & r) const;
        void display_var(std::ostream & out, theory_var v) const;
        void display_vars(std::ostream & out) const;
        void display_bound(std::ostream & out, bound * b, unsigned indent = 0) const;
        void display_atoms(std::ostream & out) const;
        void display_asserted_atoms(std::ostream & out) const;
        void display_atom(std::ostream & out, atom * a, bool show_sign) const;
        void display_bounds_in_smtlib(std::ostream & out) const;
        void display_bounds_in_smtlib() const;
        void display_nl_monomials(std::ostream & out) const;
        void display_coeff_exprs(std::ostream & out, buffer<coeff_expr> const & p) const;
        void display_interval(std::ostream& out, interval const& i);
        void display_deps(std::ostream& out, v_dependency* dep);

    protected:
        // -----------------------------------
        //
        // Debugging
        //
        // -----------------------------------
#ifdef Z3DEBUG
        bool check_vector_sizes() const;
        bool check_null_var_pos() const;
        bool has_var_kind(unsigned r_id, var_kind k) const;
        bool wf_row(unsigned r_id) const;
        bool wf_rows() const;
        bool wf_column(theory_var v) const;
        bool wf_columns() const;
        bool valid_assignment() const;
        bool valid_row_assignment() const;
        bool valid_row_assignment(row const & r) const;
        bool satisfy_bounds() const;
        bool satisfy_integrality() const;
#endif
    };
    
    class mi_ext {
    public:
        typedef rational     numeral;
        typedef inf_rational inf_numeral;
        inf_numeral   m_int_epsilon;
        inf_numeral   m_real_epsilon;
        numeral fractional_part(inf_numeral const& n) {
            SASSERT(n.is_rational());
            return n.get_rational() - floor(n);
        }
        static numeral fractional_part(numeral const & n) {
            return n - floor(n);
        }
        static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) {
            return inf_numeral(n, r);
        }
        static bool is_infinite(inf_numeral const& ) { return false; }
        mi_ext() : m_int_epsilon(rational(1)), m_real_epsilon(rational(0), true) {}
    };

    class i_ext {
    public:
        typedef rational     numeral;
        typedef rational inf_numeral;
        numeral m_int_epsilon;
        numeral m_real_epsilon;
        static numeral fractional_part(numeral const & n) {
            return n - floor(n);
        }
        static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) {
            UNREACHABLE();
            return inf_numeral(n);
        }
        static bool is_infinite(inf_numeral const& ) { return false; }

        i_ext() : m_int_epsilon(1), m_real_epsilon(1) {}
    };

    class si_ext {
    public:
        typedef s_integer  numeral;
        typedef s_integer  inf_numeral;
        numeral m_int_epsilon;
        numeral m_real_epsilon;
        static numeral fractional_part(inf_numeral const & n) {
            return n - floor(n);
        }
        static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) {
            UNREACHABLE();
            return inf_numeral(n);
        }
        static bool is_infinite(inf_numeral const& ) { return false; }

        si_ext(): m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(1)) {}
    };
    
    class smi_ext {
    public:
        typedef s_integer      numeral;
        typedef inf_s_integer  inf_numeral;
        inf_numeral m_int_epsilon;
        inf_numeral m_real_epsilon;
        static numeral fractional_part(const numeral & n) {
            UNREACHABLE();
            return numeral(0);
        }
        static numeral fractional_part(const inf_numeral & n) {
            UNREACHABLE();
            return numeral(0);
        }
        static inf_numeral mk_inf_numeral(numeral const& n, numeral const& i) {
            return inf_numeral(n, i);
        }
        static bool is_infinite(inf_numeral const& ) { return false; }

        smi_ext() : m_int_epsilon(s_integer(1)), m_real_epsilon(s_integer(0), true) {}
    };

    class inf_ext {
    public:
        typedef rational     numeral;
        typedef inf_eps_rational<inf_rational>      inf_numeral;
        inf_numeral   m_int_epsilon;
        inf_numeral   m_real_epsilon;
        numeral fractional_part(inf_numeral const& n) {
            SASSERT(n.is_rational());
            return n.get_rational() - floor(n);
        }
        static numeral fractional_part(numeral const & n) {
            return n - floor(n);
        }
        static inf_numeral mk_inf_numeral(numeral const & n, numeral const & r) {
            return inf_numeral(inf_rational(n, r));
        }
        static bool is_infinite(inf_numeral const& n) { 
            return !n.get_infinity().is_zero(); 
        }

        inf_ext() : m_int_epsilon(inf_rational(rational(1))), m_real_epsilon(inf_rational(rational(0), true)) {}
    };

    
    typedef theory_arith<mi_ext> theory_mi_arith;
    typedef theory_arith<i_ext> theory_i_arith;
    typedef smt::theory_arith<inf_ext> theory_inf_arith;
    // typedef theory_arith<si_ext> theory_si_arith;
    // typedef theory_arith<smi_ext> theory_smi_arith;

    
};