Uses observed SNR distribution to calculate normal probability SNR

    Uses state dict to calculate number of trials.
    snrs is list of all snrs in distribution.
    version used to toggle for tests. version 2 is fastest and returns zeros for filtered snr values.
    Returns list of expected snr given each input value's frequency of occurrence via the normal probability assumption

def normprob(d, snrs, inds=None, version=2):
    """ Uses observed SNR distribution to calculate normal probability SNR

    Uses state dict to calculate number of trials.
    snrs is list of all snrs in distribution.
    version used to toggle for tests. version 2 is fastest and returns zeros for filtered snr values.
    Returns list of expected snr given each input value's frequency of occurrence via the normal probability assumption
    """

    if not inds: inds = range(len(snrs))

    # define norm quantile functions
    Z = lambda quan: np.sqrt(2)*erfinv( 2*quan - 1) 
    quan = lambda ntrials, i: (ntrials + 1/2. - i)/ntrials

    # calc number of trials
    npix = d['npixx']*d['npixy']
    if d.has_key('goodintcount'):
        nints = d['goodintcount']
    else:
        nints = d['nints']
    ndms = len(d['dmarr'])
    dtfactor = np.sum([1./i for i in d['dtarr']])    # assumes dedisperse-all algorithm
    ntrials = npix*nints*ndms*dtfactor
    logger.info('Calculating normal probability distribution for npix*nints*ndms*dtfactor = %d' % (ntrials))

    # calc normal quantile
    if version == 2:
        # purely sort and numpy-based
        sortinds = np.argsort(snrs[inds])
        lenpos = len(np.where(snrs[inds] >= 0)[0])
        lenneg = len(np.where(snrs[inds] < 0)[0])
        unsortinds = np.zeros(len(sortinds), dtype=int)
        unsortinds[sortinds] = np.arange(len(sortinds))
        rank = np.concatenate( (np.arange(1, lenneg+1), np.arange(1, lenpos+1)[::-1]) )
        logger.debug('{} {}'.format(rank, sortinds))
        zval = Z(quan(ntrials, rank[unsortinds]))
        if inds != range(len(snrs)):  # add zeros for filtered data to match length to original snr array
            zval = np.array([zval[inds.index(i)] if i in inds else 0 for i in range(len(snrs))])

    elif version == 1:
        # numpy array based
        snrpos = snrs[inds][np.where(snrs[inds] > 0)]
        snrneg = snrs[inds][np.where(snrs[inds] < 0)]
        snrsortpos = np.sort(snrpos)[::-1]
        snrsortneg = np.sort(snrneg)
        
        logger.debug('Sorted pos/neg SNRs')

        zval = []
        for i,snr in enumerate(snrs):
            if i in inds:
                if snr in snrsortpos:
                    zval.append(Z(quan(ntrials, np.where(snr == snrsortpos)[0][0]+1)))
                elif snr in snrsortneg:
                    zval.append(Z(quan(ntrials, np.where(snr == snrsortneg)[0][0]+1)))

    elif version == 0:
        # list based
        snrsortpos = []
        snrsortneg = []
        for i in inds:
            if snrs[i] > 0:
                snrsortpos.append(snrs[i])
            elif snrs[i] < 0:
                snrsortneg.append(abs(snrs[i]))

        snrsortpos = sorted(snrsortpos, reverse=True)
        snrsortneg = sorted(snrsortneg, reverse=True)
        logger.debug('Sorted pos/neg SNRs')

        zval = []
        for (i, snr) in enumerate(snrs):
            if snr >= 0 and i in inds:
                zval.append(Z(quan(ntrials, snrsortpos.index(snr)+1)))
            elif snr < 0 and i in inds:
                zval.append(Z(quan(ntrials, snrsortneg.index(abs(snr))+1)))
            else:
                zval.append(0)

    return zval