text:
from __future__ import (division, print_function, absolute_import,
                        unicode_literals)

__all__ = ["API", "KOI", "Planet", "Star", "LightCurve", "TargetPixelFile"]

import os
import re
import json
import shutil
import logging
from itertools import product
from functools import partial
from tempfile import NamedTemporaryFile

import six
from six.moves import urllib

# Optional dependencies.
try:
    import pyfits
except ImportError:
    try:
        import astropy.io.fits as pyfits
    except ImportError:
        pyfits = None

try:
    import fitsio
except ImportError:
    fitsio = None

try:
    import numpy as np
except ImportError:
    np = None

try:
    import matplotlib.pyplot as pl
except ImportError:
    pl = None

try:
    from tornado import gen
    import tornado.ioloop
    from tornado.httpclient import AsyncHTTPClient, HTTPRequest
except ImportError:
    AsyncHTTPClient = None

    def async_download(*args, **kwargs):
        raise ImportError("You need to install tornado to do an async "
                          "download")
else:
    def handle_response(obj, response):
        if response.error:
            response.rethrow()
        obj._save_fetched_file(response.body)

    @gen.coroutine
    def _async_download(lcs, clobber=False):
        client = AsyncHTTPClient(max_clients=10)
        to_fetch = [l for l in lcs if clobber or not l.cache_exists]
        responses = yield [client.fetch(HTTPRequest(l.url))
                           for l in to_fetch]
        [handle_response(to_fetch[i], r) for i, r in enumerate(responses)]
        raise gen.Return()

    def async_download(lcs, clobber=False):
        f = partial(_async_download, lcs, clobber=clobber)
        ioloop = tornado.ioloop.IOLoop.instance()
        ioloop.run_sync(f)

from .config import KPLR_ROOT
from . import mast

