# Contains the following functions:
#
# Parameter calculation of r_A and r_P
#
#    * rp_parameter
#    * ra_parameter
#
# Coevolution with seperated timescales (algorithm 1, relatively fast, Fig 2-5)
#
#    * starting_value
#    * plant_evolution
#    * pollinator_evolution
#    * coevolution_seperate
#
# Coevolution with overlapping timescales (algorithm 2, relatively slow, but more realistic)
#
#    * plant_pollinator_dynamics (population dynamics with flexible number of species (Fig E1)
#    * plant_pollinator_saturate (same population dynamics but with saturating functional response (Fig E2)
#    * coevolution_overlap


#packages:
from scipy import integrate as integ
import numpy as np



########################################################################################################################
###### r_A and r_P calculation ######-------------------------------------------------------------------------------
########################################################################################################################

def rp_parameter(alpha,tof_parameter):
    alpha_max, s_a, beta_max, s_b = tof_parameter
    return (1-(alpha/alpha_max)**s_a)**(1/s_a)

#-------------------------------------------------------------------------------------------------------------------
def ra_parameter(beta,tof_parameter):
    alpha_max, s_a, beta_max, s_b = tof_parameter
    return (1-(beta/beta_max)**s_b)**(1/s_b)



########################################################################################################################
###### Co-evolution of plant and pollinator (seperate timescales) ######------------------------------------------
########################################################################################################################


def starting_values(alpha,beta,eco_parameter,tof_parameter,evo_parameter,env_parameter):

    # unpack paramter sets
    c_P, c_A, gamma_P, gamma_A    = eco_parameter
    mut_step_a, mut_step_b, x, x  = evo_parameter

    # calculate optimal alpha and beta value and starting population for parameter setting

    max_num_mut = 5000
    count_mut   = 0
    alpha_m     = alpha + mut_step_a * np.random.normal(0,1/2.5758) # initial mutant strategy
    beta_m      =  beta + mut_step_b * np.random.normal(0,1/2.5758) # initial mutant strategy
    X0          = [1.2252774066850036,1.2252774066850036] # starting population

    while (count_mut < max_num_mut): # Do the following in case the maximum number of mutations is not yet reached

        r_P = rp_parameter(alpha,tof_parameter)
        r_A = ra_parameter( beta,tof_parameter)  # calculate new r_A value for next time point

        if (count_mut % 2): # odd numbers --> new plant mutant
            alpha, alpha_m = plant_evolution(alpha,alpha_m,beta,eco_parameter,tof_parameter,evo_parameter,env_parameter)
        else: # even numbers --> new pollinator mutant
            beta, beta_m = pollinator_evolution(beta,beta_m,alpha,eco_parameter,tof_parameter,evo_parameter,env_parameter)

        count_mut = count_mut + 1

    P0      = (alpha*beta*gamma_P*r_A+r_P*c_A)/(c_A*c_P-alpha**2*beta**2*gamma_A*gamma_P)
    A0      = (alpha*beta*gamma_A*r_P+r_A*c_P)/(c_A*c_P-alpha**2*beta**2*gamma_A*gamma_P)
    X0      = [P0,A0] # using the end population value for optimal strategies

    return([alpha,beta,X0])

#-------------------------------------------------------------------------------------------------------------------


