def rate_cov_strate_sleeping_v3_uniform(k_matrix,alpha,rate_th1,lamda_u1,bw):
    # define the distribution of the activity
    first_int = (1/3)                                     # correspond to the integral in first term
    second_int = (1/2) - (1/3)                                            # correspond to the integral in second term
    #---------------------------- calibrated -----------------------------------------
    expected_activity = (1/2)                             # expected value of a      # this changes for optimization hance should be calibratable
    expected_strategic_function = (1/2)                           # expected value of s
    #---------------------------- calibrated -----------------------------------------
    noise_var = 1
    # preprocessing - define empty elements and get information about the inputs
    k_mat = k_matrix.copy()
    num_tiers = k_mat.shape[0]
    density_org = k_mat[:,2].copy()
    power = gb.db2pow(k_mat[:,1])
    density_update = np.array(density_org*([1]*(num_tiers-1)+[expected_strategic_function]))
    # define necessary values
    area_org = np.zeros(num_tiers,float)                      # original association probability
    area_sc_update = np.zeros(num_tiers,float)                # association probability of disconnected cell
    N_k_u1 = np.zeros(num_tiers,float)                        #number of users in tier K BS
    N_k_sc = np.zeros(num_tiers,float)                        #number of users in tier K BS
    N_k_total = np.zeros(num_tiers,float)
    threshold_u1 = np.zeros(num_tiers,float)                  #threshold for users in tier K BS
    t_func_main = np.zeros(num_tiers,float)
    t_func_sc = np.zeros(num_tiers,float)
    for i in range(num_tiers):
        area_org[i] = A_k(density_org,power,alpha,i)
        area_sc_update[i] = A_k(density_update,power,alpha,i)
    N_k_u1 = 1 + 1.28*lamda_u1*(area_org/density_org)
    N_k_sc = expected_activity*(1-expected_strategic_function)*density_org[-1]*N_k_u1[-1]*(area_sc_update/density_update)   #for binary optimization
    N_k_total = N_k_u1 + N_k_sc
    threshold_u1 = 2**((rate_th1/bw)*N_k_total) -1
    for i in range(num_tiers):
        first_exp_term = -(threshold_u1[i]*noise_var/power[i])
        z_term = 0 if (threshold_u1[i]==0) else (threshold_u1[i])**(2/alpha) *mp.quad(lambda u: 1/ (1+u**(alpha/2)),[(1/threshold_u1[i])**(2/alpha),mp.inf])
        second_exp_term = -mp.pi*z_term* sum(density_update*(power/power[i])**(2/alpha))
        third_exp_term_main = -mp.pi * sum(density_org*(power/power[i])**(2/alpha))
        third_exp_term_sc = -mp.pi * sum(density_update*(power/power[i])**(2/alpha))
        t_func_main[i] = mp.quad(lambda y: y * mp.exp(first_exp_term*y**alpha) * mp.exp(second_exp_term*y**2) * mp.exp(third_exp_term_main*y**2),[0,mp.inf])
        t_func_sc[i] = mp.quad(lambda y: y* mp.exp(first_exp_term*y**alpha) * mp.exp(second_exp_term*y**2) * mp.exp(third_exp_term_sc*y**2), [0,mp.inf])
    temp_second_sum = sum(2*mp.pi*density_update*t_func_sc)
    temp_third_sum = sum(2*mp.pi*density_org[0:-1]*t_func_main[0:-1])
    #rate_coverage = (2*mp.pi*density_org[-1]/expected_activity)*(t_func_main[-1]*first_int + temp_second_sum*second_int) + temp_third_sum
    rate_coverage = (area_org[-1]/expected_activity)*((2*mp.pi*density_org[-1]/area_org[-1])*t_func_main[-1]*first_int + temp_second_sum*second_int) + temp_third_sum
    #print((sum(2*mp.pi*density_update*t_func_sc)*second_int+t_func_main[-1]*first_int)*(2*mp.pi*density_org[-1]))
    #print(t_func_main[-1]*first_int)
    #print(temp_second_sum*second_int)
    #print((2*mp.pi*density_org[-1]/expected_activity)*(t_func_main[-1]*first_int) + temp_third_sum)
    #print(temp_second_sum)
    return (rate_coverage)
