def Func(arg_0,
                        arg_1,
                        arg_2,
                        arg_3,
                        arg_4=None,
                        arg_5=True,
                        arg_6=None,
                        arg_7='hat-sql',
                        arg_8=None):
    '''This drives the `get_starfeatures` function for a collection of LCs.

    Parameters
    ----------

    lclist : list of str
        The list of light curve file names to process.

    outdir : str
        The output directory where the results will be placed.

    lc_catalog_pickle : str
        The path to a catalog containing at a dict with least:

        - an object ID array accessible with `dict['objects']['objectid']`

        - an LC filename array accessible with `dict['objects']['lcfname']`

        - a `scipy.spatial.KDTree` or `cKDTree` object to use for finding
          neighbors for each object accessible with `dict['kdtree']`

        A catalog pickle of the form needed can be produced using
        :py:func:`astrobase.lcproc.catalogs.make_lclist` or
        :py:func:`astrobase.lcproc.catalogs.filter_lclist`.

    neighbor_radius_arcsec : float
        This indicates the radius in arcsec to search for neighbors for this
        object using the light curve catalog's `kdtree`, `objlist`, `lcflist`,
        and in GAIA.

    maxobjects : int
        The number of objects to process from `lclist`.

    deredden : bool
        This controls if the colors and any color classifications will be
        dereddened using 2MASS DUST.

    custom_bandpasses : dict or None
        This is a dict used to define any custom bandpasses in the
        `in_objectinfo` dict you want to make this function aware of and
        generate colors for. Use the format below for this dict::

            {
            '<bandpass_key_1>':{'dustkey':'<twomass_dust_key_1>',
                                'label':'<band_label_1>'
                                'colors':[['<bandkey1>-<bandkey2>',
                                           '<BAND1> - <BAND2>'],
                                          ['<bandkey3>-<bandkey4>',
                                           '<BAND3> - <BAND4>']]},
            .
            ...
            .
            '<bandpass_key_N>':{'dustkey':'<twomass_dust_key_N>',
                                'label':'<band_label_N>'
                                'colors':[['<bandkey1>-<bandkey2>',
                                           '<BAND1> - <BAND2>'],
                                          ['<bandkey3>-<bandkey4>',
                                           '<BAND3> - <BAND4>']]},
            }

        Where:

        `bandpass_key` is a key to use to refer to this bandpass in the
        `objectinfo` dict, e.g. 'sdssg' for SDSS g band

        `twomass_dust_key` is the key to use in the 2MASS DUST result table for
        reddening per band-pass. For example, given the following DUST result
        table (using http://irsa.ipac.caltech.edu/applications/DUST/)::

            |Filter_name|LamEff |A_over_E_B_V_SandF|A_SandF|A_over_E_B_V_SFD|A_SFD|
            |char       |float  |float             |float  |float           |float|
            |           |microns|                  |mags   |                |mags |
             CTIO U       0.3734              4.107   0.209            4.968 0.253
             CTIO B       0.4309              3.641   0.186            4.325 0.221
             CTIO V       0.5517              2.682   0.137            3.240 0.165
            .
            .
            ...

        The `twomass_dust_key` for 'vmag' would be 'CTIO V'. If you want to
        skip DUST lookup and want to pass in a specific reddening magnitude
        for your bandpass, use a float for the value of
        `twomass_dust_key`. If you want to skip DUST lookup entirely for
        this bandpass, use None for the value of `twomass_dust_key`.

        `band_label` is the label to use for this bandpass, e.g. 'W1' for
        WISE-1 band, 'u' for SDSS u, etc.

        The 'colors' list contains color definitions for all colors you want
        to generate using this bandpass. this list contains elements of the
        form::

            ['<bandkey1>-<bandkey2>','<BAND1> - <BAND2>']

        where the the first item is the bandpass keys making up this color,
        and the second item is the label for this color to be used by the
        frontends. An example::

            ['sdssu-sdssg','u - g']

    lcformat : str
        This is the `formatkey` associated with your light curve format, which
        you previously passed in to the `lcproc.register_lcformat`
        function. This will be used to look up how to find and read the light
        curves specified in `basedir` or `use_list_of_filenames`.

    lcformatdir : str or None
        If this is provided, gives the path to a directory when you've stored
        your lcformat description JSONs, other than the usual directories lcproc
        knows to search for them in. Use this along with `lcformat` to specify
        an LC format JSON file that's not currently registered with lcproc.

    Returns
    -------

    list of str
        A list of all star features pickles produced.

    '''
    # make sure to make the output directory if it doesn't exist
    if not os.path.exists(arg_1):
        os.makedirs(arg_1)

    if arg_4:
        arg_0 = arg_0[:arg_4]

    # read in the kdtree pickle
    with open(arg_2, 'rb') as infd:
        arg_9 = pickle.load(infd)

    arg_10 = arg_9['kdtree']
    arg_11 = arg_9['objects']['objectid']
    arg_12 = arg_9['objects']['lcfname']

    arg_13 = [(x, arg_1, arg_10, arg_11, arg_12,
              arg_3,
              arg_5, arg_6,
              arg_7, arg_8) for x in arg_0]

    for arg_14 in tqdm(arg_13):
        arg_15 = _starfeatures_worker(arg_14)

    return arg_15


def Func(arg_0,
                          arg_1,
                          arg_2,
                          arg_3,
                          arg_4=None,
                          arg_5=True,
                          arg_6=None,
                          arg_7='hat-sql',
                          arg_8=None,
                          arg_9=arg_10):
    '''This runs `get_starfeatures` in parallel for all light curves in `lclist`.

    Parameters
    ----------

    lclist : list of str
        The list of light curve file names to process.

    outdir : str
        The output directory where the results will be placed.

    lc_catalog_pickle : str
        The path to a catalog containing at a dict with least:

        - an object ID array accessible with `dict['objects']['objectid']`

        - an LC filename array accessible with `dict['objects']['lcfname']`

        - a `scipy.spatial.KDTree` or `cKDTree` object to use for finding
          neighbors for each object accessible with `dict['kdtree']`

        A catalog pickle of the form needed can be produced using
        :py:func:`astrobase.lcproc.catalogs.make_lclist` or
        :py:func:`astrobase.lcproc.catalogs.filter_lclist`.

    neighbor_radius_arcsec : float
        This indicates the radius in arcsec to search for neighbors for this
        object using the light curve catalog's `kdtree`, `objlist`, `lcflist`,
        and in GAIA.

    maxobjects : int
        The number of objects to process from `lclist`.

    deredden : bool
        This controls if the colors and any color classifications will be
        dereddened using 2MASS DUST.

    custom_bandpasses : dict or None
        This is a dict used to define any custom bandpasses in the
        `in_objectinfo` dict you want to make this function aware of and
        generate colors for. Use the format below for this dict::

            {
            '<bandpass_key_1>':{'dustkey':'<twomass_dust_key_1>',
                                'label':'<band_label_1>'
                                'colors':[['<bandkey1>-<bandkey2>',
                                           '<BAND1> - <BAND2>'],
                                          ['<bandkey3>-<bandkey4>',
                                           '<BAND3> - <BAND4>']]},
            .
            ...
            .
            '<bandpass_key_N>':{'dustkey':'<twomass_dust_key_N>',
                                'label':'<band_label_N>'
                                'colors':[['<bandkey1>-<bandkey2>',
                                           '<BAND1> - <BAND2>'],
                                          ['<bandkey3>-<bandkey4>',
                                           '<BAND3> - <BAND4>']]},
            }

        Where:

        `bandpass_key` is a key to use to refer to this bandpass in the
        `objectinfo` dict, e.g. 'sdssg' for SDSS g band

        `twomass_dust_key` is the key to use in the 2MASS DUST result table for
        reddening per band-pass. For example, given the following DUST result
        table (using http://irsa.ipac.caltech.edu/applications/DUST/)::

            |Filter_name|LamEff |A_over_E_B_V_SandF|A_SandF|A_over_E_B_V_SFD|A_SFD|
            |char       |float  |float             |float  |float           |float|
            |           |microns|                  |mags   |                |mags |
             CTIO U       0.3734              4.107   0.209            4.968 0.253
             CTIO B       0.4309              3.641   0.186            4.325 0.221
             CTIO V       0.5517              2.682   0.137            3.240 0.165
            .
            .
            ...

        The `twomass_dust_key` for 'vmag' would be 'CTIO V'. If you want to
        skip DUST lookup and want to pass in a specific reddening magnitude
        for your bandpass, use a float for the value of
        `twomass_dust_key`. If you want to skip DUST lookup entirely for
        this bandpass, use None for the value of `twomass_dust_key`.

        `band_label` is the label to use for this bandpass, e.g. 'W1' for
        WISE-1 band, 'u' for SDSS u, etc.

        The 'colors' list contains color definitions for all colors you want
        to generate using this bandpass. this list contains elements of the
        form::

            ['<bandkey1>-<bandkey2>','<BAND1> - <BAND2>']

        where the the first item is the bandpass keys making up this color,
        and the second item is the label for this color to be used by the
        frontends. An example::

            ['sdssu-sdssg','u - g']

    lcformat : str
        This is the `formatkey` associated with your light curve format, which
        you previously passed in to the `lcproc.register_lcformat`
        function. This will be used to look up how to find and read the light
        curves specified in `basedir` or `use_list_of_filenames`.

    lcformatdir : str or None
        If this is provided, gives the path to a directory when you've stored
        your lcformat description JSONs, other than the usual directories lcproc
        knows to search for them in. Use this along with `lcformat` to specify
        an LC format JSON file that's not currently registered with lcproc.

    nworkers : int
        The number of parallel workers to launch.

    Returns
    -------

    dict
        A dict with key:val pairs of the input light curve filename and the
        output star features pickle for each LC processed.

    '''

    try:
        arg_11 = get_lcformat(arg_7,
                                  use_lcformat_dir=arg_8)
        if arg_11:
            (arg_12, arg_13,
             arg_14, arg_15, arg_16,
             arg_17, arg_18) = arg_11
        else:
            LOGERROR("can't figure out the light curve format")
            return None
    except Exception as e:
        LOGEXCEPTION("can't figure out the light curve format")
        return None

    # make sure to make the output directory if it doesn't exist
    if not os.path.exists(arg_1):
        os.makedirs(arg_1)

    if arg_4:
        arg_0 = arg_0[:arg_4]

    # read in the kdtree pickle
    with open(arg_2, 'rb') as infd:
        arg_19 = pickle.load(infd)

    arg_20 = arg_19['kdtree']
    arg_21 = arg_19['objects']['objectid']
    arg_22 = arg_19['objects']['lcfname']

    arg_23 = [(x, arg_1, arg_20, arg_21, arg_22,
              arg_3,
              arg_5, arg_6, arg_7) for x in arg_0]

    with ProcessPoolExecutor(max_workers=arg_9) as executor:
        arg_24 = executor.map(_starfeatures_worker, arg_23)

    arg_25 = [x for x in arg_24]
    arg_26 = {os.path.basename(x):y for (x,y) in zip(arg_0, arg_25)}

    return arg_26


