U
    Cvft                     @  sl  d dl mZ d dlZd dl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mZmZmZmZmZ d dlZd dl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# d d	l$m%Z%m&Z& d d
l'm(Z(m)Z)m*Z* e	rd dl+m,Z, d dl-m.Z. d dl/m0Z0m1Z1m2Z2m3Z3 ededZ4dej5dfdddddddddZ6eeeeedf f df Z7ee7ee f Z8ee8ef Z9ee8eee(f f Z:G dd dee4 Z;ddde< ej5ddddd d!d"d#Z=ddde< dej5fd$dd%d&d'Z>dddej5de< fddd(d)dddd*dd+	d,d-Z?dddej5fdd.d(d)dddd/d0d1Z@d2d3 ZAd4d4d5d6d7ZBdd8d4d9d:d;d<ZCdS )=    )annotationsN)defaultdict)suppress)TYPE_CHECKINGAnyCallableDictGenericHashableIterableMappingTupleTypeTypeVarcast)dtypes)DataWithCoords)IndexIndexesPandasIndexPandasMultiIndexindexes_all_equalsafe_cast_to_index)is_dict_likeis_full_slice)Variableas_compatible_datacalculate_dimensions	DataArrayDataset)JoinOptionsT_DataArray	T_DatasetT_DataWithCoordsDataAlignable)boundTFzMapping[Any, Variable]Mapping[Any, Any]boolr   zdict[Hashable, Variable])	variablesdim_pos_indexerscopy
fill_valuesparsereturnc              
     s"  i }t | }t t   D ]D\}}|dk  r@| q t|t||dr | q |  D ]\}	}
t	|t
r||	tj}n|}|r|
j|d}
t fdd|
jD }tfdd|
jD }|r|
j||d}n*tdd |D r|
j|d}n|
| }|||	< qn|S )zConform a dictionary of variables onto a new set of variables reindexed
    with dimension positional indexers and possibly filled with missing values.

    Not public API.

    r   )r-   c                 3  s.   | ]&}|krt d n |t d V  qd S N)sliceget.0d)r+   unchanged_dims 9/tmp/pip-unpacked-wheel-h316xyqg/xarray/core/alignment.py	<genexpr>P   s   z$reindex_variables.<locals>.<genexpr>c                 3  s   | ]}| kV  qd S r0   r7   r3   )masked_dimsr7   r8   r9   T   s     c                 s  s   | ]}t |V  qd S r0   )r   r4   kr7   r7   r8   r9   X   s     deep)r   setitemsanyaddnpZarray_equalZaranger2   
isinstancedictr   NAZ
_as_sparsetupledimsZ_getitem_with_maskallr,   )r*   r+   r,   r-   r.   new_variables	dim_sizesdimZindxrnamevarZfill_value_Zneeds_maskingZnew_varr7   )r+   r:   r6   r8   reindex_variables.   s4    

rO   .c                   @  s  e Zd ZU dZded< ded< ded< ded< d	ed
< d	ed< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded< ded < d!ed"< d#d$e e d$d$d%ejd&f	d'dd(d)d*d+d,dddd-
d.d/Zd0d1d2d3d4Z	d5d6d7d8Z
d5d6d9d:Zd5d6d;d<Zdd6d=d>Zd?d6d@dAZd5d6dBdCZd5d6dDdEZd5d6dFdGZddHdIdJdKZdLddMdNdOdPZdLddLdNdQdRZd5d6dSdTZd5d6dUdVZd$S )WAlignerzImplements all the complex logic for the re-indexing and alignment of Xarray
    objects.

    For internal use only, not public API.
    Usage:

    aligner = Aligner(*objects, **kwargs)
    aligner.align()
    aligned_objects = aligner.results

    tuple[DataAlignable, ...]objectsresultsz)tuple[dict[MatchingIndexKey, Index], ...]objects_matching_indexesstrjoinzfrozenset[Hashable]exclude_dimsexclude_varsr)   r,   r   r-   r.   zdict[MatchingIndexKey, Index]indexesz0dict[MatchingIndexKey, dict[Hashable, Variable]]
