def Compute_dNdf(input, output, h): # Meat of the code we are computing 
    '''
        Compute d4N/(dz dM dq df) for a given df bin
        This formula is made to work with the joblib python package
        
        @param input: An array of appropriate parameters, see first few lines of this formula which unpacks the input array
        @param output: An empty array which gets filled by this formula
        @param h: The index for which df bin to fill in the output array
        '''
    alpha = input[0][h] # gravitational wave frequency specific to a bin
    Redshifts = input[1]
    logGalMass = input[2]
    MassRatios = input[3]
    dz = input[4]
    dMass = input[5]
    dq = input[6]
    Dist = input[7]
    Vc_interp = input[8] # comoving volume to use for interpolation
    z_interp = input[9] # redshift to use for interpolation
    dist4interp = input[10] # proper distance to use for interpolation
    dVdz_interp = interp.interp1d(z_interp, Vc_interp) # Does 1-dimensional interpolation to find the slope 
    dist_interp = interp.interp1d(z_interp, dist4interp)
    RandState = np.random.RandomState()
    dNdt_ET = 1e-100 # initialize number of early type memory events
    dNdt_LT = 1e-100 # initialize numer of late type memory events
    epsilon = RandState.normal(0, 0.36, (2, len(Redshifts), len(logGalMass), len(MassRatios)))
    for ii in xrange(len(Redshifts)):
        z = Redshifts[ii]
        frac = ABBSutils.KeenanPairFraction(z)
        dV = ABBSinit.CoMoveVolume(z)
        dVcdz = ABBSinit.Vdz(z)
        dtrdt = 1/(1+z)
        dist = Dist[ii]
        for jj in xrange(len(logGalMass)):
            logM1 = logGalMass[jj]
            M1 = 10**logM1
            dM = dMass[jj]
            PhiET = ABBSutils.CANDELS_ZFOURGE_MF(logM1, z, 'early')
            PhiLT = ABBSutils.CANDELS_ZFOURGE_MF(logM1, z, 'late')
            for kk in xrange(len(MassRatios)):
                q = MassRatios[kk]
                dfrac = -frac / (q * np.log(0.4)) # from Sesana 2013 - [ dfrac/d(log q) = constant from Xu 2012 ]
                tau = (1.264e16) * (M1 * 1.75e-11)**(-0.3) * (1 + (z/8)) # from Lotz 2011 - units of secs
                d3nET = (PhiET * dfrac * dtrdt * dVcdz / tau) # Differential Number of Early-Type Galaxy Mergers
                d3nLT = (PhiLT * dfrac * dtrdt * dVcdz / tau) # Differential Number of Late-Type Galaxy Mergers
                # McET, MtotET, MqET = ABBSinit.MChirpEarlyType_NoScatter(M1, q, 8.46, 1.05)
                # McLT, MtotLT, MqLT = ABBSinit.MChirpLateType_NoScatter(M1, q, 8.46, 1.05)
                ep_ET = epsilon[0, ii, jj, kk]
                McET, MtotET, MqET = ABBS.init.MChirpEarlyType(M1, q, 8.46, 1.05, ep_ET)
                ep_LT = epsilon[1, ii, jj, kk]
                McLT, MtotLT, MqLT = ABBSinit.MChirpLateType(M1, q, 8.46, 1.05, ep_LT)
               
                # # Calculate Memory Signal #
                tauET = ABBSinit.Tau_GW_alpha(alpha, MtotET, McET) #. Convert f to output of Kepler's third law (use alpha for semi-major axis)
                tauLT = ABBSinit.Tau_GW_alpha(alpha, MtotLT, McLT) #. Convert f to output of Kepler's third law
                z_coalET = ABBSinit.z_coal(z, tauET)
                z_coalLT = ABBSinit.z_coal(z, tauLT)
                #print tauET, z_coalET
                muET = MtotET * (MqET / (1 + MqET))
                muLT = MtotLT * (MqLT / (1 + MqLT))
                i_ET = np.arcsin(RandState.uniform())
                i_LT = np.arcsin(RandState.uniform())
                dist_mem_ET = dist_interp(z_coalET) * 1e6
                dist_mem_LT = dist_interp(z_coalLT) * 1e6
                h_mem_ET = ABBSinit.h_mem_plus(i_ET, muET, dist_mem_ET, z_coalET)
                h_mem_LT = ABBSinit.h_mem_plus(i_LT, muLT, dist_mem_LT, z_coalLT)
                dVcdzET_mem = dVdz_interp(z_coalET)
                dVcdzLT_mem = dVdz_interp(z_coalLT)
                dzdt_ET_mem = ABBSinit.tdz(z_coalET)**(-1)
                dzdt_LT_mem = ABBSinit.tdz(z_coalLT)**(-1)
                dtdz_merger = ABBSinit.tdz(z)


                
                
                #. In order to find the number of memory events occuring during some observation time, we need to first 
                #. convert the rate of events from that measured in the binary's rest frame to that measured on Earth. 
                #. Multiply by one Earth year to find the number of events observed.
                prefactor = (dfrac / tau) * dtdz_merger * dtrdt
                #print PhiET, prefactor, dVcdzET_mem, dzdt_ET_mem, dq, dM, dz
                dNdt_ET += PhiET * prefactor * dVcdzET_mem * dzdt_ET_mem * dq * dM * dz * (3.1e7)
                dNdt_LT += PhiLT * prefactor * dVcdzLT_mem * dzdt_LT_mem * dq * dM * dz * (3.1e7)

    print dNdt_ET, dNdt_LT            
    output[0,h] = dNdt_ET # output in 1/yr
    output[1,h] = dNdt_LT