def Func(arg_0,
                                arg_1,
                                arg_2,
                                arg_3,
                                arg_4=None,
                                arg_5=None,
                                arg_6=True,
                                arg_7=None,
                                arg_8='hat-sql',
                                arg_9=None,
                                arg_10=arg_11,
                                arg_12=True):
    '''This runs parallel star feature extraction for a directory of LCs.

    Parameters
    ----------

    lcdir : list of str
        The directory to search for light curves.

    outdir : str
        The output directory where the results will be placed.

    lc_catalog_pickle : str
        The path to a catalog containing at a dict with least:

        - an object ID array accessible with `dict['objects']['objectid']`

        - an LC filename array accessible with `dict['objects']['lcfname']`

        - a `scipy.spatial.KDTree` or `cKDTree` object to use for finding
          neighbors for each object accessible with `dict['kdtree']`

        A catalog pickle of the form needed can be produced using
        :py:func:`astrobase.lcproc.catalogs.make_lclist` or
        :py:func:`astrobase.lcproc.catalogs.filter_lclist`.

    neighbor_radius_arcsec : float
        This indicates the radius in arcsec to search for neighbors for this
        object using the light curve catalog's `kdtree`, `objlist`, `lcflist`,
        and in GAIA.

    fileglob : str
        The UNIX file glob to use to search for the light curves in `lcdir`. If
        None, the default value for the light curve format specified will be
        used.

    maxobjects : int
        The number of objects to process from `lclist`.

    deredden : bool
        This controls if the colors and any color classifications will be
        dereddened using 2MASS DUST.

    custom_bandpasses : dict or None
        This is a dict used to define any custom bandpasses in the
        `in_objectinfo` dict you want to make this function aware of and
        generate colors for. Use the format below for this dict::

            {
            '<bandpass_key_1>':{'dustkey':'<twomass_dust_key_1>',
                                'label':'<band_label_1>'
                                'colors':[['<bandkey1>-<bandkey2>',
                                           '<BAND1> - <BAND2>'],
                                          ['<bandkey3>-<bandkey4>',
                                           '<BAND3> - <BAND4>']]},
            .
            ...
            .
            '<bandpass_key_N>':{'dustkey':'<twomass_dust_key_N>',
                                'label':'<band_label_N>'
                                'colors':[['<bandkey1>-<bandkey2>',
                                           '<BAND1> - <BAND2>'],
                                          ['<bandkey3>-<bandkey4>',
                                           '<BAND3> - <BAND4>']]},
            }

        Where:

        `bandpass_key` is a key to use to refer to this bandpass in the
        `objectinfo` dict, e.g. 'sdssg' for SDSS g band

        `twomass_dust_key` is the key to use in the 2MASS DUST result table for
        reddening per band-pass. For example, given the following DUST result
        table (using http://irsa.ipac.caltech.edu/applications/DUST/)::

            |Filter_name|LamEff |A_over_E_B_V_SandF|A_SandF|A_over_E_B_V_SFD|A_SFD|
            |char       |float  |float             |float  |float           |float|
            |           |microns|                  |mags   |                |mags |
             CTIO U       0.3734              4.107   0.209            4.968 0.253
             CTIO B       0.4309              3.641   0.186            4.325 0.221
             CTIO V       0.5517              2.682   0.137            3.240 0.165
            .
            .
            ...

        The `twomass_dust_key` for 'vmag' would be 'CTIO V'. If you want to
        skip DUST lookup and want to pass in a specific reddening magnitude
        for your bandpass, use a float for the value of
        `twomass_dust_key`. If you want to skip DUST lookup entirely for
        this bandpass, use None for the value of `twomass_dust_key`.

        `band_label` is the label to use for this bandpass, e.g. 'W1' for
        WISE-1 band, 'u' for SDSS u, etc.

        The 'colors' list contains color definitions for all colors you want
        to generate using this bandpass. this list contains elements of the
        form::

            ['<bandkey1>-<bandkey2>','<BAND1> - <BAND2>']

        where the the first item is the bandpass keys making up this color,
        and the second item is the label for this color to be used by the
        frontends. An example::

            ['sdssu-sdssg','u - g']

    lcformat : str
        This is the `formatkey` associated with your light curve format, which
        you previously passed in to the `lcproc.register_lcformat`
        function. This will be used to look up how to find and read the light
        curves specified in `basedir` or `use_list_of_filenames`.

    lcformatdir : str or None
        If this is provided, gives the path to a directory when you've stored
        your lcformat description JSONs, other than the usual directories lcproc
        knows to search for them in. Use this along with `lcformat` to specify
        an LC format JSON file that's not currently registered with lcproc.

    nworkers : int
        The number of parallel workers to launch.

    Returns
    -------

    dict
        A dict with key:val pairs of the input light curve filename and the
        output star features pickle for each LC processed.

    '''

    try:
        arg_13 = get_lcformat(arg_8,
                                  use_lcformat_dir=arg_9)
        if arg_13:
            (arg_14, arg_15,
             arg_16, arg_17, arg_18,
             arg_19, arg_20) = arg_13
        else:
            LOGERROR("can't figure out the light curve format")
            return None
    except Exception as e:
        LOGEXCEPTION("can't figure out the light curve format")
        return None

    if not arg_4:
        arg_4 = arg_14

    # now find the files
    LOGINFO('searching for %s light curves in %s ...' % (arg_8, arg_0))

    if arg_12 is False:
        arg_21 = glob.glob(os.path.join(arg_0, arg_4))

    else:
        # use recursive glob for Python 3.5+
        if sys.version_info[:2] > (3,4):

            arg_21 = glob.glob(os.path.join(arg_0,
                                              '**',
                                              arg_4),arg_12=True)

        # otherwise, use os.walk and glob
        else:

            # use os.walk to go through the directories
            arg_22 = os.walk(arg_0)
            arg_21 = []

            for arg_23, arg_24, arg_25 in arg_22:
                for arg_26 in arg_24:
                    arg_27 = os.path.join(arg_23,
                                              arg_26,
                                              arg_4)
                    arg_28 = glob.glob(arg_27)

                    if arg_28:
                        arg_21.extend(arg_28)

    # now that we have all the files, process them
    if arg_21 and len(arg_21) > 0:

        LOGINFO('found %s light curves, getting starfeatures...' %
                len(arg_21))

        return parallel_starfeatures(arg_21,
                                     arg_1,
                                     arg_2,
                                     arg_3,
                                     arg_6=arg_6,
                                     arg_7=arg_7,
                                     arg_5=arg_5,
                                     arg_8=arg_8,
                                     arg_9=arg_9,
                                     arg_10=arg_10)

    else:

        LOGERROR('no light curve files in %s format found in %s' % (arg_8,
                                                                    arg_0))
        return None


def Func(arg_0):
    '''
    This is the parallel worker for the function below.

    task[0] = frequency for this worker
    task[1] = times array
    task[2] = mags array
    task[3] = fold_time
    task[4] = j_range
    task[5] = keep_threshold_1
    task[6] = keep_threshold_2
    task[7] = phasebinsize

    we don't need errs for the worker.

    '''

    arg_1 = arg_0[0]
    arg_2, arg_3 = arg_0[1], arg_0[2]
    arg_4 = arg_0[3]
    arg_5 = range(arg_0[4])
    arg_6 = arg_0[5]
    arg_7 = arg_0[6]
    arg_8 = arg_0[7]

    try:

        arg_9 = 1.0/arg_1

        # use the common phaser to phase and sort the mag
        arg_10 = phase_magseries(arg_2,
                                 arg_3,
                                 arg_9,
                                 arg_4,
                                 wrap=False,
                                 sort=True)

        # bin in phase if requested, this turns this into a sort of PDM method
        if arg_8 is not None and arg_8 > 0:
            arg_11 = pwd_phasebin(arg_10['phase'],
                                   arg_10['mags'],
                                   binsize=arg_8)
            arg_12 = arg_11[0]
            arg_13 = arg_11[1]
            arg_5 = range(len(arg_13) - 1)
        else:
            arg_12 = arg_10['phase']
            arg_13 = arg_10['mags']

        # now calculate the string length
        arg_14 = nproll(arg_13,1)
        arg_15 = nproll(arg_12,1)
        arg_16 = (
            (arg_14 - arg_13)*(arg_14 - arg_13) +
            (arg_15 - arg_12)*(arg_15 - arg_12)
        )
        arg_16[0] = (
            ((arg_13[0] - arg_13[-1]) *
             (arg_13[0] - arg_13[-1])) +
            ((arg_12[0] - arg_12[-1] + 1) *
             (arg_12[0] - arg_12[-1] + 1))
        )
        arg_17 = npsum(npsqrt(arg_16))

        if (arg_6 < arg_17 < arg_7):
            arg_18  = True
        else:
            arg_18 = False

        return (arg_9, arg_17, arg_18)

    except Exception as e:

        LOGEXCEPTION('error in DWP')
        return(arg_9, npnan, False)