index_varsz#dict[MatchingIndexKey, list[Index]]all_indexesz6dict[MatchingIndexKey, list[dict[Hashable, Variable]]]all_index_varsaligned_indexesaligned_index_varszdict[MatchingIndexKey, bool]reindexzdict[str, Any]reindex_kwargszdict[Hashable, set]unindexed_dim_sizeszIndexes[Index]new_indexesinnerNTFzIterable[DataAlignable]zMapping[Any, Any] | Noner   Iterable[Hashable]
str | None*int | float | Iterable[int | float] | None)
rR   rV   rY   rW   rX   method	tolerancer,   r-   r.   c                 C  s   t || _d| _|dkr&td| || _|| _|	| _|
| _|d krV|d krVi | _n||d| _t	|t
rr|g}t|| _t|| _|d kri }| |\| _| _i | _i | _i | _i | _i | _i | _t  | _d S )Nr7   )rc   outeroverrideexactleftrightzinvalid value for join: )rg   rh   )rG   rR   rT   
ValueErrorrV   r,   r-   r.   r`   rD   rU   	frozensetrW   rX   _normalize_indexesrY   rZ   r[   r\   ra   r]   r^   r_   rS   )selfrR   rV   rY   rW   rX   rg   rh   r,   r-   r.   r7   r7   r8   __init__   s2    



zAligner.__init__r(   z-tuple[NormalizedIndexes, NormalizedIndexVars])rY   r/   c                 C  s  t |trt|j}ni }i }| D ]\}}t |tst|d|f|fkrdtd|j d| dt	|}t
|}||_t |tjrt||}nt|||jd}||  |||< q&i }i }	t|| D ]\}}
g }t }|
 D ]&\}}|j}|||f || q|| j@ }||kr*qnF|rpddd |D }dd	d || D }td
| d| t|t|f}|||< |
|	|< q||	fS )zNormalize the indexes/indexers used for re-indexing or alignment.

        Return dictionaries of xarray Index objects and coordinate variables
        such that we can group matching indexes based on the dictionary keys.

        rH   zIndexer has dimensions z3 that are different from that to be indexed along '')Zcoord_dtype, c                 s  s   | ]}t |V  qd S r0   rU   r3   r7   r7   r8   r9      s     z-Aligner._normalize_indexes.<locals>.<genexpr>c                 s  s   | ]}t |V  qd S r0   ru   r3   r7   r7   r8   r9      s     zcannot exclude dimension(s) zY from alignment because these are used by an index together with non-excluded dimensions )rD   r   rE   r*   r@   r   getattrrn   rH   r   r   rM   pdZ
MultiIndexr   r   Zdtypeupdatecreate_variablesZgroup_by_indexr?   appendrW   rV   rG   type)rq   rY   Zxr_variablesZ
xr_indexesr<   idxdataZpd_idxZnormalized_indexesZnormalized_index_varsrZ   Zcoord_names_and_dimsZall_dimsrM   rN   rH   rW   Zexcl_dims_strZincl_dims_strkeyr7   r7   r8   rp      sP    






zAligner._normalize_indexesNone)r/   c                 C  s  t t}t t}t dd }g }| jD ]}| |j\}}|| | D ]\}}	|| |	 qL| D ]>\}}
|| |
 t|
 D ]\}}|| | | qqlq&t	|| _
|| _|| _| jdkr| D ]2}| D ]$\}}t|dkrtd|dqqd S )Nc                   S  s   t tS r0   )r   r?   r7   r7   r7   r8   <lambda>      z/Aligner.find_matching_indexes.<locals>.<lambda>rj      zPcannot align objects with join='override' with matching indexes along dimension z that don't have the same size)r   listrR   rp   xindexesrz   r@   r   rB   rG   rT   r[   r\   rV   valueslenrn   )rq   r[   r\   Zall_indexes_dim_sizesrT   objZobj_indexesZobj_index_varsr~   r|   rZ   rL   sizerK   sizesr7   r7   r8   find_matching_indexes   s.    



zAligner.find_matching_indexesc                 C  sT   t t}| jD ]:}|jD ].}|| jkr||jjkr|| |j|  qq|| _d S r0   )	r   r?   rR   rH   rW   r   rB   r   ra   )rq   ra   r   rL   r7   r7   r8   find_matching_unindexed_dims  s    

z$Aligner.find_matching_unindexed_dimsc                 C  s   t | jt | jB }tt}tt}|D ]P\}}t  }|D ]"\}}||  d7  < || q:|D ]}	||	  d7  < qbq(|df|dffD ]L\}
}dd |
 D }|rddd | D }td	| d
| dqdS )aR  Check for uniqueness of both coordinate and dimension names across all sets
        of matching indexes.

        We need to make sure that all indexes used for re-indexing or alignment
        are fully compatible and do not conflict each other.

        Note: perhaps we could choose less restrictive constraints and instead
        check for conflicts among the dimension (position) indexers returned by
        `Index.reindex_like()` for each matching pair of object index / aligned
        index?
        (ref: https://github.com/pydata/xarray/issues/1603#issuecomment-442965602)

        r   ZcoordinatesZ
dimensionsc                 S  s   i | ]\}}|d kr||qS )r   r7   r4   r<   vr7   r7   r8   
<dictcomp>A  s       z4Aligner.assert_no_index_conflict.<locals>.<dictcomp>rt   c                 s  s"   | ]\}}|d | dV  qdS )z (z conflicting indexes)Nr7   r   r7   r7   r8   r9   C  s    z3Aligner.assert_no_index_conflict.<locals>.<genexpr>zRcannot re-index or align objects with conflicting indexes found for the following : z
Conflicting indexes may occur when
- they relate to different sets of coordinate and/or dimension names
- they don't have the same type
- they may be used to reindex data along common dimensionsN)	r?   r[   rY   r   intrx   r@   rV   rn   )rq   Zmatching_keysZcoord_countZ	dim_countZcoord_names_dims_Zdims_setrM   rH   rL   countmsgdupZ	items_msgr7   r7   r8   assert_no_index_conflict&  s&    
z Aligner.assert_no_index_conflictc                 C  s   t |sdS i }|D ]:}|| jkr| j| }t|dkr> dS tt|||< q|ri }|D ]&}|d }| D ]}	||	j qpq\| D ]\}}
|	|d|
kr dS qdS )a  Whether or not we need to reindex variables for a set of
        matching indexes.

        We don't reindex when all matching indexes are equal for two reasons:
        - It's faster for the usual case (already aligned objects).
        - It ensures it's possible to do operations that don't require alignment
          on indexes with duplicate values (which cannot be reindexed with
          pandas). This is useful, e.g., for overwriting such duplicate indexes.

        Tr   F)
r   ra   r   nextiterr   rx   r   r@   r2   )rq   rH   cmp_indexesZunindexed_dims_sizesrL   r   Zindexed_dims_sizescmprZ   rN   r   r7   r7   r8   _need_reindexO  s&    

zAligner._need_reindexr   c                 C  sn   | j dkr&ttjtj|j | j dS | j dkr:tdS | j dkrNtdS | j dkrbtdS dd	 S d S )
N)ri   rc   )howrl   r   rm   r   rj   c                 S  s   d S r0   r7   )r   r7   r7   r8   r     r   z+Aligner._get_index_joiner.<locals>.<lambda>)rV   	functoolspartialreduceoperator
itemgetter)rq   	index_clsr7   r7   r8   _get_index_joinerv  s    






zAligner._get_index_joinerc                 C  s.  i }i }i }i }i }| j  D ]\}}| j| }dd |d  D }	|d }
| jdkrp|d }|d }d}n|| jkr| j| }| j| }tt|g| |g| }| 	|	|}nt
|dkr| 	|	tt||}nd}|rX| jdkrtdd	d
d |d D  | |
}||}| jdkr8|d }n| jdkrN|d }n| }n|d }|d }|||< |||< |||< | D ]\}}|||< |||< qq| j D ]Z\}}||kr| j| }d||< |||< |||< | D ]\}}|||< |||< qq|| _|| _|| _t||| _dS )zICompute all aligned indexes and their corresponding coordinate variables.c                 S  s   h | ]}|j D ]}|qqS r7   rH   )r4   Zcoordr5   r7   r7   r8   	<setcomp>  s       z(Aligner.align_indexes.<locals>.<setcomp>r   r   rj   Frk   ztcannot align objects with join='exact' where index/labels/sizes are not equal along these coordinates (dimensions): rt   c                 s  s    | ]\}}|d |V  qdS ) Nr7   )r4   rM   rH   r7   r7   r8   r9     s     z(Aligner.align_indexes.<locals>.<genexpr>rl   rm   r   N)r[   r@   r\   r   rV   rY   rZ   r   zipr   r   rn   r   ry   r]   r^   r_   r   rb   )rq   r]   r^   r_   rb   Znew_index_varsr~   matching_indexesZmatching_index_varsrH   r   Zjoined_indexZjoined_index_varsZneed_reindexr   joinerrM   rN   r|   rZ   r7   r7   r8   align_indexes  s    










zAligner.align_indexesc                 C  sn   | j  D ]^\}}| jj|}|d k	r@|| d|d}nd}t|dkr
td|d|| q
d S )Nz9 (note: an index is found along that dimension with size=) r   z(cannot reindex or align along dimension z) because of conflicting dimension sizes: )ra   r@   rb   rH   r2   rB   r   rn   )rq   rL   r   Z
index_sizeZadd_err_msgr7   r7   r8    assert_unindexed_dim_sizes_equal  s    

z(Aligner.assert_unindexed_dim_sizes_equalc                 C  s   t | j}t|dd  D ]\}}i }i }| j|d  }| j D ]L\}}||}	|	d k	rB| j|  D ]"\}
}|||
< |j| jd||
< qjqB|	||||d < qt
|| _d S )Nr   r=   )r   rR   	enumeraterT   r]   r@   r2   r^   r,   Z_overwrite_indexesrG   rS   )rq   rR   ir   rb   rJ   r   r~   aligned_idxobj_idxrM   rN   r7   r7   r8   override_indexes  s    

zAligner.override_indexeszdict[Hashable, Any])r   r/   c                 C  sR   i }| j  D ]>\}}||}|d k	r| j| r|j|f| j}|| q|S r0   )r]   r@   r2   r_   reindex_liker`   rx   )rq   r   r+   r~   r   r   indexersr7   r7   r8   _get_dim_pos_indexers  s    

