.. include:: ../global.inc
.. _decorators.combinations:
.. index::
    pair: @combinations; Syntax

.. seealso::

    * :ref:`@combinations <new_manual.combinations>` in the **Ruffus** Manual
    * :ref:`Decorators <decorators>` for more decorators


.. |input| replace:: `input`
.. _input: `decorators.combinations.input`_
.. |tuple_size| replace:: `tuple_size`
.. _tuple_size: `decorators.combinations.tuple_size`_
.. |filter| replace:: `filter`
.. _filter: `decorators.combinations.filter`_
.. |extras| replace:: `extras`
.. _extras: `decorators.combinations.extras`_
.. |output| replace:: `output`
.. _output: `decorators.combinations.output`_
.. |matching_formatter| replace:: `matching_formatter`
.. _matching_formatter: `decorators.combinations.matching_formatter`_


################################################################################################################################################
combinations
################################################################################################################################################

************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
@combinations( |input|_, |filter|_, |tuple_size|_, |output|_, [|extras|_,...] )
************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************

    **Purpose:**

        Generates the **combinations**, between all the elements of a set of |input|_ (e.g. **A B C D**),
        i.e. r-length tuples of |input|_ elements with no repeated elements (*not* **A A**)
        and where order of the tuples is irrelevant (either **A B** or  **B A**, not both).

        The effect is analogous to the python `itertools  <http://docs.python.org/2/library/itertools.html#itertools.combinations>`__
        function of the same name:

            .. code-block:: pycon
                :emphasize-lines: 2

                >>> from itertools import combinations
                >>> # combinations('ABCD', 3) --> ABC ABD ACD BCD
                >>> [ "".join(a) for a in combinations("ABCD", 3)]
                ['ABC', 'ABD', 'ACD', 'BCD']

        Only out of date tasks (comparing |input|_ and |output|_ files) will be run

        |output|_ file names and strings in the extra parameters
        are generated by string replacement via the :ref:`formatter()<decorators.formatter>` filter
        from the |input|_. This can be, for example, a list of file names or the
        |output|_ of up stream tasks.
        .
        The replacement strings require an extra level of nesting to refer to
        parsed components.

            #. The first level refers to which *set* in each tuple of |input|_.
            #. The second level refers to which |input|_ file in any particular *set* of |input|_.

        This will be clear in the following example:

    **Example**:

        Calculate the **@combinations** of **A,B,C,D** files

        If |input|_ is four pairs of file names

            .. code-block:: python
                :emphasize-lines: 1-6

                 input_files = [ [ 'A.1_start', 'A.2_start'],   # 0
                                 [ 'B.1_start', 'B.2_start'],   # 1
                                 [ 'C.1_start', 'C.2_start'],   # 2
                                 [ 'D.1_start', 'D.2_start'] ]  # 3

        The first job of:

            .. code-block:: python

                @combinations(input_files, formatter(), 3, ...)

        Will be

            .. <<python

            .. code-block:: python
                :emphasize-lines: 1-6

                # Three file pairs at a time
                ['A.1_start', 'A.2_start'],      # 0
                # versus
                ['B.1_start', 'B.2_start'],      # 1
                # versus
                ['C.1_start', 'c.2_start'],      # 2

            ..
                python

        First level of nesting:
            .. code-block:: python
                :emphasize-lines: 1-6

                ['A.1_start', 'A.2_start']  # [0]
                ['B.1_start', 'B.2_start']  # [1]
                ['C.1_start', 'C.2_start']  # [2]

        Second level of nesting:
            .. code-block:: python
                :emphasize-lines: 1-6

                'A.2_start'                 # [0][1]
                'B.2_start'                 # [1][1]
                'C.2_start'                 # [2][1]

        Parse filename without suffix
            .. code-block:: python
                :emphasize-lines: 1-6

                'A'                         # {basename[0][1]}
                'B'                         # {basename[1][1]}
                'C'                         # {basename[2][1]}

        Python code:

            .. code-block:: python
                :emphasize-lines: 13,17,20,25,28-30

                from ruffus import *
                from ruffus.combinatorics import *

                #   initial file pairs
                @originate([ ['A.1_start', 'A.2_start'],
                             ['B.1_start', 'B.2_start'],
                             ['C.1_start', 'C.2_start'],
                             ['D.1_start', 'D.2_start']])
                def create_initial_files_ABCD(output_files):
                    for output_file in output_files:
                        with open(output_file, "w") as oo: pass

                #   @combinations
                @combinations(create_initial_files_ABCD,      # Input
                              formatter(),                    # match input files

                              # tuple of 3 at a time
                              3,

                              # Output Replacement string
                              "{path[0][0]}/"
                              "{basename[0][1]}_vs_"
                              "{basename[1][1]}_vs_"
                              "{basename[2][1]}.combinations",

                              # Extra parameter: path for 1st set of files, 1st file name
                              "{path[0][0]}",

                              # Extra parameter, basename for:
                              ["{basename[0][0]}",  # 1st set of files, 1st file name
                               "{basename[1][0]}",  # 2nd
                               "{basename[2][0]}",  # 3rd
                               ])
                def combinations_task(input_file, output_parameter, shared_path, basenames):
                    print " - ".join(basenames)


                #
                #       Run
                #
                pipeline_run(verbose=0)


        This results in:

            .. code-block:: pycon

                >>> pipeline_run(verbose=0)
                A - B - C
                A - B - D
                A - C - D
                B - C - D


    **Parameters:**


.. _decorators.combinations.input:

    * **input** = *tasks_or_file_names*
       can be a:

       #.  Task / list of tasks.
            File names are taken from the |output|_ of the specified task(s)
       #.  (Nested) list of file name strings.
            File names containing ``*[]?`` will be expanded as a |glob|_.
             E.g.:``"a.*" => "a.1", "a.2"``

.. _decorators.combinations.filter:

.. _decorators.combinations.matching_formatter:

    * **filter** = *formater(...)*
       a :ref:`formatter<decorators.formatter>` indicator object containing optionally
       a  python `regular expression (re) <http://docs.python.org/library/re.html>`_.

.. _decorators.combinations.tuple_size:

    * **tuple_size** = *N*
        Select N elements at a time.

.. _decorators.combinations.output:

    * **output** = *output*
        Specifies the resulting output file name(s) after string substitution


.. _decorators.combinations.extras:

    * **extras** = *extras*
       Any extra parameters are passed verbatim to the task function

       If you are using named parameters, these can be passed as a list, i.e. ``extras= [...]``

       Any extra parameters are consumed by the task function and not forwarded further down the pipeline.