def Func(arg_0,
                            arg_1,
                            arg_2,
                            arg_3=None,
                            arg_4=5,
                            # these are depth, duration, ingress duration
                            arg_5=(-0.01,0.1,0.1),
                            # these are depth, duration, depth ratio, secphase
                            arg_6=(-0.2,0.3,0.7,0.5),
                            arg_7=1.0e-4,
                            arg_8=1.0e-4,
                            arg_9=5.0,
                            arg_10=None,
                            arg_11=None,
                            arg_12=None,
                            arg_13=None,
                            arg_14=None,
                            arg_15=None,
                            arg_16='hat-sql',
                            arg_17=None,
                            arg_18=10.0,
                            arg_19=False,
                            arg_20=None):
    '''This drives the periodicfeatures collection for a list of periodfinding
    pickles.

    Parameters
    ----------

    pfpkl_list : list of str
        The list of period-finding pickles to use.

    lcbasedir : str
        The base directory where the associated light curves are located.

    outdir : str
        The directory where the results will be written.

    starfeaturesdir : str or None
        The directory containing the `starfeatures-<objectid>.pkl` files for
        each object to use calculate neighbor proximity light curve features.

    fourierorder : int
        The Fourier order to use to generate sinusoidal function and fit that to
        the phased light curve.

    transitparams : list of floats
        The transit depth, duration, and ingress duration to use to generate a
        trapezoid planet transit model fit to the phased light curve. The period
        used is the one provided in `period`, while the epoch is automatically
        obtained from a spline fit to the phased light curve.

    ebparams : list of floats
        The primary eclipse depth, eclipse duration, the primary-secondary depth
        ratio, and the phase of the secondary eclipse to use to generate an
        eclipsing binary model fit to the phased light curve. The period used is
        the one provided in `period`, while the epoch is automatically obtained
        from a spline fit to the phased light curve.

    pdiff_threshold : float
        This is the max difference between periods to consider them the same.

    sidereal_threshold : float
        This is the max difference between any of the 'best' periods and the
        sidereal day periods to consider them the same.

    sampling_peak_multiplier : float
        This is the minimum multiplicative factor of a 'best' period's
        normalized periodogram peak over the sampling periodogram peak at the
        same period required to accept the 'best' period as possibly real.

    sampling_startp, sampling_endp : float
        If the `pgramlist` doesn't have a time-sampling Lomb-Scargle
        periodogram, it will be obtained automatically. Use these kwargs to
        control the minimum and maximum period interval to be searched when
        generating this periodogram.

    timecols : list of str or None
        The timecol keys to use from the lcdict in calculating the features.

    magcols : list of str or None
        The magcol keys to use from the lcdict in calculating the features.

    errcols : list of str or None
        The errcol keys to use from the lcdict in calculating the features.

    lcformat : str
        This is the `formatkey` associated with your light curve format, which
        you previously passed in to the `lcproc.register_lcformat`
        function. This will be used to look up how to find and read the light
        curves specified in `basedir` or `use_list_of_filenames`.

    lcformatdir : str or None
        If this is provided, gives the path to a directory when you've stored
        your lcformat description JSONs, other than the usual directories lcproc
        knows to search for them in. Use this along with `lcformat` to specify
        an LC format JSON file that's not currently registered with lcproc.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    verbose : bool
        If True, will indicate progress while working.

    maxobjects : int
        The total number of objects to process from `pfpkl_list`.

    Returns
    -------

    Nothing.

    '''

    try:
        arg_21 = get_lcformat(arg_16,
                                  use_lcformat_dir=arg_17)
        if arg_21:
            (arg_22, arg_23,
             arg_24, arg_25, arg_26,
             arg_27, arg_28) = arg_21
        else:
            LOGERROR("can't figure out the light curve format")
            return None
    except Exception as e:
        LOGEXCEPTION("can't figure out the light curve format")
        return None

    # make sure to make the output directory if it doesn't exist
    if not os.path.exists(arg_2):
        os.makedirs(arg_2)

    if arg_20:
        arg_0 = arg_0[:arg_20]

    LOGINFO('%s periodfinding pickles to process' % len(arg_0))

    # if the starfeaturedir is provided, try to find a starfeatures pickle for
    # each periodfinding pickle in pfpkl_list
    if arg_3 and os.path.exists(arg_3):

        arg_29 = []

        LOGINFO('collecting starfeatures pickles...')

        for arg_30 in arg_0:

            arg_31 = os.path.basename(arg_30).replace('periodfinding',
                                                     'starfeatures')
            arg_32 = arg_31.replace('.gz','')

            arg_33 = os.path.join(arg_3, arg_31)
            arg_34 = os.path.join(arg_3, arg_32)

            if os.path.exists(arg_33):
                arg_29.append(arg_31)
            elif os.path.exists(arg_34):
                arg_29.append(arg_32)
            else:
                arg_29.append(None)

    else:

        arg_29 = [None for x in arg_0]

    # generate the task list
    arg_35 = {'fourierorder':arg_4,
              'transitparams':arg_5,
              'ebparams':arg_6,
              'pdiff_threshold':arg_7,
              'sidereal_threshold':arg_8,
              'sampling_peak_multiplier':arg_9,
              'sampling_startp':arg_10,
              'sampling_endp':arg_11,
              'timecols':arg_13,
              'magcols':arg_14,
              'errcols':arg_15,
              'lcformat':arg_16,
              'lcformatdir':arg_17,
              'sigclip':arg_18,
              'verbose':arg_19}

    arg_36 = [(x, arg_1, arg_2, y, arg_35) for (x,y) in
             zip(arg_0, arg_29)]

    LOGINFO('processing periodfinding pickles...')

    for arg_37 in tqdm(arg_36):
        _periodicfeatures_worker(arg_37)


def Func(arg_0,
                              arg_1,
                              arg_2,
                              arg_3=None,
                              arg_4=5,
                              # these are depth, duration, ingress duration
                              arg_5=(-0.01,0.1,0.1),
                              # these are depth, duration, depth ratio, secphase
                              arg_6=(-0.2,0.3,0.7,0.5),
                              arg_7=1.0e-4,
                              arg_8=1.0e-4,
                              arg_9=5.0,
                              arg_10=None,
                              arg_11=None,
                              arg_12=None,
                              arg_13=None,
                              arg_14=None,
                              arg_15='hat-sql',
                              arg_16=None,
                              arg_17=10.0,
                              arg_18=False,
                              arg_19=None,
                              arg_20=arg_21):
    '''This runs periodic feature generation in parallel for all periodfinding
    pickles in the input list.

    Parameters
    ----------

    pfpkl_list : list of str
        The list of period-finding pickles to use.

    lcbasedir : str
        The base directory where the associated light curves are located.

    outdir : str
        The directory where the results will be written.

    starfeaturesdir : str or None
        The directory containing the `starfeatures-<objectid>.pkl` files for
        each object to use calculate neighbor proximity light curve features.

    fourierorder : int
        The Fourier order to use to generate sinusoidal function and fit that to
        the phased light curve.

    transitparams : list of floats
        The transit depth, duration, and ingress duration to use to generate a
        trapezoid planet transit model fit to the phased light curve. The period
        used is the one provided in `period`, while the epoch is automatically
        obtained from a spline fit to the phased light curve.

    ebparams : list of floats
        The primary eclipse depth, eclipse duration, the primary-secondary depth
        ratio, and the phase of the secondary eclipse to use to generate an
        eclipsing binary model fit to the phased light curve. The period used is
        the one provided in `period`, while the epoch is automatically obtained
        from a spline fit to the phased light curve.

    pdiff_threshold : float
        This is the max difference between periods to consider them the same.

    sidereal_threshold : float
        This is the max difference between any of the 'best' periods and the
        sidereal day periods to consider them the same.

    sampling_peak_multiplier : float
        This is the minimum multiplicative factor of a 'best' period's
        normalized periodogram peak over the sampling periodogram peak at the
        same period required to accept the 'best' period as possibly real.

    sampling_startp, sampling_endp : float
        If the `pgramlist` doesn't have a time-sampling Lomb-Scargle
        periodogram, it will be obtained automatically. Use these kwargs to
        control the minimum and maximum period interval to be searched when
        generating this periodogram.

    timecols : list of str or None
        The timecol keys to use from the lcdict in calculating the features.

    magcols : list of str or None
        The magcol keys to use from the lcdict in calculating the features.

    errcols : list of str or None
        The errcol keys to use from the lcdict in calculating the features.

    lcformat : str
        This is the `formatkey` associated with your light curve format, which
        you previously passed in to the `lcproc.register_lcformat`
        function. This will be used to look up how to find and read the light
        curves specified in `basedir` or `use_list_of_filenames`.

    lcformatdir : str or None
        If this is provided, gives the path to a directory when you've stored
        your lcformat description JSONs, other than the usual directories lcproc
        knows to search for them in. Use this along with `lcformat` to specify
        an LC format JSON file that's not currently registered with lcproc.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    verbose : bool
        If True, will indicate progress while working.

    maxobjects : int
        The total number of objects to process from `pfpkl_list`.

    nworkers : int
        The number of parallel workers to launch to process the input.

    Returns
    -------

    dict
        A dict containing key: val pairs of the input period-finder result and
        the output periodic feature result pickles for each input pickle is
        returned.

    '''
    # make sure to make the output directory if it doesn't exist
    if not os.path.exists(arg_2):
        os.makedirs(arg_2)

    if arg_19:
        arg_0 = arg_0[:arg_19]

    LOGINFO('%s periodfinding pickles to process' % len(arg_0))

    # if the starfeaturedir is provided, try to find a starfeatures pickle for
    # each periodfinding pickle in pfpkl_list
    if arg_3 and os.path.exists(arg_3):

        arg_22 = []

        LOGINFO('collecting starfeatures pickles...')

        for arg_23 in arg_0:

            arg_24 = os.path.basename(arg_23).replace('periodfinding',
                                                     'starfeatures')
            arg_25 = arg_24.replace('.gz','')

            arg_26 = os.path.join(arg_3, arg_24)
            arg_27 = os.path.join(arg_3, arg_25)

            if os.path.exists(arg_26):
                arg_22.append(arg_24)
            elif os.path.exists(arg_27):
                arg_22.append(arg_25)
            else:
                arg_22.append(None)

    else:

        arg_22 = [None for x in arg_0]

    # generate the task list
    arg_28 = {'fourierorder':arg_4,
              'transitparams':arg_5,
              'ebparams':arg_6,
              'pdiff_threshold':arg_7,
              'sidereal_threshold':arg_8,
              'sampling_peak_multiplier':arg_9,
              'sampling_startp':arg_10,
              'sampling_endp':arg_11,
              'timecols':arg_12,
              'magcols':arg_13,
              'errcols':arg_14,
              'lcformat':arg_15,
              'lcformatdir':arg_15,
              'sigclip':arg_17,
              'verbose':arg_18}

    arg_29 = [(x, arg_1, arg_2, y, arg_28) for (x,y) in
             zip(arg_0, arg_22)]

    LOGINFO('processing periodfinding pickles...')

    with ProcessPoolExecutor(max_workers=arg_20) as executor:
        arg_30 = executor.map(_periodicfeatures_worker, arg_29)

    arg_31 = [x for x in arg_30]
    arg_32 = {os.path.basename(x):y for (x,y) in zip(arg_0, arg_31)}

    return arg_32