# function for evolution of the plant
def plant_evolution(alpha_r,alpha_m,beta_r,eco_parameter,tof_parameter,evo_parameter,env_parameter):

    # unpack paramter sets
    c_P, c_A, gamma_P, gamma_A    = eco_parameter
    mut_step_a, mut_step_b, x, x  = evo_parameter
    env_change, r_A_change, time  = env_parameter
    alpha_max, s_a, beta_max, s_b = tof_parameter

    # calculate growth rates with given alpha and beta values
    r_P_r = rp_parameter(alpha_r,tof_parameter)
    r_P_m = rp_parameter(alpha_m,tof_parameter)

    # check if environment is degenerating, if yes, than adjust the r_A value
    r_A_r = ra_parameter(beta_r,tof_parameter) - time * r_A_change * env_change

    # determine equilibrium densities for resident plants and pollinatorss
    P_r_star = (alpha_r*beta_r*gamma_P*r_A_r+r_P_r*c_A)/(c_A*c_P-alpha_r**2*beta_r**2*gamma_A*gamma_P)
    A_r_star = (alpha_r*beta_r*gamma_A*r_P_r+r_A_r*c_P)/(c_A*c_P-alpha_r**2*beta_r**2*gamma_A*gamma_P)

    # Assume a very small density for the mutant population density
    # and calculate the plant mutant's fitness (= per-capita growth rate)
    P_m = 0.000001
    fitness_P = r_P_m - c_P * P_m - c_P * P_r_star + alpha_m * beta_r * gamma_P * A_r_star

    # check if plant mutant's fitness is positiv (+)or negativ (-) -> replace resident strategie (+) or keep resident strategie (-)
    if fitness_P > 0:

        alpha_r = alpha_m    # mutant becomes new resident
        alpha_m = alpha_r + mut_step_a*np.random.normal(0,1/2.5758)  # new mutant strategy
        alpha_m = min(max(0,alpha_m),alpha_max) # Allow only traits in reasonable range

    if fitness_P <= 0:

        alpha_m = alpha_r + mut_step_a*np.random.normal(0,1/2.5758) # new mutant strategy
        alpha_m = min(max(0,alpha_m),alpha_max) # Allow only traits in reasonable range


    return [alpha_r,alpha_m]

#-------------------------------------------------------------------------------------------------------------------

# function for evolution of the pollinator
def pollinator_evolution(beta_r,beta_m,alpha_r,eco_parameter,tof_parameter,evo_parameter,env_parameter):

    # unpack paramter sets
    c_P, c_A, gamma_P, gamma_A    = eco_parameter
    mut_step_a, mut_step_b, x, x  = evo_parameter
    env_change, r_A_change, time  = env_parameter
    alpha_max, s_a, beta_max, s_b = tof_parameter

    # calculate growth rates with given alpha and beta values
    r_P_r = rp_parameter(alpha_r,tof_parameter)

    # check if environment is degenerating, if yes, than adjust the r_A value
    r_A_r = ra_parameter(beta_r,tof_parameter) - time * r_A_change * env_change
    r_A_m = ra_parameter(beta_m,tof_parameter) - time * r_A_change * env_change

    # determine equilibrium densities for resident plants and pollinators
    P_r_star = (alpha_r*beta_r*gamma_P*r_A_r+r_P_r*c_A)/(c_A*c_P-alpha_r**2*beta_r**2*gamma_A*gamma_P)
    A_r_star = (alpha_r*beta_r*gamma_A*r_P_r+r_A_r*c_P)/(c_A*c_P-alpha_r**2*beta_r**2*gamma_A*gamma_P)

    # Assume a very small density for the mutant population density
    # and calculate the pollinator mutant's fitness (= per-capita growth rate)
    A_m = 0.000001
    fitness_A = r_A_m - c_A * A_m - c_A * A_r_star + alpha_r * beta_m * gamma_A * P_r_star

    # check if pollinator mutant's fitness is positiv (+)or negativ (-) -> replace resident strategie (+) or keep resident strategie (-)
    if fitness_A > 0:

        beta_r = beta_m      # mutant becomes new resident
        beta_m = beta_r + mut_step_b*np.random.normal(0,1/2.5758)  # new mutant strategy
        beta_m = min(max(0,beta_m),beta_max) # Allow only traits in reasonable range

    if fitness_A <= 0:

        beta_m = beta_r + mut_step_b*np.random.normal(0,1/2.5758) # new mutant strategy
        beta_m = min(max(0,beta_m),beta_max) # Allow only traits in reasonable range

    return [beta_r,beta_m]