def rate_cov_random_switching_v2(k_matrix, alpha, rate_th, lamda_user, q_on,
                                 bw):
    k_mat = k_matrix.copy()
    num_tiers = k_mat.shape[0]
    #density = k_mat[:,2]
    #density[-1] = q_on
    density = np.array(
        k_mat[:, 2] *
        ([1] * (num_tiers - 1) +
         [q_on]))  #hence last row always corresponds to small cells
    #print(density)
    power = gb.db2pow(k_mat[:, 1])
    small_cell_idx = num_tiers - 1  #indicates the index of the small cell
    #density[small_cell_idx] = q_on
    # initialize the integration result matrix
    tier_integ_result = np.zeros(num_tiers,
                                 float)  #integration results of each tier
    area_tiers = np.zeros(num_tiers, float)  #area of the tiers
    threshold_tier = np.zeros(num_tiers, float)  #threshold of the tiers
    N_k = np.zeros(num_tiers, float)  #number of users in each tier
    first_exp_term = np.zeros(num_tiers,
                              float)  #first exponential term in integral
    second_exp_term = np.zeros(num_tiers,
                               float)  #second exponential term in integral
    third_exp_term = np.zeros(num_tiers,
                              float)  #third exponential term in integral
    for i in range(num_tiers):
        area_tiers[i] = A_k(density, power, alpha, i)
        #N_k[i] = 0 if density[i]==0 else  1.28*lamda_user*area_tiers[i]/density[i]
        N_k[i] = 0 if density[
            i] == 0 else 1 + 1.28 * lamda_user * area_tiers[i] / density[i]
        threshold_tier[i] = mp.inf if (
            bw == 0) else 2**(rate_th * N_k[i] / bw) - 1
        first_exp_term[i] = threshold_tier[i] * 1 / power[i]
        third_exp_term[i] = mp.pi * sum(density *
                                        (power / power[i])**(2 / alpha))
        Z_term = 0 if (threshold_tier[i] == 0) else threshold_tier[i]**(
            2 / alpha) * mp.quad(lambda u: 1 / (1 + u**(alpha / 2)),
                                 [(threshold_tier[i])**(-2 / alpha), mp.inf])
        second_exp_term[i] = third_exp_term[i] * Z_term
    for k in range(num_tiers):
        tier_integ_result[k] = mp.quad(
            lambda y: y * mp.exp(-first_exp_term[k] * y**alpha) * mp.exp(
                -second_exp_term[k] * y**2) * mp.exp(-third_exp_term[k] * y**2
                                                     ), [0, mp.inf])
    rate_cov_prob = 2 * mp.pi * sum(density * tier_integ_result)
    return (rate_cov_prob)
def rate_cov_random_switching(k_matrix, alpha, rate_th, lamda_user):
    k_mat = k_matrix.copy()
    num_tiers = k_mat.shape[0]
    density = k_mat[:, 2]
    power = gb.db2pow(k_mat[:, 1])
    bandwidth = k_mat[:, 3]
    small_cell_idx = num_tiers - 1  #indicates the index of the small cell
    # initialize the integration result matrix
    tier_integ_result = np.zeros(num_tiers,
                                 float)  #integration results of each tier
    area_tiers = np.zeros(num_tiers, float)  #area of the tiers
    threshold_tier = np.zeros(num_tiers, float)  #threshold of the tiers
    N_k = np.zeros(num_tiers, float)  #number of users in each tier
    first_exp_term = np.zeros(num_tiers,
                              float)  #first exponential term in integral
    second_exp_term = np.zeros(num_tiers,
                               float)  #second exponential term in integral
    third_exp_term = np.zeros(num_tiers,
                              float)  #third exponential term in integral
    for i in range(num_tiers):
        area_tiers[i] = A_k(density, power, alpha, i)
        N_k[i] = 0 if density[
            i] == 0 else 1.28 * lamda_user * area_tiers[i] / density[i]
        threshold_tier[i] = 2**(rate_th * N_k[i] / bandwidth[i]) - 1
        first_exp_term[i] = threshold_tier[i] * 1 / power[i]
        third_exp_term[i] = mp.pi * sum(density *
                                        (power / power[i])**(2 / alpha))
        Z_term = 0 if (threshold_tier[i] == 0) else threshold_tier[i]**(
            2 / alpha) * mp.quad(lambda u: 1 / (1 + u**(alpha / 2)), [
                (1 / threshold_tier[i])**(2 / alpha), mp.inf
            ])
        second_exp_term[i] = third_exp_term[i] * Z_term
    for k in range(num_tiers):
        tier_integ_result[k] = mp.quad(
            lambda y: y * mp.exp(-first_exp_term[k] * y**alpha) * mp.exp(
                -second_exp_term[k] * y**2) * mp.exp(-third_exp_term[k] * y**2
                                                     ), [0, mp.inf])
    rate_cov_prob = 2 * mp.pi * sum(density * tier_integ_result)
    #print(rate_cov_prob)
    return (rate_cov_prob)