def Func(
        arg_0,
        arg_1,
        arg_2,
        arg_3='periodfinding-*.pkl*',
        arg_4=None,
        arg_5=5,
        # these are depth, duration, ingress duration
        arg_6=(-0.01,0.1,0.1),
        # these are depth, duration, depth ratio, secphase
        arg_7=(-0.2,0.3,0.7,0.5),
        arg_8=1.0e-4,
        arg_9=1.0e-4,
        arg_10=5.0,
        arg_11=None,
        arg_12=None,
        arg_13=None,
        arg_14=None,
        arg_15=None,
        arg_16='hat-sql',
        arg_17=None,
        arg_18=10.0,
        arg_19=False,
        arg_20=None,
        arg_21=arg_22,
        arg_23=True,
):
    '''This runs parallel periodicfeature extraction for a directory of
    periodfinding result pickles.

    Parameters
    ----------

    pfpkl_dir : str
        The directory containing the pickles to process.

    lcbasedir : str
        The directory where all of the associated light curve files are located.

    outdir : str
        The directory where all the output will be written.

    pfpkl_glob : str
        The UNIX file glob to use to search for period-finder result pickles in
        `pfpkl_dir`.

    starfeaturesdir : str or None
        The directory containing the `starfeatures-<objectid>.pkl` files for
        each object to use calculate neighbor proximity light curve features.

    fourierorder : int
        The Fourier order to use to generate sinusoidal function and fit that to
        the phased light curve.

    transitparams : list of floats
        The transit depth, duration, and ingress duration to use to generate a
        trapezoid planet transit model fit to the phased light curve. The period
        used is the one provided in `period`, while the epoch is automatically
        obtained from a spline fit to the phased light curve.

    ebparams : list of floats
        The primary eclipse depth, eclipse duration, the primary-secondary depth
        ratio, and the phase of the secondary eclipse to use to generate an
        eclipsing binary model fit to the phased light curve. The period used is
        the one provided in `period`, while the epoch is automatically obtained
        from a spline fit to the phased light curve.

    pdiff_threshold : float
        This is the max difference between periods to consider them the same.

    sidereal_threshold : float
        This is the max difference between any of the 'best' periods and the
        sidereal day periods to consider them the same.

    sampling_peak_multiplier : float
        This is the minimum multiplicative factor of a 'best' period's
        normalized periodogram peak over the sampling periodogram peak at the
        same period required to accept the 'best' period as possibly real.

    sampling_startp, sampling_endp : float
        If the `pgramlist` doesn't have a time-sampling Lomb-Scargle
        periodogram, it will be obtained automatically. Use these kwargs to
        control the minimum and maximum period interval to be searched when
        generating this periodogram.

    timecols : list of str or None
        The timecol keys to use from the lcdict in calculating the features.

    magcols : list of str or None
        The magcol keys to use from the lcdict in calculating the features.

    errcols : list of str or None
        The errcol keys to use from the lcdict in calculating the features.

    lcformat : str
        This is the `formatkey` associated with your light curve format, which
        you previously passed in to the `lcproc.register_lcformat`
        function. This will be used to look up how to find and read the light
        curves specified in `basedir` or `use_list_of_filenames`.

    lcformatdir : str or None
        If this is provided, gives the path to a directory when you've stored
        your lcformat description JSONs, other than the usual directories lcproc
        knows to search for them in. Use this along with `lcformat` to specify
        an LC format JSON file that's not currently registered with lcproc.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    verbose : bool
        If True, will indicate progress while working.

    maxobjects : int
        The total number of objects to process from `pfpkl_list`.

    nworkers : int
        The number of parallel workers to launch to process the input.

    Returns
    -------

    dict
        A dict containing key: val pairs of the input period-finder result and
        the output periodic feature result pickles for each input pickle is
        returned.

    '''

    try:
        arg_24 = get_lcformat(arg_16,
                                  use_lcformat_dir=arg_17)
        if arg_24:
            (arg_25, arg_26,
             arg_27, arg_28, arg_29,
             arg_30, arg_31) = arg_24
        else:
            LOGERROR("can't figure out the light curve format")
            return None
    except Exception as e:
        LOGEXCEPTION("can't figure out the light curve format")
        return None

    arg_32 = arg_3

    # now find the files
    LOGINFO('searching for periodfinding pickles in %s ...' % arg_0)

    if arg_23 is False:
        arg_33 = glob.glob(os.path.join(arg_0, arg_32))

    else:
        # use recursive glob for Python 3.5+
        if sys.version_info[:2] > (3,4):

            arg_33 = glob.glob(os.path.join(arg_0,
                                              '**',
                                              arg_32),arg_23=True)

        # otherwise, use os.walk and glob
        else:

            # use os.walk to go through the directories
            arg_34 = os.walk(arg_0)
            arg_33 = []

            for arg_35, arg_36, arg_37 in arg_34:
                for arg_38 in arg_36:
                    arg_39 = os.path.join(arg_35,
                                              arg_38,
                                              arg_32)
                    arg_40 = glob.glob(arg_39)

                    if arg_40:
                        arg_33.extend(arg_40)

    # now that we have all the files, process them
    if arg_33 and len(arg_33) > 0:

        LOGINFO('found %s periodfinding pickles, getting periodicfeatures...' %
                len(arg_33))

        return parallel_periodicfeatures(
            arg_33,
            arg_1,
            arg_2,
            arg_4=arg_4,
            arg_5=arg_5,
            arg_6=arg_6,
            arg_7=arg_7,
            arg_8=arg_8,
            arg_9=arg_9,
            arg_10=arg_10,
            arg_11=arg_11,
            arg_12=arg_12,
            arg_13=arg_13,
            arg_14=arg_14,
            arg_15=arg_15,
            arg_16=arg_16,
            arg_17=arg_17,
            arg_18=arg_18,
            arg_19=arg_19,
            arg_20=arg_20,
            arg_21=arg_21,
        )

    else:

        LOGERROR('no periodfinding pickles found in %s' % (arg_0))
        return None


def Func(arg_0, arg_1):
    '''
    This parses the header for a catalog file and returns it as a file object.

    Parameters
    ----------

    xc : str
        The file name of an xmatch catalog prepared previously.

    xk : list of str
        This is a list of column names to extract from the xmatch catalog.

    Returns
    -------

    tuple
        The tuple returned is of the form::

            (infd: the file object associated with the opened xmatch catalog,
             catdefdict: a dict describing the catalog column definitions,
             catcolinds: column number indices of the catalog,
             catcoldtypes: the numpy dtypes of the catalog columns,
             catcolnames: the names of each catalog column,
             catcolunits: the units associated with each catalog column)

    '''

    arg_2 = []

    # read in this catalog and transparently handle gzipped files
    if arg_0.endswith('.gz'):
        arg_3 = gzip.open(arg_0,'rb')
    else:
        arg_3 = open(arg_0,'rb')

    # read in the defs
    for arg_4 in arg_3:
        if arg_4.decode().startswith('#'):
            arg_2.append(
                arg_4.decode().replace('#','').strip().rstrip('\n')
            )
        if not arg_4.decode().startswith('#'):
            break

    if not len(arg_2) > 0:
        LOGERROR("catalog definition not parseable "
                 "for catalog: %s, skipping..." % arg_0)
        return None

    arg_2 = ' '.join(arg_2)
    arg_5 = json.loads(arg_2)

    arg_6 = [x['key'] for x in arg_5['columns']]
    arg_7 = [x['dtype'] for x in arg_5['columns']]
    arg_8 = [x['name'] for x in arg_5['columns']]
    arg_9 = [x['unit'] for x in arg_5['columns']]

    # get the correct column indices and dtypes for the requested columns
    # from the catdefdict

    arg_10 = []
    arg_11 = []
    arg_12 = []
    arg_13 = []

    for arg_14 in arg_1:

        if arg_14 in arg_6:

            arg_15 = arg_6.index(arg_14)

            arg_10.append(arg_15)
            arg_11.append(arg_7[arg_15])
            arg_12.append(arg_8[arg_15])
            arg_13.append(arg_9[arg_15])

    return (arg_3, arg_5,
            arg_10, arg_11, arg_12, arg_13)


def Func(arg_0, arg_1, arg_2=None):
    '''This loads the external xmatch catalogs into a dict for use in an xmatch.

    Parameters
    ----------

    xmatchto : list of str
        This is a list of paths to all the catalog text files that will be
        loaded.

        The text files must be 'CSVs' that use the '|' character as the
        separator betwen columns. These files should all begin with a header in
        JSON format on lines starting with the '#' character. this header will
        define the catalog and contains the name of the catalog and the column
        definitions. Column definitions must have the column name and the numpy
        dtype of the columns (in the same format as that expected for the
        numpy.genfromtxt function). Any line that does not begin with '#' is
        assumed to be part of the columns in the catalog. An example is shown
        below::

            # {"name":"NSVS catalog of variable stars",
            #  "columns":[
            #   {"key":"objectid", "dtype":"U20", "name":"Object ID", "unit": null},
            #   {"key":"ra", "dtype":"f8", "name":"RA", "unit":"deg"},
            #   {"key":"decl","dtype":"f8", "name": "Declination", "unit":"deg"},
            #   {"key":"sdssr","dtype":"f8","name":"SDSS r", "unit":"mag"},
            #   {"key":"vartype","dtype":"U20","name":"Variable type", "unit":null}
            #  ],
            #  "colra":"ra",
            #  "coldec":"decl",
            #  "description":"Contains variable stars from the NSVS catalog"}
            objectid1 | 45.0  | -20.0 | 12.0 | detached EB
            objectid2 | 145.0 | 23.0  | 10.0 | RRab
            objectid3 | 12.0  | 11.0  | 14.0 | Cepheid
            .
            .
            .

    xmatchkeys : list of lists
        This is the list of lists of column names (as str) to get out of each
        `xmatchto` catalog. This should be the same length as `xmatchto` and
        each element here will apply to the respective file in `xmatchto`.

    outfile : str or None
        If this is not None, set this to the name of the pickle to write the
        collected xmatch catalogs to. this pickle can then be loaded
        transparently by the :py:func:`astrobase.checkplot.pkl.checkplot_dict`,
        :py:func:`astrobase.checkplot.pkl.checkplot_pickle` functions to provide
        xmatch info to the
        :py:func:`astrobase.checkplot.pkl_xmatch.xmatch_external_catalogs`
        function below.

        If this is None, will return the loaded xmatch catalogs directly. This
        will be a huge dict, so make sure you have enough RAM.

    Returns
    -------

    str or dict
        Based on the `outfile` kwarg, will either return the path to a collected
        xmatch pickle file or the collected xmatch dict.

    '''

    arg_3 = {}

    for arg_4, arg_5 in zip(arg_0, arg_1):

        arg_6 = _parse_xmatch_catalog_header(arg_4, arg_5)

        if not arg_6:
            continue

        (arg_7, arg_8,
         arg_9, arg_10,
         arg_11, arg_12) = arg_6

        # get the specified columns out of the catalog
        arg_13 = np.genfromtxt(arg_7,
                               usecols=arg_9,
                               names=arg_5,
                               dtype=','.join(arg_10),
                               comments='#',
                               delimiter='|',
                               autostrip=True)
        arg_7.close()

        arg_14 = os.path.splitext(os.path.basename(arg_4))[0]
        arg_14 = arg_14.replace('.csv','')

        #
        # make a kdtree for this catalog
        #

        # get the ra and decl columns
        arg_15, arg_16 = (arg_13[arg_8['colra']],
                          arg_13[arg_8['coldec']])

        # get the xyz unit vectors from ra,decl
        arg_17 = np.cos(np.radians(arg_16))
        arg_18 = np.sin(np.radians(arg_16))
        arg_19 = np.cos(np.radians(arg_15))
        arg_20 = np.sin(np.radians(arg_15))
        arg_21 = np.column_stack((arg_19*arg_17,arg_20*arg_17, arg_18))

        # generate the kdtree
        arg_22 = cKDTree(arg_21,copy_data=True)

        # generate the outdict element for this catalog
        arg_23 = {'kdtree':arg_22,
                      'data':arg_13,
                      'columns':arg_5,
                      'colnames':arg_11,
                      'colunits':arg_12,
                      'name':arg_8['name'],
                      'desc':arg_8['description']}

        arg_3[arg_14] = arg_23

    if arg_2 is not None:

        # if we're on OSX, we apparently need to save the file in chunks smaller
        # than 2 GB to make it work right. can't load pickles larger than 4 GB
        # either, but 3 GB < total size < 4 GB appears to be OK when loading.
        # also see: https://bugs.python.org/issue24658.
        # fix adopted from: https://stackoverflow.com/a/38003910
        if sys.platform == 'darwin':

            arg_24 = pickle.dumps(arg_3, protocol=pickle.HIGHEST_PROTOCOL)
            arg_25 = 2**31 - 1

            with open(arg_2, 'wb') as outfd:
                for arg_26 in range(0, len(arg_24), arg_25):
                    outfd.write(arg_24[arg_26:arg_26+arg_25])

        else:
            with open(arg_2, 'wb') as outfd:
                pickle.dump(arg_3, outfd, pickle.HIGHEST_PROTOCOL)

        return arg_2

    else:

        return arg_3