zAligner._get_dim_pos_indexersr&   z6tuple[dict[Hashable, Index], dict[Hashable, Variable]])r   r   r/   c                 C  s   i }i }| j  D ]|\}}| j| }||}|d krZdd | D }	|	t|jkrZ|}|d k	r| D ]"\}
}|||
< |j| jd||
< qjq||fS )Nc                 S  s   h | ]}|j D ]}|qqS r7   r   )r4   rN   r5   r7   r7   r8   r     s       z0Aligner._get_indexes_and_vars.<locals>.<setcomp>r=   )r]   r@   r^   r2   r   r?   rH   r,   )rq   r   r   rb   rJ   r~   r   rZ   r   Zindex_vars_dimsrM   rN   r7   r7   r8   _get_indexes_and_vars  s    

zAligner._get_indexes_and_varsc              	   C  sB   |  ||\}}| |}|| |||| j| j| j}|j|_|S r0   )r   r   Z_reindex_callbackr-   rW   rX   encoding)rq   r   r   rb   rJ   r+   Znew_objr7   r7   r8   _reindex_one"  s    
	zAligner._reindex_onec                   s&   t  fddt j jD  _d S )Nc                 3  s   | ]\}}  ||V  qd S r0   )r   )r4   r   r   rq   r7   r8   r9   7  s   z&Aligner.reindex_all.<locals>.<genexpr>)rG   r   rR   rT   rS   r   r7   r   r8   reindex_all6  s     zAligner.reindex_allc                 C  sz   | j s2t| jdkr2| j\}|j| jdf| _d S |   |   |   |   | 	  | j