class API(object):
    """
    Interface with MAST and Exoplanet Archive APIs.

    :param data_root: (optional)
        The local base directory where any data should be downloaded to. This
        can also be set using the ``KPLR_ROOT`` environment variable. The
        default value is ``~/.kplr``.

    """

    ea_url = ("http://exoplanetarchive.ipac.caltech.edu/cgi-bin/nstedAPI"
              "/nph-nstedAPI")
    mast_url = "http://archive.stsci.edu/{0}/{1}/search.php"

    def __init__(self, data_root=None):
        self.data_root = data_root
        if data_root is None:
            self.data_root = KPLR_ROOT

    def __str__(self):
        return "<API(data_root=\"{0}\")>".format(self.data_root)

    def __unicode__(self):
        return self.__str__()

    def __repr__(self):
        return self.__str__()

    def ea_request(self, table, sort=None, **params):
        """
        Submit a request to the Exoplanet Archive API and return a dictionary.

        :param table:
            The table that you want to search.

        :param **params:
            Any other search parameters.

        """
        params["table"] = table

        # Deal with sort order.
        if sort is not None:
            if isinstance(sort, six.string_types):
                params["order"] = sort
            else:
                params["order"] = sort[0]
                if sort[1] == -1:
                    params["order"] += "+desc"

        # Format the URL in the *horrifying* way that EA needs it to be...
        # they don't un-escape the HTTP parameters!!!
        payload = ["{0}={1}".format(k, urllib.parse.quote_plus(v, "\"'+"))
                   for k, v in params.items()]

        # Send the request.
        encoded_data = "&".join(payload).encode("ascii")
        r = urllib.request.Request(self.ea_url, data=encoded_data)
        handler = urllib.request.urlopen(r)
        code = handler.getcode()
        txt = handler.read().decode("ascii")

        # Hack because Exoplanet Archive doesn't return HTTP errors.
        if int(code) != 200 or "ERROR" in txt:
            full_url = handler.geturl() + "?" + "&".join(payload)
            raise APIError(code, full_url, txt)

        # Parse the CSV output.
        csv = txt.splitlines()
        columns = csv[0].split(",")
        result = []
        for line in csv[1:]:
            result.append(dict(zip(columns, line.split(","))))

        return [self._munge_dict(row) for row in result]

    def mast_request(self, category, adapter=None, sort=None, mission="kepler",
                     **params):
        """
        Submit a request to the MAST API and return a dictionary of parameters.

        :param category:
            The table that you want to search.

        :param **params:
            Any other search parameters.

        """
        params["action"] = params.get("action", "Search")
        params["outputformat"] = "JSON"
        params["coordformat"] = "dec"
        params["verb"] = 3

        # Deal with sort order.
        if sort is not None:
            if isinstance(sort, six.string_types):
                params["ordercolumn1"] = sort
            else:
                params["ordercolumn1"] = sort[0]
                if sort[1] == -1:
                    params["descending1"] = "on"

        # Send the request.
        encoded_data = urllib.parse.urlencode(params).encode("ascii")
        r = urllib.request.Request(self.mast_url.format(mission, category),
                                   data=encoded_data)
        handler = urllib.request.urlopen(r)
        code = handler.getcode()
        txt = handler.read().decode("ascii")
        if int(code) != 200:
            full_url = handler.geturl() + "?" + urllib.parse.urlencode(params)
            raise APIError(code, full_url, txt)

        # Check for no rows found.
        if "no rows" in txt:
            return []

        # Parse the JSON.
        try:
            result = json.loads(txt)
        except ValueError:
            full_url = handler.geturl() + "?" + urllib.parse.urlencode(params)
            raise APIError(code, full_url,
                           "No JSON object could be decoded.\n" + txt)

        # Fake munge the types if no adapter was provided.
        if adapter is None:
            return [self._munge_dict(row) for row in result]

        return [adapter(row) for row in result]

    def _munge_dict(self, row):
        """
        Iterate through a dictionary and try to interpret the data types in a
        sane way.

        :param row:
            A dictionary of (probably) strings.

        :returns new_row:
            A dictionary with the same keys as ``row`` but reasonably typed
            values.

        """
        tmp = {}
        for k, v in row.items():
            # Good-god-what-type-is-this-parameter?!?
            try:
                tmp[k] = int(v)
            except ValueError:
                try:
                    tmp[k] = float(v)
                except ValueError:
                    tmp[k] = v

            # Empty entries are mapped to None.
            if v == "":
                tmp[k] = None

        return tmp

    def kois(self, **params):
        """
        Get a list of KOIs from The Exoplanet Archive.

        :param **params:
            The search parameters for the Exoplanet Archive API.

        """
        params["select"] = params.get("select", "*")
        return [KOI(self, k) for k in self.ea_request("cumulative", **params)]

    def koi(self, koi_number, **params):
        """
        Find a single KOI given a KOI number (e.g. 145.01).

        :param koi_number:
            The number identifying the KOI. This should be a float with the
            ``.0N`` for some value of ``N``.

        """
        kois = self.kois(where="kepoi_name+like+'K{0:08.2f}'"
                         .format(float(koi_number)), **params)
        if not len(kois):
            raise ValueError("No KOI found with the number: '{0}'"
                             .format(koi_number))
        return kois[0]

    def planets(self, **params):
        """
        Get a list of confirmed (Kepler) planets from MAST.

        :param **params:
            The search parameters for the MAST API.

        """
        planets = self.mast_request("confirmed_planets",
                                    adapter=mast.planet_adapter, **params)
        return [Planet(self, p) for p in planets]

    def planet(self, name):
        """
        Get a planet by the Kepler name (e.g. "6b" or "Kepler-62b").

        :param name:
            The name of the planet.

        """
        # Parse the planet name.
        matches = re.findall("([0-9]+)[-\s]*([a-zA-Z])", name)
        if len(matches) != 1:
            raise ValueError("Invalid planet name '{0}'".format(name))
        kepler_name = "Kepler-{0} {1}".format(*(matches[0]))

        # Query the API.
        planets = self.planets(kepler_name=kepler_name, max_records=1)
        if not len(planets):
            raise ValueError("No planet found with the name: '{0}'"
                             .format(kepler_name))
        return planets[0]

    def stars(self, **params):
        """
        Get a list of KIC targets from MAST. Only return up to 100 results by
        default.

        :param **params:
            The query parameters for the MAST API.

        """
        params["max_records"] = params.pop("max_records", 100)
        stars = self.mast_request("kic10", adapter=mast.star_adapter,
                                  **params)
        return [Star(self, s) for s in stars]

    def star(self, kepid):
        """
        Get a KIC target by id from MAST.

        :param kepid:
            The integer ID of the star in the KIC.

        """
        stars = self.stars(kic_kepler_id=kepid, max_records=1)
        if not len(stars):
            raise ValueError("No KIC target found with id: '{0}'"
                             .format(kepid))
        return stars[0]

    def k2_stars(self, **params):
        """
        Get a list of EPIC targets from MAST. Only return up to 100 results by
        default.

        :param **params:
            The query parameters for the MAST API.

        """
        params["max_records"] = params.pop("max_records", 100)
        stars = self.mast_request("epic", adapter=mast.epic_adapter,
                                  mission="k2", **params)
        return [K2Star(self, s) for s in stars]

    def k2_star(self, id_):
        """
        Get a EPIC target by id from MAST.

        :param id:
            The integer ID of the star in the EPIC.

        """
        stars = self.k2_stars(id=id_, max_records=1)
        if not len(stars):
            raise ValueError("No EPIC target found with id: '{0}'"
                             .format(id_))
        return stars[0]

    def _data_search(self, kepler_id=None, short_cadence=True,
                     adapter=mast.dataset_adapter, **params):
        """
        Run a generic data search on MAST to return a list of dictionaries
        describing the data products.

        :param kepler_id:
            The KIC ID of the target star.

        :param short_cadence:
            A boolean flag that determines whether or not the short cadence
            data should be included.

        :param ** params:
            Any other search parameters.

        """
        if kepler_id is not None:
            params["ktc_kepler_id"] = kepler_id
        if not short_cadence:
            params["ktc_target_type"] = "LC"

        data_list = self.mast_request("data_search", adapter=adapter,
                                      **params)
        return data_list

    def light_curves(self, kepler_id=None, short_cadence=True, fetch=False,
                     clobber=False, async=False, **params):
        """
        Find the set of light curves associated with a KIC target.

        :param kepler_id:
            The KIC ID of the target star.

        :param short_cadence:
            A boolean flag that determines whether or not the short cadence
            data should be included. (default: True)

        :param fetch:
            A boolean flag that determines whether or not the data file should
            be downloaded.

        :param clobber:
            A boolean flag that determines whether or not the data file should
            be overwritten even if it already exists.

        :param async:
            If ``True``, download the files asynchronously using Tornado.

        :param ** params:
            Other search parameters to be passed to the MAST data search.

        """
        lcs = [LightCurve(self, d) for d in self._data_search(kepler_id,
               short_cadence=short_cadence, **params)]
        if fetch:
            if async:
                async_download(lcs, clobber=clobber)
            else:
                [l.fetch(clobber=clobber) for l in lcs]
        return lcs

    def target_pixel_files(self, kepler_id=None, short_cadence=True,
                           fetch=False, clobber=False, async=False,
                           cls=None, **params):
        """
        Find the set of target pixel files associated with a KIC target.

        :param kepler_id:
            The KIC ID of the target star.

        :param short_cadence:
            A boolean flag that determines whether or not the short cadence
            data should be included. (default: True)

        :param fetch:
            A boolean flag that determines whether or not the data file should
            be downloaded.

        :param clobber:
            A boolean flag that determines whether or not the data file should
            be overwritten even if it already exists.

        :param async:
            If ``True``, download the files asynchronously using Tornado.

        :param ** params:
            Other search parameters to be passed to the MAST data search.

        """
        if cls is None:
            cls = TargetPixelFile
        tpfs = [cls(self, d) for d in self._data_search(kepler_id,
                short_cadence=short_cadence, **params)]
        if fetch:
            if async:
                async_download(tpfs, clobber=clobber)
            else:
                [l.fetch(clobber=clobber) for l in tpfs]
        return tpfs

class APIError(Exception):
    """
    Exception raised when an API request fails.

    :param code:
        The HTTP status code that caused the failure.

    :param url:
        The endpoint (with parameters) of the request.

    :param txt:
        A human readable description of the error.

    """

    def __init__(self, code, url, txt):
        super(APIError, self).__init__(("The API returned code {0} for URL: "
                                        "'{1}' with message:\n{2}")
                                       .format(code, url, txt))
        self.code = code
        self.txt = txt
        self.url = url

