def mk_in_space_conditions(space,var_vector):
    # print "mk_in_space_conditions"
    # print space
    # print var_vector
    basis_matrix = space.matrix().transpose().apply_map(lambda x:QQbar(x).radical_expression())
    vec_len,coeff_count = basis_matrix.dimensions()
    coeffs = MX.mk_symbol_vector(coeff_count,'c');
    # print "mk_in_space_condition:", coeff_count, coeffs, vec_len
    # print basis_matrix
    #for i in range(coeff_count):
    #    assume(coeffs[0][i],'rational')
    if coeff_count == 0:
        # if the space is empty
        return ([0 != 0],coeffs.list(),var_vector.list())
    lhs1 = MX.linear_combination_of(coeffs,basis_matrix).list()
    lhs2 = var_vector.list()
    # print "lhs2",lhs2
    # print "lhs1",lhs1
    conds=map(lambda tup: tup[0] - tup[1] == 0,zip(lhs1,lhs2))
    return (conds,coeffs.list(),var_vector.list())
def termination_check(matrix_A,matrix_B_s,matrix_B_w,show_time):
    t=take_time.TakeTime()

    mA   = matrix_A
    mB_s = matrix_B_s
    mB_w = matrix_B_w

    zvec = MX.mk_symbol_vector(mA.dimensions()[0],"z").transpose()
    t.print_tdiff("0",show_time)
    #########################################################
    # 1. compute JNF
    (mD,mP) = mA.jordan_form(QQbar,transformation=true)
    mPi = mP.inverse()

    t.print_tdiff("1",show_time)
    #########################################################
    # 2. find factors porresponding to entries in A in BPDQ
    sB_dims=mB_s.dimensions()
    wB_dims=mB_w.dimensions()
    A_dims=mA.dimensions()
    sB=MX.mk_symbol_matrix(sB_dims[0],sB_dims[1],"bs")
    wB=MX.mk_symbol_matrix(wB_dims[0],wB_dims[1],"bw")
    P=MX.mk_symbol_matrix(A_dims[0],A_dims[1],"p")
    D=MX.mk_symbol_matrix(A_dims[0],A_dims[1],"a")  # entries of this matrix must be sorted first when expanding
                                    # to make the algorithm work. Hence it is called "a"
    Q=MX.mk_symbol_matrix(A_dims[0],A_dims[1],"q")

    sBPDQ=matrix([])
    if sB.dimensions()[0] > 0:
        sBPDQ=(sB*P*D*Q).expand()           # .expand() is the same as .apply_map(expand)
    wBPDQ=matrix([])
    if wB.dimensions()[0] > 0:
        wBPDQ=(wB*P*D*Q).expand()
    # a matrix entry now has the form (apdq + apdq + apdq + ...), where each
    # a,p,d,q corresponds to one entry of the original matrices.
    #
    # split into operators and operands
    soBPDQ=map(lambda r: map (lambda c: formula_to_operands(c),r), MX.matrix_to_list(sBPDQ))
    woBPDQ=map(lambda r: map (lambda c: formula_to_operands(c),r), MX.matrix_to_list(wBPDQ))
    # Now we have a list representation of our matrix where each entry is split
    # into a tuple (operator,list of arguments).
    # Here we want to replace the symbolic values of matrix A by some abstract
    # representation of the Jordan matrix D to the power of some natural number
    soBPdQ=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(D),MX.abstract_jordan_matrix_power(mD),soBPDQ)
    woBPdQ=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(D),MX.abstract_jordan_matrix_power(mD),woBPDQ)
    # combine parts by same absolute eigenvalues and direction for every entry.
    # an entry.
    snBPdQ=MX.map_lmatrix(group_entry_summands_by_eigenvalue,soBPdQ)
    wnBPdQ=MX.map_lmatrix(group_entry_summands_by_eigenvalue,woBPdQ)
    # Entries now have the form (abs(a_0)(dir(a_0)pdq+dir(a_1)pdq+...)
    # + abs(a_2)(dir(a_2)pdq+dir(a_3)pdq+...) + ...).
    # Next the variables b p q are replaced by their values.
    snbPdQ=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(sB),MX.matrix_to_list(mB_s),snBPdQ)
    snbpdQ=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(P),MX.matrix_to_list(mP),snbPdQ)
    snbpdq=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(Q),MX.matrix_to_list(mPi),snbpdQ)

    wnbPdQ=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(wB),MX.matrix_to_list(mB_w),wnBPdQ)
    wnbpdQ=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(P),MX.matrix_to_list(mP),wnbPdQ)
    wnbpdq=MX.replace_symbols_in_lmatrix(MX.matrix_to_list(Q),MX.matrix_to_list(mPi),wnbpdQ)

    t.print_tdiff("2",show_time)
    #########################################################
    # 3. build the index set and abstract conditions
    ind=sorted(list(set.union(abs_ev_index_set_from_abstract_lmatrix(snbpdq),
        abs_ev_index_set_from_abstract_lmatrix(wnbpdq))))
    s_abs_conds = mk_abstract_conds(snbpdq)
    w_abs_conds = mk_abstract_conds(wnbpdq)

    t.print_tdiff("3",show_time)
    #########################################################
    # 4. build index_z function  and conditions for each index and constraint
    index_z_k,index_cond_c_tuples = mk_index_z(s_abs_conds,w_abs_conds,ind,zvec)

    t.print_tdiff("4",show_time)
    #########################################################
    # 5. build positive eigenspace and constraints for vectors inside of it
    #pos_eigenspace = positive_eigenspace_of(mA)
    pos_eigenspace=positive_generalized_eigenspace_of(mA.change_ring(QQbar))
    in_space_conds = mk_in_space_conditions(pos_eigenspace,zvec)

    t.print_tdiff("5",show_time)
    #########################################################
    # 6. collect all variables and coefficients used
    c_vars=in_space_conds[1] # coefficients
    v_vars=in_space_conds[2] # variables (from zvec)
    allvars=in_space_conds[1]+in_space_conds[2]

    t.print_tdiff("6",show_time)
    #########################################################
    # 7. compute the maxial satisfying index for each of the k constraints
    max_indices = max_indices_cond(index_cond_c_tuples,zvec,in_space_conds)
    if max_indices == None:
        # when no index function can be found (= no nonterminating
        # susbspace is found = dimension of S_min is zero)
        t.print_tdiff("7.0",show_time)
        print "case 0: terminating"
        return "terminating"

    t.print_tdiff("7",show_time)
    #########################################################
    # 8. compute S_min
    S_min_conds=complex_space_conditions(s_abs_conds,w_abs_conds,ind,zvec,index_cond_c_tuples,max_indices,in_space_conds)
    S_min = SE.solution_to_space(S_min_conds[0][0],c_vars,v_vars)

    t.print_tdiff("8",show_time)
    #########################################################
    # 9. compute Q_min and R_min
    algebraic_base_extends = collect_QQ_extends(mD)
    Q_min=find_Q_min(S_min,algebraic_base_extends)
    R_min=VectorSpace(SR,Q_min.degree()).subspace_with_basis(Q_min.basis())

    t.print_tdiff("9",show_time)
    #########################################################
    # 10. decide termination
    if S_min.dimension() == Q_min.dimension():
        print "case 1: non-terminating"
        clean_up(allvars)
        t.print_tdiff("10.1",show_time)
        return "non-terminating"
    if Q_min.dimension() == 0:
        #########################################################
        # 10.2 test whether zero vector is non-terminating
        if terminates_on_zero(mB_s,mB_w):
            print "case 2: terminating"
            clean_up(allvars)
            t.print_tdiff("10.2",show_time)
            return "terminating"
        else:
            print "case 2: non-terminating"
            clean_up(allvars)
            t.print_tdiff("10.2",show_time)
            return "non-terminating"
    if 0 < Q_min.dimension() < S_min.dimension():
        #########################################################
        # 10.3 run termination_check onr educed program
        print "case 3: reduce"
        rA,valphas,lin_alpha=find_reduction_of_matrix(mA,Q_min)
        rB_s,rB_w=apply_reduction_on(mB_s, mB_w, valphas,lin_alpha)
        clean_up(allvars)
        t.print_tdiff("10.3",show_time)
        termination_check(rA,rB_s,rB_w)