def rate_coverage_meanload(mk_matrix, lamda_u, rate_threshold, bw):
    mk_mat = mk_matrix.copy()
    mk_mat_size = mk_mat.shape
    mk_mat[:, 3] = gb.dbm2pow(mk_mat[:, 3])  # converts power from dBm to W
    mk_mat[:, 6] = gb.db2pow(mk_mat[:, 6])  # coverts Bias from dB to number
    mk_OpenCell_RowIdx = (np.asarray(np.where(
        mk_mat[:,
               2] == True))).flatten()  # gives row index of open-access (m,k)
    area_opencells = np.zeros(mk_mat_size[0],
                              float)  # initialize area of open cells to zero
    Nij = np.zeros(mk_mat_size[0],
                   float)  #initialize the E[N_ij] value to zero
    Gij_mk_array = [[] for i in range(mk_mat_size[0])
                    ]  #Gij(m,k) polynomial coefficient matrix
    alpha_norm_mk_array = [[] for i in range(mk_mat_size[0])]
    Tmk = mk_mat[:, 3] * mk_mat[:, 6]  #Association weight
    Tmk_OpenCell = Tmk[
        mk_OpenCell_RowIdx]  #Association weight for open access cells
    temp_integral_result = np.zeros(mk_mat_size[0], float)
    #rate_coverage = np.zeros_like(rate_threshold,float)   #output array
    for i in mk_OpenCell_RowIdx:  # loop to calculate Area, Nij and Gij(m,k) of open-access cells
        Tmk_norm = Tmk_OpenCell / Tmk[i]
        alpha_norm = mk_mat[mk_OpenCell_RowIdx, 5] / mk_mat[i, 5]
        Gij_mk = mk_mat[mk_OpenCell_RowIdx, 4] * (
            Tmk_norm**(2 / mk_mat[mk_OpenCell_RowIdx, 5])
        )  # Eq-7 is computed with (i,j) corresponding to ith row index
        Gij_mk_array[i] = Gij_mk
        alpha_norm_mk_array[i] = alpha_norm
        area_opencells[i] = A_ij(mk_mat[i, 4], Gij_mk, alpha_norm)
        Nij[i] = (1.28 * lamda_u * area_opencells[i] / mk_mat[i, 4]) + 1
        #for rate_idx in range(len(rate_threshold)):
        for i in mk_OpenCell_RowIdx:
            Dij_inp = np.array([np.zeros(5, float) for tier in range(3)
                                ])  #initialize matrix for Dij(m,k) function
            tier_RowIndx = (np.asarray(np.where(mk_mat[:, 0] == mk_mat[i, 0]))
                            ).flatten()  #index of tiers of ith rows RAT system
            for x in tier_RowIndx:  #fill Dij(m,k) input matrix
                k = mk_mat[x, 1]  # gives the Kth tier of Mth RAT system
                Dij_inp[k - 1, 0] = mk_mat[x, 3] / mk_mat[i, 3]
                Dij_inp[k - 1, 1] = Tmk[x] / Tmk[i]
                Dij_inp[k - 1, 2] = mk_mat[x, 5]
                if (mk_mat[x, 2]):
                    Dij_inp[k - 1, 3] = mk_mat[x, 4]
                else:
                    Dij_inp[k - 1, 4] = mk_mat[x, 4]
            Dij_poly = D_ij(Dij_inp, t_x((rate_threshold / bw) * Nij[i]))
            alpha_norm_dij = Dij_inp[:, 2] / mk_mat[i, 5]
            alpha_norm_dij[alpha_norm_dij ==
                           0] = 1  #to avoid divide by zero warning :):):)
            f1 = lambda y: sum(Dij_poly * (y**(2 / alpha_norm_dij)))
            f2 = lambda y: sum(
                np.asarray(Gij_mk_array[i]) *
                (y**(2 / np.asarray(alpha_norm_mk_array[i]))))
            f3 = lambda y: (t_x((rate_threshold / bw) * Nij[i]) * y**
                            (mk_mat[i, 5])) / mk_mat[i, 3]
            f4 = lambda y: -f3(y) - mp.pi * f2(y) - mp.pi * f1(y)
            temp_integral_result[i] = mp.quad(lambda y: y * mp.exp(f4(y)),
                                              [0, mp.inf])
        rate_coverage = (sum(
            (2 * np.pi * mk_mat[:, 4]) * temp_integral_result))
    return (rate_coverage)