#-------------------------------------------------------------------------------------------------------------------

def coevolution_seperate(alpha,beta,limits,eco_parameter,tof_parameter,evo_parameter,env_parameter):

    # unpack paramter sets
    c_P, c_A, gamma_P, gamma_A                      = eco_parameter
    mut_step_a, mut_step_b, P_mut_prob, A_mut_prob  = evo_parameter
    env_change, r_A_change, time_point              = env_parameter
    alpha_max, s_a, beta_max, s_b                   = tof_parameter

    A_ext, t_limit = limits

    A_last = 1 # set dummi-value for A_last

    alpha_m = alpha + mut_step_a * np.random.normal(0,1/2.5758) # initial mutant strategy
    beta_m  =  beta + mut_step_b * np.random.normal(0,1/2.5758) # initial mutant strategy

    alpha_list = []     # list of plant strategies
    beta_list  = []     # list of pollinator strategies
    r_A_list   = []     # list of pollinator growth rates
    P_list     = []     # list of plant population density
    A_list     = []     # list of pollinator population density

    while A_last > A_ext and time_point < t_limit:# loop threough until pollinator goes extinct or time runs out

        alpha_m = min(max(0,alpha_m),alpha_max) # Allow only traits in reasonable range
        beta_m = min(max(0,beta_m),beta_max) # Allow only traits in reasonable range

        r_P = rp_parameter(alpha,tof_parameter) # Update r_P
        r_A = ra_parameter( beta,tof_parameter) - time_point * r_A_change * env_change  # Update r_A

        # Calculate equilibriums population density
        P_star = (alpha*beta*gamma_P*r_A+r_P*c_A)/(c_A*c_P-alpha**2*beta**2*gamma_A*gamma_P)
        A_star = (alpha*beta*gamma_A*r_P+r_A*c_P)/(c_A*c_P-alpha**2*beta**2*gamma_A*gamma_P)

        P_list.append(P_star)    # Save plant population density values
        A_list.append(A_star)    # Save pollinator population density values
        alpha_list.append(alpha) # Save plant interaction investment
        beta_list.append(beta)   # Save pollinator interaction investment
        r_A_list.append(r_A)     # Save pollinator net growth rate

        P_chance = np.random.rand()  # pick random number for the plant evolution chance
        A_chance = np.random.rand()  # pick random number for the pollinator evolution chance

        if P_chance < A_chance: # Check wether which evolution is more likely -> this one comes first
            if P_chance < P_mut_prob: # Check if evolution is happening
                alpha, alpha_m = plant_evolution(alpha,alpha_m,beta,eco_parameter,tof_parameter,evo_parameter,env_parameter)

            if A_chance < A_mut_prob: # Check if evolution is happening
                beta, beta_m = pollinator_evolution(beta,beta_m,alpha,eco_parameter,tof_parameter,evo_parameter,env_parameter)


        if P_chance > A_chance: # Check wether which evolution is more likely -> this one comes first
            if A_chance < A_mut_prob: # Check if evolution is happening
                beta, beta_m = pollinator_evolution(beta,beta_m,alpha,eco_parameter,tof_parameter,evo_parameter,env_parameter)

            if P_chance < P_mut_prob: # Check if evolution is happening
                alpha, alpha_m = plant_evolution(alpha,alpha_m,beta,eco_parameter,tof_parameter,evo_parameter,env_parameter)

        time_point = time_point  + 1 # calculate new time_point
        A_last     = A_list[-1]

        env_parameter = [env_change, r_A_change, time_point]

    return(P_list,A_list,alpha_list,beta_list,r_A_list)




########################################################################################################################
###### Co-evolution of plant and pollinator (overlapping timescales) ######------------------------------------------
########################################################################################################################