def Func(arg_0, arg_1=False):
    '''Wraps the input angle to 360.0 degrees.

    Parameters
    ----------

    angle : float
        The angle to wrap around 360.0 deg.

    radians : bool
        If True, will assume that the input is in radians. The output will then
        also be in radians.

    Returns
    -------

    float
        Wrapped angle. If radians is True: input is assumed to be in radians,
        output is also in radians.

    '''

    if arg_1:
        arg_2 = arg_0 % (2.0*pi_value)
        if arg_2 < 0.0:
            arg_2 = 2.0*pi_value + arg_2

    else:

        arg_2 = arg_0 % 360.0
        if arg_2 < 0.0:
            arg_2 = 360.0 + arg_2

    return arg_2


def Func(arg_0, arg_1, arg_2, arg_3):
    '''Calculates the great circle angular distance between two coords.

    This calculates the great circle angular distance in arcseconds between two
    coordinates (ra1,dec1) and (ra2,dec2). This is basically a clone of GCIRC
    from the IDL Astrolib.

    Parameters
    ----------

    ra1,dec1 : float or array-like
        The first coordinate's right ascension and declination value(s) in
        decimal degrees.

    ra2,dec2 : float or array-like
        The second coordinate's right ascension and declination value(s) in
        decimal degrees.

    Returns
    -------

    float or array-like
        Great circle distance between the two coordinates in arseconds.

    Notes
    -----

    If (`ra1`, `dec1`) is scalar and (`ra2`, `dec2`) is scalar: the result is a
    float distance in arcseconds.

    If (`ra1`, `dec1`) is scalar and (`ra2`, `dec2`) is array-like: the result
    is an np.array with distance in arcseconds between (`ra1`, `dec1`) and each
    element of (`ra2`, `dec2`).

    If (`ra1`, `dec1`) is array-like and (`ra2`, `dec2`) is scalar: the result
    is an np.array with distance in arcseconds between (`ra2`, `dec2`) and each
    element of (`ra1`, `dec1`).

    If (`ra1`, `dec1`) and (`ra2`, `dec2`) are both array-like: the result is an
    np.array with the pair-wise distance in arcseconds between each element of
    the two coordinate lists. In this case, if the input array-likes are not the
    same length, then excess elements of the longer one will be ignored.

    '''

    # wrap RA if negative or larger than 360.0 deg
    arg_4 = arg_0 % 360.0
    arg_4 = arg_4 + 360.0*(arg_4 < 0.0)
    arg_5 = arg_2 % 360.0
    arg_5 = arg_5 + 360.0*(arg_4 < 0.0)

    # convert to radians
    arg_6, arg_7 = np.deg2rad(arg_4), np.deg2rad(arg_1)
    arg_8, arg_9 = np.deg2rad(arg_5), np.deg2rad(arg_3)

    arg_10 = (arg_9 - arg_7)/2.0
    arg_11 = (arg_8 - arg_6)/2.0
    arg_12 = np.sqrt(np.sin(arg_10) * np.sin(arg_10) +
                       np.cos(arg_7) * np.cos(arg_9) *
                       np.sin(arg_11) * np.sin(arg_11))

    arg_13 = 2.0 * np.arcsin(arg_12)

    # return the distance in arcseconds
    return np.rad2deg(arg_13)*3600.0


def Func(arg_0, arg_1, arg_2):

    '''This calculates the total proper motion of an object.

    Parameters
    ----------

    pmra : float or array-like
        The proper motion(s) in right ascension, measured in mas/yr.

    pmdecl : float or array-like
        The proper motion(s) in declination, measured in mas/yr.

    decl : float or array-like
        The declination of the object(s) in decimal degrees.

    Returns
    -------

    float or array-like
        The total proper motion(s) of the object(s) in mas/yr.

    '''

    arg_3 = np.sqrt( arg_1*arg_1 + arg_0*arg_0*np.cos(np.radians(arg_2)) *
                  np.cos(np.radians(arg_2)) )
    return arg_3


def Func(arg_0, arg_1):
    '''This converts from galactic coords to equatorial coordinates.

    Parameters
    ----------

    gl : float or array-like
        Galactic longitude values(s) in decimal degrees.

    gb : float or array-like
        Galactic latitude value(s) in decimal degrees.

    Returns
    -------

    tuple of (float, float) or tuple of (np.array, np.array)
        The equatorial coordinates (RA, DEC) for each element of the input
        (`gl`, `gb`) in decimal degrees. These are reported in the ICRS frame.

    '''

    arg_2 = SkyCoord(arg_0*u.degree, arg_0*u.degree, frame='galactic')

    arg_3 = arg_2.transform_to('icrs')

    return arg_3.ra.degree, arg_3.dec.degree


def Func(arg_0, arg_1,
                      arg_2, arg_3,
                      arg_4=True):
    '''This returns the image-plane projected xi-eta coords for inra, indecl.

    Parameters
    ----------

    inra,indecl : array-like
        The equatorial coordinates to get the xi, eta coordinates for in decimal
        degrees or radians.

    incenterra,incenterdecl : float
        The center coordinate values to use to calculate the plane-projected
        coordinates around.

    deg : bool
        If this is True, the input angles are assumed to be in degrees and the
        output is in degrees as well.

    Returns
    -------

    tuple of np.arrays
        This is the (`xi`, `eta`) coordinate pairs corresponding to the
        image-plane projected coordinates for each pair of input equatorial
        coordinates in (`inra`, `indecl`).

    '''

    if arg_4:

        arg_5 = np.radians(arg_0)
        arg_6 = np.radians(arg_1)
        arg_7 = np.radians(arg_2)
        arg_8 = np.radians(arg_3)

    else:

        arg_5 = arg_0
        arg_6 = arg_1
        arg_7 = arg_2
        arg_8 = arg_3

    arg_9 = np.cos(arg_8)
    arg_10 = np.sin(arg_8)
    arg_11 = np.cos(arg_7)
    arg_12 = np.sin(arg_7)

    arg_13 = np.cos(arg_6)*np.cos(arg_5)
    arg_14 = np.cos(arg_6)*np.sin(arg_5)
    arg_15 = np.sin(arg_6)

    arg_16 = arg_13*arg_9*arg_11 + arg_14*arg_9*arg_12 + arg_15*arg_10
    arg_17 = -arg_13*arg_12 + arg_14*arg_11
    arg_18 = -arg_13*arg_10*arg_11 - arg_14*arg_10*arg_12 + arg_15*arg_9
    arg_19 = arg_17*arg_17 + arg_18*arg_18

    arg_20 = np.zeros_like(arg_16)
    arg_20[arg_16 >= 1.0] = 0.0
    arg_20[arg_16 < 1.0] = np.arccos(arg_16)

    arg_21, arg_22 = np.zeros_like(arg_20), np.zeros_like(arg_20)

    arg_21[(arg_20 <= 0.0) | (arg_19 <= 0.0)] = 0.0
    arg_22[(arg_20 <= 0.0) | (arg_19 <= 0.0)] = 0.0

    arg_23 = np.sqrt(arg_19)

    arg_21[(arg_20 > 0.0) | (arg_19 > 0.0)] = arg_20*arg_17/arg_23
    arg_22[(arg_20 > 0.0) | (arg_19 > 0.0)] = arg_20*arg_18/arg_23

    if arg_4:
        return np.degrees(arg_21), np.degrees(arg_22)
    else:
        return arg_21, arg_22