dkrn|   n|   d S )Nr   r=   rj   )rY   r   rR   r,   rS   r   r   r   r   r   rV   r   r   )rq   r   r7   r7   r8   align>  s    

zAligner.align)__name__
__module____qualname____doc____annotations__ro   r   rF   rr   rp   r   r   r   r   r   r   r   r   r   r   r   r   r   r7   r7   r7   r8   rP   k   sX   
"0>"
)'QrP   rc   rV   r,   rY   excluder-   r"   rQ   )rR   rV   r,   r/   c                 G  s"   t || ||||d}|  |jS )a`  
    Given any number of Dataset and/or DataArray objects, returns new
    objects with aligned indexes and dimension sizes.

    Array from the aligned objects are suitable as input to mathematical
    operators, because along each dimension they have the same index and size.

    Missing values (if ``join != 'inner'``) are filled with ``fill_value``.
    The default fill value is NaN.

    Parameters
    ----------
    *objects : Dataset or DataArray
        Objects to align.
    join : {"outer", "inner", "left", "right", "exact", "override"}, optional
        Method for joining the indexes of the passed objects along each
        dimension:

        - "outer": use the union of object indexes
        - "inner": use the intersection of object indexes
        - "left": use indexes from the first object with each dimension
        - "right": use indexes from the last object with each dimension
        - "exact": instead of aligning, raise `ValueError` when indexes to be
          aligned are not equal
        - "override": if indexes are of same size, rewrite indexes to be
          those of the first object with that dimension. Indexes for the same
          dimension must have the same size in all objects.

    copy : bool, default: True
        If ``copy=True``, data in the return values is always copied. If
        ``copy=False`` and reindexing is unnecessary, or can be performed with
        only slice operations, then the output may share memory with the input.
        In either case, new xarray objects are always returned.
    indexes : dict-like, optional
        Any indexes explicitly provided with the `indexes` argument should be
        used in preference to the aligned indexes.
    exclude : sequence of str, optional
        Dimensions that must be excluded from alignment
    fill_value : scalar or dict-like, optional
        Value to use for newly missing values. If a dict-like, maps
        variable names to fill values. Use a data array's name to
        refer to its values.

    Returns
    -------
    aligned : tuple of DataArray or Dataset
        Tuple of objects with the same type as `*objects` with aligned
        coordinates.

    Raises
    ------
    ValueError
        If any dimensions without labels on the arguments have different sizes,
        or a different size than the size of the aligned dimension labels.

    Examples
    --------
    >>> x = xr.DataArray(
    ...     [[25, 35], [10, 24]],
    ...     dims=("lat", "lon"),
    ...     coords={"lat": [35.0, 40.0], "lon": [100.0, 120.0]},
    ... )
    >>> y = xr.DataArray(
    ...     [[20, 5], [7, 13]],
    ...     dims=("lat", "lon"),
    ...     coords={"lat": [35.0, 42.0], "lon": [100.0, 120.0]},
    ... )

    >>> x
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[25, 35],
           [10, 24]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0
      * lon      (lon) float64 100.0 120.0

    >>> y
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[20,  5],
           [ 7, 13]])
    Coordinates:
      * lat      (lat) float64 35.0 42.0
      * lon      (lon) float64 100.0 120.0

    >>> a, b = xr.align(x, y)
    >>> a
    <xarray.DataArray (lat: 1, lon: 2)>
    array([[25, 35]])
    Coordinates:
      * lat      (lat) float64 35.0
      * lon      (lon) float64 100.0 120.0
    >>> b
    <xarray.DataArray (lat: 1, lon: 2)>
    array([[20,  5]])
    Coordinates:
      * lat      (lat) float64 35.0
      * lon      (lon) float64 100.0 120.0

    >>> a, b = xr.align(x, y, join="outer")
    >>> a
    <xarray.DataArray (lat: 3, lon: 2)>
    array([[25., 35.],
           [10., 24.],
           [nan, nan]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0 42.0
      * lon      (lon) float64 100.0 120.0
    >>> b
    <xarray.DataArray (lat: 3, lon: 2)>
    array([[20.,  5.],
           [nan, nan],
           [ 7., 13.]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0 42.0
      * lon      (lon) float64 100.0 120.0

    >>> a, b = xr.align(x, y, join="outer", fill_value=-999)
    >>> a
    <xarray.DataArray (lat: 3, lon: 2)>
    array([[  25,   35],
           [  10,   24],
           [-999, -999]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0 42.0
      * lon      (lon) float64 100.0 120.0
    >>> b
    <xarray.DataArray (lat: 3, lon: 2)>
    array([[  20,    5],
           [-999, -999],
           [   7,   13]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0 42.0
      * lon      (lon) float64 100.0 120.0

    >>> a, b = xr.align(x, y, join="left")
    >>> a
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[25, 35],
           [10, 24]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0
      * lon      (lon) float64 100.0 120.0
    >>> b
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[20.,  5.],
           [nan, nan]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0
      * lon      (lon) float64 100.0 120.0

    >>> a, b = xr.align(x, y, join="right")
    >>> a
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[25., 35.],
           [nan, nan]])
    Coordinates:
      * lat      (lat) float64 35.0 42.0
      * lon      (lon) float64 100.0 120.0
    >>> b
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[20,  5],
           [ 7, 13]])
    Coordinates:
      * lat      (lat) float64 35.0 42.0
      * lon      (lon) float64 100.0 120.0

    >>> a, b = xr.align(x, y, join="exact")
    Traceback (most recent call last):
    ...
    ValueError: cannot align objects with join='exact' ...

    >>> a, b = xr.align(x, y, join="override")
    >>> a
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[25, 35],
           [10, 24]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0
      * lon      (lon) float64 100.0 120.0
    >>> b
    <xarray.DataArray (lat: 2, lon: 2)>
    array([[20,  5],
           [ 7, 13]])
    Coordinates:
      * lat      (lat) float64 35.0 40.0
      * lon      (lon) float64 100.0 120.0

    )rV   r,   rY   rW   r-   rP   r   rS   )rV   r,   rY   r   r-   rR   alignerr7   r7   r8   r   Q  s     Er   zIterable[Any])rR   rV   c                   sp  ddl m  ddlm |dkr$i } fdd}g }g }	g }
