def Func(arg_0,
                  arg_1,
                  arg_2=3.0,
                  arg_3='unlisted',
                  arg_4=False,
                  arg_5=None,
                  arg_6=None,
                  arg_7=None,
                  arg_8=None,
                  arg_9=None,
                  arg_10=None,
                  arg_11=True,
                  arg_12=None,
                  arg_13=300.0,
                  arg_14=15.0):

    '''This runs a cross-match search query.

    Parameters
    ----------

    lcc_server : str
        This is the base URL of the LCC-Server to talk to.  (e.g. for HAT, use:
        https://data.hatsurveys.org)

    file_to_upload : str
        This is the path to a text file containing objectid, RA, declination
        rows for the objects to cross-match against the LCC-Server
        collections. This should follow the format of the following example::

            # example object and coordinate list
            # objectid ra dec
            aaa 289.99698 44.99839
            bbb 293.358 -23.206
            ccc 294.197 +23.181
            ddd 19 25 27.9129 +42 47 03.693
            eee 19:25:27 -42:47:03.21
            # .
            # .
            # .
            # etc. lines starting with '#' will be ignored
            # (max 5000 objects)

    xmatch_dist_arcsec : float
        This is the maximum distance in arcseconds to consider when
        cross-matching objects in the uploaded file to the LCC-Server's
        collections. The maximum allowed distance is 30 arcseconds. Multiple
        matches to an uploaded object are possible and will be returned in order
        of increasing distance grouped by input `objectid`.

    result_visibility : {'private', 'unlisted', 'public'}
        This sets the visibility of the dataset produced from the search
        result::

               'private' -> the dataset and its products are not visible or
                            accessible by any user other than the one that
                            created the dataset.

               'unlisted' -> the dataset and its products are not visible in the
                             list of public datasets, but can be accessed if the
                             dataset URL is known

               'public' -> the dataset and its products are visible in the list
                           of public datasets and can be accessed by anyone.

    email_when_done : bool
        If True, the LCC-Server will email you when the search is complete. This
        will also set `download_data` to False. Using this requires an
        LCC-Server account and an API key tied to that account.

    collections : list of str or None
        This is a list of LC collections to search in. If this is None, all
        collections will be searched.

    columns : list of str or None
        This is a list of columns to return in the results. Matching objects'
        object IDs, RAs, DECs, and links to light curve files will always be
        returned so there is no need to specify these columns. If None, only
        these columns will be returned: 'objectid', 'ra', 'decl', 'lcfname'

    filters : str or None
        This is an SQL-like string to use to filter on database columns in the
        LCC-Server's collections. To see the columns available for a search,
        visit the Collections tab in the LCC-Server's browser UI. The filter
        operators allowed are::

            lt      -> less than
            gt      -> greater than
            ge      -> greater than or equal to
            le      -> less than or equal to
            eq      -> equal to
            ne      -> not equal to
            ct      -> contains text
            isnull  -> column value is null
            notnull -> column value is not null

        You may use the `and` and `or` operators between filter specifications
        to chain them together logically.

        Example filter strings::

            "(propermotion gt 200.0) and (sdssr lt 11.0)"
            "(dered_jmag_kmag gt 2.0) and (aep_000_stetsonj gt 10.0)"
            "(gaia_status ct 'ok') and (propermotion gt 300.0)"
            "(simbad_best_objtype ct 'RR') and (dered_sdssu_sdssg lt 0.5)"

    sortspec : tuple of two strs or None
        If not None, this should be a tuple of two items::

            ('column to sort by', 'asc|desc')

        This sets the column to sort the results by. For cone_search, the
        default column and sort order are 'dist_arcsec' and 'asc', meaning the
        distance from the search center in ascending order.

    samplespec : int or None
        If this is an int, will indicate how many rows from the initial search
        result will be uniformly random sampled and returned.

    limitspec : int or None
        If this is an int, will indicate how many rows from the initial search
        result to return in total.

        `sortspec`, `samplespec`, and `limitspec` are applied in this order:

            sample -> sort -> limit

    download_data : bool
        This sets if the accompanying data from the search results will be
        downloaded automatically. This includes the data table CSV, the dataset
        pickle file, and a light curve ZIP file. Note that if the search service
        indicates that your query is still in progress, this function will block
        until the light curve ZIP file becomes available. The maximum wait time
        in seconds is set by maxtimeout and the refresh interval is set by
        refresh.

        To avoid the wait block, set download_data to False and the function
        will write a pickle file to `~/.astrobase/lccs/query-[setid].pkl`
        containing all the information necessary to retrieve these data files
        later when the query is done. To do so, call the
        `retrieve_dataset_files` with the path to this pickle file (it will be
        returned).

    outdir : str or None
        If this is provided, sets the output directory of the downloaded dataset
        files. If None, they will be downloaded to the current directory.

    maxtimeout : float
        The maximum time in seconds to wait for the LCC-Server to respond with a
        result before timing out. You can use the `retrieve_dataset_files`
        function to get results later as needed.

    refresh : float
        The time to wait in seconds before pinging the LCC-Server to see if a
        search query has completed and dataset result files can be downloaded.

    Returns
    -------

    tuple
        Returns a tuple with the following elements::

            (search result status dict,
             search result CSV file path,
             search result LC ZIP path)

    '''

    with open(arg_1) as infd:
        arg_15 = infd.read()

    # check the number of lines in the input
    arg_16 = len(arg_15.split('\n')[:-1])

    if arg_16 > 5000:

        LOGERROR('you have more than 5000 lines in the file to upload: %s' %
                 arg_1)
        return None, None, None

    # turn the input into a param dict
    arg_17 = {'xmq':arg_15,
              'xmd':arg_2}

    if arg_5:
        arg_17['collections'] = arg_5
    if arg_6:
        arg_17['columns'] = arg_6
    if arg_7:
        arg_17['filters'] = arg_7

    if arg_8:
        arg_17['sortspec'] = json.dumps([arg_8])
    if arg_10:
        arg_17['samplespec'] = int(arg_10)
    if arg_9:
        arg_17['limitspec'] = int(arg_9)

    arg_17['visibility'] = arg_3
    arg_17['emailwhendone'] = arg_4

    # we won't wait for the LC ZIP to complete if email_when_done = True
    if arg_4:
        arg_11 = False

    # check if we have an API key already
    arg_18, arg_19, arg_20 = check_existing_apikey(arg_0)

    # if not, get a new one
    if not arg_18:
        arg_19, arg_20 = get_new_apikey(arg_0)

    # hit the server
    arg_21 = '%s/api/xmatch' % arg_0

    arg_22 = submit_post_searchquery(arg_21, arg_17, arg_19)

    # check the status of the search
    arg_23 = arg_22[0]

    # now we'll check if we want to download the data
    if arg_11:

        if arg_23 == 'ok':

            LOGINFO('query complete, downloading associated data...')
            arg_24, arg_25, arg_26 = retrieve_dataset_files(arg_22,
                                                     arg_12=arg_12,
                                                     arg_19=arg_19)

            if arg_26:
                return arg_22[1], arg_24, arg_25, arg_26
            else:
                return arg_22[1], arg_24, arg_25

        elif arg_23 == 'background':

            LOGINFO('query is not yet complete, '
                    'waiting up to %.1f minutes, '
                    'updates every %s seconds (hit Ctrl+C to cancel)...' %
                    (arg_13/60.0, arg_14))

            arg_27 = 0.0

            while arg_27 < arg_13:

                try:

                    time.sleep(arg_14)
                    arg_24, arg_25, arg_26 = retrieve_dataset_files(arg_22,
                                                             arg_12=arg_12,
                                                             arg_19=arg_19)

                    if (arg_24 and os.path.exists(arg_24) and
                        arg_25 and os.path.exists(arg_25)):

                        LOGINFO('all dataset products collected')
                        return arg_22[1], arg_24, arg_25

                    arg_27 = arg_27 + arg_14

                except KeyboardInterrupt:

                    LOGWARNING('abandoned wait for downloading data')
                    return arg_22[1], None, None

            LOGERROR('wait timed out.')
            return arg_22[1], None, None

        else:

            LOGERROR('could not download the data for this query result')
            return arg_22[1], None, None

    else:

        return arg_22[1], None, None


def Func(arg_0,
                arg_1,
                arg_2=False,
                arg_3=1):
    '''This downloads a JSON form of a dataset from the specified lcc_server.

    If the dataset contains more than 1000 rows, it will be paginated, so you
    must use the `page` kwarg to get the page you want. The dataset JSON will
    contain the keys 'npages', 'currpage', and 'rows_per_page' to help with
    this. The 'rows' key contains the actual data rows as a list of tuples.

    The JSON contains metadata about the query that produced the dataset,
    information about the data table's columns, and links to download the
    dataset's products including the light curve ZIP and the dataset CSV.

    Parameters
    ----------

    lcc_server : str
        This is the base URL of the LCC-Server to talk to.

    dataset_id : str
        This is the unique setid of the dataset you want to get. In the results
        from the `*_search` functions above, this is the value of the
        `infodict['result']['setid']` key in the first item (the infodict) in
        the returned tuple.

    strformat : bool
        This sets if you want the returned data rows to be formatted in their
        string representations already. This can be useful if you're piping the
        returned JSON straight into some sort of UI and you don't want to deal
        with formatting floats, etc. To do this manually when strformat is set
        to False, look at the `coldesc` item in the returned dict, which gives
        the Python and Numpy string format specifiers for each column in the
        data table.

    page : int
        This sets which page of the dataset should be retrieved.

    Returns
    -------

    dict
        This returns the dataset JSON loaded into a dict.

    '''

    arg_4 = {'strformat':1 if arg_2 else 0,
                 'page':arg_3,
                 'json':1}
    arg_5 = urlencode(arg_4)

    arg_6 = '%s/set/%s?%s' % (arg_0, arg_1, arg_5)

    LOGINFO('retrieving dataset %s from %s, using URL: %s ...' % (arg_0,
                                                                  arg_1,
                                                                  arg_6))

    try:

        # check if we have an API key already
        arg_7, arg_8, arg_9 = check_existing_apikey(arg_0)

        # if not, get a new one
        if not arg_7:
            arg_8, arg_9 = get_new_apikey(arg_0)

        # if apikey is not None, add it in as an Authorization: Bearer [apikey]
        # header
        if arg_8:
            arg_10 = {'Authorization':'Bearer: %s' % arg_8}
        else:
            arg_10 = {}

        # hit the server
        arg_11 = Request(arg_6, data=None, arg_10=arg_10)
        arg_12 = urlopen(arg_11)
        arg_13 = json.loads(arg_12.read())
        return arg_13

    except Exception as e:

        LOGEXCEPTION('could not retrieve the dataset JSON!')
        return None