def Func(
        arg_0,
        arg_1=None,
        arg_2=None,
        arg_3={'transitperiod':arg_4.uniform(arg_6=0.1,arg_7=49.9),
                    'transitdepth':arg_4.uniform(arg_6=1.0e-4,arg_7=2.0e-2),
                    'transitduration':arg_4.uniform(arg_6=0.01,arg_7=0.29)},
        arg_8=False,
):
    '''This generates fake planet transit light curves.

    Parameters
    ----------

    times : np.array
        This is an array of time values that will be used as the time base.

    mags,errs : np.array
        These arrays will have the model added to them. If either is
        None, `np.full_like(times, 0.0)` will used as a substitute and the model
        light curve will be centered around 0.0.

    paramdists : dict
        This is a dict containing parameter distributions to use for the
        model params, containing the following keys ::

            {'transitperiod', 'transitdepth', 'transitduration'}

        The values of these keys should all be 'frozen' scipy.stats distribution
        objects, e.g.:

        https://docs.scipy.org/doc/scipy/reference/stats.html#continuous-distributions
        The variability epoch will be automatically chosen from a uniform
        distribution between `times.min()` and `times.max()`.

        The ingress duration will be automatically chosen from a uniform
        distribution ranging from 0.05 to 0.5 of the transitduration.

        The transitdepth will be flipped automatically as appropriate if
        `magsarefluxes=True`.

    magsarefluxes : bool
        If the generated time series is meant to be a flux time-series, set this
        to True to get the correct sign of variability amplitude.

    Returns
    -------

    dict
        A dict of the form below is returned::

            {'vartype': 'planet',
             'params': {'transitperiod': generated value of period,
                        'transitepoch': generated value of epoch,
                        'transitdepth': generated value of transit depth,
                        'transitduration': generated value of transit duration,
                        'ingressduration': generated value of transit ingress
                                           duration},
             'times': the model times,
             'mags': the model mags,
             'errs': the model errs,
             'varperiod': the generated period of variability == 'transitperiod'
             'varamplitude': the generated amplitude of
                             variability == 'transitdepth'}

    '''

    if arg_1 is None:
        arg_1 = np.full_like(arg_0, 0.0)

    if arg_2 is None:
        arg_2 = np.full_like(arg_0, 0.0)

    # choose the epoch
    arg_9 = npr.random()*(arg_0.max() - arg_0.min()) + arg_0.min()

    # choose the period, depth, duration
    arg_10 = arg_3['transitperiod'].rvs(size=1)
    arg_11 = arg_3['transitdepth'].rvs(size=1)
    arg_12 = arg_3['transitduration'].rvs(size=1)

    # figure out the ingress duration
    arg_13 = npr.random()*(0.5*arg_12 - 0.05*arg_12) + 0.05*arg_12

    # fix the transit depth if it needs to be flipped
    if arg_8 and arg_11 < 0.0:
        arg_11 = -arg_11
    elif not arg_8 and arg_11 > 0.0:
        arg_11 = -arg_11

    # generate the model
    arg_14, arg_15, arg_16, arg_17, arg_18 = (
        transits.trapezoid_transit_func([arg_10, arg_9, arg_11,
                                         arg_12, arg_13],
                                        arg_0,
                                        arg_1,
                                        arg_2)
    )

    # resort in original time order
    arg_19 = np.argsort(arg_16)
    arg_20 = arg_16[arg_19]
    arg_21 = arg_14[arg_19]
    arg_22 = arg_18[arg_19]

    # return a dict with everything
    arg_23 = {
        'vartype':'planet',
        'params':{x:np.asscalar(y) for x,y in zip(['transitperiod',
                                                   'transitepoch',
                                                   'transitdepth',
                                                   'transitduration',
                                                   'ingressduration'],
                                                  [arg_10,
                                                   arg_9,
                                                   arg_11,
                                                   arg_12,
                                                   arg_13])},
        'times':arg_20,
        'mags':arg_21,
        'errs':arg_22,
        # these are standard keys that help with later characterization of
        # variability as a function period, variability amplitude, object mag,
        # ndet, etc.
        'varperiod':arg_10,
        'varamplitude':arg_11
    }

    return arg_23


def Func(
        arg_0,
        arg_1=None,
        arg_2=None,
        arg_3={
            # flare peak amplitude from 0.01 mag to 1.0 mag above median.  this
            # is tuned for redder bands, flares are much stronger in bluer
            # bands, so tune appropriately for your situation.
            'amplitude':arg_4.uniform(arg_6=0.01,arg_7=0.99),
            # up to 5 flares per LC and at least 1
            'nflares':[1,5],
            # 10 minutes to 1 hour for rise stdev
            'risestdev':arg_4.uniform(arg_6=0.007, arg_7=0.04),
            # 1 hour to 4 hours for decay time constant
            'decayconst':arg_4.uniform(arg_6=0.04, arg_7=0.163)
        },
        arg_8=False,
):
    '''This generates fake flare light curves.

    Parameters
    ----------

    times : np.array
        This is an array of time values that will be used as the time base.

    mags,errs : np.array
        These arrays will have the model added to them. If either is
        None, `np.full_like(times, 0.0)` will used as a substitute and the model
        light curve will be centered around 0.0.

    paramdists : dict
        This is a dict containing parameter distributions to use for the
        model params, containing the following keys ::

            {'amplitude', 'nflares', 'risestdev', 'decayconst'}

        The values of these keys should all be 'frozen' scipy.stats distribution
        objects, e.g.:

        https://docs.scipy.org/doc/scipy/reference/stats.html#continuous-distributions
        The `flare_peak_time` for each flare will be generated automatically
        between `times.min()` and `times.max()` using a uniform distribution.

        The `amplitude` will be flipped automatically as appropriate if
        `magsarefluxes=True`.

    magsarefluxes : bool
        If the generated time series is meant to be a flux time-series, set this
        to True to get the correct sign of variability amplitude.

    Returns
    -------

    dict
        A dict of the form below is returned::

            {'vartype': 'flare',
             'params': {'amplitude': generated value of flare amplitudes,
                        'nflares': generated value of number of flares,
                        'risestdev': generated value of stdev of rise time,
                        'decayconst': generated value of decay constant,
                        'peaktime': generated value of flare peak time},
             'times': the model times,
             'mags': the model mags,
             'errs': the model errs,
             'varamplitude': the generated amplitude of
                             variability == 'amplitude'}

    '''

    if arg_1 is None:
        arg_1 = np.full_like(arg_0, 0.0)

    if arg_2 is None:
        arg_2 = np.full_like(arg_0, 0.0)

    arg_9 = npr.randint(arg_3['nflares'][0],
                          high=arg_3['nflares'][1])

    # generate random flare peak times based on the number of flares
    arg_10 = (
        npr.random(
            size=arg_9
        )*(arg_0.max() - arg_0.min()) + arg_0.min()
    )

    # now add the flares to the time-series
    arg_11 = {'nflares':arg_9}

    for arg_12, arg_13 in zip(range(arg_9), arg_10):

        # choose the amplitude, rise stdev and decay time constant
        arg_14 = arg_3['amplitude'].rvs(size=1)
        arg_15 = arg_3['risestdev'].rvs(size=1)
        arg_16 = arg_3['decayconst'].rvs(size=1)

        # fix the transit depth if it needs to be flipped
        if arg_8 and arg_14 < 0.0:
            arg_14 = -arg_14
        elif not arg_8 and arg_14 > 0.0:
            arg_14 = -arg_14

        # add this flare to the light curve
        arg_17, arg_18, arg_19, arg_20 = (
            flares.flare_model(
                [arg_14, arg_13, arg_15, arg_16],
                arg_0,
                arg_1,
                arg_2
            )
        )

        # update the mags
        arg_1 = arg_17

        # add the flare params to the modeldict
        arg_11[arg_12] = {'peaktime':arg_13,
                            'amplitude':arg_14,
                            'risestdev':arg_15,
                            'decayconst':arg_16}

    #
    # done with all flares
    #

    # return a dict with everything
    arg_21 = {
        'vartype':'flare',
        'params':arg_11,
        'times':arg_0,
        'mags':arg_1,
        'errs':arg_2,
        'varperiod':None,
        # FIXME: this is complicated because we can have multiple flares
        # figure out a good way to handle this upstream
        'varamplitude':[arg_11[x]['amplitude']
                        for x in range(arg_11['nflares'])],
    }

    return arg_21


def Func(arg_0):
    '''
    This wraps `process_fakelc` for `make_fakelc_collection` below.

    Parameters
    ----------

    task : tuple
        This is of the form::

            task[0] = lcfile
            task[1] = outdir
            task[2] = magrms
            task[3] = dict with keys: {'lcformat', 'timecols', 'magcols',
                                       'errcols', 'randomizeinfo'}

    Returns
    -------

    tuple
        This returns a tuple of the form::

            (fakelc_fpath,
             fakelc_lcdict['columns'],
             fakelc_lcdict['objectinfo'],
             fakelc_lcdict['moments'])
    '''

    arg_1, arg_2, arg_3 = arg_0

    try:

        arg_4 = make_fakelc(
            arg_1,
            arg_2,
            **arg_3
        )

        return arg_4

    except Exception as e:

        LOGEXCEPTION('could not process %s into a fakelc' % arg_1)
        return None


def Func(arg_0,
                                         arg_1=None,
                                         arg_2=False):
    '''This adds variability and noise to all fake LCs in `simbasedir`.

    If an object is marked as variable in the `fakelcs-info`.pkl file in
    `simbasedir`, a variable signal will be added to its light curve based on
    its selected type, default period and amplitude distribution, the
    appropriate params, etc. the epochs for each variable object will be chosen
    uniformly from its time-range (and may not necessarily fall on a actual
    observed time). Nonvariable objects will only have noise added as determined
    by their params, but no variable signal will be added.

    Parameters
    ----------

    simbasedir : str
        The directory containing the fake LCs to process.

    override_paramdists : dict
        This can be used to override the stored variable parameters in each fake
        LC. It should be a dict of the following form::

            {'<vartype1>': {'<param1>: a scipy.stats distribution function or
                                       the np.random.randint function,
                            .
                            .
                            .
                            '<paramN>: a scipy.stats distribution function
                                       or the np.random.randint function}

        for any vartype in VARTYPE_LCGEN_MAP. These are used to override the
        default parameter distributions for each variable type.

    overwrite_existingvar : bool
        If this is True, then will overwrite any existing variability in the
        input fake LCs in `simbasedir`.

    Returns
    -------

    dict
        This returns a dict containing the fake LC filenames as keys and
        variability info for each as values.

    '''

    # open the fakelcs-info.pkl
    arg_3 = os.path.join(arg_0,'fakelcs-info.pkl')
    with open(arg_3, 'rb') as infd:
        arg_4 = pickle.load(infd)

    arg_5 = arg_4['lcfpath']
    arg_6 = arg_4['isvariable']
    arg_7 = arg_4['vartype']

    arg_8 = 0

    arg_9 = {}

    # go through all the LCs and add the required type of variability
    for arg_10, arg_11, arg_12 in zip(arg_5, arg_6, range(len(arg_5))):

        # if this object is variable, add variability
        if arg_11:

            arg_13 = arg_7[arg_8]

            if (arg_1 and
                isinstance(arg_1, dict) and
                arg_13 in arg_1 and
                isinstance(arg_1[arg_13], dict)):

                arg_14 = arg_1[arg_13]
            else:
                arg_14 = None

            arg_15 = add_fakelc_variability(
                arg_10, arg_13,
                arg_1=arg_14,
                overwrite=arg_2
            )
            arg_9[arg_15['objectid']] = {'params': arg_15['actual_varparams'],
                                          'vartype': arg_15['actual_vartype']}

            # update vartind
            arg_8 = arg_8 + 1

        else:

            arg_15 = add_fakelc_variability(
                arg_10, None,
                overwrite=arg_2
            )
            arg_9[arg_15['objectid']] = {'params': arg_15['actual_varparams'],
                                          'vartype': arg_15['actual_vartype']}

    #
    # done with all objects
    #

    # write the varinfo back to the dict and fakelcs-info.pkl
    arg_4['varinfo'] = arg_9

    arg_16 = '%s.%s' % (arg_3, md5(npr.bytes(4)).hexdigest()[-8:])
    with open(arg_16, 'wb') as outfd:
        pickle.dump(arg_4, outfd, pickle.HIGHEST_PROTOCOL)

    if os.path.exists(arg_16):
        shutil.copy(arg_16, arg_3)
        os.remove(arg_16)
    else:
        LOGEXCEPTION('could not write output light curve file to dir: %s' %
                     os.path.dirname(arg_16))
        # fail here
        raise

    return arg_4