def Compute_dNdf(input, output, h):
    '''
    Compute d4N/(dz dM dq df) for a given df bin
    This formula is made to work with the joblib python package
    
    @param input: An array of appropriate parameters, see first few lines of this formula which unpacks the input array
    @param output: An empty array which gets filled by this formula
    @param h: The index for which df bin to fill in the output array
    '''
    f = input[0][h]
    dfdlnf = f
    Redshifts = input[1]
    logGalMass = input[2]
    MassRatios = input[3]
    dz = input[4]
    dMass = input[5]
    dq = input[6]
    dlnf = input[7][h]
    Dist = input[8]
    Vc_interp = input[9]
    z_interp = input[10]
    dist4interp = input[11]
    dVdz_interp = interp.interp1d(z_interp, Vc_interp)
    dist_interp = interp.interp1d(z_interp, dist4interp)
    RandState = np.random.RandomState()
    for ii in xrange(len(Redshifts)):
        z = Redshifts[ii]
        frac = ABBSutils.KeenanPairFraction(z)
        dVcdz = ABBSinit.Vdz(z)
        dtrdt = 1/(1+z)
        dtdz = ABBSinit.tdz(z)
        dist = Dist[ii]
        for jj in xrange(len(logGalMass)):
            logM1 = logGalMass[jj]
            M1 = 10**logM1
            dM = dMass[jj]
            PhiET = ABBSutils.CANDELS_ZFOURGE_MF(logM1, z, 'early')
            PhiLT = ABBSutils.CANDELS_ZFOURGE_MF(logM1, z, 'late')
            for kk in xrange(len(MassRatios)):
                q = MassRatios[kk]
                dfrac = -frac / (q * np.log(0.4)) # from Sesana 2013 - [ dfrac/d(log q) = constant from Xu 2012 ]
                tau = (1.264e16) * (M1 * 1.75e-11)**(-0.3) * (1 + (z/8)) # from Lotz 2011 - units of secs
                d3nET = (PhiET * dfrac * dtrdt * dVcdz / tau) # Differential Number of Early-Type Galaxy Mergers
                d3nLT = (PhiLT * dfrac * dtrdt * dVcdz / tau) # Differential Number of Late-Type Galaxy Mergers
                McET, MtotET, MqET = ABBSinit.MChirpEarlyType_NoScatter(M1, q, 8.46, 1.05)
                McLT, MtotLT, MqLT = ABBSinit.MChirpLateType_NoScatter(M1, q, 8.46, 1.05)
                dtdfET = ABBSinit.tdf(z, McET, f)
                dtdfLT = ABBSinit.tdf(z, McLT, f)
                h_insp_ET = ABBSinit.RMSStrainFAST(z, McET, f, dist)
                h_insp_LT = ABBSinit.RMSStrainFAST(z, McLT, f, dist)
                d4NET_insp = d3nET * dtdfET * dfdlnf * dz * dM * dq * dlnf
                d4NLT_insp = d3nLT * dtdfLT * dfdlnf * dz * dM * dq * dlnf
                # Calculate Memory Signal #
#                tau_GW_ET = ABBSinit.Tau_GW(z, McET, f)
#                tau_GW_LT = ABBSinit.Tau_GW(z, McLT, f)
#                z_coalET = ABBSinit.z_coal(z, tau_GW_ET)
#                z_coalLT = ABBSinit.z_coal(z, tau_GW_LT)
                muET = MtotET * (MqET / (1 + MqET)**2)
                muLT = MtotLT * (MqLT / (1 + MqLT)**2)
                i_ET = np.pi / 2. #np.arcsin(RandState.uniform())
                i_LT = np.pi / 2. #np.arcsin(RandState.uniform())
#                dist_mem_ET = dist_interp(z_coalET) * 1e6
#                dist_mem_LT = dist_interp(z_coalLT) * 1e6
                h_mem_ET = ABBSinit.h_mem_plus(i_ET, muET, dist * 1e6, z) # dist needs to be in parsec
                h_mem_LT = ABBSinit.h_mem_plus(i_LT, muLT, dist * 1e6, z) # dist needs to be in parsec
#                dVcdzET_mem = dVdz_interp(z_coalET)
#                dVcdzLT_mem = dVdz_interp(z_coalLT)
#                dzdt_ET_mem = ABBSinit.tdz(z_coalET)**(-1)
#                dzdt_LT_mem = ABBSinit.tdz(z_coalLT)**(-1)
#                d3nET_mem = (PhiET * dfrac * dtrdt * dtdz * dzdt_ET_mem * dVcdzET_mem / tau) 
#                d3nLT_mem = (PhiLT * dfrac * dtrdt * dtdz * dzdt_LT_mem * dVcdzLT_mem / tau) 
                d4NET_mem = d3nET * dz * dM * dq * dfdlnf
                d4NLT_mem = d3nLT * dz * dM * dq * dfdlnf
                output[0, h, ii, jj, kk] = d4NET_insp
                output[1, h, ii, jj, kk] = d4NLT_insp
                output[2, h, ii, jj, kk] = h_insp_ET**2
                output[3, h, ii, jj, kk] = h_insp_LT**2
                output[4, h, ii, jj, kk] = d4NET_mem
                output[5, h, ii, jj, kk] = d4NLT_mem
                output[6, h, ii, jj, kk] = (h_mem_ET / f)**2
                output[7, h, ii, jj, kk] = (h_mem_LT / f)**2