def Func(arg_0, arg_1, arg_2):
    '''This gets information on a single object from the LCC-Server.

    Returns a dict with all of the available information on an object, including
    finding charts, comments, object type and variability tags, and
    period-search results (if available).

    If you have an LCC-Server API key present in `~/.astrobase/lccs/` that is
    associated with an LCC-Server user account, objects that are visible to this
    user will be returned, even if they are not visible to the public. Use this
    to look up objects that have been marked as 'private' or 'shared'.

    NOTE: you can pass the result dict returned by this function directly into
    the `astrobase.checkplot.checkplot_pickle_to_png` function, e.g.::

        astrobase.checkplot.checkplot_pickle_to_png(result_dict,
                                                    'object-%s-info.png' %
                                                    result_dict['objectid'])

    to generate a quick PNG overview of the object information.

    Parameters
    ----------

    lcc_server : str
        This is the base URL of the LCC-Server to talk to.

    objectid : str
        This is the unique database ID of the object to retrieve info for. This
        is always returned as the `db_oid` column in LCC-Server search results.

    db_collection_id : str
        This is the collection ID which will be searched for the object. This is
        always returned as the `collection` column in LCC-Server search results.

    Returns
    -------

    dict
        A dict containing the object info is returned. Some important items in
        the result dict:

        - `objectinfo`: all object magnitude, color, GAIA cross-match, and
          object type information available for this object

        - `objectcomments`: comments on the object's variability if available

        - `varinfo`: variability comments, variability features, type tags,
          period and epoch information if available

        - `neighbors`: information on the neighboring objects of this object in
          its parent light curve collection

        - `xmatch`: information on any cross-matches to external catalogs
          (e.g. KIC, EPIC, TIC, APOGEE, etc.)

        - `finderchart`: a base-64 encoded PNG image of the object's DSS2 RED
          finder chart. To convert this to an actual PNG, try the function:
          `astrobase.checkplot.pkl_io._b64_to_file`.

        - `magseries`: a base-64 encoded PNG image of the object's light
          curve. To convert this to an actual PNG, try the function:
          `astrobase.checkplot.pkl_io._b64_to_file`.

        - `pfmethods`: a list of period-finding methods applied to the object if
          any. If this list is present, use the keys in it to get to the actual
          period-finding results for each method. These will contain base-64
          encoded PNGs of the periodogram and phased light curves using the best
          three peaks in the periodogram, as well as period and epoch
          information.

    '''

    arg_3 = {
        'objectid':arg_1,
        'collection':arg_2
    }

    arg_4 = urlencode(arg_3)
    arg_5 = '%s/api/object?%s' % (arg_0, arg_4)

    try:

        LOGINFO(
            'getting info for %s in collection %s from %s' % (
                arg_1,
                arg_2,
                arg_0
            )
        )

        # check if we have an API key already
        arg_6, arg_7, arg_8 = check_existing_apikey(arg_0)

        # if not, get a new one
        if not arg_6:
            arg_7, arg_8 = get_new_apikey(arg_0)

        # if apikey is not None, add it in as an Authorization: Bearer [apikey]
        # header
        if arg_7:
            arg_9 = {'Authorization':'Bearer: %s' % arg_7}
        else:
            arg_9 = {}

        # hit the server
        arg_10 = Request(arg_5, data=None, arg_9=arg_9)
        arg_11 = urlopen(arg_10)
        arg_12 = json.loads(arg_11.read())['result']
        return arg_12

    except HTTPError as e:

        if e.code == 404:

            LOGERROR(
                'additional info for object %s not '
                'found in collection: %s' % (arg_1,
                                             arg_2)
            )

        else:

            LOGERROR('could not retrieve object info, '
                     'URL used: %s, error code: %s, reason: %s' %
                     (arg_5, e.code, e.reason))

        return None


def Func(arg_0, arg_1=25):
    '''This lists recent publicly visible datasets available on the LCC-Server.

    If you have an LCC-Server API key present in `~/.astrobase/lccs/` that is
    associated with an LCC-Server user account, datasets that belong to this
    user will be returned as well, even if they are not visible to the public.

    Parameters
    ----------

    lcc_server : str
        This is the base URL of the LCC-Server to talk to.

    nrecent : int
        This indicates how many recent public datasets you want to list. This is
        always capped at 1000.

    Returns
    -------

    list of dicts
        Returns a list of dicts, with each dict containing info on each dataset.

    '''

    arg_2 = {'nsets':arg_1}
    arg_3 = urlencode(arg_2)

    arg_4 = '%s/api/datasets?%s' % (arg_0, arg_3)

    try:

        LOGINFO(
            'getting list of recent publicly '
            'visible and owned datasets from %s' % (
                arg_0,
            )
        )

        # check if we have an API key already
        arg_5, arg_6, arg_7 = check_existing_apikey(arg_0)

        # if not, get a new one
        if not arg_5:
            arg_6, arg_7 = get_new_apikey(arg_0)

        # if apikey is not None, add it in as an Authorization: Bearer [apikey]
        # header
        if arg_6:
            arg_8 = {'Authorization':'Bearer: %s' % arg_6}
        else:
            arg_8 = {}

        # hit the server
        arg_9 = Request(arg_4, data=None, arg_8=arg_8)
        arg_10 = urlopen(arg_9)
        arg_11 = json.loads(arg_10.read())['result']
        return arg_11

    except HTTPError as e:

        LOGERROR('could not retrieve recent datasets list, '
                 'URL used: %s, error code: %s, reason: %s' %
                 (arg_4, e.code, e.reason))

        return None


def Func(arg_0):
    '''This lists all light curve collections made available on the LCC-Server.

    If you have an LCC-Server API key present in `~/.astrobase/lccs/` that is
    associated with an LCC-Server user account, light curve collections visible
    to this user will be returned as well, even if they are not visible to the
    public.

    Parameters
    ----------

    lcc_server : str
        The base URL of the LCC-Server to talk to.

    Returns
    -------

    dict
        Returns a dict containing lists of info items per collection. This
        includes collection_ids, lists of columns, lists of indexed columns,
        lists of full-text indexed columns, detailed column descriptions, number
        of objects in each collection, collection sky coverage, etc.

    '''

    arg_1 = '%s/api/collections' % arg_0

    try:

        LOGINFO(
            'getting list of recent publicly visible '
            'and owned LC collections from %s' % (
                arg_0,
            )
        )

        # check if we have an API key already
        arg_2, arg_3, arg_4 = check_existing_apikey(arg_0)

        # if not, get a new one
        if not arg_2:
            arg_3, arg_4 = get_new_apikey(arg_0)

        # if apikey is not None, add it in as an Authorization: Bearer [apikey]
        # header
        if arg_3:
            arg_5 = {'Authorization':'Bearer: %s' % arg_3}
        else:
            arg_5 = {}

        # hit the server
        arg_6 = Request(arg_1, data=None, arg_5=arg_5)
        arg_7 = urlopen(arg_6)
        arg_8 = json.loads(arg_7.read())['result']['collections']
        return arg_8

    except HTTPError as e:

        LOGERROR('could not retrieve list of collections, '
                 'URL used: %s, error code: %s, reason: %s' %
                 (arg_1, e.code, e.reason))

        return None


def Func(arg_0, arg_1, arg_2, arg_3=False):
    '''This calculates the Stetson index for the magseries, based on consecutive
    pairs of observations.

    Based on Nicole Loncke's work for her Planets and Life certificate at
    Princeton in 2014.

    Parameters
    ----------

    ftimes,fmags,ferrs : np.array
        The input mag/flux time-series with all non-finite elements removed.

    weightbytimediff : bool
        If this is True, the Stetson index for any pair of mags will be
        reweighted by the difference in times between them using the scheme in
        Fruth+ 2012 and Zhange+ 2003 (as seen in Sokolovsky+ 2017)::

            w_i = exp(- (t_i+1 - t_i)/ delta_t )

    Returns
    -------

    float
        The calculated Stetson J variability index.

    '''

    arg_4 = len(arg_1)

    if arg_4 > 9:

        # get the median and ndet
        arg_5 = npmedian(arg_1)

        # get the stetson index elements
        arg_6 = (arg_4/(arg_4 - 1))
        arg_7 = arg_6*(arg_1 - arg_5)/arg_2

        # Nicole's clever trick to advance indices by 1 and do x_i*x_(i+1)
        arg_8 = nproll(arg_7,1)

        if arg_3:

            arg_9 = npdiff(arg_0)
            arg_10 = npmedian(arg_9)

            arg_11 = npexp(- arg_9/arg_10 )
            arg_12 = (arg_11*arg_7[1:]*arg_8[1:])
        else:
            # ignore first elem since it's actually x_0*x_n
            arg_12 = (arg_7*arg_8)[1:]

        arg_13 = (
            npsum(npsign(arg_12) * npsqrt(npabs(arg_12)))
        ) / arg_4

        return arg_13

    else:

        LOGERROR('not enough detections in this magseries '
                 'to calculate stetson J index')
        return npnan


def Func(arg_0, arg_1, arg_2):
    '''This calculates the weighted mean, stdev, median, MAD, percentiles, skew,
    kurtosis, fraction of LC beyond 1-stdev, and IQR.

    Parameters
    ----------

    ftimes,fmags,ferrs : np.array
        The input mag/flux time-series with all non-finite elements removed.

    Returns
    -------

    dict
        A dict with all of the light curve moments calculated.

    '''

    arg_3 = len(arg_1)

    if arg_3 > 9:

        # now calculate the various things we need
        arg_4 = npmedian(arg_1)
        arg_5 = (
            npsum(arg_1*(1.0/(arg_2*arg_2)))/npsum(1.0/(arg_2*arg_2))
        )
        arg_6 = npmedian(npabs(arg_1 - arg_4))
        arg_7 = 1.483*arg_6
        arg_8 = spskew(arg_1)
        arg_9 = spkurtosis(arg_1)

        # get the beyond1std fraction
        arg_10 = len(arg_1[arg_1 > (arg_4 + arg_7)])
        arg_11 = len(arg_1[arg_1 < (arg_4 - arg_7)])

        # this is the fraction beyond 1 stdev
        arg_12 = (arg_10 + arg_11)/float(arg_3)

        # get the magnitude percentiles
        arg_13 = nppercentile(
            arg_1,
            [5.0,10,17.5,25,32.5,40,60,67.5,75,82.5,90,95]
        )

        return {
            'median':arg_4,
            'wmean':arg_5,
            'mad':arg_6,
            'stdev':arg_7,
            'skew':arg_8,
            'kurtosis':arg_9,
            'beyond1std':arg_12,
            'mag_percentiles':arg_13,
            'mag_iqr': arg_13[8] - arg_13[3],
        }

    else:
        LOGERROR('not enough detections in this magseries '
                 'to calculate light curve moments')
        return None