def Func(arg_0, arg_1, arg_2,
                      arg_3=97,
                      arg_4=4.0,
                      arg_5=1,
                      arg_6=3,
                      arg_7=False,
                      arg_8=2,
                      **arg_9):
    '''This finds flares in time series using the method in Walkowicz+ 2011.

    FIXME: finish this.

    Parameters
    ----------

    times,mags,errs : np.array
        The input time-series to find flares in.

    smoothbinsize : int
        The number of consecutive light curve points to smooth over in the time
        series using a Savitsky-Golay filter. The smoothed light curve is then
        subtracted from the actual light curve to remove trends that potentially
        last `smoothbinsize` light curve points. The default value is chosen as
        ~6.5 hours (97 x 4 minute cadence for HATNet/HATSouth).

    flare_minsigma : float
        The minimum sigma above the median LC level to designate points as
        belonging to possible flares.

    flare_maxcadencediff : int
        The maximum number of light curve points apart each possible flare event
        measurement is allowed to be. If this is 1, then we'll look for
        consecutive measurements.

    flare_mincadencepoints : int
        The minimum number of light curve points (each `flare_maxcadencediff`
        points apart) required that are at least `flare_minsigma` above the
        median light curve level to call an event a flare.

    magsarefluxes: bool
        If True, indicates that mags is actually an array of fluxes.

    savgol_polyorder: int
        The polynomial order of the function used by the Savitsky-Golay filter.

    savgol_kwargs : extra kwargs
        Any remaining keyword arguments are passed directly to the
        `savgol_filter` function from `scipy.signal`.

    Returns
    -------

    (nflares, flare_indices) : tuple
        Returns the total number of flares found and their time-indices (start,
        end) as tuples.

    '''

    # if no errs are given, assume 0.1% errors
    if arg_2 is None:
        arg_2 = 0.001*arg_1

    # get rid of nans first
    arg_10 = np.isfinite(arg_0) & np.isfinite(arg_1) & np.isfinite(arg_2)
    arg_11 = arg_0[arg_10]
    arg_12 = arg_1[arg_10]
    arg_13 = arg_2[arg_10]

    # now get the smoothed mag series using the filter
    # kwargs are provided to the savgol_filter function
    arg_14 = savgol_filter(arg_12,
                             arg_3,
                             arg_8,
                             **arg_9)
    arg_15 = arg_12 - arg_14

    # calculate some stats
    # the series_median is ~zero after subtraction
    arg_16 = np.median(np.abs(arg_15))
    arg_17 = 1.483*arg_16

    # find extreme positive deviations
    if arg_7:
        arg_18 = np.where(arg_15 > (arg_4*arg_17))
    else:
        arg_18 = np.where(arg_15 < (-arg_4*arg_17))

    # see if there are any extrema
    if arg_18 and arg_18[0]:

        arg_19 = arg_18[0]
        arg_20 = []

        # find the deviations within the requested flaremaxcadencediff
        for arg_21, arg_22 in enumerate(arg_19):
            # FIXME: finish this
            pass


def Func(arg_0, arg_1, arg_2=20, arg_3=1):
    '''This calculates the relative peak heights for first npeaks in ACF.

    Usually, the first peak or the second peak (if its peak height > first peak)
    corresponds to the correct lag. When we know the correct lag, the period is
    then::

        bestperiod = time[lags == bestlag] - time[0]

    Parameters
    ----------

    lags : np.array
        An array of lags that the ACF is calculated at.

    acf : np.array
        The array containing the ACF values.

    npeaks : int
        THe maximum number of peaks to consider when finding peak heights.

    searchinterval : int
        From `scipy.signal.argrelmax`: "How many points on each side to use for
        the comparison to consider comparator(n, n+x) to be True." This
        effectively sets how many points on each of the current peak will be
        used to check if the current peak is the local maximum.

    Returns
    -------

    dict
        This returns a dict of the following form::

            {'maxinds':the indices of the lag array where maxes are,
             'maxacfs':the ACF values at each max,
             'maxlags':the lag values at each max,
             'mininds':the indices of the lag array where mins are,
             'minacfs':the ACF values at each min,
             'minlags':the lag values at each min,
             'relpeakheights':the relative peak heights of each rel. ACF peak,
             'relpeaklags':the lags at each rel. ACF peak found,
             'peakindices':the indices of arrays where each rel. ACF peak is,
             'bestlag':the lag value with the largest rel. ACF peak height,
             'bestpeakheight':the largest rel. ACF peak height,
             'bestpeakindex':the largest rel. ACF peak's number in all peaks}

    '''

    arg_4 = argrelmax(arg_1, order=arg_3)[0]
    arg_5 = arg_1[arg_4]
    arg_6 = arg_0[arg_4]
    arg_7 = argrelmin(arg_1, order=arg_3)[0]
    arg_8 = arg_1[arg_7]
    arg_9 = arg_0[arg_7]

    arg_10 = npzeros(arg_2)
    arg_11 = npzeros(arg_2,dtype=npint64)
    arg_12 = npzeros(arg_2,dtype=npint64)

    for arg_13, arg_14 in enumerate(arg_4[:arg_2]):

        # check if there are no mins to the left
        # throw away this peak because it's probably spurious
        # (FIXME: is this OK?)
        if npall(arg_14 < arg_7):
            continue

        arg_15 = arg_7[arg_7 < arg_14][-1]  # the last index to the left
        arg_16 = arg_7[arg_7 > arg_14][0]  # the first index to the right
        arg_10[arg_13] = (
            arg_1[arg_14] - (arg_1[arg_15] + arg_1[arg_16])/2.0
        )
        arg_11[arg_13] = arg_0[arg_14]
        arg_12[arg_13] = arg_13

    # figure out the bestperiod if possible
    if arg_10[0] > arg_10[1]:
        arg_17 = arg_11[0]
        arg_18 = arg_10[0]
        arg_19 = arg_12[0]
    else:
        arg_17 = arg_11[1]
        arg_18 = arg_10[1]
        arg_19 = arg_12[1]

    return {'maxinds':arg_4,
            'maxacfs':arg_5,
            'maxlags':arg_6,
            'mininds':arg_7,
            'minacfs':arg_8,
            'minlags':arg_9,
            'relpeakheights':arg_10,
            'relpeaklags':arg_11,
            'peakindices':arg_12,
            'bestlag':arg_17,
            'bestpeakheight':arg_18,
            'bestpeakindex':arg_19}


def Func(arg_0, arg_1, arg_2, arg_3, arg_4):
    '''
    This is yet another alternative to calculate the autocorrelation.

    Taken from: `Bayesian Methods for Hackers by Cameron Pilon <http://nbviewer.jupyter.org/github/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers/blob/master/Chapter3_MCMC/Chapter3.ipynb#Autocorrelation>`_

    (This should be the fastest method to calculate ACFs.)

    Parameters
    ----------

    mags : np.array
        This is the magnitudes array. MUST NOT have any nans.

    lag : float
        The specific lag value to calculate the auto-correlation for. This MUST
        be less than total number of observations in `mags`.

    maglen : int
        The number of elements in the `mags` array.

    magmed : float
        The median of the `mags` array.

    magstd : float
        The standard deviation of the `mags` array.

    Returns
    -------

    float
        The auto-correlation at this specific `lag` value.

    '''

    # from http://tinyurl.com/afz57c4
    arg_5 = npcorrelate(arg_0, arg_0, mode='full')
    arg_5 = arg_5 / npmax(arg_5)

    return arg_5[int(arg_5.size / 2):]


def Func(arg_0, arg_1, arg_2,
                       arg_3=1000,
                       arg_4=arg_5,
                       arg_6=0.0,
                       arg_7=11,
                       arg_8=None,
                       arg_9=3.0,
                       arg_10=False,
                       arg_11=True):
    '''This calculates the ACF of a light curve.

    This will pre-process the light curve to fill in all the gaps and normalize
    everything to zero. If `fillgaps = 'noiselevel'`, fills the gaps with the
    noise level obtained via the procedure above. If `fillgaps = 'nan'`, fills
    the gaps with `np.nan`.

    Parameters
    ----------

    times,mags,errs : np.array
        The measurement time-series and associated errors.

    maxlags : int
        The maximum number of lags to calculate.

    func : Python function
        This is a function to calculate the lags.

    fillgaps : 'noiselevel' or float
        This sets what to use to fill in gaps in the time series. If this is
        'noiselevel', will smooth the light curve using a point window size of
        `filterwindow` (this should be an odd integer), subtract the smoothed LC
        from the actual LC and estimate the RMS. This RMS will be used to fill
        in the gaps. Other useful values here are 0.0, and npnan.

    filterwindow : int
        The light curve's smoothing filter window size to use if
        `fillgaps='noiselevel`'.

    forcetimebin : None or float
        This is used to force a particular cadence in the light curve other than
        the automatically determined cadence. This effectively rebins the light
        curve to this cadence. This should be in the same time units as `times`.

    sigclip : float or int or sequence of two floats/ints or None
        If a single float or int, a symmetric sigma-clip will be performed using
        the number provided as the sigma-multiplier to cut out from the input
        time-series.

        If a list of two ints/floats is provided, the function will perform an
        'asymmetric' sigma-clip. The first element in this list is the sigma
        value to use for fainter flux/mag values; the second element in this
        list is the sigma value to use for brighter flux/mag values. For
        example, `sigclip=[10., 3.]`, will sigclip out greater than 10-sigma
        dimmings and greater than 3-sigma brightenings. Here the meaning of
        "dimming" and "brightening" is set by *physics* (not the magnitude
        system), which is why the `magsarefluxes` kwarg must be correctly set.

        If `sigclip` is None, no sigma-clipping will be performed, and the
        time-series (with non-finite elems removed) will be passed through to
        the output.

    magsarefluxes : bool
        If your input measurements in `mags` are actually fluxes instead of
        mags, set this is True.

    verbose : bool
        If True, will indicate progress and report errors.

    Returns
    -------

    dict
        A dict of the following form is returned::

            {'itimes': the interpolated time values after gap-filling,
             'imags': the interpolated mag/flux values after gap-filling,
             'ierrs': the interpolated mag/flux values after gap-filling,
             'cadence': the cadence of the output mag/flux time-series,
             'minitime': the minimum value of the interpolated times array,
             'lags': the lags used to calculate the auto-correlation function,
             'acf': the value of the ACF at each lag used}

    '''

    # get the gap-filled timeseries
    arg_12 = fill_magseries_gaps(arg_0, arg_1, arg_2,
                                       arg_6=arg_6,
                                       arg_8=arg_8,
                                       arg_9=arg_9,
                                       arg_10=arg_10,
                                       arg_7=arg_7,
                                       arg_11=arg_11)

    if not arg_12:
        print('failed to interpolate light curve to minimum cadence!')
        return None

    arg_13, arg_14 = arg_12['itimes'], arg_12['imags'],

    # calculate the lags up to maxlags
    if arg_3:
        arg_15 = nparange(0, arg_3)
    else:
        arg_15 = nparange(arg_13.size)

    arg_16 = 1.483*npmedian(npabs(arg_14))

    if arg_4 != arg_5:

        # get the autocorrelation as a function of the lag of the mag series
        arg_17 = nparray([arg_4(arg_14, x, arg_14.size, 0.0, arg_16)
                            for x in arg_15])

    # this doesn't need a lags array
    else:

        arg_17 = arg_5(arg_14, arg_15[0], arg_14.size,
                                   0.0, arg_16)
        # return only the maximum number of lags
        if arg_3 is not None:
            arg_17 = arg_17[:arg_3]

    arg_12.update({'minitime':arg_13.min(),
                         'lags':arg_15,
                         'acf':arg_17})

    return arg_12