class Model(object):
    """
    An abstract base class that provides functions for converting the JSON
    dictionaries returned by the API into Python objects with the correct
    properties.

    :param api:
        A reference to the :class:`API` object that made the request.

    :param params:
        The dictionary of values returned by the API.

    """

    # A format string that generates a unique identifier for the model that
    # should be overloaded by subclasses.
    _id = "..."

    def __init__(self, api, params):
        self.api = api
        self.params = params
        for k, v in params.items():
            setattr(self, k, v)

        self._name = self._id.format(**params)

    def __str__(self):
        return "<{0}({1})>".format(self.__class__.__name__, self._name)

    def __unicode__(self):
        return self.__str__()

    def __repr__(self):
        return self.__str__()

    def get_light_curves(self, **kwargs):
        """
        Get a list of light curve datasets for the model and optionally
        download the FITS files. See :func:`API.light_curves` for parameter
        documentation.

        """
        return self.api.light_curves(self.kepid, **kwargs)

    def get_target_pixel_files(self, **kwargs):
        """
        Get a list of target pixel datasets for the model and optionally
        download the FITS files. See :func:`API.target_pixel_files` for
        parameter documentation.

        """
        return self.api.target_pixel_files(self.kepid, **kwargs)

class KOI(Model):
    """
    A model specifying a Kepler Object of Interest (KOI).

    """

    _id = "\"{kepoi_name}\""

    def __init__(self, *args, **params):
        super(KOI, self).__init__(*args, **params)
        self._star = None

    @property
    def star(self):
        """
        The :class:`Star` entry from the Kepler Input Catalog associated with
        this object.

        """
        if self._star is None:
            self._star = self.api.star(self.kepid)
        return self._star

class Planet(Model):
    """
    A confirmed planet from the `MAST confirmed_planets table
    <http://archive.stsci.edu/search_fields.php?mission=kepler_cp>`_. This
    table has far less—and far less accurate—information than the KOI table
    so it's generally a good idea to use the ``koi`` property to access the
    catalog values.

    """

    _id = "\"{kepler_name}\""

    def __init__(self, *args, **params):
        super(Planet, self).__init__(*args, **params)
        self._koi = None
        self._star = None

    @property
    def koi(self):
        """
        The :class:`KOI` entry that led to this planet. The KOI table is much
        more complete so the use of this object tends to be preferred over the
        built in :class:`Planet` property values.

        """
        if self._koi is None:
            self._koi = self.api.koi(self.koi_number)
        return self._koi

    @property
    def star(self):
        """
        The :class:`Star` entry from the Kepler Input Catalog associated with
        this object.

        """
        if self._star is None:
            self._star = self.api.star(self.kepid)
        return self._star

class Star(Model):
    """
    A star from the `Kepler Input Catalog (KIC)
    <http://archive.stsci.edu/search_fields.php?mission=kic10>`_.

    """

    _id = "{kic_kepler_id}"

    def __init__(self, *args, **params):
        super(Star, self).__init__(*args, **params)
        self.kepid = self.kic_kepler_id
        self._kois = None

    @property
    def kois(self):
        """
        The list of :class:`KOI` entries found in this star's light curve.

        """
        if self._kois is None:
            self._kois = self.api.kois(where="kepid like '{0}'"
                                       .format(self.kepid))
        return self._kois

class K2Star(Model):
    """
    A star from the `K2 EPIC Catalog (EPIC)
    <http://archive.stsci.edu/search_fields.php?mission=epic>`_.

    """

    _id = "{id}"

    def get_light_curves(self, **kwargs):
        """
        Get a list of light curve datasets for the model and optionally
        download the FITS files. See :func:`API.light_curves` for parameter
        documentation.

        """
        raise NotImplementedError("There aren't any light curves for K2 stars")

    def get_target_pixel_files(self, **kwargs):
        """
        Get a list of target pixel datasets for the model and optionally
        download the FITS files. See :func:`API.target_pixel_files` for
        parameter documentation.

        """
        return self.api.target_pixel_files(ktc_k2_id=self.id, mission="k2",
                                           adapter=mast.k2_dataset_adapter,
                                           cls=K2TargetPixelFile,
                                           **kwargs)

class _datafile(Model):

    _id = "\"{sci_data_set_name}_{ktc_target_type}\""

    kepid_template = "{ktc_kepler_id:09d}"

    product = None
    suffixes = None
    filetype = None

    def __init__(self, *args, **params):
        super(_datafile, self).__init__(*args, **params)
        self.kepid = self.kepid_template.format(**(self.params))
        suffix = self.suffixes[int(self.ktc_target_type != "LC")]
        self._filename = "{0}_{1}{2}".format(self.sci_data_set_name,
                                             suffix, self.filetype).lower()

    @classmethod
    def local(cls, fn):
        self = cls.__new__(cls)
        self._name = "'local:{0}'".format(fn)
        self.base_dir = ""
        self._filename = fn
        return self

    @property
    def base_dir(self):
        """
        The local base directory for these types of products.

        """
        return os.path.join(self.api.data_root, "data", self.product,
                            self.kepid)

    @property
    def filename(self):
        """
        The local filename of the data file. This file is only guaranteed to
        exist after ``fetch()`` has been called.

        """
        return os.path.join(self.base_dir, self._filename)

    @property
    def url(self):
        """
        The remote URL for the data file on the MAST servers.

        """
        base_url = "http://archive.stsci.edu/pub/kepler/{0}/{1}/{2}/{3}"
        return base_url.format(self.product, self.kepid[:4], self.kepid,
                               self._filename)

    def open(self, clobber=False, **kwargs):
        """
        Open the FITS data file and return the ``pyfits.HDUList``. This will
        download the file if it isn't already saved locally.

        :param clobber:
            Overwrite the local file even if it exists? This can be helpful if
            the file gets corrupted somehow.

        :param **kwargs:
            Any keyword arguments that you would like to pass to the
            :func:`pyfits.open` function.

        """
        if pyfits is None:
            raise ImportError("The pyfits module is required to read data "
                              "files.")
        fn = self.filename
        self.fetch(clobber=clobber)
        return pyfits.open(fn, **kwargs)

    def read(self, clobber=False, **kwargs):
        """
        Read a FITS file using `fitsio <https://github.com/esheldon/fitsio>`_.

        :param clobber:
            Overwrite the local file even if it exists? This can be helpful if
            the file gets corrupted somehow.

        :param **kwargs:
            Any keyword arguments that you would like to pass to the
            :func:`fitsio.read` function.

        """
        if fitsio is None:
            raise ImportError("The fitsio module is required to read data "
                              "files.")
        fn = self.filename
        self.fetch(clobber=clobber)
        return fitsio.read(fn, **kwargs)

    @property
    def cache_exists(self):
        return os.path.exists(self.filename)

    def fetch(self, clobber=False):
        """
        Download the data file from the server and save it locally. The local
        file will be saved in the directory specified by the ``data_root``
        property of the API.

        :param clobber:
            Should an existing local file be overwritten? (default: False)

        """
        # Check if the file already exists.
        filename = self.filename
        if self.cache_exists and not clobber:
            logging.info("Found local file: '{0}'".format(filename))
            return self

        # Fetch the remote file.
        url = self.url
        logging.info("Downloading file from: '{0}'".format(url))
        r = urllib.request.Request(url)
        handler = urllib.request.urlopen(r)
        code = handler.getcode()
        if int(code) != 200:
            raise APIError(code, url, "")
        return self._save_fetched_file(handler.read())

    def _save_fetched_file(self, data):
        # Make sure that the root directory exists.
        try:
            os.makedirs(self.base_dir)
        except os.error:
            pass

        # Save the contents of the file.
        filename = self.filename
        logging.info("Saving file to: '{0}'".format(filename))

        # Atomically write to disk.
        # http://stackoverflow.com/questions/2333872/ \
        #        atomic-writing-to-file-with-python
        f = NamedTemporaryFile("wb", delete=False)
        f.write(data)
        f.flush()
        os.fsync(f.fileno())
        f.close()
        shutil.move(f.name, filename)

        return self

    def plot(self):
        if np is None:
            raise ImportError("numpy is required for plotting.")
        if pl is None:
            raise ImportError("matplotlib is required for plotting.")

