<reponame>gilbertohasnofb/auxjad
from math import ceil
from typing import Optional, Union

import abjad

from .. import mutate
from ._LooperParent import _LooperParent


class WindowLooper(_LooperParent):
    r"""Outputs slices of an |abjad.Container| (or child class) using the
    metaphor of a looping window of a constant size (given by an
    |abjad.Duration|).

    Basic usage:
        Calling the object will return an |abjad.Selection| generated by the
        looping process. Each call of the object will move the window forwards
        and output the sliced window. It requires an :attr:`window_size` and a
        :attr:`step_size`.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> abjad.show(container)

        ..  docs::

            {
                c'4
                d'2
                e'4
                f'2
                ~
                f'8
                g'4.
            }

        ..  figure:: ../_images/WindowLooper-trzwtEJ4uG.png

        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'2
                e'4
            }

        ..  figure:: ../_images/WindowLooper-a9k9q8xy1j.png

        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'8.
                d'16
                ~
                d'4
                ~
                d'8.
                e'16
                ~
                e'8.
                f'16
            }

        ..  figure:: ../_images/WindowLooper-oori8gjer9s.png

        The property :attr:`current_window` can be used to access the current
        window without moving the head forwards.

        >>> notes = looper.current_window()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'8.
                d'16
                ~
                d'4
                ~
                d'8.
                e'16
                ~
                e'8.
                f'16
            }

        ..  figure:: ../_images/WindowLooper-7zc9e7o3dlr.png

    :attr:`process_on_first_call`:
        The very first call will output the input container without processing
        it. To disable this behaviour and have the looping window move on the
        very first call, initialise the class with the keyword argument
        :attr:`process_on_first_call` set to ``True``.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              process_on_first_call=True,
        ...                              )
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'8.
                d'16
                ~
                d'4
                ~
                d'8.
                e'16
                ~
                e'8.
                f'16
            }

        ..  figure:: ../_images/WindowLooper-dual73tnheq.png

    :attr:`window_size` and :attr:`step_size`:
        The arguments :attr:`window_size` and :attr:`step_size` are used to set
        the sizes of the window and the process step. :attr:`window_size` can
        take a :obj:`tuple` or an |abjad.Meter| as input, while
        :attr:`step_size` takes a :obj:`tuple` or an |abjad.Duration|.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 8),
        ...                              )
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'2
            }

        ..  figure:: ../_images/WindowLooper-0wh7ajyal0qj.png

        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'8
                d'8
                ~
                d'4.
                e'8
            }

        ..  figure:: ../_images/WindowLooper-tt90u6gg2tp.png

    Using as iterator:
        The instances of this class can also be used as an iterator, which can
        then be used in a for loop to exhaust all windows. Notice how it
        appends rests at the end of the container, until it is totally
        exhausted. Note that unlike the methods :meth:`output_n` and
        :meth:`output_all`, time signatures are added to each window returned
        by the shuffler. Use the function
        |auxjad.mutate.remove_repeated_time_signatures()| to clean the output
        when using this class in this way.

        >>> container = abjad.Container(r"c'4 d'2 e'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 8),
        ...                              )
        >>> staff = abjad.Staff()
        >>> for window in looper:
        ...     staff.append(window)
        >>> auxjad.mutate.remove_repeated_time_signatures(staff[:])
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'2
                c'8
                d'8
                ~
                d'4.
                e'8
                d'2
                e'4
                d'4.
                e'4
                r8
                d'4
                e'4
                r4
                d'8
                e'4
                r4.
                e'4
                r2
                e'8
                r8
                r2
            }

        ..  figure:: ../_images/WindowLooper-9sldax4dumb.png

    :attr:`fill_with_rests`:
        In order to stop the process when the end of the looping window matches
        the end of the :attr:`contents` (and thus appending rests to the
        output), set the optional keyword argument :attr:`fill_with_rests` to
        ``True``. Compare the two approaches below.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              )
        >>> staff = abjad.Staff()
        >>> for window in looper:
        ...     staff.append(window)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'4
                e'4
                d'4
                e'4
                f'4
                e'4
                f'4
                r4
                f'4
                r2
            }

        ..  figure:: ../_images/WindowLooper-4l025g7ycxr.png

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              fill_with_rests=False,
        ...                              )
        >>> staff = abjad.Staff()
        >>> for window in looper:
        ...     staff.append(window)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'4
                e'4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/WindowLooper-lq4cwlzlpp.png

    Arguments and properties:
        This class can take many optional keyword arguments during its
        creation. :attr:`max_steps` sets the maximum number of steps that the
        window can advance when the object is called, ranging between ``1`` and
        the input value (default is also ``1``). :attr:`repetition_chance` sets
        the chance of a window result repeating itself (that is, the window not
        moving forwards when called). It should range from ``0.0`` to ``1.0``
        (default ``0.0``, i.e. no repetition). :attr:`forward_bias` sets the
        chance of the window moving forward instead of backwards. It should
        range from ``0.0`` to ``1.0`` (default ``1.0``, which means the window
        can only move forwards. A value of ``0.5`` gives 50% chance of moving
        forwards while a value of ``0.0`` will move the window only backwards).
        :attr:`head_position` can be used to offset the starting position of
        the looping window. It must be a :obj:`tuple` or an |abjad.Duration|,
        and its default value is ``0``. The properties :attr:`boundary_depth`,
        :attr:`maximum_dot_count`, and :attr:`rewrite_tuplets` are passed as
        arguments to |abjad.Meter.rewrite_meter()|, see its documentation
        for more information. By default, calling the object will first return
        the original container and subsequent calls will process it; set
        :attr:`process_on_first_call` to ``True`` and the looping process will
        be applied on the very first call. The attribute :attr:`after_rest`
        sets the length of rests that separate consecutive iterations (default
        is ``abjad.Duration(0)``, i.e. no rest). When using after rests,
        :attr:`after_rest_in_new_measure` dictates whether or not rests are
        appended to the same output measure or to a new one. When in their own
        measure, :attr:`use_multimeasure_rests` sets if multi-measure rests
        should be used or not.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(5, 8),
        ...                              max_steps=2,
        ...                              repetition_chance=0.25,
        ...                              forward_bias=0.2,
        ...                              head_position=(2, 8),
        ...                              omit_time_signatures=False,
        ...                              fill_with_rests=False,
        ...                              boundary_depth=0,
        ...                              maximum_dot_count=1,
        ...                              rewrite_tuplets=False,
        ...                              process_on_first_call=True,
        ...                              after_rest=(1, 8),
        ...                              after_rest_in_new_measure=True,
        ...                              use_multimeasure_rests=False,
        ...                              )
        >>> looper.window_size
        3/4
        >>> looper.step_size
        5/8
        >>> looper.repetition_chance
        0.25
        >>> looper.forward_bias
        0.2
        >>> looper.max_steps
        2
        >>> looper.head_position
        1/4
        >>> looper.omit_time_signatures
        False
        >>> looper.fill_with_rests
        False
        >>> looper.boundary_depth
        0
        >>> looper.maximum_dot_count
        1
        >>> looper.rewrite_tuplets
        False
        >>> looper.boundary_depth
        0
        >>> looper.maximum_dot_count
        1
        >>> looper.rewrite_tuplets
        False
        >>> looper.process_on_first_call
        True
        >>> looper.after_rest
        abjad.Duration((1, 8))
        >>> looper.after_rest_in_new_measure
        True
        >>> looper.use_multimeasure_rests
        False

        Use the properties below to change these values after initialisation.

        >>> looper.window_size = (5, 4)
        >>> looper.step_size = (1, 4)
        >>> looper.max_steps = 3
        >>> looper.repetition_chance = 0.1
        >>> looper.forward_bias = 0.8
        >>> looper.head_position = 0
        >>> looper.omit_time_signatures = True
        >>> looper.boundary_depth = 1
        >>> looper.maximum_dot_count = 2
        >>> looper.rewrite_tuplets = True
        >>> looper.process_on_first_call = False
        >>> looper.after_rest = 0
        >>> looper.after_rest_in_new_measure = False
        >>> looper.use_multimeasure_rests = True
        >>> looper.window_size
        5/4
        >>> looper.step_size
        1/4
        >>> looper.max_steps
        3
        >>> looper.repetition_chance
        0.1
        >>> looper.forward_bias
        0.8
        >>> looper.head_position
        0
        >>> looper.omit_time_signatures
        True
        >>> looper.boundary_depth
        1
        >>> looper.maximum_dot_count
        2
        >>> looper.rewrite_tuplets
        True
        >>> looper.process_on_first_call
        False
        >>> looper.after_rest
        abjad.Duration(0)
        >>> looper.after_rest_in_new_measure
        False
        >>> looper.use_multimeasure_rests
        True

    Setting :attr:`forward_bias` to ``0.0``:
        Set :attr:`forward_bias` to ``0.0`` to move backwards instead of
        forwards (default is ``1.0``). The initial :attr:`head_position` must
        be greater than ``0`` otherwise the contents will already be exhausted
        in the very first call (since it will not be able to move backwards
        from that position).

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4 g'4 a'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              head_position=(3, 4),
        ...                              forward_bias=0.0,
        ...                              )
        >>> notes = looper.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                f'4
                g'4
                a'4
                e'4
                f'4
                g'4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/WindowLooper-ty5vqw6qk9d.png

    :attr:`forward_bias` between ``0.0`` and ``1.0``:
        Setingt :attr:`forward_bias` to a value in between ``0.0`` and ``1.0``
        will result in random steps being taken forward or backward, according
        to the bias. The initial value of :attr:`head_position` will once gain
        play an important role here, as the contents might be exhausted if the
        looper attempts to move backwards after reaching the head position
        ``0``.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4 g'4 a'4 b'4 c''4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              head_position=(3, 4),
        ...                              forward_bias=0.5,
        ...                              )
        >>> notes = looper.output_n(5)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                f'4
                g'4
                a'4
                e'4
                f'4
                g'4
                d'4
                e'4
                f'4
                e'4
                f'4
                g'4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/WindowLooper-p91xgsnmp3o.png

    :attr:`max_steps`:
        Setting the keyword argument :attr:`max_steps` to a value larger than
        ``1`` will result in a random number of steps (between ``1`` and
        :attr:`max_steps`) being applied at each call.

        >>> container = abjad.Container(
        ...     r"c'4 d'4 e'4 f'4 g'4 a'4 b'4 c''4 d''4 e''4 f''4"
        ... )
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              max_steps=4,
        ...                              )
        >>> notes = looper.output_n(4)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'4
                e'4
                e'4
                f'4
                g'4
                f'4
                g'4
                a'4
                c''4
                d''4
                e''4
            }

        ..  figure:: ../_images/WindowLooper-81mbjuesmbr.png

    :func:`len()`:
        The function :func:`len()` can be used to get the total number of steps
        in the contents (always rounded up).

        >>> container = abjad.Container(r"c'1")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> len(looper)
        16
        >>> container = abjad.Container(r"c'1")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 4),
        ...                              )
        >>> len(looper)
        4
        >>> container = abjad.Container(r"c'2..")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(2, 4),
        ...                              step_size=(1, 4),
        ...                              )
        >>> len(looper)
        4

    :meth:`output_all`:
        To run through the whole process and output it as a single container,
        from the initial head position until the process outputs the single
        last element, use the method :meth:`output_all`. As shown above, set
        the optional keyword argument :attr:`fill_with_rests` to ``False`` if
        the process is to be stopped when the end of the looping window reaches
        the end of the contents (thus not appending rests).

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              )
        >>> notes = looper.output_all()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'4
                e'4
                d'4
                e'4
                f'4
                e'4
                f'4
                r4
                f'4
                r2
            }

        ..  figure:: ../_images/WindowLooper-y734t07uio.png

    ``tie_identical_pitches``:
        When using :meth:`output_all`, set the keyword argument
        ``tie_identical_pitches`` to ``True`` in order to tie identical notes
        or chords at the end and beginning of consecutive windows.

        >>> container = abjad.Container(r"c'4 <e' f' g'>2 r4 f'2.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              )
        >>> notes = looper.output_all(tie_identical_pitches=True)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                <e' f' g'>2
                ~
                <e' f' g'>2
                r4
                <e' f' g'>4
                r4
                f'4
                r4
                f'2
                ~
                f'2.
                ~
                f'2
                r4
                f'4
                r2
            }

        ..  figure:: ../_images/WindowLooper-1giyp118geth.png

    :meth:`output_n`:
        To run through just part of the process and output it as a single
        container, starting from the initial head position, use the method
        :meth:`output_n` and pass the number of iterations as argument.
        Similarly to :meth:`output_all`, the optional keyword arguments
        ``tie_identical_pitches`` and :attr:`fill_with_rests` are available.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 4),
        ...                              )
        >>> notes = looper.output_n(2)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'4
                e'4
                d'4
                e'4
                f'4
            }

        ..  figure:: ../_images/WindowLooper-uikg5s4t26.png

    ..  tip::

        All methods that return an |abjad.Selection| will add an initial time
        signature to it. The :meth:`output_n` and :meth:`output_all` methods
        automatically remove repeated time signatures. When joining selections
        output by multiple method calls, use
        |auxjad.mutate.remove_repeated_time_signatures()| on the whole
        container after fusing the selections to remove any unecessary time
        signature changes.

    :attr:`omit_time_signatures`:
        To disable time signatures altogether, initialise this class with the
        keyword argument :attr:`omit_time_signatures` set to ``True`` (default
        is ``False``), or use the :attr:`omit_time_signatures` property after
        initialisation.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              omit_time_signatures=True,
        ...                              )
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                c'4
                d'2
                e'4
            }

        ..  figure:: ../_images/WindowLooper-24ipt4uf4x8.png

    :attr:`after_rest`:
        To append rests after the output (useful to create separations between
        consecutive windows), set the keyword argument :attr:`after_rest` to
        an :obj:`int`, :obj:`float`, :obj:`tuple`, or |abjad.Duration| with the
        total length of the rests. Setting it to ``abjad.Duration(0)`` will
        disable rests (which is the default value).

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 16),
        ...                              after_rest=(1, 4),
        ...                              )
        >>> notes = looper.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'2
                r4
                c'8.
                d'16
                ~
                d'4
                ~
                d'8.
                e'16
                r4
                c'8
                d'8
                ~
                d'4
                ~
                d'8
                e'8
                r4
            }

        ..  figure:: ../_images/WindowLooper-Oh9HqIcxyr.png

    :attr:`after_rest_in_new_measure`:
        When using after rests, the keyword argument
        :attr:`after_rest_in_new_measure` controls whether or not the rests are
        appended to the same output measure or to a new one.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 16),
        ...                              after_rest=(1, 4),
        ...                              after_rest_in_new_measure=True,
        ...                              )
        >>> notes = looper.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'2
                \time 1/4
                R1 * 1/4
                \time 3/4
                c'8.
                d'16
                ~
                d'4..
                e'16
                \time 1/4
                R1 * 1/4
                \time 3/4
                c'8
                d'8
                ~
                d'4.
                e'8
                \time 1/4
                R1 * 1/4
            }

        ..  figure:: ../_images/WindowLooper-PsrOr76UJu.png

    :attr:`use_multimeasure_rests`:
        When after rests are used and :attr:`after_rest_in_new_measure` is set
        to ``True``, multi-measure rests are automatically used. To use regular
        rests, set :attr:`use_multimeasure_rests` to ``False``.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 16),
        ...                              after_rest=(1, 4),
        ...                              after_rest_in_new_measure=True,
        ...                              use_multimeasure_rests=False,
        ...                              )
        >>> notes = looper.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'2
                \time 1/4
                r4
                \time 3/4
                c'8.
                d'16
                ~
                d'4..
                e'16
                \time 1/4
                r4
                \time 3/4
                c'8
                d'8
                ~
                d'4.
                e'8
                \time 1/4
                r4
            }

        ..  figure:: ../_images/WindowLooper-7Kg5fllsiV.png

    ..  note::

        Multi-measure rests will not be used if :attr:`omit_time_signatures` is
        ``True``, regardless of the value of :attr:`use_multimeasure_rests`.

    :attr:`window_size`:
        To change the size of the looping window after instantiation, use the
        property :attr:`window_size`. In the example below, the initial window
        is of size ``(4, 4)``, but changes to ``(3, 8)`` after three calls.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> notes = looper.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'2
                e'4
                c'8.
                d'16
                ~
                d'4
                ~
                d'8.
                e'16
                ~
                e'8.
                f'16
                c'8
                d'8
                ~
                d'4
                ~
                d'8
                e'4
                f'8
            }

        ..  figure:: ../_images/WindowLooper-52p5g0hqnep.png

        >>> looper.window_size = (3, 8)
        >>> notes = looper.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/8
                c'16
                d'16
                ~
                d'4
                d'4.
                d'4.
            }

        ..  figure:: ../_images/WindowLooper-kzrb0fwup2.png

    :attr:`contents`:
        Use the :attr:`contents` property to read as well as overwrite the
        contents of the looper. Notice that the :attr:`head_position` will
        remain on its previous value and must be reset to ``0`` if that's
        required.

        >>> container = abjad.Container(r"c'4 d'2 e'4 f'2 ~ f'8 g'4.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                d'2
                e'4
            }

        ..  figure:: ../_images/WindowLooper-hx2wjkgko3j.png

        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'8.
                d'16
                ~
                d'4
                ~
                d'8.
                e'16
                ~
                e'8.
                f'16
            }

        ..  figure:: ../_images/WindowLooper-bjabk27oapb.png

        >>> looper.contents = abjad.Container(r"c'16 d'16 e'16 f'16 g'2. a'1")
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                d'16
                e'16
                f'16
                g'16
                ~
                g'2
                ~
                g'8.
                a'16
            }

        ..  figure:: ../_images/WindowLooper-fdioda2e2ed.png

        >>> looper.head_position = 0
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'16
                d'16
                e'16
                f'16
                g'2.
            }

        ..  figure:: ../_images/WindowLooper-vh12q82nw0e.png

    Indicators:
        This class can handle dynamics, articulations and slurs. When a leaf is
        shortened by the looping window's movement, the dynamics and
        articulations are still applied to it.

        >>> container = abjad.Container(
        ...     r"c'4-.\p\< d'2--\f e'4->\ppp f'2 ~ f'8"
        ... )
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> notes = looper.output_n(2)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                \p
                - \staccato
                \<
                d'2
                \f
                - \tenuto
                e'4
                \ppp
                - \accent
                c'8.
                \p
                - \staccato
                \<
                d'16
                \f
                - \tenuto
                ~
                d'4
                ~
                d'8.
                e'16
                \ppp
                - \accent
                ~
                e'8.
                f'16
            }

        ..  figure:: ../_images/WindowLooper-23jcqhmpqfm.png

    ..  tip::

        The functions |auxjad.mutate.remove_repeated_dynamics()| and
        |auxjad.mutate.reposition_clefs()| can be used to clean the output
        and remove repeated dynamics and unnecessary clef changes.

    ..  warning::

        Do note that some elements that span multiple notes (such as ottava
        indicators, manual beams, etc.) can become problematic when notes
        containing them are split into two. As a rule of thumb, it is always
        better to attach those to the music after the fading process has ended.

    :attr:`disable_rewrite_meter`:
        By default, this class uses the |abjad.Meter.rewrite_meter()|
        mutation.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4 g'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> notes = looper.output_n(2)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'4
                e'4
                c'8.
                d'16
                ~
                d'8.
                e'16
                ~
                e'8.
                f'16
            }

        ..  figure:: ../_images/LeafLooper-toNYz7MCq3.png

        Set :attr:`disable_rewrite_meter` to ``True`` in order to disable this
        behaviour.

        >>> container = abjad.Container(r"c'4 d'4 e'4 f'4 g'4")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 16),
        ...                              disable_rewrite_meter=True,
        ...                              )
        >>> notes = looper.output_n(2)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 3/4
                c'4
                d'4
                e'4
                c'8.
                d'4
                e'4
                f'16
            }

        ..  figure:: ../_images/LeafLooper-osLcSeQ6gP.png

    Tweaking |abjad.Meter.rewrite_meter()|:
        This class uses the default logical tie splitting algorithm from
        |abjad.Meter.rewrite_meter()|.

        >>> container = abjad.Container(r"c'4. d'8 e'2")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4.
                d'8
                e'2
            }

        ..  figure:: ../_images/WindowLooper-5iew8d28rqj.png

        Set :attr:`boundary_depth` to a different number to change its
        behaviour.

        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(4, 4),
        ...                              step_size=(1, 16),
        ...                              boundary_depth=1,
        ...                              )
        >>> notes = looper()
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \time 4/4
                c'4
                ~
                c'8
                d'8
                e'2
            }

        ..  figure:: ../_images/WindowLooper-hzetruc3kz4.png

        Other arguments available for tweaking the output of
        |abjad.Meter.rewrite_meter()| are :attr:`maximum_dot_count` and
        :attr:`rewrite_tuplets`, which work exactly as the identically named
        arguments of |abjad.Meter.rewrite_meter()|.

        This class also accepts the arguments ``fuse_across_groups_of_beats``,
        ``fuse_quadruple_meter``, ``fuse_triple_meter``, and
        ``extract_trivial_tuplets``, which are passed on to
        |auxjad.mutate.prettify_rewrite_meter()| (the latter can be disabled
        by setting ``prettify_rewrite_meter`` to ``False``). See the
        documentation of this function for more details on these arguments.

    ..  warning::

        This class can handle tuplets, but the output is often quite complex.
        Although the result will be rhythmically correct, consecutive tuplets
        are not fused together, and tuplets may be output off-beat. This
        functionality should be considered experimental.

        >>> container = abjad.Container(r"\times 2/3 {c'8 d'8 e'8} d'2.")
        >>> looper = auxjad.WindowLooper(container,
        ...                              window_size=(3, 4),
        ...                              step_size=(1, 16),
        ...                              )
        >>> notes = looper.output_n(3)
        >>> staff = abjad.Staff(notes)
        >>> abjad.show(staff)

        ..  docs::

            \new Staff
            {
                \times 2/3
                {
                    \time 3/4
                    c'8
                    d'8
                    e'8
                }
                d'2
                \times 2/3
                {
                    c'32
                    d'16
                    ~
                    d'16
                    e'8
                }
                d'16
                ~
                d'2
                \times 2/3
                {
                    d'16
                    e'8
                }
                d'8
                ~
                d'2
            }

        ..  figure:: ../_images/WindowLooper-cevvf9c9a9.png
    """

    ### CLASS VARIABLES ###

    __slots__ = ('_omit_time_signatures',
                 '_fill_with_rests',
                 '_contents_length',
                 '_contents_no_time_signature',
                 '_disable_rewrite_meter',
                 '_boundary_depth',
                 '_maximum_dot_count',
                 '_rewrite_tuplets',
                 '_prettify_rewrite_meter',
                 '_extract_trivial_tuplets',
                 '_fuse_across_groups_of_beats',
                 '_fuse_quadruple_meter',
                 '_fuse_triple_meter',
                 '_after_rest',
                 '_after_rest_in_new_measure',
                 '_use_multimeasure_rests',
                 )

    ### INITIALISER ###

    def __init__(self,
                 contents: abjad.Container,
                 *,
                 window_size: Union[int,
                                    float,
                                    str,
                                    tuple[int],
                                    abjad.Duration,
                                    abjad.Meter,
                                    ],
                 step_size: Union[int,
                                  float,
                                  str,
                                  tuple[int],
                                  abjad.Duration,
                                  ],
                 max_steps: int = 1,
                 repetition_chance: float = 0.0,
                 forward_bias: float = 1.0,
                 head_position: Union[int,
                                      float,
                                      tuple[int],
                                      abjad.Duration,
                                      ] = 0,
                 omit_time_signatures: bool = False,
                 process_on_first_call: bool = False,
                 fill_with_rests: bool = True,
                 disable_rewrite_meter: bool = False,
                 boundary_depth: Optional[int] = None,
                 maximum_dot_count: Optional[int] = None,
                 rewrite_tuplets: bool = True,
                 prettify_rewrite_meter: bool = True,
                 extract_trivial_tuplets: bool = True,
                 fuse_across_groups_of_beats: bool = True,
                 fuse_quadruple_meter: bool = True,
                 fuse_triple_meter: bool = True,
                 after_rest: Union[int,
                                   float,
                                   str,
                                   tuple[int],
                                   abjad.Duration,
                                   abjad.Rest,
                                   ] = 0,
                 after_rest_in_new_measure: bool = False,
                 use_multimeasure_rests: bool = True,
                 ) -> None:
        r'Initialises self.'
        self.contents = contents
        self.omit_time_signatures = omit_time_signatures
        self.fill_with_rests = fill_with_rests
        self.disable_rewrite_meter = disable_rewrite_meter
        self.boundary_depth = boundary_depth
        self.maximum_dot_count = maximum_dot_count
        self.rewrite_tuplets = rewrite_tuplets
        self.prettify_rewrite_meter = prettify_rewrite_meter
        self.extract_trivial_tuplets = extract_trivial_tuplets
        self.fuse_across_groups_of_beats = fuse_across_groups_of_beats
        self.fuse_quadruple_meter = fuse_quadruple_meter
        self.fuse_triple_meter = fuse_triple_meter
        self.after_rest = after_rest
        self.after_rest_in_new_measure = after_rest_in_new_measure
        self.use_multimeasure_rests = use_multimeasure_rests
        super().__init__(head_position=head_position,
                         window_size=window_size,
                         step_size=step_size,
                         max_steps=max_steps,
                         repetition_chance=repetition_chance,
                         forward_bias=forward_bias,
                         process_on_first_call=process_on_first_call,
                         )

    ### SPECIAL METHODS ###

    def __repr__(self) -> str:
        r'Returns interpreter representation of  :attr:`contents`.'
        return abjad.lilypond(self._contents)

    def __len__(self) -> int:
        r"""Returns the length of :attr:`contents` in terms of \
        :attr:`step_size`.
        """
        return ceil(self._contents_length / self._step_size)

    def __call__(self) -> abjad.Selection:
        r"""Calls the looping process for one iteration, returning an
        |abjad.Selection|.
        """
        return super().__call__()

    def __next__(self) -> abjad.Selection:
        r"""Calls the looping process for one iteration, returning an
        |abjad.Selection|.
        """
        return super().__next__()

    ### PRIVATE METHODS ###

    def _slice_contents(self) -> None:
        r"""This method takes a slice of size :attr:`window_size` out of
        :attr:`contents` starting at the current :attr:`head_position`.
        """
        head = self._head_position
        window_size = self._window_size
        dummy_container = abjad.mutate.copy(self._contents_no_time_signature)
        # splitting leaves at both slicing points
        if head > abjad.Duration(0):
            abjad.mutate.split(dummy_container[:],
                               [head, window_size.duration],
                               )
        else:
            abjad.mutate.split(dummy_container[:], [window_size.duration])
        # finding start and end indeces for the window
        for start in range(len(dummy_container)):
            if abjad.get.duration(dummy_container[:start + 1]) > head:
                break
        for end in range(start + 1, len(dummy_container)):
            if (abjad.get.duration(dummy_container[start : end])
                    == window_size.duration):
                break
        else:
            end = len(dummy_container)
        self._notate_music(dummy_container, start, end)

    def _notate_music(self,
                      dummy_container: abjad.Container,
                      start: int,
                      end: int,
                      ) -> None:
        r'Handles the notation aspects of the looping window.'
        window_size = self._window_size
        # passing on indicators from the head of an initial splitted leaf
        for index in range(start - 1, -1, -1):
            if abjad.get.indicator(dummy_container[index], abjad.Tie):
                if index == 0 or not abjad.get.indicator(
                    dummy_container[index - 1],
                    abjad.Tie,
                ):
                    for indicator in abjad.get.indicators(
                        dummy_container[index],
                    ):
                        if (not isinstance(indicator, (abjad.TimeSignature,
                                                       abjad.Tie))
                                and abjad.get.indicator(dummy_container[start],
                                                        type(indicator),
                                                        )
                                is None):
                            abjad.attach(indicator, dummy_container[start])
        # removing ties generated by the split mutation
        abjad.detach(abjad.Tie, dummy_container[start - 1])
        abjad.detach(abjad.Tie, dummy_container[end - 1])
        # handling initial dynamics and slurs
        start_head = abjad.select(dummy_container[start:]).logical_tie(0)[0]
        start_tail = abjad.select(dummy_container[start:]).logical_tie(0)[-1]
        if (abjad.get.indicator(start_head, abjad.StartSlur) is None
                and abjad.get.indicator(start_tail, abjad.StopSlur)
                is None):
            for leaf in dummy_container[start - 1::-1].leaves():
                if abjad.get.indicator(leaf, abjad.StartSlur) is not None:
                    abjad.attach(abjad.StartSlur(), start_head)
                    break
                elif abjad.get.indicator(leaf, abjad.StopSlur) is not None:
                    break
        if (abjad.get.indicator(start_head, abjad.Dynamic) is None
                and not isinstance(start_head, (abjad.Rest,
                                                abjad.MultimeasureRest,
                                                ))):
            for leaf in dummy_container[start - 1::-1].leaves():
                dynamic = abjad.get.indicator(leaf, abjad.Dynamic)
                if dynamic is not None:
                    abjad.attach(dynamic, start_head)
                    break
        # appending rests if necessary
        contents_dur = abjad.get.duration(dummy_container[start : end])
        if contents_dur < window_size.duration:
            missing_dur = window_size.duration - contents_dur
            rests = abjad.LeafMaker()(None, missing_dur)
            dummy_container.extend(rests)
            end += len(rests)
        # transforming abjad.Selection -> abjad.Container for rewrite_meter
        dummy_container = abjad.Container(
            abjad.mutate.copy(dummy_container[start : end])
        )
        # handling after rests and time signatures
        time_signature_duration = window_size.duration
        if self._after_rest > 0:
            if (self._after_rest_in_new_measure
                    and self._use_multimeasure_rests
                    and not self._omit_time_signatures):
                if self._after_rest == 1:
                    multiplier = None
                else:
                    multiplier = abjad.Multiplier(self._after_rest)
                rest = abjad.MultimeasureRest((4, 4),
                                              multiplier=multiplier,
                                              )
            else:
                rest = abjad.LeafMaker()([None], [self._after_rest])
            if not self._after_rest_in_new_measure:
                time_signature_duration += self._after_rest
            else:
                time_signature = abjad.TimeSignature(self._after_rest)
                time_signature.simplify_ratio()
                abjad.attach(time_signature, abjad.select(rest).leaf(0))
            dummy_container.append(rest)
        time_signature = abjad.TimeSignature(time_signature_duration)
        time_signature.simplify_ratio()
        abjad.attach(time_signature, abjad.select(dummy_container).leaf(0))
        # rewriting meter
        if not self._disable_rewrite_meter:
            mutate.auto_rewrite_meter(
                dummy_container,
                boundary_depth=self._boundary_depth,
                maximum_dot_count=self._maximum_dot_count,
                rewrite_tuplets=self._rewrite_tuplets,
                prettify_rewrite_meter=self._prettify_rewrite_meter,
                extract_trivial_tuplets=self._extract_trivial_tuplets,
                fuse_across_groups_of_beats=self._fuse_across_groups_of_beats,
                fuse_quadruple_meter=self._fuse_quadruple_meter,
                fuse_triple_meter=self._fuse_triple_meter,
            )
        mutate.reposition_dynamics(dummy_container[:])
        mutate.reposition_slurs(dummy_container[:])
        self._current_window = dummy_container[:]
        dummy_container[:] = []

    def _get_lilypond_format(self) -> str:
        r'Returns interpreter representation of  :attr:`contents`.'
        return self.__repr__()

    ### PUBLIC PROPERTIES ###

    @property
    def contents(self) -> abjad.Container:
        r'The |abjad.Container| to be sliced and looped.'
        return abjad.mutate.copy(self._contents)

    @contents.setter
    def contents(self,
                 contents: abjad.Container,
                 ) -> None:
        if not isinstance(contents, abjad.Container):
            raise TypeError("'contents' must be 'abjad.Container' or "
                            "child class")
        if not abjad.select(contents).leaves().are_contiguous_logical_voice():
            raise ValueError("'contents' must be contiguous logical voice")
        if isinstance(contents, abjad.Score):
            self._contents = abjad.mutate.copy(contents[0])
        elif isinstance(contents, abjad.Tuplet):
            self._contents = abjad.Container([abjad.mutate.copy(contents)])
        else:
            self._contents = abjad.mutate.copy(contents)
        self._contents_length = abjad.get.duration(self._contents[:])
        self._contents_no_time_signature = abjad.mutate.copy(self._contents)
        self._remove_all_time_signatures(self._contents_no_time_signature)
        self._is_first_window = True

    @property
    def head_position(self) -> abjad.Duration:
        r'The position of the head at the start of a looping window.'
        return self._head_position

    @head_position.setter
    def head_position(self,
                      head_position: Union[int,
                                           float,
                                           str,
                                           tuple[int],
                                           abjad.Duration,
                                           ],
                      ) -> None:
        r"""This setter method replaces the parent's one since the parent's
        method uses :obj:`int` as input intead of number, :obj:`tuple`, or
        |abjad.Duration|.
        """
        if not isinstance(head_position,
                          (abjad.Duration, str, tuple, int, float),
                          ):
            raise TypeError("'head_position' must be 'abjad.Duration', 'str', "
                            "'tuple', or a number")
        if abjad.Duration(head_position) >= self._contents_length:
            raise ValueError("'head_position' must be smaller than the "
                             "length of 'contents'")
        self._is_first_window = True
        self._head_position = abjad.Duration(head_position)

    @property
    def window_size(self) -> abjad.Meter:
        r'The length of the looping window.'
        return self._window_size

    @window_size.setter
    def window_size(self,
                    window_size: Union[int,
                                       float,
                                       str,
                                       tuple[int],
                                       abjad.Duration,
                                       abjad.Meter,
                                       ],
                    ) -> None:
        r"""This setter method replaces the parent's one since the parent's
        method uses :obj:`int` as input intead of number, :obj:`tuple`, or
        |abjad.Meter|.
        """
        if not isinstance(window_size, (int,
                                        float,
                                        tuple,
                                        abjad.Duration,
                                        abjad.Meter,
                                        )):
            raise TypeError("'window_size' must be a number, 'tuple', "
                            "'abjad.Duration', or 'abjad.Meter'")
        if (abjad.Meter(window_size).duration
                > self._contents_length - self._head_position):
            raise ValueError("'window_size' must be smaller than or equal "
                             "to the length of 'contents'")
        self._window_size = abjad.Meter(window_size)

    @property
    def step_size(self) -> abjad.Duration:
        r'The size of each step when moving the head.'
        return self._step_size

    @step_size.setter
    def step_size(self,
                  step_size: Union[int,
                                   float,
                                   tuple[int],
                                   abjad.Duration,
                                   ],
                  ) -> None:
        r"""This setter method replaces the parent's one since the parent's
        method uses :obj:`int` as input intead of number, :obj:`tuple`, or
        |abjad.Duration|.
        """
        if not isinstance(step_size, (abjad.Duration, str, tuple, int, float)):
            raise TypeError("'step_size' must be 'abjad.Duration', 'str', "
                            "'tuple', or a number")
        self._step_size = abjad.Duration(step_size)

    @property
    def omit_time_signatures(self) -> bool:
        r'When ``True``, the output will contain no time signatures.'
        return self._omit_time_signatures

    @omit_time_signatures.setter
    def omit_time_signatures(self,
                             omit_time_signatures: bool,
                             ) -> None:
        if not isinstance(omit_time_signatures, bool):
            raise TypeError("'omit_time_signatures' must be 'bool'")
        self._omit_time_signatures = omit_time_signatures

    @property
    def disable_rewrite_meter(self) -> bool:
        r"""When ``True``, the durations of the notes in the output will not be
        rewritten by the |abjad.Meter.rewrite_meter()| mutation. Rests will
        have the same duration as the logical ties they replaced.
        """
        return self._disable_rewrite_meter

    @disable_rewrite_meter.setter
    def disable_rewrite_meter(self,
                              disable_rewrite_meter: bool,
                              ) -> None:
        if not isinstance(disable_rewrite_meter, bool):
            raise TypeError("'disable_rewrite_meter' must be 'bool'")
        self._disable_rewrite_meter = disable_rewrite_meter

    @property
    def fill_with_rests(self) -> bool:
        r'When ``True``, the output will contain no time signatures.'
        return self._fill_with_rests

    @fill_with_rests.setter
    def fill_with_rests(self,
                        fill_with_rests: bool,
                        ) -> None:
        if not isinstance(fill_with_rests, bool):
            raise TypeError("'fill_with_rests' must be 'bool'")
        self._fill_with_rests = fill_with_rests

    @property
    def boundary_depth(self) -> Union[int, None]:
        r"""Sets the argument ``boundary_depth`` of
        |abjad.Meter.rewrite_meter()|.
        """
        return self._boundary_depth

    @boundary_depth.setter
    def boundary_depth(self,
                       boundary_depth: Optional[int],
                       ) -> None:
        if boundary_depth is not None:
            if not isinstance(boundary_depth, int):
                raise TypeError("'boundary_depth' must be 'int'")
        self._boundary_depth = boundary_depth

    @property
    def maximum_dot_count(self) -> Union[int, None]:
        r"""Sets the argument ``maximum_dot_count`` of
        |abjad.Meter.rewrite_meter()|.
        """
        return self._maximum_dot_count

    @maximum_dot_count.setter
    def maximum_dot_count(self,
                          maximum_dot_count: Optional[int],
                          ) -> None:
        if maximum_dot_count is not None:
            if not isinstance(maximum_dot_count, int):
                raise TypeError("'maximum_dot_count' must be 'int'")
        self._maximum_dot_count = maximum_dot_count

    @property
    def rewrite_tuplets(self) -> bool:
        r"""Sets the argument ``rewrite_tuplets`` of
        |abjad.Meter.rewrite_meter()|.
        """
        return self._rewrite_tuplets

    @rewrite_tuplets.setter
    def rewrite_tuplets(self,
                        rewrite_tuplets: bool,
                        ) -> None:
        if not isinstance(rewrite_tuplets, bool):
            raise TypeError("'rewrite_tuplets' must be 'bool'")
        self._rewrite_tuplets = rewrite_tuplets

    @property
    def prettify_rewrite_meter(self) -> bool:
        r"""Used to enable or disable the mutation
        |auxjad.mutate.prettify_rewrite_meter()| (default ``True``).
        """
        return self._prettify_rewrite_meter

    @prettify_rewrite_meter.setter
    def prettify_rewrite_meter(self,
                               prettify_rewrite_meter: bool,
                               ) -> None:
        if not isinstance(prettify_rewrite_meter, bool):
            raise TypeError("'prettify_rewrite_meter' must be 'bool'")
        self._prettify_rewrite_meter = prettify_rewrite_meter

    @property
    def extract_trivial_tuplets(self) -> bool:
        r"""Sets the argument ``extract_trivial_tuplets`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._extract_trivial_tuplets

    @extract_trivial_tuplets.setter
    def extract_trivial_tuplets(self,
                                extract_trivial_tuplets: bool,
                                ) -> None:
        if not isinstance(extract_trivial_tuplets, bool):
            raise TypeError("'extract_trivial_tuplets' must be 'bool'")
        self._extract_trivial_tuplets = extract_trivial_tuplets

    @property
    def fuse_across_groups_of_beats(self) -> bool:
        r"""Sets the argument ``fuse_across_groups_of_beats`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._fuse_across_groups_of_beats

    @fuse_across_groups_of_beats.setter
    def fuse_across_groups_of_beats(self,
                                    fuse_across_groups_of_beats: bool,
                                    ) -> None:
        if not isinstance(fuse_across_groups_of_beats, bool):
            raise TypeError("'fuse_across_groups_of_beats' must be 'bool'")
        self._fuse_across_groups_of_beats = fuse_across_groups_of_beats

    @property
    def fuse_quadruple_meter(self) -> bool:
        r"""Sets the argument ``fuse_quadruple_meter`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._fuse_quadruple_meter

    @fuse_quadruple_meter.setter
    def fuse_quadruple_meter(self,
                             fuse_quadruple_meter: bool,
                             ) -> None:
        if not isinstance(fuse_quadruple_meter, bool):
            raise TypeError("'fuse_quadruple_meter' must be 'bool'")
        self._fuse_quadruple_meter = fuse_quadruple_meter

    @property
    def fuse_triple_meter(self) -> bool:
        r"""Sets the argument ``fuse_triple_meter`` of
        |auxjad.mutate.prettify_rewrite_meter()|.
        """
        return self._fuse_triple_meter

    @fuse_triple_meter.setter
    def fuse_triple_meter(self,
                          fuse_triple_meter: bool,
                          ) -> None:
        if not isinstance(fuse_triple_meter, bool):
            raise TypeError("'fuse_triple_meter' must be 'bool'")
        self._fuse_triple_meter = fuse_triple_meter

    @property
    def after_rest(self) -> abjad.Duration:
        r"""Sets the length of the rest appended at the end of the window
        (default is ``0``).
        """
        return self._after_rest

    @after_rest.setter
    def after_rest(self,
                   after_rest: Union[int,
                                     float,
                                     str,
                                     tuple[int],
                                     abjad.Duration,
                                     abjad.Rest,
                                     ],
                   ) -> None:
        if not isinstance(after_rest,
                          (abjad.Duration, abjad.Rest, str, tuple, int, float),
                          ):
            raise TypeError("'after_rest' must be 'abjad.Duration', "
                            "'abjad.Rest', 'str', 'tuple', or a number")
        if isinstance(after_rest, abjad.Rest):
            after_rest = abjad.get.duration(after_rest)
        self._after_rest = abjad.Duration(after_rest)

    @property
    def after_rest_in_new_measure(self) -> bool:
        r"""If ``True``, then after rests will be added to their own measure
        (default is ``False``).
        """
        return self._after_rest_in_new_measure

    @after_rest_in_new_measure.setter
    def after_rest_in_new_measure(self,
                                  after_rest_in_new_measure: bool,
                                  ) -> None:
        if not isinstance(after_rest_in_new_measure, bool):
            raise TypeError("'after_rest_in_new_measure' must be 'bool'")
        self._after_rest_in_new_measure = after_rest_in_new_measure

    @property
    def use_multimeasure_rests(self) -> bool:
        r"""If ``True``, then multi-measure rests will be used for after rests
        when added to their own measure (default is ``True``).
        """
        return self._use_multimeasure_rests

    @use_multimeasure_rests.setter
    def use_multimeasure_rests(self,
                               use_multimeasure_rests: bool,
                               ) -> None:
        if not isinstance(use_multimeasure_rests, bool):
            raise TypeError("'use_multimeasure_rests' must be 'bool'")
        self._use_multimeasure_rests = use_multimeasure_rests

    ### PRIVATE PROPERTIES ###

    @property
    def _done(self) -> bool:
        r""":obj:`bool` indicating whether the process is done (i.e. whether
        the head position has overtaken the :attr:`contents`'s length).

        This property replaces the parent's one since the parent's property
        uses the number of indeces of :attr:`contents`.
        """
        if self._fill_with_rests:
            return (self._head_position >= self._contents_length
                    or self._head_position < 0)
        else:
            return (self._head_position >= self._contents_length
                    - self._head_position or self._head_position < 0)