def Func(arg_0, arg_1, arg_2, arg_3,
                arg_4, arg_5):
    '''This calculates the harmonic AoV theta statistic for a frequency.

    This is a mostly faithful translation of the inner loop in `aovper.f90`. See
    the following for details:

    - http://users.camk.edu.pl/alex/
    - Schwarzenberg-Czerny (`1996
      <http://iopscience.iop.org/article/10.1086/309985/meta>`_)

    Schwarzenberg-Czerny (1996) equation 11::

        theta_prefactor = (K - 2N - 1)/(2N)
        theta_top = sum(c_n*c_n) (from n=0 to n=2N)
        theta_bot = variance(timeseries) - sum(c_n*c_n) (from n=0 to n=2N)

        theta = theta_prefactor * (theta_top/theta_bot)

        N = number of harmonics (nharmonics)
        K = length of time series (times.size)

    Parameters
    ----------

    times,mags,errs : np.array
        The input time-series to calculate the test statistic for. These should
        all be of nans/infs and be normalized to zero.

    frequency : float
        The test frequency to calculate the statistic for.

    nharmonics : int
        The number of harmonics to calculate up to.The recommended range is 4 to
        8.

    magvariance : float
        This is the (weighted by errors) variance of the magnitude time
        series. We provide it as a pre-calculated value here so we don't have to
        re-calculate it for every worker.

    Returns
    -------

    aov_harmonic_theta : float
        THe value of the harmonic AoV theta for the specified test `frequency`.

    '''

    arg_6 = 1.0/arg_3

    arg_7 = arg_0.size
    arg_8 = arg_4 + arg_4

    # phase with test period
    arg_9 = phase_magseries_with_errs(
        arg_0, arg_1, arg_2, arg_6, arg_0[0],
        sort=True, wrap=False
    )

    # get the phased quantities
    arg_10 = arg_9['phase']
    arg_11 = arg_9['mags']
    arg_12 = arg_9['errs']

    # this is sqrt(1.0/errs^2) -> the weights
    arg_13 = 1.0/arg_12

    # multiply by 2.0*PI (for omega*time)
    arg_10 = arg_10 * 2.0 * pi_value

    # this is the z complex vector
    arg_14 = npcos(arg_10) + 1.0j*npsin(arg_10)

    # multiply phase with N
    arg_10 = arg_4 * arg_10

    # this is the psi complex vector
    arg_15 = arg_11 * arg_13 * (npcos(arg_10) + 1j*npsin(arg_10))

    # this is the initial value of z^n
    arg_16 = 1.0 + 0.0j

    # this is the initial value of phi
    arg_17 = arg_13 + 0.0j

    # initialize theta to zero
    arg_18 = 0.0

    # go through all the harmonics now up to 2N
    for arg_19 in range(arg_8):

        # this is <phi, phi>
        arg_20 = npsum(arg_17 * arg_17.conjugate())

        # this is the alpha_n numerator
        arg_21 = npsum(arg_13 * arg_14 * arg_17)

        # this is <phi, psi>. make sure to use npvdot and NOT npdot to get
        # complex conjugate of first vector as expected for complex vectors
        arg_22 = npvdot(arg_17, arg_15)

        # make sure phi_dot_phi is not zero
        arg_20 = npmax([arg_20, 10.0e-9])

        # this is the expression for alpha_n
        arg_21 = arg_21 / arg_20

        # update theta_aov for this harmonic
        arg_18 = (arg_18 +
                     npabs(arg_22) * npabs(arg_22) / arg_20)

        # use the recurrence relation to find the next phi
        arg_17 = arg_17 * arg_14 - arg_21 * arg_16 * arg_17.conjugate()

        # update z^n
        arg_16 = arg_16 * arg_14

    # done with all harmonics, calculate the theta_aov for this freq
    # the max below makes sure that magvariance - theta_aov > zero
    arg_18 = ( (arg_7 - arg_8 - 1.0) * arg_18 /
                  (arg_8 * npmax([arg_5 - arg_18,
                                           1.0e-9])) )

    return arg_18


def Func(arg_0, arg_1, arg_2, arg_3, arg_4):
        '''This Funcs a new database connection.

        Parameters
        ----------

        database : str
            Name of the database to connect to.

        user : str
            User name of the database server user.

        password : str
            Password for the database server user.

        host : str
            Database hostname or IP address to connect to.

        '''

        try:

            arg_0.connection = pg.connect(arg_2=arg_2,
                                         arg_3=arg_3,
                                         arg_1=arg_1,
                                         arg_4=arg_4)

            LOGINFO('postgres connection successfully '
                    'created, using DB %s, user %s' % (arg_1,
                                                       arg_2))

            arg_0.database = arg_1
            arg_0.user = arg_2

        except Exception as e:

            LOGEXCEPTION('postgres connection failed, '
                         'using DB %s, user %s' % (arg_1,
                                                   arg_2))

            arg_0.database = None
            arg_0.user = None


def Func(arg_0,
                                    arg_1,
                                    arg_2=2.0,
                                    arg_3=True,
                                    arg_4=None):
    '''This xmatches external catalogs to a collection of checkplots.

    Parameters
    ----------

    cplist : list of str
        This is the list of checkplot pickle files to process.

    xmatchpkl : str
        The filename of a pickle prepared beforehand with the
        `checkplot.pkl_xmatch.load_xmatch_external_catalogs` function,
        containing collected external catalogs to cross-match the objects in the
        input `cplist` against.

    xmatchradiusarcsec : float
        The match radius to use for the cross-match in arcseconds.

    updateexisting : bool
        If this is True, will only update the `xmatch` dict in each checkplot
        pickle with any new cross-matches to the external catalogs. If False,
        will overwrite the `xmatch` dict with results from the current run.

    resultstodir : str or None
        If this is provided, then it must be a directory to write the resulting
        checkplots to after xmatch is done. This can be used to keep the
        original checkplots in pristine condition for some reason.

    Returns
    -------

    dict
        Returns a dict with keys = input checkplot pickle filenames and vals =
        xmatch status dict for each checkplot pickle.

    '''

    # load the external catalog
    with open(arg_1,'rb') as infd:
        arg_5 = pickle.load(infd)

    # match each object. this is fairly fast, so this is not parallelized at the
    # moment

    arg_6 = {}

    for arg_7 in arg_0:

        arg_8 = _read_checkplot_picklefile(arg_7)

        try:

            # match in place
            xmatch_external_catalogs(arg_8, arg_5,
                                     arg_2=arg_2,
                                     updatexmatch=arg_3)

            for arg_9 in arg_8['xmatch']:

                if arg_8['xmatch'][arg_9]['found']:
                    LOGINFO('checkplot %s: %s matched to %s, '
                            'match dist: %s arcsec' %
                            (os.path.basename(arg_7),
                             arg_8['objectid'],
                             arg_8['xmatch'][arg_9]['name'],
                             arg_8['xmatch'][arg_9]['distarcsec']))

                if not arg_4:
                    arg_10 = _write_checkplot_picklefile(arg_8,
                                                         outfile=arg_7)
                else:
                    arg_11 = os.path.join(arg_4, os.path.basename(arg_7))
                    arg_10 = _write_checkplot_picklefile(arg_8,
                                                         outfile=arg_11)

            arg_6[arg_7] = arg_10

        except Exception as e:

            LOGEXCEPTION('failed to match objects for %s' % arg_7)
            arg_6[arg_7] = None

    return arg_6


def Func(arg_0,
                                   arg_1,
                                   arg_2='checkplot-*.pkl*',
                                   arg_3=2.0,
                                   arg_4=True,
                                   arg_5=None):
    '''This xmatches external catalogs to all checkplots in a directory.

    Parameters
    -----------

    cpdir : str
        This is the directory to search in for checkplots.

    xmatchpkl : str
        The filename of a pickle prepared beforehand with the
        `checkplot.pkl_xmatch.load_xmatch_external_catalogs` function,
        containing collected external catalogs to cross-match the objects in the
        input `cplist` against.

    cpfileglob : str
        This is the UNIX fileglob to use in searching for checkplots.

    xmatchradiusarcsec : float
        The match radius to use for the cross-match in arcseconds.

    updateexisting : bool
        If this is True, will only update the `xmatch` dict in each checkplot
        pickle with any new cross-matches to the external catalogs. If False,
        will overwrite the `xmatch` dict with results from the current run.

    resultstodir : str or None
        If this is provided, then it must be a directory to write the resulting
        checkplots to after xmatch is done. This can be used to keep the
        original checkplots in pristine condition for some reason.

    Returns
    -------

    dict
        Returns a dict with keys = input checkplot pickle filenames and vals =
        xmatch status dict for each checkplot pickle.

    '''

    arg_6 = glob.glob(os.path.join(arg_0, arg_2))

    return xmatch_cplist_external_catalogs(
        arg_6,
        arg_1,
        arg_3=arg_3,
        arg_4=arg_4,
        arg_5=arg_5
    )