def Func(arg_0, arg_1, arg_2, arg_3=False):
    '''This calculates percentiles and percentile ratios of the flux.

    Parameters
    ----------

    ftimes,fmags,ferrs : np.array
        The input mag/flux time-series with all non-finite elements removed.

    magsarefluxes : bool
        If the `fmags` array actually contains fluxes, will not convert `mags`
        to fluxes before calculating the percentiles.

    Returns
    -------

    dict
        A dict with all of the light curve flux percentiles and percentile
        ratios calculated.

    '''

    arg_4 = len(arg_1)

    if arg_4 > 9:

        # get the fluxes
        if arg_3:
            arg_5 = arg_1
        else:
            arg_5 = 10.0**(-0.4*arg_1)

        arg_6 = npmedian(arg_5)

        # get the percent_amplitude for the fluxes
        arg_7 = (
            npmax(npabs(arg_5))/arg_6
        )

        # get the flux percentiles
        arg_8 = nppercentile(
            arg_5,
            [5.0,10,17.5,25,32.5,40,60,67.5,75,82.5,90,95]
        )
        arg_9 = (
            arg_8[-1] - arg_8[0]
        )
        arg_10 = (
            arg_8[-2] - arg_8[1]
        )
        arg_11 = (
            arg_8[-3] - arg_8[2]
        )
        arg_12 = (
            arg_8[-4] - arg_8[3]
        )
        arg_13 = (
            arg_8[-5] - arg_8[4]
        )
        arg_14 = (
            arg_8[-6] - arg_8[5]
        )

        # calculate the flux percentile ratios
        arg_15 = arg_14/arg_9
        arg_16 = arg_13/arg_9
        arg_17 = arg_12/arg_9
        arg_18 = arg_11/arg_9
        arg_19 = arg_10/arg_9

        # calculate the ratio of F595/median flux
        arg_20 = (
            arg_9/arg_6
        )
        arg_21 = -2.5*nplog10(
            arg_20
        )

        return {
            'flux_median':arg_6,
            'flux_percent_amplitude':arg_7,
            'flux_percentiles':arg_8,
            'flux_percentile_ratio_mid20':arg_15,
            'flux_percentile_ratio_mid35':arg_16,
            'flux_percentile_ratio_mid50':arg_17,
            'flux_percentile_ratio_mid65':arg_18,
            'flux_percentile_ratio_mid80':arg_19,
            'percent_difference_flux_percentile':arg_21,
        }

    else:

        LOGERROR('not enough detections in this magseries '
                 'to calculate flux measures')
        return None


def Func(arg_0, arg_1, arg_2,
                             arg_3=False,
                             arg_4=True):
    '''This rolls up the feature functions above and returns a single dict.

    NOTE: this doesn't calculate the CDPP to save time since binning and
    smoothing takes a while for dense light curves.

    Parameters
    ----------

    times,mags,errs : np.array
        The input mag/flux time-series to calculate CDPP for.

    magsarefluxes : bool
        If True, indicates `mags` is actually an array of flux values.

    stetson_weightbytimediff : bool
        If this is True, the Stetson index for any pair of mags will be
        reweighted by the difference in times between them using the scheme in
        Fruth+ 2012 and Zhange+ 2003 (as seen in Sokolovsky+ 2017)::

            w_i = exp(- (t_i+1 - t_i)/ delta_t )

    Returns
    -------

    dict
        Returns a dict with all of the variability features.

    '''

    # remove nans first
    arg_5 = npisfinite(arg_0) & npisfinite(arg_1) & npisfinite(arg_2)
    arg_6, arg_7, arg_8 = arg_0[arg_5], arg_1[arg_5], arg_2[arg_5]

    # remove zero errors
    arg_9 = npnonzero(arg_8)
    arg_6, arg_7, arg_8 = arg_6[arg_9], arg_7[arg_9], arg_8[arg_9]

    arg_10 = nonperiodic_lightcurve_features(arg_0, arg_1, arg_2,
                                                arg_3=arg_3)
    arg_11 = stetson_jindex(arg_6, arg_7, arg_8,
                           weightbytimediff=arg_4)
    arg_12 = stetson_kindex(arg_7, arg_8)

    arg_10.update({'stetsonj':arg_11,
                      'stetsonk':arg_12})

    return arg_10


def Func(arg_0):
    '''
    This wraps the BLS function for the parallel driver below.

    Parameters
    ----------

    tasks : tuple
        This is of the form::

            task[0] = times
            task[1] = mags
            task[2] = nfreq
            task[3] = freqmin
            task[4] = stepsize
            task[5] = nbins
            task[6] = minduration
            task[7] = maxduration

    Returns
    -------

    dict
        Returns a dict of the form::

            {
                'power':           the periodogram power array,
                'bestperiod':      the best period found,
                'bestpower':       the highest peak of the periodogram power,
                'transdepth':      transit depth found by eebls.f,
                'transduration':   transit duration found by eebls.f,
                'transingressbin': transit ingress bin found by eebls.f,
                'transegressbin':  transit egress bin found by eebls.f,
            }

    '''

    try:

        return _bls_runner(*arg_0)

    except Exception as e:

        LOGEXCEPTION('BLS failed for task %s' % repr(arg_0[2:]))

        return {
            'power':nparray([npnan for arg_1 in range(arg_0[2])]),
            'bestperiod':npnan,
            'bestpower':npnan,
            'transdepth':npnan,
            'transduration':npnan,
            'transingressbin':npnan,
            'transegressbin':npnan
        }


def Func(arg_0, arg_1, arg_2, arg_3,
                           arg_4=False,
                           arg_5=10.0,
                           arg_6=10,
                           arg_7=200,
                           arg_8=0.01,
                           arg_9=0.4,
                           arg_10=0.1,
                           arg_11=True):
    '''This calculates the SNR, depth, duration, a refit period, and time of
    center-transit for a single period.

    The equation used for SNR is::

        SNR = (transit model depth / RMS of LC with transit model subtracted)
              * sqrt(number of points in transit)

    NOTE: you should set the kwargs `sigclip`, `nphasebins`,
    `mintransitduration`, `maxtransitduration` to what you used for an initial
    BLS run to detect transits in the input light curve to match those input
    conditions.

    Parameters
    ----------

    times,mags,errs : np.array
        These contain the magnitude/flux time-series and any associated errors.

    period : float
        The period to search around and refit the transits. This will be used to
        calculate the start and end periods of a rerun of BLS to calculate the
        stats.

    magsarefluxes : bool
        Set to True if the input measurements in `mags` are actually fluxes and
        not magnitudes.

    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.

    perioddeltapercent : float
        The fraction of the period provided to use to search around this
        value. This is a percentage. The period range searched will then be::

            [period - (perioddeltapercent/100.0)*period,
             period + (perioddeltapercent/100.0)*period]

    nphasebins : int
        The number of phase bins to use in the BLS run.

    mintransitduration : float
        The minimum transit duration in phase to consider.

    maxtransitduration : float
        The maximum transit duration to consider.

    ingressdurationfraction : float
        The fraction of the transit duration to use to generate an initial value
        of the transit ingress duration for the BLS model refit. This will be
        fit by this function.

    verbose : bool
        If True, will indicate progress and any problems encountered.

    Returns
    -------

    dict
        A dict of the following form is returned::

            {'period': the refit best period,
             'epoch': the refit epoch (i.e. mid-transit time),
             'snr':the SNR of the transit,
             'transitdepth':the depth of the transit,
             'transitduration':the duration of the transit,
             'nphasebins':the input value of nphasebins,
             'transingressbin':the phase bin containing transit ingress,
             'transegressbin':the phase bin containing transit egress,
             'blsmodel':the full BLS model used along with its parameters,
             'subtractedmags':BLS model - phased light curve,
             'phasedmags':the phase light curve,
             'phases': the phase values}

    '''

    # get rid of nans first and sigclip
    arg_12, arg_13, arg_14 = sigclip_magseries(arg_0,
                                             arg_1,
                                             arg_2,
                                             arg_4=arg_4,
                                             arg_5=arg_5)

    # make sure there are enough points to calculate a spectrum
    if len(arg_12) > 9 and len(arg_13) > 9 and len(arg_14) > 9:

        # get the period interval
        arg_15 = arg_3 - arg_6*arg_3/100.0

        if arg_15 < 0:
            arg_15 = arg_3

        arg_16 = arg_3 + arg_6*arg_3/100.0

        # rerun BLS in serial mode around the specified period to get the
        # transit depth, duration, ingress and egress bins
        arg_17 = bls_serial_pfind(arg_12, arg_13, arg_14,
                                  arg_11=arg_11,
                                  arg_15=arg_15,
                                  arg_16=arg_16,
                                  arg_7=arg_7,
                                  arg_8=arg_8,
                                  arg_9=arg_9,
                                  arg_4=arg_4,
                                  get_stats=False,
                                  arg_5=None)

        if (not arg_17 or
            'blsresult' not in arg_17 or
            arg_17['blsresult'] is None):
            LOGERROR("BLS failed during a period-search "
                     "performed around the input best period: %.6f. "
                     "Can't continue. " % arg_3)
            return None

        arg_18 = arg_17['blsresult']['transdepth']
        arg_19 = arg_17['blsresult']['transduration']
        arg_20 = arg_17['bestperiod']
        arg_21 = arg_17['blsresult']['transingressbin']
        arg_22 = arg_17['blsresult']['transegressbin']
        arg_23 = arg_7

        arg_24 = _get_bls_stats(arg_12,
                               arg_13,
                               arg_14,
                               arg_18,
                               arg_19,
                               arg_10,
                               arg_7,
                               arg_21,
                               arg_22,
                               arg_20,
                               arg_23,
                               arg_4=arg_4,
                               arg_11=arg_11)

        return arg_24

    # if there aren't enough points in the mag series, bail out
    else:

        LOGERROR('no good detections for these times and mags, skipping...')
        return None