def rate_coverage(mk_matrix, lamda_u, rate_threshold):
    mk_mat = mk_matrix.copy()
    mk_mat_size = mk_mat.shape
    mk_mat[:, 3] = gb.dbm2pow(mk_mat[:, 3])  # converts power from dBm to W
    mk_mat[:, 6] = gb.db2pow(mk_mat[:, 6])  # coverts Bias from dB to number
    mk_OpenCell_RowIdx = (np.asarray(np.where(
        mk_mat[:,
               2] == True))).flatten()  # gives row index of open-access (m,k)
    area_opencells = np.zeros(mk_mat_size[0],
                              float)  # initialize area of open cells to zero
    Gij_mk_array = [[] for i in range(mk_mat_size[0])
                    ]  #Gij(m,k) polynomial coefficient matrix
    alpha_norm_mk_array = [[] for i in range(mk_mat_size[0])]
    Tmk = mk_mat[:, 3] * mk_mat[:, 6]  #Association weight
    Tmk_OpenCell = Tmk[
        mk_OpenCell_RowIdx]  #Association weight for open access cells
    temp_sum_integral_result = np.zeros(mk_mat_size[0], float)
    rate_coverage = np.zeros_like(rate_threshold, float)  #output array
    N_max = 4 * lamda_u
    for i in mk_OpenCell_RowIdx:  # loop to calculate Area, Nij and Gij(m,k) of open-access cells
        Tmk_norm = Tmk_OpenCell / Tmk[i]
        alpha_norm = mk_mat[mk_OpenCell_RowIdx, 5] / mk_mat[i, 5]
        Gij_mk = mk_mat[mk_OpenCell_RowIdx, 4] * (
            Tmk_norm**(2 / mk_mat[mk_OpenCell_RowIdx, 5])
        )  # Eq-7 is computed with (i,j) corresponding to ith row index
        Gij_mk_array[i] = Gij_mk
        alpha_norm_mk_array[i] = alpha_norm
        area_opencells[i] = A_ij(mk_mat[i, 4], Gij_mk, alpha_norm)
    for rate_idx in range(len(rate_threshold)):
        for i in mk_OpenCell_RowIdx:
            f1_temp = lamda_u * area_opencells[i] / mk_mat[i, 4]
            f1 = lambda n: ((3.5**3.5) / mp.fac(n)) * (mp.gamma(
                n + 4.5) / 3.32335097044784) * (f1_temp**n) * (
                    3.5 + f1_temp)**(
                        -n - 4.5)  #computation of sum terms in equation 20
            temp_sum = np.zeros(N_max + 1, float)
            temp_integral = np.zeros(N_max + 1, float)
            f2 = lambda y: sum(
                np.asarray(Gij_mk_array[i]) * (y**(2 / np.asarray(
                    alpha_norm_mk_array[i]))))  #Gij(m,k) term in eq 20
            Dij_inp = np.array([np.zeros(5, float) for tier in range(3)
                                ])  #initialize matrix for Dij(m,k) function
            #alpha_norm_dij = Dij_inp[:,2]/mk_mat[i,5]
            #alpha_norm_dij[alpha_norm_dij==0]=1  #to avoid divide by zero warning :):):)
            tier_RowIndx = (np.asarray(np.where(mk_mat[:, 0] == mk_mat[i, 0]))
                            ).flatten()  #index of tiers of ith rows RAT system
            for x in tier_RowIndx:  #fill Dij(m,k) input matrix
                k = mk_mat[x, 1]  # gives the Kth tier of Mth RAT system
                Dij_inp[k - 1, 0] = mk_mat[x, 3] / mk_mat[i, 3]
                Dij_inp[k - 1, 1] = Tmk[x] / Tmk[i]
                Dij_inp[k - 1, 2] = mk_mat[x, 5]
                if (mk_mat[x, 2]):
                    Dij_inp[k - 1, 3] = mk_mat[x, 4]
                else:
                    Dij_inp[k - 1, 4] = mk_mat[x, 4]
            for n in range(0, N_max + 1):
                temp_sum[n] = f1(n)
                sinr = (rate_threshold[rate_idx] / mk_mat[i, 7]) * (n + 1)
                #print(sinr)
                Dij_poly = D_ij(
                    Dij_inp,
                    t_x((rate_threshold[rate_idx] / mk_mat[i, 7]) * (n + 1)))
                alpha_norm_dij = Dij_inp[:, 2] / mk_mat[i, 5]
                alpha_norm_dij[alpha_norm_dij ==
                               0] = 1  #to avoid divide by zero warning :):):)
                f3 = lambda y: sum(Dij_poly * (y**(2 / alpha_norm_dij))
                                   )  #Dij term in integration
                f4 = lambda y: (t_x(
                    (rate_threshold[rate_idx] / mk_mat[i, 7]) *
                    (n + 1)) * y**(mk_mat[i, 5])) / mk_mat[i, 3]
                f5 = lambda y: -f4(y) - mp.pi * f3(y) - mp.pi * f2(y)
                temp_integral[n] = mp.quad(lambda y: y * mp.exp(f5(y)),
                                           [0, mp.inf])
            temp_sum_integral_result[i] = np.dot(temp_sum, temp_integral)
        rate_coverage[rate_idx] = (sum(
            (2 * np.pi * mk_mat[:, 4]) * temp_sum_integral_result))
    return (rate_coverage, rate_threshold)