g }t }t }t| D ]\}}||r|| |	| || |
| qVt|ri }| D ]H\}}||r||kr|| |	| || |||< q|||< q|
| qV|rt	d
|qV|
| qVt||||||d}t||	|D ],\}}}||kr\||
|< n||
| |< q>|
S )	zgAlign objects for merging, recursing into dictionary values.

    This function is not public API.
    r   r   r    Nc                   s   t |  fS r0   )rD   )r   r   r!   r7   r8   is_alignable4  s    z deep_align.<locals>.is_alignablezXobject to align is neither an xarray.Dataset, an xarray.DataArray nor a dictionary: {!r}r   )xarray.core.dataarrayr   xarray.core.datasetr!   objectr   rz   r   r@   rn   formatr   r   )rR   rV   r,   rY   r   Zraise_on_invalidr-   r   Z	positionskeysouttargetsZno_keyZnot_replacedpositionr*   Zcurrent_outr<   r   Zalignedr~   Zaligned_objr7   r   r8   
deep_align!  s^    







	

r   re   rf   rd   )	r   r   rg   rh   r,   r-   r.   rX   r/   c           	   
   C  s,   t | f|||||||d}|  |jd S )zDRe-index either a Dataset or a DataArray.

    Not public API.

    )rY   rg   rh   r,   r-   r.   rX   r   r   )	r   r   rg   rh   r,   r-   r.   rX   r   r7   r7   r8   r_   n  s    