def Func(arg_0):
    '''
    This is a parallel worker that reforms light curves for TFA.

    task[0] = lcfile
    task[1] = lcformat
    task[2] = lcformatdir
    task[3] = timecol
    task[4] = magcol
    task[5] = errcol
    task[6] = timebase
    task[7] = interpolate_type
    task[8] = sigclip

    '''

    try:

        (arg_1, arg_2, arg_3,
         arg_4, arg_5, arg_6,
         arg_7, arg_8, arg_9) = arg_0

        try:
            arg_10 = get_lcformat(arg_2,
                                      use_lcformat_dir=arg_3)
            if arg_10:
                (arg_11, arg_12,
                 arg_13, arg_14, arg_15,
                 arg_16, arg_17) = arg_10
            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

        # get the LC into a dict
        arg_18 = arg_12(arg_1)

        # this should handle lists/tuples being returned by readerfunc
        # we assume that the first element is the actual lcdict
        # FIXME: figure out how to not need this assumption
        if ( (isinstance(arg_18, (list, tuple))) and
             (isinstance(arg_18[0], dict)) ):
            arg_18 = arg_18[0]

        arg_19 = {}

        # dereference the columns and get them from the lcdict
        if '.' in arg_4:
            arg_20 = arg_4.split('.')
        else:
            arg_20 = [arg_4]
        arg_21 = _dict_get(arg_18, arg_20)

        if '.' in arg_5:
            arg_22 = arg_5.split('.')
        else:
            arg_22 = [arg_5]
        arg_23 = _dict_get(arg_18, arg_22)

        if '.' in arg_6:
            arg_24 = arg_6.split('.')
        else:
            arg_24 = [arg_6]
        arg_25 = _dict_get(arg_18, arg_24)

        # normalize here if not using special normalization
        if arg_17 is None:
            arg_26, arg_27 = normalize_magseries(
                arg_21, arg_23,
                arg_16=arg_16
            )

        arg_21, arg_23, arg_25 = arg_26, arg_27, arg_25

        #
        # now we'll do: 1. sigclip, 2. reform to timebase, 3. renorm to zero
        #

        # 1. sigclip as requested
        arg_28, arg_29, arg_30 = sigclip_magseries(arg_21,
                                                 arg_23,
                                                 arg_25,
                                                 arg_9=arg_9)

        # 2. now, we'll renorm to the timebase
        arg_31 = spi.interp1d(arg_28, arg_29,
                                         kind=arg_8,
                                         fill_value='extrapolate')
        arg_32 = spi.interp1d(arg_28, arg_30,
                                         kind=arg_8,
                                         fill_value='extrapolate')

        arg_33 = arg_31(arg_7)
        arg_34 = arg_32(arg_7)

        # 3. renorm to zero
        arg_35 = np.median(arg_33)

        arg_36 = arg_33 - arg_35

        # update the dict
        arg_19 = {'mags':arg_36,
                   'errs':arg_34,
                   'origmags':arg_33}

        #
        # done with this magcol
        #
        return arg_19

    except Exception as e:

        LOGEXCEPTION('reform LC task failed: %s' % repr(arg_0))
        return None


def Func(arg_0,
                        arg_1,
                        arg_2=None,
                        arg_3=None,
                        arg_4=None,
                        arg_5='hat-sql',
                        arg_6=None,
                        arg_7='nearest',
                        arg_8=5.0,
                        arg_9=10.0,
                        arg_10=arg_11,
                        arg_12=1000):
    '''This applies TFA in parallel to all LCs in the given list of file names.

    Parameters
    ----------

    lclist : str
        This is a list of light curve files to apply TFA correction to.

    templateinfo : dict or str
        This is either the dict produced by `tfa_templates_lclist` or the pickle
        produced by the same function.

    timecols : list of str or None
        The timecol keys to use from the lcdict in applying TFA corrections.

    magcols : list of str or None
        The magcol keys to use from the lcdict in applying TFA corrections.

    errcols : list of str or None
        The errcol keys to use from the lcdict in applying TFA corrections.

    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.

    interp : str
        This is passed to scipy.interpolate.interp1d as the kind of
        interpolation to use when reforming the light curves to the timebase of
        the TFA templates.

    sigclip : float or sequence of two floats or None
        This is the sigma clip to apply to the light curves before running TFA
        on it.

    mintemplatedist_arcmin : float
        This sets the minimum distance required from the target object for
        objects in the TFA template ensemble. Objects closer than this distance
        will be removed from the ensemble.

    nworkers : int
        The number of parallel workers to launch

    maxworkertasks : int
        The maximum number of tasks per worker allowed before it's replaced by a
        fresh one.

    Returns
    -------

    dict
        Contains the input file names and output TFA light curve filenames per
        input file organized by each `magcol` in `magcols`.

    '''

    # open the templateinfo first
    if isinstance(arg_1,str) and os.path.exists(arg_1):
        with open(arg_1,'rb') as infd:
            arg_1 = pickle.load(infd)

    try:
        arg_13 = get_lcformat(arg_5,
                                  use_lcformat_dir=arg_6)
        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 arg_24:
        LOGEXCEPTION("can't figure out the light curve format")
        return None

    # override the default timecols, magcols, and errcols
    # using the ones provided to the function
    # we'll get the defaults from the templateinfo object
    if arg_2 is None:
        arg_2 = arg_1['timecols']
    if arg_3 is None:
        arg_3 = arg_1['magcols']
    if arg_4 is None:
        arg_4 = arg_1['errcols']

    arg_21 = {}

    # run by magcol
    for arg_22, arg_23, arg_24 in zip(arg_2, arg_3, arg_4):

        arg_25 = [(x, arg_22, arg_23, arg_24, arg_1,
                  arg_5, arg_6,
                  arg_7, arg_8) for
                 x in arg_0]

        arg_26 = mp.Pool(arg_10, maxtasksperchild=arg_12)
        arg_27 = arg_26.map(_parallel_tfa_worker, arg_25)
        arg_26.close()
        arg_26.join()

        arg_21[arg_23] = arg_27

    return arg_21


def Func(arg_0):
    '''
    This just reads a light curve pickle file.

    Parameters
    ----------

    lcfile : str
        The file name of the pickle to open.

    Returns
    -------

    dict
        This returns an lcdict.

    '''

    if arg_0.endswith('.gz'):

        try:
            with gzip.open(arg_0,'rb') as infd:
                arg_1 = pickle.load(infd)
        except UnicodeDecodeError:
            with gzip.open(arg_0,'rb') as infd:
                arg_1 = pickle.load(infd, encoding='latin1')

    else:

        try:
            with open(arg_0,'rb') as infd:
                arg_1 = pickle.load(infd)
        except UnicodeDecodeError:
            with open(arg_0,'rb') as infd:
                arg_1 = pickle.load(infd, encoding='latin1')

    return arg_1


def Func(arg_0, arg_1):
    '''This imports the module specified.

    Used to dynamically import Python modules that are needed to support LC
    formats not natively supported by astrobase.

    Parameters
    ----------

    module : str
        This is either:

        - a Python module import path, e.g. 'astrobase.lcproc.catalogs' or
        - a path to a Python file, e.g. '/astrobase/hatsurveys/hatlc.py'

        that contains the Python module that contains functions used to open
        (and optionally normalize) a custom LC format that's not natively
        supported by astrobase.

    formatkey : str
        A str used as the unique ID of this LC format for all lcproc functions
        and can be used to look it up later and import the correct functions
        needed to support it for lcproc operations. For example, we use
        'kep-fits' as a the specifier for Kepler FITS light curves, which can be
        read by the `astrobase.astrokep.read_kepler_fitslc` function as
        specified by the `<astrobase install path>/data/lcformats/kep-fits.json`
        LC format specification JSON.

    Returns
    -------

    Python module
        This returns a Python module if it's able to successfully import it.

    '''

    try:

        if os.path.exists(arg_0):

            sys.path.append(os.path.dirname(arg_0))
            arg_2 = importlib.import_module(
                os.path.basename(arg_0.replace('.py',''))
            )

        else:
            arg_2 = importlib.import_module(arg_0)

    except Exception as e:

        LOGEXCEPTION('could not import the module: %s for LC format: %s. '
                     'check the file path or fully qualified module name?'
                     % (arg_0, arg_1))
        arg_2 = False

    return arg_2


def Func(arg_0,
            arg_1,
            arg_2='ec2-user',
            arg_3=False):
    """This opens an SSH connection to the EC2 instance at `ip_address`.

    Parameters
    ----------

    ip_address : str
        IP address of the AWS EC2 instance to connect to.

    keypem_file : str
        The path to the keypair PEM file generated by AWS to allow SSH
        connections.

    username : str
        The username to use to login to the EC2 instance.

    raiseonfail : bool
        If True, will re-raise whatever Exception caused the operation to fail
        and break out immediately.

    Returns
    -------

    paramiko.SSHClient
        This has all the usual `paramiko` functionality:

        - Use `SSHClient.exec_command(command, environment=None)` to exec a
          shell command.

        - Use `SSHClient.open_sftp()` to get a `SFTPClient` for the server. Then
          call SFTPClient.get() and .put() to copy files from and to the server.

    """

    arg_4 = paramiko.client.SSHClient()
    arg_4.load_system_host_keys()
    arg_4.set_missing_host_key_policy(paramiko.client.AutoAddPolicy)

    # load the private key from the AWS keypair pem
    arg_5 = paramiko.RSAKey.from_private_key_file(arg_1)

    # connect to the server
    try:

        arg_4.connect(arg_0,
                  pkey=arg_5,
                  arg_2='ec2-user')

        return arg_4

    except Exception as e:
        LOGEXCEPTION('could not connect to EC2 instance at %s '
                     'using keyfile: %s and user: %s' %
                     (arg_0, arg_1, arg_2))
        if arg_3:
            raise

        return None


def Func(arg_0,
                arg_1,
                arg_2,
                arg_3=None,
                arg_4=None,
                arg_5=False):

    """This gets a file from an S3 bucket.

    Parameters
    ----------

    bucket : str
        The AWS S3 bucket name.

    filename : str
        The full filename of the file to get from the bucket

    local_file : str
        Path to where the downloaded file will be stored.

    altexts : None or list of str
        If not None, this is a list of alternate extensions to try for the file
        other than the one provided in `filename`. For example, to get anything
        that's an .sqlite where .sqlite.gz is expected, use altexts=[''] to
        strip the .gz.

    client : boto3.Client or None
        If None, this function will instantiate a new `boto3.Client` object to
        use in its operations. Alternatively, pass in an existing `boto3.Client`
        instance to re-use it here.

    raiseonfail : bool
        If True, will re-raise whatever Exception caused the operation to fail
        and break out immediately.

    Returns
    -------

    str
        Path to the downloaded filename or None if the download was
        unsuccessful.

    """

    if not arg_4:
        arg_4 = boto3.client('s3')

    try:

        arg_4.download_file(arg_0, arg_1, arg_2)
        return arg_2

    except Exception as e:

        if arg_3 is not None:

            for arg_6 in arg_3:

                arg_7 = os.path.splitext(arg_1)
                arg_8 = arg_7[0] + arg_6
                try:
                    arg_4.download_file(
                        arg_0,
                        arg_8,
                        arg_2.replace(arg_7[-1],
                                           arg_6)
                    )
                    return arg_2.replace(arg_7[-1],
                                              arg_6)
                except Exception as e:
                    pass

        else:

            LOGEXCEPTION('could not download s3://%s/%s' % (arg_0, arg_1))

            if arg_5:
                raise

            return None