class LightCurve(_datafile):
    """
    A reference to a light curve dataset on the MAST severs. This object
    handles local caching of the file in a strict directory structure.

    """

    product = "lightcurves"
    suffixes = ["llc", "slc"]
    filetype = ".fits"

    def plot(self):
        """
        Make a simple diagnostic plot of the light curve.

        :return fig:
            A :class:`matplotlib.Figure` object containing the plot.

        """
        super(LightCurve, self).plot()

        # Load the data.
        with self.open() as f:
            data = f[1].data
        time, sapflux, pdcflux, qual = (data["time"], data["sap_flux"],
                                        data["pdcsap_flux"],
                                        data["sap_quality"])

        # Set up the figure.
        fig, axes = pl.subplots(2, 1, figsize=(6, 6))
        fig.subplots_adjust(left=0.17, top=0.99, right=0.99,
                            wspace=0.0, hspace=0.0)

        # Plot the data.
        m = np.isfinite(time)
        xlim = [np.min(time[m]), np.max(time[m])]
        for i, (f, nm) in enumerate(zip([sapflux, pdcflux],
                                        ["sap flux", "pdc flux"])):
            ax = axes[i]
            m = np.isfinite(time) * np.isfinite(f)
            m1 = m * (qual == 0)
            m2 = m * (qual != 0)
            mu = np.median(f[m1])
            f1 = (f[m1] / mu - 1) * 1e6
            ax.plot(time[m1], f1, ".k", ms=3, alpha=0.6)
            ax.plot(time[m2], (f[m2] / np.median(f[m2]) - 1) * 1e6, ".r", ms=3,
                    alpha=0.6)
            ax.set_xlim(xlim)
            ax.set_ylim(np.min(f1), np.max(f1))
            ax.annotate("relative " + nm + " [ppm]",
                        xy=(1, 1), xycoords="axes fraction",
                        xytext=(-3, -3), textcoords="offset points",
                        horizontalalignment="right", verticalalignment="top")

        axes[0].set_xticklabels([])
        axes[1].set_xlabel("time [KBJD]")

        return fig

class TargetPixelFile(_datafile):
    """
    A reference to a target pixel dataset on the MAST severs. Like the
    :class:`LightCurve` object, this object handles local caching of the file
    in a strict directory structure.

    """

    product = "target_pixel_files"
    suffixes = ["lpd-targ", "spd-targ"]
    filetype = ".fits.gz"

    def plot(self, normed=False):
        """
        Make a simple diagnostic plot of the target pixel light curves. The
        pixels in the optimal aperture are plotted in black and those outside
        are plotted in red.

        :return fig:
            A :class:`matplotlib.Figure` object containing the grid of axes.

        """
        super(TargetPixelFile, self).plot()

        # Load the data.
        with self.open() as f:
            data = f[1].data
            aperture = f[2].data
        time, flux = data["time"], data["flux"]
        nx, ny = flux[0].shape

        # Set up the figures.
        factor = 3.0
        dx = factor * nx
        dy = factor * ny
        fig, axes = pl.subplots(nx, ny, figsize=(dy, dx))
        fig.subplots_adjust(left=0.0, right=1.0, bottom=0.0, top=1.0,
                            wspace=0.0, hspace=0.0)

        # Loop over the pixels.
        m = np.isfinite(time)
        xlim = [np.min(time[m]), np.max(time[m])]
        ylim = [0., 0.]
        for xi, yi in product(range(nx), range(ny)):
            ax = axes[xi, yi]
            f = flux[:, xi, yi]

            m = np.isfinite(f)
            if normed:
                if len(f[m]) > 0:
                    ax.plot(time[m], (f[m]/np.median(f[m]))-1.0, ".",
                            color="rk"[int(aperture[xi, yi] == 3)], ms=2)
                    minval = np.where(
                        np.min((f[m]/np.median(f[m]))-1.0) < ylim[0],
                        np.min((f[m]/np.median(f[m]))-1.0),
                        ylim[0])
                    maxval = np.where(
                        np.max((f[m]/np.median(f[m]))-1.0) > ylim[1],
                        np.max((f[m]/np.median(f[m]))-1.0),
                        ylim[1])
                    minmax = [minval, maxval]
            else:
                ax.plot(time[m], f[m], ".",
                        color="rk"[int(aperture[xi, yi] == 3)], ms=2)

            ax.set_xlim(xlim)
            ax.set_xticklabels([])
            ax.set_yticklabels([])

        # If normed, put everything on the same scale
        if normed:
            for ax in axes.flatten():
                try:
                    ax.set_ylim(minmax)
                except TypeError:
                    continue

        return fig

class K2TargetPixelFile(TargetPixelFile):
    """
    A reference to a K2 target pixel dataset on the MAST severs. Like the
    :class:`LightCurve` object, this object handles local caching of the file
    in a strict directory structure.

    """

    _id = "\"{sci_data_set_name}_{ktc_target_type}\""
    kepid_template = "{ktc_k2_id:09d}"
    product = "target_pixel_files"
    suffixes = ["lpd-targ", "spd-targ"]
    filetype = ".fits.gz"

    @property
    def base_dir(self):
        """
        The local base directory for these types of products.

        """
        return os.path.join(self.api.data_root, "data", "k2", self.product,
                            self.kepid)

    @property
    def filename(self):
        """
        The local filename of the data file. This file is only guaranteed to
        exist after ``fetch()`` has been called.

        """
        return os.path.join(self.base_dir, self._filename)

    @property
    def url(self):
        """
        The remote URL for the data file on the MAST servers.

        """
        if self.sci_campaign != 0:
            raise NotImplementedError("Only campaign 0 is supported for now")
        base_url = "http://archive.stsci.edu/pub/k2/"
        if self.ktc_k2_id < 201000000:
            base_url += "{0}/c0/200000000/{1:05d}/{2}"
        else:
            base_url += "{{0}}/c0/{0}/{{1:05d}}/{{2}}" \
                .format(int(int(self.ktc_k2_id * 1e-5) * 1e5))

        return base_url.format(self.product,
                               int(int(int(self.kepid[-5:][-5:])*1e-3)*1e3),
                               self._filename)