r_   zDataset | DataArray)r   otherrg   rh   r,   r-   r/   c           	      C  sh   |j sR|jD ]D}|| jkr|j| }| j| }||krtd|d|d|qt| |j||||dS )zcRe-index either a Dataset or a DataArray like another Dataset/DataArray.

    Not public API.

    z3different size for unlabeled dimension on argument r   z vs )r   rg   rh   r,   r-   )_indexesrH   r   rn   r_   r   )	r   r   rg   rh   r,   r-   rL   Z
other_sizeZobj_sizer7   r7   r8   r     s"    



r   c                 C  s`   i }i }| D ]J}|j D ]>}||kr||kr|j| ||< ||jkr||j| qq||fS r0   )rH   r   r   rx   r   Zget_all_coords)argsr   common_coordsdims_mapargrL   r7   r7   r8   %_get_broadcast_dims_map_common_coords  s    

r   r%   )r   r/   c                   s   ddl m} ddlm} fdd ddd fdd	}d
d
d fdd}t| |rjtd|| S t| |rtd|| S tdd S )Nr   r   r    c              
     sF      }D ].}tt | j| j| ||< W 5 Q R X q| |S r0   )r,   r   rn   shaperH   indexZset_dims)rN   Zvar_dims_maprL   )r   r   r7   r8   	_set_dims  s
    
"z$_broadcast_helper.<locals>._set_dimsr#   )arrayr/   c                   s8    | j }t| j}| | j|||j| j| jdS )N)rM   attrs)variablerE   coordsrx   	__class__rH   rM   r   )r   r}   r   r   r   r7   r8   _broadcast_array  s    


    z+_broadcast_helper.<locals>._broadcast_arrayr$   )dsr/   c                   s:    fdd j D }t j}|  || jS )Nc                   s   i | ]}| j | qS r7   )r*   r;   )r   r   r7   r8   r     s      zA_broadcast_helper.<locals>._broadcast_dataset.<locals>.<dictcomp>)	data_varsrE   r   rx   r   r   )r   r   r   r   )r   r8   _broadcast_dataset  s    

z-_broadcast_helper.<locals>._broadcast_datasetr%   z.all input must be Dataset or DataArray objects)r   r   r   r!   rD   r   rn   )r   r   r   r   r   r!   r   r   r7   )r   r   r   r   r8   _broadcast_helper  s    


r   )r   ztuple[T_DataWithCoords, ...])r   r/   c                   sL   dkrt  t|ddd}t|\  fdd|D }t|S )a^  Explicitly broadcast any number of DataArray or Dataset objects against
    one another.

    xarray objects automatically broadcast against each other in arithmetic
    operations, so this function should not be necessary for normal use.

    If no change is needed, the input data is returned to the output without
    being copied.

    Parameters
    ----------
    *args : DataArray or Dataset
        Arrays to broadcast against each other.
    exclude : sequence of str, optional
        Dimensions that must not be broadcasted

    Returns
    -------
    broadcast : tuple of DataArray or tuple of Dataset
        The same data as the input arrays, but with additional dimensions
        inserted so that all data arrays have the same dimensions and shape.

    Examples
    --------
    Broadcast two data arrays against one another to fill out their dimensions:

    >>> a = xr.DataArray([1, 2, 3], dims="x")
    >>> b = xr.DataArray([5, 6], dims="y")
    >>> a
    <xarray.DataArray (x: 3)>
    array([1, 2, 3])
    Dimensions without coordinates: x
    >>> b
    <xarray.DataArray (y: 2)>
    array([5, 6])
    Dimensions without coordinates: y
    >>> a2, b2 = xr.broadcast(a, b)
    >>> a2
    <xarray.DataArray (x: 3, y: 2)>
    array([[1, 1],
           [2, 2],
           [3, 3]])
    Dimensions without coordinates: x, y
    >>> b2
    <xarray.DataArray (x: 3, y: 2)>
    array([[5, 6],
           [5, 6],
           [5, 6]])
    Dimensions without coordinates: x, y

    Fill out the dimensions of all data variables in a dataset:

    >>> ds = xr.Dataset({"a": a, "b": b})
    >>> (ds2,) = xr.broadcast(ds)  # use tuple unpacking to extract one dataset
    >>> ds2
    <xarray.Dataset>
    Dimensions:  (x: 3, y: 2)
    Dimensions without coordinates: x, y
    Data variables:
        a        (x, y) int64 1 1 2 2 3 3
        b        (x, y) int64 5 6 5 6 5 6
    Nri   F)rV   r,   r   c                   s   g | ]}t | qS r7   )r   )r4   r   r   r   r   r7   r8   
<listcomp>5  s     zbroadcast.<locals>.<listcomp>)r?   r   r   rG   )r   r   resultr7   r   r8   	broadcast  s    @r   )D
__future__r   r   r   collectionsr   
contextlibr   typingr   r   r   r   r	   r
   r   r   r   r   r   r   ZnumpyrC   Zpandasrw   Zxarray.corer   Zxarray.core.commonr   Zxarray.core.indexesr   r   r   r   r   r   Zxarray.core.utilsr   r   Zxarray.core.variabler   r   r   r   r   r   r!   Zxarray.core.typesr"   r#   r$   r%   r&   rF   rO   ZCoordNamesAndDimsZMatchingIndexKeyZNormalizedIndexesZNormalizedIndexVarsrP   ro   r   r   r_   r   r   r   r   r7   r7   r7   r8   <module>   st   8 7   k SP *#*