def Func(arg_0, arg_1, arg_2=None, arg_3=False):
    """This deletes a file from S3.

    Parameters
    ----------

    bucket : str
        The AWS S3 bucket to delete the file from.

    filename : str
        The full file name of the file to delete, including any prefixes.

    client : boto3.Client or None
        If None, this function will instantiate a new `boto3.Client` object to
        use in its operations. Alternatively, pass in an existing `boto3.Client`
        instance to re-use it here.

    raiseonfail : bool
        If True, will re-raise whatever Exception caused the operation to fail
        and break out immediately.

    Returns
    -------

    str or None
        If the file was successfully deleted, will return the delete-marker
        (https://docs.aws.amazon.com/AmazonS3/latest/dev/DeleteMarker.html). If
        it wasn't, returns None

    """

    if not arg_2:
        arg_2 = boto3.client('s3')

    try:
        arg_4 = arg_2.delete_object(Bucket=arg_0, Key=arg_1)
        if not arg_4:
            LOGERROR('could not delete file %s from bucket %s' % (arg_1,
                                                                  arg_0))
        else:
            return arg_4['DeleteMarker']
    except Exception as e:
        LOGEXCEPTION('could not delete file %s from bucket %s' % (arg_1,
                                                                  arg_0))
        if arg_3:
            raise

        return None


def Func(arg_0, arg_1=None):
    """This deletes an SQS queue given its URL

    Parameters
    ----------

    queue_url : str
        The SQS URL of the queue to delete.

    client : boto3.Client or None
        If None, this function will instantiate a new `boto3.Client` object to
        use in its operations. Alternatively, pass in an existing `boto3.Client`
        instance to re-use it here.

    Returns
    -------

    bool
        True if the queue was deleted successfully. False otherwise.

    """

    if not arg_1:
        arg_1 = boto3.client('sqs')

    try:

        arg_1.delete_queue(QueueUrl=arg_0)
        return True

    except Exception as e:
        LOGEXCEPTION('could not delete the specified queue: %s'
                     % (arg_0,))
        return False


def Func(arg_0,
                 arg_1,
                 arg_2=0,
                 arg_3=None,
                 arg_4=False):
    """This pushes a dict serialized to JSON to the specified SQS queue.

    Parameters
    ----------

    queue_url : str
        The SQS URL of the queue to push the object to.

    item : dict
        The dict passed in here will be serialized to JSON.

    delay_seconds : int
        The amount of time in seconds the pushed item will be held before going
        'live' and being visible to all queue consumers.

    client : boto3.Client or None
        If None, this function will instantiate a new `boto3.Client` object to
        use in its operations. Alternatively, pass in an existing `boto3.Client`
        instance to re-use it here.

    raiseonfail : bool
        If True, will re-raise whatever Exception caused the operation to fail
        and break out immediately.

    Returns
    -------

    boto3.Response or None
        If the item was successfully put on the queue, will return the response
        from the service. If it wasn't, will return None.

    """

    if not arg_3:
        arg_3 = boto3.client('sqs')

    try:

        arg_5 = json.dumps(arg_1)

        arg_6 = arg_3.send_message(
            QueueUrl=arg_0,
            MessageBody=arg_5,
            DelaySeconds=arg_2,
        )
        if not arg_6:
            LOGERROR('could not send item to queue: %s' % arg_0)
            return None
        else:
            return arg_6

    except Exception as e:

        LOGEXCEPTION('could not send item to queue: %s' % arg_0)

        if arg_4:
            raise

        return None


def Func(arg_0,
                 arg_1=1,
                 arg_2=5,
                 arg_3=None,
                 arg_4=False):
    """This gets a single item from the SQS queue.

    The `queue_url` is composed of some internal SQS junk plus a
    `queue_name`. For our purposes (`lcproc_aws.py`), the queue name will be
    something like::

        lcproc_queue_<action>

    where action is one of::

        runcp
        runpf

    The item is always a JSON object::

        {'target': S3 bucket address of the file to process,
         'action': the action to perform on the file ('runpf', 'runcp', etc.)
         'args': the action's args as a tuple (not including filename, which is
                 generated randomly as a temporary local file),
         'kwargs': the action's kwargs as a dict,
         'outbucket: S3 bucket to write the result to,
         'outqueue': SQS queue to write the processed item's info to (optional)}

    The action MUST match the <action> in the queue name for this item to be
    processed.

    Parameters
    ----------

    queue_url : str
        The SQS URL of the queue to get messages from.

    max_items : int
        The number of items to pull from the queue in this request.

    wait_time_seconds : int
        This specifies how long the function should block until a message is
        received on the queue. If the timeout expires, an empty list will be
        returned. If the timeout doesn't expire, the function will return a list
        of items received (up to `max_items`).

    client : boto3.Client or None
        If None, this function will instantiate a new `boto3.Client` object to
        use in its operations. Alternatively, pass in an existing `boto3.Client`
        instance to re-use it here.

    raiseonfail : bool
        If True, will re-raise whatever Exception caused the operation to fail
        and break out immediately.

    Returns
    -------

    list of dicts or None
        For each item pulled from the queue in this request (up to `max_items`),
        a dict will be deserialized from the retrieved JSON, containing the
        message items and various metadata. The most important item of the
        metadata is the `receipt_handle`, which can be used to acknowledge
        receipt of all items in this request (see `sqs_delete_item` below).

        If the queue pull fails outright, returns None. If no messages are
        available for this queue pull, returns an empty list.

    """

    if not arg_3:
        arg_3 = boto3.client('sqs')

    try:

        arg_5 = arg_3.receive_message(
            QueueUrl=arg_0,
            AttributeNames=['All'],
            MaxNumberOfMessages=arg_1,
            WaitTimeSeconds=arg_2
        )

        if not arg_5:
            LOGERROR('could not receive messages from queue: %s' %
                     arg_0)

        else:

            arg_6 = []

            for arg_7 in arg_5.get('Messages',[]):

                try:
                    arg_6.append({
                        'id':arg_7['MessageId'],
                        'receipt_handle':arg_7['ReceiptHandle'],
                        'md5':arg_7['MD5OfBody'],
                        'attributes':arg_7['Attributes'],
                        'item':json.loads(arg_7['Body']),
                    })
                except Exception as e:
                    LOGEXCEPTION(
                        'could not deserialize message ID: %s, body: %s' %
                        (arg_7['MessageId'], arg_7['Body'])
                    )
                    continue

            return arg_6

    except Exception as e:
        LOGEXCEPTION('could not get items from queue: %s' % arg_0)

        if arg_4:
            raise

        return None


def Func(
        arg_0,
        arg_1=None
):
    """This deletes EC2 nodes and terminates the instances.

    Parameters
    ----------

    instance_id_list : list of str
        A list of EC2 instance IDs to terminate.

    client : boto3.Client or None
        If None, this function will instantiate a new `boto3.Client` object to
        use in its operations. Alternatively, pass in an existing `boto3.Client`
        instance to re-use it here.

    Returns
    -------

    Nothing.

    """

    if not arg_1:
        arg_1 = boto3.client('ec2')

    arg_2 = arg_1.terminate_instances(
        InstanceIds=arg_0
    )

    return arg_2


def Func(
        arg_0,
        arg_1=None,
):
    """
    This deletes a spot-fleet cluster.

    Parameters
    ----------

    spot_fleet_reqid : str
        The fleet request ID returned by `make_spot_fleet_cluster`.

    client : boto3.Client or None
        If None, this function will instantiate a new `boto3.Client` object to
        use in its operations. Alternatively, pass in an existing `boto3.Client`
        instance to re-use it here.

    Returns
    -------

    Nothing.

    """

    if not arg_1:
        arg_1 = boto3.client('ec2')

    arg_2 = arg_1.cancel_spot_fleet_requests(
        SpotFleetRequestIds=[arg_0],
        TerminateInstances=True
    )

    return arg_2


def Func(arg_0,
                 arg_1,
                 arg_2=None,
                 arg_3=None,
                 arg_4=False):
    """This puts a single file into a Google Cloud Storage bucket.

    Parameters
    ----------

    local_file : str
        Path to the file to upload to GCS.

    bucket : str
        The GCS bucket to upload the file to.

    service_account_json : str
        Path to a downloaded GCS credentials JSON file.

    client : google.cloud.storage.Client instance
        The instance of the Client to use to perform the download operation. If
        this is None, a new Client will be used. If this is None and
        `service_account_json` points to a downloaded JSON file with GCS
        credentials, a new Client with the provided credentials will be used. If
        this is not None, the existing Client instance will be used.

    raiseonfail : bool
        If True, will re-raise whatever Exception caused the operation to fail
        and break out immediately.

    Returns
    -------

    str or None
        If the file upload is successful, returns the gs:// URL of the uploaded
        file. If it failed, will return None.

    """

    if not arg_3:

        if (arg_2 is not None and
            os.path.exists(arg_2)):
            arg_3 = storage.Client.from_service_account_json(
                arg_2
            )
        else:
            arg_3 = storage.Client()

    try:

        arg_5 = arg_3.get_bucket(arg_1)
        arg_6 = arg_5.blob(arg_0)
        arg_6.upload_from_filename(arg_0)
        return 'gs://%s/%s' % (arg_1, arg_0.lstrip('/'))

    except Exception as e:

        LOGEXCEPTION('could not upload %s to bucket %s' % (arg_0,
                                                           arg_5))

        if arg_4:
            raise

        return None


