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

.. seealso::

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

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

################################################################################################################################################
permutations
################################################################################################################################################
************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
@permutations( |input|_, |filter|_, |tuple_size|_, |output|_, [|extras|_,...] )
************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************
    **Purpose:**

        Generates the **permutations**, between all the elements of a set of |input|_ (e.g. **A B C D**),

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

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

            >>> from itertools import permutations
            >>> # permutations('ABCD', 2) --> AB AC AD BA BC BD CA CB CD DA DB DC
            >>> [ "".join(a) for a in permutations("ABCD", 2)]
            ['AB', 'AC', 'AD', 'BA', 'BC', 'BD', 'CA', 'CB', 'CD', 'DA', 'DB', 'DC']

        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 **@permutations** 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

                @permutations(input_files, formatter(), 2, ...)

        Will be

            .. <<python

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

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

            ..
                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]

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

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

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

                'A'                         # {basename[0][1]}
                'B'                         # {basename[1][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

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

                              # tuple of 2 at a time
                              2,

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

                              # 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
                               ])
                def permutations_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
                A - C
                A - D
                B - A
                B - C
                B - D
                C - A
                C - B
                C - D
                D - A
                D - B
                D - C


    **Parameters:**


.. _decorators.permutations.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.permutations.filter:

.. _decorators.permutations.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.permutations.tuple_size:

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

.. _decorators.permutations.output:

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


.. _decorators.permutations.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.