def plant_pollinator_dynamics(X0,t,alpha,beta,r_P,r_A,eco_parameter,I):

    c_P, c_A, gamma_P, gamma_A    = eco_parameter

    P = X0[:I]
    A = X0[I:]


    dPdt = r_P * P - c_P * P * P.sum() + alpha * gamma_P * P * np.dot(beta,A)
    dAdt = r_A * A - c_A * A * A.sum() +  beta * gamma_A * A * np.dot(alpha,P)


    return np.hstack([dPdt,dAdt])


#-------------------------------------------------------------------------------------------------------------------

def plant_pollinator_saturate(X0,t,alpha,beta,r_P,r_A,eco_parameter,I):

    c_P, c_A, gamma_P, gamma_A   = eco_parameter

    P = X0[:I]
    A = X0[I:]
    
    x=np.dot(beta,A)
    y=np.dot(alpha,P)

    dPdt = r_P * P - c_P * P * P.sum() + alpha * gamma_P * P * (x / (1.0 + x))
    dAdt = r_A * A - c_A * A * A.sum() +  beta * gamma_A * A * (y / (1.0 + y))


    return np.hstack([dPdt,dAdt])

#-------------------------------------------------------------------------------------------------------------------


def coevolution_overlap(alpha_0,beta_0,t_span,limits,eco_parameter,tof_parameter,evo_parameter,env_parameter, saturate, longoutput):

    # unpack paramter sets
    c_P, c_A, gamma_P, gamma_A                      = eco_parameter
    alpha_max, s_a, beta_max, s_b                   = tof_parameter
    mut_step_a, mut_step_b, P_mut_prob, A_mut_prob  = evo_parameter
    env_change, r_A_change, time_point              = env_parameter

    A_ext, P_ext, t_limit, I_J_limit                = limits


    # Setup for the beginning #-------------------------------------------------------------------------------------

    # Designate initial interaction investments
    alpha = np.array([alpha_0])
    beta  = np.array([ beta_0])

    alpha_min = 0
    beta_min  = 0

    # Calculate inital growth rates from inital interaction investments
    r_P = rp_parameter(alpha,tof_parameter)
    r_A = ra_parameter( beta,tof_parameter)

    # Calculate initial equilibrium population densities from initial growth rates and interaction investments
    P0  = (alpha*beta*gamma_P*r_A+r_P*c_A)/(c_A*c_P-alpha**2*beta**2*gamma_A*gamma_P)
    A0  = (alpha*beta*gamma_A*r_P+r_A*c_P)/(c_A*c_P-alpha**2*beta**2*gamma_A*gamma_P)

    X0 = np.array([P0[0],A0[0]]) # Put equilibrium population densities in array

    I       = 1  # Determine number of total plant mutant for initial iteration
    J       = 1  # Determine number of total pollinator mutant for initial iteration
    A_total = A0 # Determine total population density of pollinators

    t_pop_dyn  = np.linspace(0,t_span-1,t_span)

    # prepare array for storing data #-------------------------------------------------------------------------------

    P_list     = np.array([[ ]]) # Return array at the end
    A_list     = np.array([[ ]]) # Return array at the end

    P_extinct  = np.array([[ ]]) # Working array in the while loop
    A_extinct  = np.array([[ ]]) # Working array in the while loop

    t_P_mut    = [0] # Working array in the while loop
    t_A_mut    = [0] # Working array in the while loop

    P_mut_time = [ ] # Return array at the end
    A_mut_time = [ ] # Return array at the end

    P_ext_time = [ ] # Return array at the end
    A_ext_time = [ ] # Return array at the end

    alpha_list = [ ] # Return array at the end
    beta_list  = [ ] # Return array at the end

    I_list     = [1] # Return array at the end
    J_list     = [1] # Return array at the end

    while A_total > A_ext and time_point < t_limit:  # repeat until pollinator went extinct or time runs out

        #print(time_point)

        ######################################################################################################################
        #################################################### Environment #####################################################
        ######################################################################################################################


        r_A = [] # Reset r_A, array may be smaler than bevor due to extinction
        r_P = [] # Reset r_P, array may be smaler than bevor due to extinction

        # Ajust growth rate dependent on environmental decay
        
        r_A = ra_parameter( beta,tof_parameter) - time_point * t_span * r_A_change * env_change # Adjust r_A list
        r_P = rp_parameter(alpha,tof_parameter) # Update r_P list
        
        
        ######################################################################################################################
        ###################################################### Ecology #######################################################
        ######################################################################################################################


        # Do population dynamics (either with or without saturation of the functional response)
        
        if saturate:             
            P_A = integ.odeint(plant_pollinator_saturate,X0,t_pop_dyn,args=(alpha,beta,r_P,r_A,eco_parameter,I)).T
        else: 
            P_A = integ.odeint(plant_pollinator_dynamics,X0,t_pop_dyn,args=(alpha,beta,r_P,r_A,eco_parameter,I)).T
            

        # Store results in lists #------------------------------------------------------------------------------------

        if longoutput: 
            # version A with full output, e.g. for detailed time series
            P_list = np.hstack([P_list,P_A[:I]]) # Add results of odeint to dataset of plant
            A_list = np.hstack([A_list,P_A[I:]]) # Add results of odeint to dataset of pollinator
        else: 
            # version B with reduced output, only the last t_span time steps are saved, the rest is overwritten
            P_list = P_A[:I] 
            A_list = P_A[I:] 

        ### Plant extinction ###--------------------------------------------------------------------------------------

        if np.any(P_list[:,-1]<P_ext): # Check if any plant went extinct

            P_death_mask = np.where(P_list[:,-1]<P_ext) # Create mask of where the extinct plants are


            # Safe current extinced mutant pop date #-----------------------------------------------------------

            P_storage = P_extinct                                   # Safe curent P_list temporary storage array
            P_extinct = np.zeros((len(P_storage),len(P_list[0])))   # Create new P_list with ne length
            P_extinct[:,0:len(P_storage[0])] = P_storage            # Insert existing P_extinced data in new P_list
            P_extinct = np.vstack([P_extinct,P_list[P_death_mask]]) # Add new extinced plant to P_list


            # Safe strategy & extras information #---------------------------------------------------------------

            alpha_list = np.append(alpha_list,alpha[P_death_mask])   # Save extinced species strategie in array
            P_mut_time = np.append(P_mut_time,t_P_mut[P_death_mask]) # Add time point of   mutation to list
            P_ext_time = np.append(P_ext_time,time_point)            # Add time point of extinction to list


            # Delete data from new extinced plants #-------------------------------------------------------------

            P_list  = np.delete( P_list,P_death_mask,0) # Delete extinced species from Pop array
            alpha   = np.delete(  alpha,P_death_mask,0) # Delete extinced species from investment array
            t_P_mut = np.delete(t_P_mut,P_death_mask,0) # Delete extinced species from mutation time array


        ### Pollinator extinction ###----------------------------------------------------------------------------------


        if np.any(A_list[:,-1]<A_ext): # Check if any pollinator went extinct

            if len(A_list) ==1:
                A_death_mask = int(np.where(A_list[:,-1]<A_ext)[0])
            else:
                A_death_mask = np.where(A_list[:,-1]<A_ext) # Create mask of where the extinct plants are


            # Safe current extinced mutant pop date #-----------------------------------------------------------

            A_storage = A_extinct                                   # Safe curent A_extinct to temporary storage array
            A_extinct = np.zeros((len(A_storage),len(A_list[0])))   # Create new A_extinct with right shape
            A_extinct[:,0:len(A_storage[0])] = A_storage            # Insert existing A_extinced data in new array
            A_extinct = np.vstack([A_extinct,A_list[A_death_mask]]) # Add new extinced pollinators to A_extinct


            # Safe strategy & extras information #---------------------------------------------------------------

            beta_list  = np.append( beta_list,beta[A_death_mask])    # Save extinced species strategie in array
            A_mut_time = np.append(A_mut_time,t_A_mut[A_death_mask]) # Add time point of   mutation to list
            A_ext_time = np.append(A_ext_time,time_point)            # Add time point of extinction to list


            # Delete data from new extinced pollinators #--------------------------------------------------------

            A_list  = np.delete( A_list,A_death_mask,0) # Delete extinced species from Pop array
            beta    = np.delete(   beta,A_death_mask,0) # Delete extinced species from investment array
            t_A_mut = np.delete(t_A_mut,A_death_mask,0) # Delete extinced species from mutation time array




        ######################################################################################################################
        ##################################################### Evolution ######################################################
        ######################################################################################################################

        # Count how many mutants are still alive #------------------------------------------------------------------

        I = len(P_list) # Update number of plant mutant

        ### Plant mutation ###--------------------------------------------------------------------------------------

        if P_mut_prob > 0 and I <= I_J_limit and I > 0: # Check if plants can mutate and mutant number limit is not reached

            P_chance = np.random.rand()  # Pick random number for the plant evolution chance

            if P_chance <= P_mut_prob: # Check if evolution is happening by chance

                # Mutant parent #----------------------------------------------------------

                P_index = np.arange(I) # Update list for indices to identify the mutant parent
                # Choose next plant mutant parent weighted after population density
                i       = np.random.choice(P_index,1,p=abs((P_list[:,-1])/(P_list[:,-1].sum())))[0]
                # Gives out an array, but with [0] at the end a number is safed as i instead of array


                # Trait evolution #--------------------------------------------------------

                # Change parent interaction trait by maximum mutation step and gaussian distribution
                alpha_new = alpha[i] + mut_step_a*np.random.normal(0,1/2.5758)
                alpha_new = min(max(alpha_min,alpha_new),alpha_max) # Allow only traits in reasonable range


                # Add new specie to lists #--------------------------------------------------

                # Add a new list in array, with the same length as all other, filled with zeros
                P_list  = np.vstack([P_list,np.zeros(len(P_list[0]))])
                P_list[-1,-1] = P_ext#P_list[i,-1]/500  # Set starting population for the new mutant
                alpha   = np.append(alpha,alpha_new)    # Add new alpha to list
                t_P_mut = np.append(t_P_mut,time_point) # Add time point of mutation to list


        ### Pollinator mutation ###----------------------------------------------------------------------------------

        J = len(A_list) # Update number of pollinator mutant

        if A_mut_prob > 0 and J <= I_J_limit and J > 0: # Check if pollinators can mutate and mutant number limit is not reached

            A_chance = np.random.rand()  # Pick random number for the pollinator evolution chance

            if A_chance <= A_mut_prob: # Check if evolution is happening

                # Mutant parent #------------------------------------------------------------

                A_index = np.arange(J) # Update list for indices to identify the mutant parent
                # Choose next plant mutant parent weighted after population density
                j       = np.random.choice(A_index,1,p=abs((A_list[:,-1])/(A_list[:,-1].sum())))[0]
                # Gives out a list, but with [0] at the end a number is safed as j instead of array


                # Trait evolution #--------------------------------------------------------

                # Change parent interaction trait by maximum mutation step and gaussian distribution
                beta_new = beta[j] + mut_step_b*np.random.normal(0,1/2.5758)
                beta_new = min(max(beta_min,beta_new),beta_max) # Allow only traits in reasonable range


                # Add new specie to lists #---------------------------------------------------

                # Add a new list in array, with the same length as all other, filled with zeros
                A_list  = np.vstack([A_list,np.zeros(len(A_list[0]))])
                A_list[-1,-1] = A_ext#A_list[j,-1]/500  #Set starting population for the new mutant
                beta    = np.append(beta,beta_new)      # Add new beta to the list
                t_A_mut = np.append(t_A_mut,time_point) # Add time point of mutation to list



        ######################################################################################################################
        ############################################## Prepare for next itaration ############################################
        ######################################################################################################################


        I      = len(P_list) # Update number of plant      mutant
        J      = len(A_list) # Update number of pollinator mutant

        I_list = np.append(I_list,I) # Add number of plan       to list
        J_list = np.append(J_list,J) # Add number of pollinator to list

        X0         = np.hstack([P_list[:,-1],A_list[:,-1]]) # Update starting population for next itaration
        A_total    = A_list[:,-1].sum() # Update total population density of pollinators

        time_point = time_point + 1  # Update time point by adding one



    ##########################################################################################################################
    ############################################### Loop finished ############################################################
    ##########################################################################################################################


    # Put surviving populations in return array #------------------------------------------------------------------

    if len(P_list) == 1:
        P_mask = int(np.where(P_list[:,-1]>=P_ext)[0])
    else:
        P_mask = np.where(P_list[:,-1]>=P_ext) # Create mask of where the extinct plants are

    P_storage = P_extinct # Safe curent P_list temporary storage array
    P_extinct = np.zeros((len(P_storage),len(P_list[0]))) # Create new P_list with right length
    P_extinct[:,0:len(P_storage[0])] = P_storage # Insert existing P_extinced data in new P_list
    P_extinct = np.vstack([P_extinct,P_list[P_mask]]) # Add new extinced plant to P_list


    # Safe strategy & extras information#---------------------------------------------------------------

    alpha_list = np.append(alpha_list,alpha[P_mask])  # Save extinced species strategie in array
    P_mut_time = np.append(P_mut_time,t_P_mut[P_mask]) # Add time point of   mutation to list
    P_ext_time = np.append(P_ext_time,np.ones(len(P_list))*time_point-1) # Add time point of extinction to list

    # Delete data from new extinced plants #-----------------------------------------------------------

    P_list  = np.delete( P_list,P_mask,0) # Delete extinced species from Pop array
    alpha   = np.delete(  alpha,P_mask,0) # Delete extinced species from investment array
    t_P_mut = np.delete(t_P_mut,P_mask,0) # Delete extinced species from mutation time array

    #For simulations without extinction #-----------------------------------------------------------------------------

    if np.any(A_list[:,-1]>=A_ext):
        if len(A_list) == 1:
            A_mask = int(np.where(A_list[:,-1]>=A_ext)[0])
        else:
            A_mask = np.where(A_list[:,-1]>=A_ext) # Create mask of where the extinct pollinators are

        # Safe current extinced mutant pop date #----------------------------------------------------------

        A_storage = A_extinct # Safe curent A_extinct to temporary storage array
        A_extinct = np.zeros((len(A_storage),len(A_list[0]))) # Create new A_extinct with right shape
        A_extinct[:,0:len(A_storage[0])] = A_storage # Insert existing A_extinced data in new array
        A_extinct = np.vstack([A_extinct,A_list[A_mask]]) # Add new extinced pollinators to A_extinct


        # Safe strategy & extras information#---------------------------------------------------------------

        beta_list  = np.append( beta_list,beta[A_mask]) # Save extinced species strategie in array
        A_mut_time = np.append(A_mut_time,t_A_mut[A_mask]) # Add time point of   mutation to list
        A_ext_time = np.append(A_ext_time,np.ones(len(A_mask[0]))*time_point-1) # Add time point of extinction to list


        A_list  = np.delete( A_list,A_mask,0) # Delete extinced species from Pop array
        beta    = np.delete(   beta,A_mask,0) # Delete extinced species from investment array
        t_A_mut = np.delete(t_A_mut,A_mask,0) # Delete extinced species from mutation time array

    time_list = [P_mut_time,A_mut_time,P_ext_time,A_ext_time,time_point]

    I_J_list = [I_list,J_list]


    return([P_extinct[1:],A_extinct[1:],alpha_list,beta_list,time_list,I_J_list])