text:
function [B] = cfilter(A, fun, varargin)
% CFILTER Filter elements from a collection.
%
% SYNTAX:
% B = cfilter(A, @fun)
%
% INPUT:
% A = a vector, either numeric, a cell array, or a struct array.
% fun = a function of one of the following prototypes:
%           function [b] = fun(item)
%           function [b] = fun(item, idx)
%       with:
%           item = an item from the collection.
%           idx = index of the item within the collection.
%           b = a boolean, indicating if the item should be kept.
%
% OUTPUT:
% B = a vector, with only the elements from A remaining for which the filter
%       function returned 'true'.

keep = cmap(A, fun, varargin{:});
B = A(keep);

end

redpajama_set_name: RedPajamaGithub


text:
rm -rf output
node r.js -o build.js
node r.js -o cssIn=../css/styles.css out=output/css/styles.css

cp ../index.html output/index.html
REQUIRE_VERSION='2.1.5'
SEDCMD='sed -i'
if [[ $OSTYPE == *"darwin"* ]]; then
  SEDCMD=$SEDCMD' .tmp'
fi
SEDCMD=$SEDCMD' s/js\/libs\/require\/require.js/http:\/\/requirejs.org\/docs\/release\/'$REQUIRE_VERSION'\/minified\/require.js/g output/index.html'
$SEDCMD
rm -f output/*.tmp


text:
"""Functional tests for slice op."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import numpy as np
from six.moves import xrange  # pylint: disable=redefined-builtin

from tensorflow.python.eager import backprop
from tensorflow.python.eager import def_function
from tensorflow.python.framework import constant_op
from tensorflow.python.framework import dtypes
from tensorflow.python.framework import errors_impl
from tensorflow.python.framework import ops
from tensorflow.python.framework import test_util
from tensorflow.python.ops import array_ops
from tensorflow.python.ops import gradients_impl
from tensorflow.python.ops import math_ops
from tensorflow.python.ops import nn_ops
from tensorflow.python.platform import test

class SliceTest(test.TestCase):

  def testEmpty(self):
    inp = np.random.rand(4, 4).astype("f")
    for k in xrange(4):
      with self.cached_session():
        a = constant_op.constant(inp, shape=[4, 4], dtype=dtypes.float32)
        slice_t = a[2, k:k]
        slice_val = self.evaluate(slice_t)
      self.assertAllEqual(slice_val, inp[2, k:k])

  def testInt32(self):
    inp = np.random.rand(4, 4).astype("i")
    for k in xrange(4):
      with self.cached_session():
        a = constant_op.constant(inp, shape=[4, 4], dtype=dtypes.int32)
        slice_t = a[2, k:k]
        slice_val = self.evaluate(slice_t)
      self.assertAllEqual(slice_val, inp[2, k:k])

  def testSlicingWithInt64Index(self):
    with self.cached_session(force_gpu=test.is_gpu_available()):
      a = constant_op.constant([0, 1, 2], dtype=dtypes.int32)

      # Slice using int64 Tensor.
      i = constant_op.constant(1, dtype=dtypes.int64)
      slice_t = a[i]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual(1, slice_val)
      slice_t = a[i:i+1]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual([1], slice_val)

      # Slice using int64 integer.
      i = np.asarray(1).astype(np.int64)
      slice_t = a[i]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual(1, slice_val)
      slice_t = a[i:i+1]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual([1], slice_val)

      a_int32 = constant_op.constant([0, 1, 2], dtype=dtypes.int32)
      slice_t = array_ops.slice(a_int32,
                                np.asarray([1]).astype(np.int64),
                                np.asarray([2]).astype(np.int64))
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual([1, 2], slice_val)

      a_float32 = constant_op.constant([0, 1, 2], dtype=dtypes.float32)
      slice_t = array_ops.slice(a_float32,
                                np.asarray([1]).astype(np.int64),
                                np.asarray([2]).astype(np.int64))
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual([1, 2], slice_val)

  def testSlicingInt64Tensor(self):
    with self.cached_session(force_gpu=test.is_gpu_available()):
      a = constant_op.constant([0, 1, 2], dtype=dtypes.int64)

      # Slice using int32 Tensor.
      i = constant_op.constant(1, dtype=dtypes.int32)
      slice_t = a[i]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual(1, slice_val)
      slice_t = a[i:i + 1]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual([1], slice_val)

      # Slice using int32 integer.
      i = np.asarray(1).astype(np.int32)
      slice_t = a[i]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual(1, slice_val)
      slice_t = a[i:i + 1]
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual([1], slice_val)

      slice_t = array_ops.slice(a, [1], [2])
      slice_val = self.evaluate(slice_t)
      self.assertAllEqual([1, 2], slice_val)

  def testSelectAll(self):
    for _ in range(10):
      with self.cached_session():
        inp = np.random.rand(4, 4, 4, 4).astype("f")
        a = constant_op.constant(inp, shape=[4, 4, 4, 4], dtype=dtypes.float32)

        slice_explicit_t = array_ops.slice(a, [0, 0, 0, 0], [-1, -1, -1, -1])
        slice_implicit_t = a[:, :, :, :]

        self.assertAllEqual(inp, self.evaluate(slice_explicit_t))
        self.assertAllEqual(inp, self.evaluate(slice_implicit_t))
        self.assertEqual(inp.shape, slice_explicit_t.get_shape())
        self.assertEqual(inp.shape, slice_implicit_t.get_shape())

  def testSingleDimension(self):
    for _ in range(10):
      with self.cached_session():
        inp = np.random.rand(10).astype("f")
        a = constant_op.constant(inp, shape=[10], dtype=dtypes.float32)

        hi = np.random.randint(0, 9)
        scalar_t = a[hi]
        scalar_val = self.evaluate(scalar_t)
        self.assertAllEqual(scalar_val, inp[hi])

        if hi > 0:
          lo = np.random.randint(0, hi)
        else:
          lo = 0
        slice_t = a[lo:hi]
        slice_val = self.evaluate(slice_t)
        self.assertAllEqual(slice_val, inp[lo:hi])

  def test3Dimension(self):
    with self.cached_session():
      input_shape = [8, 16, 16, 16, 8]
      total_input_size = 1
      for s in input_shape:
        total_input_size *= s
      inputs = [
          i * 1.0 / total_input_size for i in range(1, total_input_size + 1)
      ]
      a = constant_op.constant(inputs, shape=input_shape, dtype=dtypes.float32)

      filter_shape = [1, 1, 1, 8, 8]
      total_filter_size = 1
      for s in filter_shape:
        total_filter_size *= s
      filters = [
          i * 1.0 / total_filter_size for i in range(1, total_filter_size + 1)
      ]
      f = constant_op.constant(
          filters, shape=filter_shape, dtype=dtypes.float32)

      conv_t = nn_ops.conv3d(
          a, filter=f, strides=[1, 1, 1, 1, 1], padding="VALID")
      slice_t = array_ops.slice(conv_t, [0, 1, 1, 1, 0], [1, 1, 1, 1, 8])
      result = self.evaluate(slice_t)
      expected = [
          0.03028321, 0.03132677, 0.03237033, 0.03341389, 0.03445745, 0.035501,
          0.03654456, 0.03758812
      ]
      self.assertAllClose(expected, result.flatten(), rtol=1e-6)

  def testScalarInput(self):
    input_val = 0
    # Test with constant input; shape inference fails.
    with self.assertRaisesWithPredicateMatch(
        (ValueError, errors_impl.InvalidArgumentError), "out of range"):
      constant_op.constant(input_val)[:].get_shape()

    # Test evaluating with non-constant input; kernel execution fails.
    @def_function.function
    def func(input_t):
      slice_t = input_t[:]
      return slice_t

    with self.assertRaisesWithPredicateMatch(TypeError, "not subscriptable"):
      self.evaluate(func(input_val))

  def testInvalidIndex(self):
    input_val = [1, 2]
    # Test with constant input; shape inference fails.
    with self.assertRaisesWithPredicateMatch(
        (ValueError, errors_impl.InvalidArgumentError), "out of range"):
      constant_op.constant(input_val)[1:, 1:].get_shape()

    # Test evaluating with non-constant input; kernel execution fails.
    @def_function.function
    def func(input_t):
      slice_t = input_t[1:, 1:]
      return slice_t

    with self.assertRaisesWithPredicateMatch(
        TypeError, "must be integers or slices, not tuple"):
      self.evaluate(func(input_val))

  def _testSliceMatrixDim0(self, x, begin, size):
    tf_ans = self.evaluate(array_ops.slice(x, [begin, 0], [size, x.shape[1]]))
    np_ans = x[begin:begin + size, :]
    self.assertAllEqual(tf_ans, np_ans)

  def testSliceMatrixDim0(self):
    x = np.random.rand(8, 4).astype("f")
    self._testSliceMatrixDim0(x, 1, 2)
    self._testSliceMatrixDim0(x, 3, 3)
    y = np.random.rand(8, 7).astype("f")  # 7 * sizeof(float) is not aligned
    self._testSliceMatrixDim0(y, 1, 2)
    self._testSliceMatrixDim0(y, 3, 3)

  def testSingleElementAll(self):
    for _ in range(10):
      with self.cached_session():
        inp = np.random.rand(4, 4).astype("f")
        a = constant_op.constant(inp, shape=[4, 4], dtype=dtypes.float32)

        x, y = np.random.randint(0, 3, size=2).tolist()
        slice_t = a[x, 0:y]
        slice_val = self.evaluate(slice_t)
      self.assertAllEqual(slice_val, inp[x, 0:y])

  def testSimple(self):
    with test_util.use_gpu():
      for dtype in [
          np.uint8,
          np.int8,
          np.uint16,
          np.int16,
          np.int32,
          np.int64,
          np.bool_,
          np.float16,
          np.float32,
          np.float64,
          np.complex64,
          np.complex128,
      ]:
        inp = np.random.rand(4, 4).astype(dtype)
        a = constant_op.constant(
            [float(x) for x in inp.ravel(order="C")],
            shape=[4, 4],
            dtype=dtypes.float32)
        slice_t = array_ops.slice(a, [0, 0], [2, 2])
        slice2_t = a[:2, :2]
        slice_val, slice2_val = self.evaluate([slice_t, slice2_t])
        self.assertAllEqual(slice_val, np.array(inp[:2, :2], dtype=np.float32))
        self.assertAllEqual(slice2_val, np.array(inp[:2, :2], dtype=np.float32))
        self.assertEqual(slice_val.shape, slice_t.get_shape())
        self.assertEqual(slice2_val.shape, slice2_t.get_shape())

  def testComplex(self):
    inp = np.random.rand(4, 10, 10, 4).astype("f")
    a = constant_op.constant(inp, dtype=dtypes.float32)

    x = np.random.randint(0, 9)
    z = np.random.randint(0, 9)
    if z > 0:
      y = np.random.randint(0, z)
    else:
      y = 0
    slice_t = a[:, x, y:z, :]
    self.assertAllEqual(slice_t, inp[:, x, y:z, :])

  def testRandom(self):
    # Random dims of rank 6
    input_shape = np.random.randint(0, 20, size=6)
    inp = np.random.rand(*input_shape).astype("f")
    a = constant_op.constant([float(x) for x in inp.ravel(order="C")],
                             shape=input_shape,
                             dtype=dtypes.float32)
    indices = [0 if x == 0 else np.random.randint(x) for x in input_shape]
    sizes = [
        np.random.randint(0, input_shape[i] - indices[i] + 1) for i in range(6)
    ]
    slice_t = array_ops.slice(a, indices, sizes)
    slice2_t = a[indices[0]:indices[0] + sizes[0],
                 indices[1]:indices[1] + sizes[1],
                 indices[2]:indices[2] + sizes[2],
                 indices[3]:indices[3] + sizes[3],
                 indices[4]:indices[4] + sizes[4],
                 indices[5]:indices[5] + sizes[5]]

    slice_val, slice2_val = self.evaluate([slice_t, slice2_t])

    expected_val = inp[indices[0]:indices[0] + sizes[0],
                       indices[1]:indices[1] + sizes[1],
                       indices[2]:indices[2] + sizes[2],
                       indices[3]:indices[3] + sizes[3],
                       indices[4]:indices[4] + sizes[4],
                       indices[5]:indices[5] + sizes[5]]
    self.assertAllEqual(slice_val, expected_val)
    self.assertAllEqual(slice2_val, expected_val)
    self.assertEqual(expected_val.shape, slice_t.get_shape())
    self.assertEqual(expected_val.shape, slice2_t.get_shape())

  def testPartialShapeInference(self):
    z = array_ops.zeros((1, 2, 3))
    self.assertAllEqual(z.get_shape().as_list(), [1, 2, 3])

    m1 = array_ops.slice(z, [0, 0, 0], [-1, -1, -1])
    self.assertAllEqual(m1.get_shape().as_list(), [1, 2, 3])

    m2 = array_ops.slice(z, [0, 0, 0], [constant_op.constant(1) + 0, 2, -1])
    self.assertAllEqual(m2.get_shape().as_list(), [1, 2, 3])

  def _testGradientSlice(self, input_shape, slice_begin, slice_size):
    with self.cached_session():
      num_inputs = np.prod(input_shape)
      num_grads = np.prod(slice_size)
      inp = np.random.rand(num_inputs).astype("f").reshape(input_shape)
      a = constant_op.constant(
          [float(x) for x in inp.ravel(order="C")],
          shape=input_shape,
          dtype=dtypes.float32)
      slice_t = array_ops.slice(a, slice_begin, slice_size)
      grads = np.random.rand(num_grads).astype("f").reshape(slice_size)
      grad_tensor = constant_op.constant(grads)
      grad = gradients_impl.gradients(slice_t, [a], grad_tensor)[0]
      result = self.evaluate(grad)

    # Create a zero tensor of the input shape ane place
    # the grads into the right location to compare against TensorFlow.
    np_ans = np.zeros(input_shape)
    slices = []
    for i in xrange(len(input_shape)):
      slices.append(slice(slice_begin[i], slice_begin[i] + slice_size[i]))
    np_ans[slices] = grads

    self.assertAllClose(np_ans, result)

  def _testGradientSliceTape(self, input_shape, slice_begin, slice_size):
    with backprop.GradientTape() as tape:
      num_inputs = np.prod(input_shape)
      num_grads = np.prod(slice_size)
      inp = np.random.rand(num_inputs).astype("f").reshape(input_shape)
      a = constant_op.constant([float(x) for x in inp.ravel(order="C")],
                               shape=input_shape,
                               dtype=dtypes.float32)
      tape.watch(a)
      slice_t = array_ops.slice(a, slice_begin, slice_size)
      grads = np.random.rand(num_grads).astype("f").reshape(slice_size)
      grad_tensor = constant_op.constant(grads)
    grad = tape.gradient(slice_t, [a], grad_tensor)[0]
    result = self.evaluate(grad)

    # Create a zero tensor of the input shape ane place
    # the grads into the right location to compare against TensorFlow.
    np_ans = np.zeros(input_shape)
    slices = []
    for i in xrange(len(input_shape)):
      slices.append(slice(slice_begin[i], slice_begin[i] + slice_size[i]))
    np_ans[slices] = grads

    self.assertAllClose(np_ans, result)

  def _testGradientVariableSize(self):
    with self.cached_session():
      inp = constant_op.constant([1.0, 2.0, 3.0], name="in")
      out = array_ops.slice(inp, [1], [-1])
      grad_actual = self.evaluate(gradients_impl.gradients(out, inp)[0])
    self.assertAllClose([0., 1., 1.], grad_actual)

  def _testGradientVariableSizeTape(self):
    with backprop.GradientTape() as tape:
      inp = constant_op.constant([1.0, 2.0, 3.0], name="in")
      tape.watch(inp)
      out = array_ops.slice(inp, [1], [-1])
    grad_actual = self.evaluate(tape.gradient(out, inp))
    self.assertAllClose([0., 1., 1.], grad_actual)

  def _testGradientVariableSize2D(self):
    # Regression test for bug in slice. A low-level bug in Eigen was causing
    # incorrect results for negative indices in multi-dimensional tensors.
    # See b/114318298.
    with self.cached_session():
      x = constant_op.constant([[1., 2., 3.], [4., 5., 6.], [7., 8., 7]])
      loss1 = math_ops.reduce_sum(x[:-1, :-1] * 1.0)
      loss2 = math_ops.reduce_sum(x[:-1][:, :-1])

      g1 = gradients_impl.gradients(loss1, x)[0]
      g2 = gradients_impl.gradients(loss2, x)[0]

      g1_val, g2_val = self.evaluate([g1, g2])
    self.assertAllEqual(g1_val, g2_val)

  def _testGradientVariableSize2DTape(self):
    # Regression test for bug in slice. A low-level bug in Eigen was causing
    # incorrect results for negative indices in multi-dimensional tensors.
    # See b/114318298.
    with backprop.GradientTape(persistent=True) as tape:
      x = constant_op.constant([[1., 2., 3.], [4., 5., 6.], [7., 8., 7]])
      tape.watch(x)
      loss1 = math_ops.reduce_sum(x[:-1, :-1] * 1.0)
      loss2 = math_ops.reduce_sum(x[:-1][:, :-1])

    g1 = tape.gradient(loss1, x)
    g2 = tape.gradient(loss2, x)
    g1_val, g2_val = self.evaluate([g1, g2])
    self.assertAllEqual(g1_val, g2_val)

  def testGradientsAll(self):
    with ops.Graph().as_default():
      # Slice the middle square out of a 4x4 input
      self._testGradientSlice([4, 4], [1, 1], [2, 2])

      # Slice the upper left square out of a 4x4 input
      self._testGradientSlice([4, 4], [0, 0], [2, 2])

      # Slice a non-square input starting from (2,1)
      self._testGradientSlice([4, 4], [2, 1], [1, 2])

      # Slice a 3D tensor
      self._testGradientSlice([3, 3, 3], [0, 1, 0], [2, 1, 1])

      # Use -1 as a slice dimension.
      self._testGradientVariableSize()

      # Use -1 as a slice dimension on a 2D tensor.
      self._testGradientVariableSize2D()

  def testGradientsAllTape(self):
    # Slice the middle square out of a 4x4 input
    self._testGradientSliceTape([4, 4], [1, 1], [2, 2])

    # Slice the upper left square out of a 4x4 input
    self._testGradientSliceTape([4, 4], [0, 0], [2, 2])

    # Slice a non-square input starting from (2,1)
    self._testGradientSliceTape([4, 4], [2, 1], [1, 2])

    # Slice a 3D tensor
    self._testGradientSliceTape([3, 3, 3], [0, 1, 0], [2, 1, 1])

    # Use -1 as a slice dimension.
    self._testGradientVariableSizeTape()

    # Use -1 as a slice dimension on a 2D tensor.
    self._testGradientVariableSize2DTape()

  def testNotIterable(self):
    # Tensor iteration is disabled explicitly for only graph mode.
    with ops.Graph().as_default():
      # NOTE(mrry): If we register __getitem__ as an overloaded
      # operator, Python will valiantly attempt to iterate over the
      # Tensor from 0 to infinity.  This test ensures that this
      # unintended behavior is prevented.
      c = constant_op.constant(5.0)
      with self.assertRaisesRegex(errors_impl.OperatorNotAllowedInGraphError,
                                  "iterating over `tf.Tensor`"):
        for _ in c:
          pass

  def testComputedShape(self):
    # NOTE(mrry): We cannot currently handle partially-known values,
    # because `tf.slice()` uses -1 to specify a wildcard size, and
    # this can't be handled using the
    # `tensor_util.constant_value_as_shape()` trick.
    a = constant_op.constant([[1, 2, 3], [4, 5, 6]])
    begin = constant_op.constant(0)
    size = constant_op.constant(1)
    b = array_ops.slice(a, [begin, 0], [size, 2])
    self.assertEqual([1, 2], b.get_shape())

    # placeholders only make sense in a graph.
    with ops.Graph().as_default():
      a = constant_op.constant([[1, 2, 3], [4, 5, 6]])
      begin = array_ops.placeholder(dtypes.int32, shape=())
      c = array_ops.slice(a, [begin, 0], [-1, 2])
      self.assertEqual([None, 2], c.get_shape().as_list())

  def testSliceOfSlice(self):
    with self.session():
      a = constant_op.constant([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
      b = a[1:, :]
      c = b[:-1, :]
      d = c[1, :]
      res = 2 * d - c[1, :] + a[2, :] - 2 * b[-2, :]
      self.assertAllEqual([0, 0, 0], self.evaluate(res))

if __name__ == "__main__":
  test.main()


text:
This chapter will tell you how to make your library installable through
Composer.

## Every project is a package

As soon as you have a `composer.json` in a directory, that directory is a
package. When you add a [`require`](04-schema.md#require) to a project, you are
making a package that depends on other packages. The only difference between
your project and a library is that your project is a package without a name.

In order to make that package installable you need to give it a name. You do
this by adding the [`name`](04-schema.md#name) property in `composer.json`:

```json
{
    "name": "acme/hello-world",
    "require": {
        "monolog/monolog": "1.0.*"
    }
}
```

In this case the project name is `acme/hello-world`, where `acme` is the vendor
name. Supplying a vendor name is mandatory.

> **Note:** If you don't know what to use as a vendor name, your GitHub
> username is usually a good bet. While package names are case insensitive, the
> convention is all lowercase and dashes for word separation.

## Library Versioning

In the vast majority of cases, you will be maintaining your library using some
sort of version control system like git, svn, hg or fossil. In these cases,
Composer infers versions from your VCS and you **should not** specify a version
in your `composer.json` file. (See the [Versions article](articles/versions.md)
to learn about how Composer uses VCS branches and tags to resolve version
constraints.)

If you are maintaining packages by hand (i.e., without a VCS), you'll need to
specify the version explicitly by adding a `version` value in your `composer.json`
file:

```json
{
    "version": "1.0.0"
}
```

> **Note:** When you add a hardcoded version to a VCS, the version will conflict
> with tag names. Composer will not be able to determine the version number.

### VCS Versioning

Composer uses your VCS's branch and tag features to resolve the version
constraints you specify in your `require` field to specific sets of files.
When determining valid available versions, Composer looks at all of your tags
and branches and translates their names into an internal list of options that
it then matches against the version constraint you provided.

For more on how Composer treats tags and branches and how it resolves package
version constraints, read the [versions](articles/versions.md) article.

## Lock file

For your library you may commit the `composer.lock` file if you want to. This
can help your team to always test against the same dependency versions.
However, this lock file will not have any effect on other projects that depend
on it. It only has an effect on the main project.

If you do not want to commit the lock file and you are using git, add it to
the `.gitignore`.

## Publishing to a VCS

Once you have a VCS repository (version control system, e.g. git) containing a
`composer.json` file, your library is already composer-installable. In this
example we will publish the `acme/hello-world` library on GitHub under
`github.com/username/hello-world`.

Now, to test installing the `acme/hello-world` package, we create a new
project locally. We will call it `acme/blog`. This blog will depend on
`acme/hello-world`, which in turn depends on `monolog/monolog`. We can
accomplish this by creating a new `blog` directory somewhere, containing a
`composer.json`:

```json
{
    "name": "acme/blog",
    "require": {
        "acme/hello-world": "dev-master"
    }
}
```

The name is not needed in this case, since we don't want to publish the blog
as a library. It is added here to clarify which `composer.json` is being
described.

Now we need to tell the blog app where to find the `hello-world` dependency.
We do this by adding a package repository specification to the blog's
`composer.json`:

```json
{
    "name": "acme/blog",
    "repositories": [
        {
            "type": "vcs",
            "url": "https://github.com/username/hello-world"
        }
    ],
    "require": {
        "acme/hello-world": "dev-master"
    }
}
```

For more details on how package repositories work and what other types are
available, see [Repositories](05-repositories.md).

That's all. You can now install the dependencies by running Composer's
[`install`](03-cli.md#install) command!

**Recap:** Any git/svn/hg/fossil repository containing a `composer.json` can be
added to your project by specifying the package repository and declaring the
dependency in the [`require`](04-schema.md#require) field.

## Publishing to packagist

Alright, so now you can publish packages. But specifying the VCS repository
every time is cumbersome. You don't want to force all your users to do that.

The other thing that you may have noticed is that we did not specify a package
repository for `monolog/monolog`. How did that work? The answer is Packagist.

[Packagist](https://packagist.org/) is the main package repository for
Composer, and it is enabled by default. Anything that is published on
Packagist is available automatically through Composer. Since
[Monolog is on Packagist](https://packagist.org/packages/monolog/monolog), we
can depend on it without having to specify any additional repositories.

If we wanted to share `hello-world` with the world, we would publish it on
Packagist as well. Doing so is really easy.

You simply visit [Packagist](https://packagist.org) and hit the "Submit"
button. This will prompt you to sign up if you haven't already, and then
allows you to submit the URL to your VCS repository, at which point Packagist
will start crawling it. Once it is done, your package will be available to
anyone!

← [Basic usage](01-basic-usage.md) |  [Command-line interface](03-cli.md) →