def Func(arg_0,
                    arg_1=1000,
                    arg_2=None):
    '''This runs `lcproc.lcvfeatures.parallel_varfeatures` on fake LCs in
    `simbasedir`.

    Parameters
    ----------

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

    mindet : int
        The minimum number of detections needed to accept an LC and process it.

    nworkers : int or None
        The number of parallel workers to use when extracting variability
        features from the input light curves.

    Returns
    -------

    str
        The path to the `varfeatures` pickle created after running the
        `lcproc.lcvfeatures.parallel_varfeatures` function.

    '''

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

    arg_4 = arg_3['lcfpath']
    arg_5 = os.path.join(arg_0,'varfeatures')

    # get the column defs for the fakelcs
    arg_6 = arg_3['timecols']
    arg_7 = arg_3['magcols']
    arg_8 = arg_3['errcols']

    # get the column defs for the fakelcs
    arg_6 = arg_3['timecols']
    arg_7 = arg_3['magcols']
    arg_8 = arg_3['errcols']

    # register the fakelc pklc as a custom lcproc format
    # now we should be able to use all lcproc functions correctly
    arg_9 = 'fake-%s' % arg_3['lcformat']
    lcproc.register_lcformat(
        arg_9,
        '*-fakelc.pkl',
        arg_6,
        arg_7,
        arg_8,
        'astrobase.lcproc',
        '_read_pklc',
        magsarefluxes=arg_3['magsarefluxes']
    )

    # now we can use lcproc.parallel_varfeatures directly
    arg_10 = lcvfeatures.parallel_varfeatures(arg_4,
                                               arg_5,
                                               lcformat=arg_9,
                                               arg_1=arg_1,
                                               arg_2=arg_2)

    with open(os.path.join(arg_0,'fakelc-varfeatures.pkl'),'wb') as outfd:
        pickle.dump(arg_10, outfd, pickle.HIGHEST_PROTOCOL)

    return os.path.join(arg_0,'fakelc-varfeatures.pkl')


def Func(arg_0, arg_1):
    '''
    This calculates Func.

    https://en.wikipedia.org/wiki/Precision_and_recall

    Parameters
    ----------

    ntp : int
        The number of true positives.

    nfp : int
        The number of false positives.

    Returns
    -------

    float
        The Func calculated using `ntp/(ntp + nfp)`.

    '''

    if (arg_0+arg_1) > 0:
        return arg_0/(arg_0+arg_1)
    else:
        return np.nan


def Func(arg_0, arg_1):
    '''
    This calculates Func.

    https://en.wikipedia.org/wiki/Precision_and_Func

    Parameters
    ----------

    ntp : int
        The number of true positives.

    nfn : int
        The number of false negatives.

    Returns
    -------

    float
        The precision calculated using `ntp/(ntp + nfn)`.

    '''

    if (arg_0+arg_1) > 0:
        return arg_0/(arg_0+arg_1)
    else:
        return np.nan


def Func(arg_0,
                                     arg_1=(1.0,20.0),
                                     arg_2=(1.0,20.0),
                                     arg_3=(1.0,20.0),
                                     arg_4=32,
                                     arg_5=None):
    '''This runs a variable index grid search per magbin.

    For each magbin, this does a grid search using the stetson and inveta ranges
    provided and tries to optimize the Matthews Correlation Coefficient (best
    value is +1.0), indicating the best possible separation of variables
    vs. nonvariables. The thresholds on these two variable indexes that produce
    the largest coeff for the collection of fake LCs will probably be the ones
    that work best for actual variable classification on the real LCs.

    https://en.wikipedia.org/wiki/Matthews_correlation_coefficient

    For each grid-point, calculates the true positives, false positives, true
    negatives, false negatives. Then gets the precision and recall, confusion
    matrix, and the ROC curve for variable vs. nonvariable.

    Once we've identified the best thresholds to use, we can then calculate
    variable object numbers:

    - as a function of magnitude
    - as a function of period
    - as a function of number of detections
    - as a function of amplitude of variability

    Writes everything back to `simbasedir/fakevar-recovery.pkl`. Use the
    plotting function below to make plots for the results.

    Parameters
    ----------

    simbasedir : str
        The directory where the fake LCs are located.

    stetson_stdev_range : sequence of 2 floats
        The min and max values of the Stetson J variability index to generate a
        grid over these to test for the values of this index that produce the
        'best' recovery rate for the injected variable stars.

    inveta_stdev_range : sequence of 2 floats
        The min and max values of the 1/eta variability index to generate a
        grid over these to test for the values of this index that produce the
        'best' recovery rate for the injected variable stars.

    iqr_stdev_range : sequence of 2 floats
        The min and max values of the IQR variability index to generate a
        grid over these to test for the values of this index that produce the
        'best' recovery rate for the injected variable stars.

    ngridpoints : int
        The number of grid points for each variability index grid. Remember that
        this function will be searching in 3D and will require lots of time to
        run if ngridpoints is too large.

        For the default number of grid points and 25000 simulated light curves,
        this takes about 3 days to run on a 40 (effective) core machine with 2 x
        Xeon E5-2650v3 CPUs.

    ngridworkers : int or None
        The number of parallel grid search workers that will be launched.

    Returns
    -------

    dict
        The returned dict contains a list of recovery stats for each magbin and
        each grid point in the variability index grids that were used. This dict
        can be passed to the plotting function below to plot the results.

    '''

    # make the output directory where all the pkls from the variability
    # threshold runs will go
    arg_6 = os.path.join(arg_0,'recvar-threshold-pkls')
    if not os.path.exists(arg_6):
        os.mkdir(arg_6)

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

    # get the column defs for the fakelcs
    arg_8 = arg_7['timecols']
    arg_9 = arg_7['magcols']
    arg_10 = arg_7['errcols']

    # get the magbinmedians to use for the recovery processing
    arg_11 = arg_7['magrms'][arg_9[0]]['binned_sdssr_median']

    # generate the grids for stetson and inveta
    arg_12 = np.linspace(arg_1[0],
                               arg_1[1],
                               num=arg_4)
    arg_13 = np.linspace(arg_2[0],
                              arg_2[1],
                              num=arg_4)
    arg_14 = np.linspace(arg_3[0],
                           arg_3[1],
                           num=arg_4)

    # generate the grid
    arg_15 = []
    for arg_16 in arg_12:
        for arg_17 in arg_13:
            for arg_18 in arg_14:
                arg_19 = [arg_16, arg_17, arg_18]
                arg_15.append(arg_19)

    # the output dict
    arg_20 = {'stetson_grid':arg_12,
                    'inveta_grid':arg_13,
                    'iqr_grid':arg_14,
                    'stet_inveta_iqr_grid':arg_15,
                    'magbinmedians':arg_11,
                    'timecols':arg_8,
                    'magcols':arg_9,
                    'errcols':arg_10,
                    'simbasedir':os.path.abspath(arg_0),
                    'recovery':[]}

    # set up the pool
    arg_21 = mp.Pool(arg_5)

    # run the grid search per magbinmedian
    for arg_22 in arg_11:

        LOGINFO('running stetson J-inveta grid-search '
                'for magbinmedian = %.3f...' % arg_22)

        arg_23 = [(arg_0, gp, arg_22) for gp in arg_15]
        arg_24 = arg_21.map(magbin_varind_gridsearch_worker, arg_23)
        arg_20['recovery'].append(arg_24)

    arg_21.close()
    arg_21.join()

    LOGINFO('done.')
    with open(os.path.join(arg_0,
                           'fakevar-recovery-per-magbin.pkl'),'wb') as outfd:
        pickle.dump(arg_20,outfd,pickle.HIGHEST_PROTOCOL)

    return arg_20


def Func(arg_0,
                      arg_1=('gls','pdm','bls'),
                      arg_2=({},{},{'startp':1.0,'maxtransitduration':0.3}),
                      arg_3=False,
                      arg_4=5.0,
                      arg_5=10,
                      arg_6=4,
                      arg_7=None,
                      arg_8=None):
    '''This runs periodfinding using several period-finders on a collection of
    fake LCs.

    As a rough benchmark, 25000 fake LCs with 10000--50000 points per LC take
    about 26 days in total to run on an invocation of this function using
    GLS+PDM+BLS and 10 periodworkers and 4 controlworkers (so all 40 'cores') on
    a 2 x Xeon E5-2660v3 machine.

    Parameters
    ----------

    pfmethods : sequence of str
        This is used to specify which periodfinders to run. These must be in the
        `lcproc.periodsearch.PFMETHODS` dict.

    pfkwargs : sequence of dict
        This is used to provide optional kwargs to the period-finders.

    getblssnr : bool
        If this is True, will run BLS SNR calculations for each object and
        magcol. This takes a while to run, so it's disabled (False) by default.

    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.

    nperiodworkers : int
        This is the number of parallel period-finding worker processes to use.

    ncontrolworkers : int
        This is the number of parallel period-finding control workers to
        use. Each control worker will launch `nperiodworkers` worker processes.

    liststartindex : int
        The starting index of processing. This refers to the filename list
        generated by running `glob.glob` on the fake LCs in `simbasedir`.

    maxobjects : int
        The maximum number of objects to process in this run. Use this with
        `liststartindex` to effectively distribute working on a large list of
        input light curves over several sessions or machines.

    Returns
    -------

    str
        The path to the output summary pickle produced by
        `lcproc.periodsearch.parallel_pf`

    '''

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

    arg_10 = arg_9['lcfpath']
    arg_11 = os.path.join(arg_0,'periodfinding')

    # get the column defs for the fakelcs
    arg_12 = arg_9['timecols']
    arg_13 = arg_9['magcols']
    arg_14 = arg_9['errcols']

    # register the fakelc pklc as a custom lcproc format
    # now we should be able to use all lcproc functions correctly
    arg_15 = 'fake-%s' % arg_9['lcformat']
    lcproc.register_lcformat(
        arg_15,
        '*-fakelc.pkl',
        arg_12,
        arg_13,
        arg_14,
        'astrobase.lcproc',
        '_read_pklc',
        magsarefluxes=arg_9['magsarefluxes']
    )

    if arg_7:
        arg_10 = arg_10[arg_7:]

    if arg_8:
        arg_10 = arg_10[:arg_8]

    arg_16 = periodsearch.parallel_pf(arg_10,
                                      arg_11,
                                      lcformat=arg_15,
                                      arg_1=arg_1,
                                      arg_2=arg_2,
                                      arg_3=arg_3,
                                      arg_4=arg_4,
                                      arg_5=arg_5,
                                       arg_6=arg_6)

    with open(os.path.join(arg_0,
                           'fakelc-periodsearch.pkl'),'wb') as outfd:
        pickle.dump(arg_16, outfd, pickle.HIGHEST_PROTOCOL)

    return os.path.join(arg_0,'fakelc-periodsearch.pkl')