def rate_cov_two_rat_two_com(mk_matrix,alpha,lamda_u1,lamda_u2,rate_th1,rate_th2):
    mk_mat = mk_matrix.copy()
    mk_mat_size = mk_mat.shape
    mk_mat[:,1] = gb.dbm2pow(mk_mat[:,1])
    mk_mat[:,4] = gb.db2pow(mk_mat[:,4])       # bias for C1 users
    mk_mat[:,5] = gb.db2pow(mk_mat[:,5])       # bias for C2 users
    density = mk_mat[:,2].copy()
    power = mk_mat[:,1].copy()
    bias_c1 = mk_mat[:,4].copy()
    bias_c2 = mk_mat[:,5].copy()
    # initialize the needed values
    area_c1 = np.zeros(mk_mat_size[0])         # association probability for community1 users
    area_c2 = np.zeros(mk_mat_size[0])         # association probability for community2 users
    N_ij_c1 = np.zeros(mk_mat_size[0])         # number of users in for C1 users
    N_ij_c2 = np.zeros(mk_mat_size[0])         # number of users in for C2 users
    threshold_c1 = np.zeros(mk_mat_size[0])    # threshold for C1 users
    threshold_c2 = np.zeros(mk_mat_size[0])    # threshold for C2 users
    bw_c1 = np.zeros(mk_mat_size[0])           # bandwidth for C1 users
    bw_c2 = np.zeros(mk_mat_size[0])           # bandwidth for C2 users
    kappa = np.zeros(mk_mat_size[0])           # fraction of BW for C1 users
    rate_cov_c1 = np.zeros(mk_mat_size[0])     # rate coverage for C1 users
    rate_cov_c2 = np.zeros(mk_mat_size[0])     # rate coverage for C2 users
    temp_sum_c1 = mk_mat[:,2] * (mk_mat[:,1]* mk_mat[:,4])**(2/alpha)        #temporary sum for denominator of G function of C1
    temp_sum_c2 = mk_mat[:,2] * (mk_mat[:,1]* mk_mat[:,5])**(2/alpha)        #temporary sum for denominator of G function of C2
    #fill the values in arrays
    for i in range(mk_mat_size[0]):
        area_c1[i] = temp_sum_c1[i]/(sum(temp_sum_c1))                # area of C1
        area_c2[i] = temp_sum_c2[i]/(sum(temp_sum_c2))                # area of C2
        #kappa[i] = (lamda_u1*rate_th1*area_c1[i])/((lamda_u1*rate_th1*area_c1[i])+(lamda_u2*rate_th2*area_c2[i]))
    N_ij_c1 = 1 + 1.28*lamda_u1*(area_c1/mk_mat[:,2])
    N_ij_c2 = 1 + 1.28*lamda_u2*(area_c2/mk_mat[:,2])
    #N_ij_c1 =  1 + lamda_u1*(area_c1/mk_mat[:,2])
    #N_ij_c2 = 1 + lamda_u2*(area_c2/mk_mat[:,2])
    kappa = 0 if (rate_th1==0 and rate_th2==0) else (lamda_u1*rate_th1*area_c1)/((lamda_u1*rate_th1*area_c1)+(lamda_u2*rate_th2*area_c2))
    #kappa = np.array([0,0])
    bw_c1 = kappa * mk_mat[:,3]
    bw_c2 = (1-kappa) * mk_mat[:,3]
    for i in range(mk_mat_size[0]):
        threshold_c1[i] = 0 if (bw_c1[i]==0) else 2**((rate_th1*N_ij_c1[i])/(bw_c1[i])) - 1
        threshold_c2[i] = 0 if (bw_c2[i]==0) else 2**((rate_th2*N_ij_c2[i])/(bw_c2[i])) - 1
    #threshold_c1 = 2**((rate_th1*N_ij_c1)/(bw_c1)) - 1
    #threshold_c2 = 2**((rate_th2*N_ij_c2)/(bw_c2)) - 1
    # finding rate coverage for community 1 users
    for i in range(mk_mat_size[0]):
        z_func_c1 = 0 if (threshold_c1[i]==0) else (threshold_c1[i])**(2/alpha) * mp.quad(lambda u: (1/(1+u**(alpha/2))), [(1/threshold_c1[i])**(2/alpha), mp.inf])
        sec_term_denom_c1 = sum(temp_sum_c1)/((power[i]*bias_c1[i])**(2/alpha))
        rate_cov_c1[i] = density[i]/(density[i]*z_func_c1 + sec_term_denom_c1)
        z_func_c2 = 0 if (threshold_c2[i]==0) else (threshold_c2[i])**(2/alpha) * mp.quad(lambda u: (1/(1+u**(alpha/2))), [(1/threshold_c2[i])**(2/alpha), mp.inf])
        sec_term_denom_c2 = sum(temp_sum_c2)/((power[i]*bias_c2[i])**(2/alpha))
        rate_cov_c2[i] = density[i]/(density[i]*z_func_c2 + sec_term_denom_c2)
    total_rate_cov = (lamda_u1/(lamda_u1+lamda_u2)) * (sum(rate_cov_c1)) + (lamda_u2/(lamda_u1+lamda_u2)) * (sum(rate_cov_c2))
    ase = (lamda_u1*rate_th1*sum(rate_cov_c1) + lamda_u2*rate_th2*sum(rate_cov_c2))/(10*10**6)
    #print(threshold_c1)
    #print(threshold_c2)
    #print(rate_cov_c1,sum(rate_cov_c1))
    #print(rate_cov_c2,sum(rate_cov_c2))
    #print(kappa)
    #print(rate_cov_c1,sum(rate_cov_c1))
    #print(rate_cov_c2,sum(rate_cov_c2))
    #print(ase)
    #print(N_ij_c1)
    #print(N_ij_c2)
    return (total_rate_cov)