
    &`il                        d dl mZ d dlmZ d dlmZmZmZm	Z	mZm
Z
mZmZmZ d dlmZ d dlmZ d dlmZ d dlmZmZmZmZmZmZmZ d dlmZmZmZm Z m!Z! d d	l"m#Z# d d
l$m%Z%m&Z& d dl'm(Z(m)Z)m*Z* d dl+m,Z, dZ-dZ. G d d          Z/deee dge f         ee dgee          f         f         dede
e0         dee0         dededee          fdZ1e/Z2dS )    )Iterator)partial)	AnyCallableDictIterabler   ListOptionalTupleUnion)ComputeStrategy)LogicalPlan)	Aggregate)AggregateFnCountMaxMeanMinStdSum)BlockBlockAccessorCallableClass	DataBatchUserDefinedFunction)ShuffleStrategy)EXPRESSION_API_GROUPDataset)DownloadExprExprStarExpr)	PublicAPIz!Computations or Descriptive StatszFunction Applicationc            !          e Zd ZdZdedeeeee         f                  dee	         fdZ
defdZ ee          d	edefd
            Zdedeeee         f         fdZ ee          ddddddddddddddeeef         dedeeef         dee         deee                  deeeef                  deee                  deeeef                  dee         dee         dee         deee	ee	e	f         ee	e	e	f         f                  deeg eeef         f                  ddfd             Z eed!"          d#ed$edefd%            Z ee           defd&            Z! ee           	 d/deeee         f         d'edefd(            Z" ee           	 d/deeee         f         d'edefd)            Z# ee           	 d/deeee         f         d'edefd*            Z$ ee           	 d/deeee         f         d'edefd+            Z% ee           	 	 	 d0deeee         f         d-e	d'edefd.            Z&dS )1GroupedDatazRepresents a grouped dataset created by calling ``Dataset.groupby()``.

    The actual groupby is deferred until an aggregation is applied.
    datasetkeynum_partitionsc                0    || _         || _        || _        dS )zConstruct a dataset grouped by key (internal API).

        The constructor is not part of the GroupedData API.
        Use the ``Dataset.groupby()`` method to construct one.
        N)_dataset_key_num_partitions)selfr%   r&   r'   s       i/home/jaya/work/projects/VOICE-AGENT/VIET/agent-env/lib/python3.11/site-packages/ray/data/grouped_data.py__init__zGroupedData.__init__   s      ")58	.<    returnc                 @    | j         j         d| j         d| j        dS )Nz	(dataset=z, key=))	__class____name__r)   r*   r,   s    r-   __repr__zGroupedData.__repr__/   s,    ~&XXXX$)XXX	
r/   )	api_groupaggsc                     | j         j                                        }t          | j         j        j        | j        || j                  }t          || j         j	                  }t          ||          S )a  Implements an accumulator-based aggregation.

        Args:
            aggs: Aggregations to do.

        Returns:
            The output is an dataset of ``n + 1`` columns where the first column
            is the groupby key and the second through ``n + 1`` columns are the
            results of the aggregations.
            If groupby key is ``None`` then the key part of return is omitted.
        )r&   r8   r'   )r)   _plancopyr   _logical_plandagr*   r+   r   contextr   )r,   r8   planoplogical_plans        r-   	aggregatezGroupedData.aggregate4   su     }"''))M'+	/	
 
 
 #2t}'<==
 
 	
r/   agg_clsonc                 R     | j         j        ||g|R d| j        i|} | j        | S )aV  Helper for aggregating on a particular subset of the dataset.

        This validates the `on` argument, and converts a list of column names
        to a multi-aggregation. A null `on` results in a
        multi-aggregation on all columns for an Arrow Dataset, and a single
        aggregation on the entire row for a simple Dataset.
        	skip_cols)r)   _build_multicolumn_aggsr*   rB   )r,   rC   rD   argskwargsr8   s         r-   _aggregate_onzGroupedData._aggregate_onO   sT     5t}4R

 
 
*.)
7=
 
 t~t$$r/   TNdefault)zero_copy_batchcomputebatch_formatfn_args	fn_kwargsfn_constructor_argsfn_constructor_kwargsnum_cpusnum_gpusmemoryconcurrencyray_remote_args_fnfnrL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   rV   rW   r   c                   | j         | j                            d          }ny| j        j        j        t
          j        k    r;| j        p| j        j        j        }| j                            || j         d          }n| j        	                    | j                   }| j         g n]t          | j         t                    r	| j         gn:t          | j         t                    r| j         nt          d| j          d          t          t                    r G fdd          }nfd	}t          t                    rj        j        |_        nj        |_         |j        |fd|d||||||	|
||d
|d|S )a  Apply the given function to each group of records of this dataset.

        While map_groups() is very flexible, note that it comes with downsides:

        * It may be slower than using more specific methods such as min(), max().
        * It requires that each group fits in memory on a single node.

        In general, prefer to use `aggregate()` instead of `map_groups()`.

        .. warning::
            Specifying both ``num_cpus`` and ``num_gpus`` for map tasks is experimental,
            and may result in scheduling or stability issues. Please
            `report any issues <https://github.com/ray-project/ray/issues/new/choose>`_
            to the Ray team.

        Examples:
            >>> # Return a single record per group (list of multiple records in,
            >>> # list of a single record out).
            >>> import ray
            >>> import pandas as pd
            >>> import numpy as np
            >>> # Get first value per group.
            >>> ds = ray.data.from_items([ # doctest: +SKIP
            ...     {"group": 1, "value": 1},
            ...     {"group": 1, "value": 2},
            ...     {"group": 2, "value": 3},
            ...     {"group": 2, "value": 4}])
            >>> ds.groupby("group").map_groups( # doctest: +SKIP
            ...     lambda g: {"result": np.array([g["value"][0]])})

            >>> # Return multiple records per group (dataframe in, dataframe out).
            >>> df = pd.DataFrame(
            ...     {"A": ["a", "a", "b"], "B": [1, 1, 3], "C": [4, 6, 5]}
            ... )
            >>> ds = ray.data.from_pandas(df) # doctest: +SKIP
            >>> grouped = ds.groupby("A") # doctest: +SKIP
            >>> grouped.map_groups( # doctest: +SKIP
            ...     lambda g: g.apply(
            ...         lambda c: c / g[c.name].sum() if c.name in ["B", "C"] else c
            ...     )
            ... ) # doctest: +SKIP

        Args:
            fn: The function to apply to each group of records, or a class type
                that can be instantiated to create such a callable. It takes as
                input a batch of all records from a single group, and returns a
                batch of zero or more records, similar to map_batches().
            zero_copy_batch: If True, each group of rows (batch) will be provided w/o
                making an additional copy.
            compute: The compute strategy to use for the map operation.

                * If ``compute`` is not specified for a function, will use ``ray.data.TaskPoolStrategy()`` to launch concurrent tasks based on the available resources and number of input blocks.

                * Use ``ray.data.TaskPoolStrategy(size=n)`` to launch at most ``n`` concurrent Ray tasks.

                * If ``compute`` is not specified for a callable class, will use ``ray.data.ActorPoolStrategy(min_size=1, max_size=None)`` to launch an autoscaling actor pool from 1 to unlimited workers.

                * Use ``ray.data.ActorPoolStrategy(size=n)`` to use a fixed size actor pool of ``n`` workers.

                * Use ``ray.data.ActorPoolStrategy(min_size=m, max_size=n)`` to use an autoscaling actor pool from ``m`` to ``n`` workers.

                * Use ``ray.data.ActorPoolStrategy(min_size=m, max_size=n, initial_size=initial)`` to use an autoscaling actor pool from ``m`` to ``n`` workers, with an initial size of ``initial``.

            batch_format: Specify ``"default"`` to use the default block format
                (NumPy), ``"pandas"`` to select ``pandas.DataFrame``, "pyarrow" to
                select ``pyarrow.Table``, or ``"numpy"`` to select
                ``Dict[str, numpy.ndarray]``, or None to return the underlying block
                exactly as is with no additional formatting.
            fn_args: Arguments to `fn`.
            fn_kwargs: Keyword arguments to `fn`.
            fn_constructor_args: Positional arguments to pass to ``fn``'s constructor.
                You can only provide this if ``fn`` is a callable class. These arguments
                are top-level arguments in the underlying Ray actor construction task.
            fn_constructor_kwargs: Keyword arguments to pass to ``fn``'s constructor.
                This can only be provided if ``fn`` is a callable class. These arguments
                are top-level arguments in the underlying Ray actor construction task.
            num_cpus: The number of CPUs to reserve for each parallel map worker.
            num_gpus: The number of GPUs to reserve for each parallel map worker. For
                example, specify `num_gpus=1` to request 1 GPU for each parallel map
                worker.
            memory: The heap memory in bytes to reserve for each parallel map worker.
            ray_remote_args_fn: A function that returns a dictionary of remote args
                passed to each map worker. The purpose of this argument is to generate
                dynamic arguments for each actor or task, and will be called each time prior
                to initializing the worker. Args returned from this dict will always
                override the args in ``ray_remote_args``. Note: this is an advanced,
                experimental feature.
            concurrency: This argument is deprecated. Use ``compute`` argument.
            ray_remote_args: Additional resource requirements to request from
                Ray (e.g., num_gpus=1 to request GPUs for the map tasks). See
                :func:`ray.remote` for details.

        Returns:
            The return type is determined by the return type of ``fn``, and the return
            value is combined from results of all groups.

        .. seealso::

            :meth:`GroupedData.aggregate`
                Use this method for common aggregation use cases.
        N   T)keyssortzYGroup-by keys are expected to either be a single column (str) or a list of columns (got 'z')c                   &    e Zd ZfdZ fdZdS )*GroupedData.map_groups.<locals>.wrapped_fnc                 "     |i || _         d S N)rX   )r,   rH   rI   rX   s      r-   r.   z3GroupedData.map_groups.<locals>.wrapped_fn.__init__	  s     b$1&11DGGGr/   c              ?   H   K   t          | j        |g|R i |E d {V  d S r`   )_apply_udf_to_groupsrX   )r,   batchrH   rI   rN   r[   s       r-   __call__z3GroupedData.map_groups.<locals>.wrapped_fn.__call__  sf      3l =A     EK            r/   N)r4   