def Func(
        arg_0,
        arg_1,
        arg_2=5.0,
        arg_3='v0',
        arg_4=False,
        arg_5='~/.astrobase/mast-cache',
        arg_6=True,
        arg_7=10.0,
        arg_8=5.0,
        arg_9=90.0,
        arg_10=3,
        arg_11=5.0,
        arg_12=False
):
    '''This runs a TESS Input Catalog cone search on MAST.

    If you use this, please cite the TIC paper (Stassun et al 2018;
    http://adsabs.harvard.edu/abs/2018AJ....156..102S). Also see the "living"
    TESS input catalog docs:

    https://docs.google.com/document/d/1zdiKMs4Ld4cXZ2DW4lMX-fuxAF6hPHTjqjIwGqnfjqI

    Also see: https://mast.stsci.edu/api/v0/_t_i_cfields.html for the fields
    returned by the service and present in the result JSON file.

    Parameters
    ----------

    ra,decl : float
        The center coordinates of the cone-search in decimal degrees.

    radius_arcmin : float
        The cone-search radius in arcminutes.

    apiversion : str
        The API version of the MAST service to use. This sets the URL that this
        function will call, using `apiversion` as key into the `MAST_URLS` dict
        above.

    forcefetch : bool
        If this is True, the query will be retried even if cached results for
        it exist.

    cachedir : str
        This points to the directory where results will be downloaded.

    verbose : bool
        If True, will indicate progress and warn of any issues.

    timeout : float
        This sets the amount of time in seconds to wait for the service to
        respond to our initial request.

    refresh : float
        This sets the amount of time in seconds to wait before checking if the
        result file is available. If the results file isn't available after
        `refresh` seconds have elapsed, the function will wait for `refresh`
        seconds continuously, until `maxtimeout` is reached or the results file
        becomes available.

    maxtimeout : float
        The maximum amount of time in seconds to wait for a result to become
        available after submitting our query request.

    maxtries : int
        The maximum number of tries (across all mirrors tried) to make to either
        submit the request or download the results, before giving up.

    jitter : float
        This is used to control the scale of the random wait in seconds before
        starting the query. Useful in parallelized situations.

    raiseonfail : bool
        If this is True, the function will raise an Exception if something goes
        wrong, instead of returning None.

    Returns
    -------

    dict
        This returns a dict of the following form::

            {'params':dict of the input params used for the query,
             'provenance':'cache' or 'new download',
             'result':path to the file on disk with the downloaded data table}

    '''

    arg_13 = {'ra':arg_0,
              'dec':arg_1,
              'radius':arg_2/60.0}
    arg_14 = 'Mast.Catalogs.Tic.Cone'

    return mast_query(arg_14,
                      arg_13,
                      arg_11=arg_11,
                      arg_3=arg_3,
                      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_12=arg_12)


def Func(
        arg_0,
        arg_1="ID",
        arg_2='v0',
        arg_3=False,
        arg_4='~/.astrobase/mast-cache',
        arg_5=True,
        arg_6=90.0,
        arg_7=5.0,
        arg_8=180.0,
        arg_9=3,
        arg_10=5.0,
        arg_11=False
):
    '''
    This runs a TIC search for a specified TIC ID.

    Parameters
    ----------

    objectid : str
        The object ID to look up information for.

    idcol_to_use : str
        This is the name of the object ID column to use when looking up the
        provided `objectid`. This is one of {'ID', 'HIP', 'TYC', 'UCAC',
        'TWOMASS', 'ALLWISE', 'SDSS', 'GAIA', 'APASS', 'KIC'}.

    apiversion : str
        The API version of the MAST service to use. This sets the URL that this
        function will call, using `apiversion` as key into the `MAST_URLS` dict
        above.

    forcefetch : bool
        If this is True, the query will be retried even if cached results for
        it exist.

    cachedir : str
        This points to the directory where results will be downloaded.

    verbose : bool
        If True, will indicate progress and warn of any issues.

    timeout : float
        This sets the amount of time in seconds to wait for the service to
        respond to our initial request.

    refresh : float
        This sets the amount of time in seconds to wait before checking if the
        result file is available. If the results file isn't available after
        `refresh` seconds have elapsed, the function will wait for `refresh`
        seconds continuously, until `maxtimeout` is reached or the results file
        becomes available.

    maxtimeout : float
        The maximum amount of time in seconds to wait for a result to become
        available after submitting our query request.

    maxtries : int
        The maximum number of tries (across all mirrors tried) to make to either
        submit the request or download the results, before giving up.

    jitter : float
        This is used to control the scale of the random wait in seconds before
        starting the query. Useful in parallelized situations.

    raiseonfail : bool
        If this is True, the function will raise an Exception if something goes
        wrong, instead of returning None.

    Returns
    -------

    dict
        This returns a dict of the following form::

            {'params':dict of the input params used for the query,
             'provenance':'cache' or 'new download',
             'result':path to the file on disk with the downloaded data table}
    '''

    arg_12 = {
        'columns':'*',
        'filters':[
            {"paramName": arg_1,
             "values":[str(arg_0)]}
        ]
    }
    arg_13 = 'Mast.Catalogs.Filtered.Tic'

    return mast_query(arg_13,
                      arg_12,
                      arg_10=arg_10,
                      arg_2=arg_2,
                      arg_3=arg_3,
                      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_11=arg_11)


def Func(arg_0,
               arg_1,
               arg_2,
               arg_3,
               arg_4,
               arg_5=None,
               arg_6=None,
               arg_7=None):
    '''This sends an email to addresses, informing them about events.

    The email account settings are retrieved from the settings file as described
    above.

    Parameters
    ----------

    sender : str
        The name of the sender to use in the email header.

    subject : str
        Subject of the email.

    content : str
        Content of the email.

    email_recipient list : list of str
        This is a list of email recipient names of the form:
        `['Example Person 1', 'Example Person 1', ...]`

    email_recipient list : list of str
        This is a list of email recipient addresses of the form:
        `['example1@example.com', 'example2@example.org', ...]`

    email_user : str
        The username of the email server account that will send the emails. If
        this is None, the value of EMAIL_USER from the
        ~/.astrobase/.emailsettings file will be used. If that is None as well,
        this function won't work.

    email_pass : str
        The password of the email server account that will send the emails. If
        this is None, the value of EMAIL_PASS from the
        ~/.astrobase/.emailsettings file will be used. If that is None as well,
        this function won't work.

    email_server : str
        The address of the email server that will send the emails. If this is
        None, the value of EMAIL_USER from the ~/.astrobase/.emailsettings file
        will be used. If that is None as well, this function won't work.

    Returns
    -------

    bool
        True if email sending succeeded. False if email sending failed.

    '''

    if not arg_5:
        arg_5 = EMAIL_USER

    if not arg_6:
        arg_6 = EMAIL_PASSWORD

    if not arg_7:
        arg_7 = EMAIL_SERVER

    if not arg_7 and arg_5 and arg_6:
        raise ValueError("no email server address and "
                         "credentials available, can't continue")

    arg_8 = EMAIL_TEMPLATE.format(
        arg_0=arg_0,
        hostname=socket.gethostname(),
        activity_time='%sZ' % datetime.utcnow().isoformat(),
        activity_report=arg_2
    )

    arg_9 = '%s <%s>' % (arg_0, EMAIL_USER)

    # put together the recipient and email lists
    arg_10 = [('%s <%s>' % (x,y))
                        for (x,y) in zip(arg_3,
                                         arg_4)]

    # put together the rest of the message
    arg_11 = MIMEText(arg_8)
    arg_11['From'] = arg_9
    arg_11['To'] = ', '.join(arg_10)
    arg_11['Message-Id'] = make_msgid()
    arg_11['Subject'] = '[%s on %s] %s' % (
        arg_0,
        socket.gethostname(),
        arg_1
    )
    arg_11['Date'] = formatdate(time.time())

    # start the email process

    try:
        arg_12 = smtplib.SMTP(EMAIL_SERVER, 587)
        arg_13 = arg_12.ehlo()

        if arg_12.has_extn('STARTTLS'):

            try:

                arg_14 = arg_12.starttls()
                arg_15 = arg_12.ehlo()

                arg_16 = arg_12.login(EMAIL_USER, EMAIL_PASSWORD)

                arg_17 = (
                    arg_12.sendmail(arg_9,
                                    arg_4,
                                    arg_11.as_string())
                )

            except Exception as e:

                print('script email sending failed with error: %s'
                      % e)
                arg_17 = None

            if arg_17 is not None:

                print('script email sent successfully')
                arg_18 = arg_12.quit()
                return True

            else:

                arg_18 = arg_12.quit()
                return False

        else:

            print('email server does not support STARTTLS,'
                  ' bailing out...')
            arg_18 = arg_12.quit()
            return False

    except Exception as e:
        print('sending email failed with error: %s' % e)
        arg_19 = False

    arg_18 = arg_12.quit()
    return arg_19


def Func(arg_0, arg_1, arg_2, arg_3):
    '''This generates a sinusoidal light curve using a Fourier cosine series.

    Parameters
    ----------

    fourierparams : list
        This MUST be a list of the following form like so::

            [period,
             epoch,
             [amplitude_1, amplitude_2, amplitude_3, ..., amplitude_X],
             [phase_1, phase_2, phase_3, ..., phase_X]]

        where X is the Fourier order.

    times,mags,errs : np.array
        The input time-series of measurements and associated errors for which
        the model will be generated. The times will be used to generate model
        mags, and the input `times`, `mags`, and `errs` will be resorted by
        model phase and returned.

    Returns
    -------

    (modelmags, phase, ptimes, pmags, perrs) : tuple
        Returns the model mags and phase values. Also returns the input `times`,
        `mags`, and `errs` sorted by the model's phase.

    '''

    arg_4, arg_5, arg_6, arg_7 = arg_0

    # figure out the order from the length of the Fourier param list
    arg_8 = len(arg_6)

    # phase the times with this period
    arg_9 = (arg_1 - arg_5)/arg_4
    arg_9 = arg_9 - np.floor(arg_9)

    arg_10 = np.argsort(arg_9)
    arg_11 = arg_9[arg_10]
    arg_12 = arg_1[arg_10]
    arg_13 = arg_2[arg_10]
    arg_14 = arg_3[arg_10]

    # calculate all the individual terms of the series
    arg_15 = [arg_6[x]*np.cos(2.0*np.pi*x*arg_11 + arg_7[x])
               for x in range(arg_8)]

    # this is the zeroth order coefficient - a constant equal to median mag
    arg_16 = np.median(arg_2)

    # sum the series
    for arg_17 in arg_15:
        arg_16 += arg_17

    return arg_16, arg_11, arg_12, arg_13, arg_14


