import unittest

import yices_api as yapi

from ctypes import (  c_int32, c_int64, pointer  )
#import gmpy2

class TestTerms(unittest.TestCase):

    def setUp(self):
        yapi.yices_init()

    def tearDown(self):
        yapi.yices_exit()

    def test_terms(self):
        true_ = yapi.yices_true()
        false_ = yapi.yices_false()
        bool_t = yapi.yices_bool_type()
        int_t = yapi.yices_int_type()
        unint_t = yapi.yices_new_uninterpreted_type()
        self.assertNotEqual(true_, false_)
        const1 = yapi.yices_constant(unint_t, 0)
        const2 = yapi.yices_new_uninterpreted_term(unint_t)
        bconst1 = yapi.yices_new_uninterpreted_term(bool_t)
        iconst1 = yapi.yices_new_uninterpreted_term(int_t)
        var1 = yapi.yices_new_variable(unint_t)
        bvar1 = yapi.yices_new_variable(bool_t)
        ivar1 = yapi.yices_new_variable(int_t)
        ivar2 = yapi.yices_new_variable(int_t)
        ivar3 = yapi.yices_new_variable(int_t)
        ivar4 = yapi.yices_new_variable(int_t)
        zero = yapi.yices_zero()
        int1 = yapi.yices_int32(13)
        int2 = yapi.yices_int32(17)
        self.assertEqual(zero, yapi.yices_int32(0))
        fun1_t = yapi.yices_function_type1(int_t, bool_t)
        fun1 = yapi.yices_new_variable(fun1_t)
        app1 = yapi.yices_application1(fun1, int1)
        fun2_t = yapi.yices_function_type2(int_t, int_t, bool_t)
        fun2 = yapi.yices_new_variable(fun2_t)
        app2 = yapi.yices_application2(fun2, int1, int1)
        fun3_t = yapi.yices_function_type3(int_t, int_t, int_t, bool_t)
        fun3 = yapi.yices_new_variable(fun3_t)
        app3 = yapi.yices_application3(fun3, int1, int1, int1)
        tup3_t = yapi.yices_tuple_type3(bool_t, int_t, unint_t)
        tupconst1 = yapi.yices_new_variable(tup3_t)
        ta4 = yapi.make_type_array([int_t, int_t, int_t, int_t])
        int4 = yapi.make_type_array([int1, int2, iconst1, ivar1])
        int4_2 = yapi.make_term_array([ivar1, ivar2, ivar3, ivar4])
        fun4_t = yapi.yices_function_type(4, ta4, bool_t)
        fun4 = yapi.yices_new_variable(fun4_t)
        app4 = yapi.yices_application(fun4, 4, int4)
        ite1 = yapi.yices_ite(bconst1, int1, int2)
        eq1 = yapi.yices_eq(int1, int1)
        neq1 = yapi.yices_neq(int1, int1)
        not1 = yapi.yices_not(false_)
        bool5 = yapi.make_term_array([false_, eq1, neq1, app4, false_])
        or1 = yapi.yices_or(5, bool5)
        and1 = yapi.yices_and(5, bool5)
        xor1 = yapi.yices_xor(5, bool5)
        or2 = yapi.yices_or2(or1, and1)
        and2 = yapi.yices_and2(or1, and1)
        xor2 = yapi.yices_xor2(or1, and1)
        or3 = yapi.yices_or3(or1, and1, or2)
        and3 = yapi.yices_and3(or1, and1, and2)
        xor3 = yapi.yices_xor3(or1, and1, xor2)
        iff1 = yapi.yices_iff(and1, or1)
        implies1 = yapi.yices_implies(and1, or1)
        tup1 = yapi.yices_tuple(4, int4)
        pair1 = yapi.yices_pair(eq1, xor2)
        triple1 = yapi.yices_triple(ite1, fun4, or3)
        select1 = yapi.yices_select(2, tup1)
        select2 = yapi.yices_select(2, tupconst1)
        tupup1 = yapi.yices_tuple_update(tup1, 2, int2)
        update1 = yapi.yices_update1(fun1, int1, false_)
        update2 = yapi.yices_update2(fun2, int1, int1, false_)
        update3 = yapi.yices_update3(fun3, int1, int1, int1, false_)
        update4 = yapi.yices_update(fun4, 4, int4, false_)
        distinct1 = yapi.yices_distinct(4, int4)
        var2 = yapi.yices_new_variable(unint_t)
        vareq = yapi.yices_eq(var1, var2)
        vars2 = yapi.make_term_array([var1, var2])
        forall1 = yapi.yices_forall(2, vars2, vareq)
        exists1 = yapi.yices_exists(2, vars2, vareq)
        lambda1 = yapi.yices_lambda(2, vars2, vareq)
        zero = yapi.yices_zero()
        int64_1 = yapi.yices_int64(42)
        rat32_1 = yapi.yices_rational32(13, 7)
        rat64_1 = yapi.yices_rational64(-47, 111)
        gmpz = yapi.yices_new_mpz(42)
        mpz1 = yapi.yices_mpz(gmpz)
        gmpq = yapi.yices_new_mpq(42, 77)
        mpq1 = yapi.yices_mpq(gmpq)
        rat1 = yapi.yices_parse_rational('-3/117')
        float1 = yapi.yices_parse_float('-3.117e-2')
        add1 = yapi.yices_add(int1, int1)
        sub1 = yapi.yices_sub(int1, zero)
        neg1 = yapi.yices_neg(int1)
        self.assertEqual(yapi.yices_neg(zero), zero)
        self.assertNotEqual(neg1, int1)
        mul1 = yapi.yices_mul(int1, int1)
        square1 = yapi.yices_square(int1)
        self.assertEqual(mul1, square1)
        power1 = yapi.yices_power(int1, 4)
        sum1 = yapi.yices_sum(4, int4)
        product1 = yapi.yices_product(4, int4)
        product2 = yapi.yices_product(4, int4_2)
        div1 = yapi.yices_division(int1, int1)
        idiv1 = yapi.yices_idiv(int1, int1)
        imod1 = yapi.yices_imod(int1, int1)
        divatom1 = yapi.yices_divides_atom(int1, int1)
        intatom1 = yapi.yices_is_int_atom(int1)
        abs1 = yapi.yices_abs(neg1)
        self.assertEqual(abs1, int1)
        floor1 = yapi.yices_floor(rat1)
        ceil1 = yapi.yices_ceil(rat1)
        poly32 = yapi.yices_poly_int32(4, yapi.make_int32_array([2, 3, 4, 5]), int4)
        poly64 = yapi.yices_poly_int64(4, yapi.make_int64_array([3, 4, 5, 6]), int4)
        polyrat32 = yapi.yices_poly_rational32(4, yapi.make_int32_array([2, 3, 4, 5]), yapi.make_int32_array([12, 13, 14, 15]), int4)
        polyrat64 = yapi.yices_poly_rational64(4, yapi.make_int64_array([2, 3, 4, 5]), yapi.make_int64_array([12, 13, 14, 15]), int4)
        areqatom1 = yapi.yices_arith_eq_atom(int1, zero)
        arneqatom1 = yapi.yices_arith_neq_atom(int1, zero)
        argeqatom1 = yapi.yices_arith_geq_atom(int1, zero)
        arleqatom1 = yapi.yices_arith_leq_atom(int1, zero)
        argtatom1 = yapi.yices_arith_gt_atom(int1, zero)
        arltatom1 = yapi.yices_arith_lt_atom(int1, zero)
        areq0atom1 = yapi.yices_arith_eq0_atom(int1)
        arneq0atom1 = yapi.yices_arith_neq0_atom(int1)
        argeq0atom1 = yapi.yices_arith_geq0_atom(int1)
        arleq0atom1 = yapi.yices_arith_leq0_atom(int1)
        argt0atom1 = yapi.yices_arith_gt0_atom(int1)
        arlt0atom1 = yapi.yices_arith_lt0_atom(int1)
        bv_t = yapi.yices_bv_type(8)
        bvconstu32_1 = yapi.yices_bvconst_uint32(8, 42)
        bvconstu64_1 = yapi.yices_bvconst_uint64(8, 42)
        bvconst32_1 = yapi.yices_bvconst_int32(8, 42)
        bvconst64_1 = yapi.yices_bvconst_int64(8, 42)
        bvconstzero_1 = yapi.yices_bvconst_zero(16)
        bvconstone_1 = yapi.yices_bvconst_one(16)
        bvconstminusone_1 = yapi.yices_bvconst_minus_one(32)
        bvconstarray1 = yapi.yices_bvconst_from_array(4, yapi.make_int32_array([1, 0, 1, 0]))
        bvvar1 = yapi.yices_new_variable(bv_t)
        bvvar2 = yapi.yices_new_variable(bv_t)
        bvvar3 = yapi.yices_new_variable(bv_t)
        bvvar4 = yapi.yices_new_variable(bv_t)
        bvbin1 = yapi.yices_parse_bvbin('100101')
        bvhex1 = yapi.yices_parse_bvhex('f0a1b3')
        bvadd1 = yapi.yices_bvadd(bvbin1, bvbin1)
        bvsub1 = yapi.yices_bvsub(bvbin1, bvbin1)
        bvneg1 = yapi.yices_bvneg(bvbin1)
        bvmul1 = yapi.yices_bvmul(bvbin1, bvbin1)
        bvsquare1 = yapi.yices_bvsquare(bvbin1)
        bvpower1 = yapi.yices_bvpower(bvbin1, 3)
        bvdiv1 = yapi.yices_bvdiv(bvbin1, bvbin1)
        bvrem1 = yapi.yices_bvrem(bvbin1, bvbin1)
        bvsdiv1 = yapi.yices_bvsdiv(bvbin1, bvbin1)
        bvsrem1 = yapi.yices_bvsrem(bvbin1, bvbin1)
        bvsmod1 = yapi.yices_bvsmod(bvbin1, bvbin1)
        bvnot1 = yapi.yices_bvnot(bvbin1)
        bvnand1 = yapi.yices_bvnand(bvbin1, bvbin1)
        bvnor1 = yapi.yices_bvnor(bvbin1, bvbin1)
        bvxnor1 = yapi.yices_bvxnor(bvbin1, bvbin1)
        bvshl1 = yapi.yices_bvshl(bvbin1, bvbin1)
        bvlshr1 = yapi.yices_bvlshr(bvbin1, bvbin1)
        bvashr1 = yapi.yices_bvashr(bvbin1, bvbin1)
        bvand1 = yapi.yices_bvand(4, yapi.make_term_array([bvbin1, bvbin1, bvbin1, bvbin1]))
        bvor1 = yapi.yices_bvor(4, yapi.make_term_array([bvbin1, bvbin1, bvbin1, bvbin1]))
        bvand2_1 = yapi.yices_bvand2(bvbin1, bvbin1)
        bvor2_1 = yapi.yices_bvor2(bvbin1, bvbin1)
        bvxor2_1 = yapi.yices_bvxor2(bvbin1, bvbin1)
        bvand3_1 = yapi.yices_bvand3(bvbin1, bvbin1, bvbin1)
        bvor3_1 = yapi.yices_bvor3(bvbin1, bvbin1, bvbin1)
        bvxor3_1 = yapi.yices_bvxor3(bvbin1, bvbin1, bvbin1)
        bvsum1 = yapi.yices_bvsum(4, yapi.make_term_array([bvbin1, bvbin1, bvbin1, bvbin1]))
        bvsum2 = yapi.yices_bvsum(4, yapi.make_term_array([bvvar1, bvvar2, bvvar3, bvvar4]))
        bvproduct1 = yapi.yices_bvproduct(4,yapi.make_term_array([bvbin1, bvbin1, bvbin1, bvbin1]))
        shleft0_1 = yapi.yices_shift_left0(bvbin1, 5)
        shleft1_1 = yapi.yices_shift_left1(bvbin1, 4)
        shright0_1 = yapi.yices_shift_right0(bvbin1, 3)
        shright1_1 = yapi.yices_shift_right1(bvbin1, 2)
        ashright_1 = yapi.yices_ashift_right(bvbin1, 1)
        rotleft_1 = yapi.yices_rotate_left(bvbin1, 6)
        rotright_1 = yapi.yices_rotate_right(bvbin1, 5)
        bvextract1 = yapi.yices_bvextract(bvbin1, 2, 4)
        bvconcat2_1 = yapi.yices_bvconcat2(bvbin1, bvbin1)
        bvconcat_1 = yapi.yices_bvconcat(4, yapi.make_term_array([bvbin1, bvbin1, bvbin1, bvbin1]))
        bvrepeat1 = yapi.yices_bvrepeat(bvbin1, 8)
        signext1 = yapi.yices_sign_extend(bvbin1, 3)
        zeroext1 = yapi.yices_zero_extend(bvbin1, 4)
        redand1 = yapi.yices_redand(bvbin1)
        redor1 = yapi.yices_redor(bvbin1)
        redcomp1 = yapi.yices_redcomp(bvbin1, bvbin1)
        bvarray1 = yapi.yices_bvarray(4, yapi.make_term_array([true_, false_, true_, false_]))
        bitextract1 = yapi.yices_bitextract(bvbin1, 3)
        bveqatom1 = yapi.yices_bveq_atom(bvbin1, bvbin1)
        bvneqatom1 = yapi.yices_bvneq_atom(bvbin1, bvbin1)
        bvgeatom1 = yapi.yices_bvge_atom(bvbin1, bvbin1)
        bvgtatom1 = yapi.yices_bvgt_atom(bvbin1, bvbin1)
        bvleatom1 = yapi.yices_bvle_atom(bvbin1, bvbin1)
        bvltatom1 = yapi.yices_bvlt_atom(bvbin1, bvbin1)
        bvsgeatom1 = yapi.yices_bvsge_atom(bvbin1, bvbin1)
        bvsgtatom1 = yapi.yices_bvsgt_atom(bvbin1, bvbin1)
        bvsleatom1 = yapi.yices_bvsle_atom(bvbin1, bvbin1)
        bvsltatom1 = yapi.yices_bvslt_atom(bvbin1, bvbin1)
        ptype1 = yapi.yices_parse_type('int')
        self.assertEqual(ptype1, yapi.yices_int_type())
        pterm1 = yapi.yices_parse_term('42')
        self.assertEqual(pterm1, yapi.yices_int32(42))
        subst1 = yapi.yices_subst_term(2,
                                  yapi.make_term_array([yapi.yices_new_variable(ptype1),yapi.yices_new_variable(ptype1)]),
                                  yapi.make_term_array([yapi.yices_int32(2), yapi.yices_int32(3)]),
                                  yapi.yices_int32(42))
        substarr1 = yapi.yices_subst_term_array(2,
                                           yapi.make_term_array([yapi.yices_new_variable(ptype1), yapi.yices_new_variable(ptype1)]),
                                           yapi.make_term_array([yapi.yices_int32(2), yapi.yices_int32(3)]),
                                           3,
                                           yapi.make_term_array([yapi.yices_int32(2), yapi.yices_int32(3), yapi.yices_int32(7)]))
        settypename1 = yapi.yices_set_type_name(ptype1, 'I')
        self.assertEqual(settypename1, 0)
        settermname1 = yapi.yices_set_term_name(pterm1, 'answer')
        self.assertEqual(settermname1, 0)
        gettype1 = yapi.yices_get_type_by_name('I')
        self.assertEqual(gettype1, ptype1)
        getterm1 = yapi.yices_get_term_by_name('answer')
        self.assertEqual(getterm1, pterm1)
        gettypename1 = yapi.yices_get_type_name(ptype1)
        self.assertEqual(gettypename1, 'I')
        gettermname1 = yapi.yices_get_term_name(pterm1)
        self.assertEqual(gettermname1, 'answer')
        yapi.yices_remove_type_name('I')
        yapi.yices_remove_term_name('answer')
        yapi.yices_clear_type_name(ptype1)
        yapi.yices_clear_term_name(pterm1)
        typeofterm1 = yapi.yices_type_of_term(pterm1)
        self.assertEqual(typeofterm1, yapi.yices_int_type())
        self.assertEqual(yapi.yices_term_is_bool(false_), 1)
        self.assertEqual(yapi.yices_term_is_bool(pterm1), 0)
        self.assertEqual(yapi.yices_term_is_int(false_), 0)
        self.assertEqual(yapi.yices_term_is_int(pterm1), 1)
        self.assertEqual(yapi.yices_term_is_real(false_), 0)
        self.assertEqual(yapi.yices_term_is_real(pterm1), 0)
        self.assertEqual(yapi.yices_term_is_arithmetic(false_), 0)
        self.assertEqual(yapi.yices_term_is_arithmetic(pterm1), 1)
        self.assertEqual(yapi.yices_term_is_bitvector(false_), 0)
        self.assertEqual(yapi.yices_term_is_bitvector(bvbin1), 1)
        self.assertEqual(yapi.yices_term_is_tuple(false_), 0)
        self.assertEqual(yapi.yices_term_is_tuple(tup1), 1)
        self.assertEqual(yapi.yices_term_is_function(false_), 0)
        self.assertEqual(yapi.yices_term_is_function(fun1), 1)
        self.assertEqual(yapi.yices_term_is_scalar(false_), 0)
        self.assertEqual(yapi.yices_term_is_scalar(fun1), 0)
        self.assertEqual(yapi.yices_term_bitsize(bvbin1), 6L)
        self.assertEqual(yapi.yices_term_is_ground(false_), 1)
        self.assertEqual(yapi.yices_term_is_ground(var1), 0)
        self.assertEqual(yapi.yices_term_is_atomic(false_), 1)
        # or1 is atomic because it simplifies to true
        self.assertEqual(yapi.yices_term_is_atomic(or1), 1)
        self.assertEqual(yapi.yices_term_is_composite(false_), 0)
        self.assertEqual(yapi.yices_term_is_composite(ite1), 1)
        self.assertEqual(yapi.yices_term_is_composite(tup1), 1)
        self.assertEqual(yapi.yices_term_is_projection(false_), 0)
        # Select1 simplifies
        self.assertEqual(yapi.yices_term_is_projection(select1), 0)
        self.assertEqual(yapi.yices_term_is_projection(select2), 1)
        self.assertEqual(yapi.yices_term_is_sum(ite1), 0)
        self.assertEqual(yapi.yices_term_is_sum(sum1), 1)
        self.assertEqual(yapi.yices_term_is_bvsum(select1), 0)
        # bvsum1 simplifies since the terms are all numbers
        self.assertEqual(yapi.yices_term_is_bvsum(bvsum1), 0)
        self.assertEqual(yapi.yices_term_is_bvsum(bvsum2), 1)
        self.assertEqual(yapi.yices_term_is_product(ite1), 0)
        self.assertEqual(yapi.yices_term_is_product(product1), 0)
        self.assertEqual(yapi.yices_term_is_product(product2), 1)
        self.assertEqual(yapi.yices_term_constructor(true_), 0L)
        self.assertEqual(yapi.yices_term_constructor(int1), 1L)
        self.assertEqual(yapi.yices_term_constructor(bvconst32_1), 2L)
        self.assertEqual(yapi.yices_term_num_children(bvconst32_1), 0L)
        self.assertEqual(yapi.yices_term_num_children(select2), 1L)
        self.assertEqual(yapi.yices_term_num_children(tup1), 4L)
        self.assertEqual(yapi.yices_term_child(tup1, 2), iconst1)
        projarg1 = yapi.yices_proj_arg(select2)
        self.assertEqual(yapi.yices_proj_index(select2), 2)
        self.assertEqual(yapi.yices_proj_arg(select2), tupconst1)
        val_p = yapi.make_empty_int32_array(1)
        self.assertEqual(yapi.yices_bool_const_value(true_, val_p), 0)
        self.assertEqual(val_p[0], 1)
        bval = yapi.make_empty_int32_array(8)
        self.assertEqual(yapi.yices_bv_const_value(bvconst32_1, bval), 0)
        self.assertEqual(bval[0:7], [0, 1, 0, 1, 0, 1, 0])
        scalar_t = yapi.yices_new_scalar_type(20)
        scalar_c = yapi.yices_constant(scalar_t, 13)
        self.assertEqual(yapi.yices_scalar_const_value(scalar_c, val_p), 0)
        self.assertEqual(val_p[0], 13)
        self.assertEqual(yapi.yices_rational_const_value(rat32_1, gmpq), 0)
        pterm = yapi.term_t()
        self.assertEqual(yapi.yices_sum_component(sum1, 2, gmpq, pterm), 0)
        val = yapi.make_empty_int32_array(8)
        self.assertEqual(yapi.yices_type_of_term(bvsum2), 13)  #BD: not very robust
        # val must be an array of eight integers since bvsum has type (bitvector 8)
        dodgy = yapi.yices_bvsum_component(bvsum2, 1, val, pterm)
        self.assertEqual(dodgy, 0)
        self.assertEqual(yapi.yices_term_is_bitvector(pterm), 1)
        self.assertEqual(yapi.yices_term_is_bitvector(bvvar2), 1)
        self.assertEqual(val[0], 1)
        self.assertEqual(val[1], 0)
        self.assertEqual(val[2], 0)
        self.assertEqual(val[3], 0)
        self.assertEqual(val[4], 0)
        self.assertEqual(val[5], 0)
        self.assertEqual(val[6], 0)
        self.assertEqual(val[7], 0)
        # Why is pterm.value needed here?
        self.assertEqual(pterm.value, bvvar2)
        exp1 = c_int32()
        self.assertEqual(yapi.yices_product_component(product2, 1, pterm, exp1), 0)
        self.assertEqual(exp1.value, 1)
        # Why is pterm.value needed here?
        self.assertEqual(pterm.value, ivar2)
        # Note that the next two can change easily
        self.assertEqual(yapi.yices_num_terms(), 103L)
        self.assertEqual(yapi.yices_num_types(), 26L)
        self.assertEqual(yapi.yices_incref_term(pterm), 0)
        self.assertEqual(yapi.yices_num_posref_terms(), 1)
        self.assertEqual(yapi.yices_decref_term(pterm), 0)
        self.assertEqual(yapi.yices_num_posref_terms(), 0)
        self.assertEqual(yapi.yices_incref_type(unint_t), 0)
        self.assertEqual(yapi.yices_num_posref_types(), 1)
        self.assertEqual(yapi.yices_decref_type(unint_t), 0)
        self.assertEqual(yapi.yices_num_posref_types(), 0)
        self.assertEqual(yapi.yices_incref_term(int1), 0)
        self.assertEqual(yapi.yices_incref_type(int_t), 0)
        yapi.yices_garbage_collect(int4, 1, ta4, 1, 0)
        self.assertEqual(yapi.yices_num_terms(), 5L)
        self.assertEqual(yapi.yices_num_types(), 3L)