__module____qualname__r.   rd   )rN   rX   r[   s   r-   
wrapped_fnr^     sL        2 2 2 2 2       r/   rg   c              ?   >   K   t          | g|R i |E d {V  d S r`   )rb   )rc   rH   rI   rN   rX   r[   s      r-   rg   z*GroupedData.map_groups.<locals>.wrapped_fn  sd      /t\48  <B          r/   F)
batch_sizerM   rN   rL   rO   rP   rQ   rR   rS   rT   rU   rV   udf_modifying_row_countrW   )r*   r)   repartitionr>   shuffle_strategyr   HASH_SHUFFLEr+    default_hash_shuffle_parallelismr\   
isinstancestrr	   
ValueErrorr   r   funcr4   *_map_batches_without_batch_size_validation)r,   rX   rL   rM   rN   rO   rP   rQ   rR   rS   rT   rU   rV   rW   ray_remote_argsshuffled_dsr'   rg   r[   s    `  `             @r-   
map_groupszGroupedData.map_groupsb   s   @ 9-33A66KK]"37SSS$ J=(I  -33Y  4  KK -,,TY77K
 9DD	3'' 	I;DD	4(( 	9DD<.2i< < <   b-(( 	                   b'"" 	."$'"2J"$+J F{E
 + 3"7#$)1%
 
& '
 
 	
r/   alpha)r7   	stabilitycolumn_nameexprc                 z   t          |t                    r|st          d|          t          |t                    st	          d          t          |t
                    rt	          d          |                    |          }t                      |gdt          dt          ffd} | j	        |fddd	|S )
a  Add a new column to each group using an expression.

        The supplied expression is evaluated against every row in each group, and
        the resulting column is appended to the group's records. The output dataset
        preserves the original rows and columns.

        Examples:
            >>> import ray
            >>> from ray.data.expressions import col
            >>> ds = (
            ...     ray.data.from_items([{"group": 1, "value": 1}, {"group": 1, "value": 2}])
            ...     .groupby("group")
            ...     .with_column("value_twice", col("value") * 2)
            ...     .sort(["group", "value"])
            ... )
            >>> ds.take_all()
            [{'group': 1, 'value': 1, 'value_twice': 2}, {'group': 1, 'value': 2, 'value_twice': 4}]

        Args:
            column_name: Name of the column to add.
            expr: Expression that yields the values for the new column.
            **ray_remote_args: Additional resource requirements to request from Ray
                for the underlying map tasks (for example, ``num_gpus=1``).

        Returns:
            A new :class:`~ray.data.Dataset` containing all existing columns plus
            the newly computed column.
        z-column_name must be a non-empty string, got: zBexpr must be a Ray Data expression created via the expression API.zBGroupedData.with_column does not yet support download expressions.blockr0   c                 (    ddl m}  ||           S )Nr   )eval_projection)?ray.data._internal.planner.plan_expression.expression_evaluatorr~   )r|   r~   projection_exprss     r-   _project_groupz/GroupedData.with_column.<locals>._project_groupj  s6          #?#3U;;;r/   NT)rN   rL   )
ro   rp   rq   r    	TypeErrorr   aliasr!   r   rv   )r,   ry   rz   rt   aliased_exprr   r   s         @r-   with_columnzGroupedData.with_column7  s   F +s++ 	; 	OOO   $%% 	T   dL)) 	T   zz+..$JJ5	<% 	<E 	< 	< 	< 	< 	< 	< t
 
 
 	
 
 	
r/   c                 D    |                      t                                S )a  Compute count aggregation.

        Examples:
            >>> import ray
            >>> ray.data.from_items([ # doctest: +SKIP
            ...     {"A": x % 3, "B": x} for x in range(100)]).groupby( # doctest: +SKIP
            ...     "A").count() # doctest: +SKIP

        Returns:
            A dataset of ``[k, v]`` columns where ``k`` is the groupby key and
            ``v`` is the number of rows with that key.
            If groupby key is ``None`` then the key part of return is omitted.
        )rB   r   r5   s    r-   countzGroupedData.countx  s     ~~egg&&&r/   ignore_nullsc                 <    |                      t          ||          S )a  Compute grouped sum aggregation.

        Examples:
            >>> import ray
            >>> ray.data.from_items([ # doctest: +SKIP
            ...     (i % 3, i, i**2) # doctest: +SKIP
            ...     for i in range(100)])  # doctest: +SKIP
            ...     .groupby(lambda x: x[0] % 3)  # doctest: +SKIP
            ...     .sum(lambda x: x[2]) # doctest: +SKIP
            >>> ray.data.range(100).groupby("id").sum() # doctest: +SKIP
            >>> ray.data.from_items([ # doctest: +SKIP
            ...     {"A": i % 3, "B": i, "C": i**2} # doctest: +SKIP
            ...     for i in range(100)])  # doctest: +SKIP
            ...     .groupby("A")  # doctest: +SKIP
            ...     .sum(["B", "C"]) # doctest: +SKIP

        Args:
            on: a column name or a list of column names to aggregate.
            ignore_nulls: Whether to ignore null values. If ``True``, null
                values will be ignored when computing the sum; if ``False``,
                if a null value is encountered, the output will be null.
                We consider np.nan, None, and pd.NaT to be null values.
                Default is ``True``.

        Returns:
            The sum result.

            For different values of ``on``, the return varies:

            - ``on=None``: a dataset containing a groupby key column,
              ``"k"``, and a column-wise sum column for each original column
              in the dataset.
            - ``on=["col_1", ..., "col_n"]``: a dataset of ``n + 1``
              columns where the first column is the groupby key and the second
              through ``n + 1`` columns are the results of the aggregations.

            If groupby key is ``None`` then the key part of return is omitted.
        r   )rJ   r   r,   rD   r   s      r-   sumzGroupedData.sum  s     T !!#r!EEEr/   c                 <    |                      t          ||          S )a  Compute grouped min aggregation.

        Examples:
            >>> import ray
            >>> ray.data.le(100).groupby("value").min() # doctest: +SKIP
            >>> ray.data.from_items([ # doctest: +SKIP
            ...     {"A": i % 3, "B": i, "C": i**2} # doctest: +SKIP
            ...     for i in range(100)])  # doctest: +SKIP
            ...     .groupby("A")  # doctest: +SKIP
            ...     .min(["B", "C"]) # doctest: +SKIP

        Args:
            on: a column name or a list of column names to aggregate.
            ignore_nulls: Whether to ignore null values. If ``True``, null
                values will be ignored when computing the min; if ``False``,
                if a null value is encountered, the output will be null.
                We consider np.nan, None, and pd.NaT to be null values.
                Default is ``True``.

        Returns:
            The min result.

            For different values of ``on``, the return varies:

            - ``on=None``: a dataset containing a groupby key column,
              ``"k"``, and a column-wise min column for each original column in
              the dataset.
            - ``on=["col_1", ..., "col_n"]``: a dataset of ``n + 1``
              columns where the first column is the groupby key and the second
              through ``n + 1`` columns are the results of the aggregations.

            If groupby key is ``None`` then the key part of return is omitted.
        r   )rJ   r   r   s      r-   minzGroupedData.min       J !!#r!EEEr/   c                 <    |                      t          ||          S )a  Compute grouped max aggregation.

        Examples:
            >>> import ray
            >>> ray.data.le(100).groupby("value").max() # doctest: +SKIP
            >>> ray.data.from_items([ # doctest: +SKIP
            ...     {"A": i % 3, "B": i, "C": i**2} # doctest: +SKIP
            ...     for i in range(100)])  # doctest: +SKIP
            ...     .groupby("A")  # doctest: +SKIP
            ...     .max(["B", "C"]) # doctest: +SKIP

        Args:
            on: a column name or a list of column names to aggregate.
            ignore_nulls: Whether to ignore null values. If ``True``, null
                values will be ignored when computing the max; if ``False``,
                if a null value is encountered, the output will be null.
                We consider np.nan, None, and pd.NaT to be null values.
                Default is ``True``.

        Returns:
            The max result.

            For different values of ``on``, the return varies:

            - ``on=None``: a dataset containing a groupby key column,
              ``"k"``, and a column-wise max column for each original column in
              the dataset.
            - ``on=["col_1", ..., "col_n"]``: a dataset of ``n + 1``
              columns where the first column is the groupby key and the second
              through ``n + 1`` columns are the results of the aggregations.

            If groupby key is ``None`` then the key part of return is omitted.
        r   )rJ   r   r   s      r-   maxzGroupedData.max  r   r/   c                 <    |                      t          ||          S )a  Compute grouped mean aggregation.

        Examples:
            >>> import ray
            >>> ray.data.le(100).groupby("value").mean() # doctest: +SKIP
            >>> ray.data.from_items([ # doctest: +SKIP
            ...     {"A": i % 3, "B": i, "C": i**2} # doctest: +SKIP
            ...     for i in range(100)])  # doctest: +SKIP
            ...     .groupby("A")  # doctest: +SKIP
            ...     .mean(["B", "C"]) # doctest: +SKIP

        Args:
            on: a column name or a list of column names to aggregate.
            ignore_nulls: Whether to ignore null values. If ``True``, null
                values will be ignored when computing the mean; if ``False``,
                if a null value is encountered, the output will be null.
                We consider np.nan, None, and pd.NaT to be null values.
                Default is ``True``.

        Returns:
            The mean result.

            For different values of ``on``, the return varies:

            - ``on=None``: a dataset containing a groupby key column,
              ``"k"``, and a column-wise mean column for each original column
              in the dataset.
            - ``on=["col_1", ..., "col_n"]``: a dataset of ``n + 1``
              columns where the first column is the groupby key and the second
              through ``n + 1`` columns are the results of the aggregations.

            If groupby key is ``None`` then the key part of return is omitted.
        r   )rJ   r   r   s      r-   meanzGroupedData.mean  s     J !!$!FFFr/   rZ   ddofc                 >    |                      t          |||          S )ah  Compute grouped standard deviation aggregation.

        Examples:
            >>> import ray
            >>> ray.data.range(100).groupby("id").std(ddof=0) # doctest: +SKIP
            >>> ray.data.from_items([ # doctest: +SKIP
            ...     {"A": i % 3, "B": i, "C": i**2} # doctest: +SKIP
            ...     for i in range(100)])  # doctest: +SKIP
            ...     .groupby("A")  # doctest: +SKIP
            ...     .std(["B", "C"]) # doctest: +SKIP

        NOTE: This uses Welford's online method for an accumulator-style
        computation of the standard deviation. This method was chosen due to
        it's numerical stability, and it being computable in a single pass.
        This may give different (but more accurate) results than NumPy, Pandas,
        and sklearn, which use a less numerically stable two-pass algorithm.
        See
        https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm

        Args:
            on: a column name or a list of column names to aggregate.
            ddof: Delta Degrees of Freedom. The divisor used in calculations
                is ``N - ddof``, where ``N`` represents the number of elements.
            ignore_nulls: Whether to ignore null values. If ``True``, null
                values will be ignored when computing the std; if ``False``,
                if a null value is encountered, the output will be null.
                We consider np.nan, None, and pd.NaT to be null values.
                Default is ``True``.

        Returns:
            The standard deviation result.

            For different values of ``on``, the return varies:

            - ``on=None``: a dataset containing a groupby key column,
              ``"k"``, and a column-wise std column for each original column in
              the dataset.
            - ``on=["col_1", ..., "col_n"]``: a dataset of ``n + 1``
              columns where the first column is the groupby key and the second
              through ``n + 1`` columns are the results of the aggregations.

            If groupby key is ``None`` then the key part of return is omitted.
        )r   r   )rJ   r   )r,   rD   r   r   s       r-   stdzGroupedData.std*  s"    d !!#r4!PPPr/   )NT)NrZ   T)'r4   re   rf   __doc__r   r
   r   rp   r	   intr.   r6   r"   FA_API_GROUPr   rB   typerJ   r   r   boolr   r   r   r   floatr   r   rv   r   r    r   CDS_API_GROUPr   r   r   r   r   r    r/   r-   r$   r$      s        
== eCcN+,=
 != = = = 
# 
 
 
 

 Y&&&
{ 
w 
 
 
 '&
4%% #tCy.!% % % %& Y&&&
 !%/3&/+/.27;:>$($("&SWEIR
 R
 R
	9 45R
 	R

 sO+,R
 smR
 (3-(R
 DcN+R
 &hsm4R
  (S#X7R
 5/R
 5/R
 R
 eCsCx%S#:N$NOPR
 %Xb$sCx..@%ABR
" 
#R
 R
 R
 '&R
h Y-AAA>
>
 >

 
>
 >
 >
 BA>
@ Y''''w ' ' ' (''  Y'''EI)F )FT#Y')F>B)F	)F )F )F (')FV Y'''EI$F $FT#Y'$F>B$F	$F $F $F ('$FL Y'''EI$F $FT#Y'$F>B$F	$F $F $F ('$FL Y'''EI$G $GT#Y'$G>B$G	$G $G $G ('$GL Y''' %)!	1Q 1Q#tCy.!1Q 1Q 	1Q
 
1Q 1Q 1Q ('1Q 1Q 1Qr/   r$   udf.r|   r[   rN   rH   rI   r0   c              /     K   t          j        |          }|                    |          }t          |dd         |dd                   D ]r\  }}	|                    ||	d          }
t          j        |
          } | |                    |          g|R i |}t          |t                    r	|E d{V  n|V  sdS )zApply UDF to groups of rows having the same set of values of the specified
    columns (keys).

    NOTE: This function is defined at module level to avoid capturing closures and make it serializable.
    NrZ   F)r;   )r   	for_block_get_group_boundaries_sortedzipsliceto_batch_formatro   IteratorABC)r   r|   r[   rN   rH   rI   block_accessor
boundariesstartendgroup_blockgroup_block_accessorresults                r-   rb   rb   _  s       #,U33N<<TBBJ*SbS/:abb>::  
s$**5#E*BB,6{CC
  00>>
AE
 
 
IO
 

 fk** 	 LLLL# r/   N)3collections.abcr   r   	functoolsr   typingr   r   r   r   r	   r
   r   r   ray.data._internal.computer   %ray.data._internal.logical.interfacesr   8ray.data._internal.logical.operators.all_to_all_operatorr   ray.data.aggregater   r   r   r   r   r   r   ray.data.blockr   r   r   r   r   ray.data.contextr   ray.data.datasetr   r   ray.data.expressionsr   r    r!   ray.util.annotationsr"   r   r   r$   rp   rb   GroupedDatasetr   r/   r-   <module>r      s`   3 3 3 3 3 3       X X X X X X X X X X X X X X X X X X X X X X 6 6 6 6 6 6 = = = = = = N N N N N N K K K K K K K K K K K K K K K K K K              - , , , , , : : : : : : : : = = = = = = = = = = * * * * * *3%C	Q C	Q C	Q C	Q C	Q C	Q C	Q C	QL%	)S!9,-)S!8I#667	9
%
 % s)% 3-% % % i% % % %R r/   