def Func(arg_0,
                         arg_1,
                         arg_2,
                         arg_3,
                         arg_4=False,
                         arg_5=2.0):
    '''Makes the mag-series plot tile for `checkplot_png` and
    `twolsp_checkplot_png`.

    axes : matplotlib.axes.Axes object
        The Axes object where the generated plot will go.

    stimes,smags,serrs : np.array
        The mag/flux time-series arrays along with associated errors. These
        should all have been run through nan-stripping and sigma-clipping
        beforehand.

    magsarefluxes : bool
        If True, indicates the input time-series is fluxes and not mags so the
        plot y-axis direction and range can be set appropriately.

    ms : float
        The `markersize` kwarg to use when making the mag-series plot.

    Returns
    -------

    Does not return anything, works on the input Axes object directly.

    '''

    arg_6 = arg_1 - npmin(arg_1)

    arg_0.plot(arg_6,
              arg_2,
              marker='o',
              arg_5=arg_5, ls='None',mew=0,
              color='green',
              rasterized=True)

    # flip y axis for mags
    if not arg_4:
        arg_7 = arg_0.get_ylim()
        arg_0.set_ylim((arg_7[1], arg_7[0]))

    # set the x axis limit
    arg_0.set_xlim((npmin(arg_6)-1.0,
                   npmax(arg_6)+1.0))

    # make a grid
    arg_0.grid(color='#a9a9a9',
              alpha=0.9,
              zorder=0,
              linewidth=1.0,
              linestyle=':')

    # make the x and y axis labels
    arg_8 = 'JD - %.3f' % npmin(arg_1)
    if arg_4:
        arg_9 = 'flux'
    else:
        arg_9 = 'magnitude'

    arg_0.set_xlabel(arg_8)
    arg_0.set_ylabel(arg_9)

    # fix the yaxis ticks (turns off offset and uses the full
    # value of the yaxis tick)
    arg_0.get_yaxis().get_major_formatter().set_useOffset(False)
    arg_0.get_xaxis().get_major_formatter().set_useOffset(False)


def Func(arg_0, arg_1,
                        arg_2, arg_3,
                        arg_4=None,
                        arg_5=0.0,
                        arg_6=0.0,
                        arg_7=False):
    '''Precesses target coordinates `ra`, `dec` from `epoch_one` to `epoch_two`.

    This takes into account the jd of the observations, as well as the proper
    motion of the target mu_ra, mu_dec. Adapted from J. D. Hartman's
    VARTOOLS/converttime.c [coordprecess].

    Parameters
    ----------

    ra,dec : float
        The equatorial coordinates of the object at `epoch_one` to precess in
        decimal degrees.

    epoch_one : float
        Origin epoch to precess from to target epoch. This is a float, like:
        1985.0, 2000.0, etc.

    epoch_two : float
        Target epoch to precess from origin epoch. This is a float, like:
        2000.0, 2018.0, etc.

    jd : float
        The full Julian date to use along with the propermotions in `mu_ra`, and
        `mu_dec` to handle proper motion along with the coordinate frame
        precession. If one of `jd`, `mu_ra`, or `mu_dec` is missing, the proper
        motion will not be used to calculate the final precessed coordinates.

    mu_ra,mu_dec : float
        The proper motion in mas/yr in right ascension and declination. If these
        are provided along with `jd`, the total proper motion of the object will
        be taken into account to calculate the final precessed coordinates.

    outscalar : bool
        If True, converts the output coordinates from one-element np.arrays to
        scalars.

    Returns
    -------

    precessed_ra, precessed_dec : float
        A tuple of precessed equatorial coordinates in decimal degrees at
        `epoch_two` taking into account proper motion if `jd`, `mu_ra`, and
        `mu_dec` are provided.

    '''

    arg_8, arg_9 = np.radians(arg_0), np.radians(arg_1)

    if ((arg_5 != 0.0) and (arg_6 != 0.0) and arg_4):

        arg_10 = JD2000 + (arg_2 - arg_3)*365.25
        arg_8 = (
            arg_8 +
            (arg_4 - arg_10)*arg_5*MAS_P_YR_TO_RAD_P_DAY/np.cos(arg_9)
        )
        arg_9 = arg_9 + (arg_4 - arg_10)*arg_6*MAS_P_YR_TO_RAD_P_DAY

    arg_11 = np.cos(arg_8)
    arg_12 = np.cos(arg_9)
    arg_13 = np.sin(arg_8)
    arg_14 = np.sin(arg_9)

    if arg_2 != arg_3:

        arg_15 = 1.0e-3 * (arg_3 - arg_2)
        arg_16 = 1.0e-3 * (arg_2 - 2000.0)

        arg_17 = ( arg_15*ARCSEC_TO_RADIANS * (23062.181 + arg_16*(139.656 + 0.0139*arg_16) +
                                      arg_15*(30.188 - 0.344*arg_16+17.998*arg_15)) )
        arg_18 = arg_15*arg_15*ARCSEC_TO_RADIANS*(79.280 + 0.410*arg_16 + 0.205*arg_15) + arg_17
        arg_19 = (
            ARCSEC_TO_RADIANS*arg_15*(20043.109 - arg_16*(85.33 + 0.217*arg_16) +
                                  arg_15*(-42.665 - 0.217*arg_16 - 41.833*arg_16))
        )
        arg_20, arg_21, arg_22 = np.sin(arg_17), np.sin(arg_18), np.sin(arg_19)
        arg_23, arg_24, arg_25 = np.cos(arg_17), np.cos(arg_18), np.cos(arg_19)

        arg_26 = np.matrix([[arg_23*arg_24*arg_25 - arg_20*arg_21,
                                 arg_20*arg_24 + arg_23*arg_21*arg_25,
                                 arg_23*arg_22],
                                [-arg_23*arg_21 - arg_20*arg_24*arg_25,
                                 arg_23*arg_24 - arg_20*arg_21*arg_25,
                                 -arg_20*arg_22],
                                [-arg_24*arg_22,
                                 -arg_21*arg_22,
                                 arg_25]])

        arg_26 = arg_26.transpose()

        arg_27 = (np.matrix([arg_12*arg_11, arg_12*arg_13, arg_14])).transpose()

        arg_28 = arg_26 * arg_27

        arg_29 = np.arctan2(arg_28[1],arg_28[0])
        arg_30 = np.arcsin(arg_28[2])

        arg_31 = np.rad2deg(arg_29)
        arg_32 = np.rad2deg(arg_30)

        if arg_31 < 0.0:
            arg_31 = arg_31 + 360.0

        if arg_7:
            return float(arg_31), float(arg_32)
        else:
            return arg_31, arg_32

    else:

        # if the epochs are the same and no proper motion, this will be the same
        # as the input values. if the epochs are the same, but there IS proper
        # motion (and a given JD), then these will be perturbed from the input
        # values of ra, dec by the appropriate amount of motion
        return np.degrees(arg_8), np.degrees(arg_9)


def Func(arg_0):
    '''This returns True if only one True-ish element exists in `iterable`.

    Parameters
    ----------

    iterable : iterable

    Returns
    -------

    bool
        True if only one True-ish element exists in `iterable`. False otherwise.

    '''

    # return True if exactly one true found
    arg_1 = iter(arg_0)

    # consume from "i" until first true or it's exhausted
    arg_2 = any(arg_1)

    # carry on consuming until another true value / exhausted
    arg_3 = any(arg_1)

    return arg_2 and not arg_3


def Func(
        arg_0,
        arg_1,
        arg_2=None,
        arg_3=None,
        arg_4=None,
        arg_5=False
):
    '''This calculates the future epochs for a transit, given a period and a
    starting epoch

    The equation used is::

        t_mid = period*epoch + t0

    Default behavior if no kwargs are used is to define `t0` as the median
    finite time of the passed `t_mid` array.

    Only one of `err_t_mid` or `t0_fixed` should be passed.

    Parameters
    ----------

    t_mid : np.array
        A np.array of transit mid-time measurements

    period : float
        The period used to calculate epochs, per the equation above. For typical
        use cases, a period precise to ~1e-5 days is sufficient to get correct
        epochs.

    err_t_mid : None or np.array
        If provided, contains the errors of the transit mid-time
        measurements. The zero-point epoch is then set equal to the average of
        the transit times, weighted as `1/err_t_mid^2` . This minimizes the
        covariance between the transit epoch and the period (e.g., Gibson et
        al. 2013). For standard O-C analysis this is the best method.

    t0_fixed : None or float:
        If provided, use this t0 as the starting epoch. (Overrides all others).

    t0_percentile : None or float
        If provided, use this percentile of `t_mid` to define `t0`.

    Returns
    -------

    tuple
        This is the of the form `(integer_epoch_array, t0)`.
        `integer_epoch_array` is an array of integer epochs (float-type),
        of length equal to the number of *finite* mid-times passed.

    '''

    arg_6 = np.array([isinstance(arg_2,np.ndarray),
                         arg_3,
                         arg_4])
    if not _single_true(arg_6) and not np.all(~arg_6.astype(bool)):
        raise AssertionError(
            'can have at most one of err_t_mid, t0_fixed, t0_percentile')

    arg_0 = arg_0[np.isfinite(arg_0)]
    arg_7 = len(arg_0)

    if arg_3:
        arg_8 = arg_3
    elif isinstance(arg_2,np.ndarray):
        # get the weighted average. then round it to the nearest transit epoch.
        arg_9 = np.average(arg_0, weights=1/arg_2**2)
        arg_10 = np.arange(min(arg_0), max(arg_0)+arg_1, arg_1)
        arg_8 = arg_10[np.argmin(np.abs(arg_10 - arg_9))]
    else:
        if not arg_4:
            # if there are an odd number of times, take the median time as
            # epoch=0.  elif there are an even number of times, take the lower
            # of the two middle times as epoch=0.
            if arg_7 % 2 == 1:
                arg_8 = np.median(arg_0)
            else:
                arg_8 = arg_0[int(arg_7/2)]
        else:
            arg_8 = np.sort(arg_0)[int(arg_7*arg_4/100)]

    arg_11 = (arg_0 - arg_8)/arg_1

    # do not convert numpy entries to actual ints, because np.nan is float type
    arg_12 = np.round(arg_11, 0)

    if arg_5:
        LOGINFO('epochs before rounding')
        LOGINFO('\n{:s}'.format(repr(arg_11)))
        LOGINFO('epochs after rounding')
        LOGINFO('\n{:s}'.format(repr(arg_12)))

    return arg_12, arg_8


def Func(arg_0, arg_1=False):
    '''This converts a UTC JD to a Python `datetime` object or ISO date string.

    Parameters
    ----------

    jd : float
        The Julian date measured at UTC.

    returniso : bool
        If False, returns a naive Python `datetime` object corresponding to
        `jd`. If True, returns the ISO format string corresponding to the date
        and time at UTC from `jd`.

    Returns
    -------

    datetime or str
        Depending on the value of `returniso`.

    '''

    arg_2 = astime.Time(arg_0, format='jd', scale='utc')

    if arg_1:
        return arg_2.iso
    else:
        return arg_2.datetime