ccdproc-2.4.3/ccdproc/__init__.py0000644000000000000000000000214313615410400013603 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst """ The ccdproc package is a collection of code that will be helpful in basic CCD processing. These steps will allow reduction of basic CCD data as either a stand-alone processing or as part of a pipeline. """ # Affiliated packages may add whatever they like to this file, but # should keep this content at the top. # ---------------------------------------------------------------------------- from ._astropy_init import * # noqa # ---------------------------------------------------------------------------- # set up namespace from .core import * # noqa from .ccddata import * # noqa from .combiner import * # noqa from .image_collection import * # noqa from astropy import config as _config class Conf(_config.ConfigNamespace): """Configuration parameters for ccdproc.""" auto_logging = _config.ConfigItem( True, "Whether to automatically log operations to metadata" "If set to False, there is no need to specify add_keyword=False" "when calling processing operations.", ) conf = Conf() del _config ccdproc-2.4.3/ccdproc/_astropy_init.py0000644000000000000000000000054413615410400014732 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import os __all__ = ["__version__", "test"] try: from .version import version as __version__ except ImportError: __version__ = "" # Create the test function for self test from astropy.tests.runner import TestRunner test = TestRunner.make_test_runner_in(os.path.dirname(__file__)) ccdproc-2.4.3/ccdproc/_version.py0000644000000000000000000000063313615410400013672 0ustar00# file generated by setuptools_scm # don't change, don't track in version control TYPE_CHECKING = False if TYPE_CHECKING: from typing import Tuple, Union VERSION_TUPLE = Tuple[Union[int, str], ...] else: VERSION_TUPLE = object version: str __version__: str __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE __version__ = version = '2.4.3' __version_tuple__ = version_tuple = (2, 4, 3) ccdproc-2.4.3/ccdproc/ccddata.py0000644000000000000000000000062713615410400013434 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst """This module implements the base CCDData class.""" from astropy.nddata import CCDData, fits_ccddata_reader, fits_ccddata_writer __all__ = ["CCDData", "fits_ccddata_reader", "fits_ccddata_writer"] # This should be be a tuple to ensure it isn't inadvertently changed # elsewhere. _recognized_fits_file_extensions = ("fit", "fits", "fts") ccdproc-2.4.3/ccdproc/combiner.py0000644000000000000000000011165113615410400013647 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst """This module implements the combiner class.""" import numpy as np from numpy import ma try: import bottleneck as bn except ImportError: HAS_BOTTLENECK = False # pragma: no cover else: HAS_BOTTLENECK = True from astropy import log from astropy.nddata import CCDData, StdDevUncertainty from astropy.stats import sigma_clip from astropy.utils import deprecated_renamed_argument from .core import sigma_func __all__ = ["Combiner", "combine"] def _default_median(): # pragma: no cover if HAS_BOTTLENECK: return bn.nanmedian else: return np.nanmedian def _default_average(): # pragma: no cover if HAS_BOTTLENECK: return bn.nanmean else: return np.nanmean def _default_sum(): # pragma: no cover if HAS_BOTTLENECK: return bn.nansum else: return np.nansum def _default_std(): # pragma: no cover if HAS_BOTTLENECK: return bn.nanstd else: return np.nanstd _default_sum_func = _default_sum() _default_std_dev_func = _default_std() class Combiner: """ A class for combining CCDData objects. The Combiner class is used to combine together `~astropy.nddata.CCDData` objects including the method for combining the data, rejecting outlying data, and weighting used for combining frames. Parameters ----------- ccd_iter : list or generator A list or generator of CCDData objects that will be combined together. dtype : str or `numpy.dtype` or None, optional Allows user to set dtype. See `numpy.array` ``dtype`` parameter description. If ``None`` it uses ``np.float64``. Default is ``None``. Raises ------ TypeError If the ``ccd_iter`` are not `~astropy.nddata.CCDData` objects, have different units, or are different shapes. Examples -------- The following is an example of combining together different `~astropy.nddata.CCDData` objects:: >>> import numpy as np >>> import astropy.units as u >>> from astropy.nddata import CCDData >>> from ccdproc import Combiner >>> ccddata1 = CCDData(np.ones((4, 4)), unit=u.adu) >>> ccddata2 = CCDData(np.zeros((4, 4)), unit=u.adu) >>> ccddata3 = CCDData(np.ones((4, 4)), unit=u.adu) >>> c = Combiner([ccddata1, ccddata2, ccddata3]) >>> ccdall = c.average_combine() >>> ccdall # doctest: +FLOAT_CMP CCDData([[ 0.66666667, 0.66666667, 0.66666667, 0.66666667], [ 0.66666667, 0.66666667, 0.66666667, 0.66666667], [ 0.66666667, 0.66666667, 0.66666667, 0.66666667], [ 0.66666667, 0.66666667, 0.66666667, 0.66666667]]...) """ def __init__(self, ccd_iter, dtype=None): if ccd_iter is None: raise TypeError( "ccd_iter should be a list or a generator of CCDData objects." ) if dtype is None: dtype = np.float64 default_shape = None default_unit = None ccd_list = list(ccd_iter) for ccd in ccd_list: # raise an error if the objects aren't CCDData objects if not isinstance(ccd, CCDData): raise TypeError("ccd_list should only contain CCDData objects.") # raise an error if the shape is different if default_shape is None: default_shape = ccd.shape else: if not (default_shape == ccd.shape): raise TypeError("CCDData objects are not the same size.") # raise an error if the units are different if default_unit is None: default_unit = ccd.unit else: if not (default_unit == ccd.unit): raise TypeError("CCDData objects don't have the same unit.") self.ccd_list = ccd_list self.unit = default_unit self.weights = None self._dtype = dtype # set up the data array new_shape = (len(ccd_list),) + default_shape self.data_arr = ma.masked_all(new_shape, dtype=dtype) # populate self.data_arr for i, ccd in enumerate(ccd_list): self.data_arr[i] = ccd.data if ccd.mask is not None: self.data_arr.mask[i] = ccd.mask else: self.data_arr.mask[i] = ma.zeros(default_shape) # Must be after self.data_arr is defined because it checks the # length of the data array. self.scaling = None @property def dtype(self): return self._dtype @property def weights(self): """ Weights used when combining the `~astropy.nddata.CCDData` objects. Parameters ---------- weight_values : `numpy.ndarray` or None An array with the weight values. The dimensions should match the the dimensions of the data arrays being combined. """ return self._weights @weights.setter def weights(self, value): if value is not None: if isinstance(value, np.ndarray): if value.shape != self.data_arr.data.shape: if value.ndim != 1: raise ValueError( "1D weights expected when shapes of the " "data and weights differ." ) if value.shape[0] != self.data_arr.data.shape[0]: raise ValueError( "Length of weights not compatible with specified axis." ) self._weights = value else: raise TypeError("weights must be a numpy.ndarray.") else: self._weights = None @property def scaling(self): """ Scaling factor used in combining images. Parameters ---------- scale : function or `numpy.ndarray`-like or None, optional Images are multiplied by scaling prior to combining them. Scaling may be either a function, which will be applied to each image to determine the scaling factor, or a list or array whose length is the number of images in the `~ccdproc.Combiner`. """ return self._scaling @scaling.setter def scaling(self, value): if value is None: self._scaling = value else: n_images = self.data_arr.data.shape[0] if callable(value): self._scaling = [value(self.data_arr[i]) for i in range(n_images)] self._scaling = np.array(self._scaling) else: try: len(value) except TypeError as err: raise TypeError( "scaling must be a function or an array " "the same length as the number of images.", ) from err if len(value) != n_images: raise ValueError( "scaling must be a function or an array " "the same length as the number of images." ) self._scaling = np.array(value) # reshape so that broadcasting occurs properly for _ in range(len(self.data_arr.data.shape) - 1): self._scaling = self.scaling[:, np.newaxis] # set up IRAF-like minmax clipping def clip_extrema(self, nlow=0, nhigh=0): """Mask pixels using an IRAF-like minmax clipping algorithm. The algorithm will mask the lowest nlow values and the highest nhigh values before combining the values to make up a single pixel in the resulting image. For example, the image will be a combination of Nimages-nlow-nhigh pixel values instead of the combination of Nimages. Parameters ----------- nlow : int or None, optional If not None, the number of low values to reject from the combination. Default is 0. nhigh : int or None, optional If not None, the number of high values to reject from the combination. Default is 0. Notes ----- Note that this differs slightly from the nominal IRAF imcombine behavior when other masks are in use. For example, if ``nhigh>=1`` and any pixel is already masked for some other reason, then this algorithm will count the masking of that pixel toward the count of nhigh masked pixels. Here is a copy of the relevant IRAF help text [0]_: nlow = 1, nhigh = (minmax) The number of low and high pixels to be rejected by the "minmax" algorithm. These numbers are converted to fractions of the total number of input images so that if no rejections have taken place the specified number of pixels are rejected while if pixels have been rejected by masking, thresholding, or nonoverlap, then the fraction of the remaining pixels, truncated to an integer, is used. References ---------- .. [0] image.imcombine help text. http://stsdas.stsci.edu/cgi-bin/gethelp.cgi?imcombine """ if nlow is None: nlow = 0 if nhigh is None: nhigh = 0 argsorted = np.argsort(self.data_arr.data, axis=0) mg = np.mgrid[ [slice(ndim) for i, ndim in enumerate(self.data_arr.shape) if i > 0] ] for i in range(-1 * nhigh, nlow): # create a tuple with the indices where = tuple([argsorted[i, :, :].ravel()] + [i.ravel() for i in mg]) self.data_arr.mask[where] = True # set up min/max clipping algorithms def minmax_clipping(self, min_clip=None, max_clip=None): """Mask all pixels that are below min_clip or above max_clip. Parameters ----------- min_clip : float or None, optional If not None, all pixels with values below min_clip will be masked. Default is ``None``. max_clip : float or None, optional If not None, all pixels with values above min_clip will be masked. Default is ``None``. """ if min_clip is not None: mask = self.data_arr < min_clip self.data_arr.mask[mask] = True if max_clip is not None: mask = self.data_arr > max_clip self.data_arr.mask[mask] = True # set up sigma clipping algorithms @deprecated_renamed_argument( "use_astropy", None, arg_in_kwargs=True, since="2.4.0", message="The use_astropy argument has been removed because " "astropy sigma clipping is now always used.", ) def sigma_clipping( self, low_thresh=3, high_thresh=3, func="mean", dev_func="std", **kwd ): """ Pixels will be rejected if they have deviations greater than those set by the threshold values. The algorithm will first calculated a baseline value using the function specified in func and deviation based on dev_func and the input data array. Any pixel with a deviation from the baseline value greater than that set by high_thresh or lower than that set by low_thresh will be rejected. Parameters ----------- low_thresh : positive float or None, optional Threshold for rejecting pixels that deviate below the baseline value. If negative value, then will be convert to a positive value. If None, no rejection will be done based on low_thresh. Default is 3. high_thresh : positive float or None, optional Threshold for rejecting pixels that deviate above the baseline value. If None, no rejection will be done based on high_thresh. Default is 3. func : {'median', 'mean'} or callable, optional The statistic or callable function/object used to compute the center value for the clipping. If using a callable function/object and the ``axis`` keyword is used, then it must be able to ignore NaNs (e.g., `numpy.nanmean`) and it must have an ``axis`` keyword to return an array with axis dimension(s) removed. The default is ``'median'``. dev_func : {'std', 'mad_std'} or callable, optional The statistic or callable function/object used to compute the standard deviation about the center value. If using a callable function/object and the ``axis`` keyword is used, then it must be able to ignore NaNs (e.g., `numpy.nanstd`) and it must have an ``axis`` keyword to return an array with axis dimension(s) removed. The default is ``'std'``. kwd Any remaining keyword arguments are passed to astropy's :func:`~astropy.stats.sigma_clip` function. """ # Remove in 3.0 _ = kwd.pop("use_astropy", True) self.data_arr.mask = sigma_clip( self.data_arr.data, sigma_lower=low_thresh, sigma_upper=high_thresh, axis=kwd.get("axis", 0), copy=kwd.get("copy", False), maxiters=kwd.get("maxiters", 1), cenfunc=func, stdfunc=dev_func, masked=True, **kwd, ).mask def _get_scaled_data(self, scale_arg): if scale_arg is not None: return self.data_arr * scale_arg if self.scaling is not None: return self.data_arr * self.scaling return self.data_arr def _get_nan_substituted_data(self, data): # Get the data as an unmasked array with masked values filled as NaN if self.data_arr.mask.any(): data = np.ma.filled(data, fill_value=np.nan) else: data = data.data return data def _combination_setup(self, user_func, default_func, scale_to): """ Handle the common pieces of image combination data/mask setup. """ data = self._get_scaled_data(scale_to) # Play it safe for now and only do the nan thing if the user is using # the default combination function. if user_func is None: combo_func = default_func # Subtitute NaN for masked entries data = self._get_nan_substituted_data(data) masked_values = np.isnan(data).sum(axis=0) else: masked_values = self.data_arr.mask.sum(axis=0) combo_func = user_func return data, masked_values, combo_func # set up the combining algorithms def median_combine( self, median_func=None, scale_to=None, uncertainty_func=sigma_func ): """ Median combine a set of arrays. A `~astropy.nddata.CCDData` object is returned with the data property set to the median of the arrays. If the data was masked or any data have been rejected, those pixels will not be included in the median. A mask will be returned, and if a pixel has been rejected in all images, it will be masked. The uncertainty of the combined image is set by 1.4826 times the median absolute deviation of all input images. Parameters ---------- median_func : function, optional Function that calculates median of a `numpy.ma.MaskedArray`. Default is `numpy.ma.median`. scale_to : float or None, optional Scaling factor used in the average combined image. If given, it overrides `scaling`. Defaults to None. uncertainty_func : function, optional Function to calculate uncertainty. Defaults is `~ccdproc.sigma_func`. Returns ------- combined_image: `~astropy.nddata.CCDData` CCDData object based on the combined input of CCDData objects. Warnings -------- The uncertainty currently calculated using the median absolute deviation does not account for rejected pixels. """ data, masked_values, median_func = self._combination_setup( median_func, _default_median(), scale_to ) medianed = median_func(data, axis=0) # set the mask mask = masked_values == len(self.data_arr) # set the uncertainty # This still uses numpy for the median because the astropy # code requires that the median function take the argument # overwrite_input and bottleneck doesn't allow that argument. # This is ugly, but setting ignore_nan to True should make sure # that either nans or masks are handled properly. if uncertainty_func is sigma_func: uncertainty = uncertainty_func(data, axis=0, ignore_nan=True) else: uncertainty = uncertainty_func(data, axis=0) # Divide uncertainty by the number of pixel (#309) uncertainty /= np.sqrt(len(self.data_arr) - masked_values) # Convert uncertainty to plain numpy array (#351) # There is no need to care about potential masks because the # uncertainty was calculated based on the data so potential masked # elements are also masked in the data. No need to keep two identical # masks. uncertainty = np.asarray(uncertainty) # create the combined image with a dtype matching the combiner combined_image = CCDData( np.asarray(medianed, dtype=self.dtype), mask=mask, unit=self.unit, uncertainty=StdDevUncertainty(uncertainty), ) # update the meta data combined_image.meta["NCOMBINE"] = len(self.data_arr) # return the combined image return combined_image def _weighted_sum(self, data, sum_func): """ Perform weighted sum, used by both ``sum_combine`` and in some cases by ``average_combine``. """ if self.weights.shape != data.shape: # Add extra axes to the weights for broadcasting weights = np.reshape(self.weights, [len(self.weights), 1, 1]) else: weights = self.weights # Turns out bn.nansum has an implementation that is not # precise enough for float32 sums. Doing this should # ensure the sums are carried out as float64 weights = weights.astype("float64") weighted_sum = sum_func(data * weights, axis=0) return weighted_sum, weights def average_combine( self, scale_func=None, scale_to=None, uncertainty_func=_default_std_dev_func, sum_func=_default_sum_func, ): """ Average combine together a set of arrays. A `~astropy.nddata.CCDData` object is returned with the data property set to the average of the arrays. If the data was masked or any data have been rejected, those pixels will not be included in the average. A mask will be returned, and if a pixel has been rejected in all images, it will be masked. The uncertainty of the combined image is set by the standard deviation of the input images. Parameters ---------- scale_func : function, optional Function to calculate the average. Defaults to `numpy.nanmean`. scale_to : float or None, optional Scaling factor used in the average combined image. If given, it overrides `scaling`. Defaults to ``None``. uncertainty_func : function, optional Function to calculate uncertainty. Defaults to `numpy.ma.std`. sum_func : function, optional Function used to calculate sums, including the one done to find the weighted average. Defaults to `numpy.nansum`. Returns ------- combined_image: `~astropy.nddata.CCDData` CCDData object based on the combined input of CCDData objects. """ data, masked_values, scale_func = self._combination_setup( scale_func, _default_average(), scale_to ) # Do NOT modify data after this -- we need it to be intact when we # we get to the uncertainty calculation. if self.weights is not None: weighted_sum, weights = self._weighted_sum(data, sum_func) mean = weighted_sum / sum_func(weights, axis=0) else: mean = scale_func(data, axis=0) # calculate the mask mask = masked_values == len(self.data_arr) # set up the deviation uncertainty = uncertainty_func(data, axis=0) # Divide uncertainty by the number of pixel (#309) uncertainty /= np.sqrt(len(data) - masked_values) # Convert uncertainty to plain numpy array (#351) uncertainty = np.asarray(uncertainty) # create the combined image with a dtype that matches the combiner combined_image = CCDData( np.asarray(mean, dtype=self.dtype), mask=mask, unit=self.unit, uncertainty=StdDevUncertainty(uncertainty), ) # update the meta data combined_image.meta["NCOMBINE"] = len(data) # return the combined image return combined_image def sum_combine( self, sum_func=None, scale_to=None, uncertainty_func=_default_std_dev_func ): """ Sum combine together a set of arrays. A `~astropy.nddata.CCDData` object is returned with the data property set to the sum of the arrays. If the data was masked or any data have been rejected, those pixels will not be included in the sum. A mask will be returned, and if a pixel has been rejected in all images, it will be masked. The uncertainty of the combined image is set by the multiplication of summation of standard deviation of the input by square root of number of images. Because sum_combine returns 'pure sum' with masked pixels ignored, if re-scaled sum is needed, average_combine have to be used with multiplication by number of images combined. Parameters ---------- sum_func : function, optional Function to calculate the sum. Defaults to `numpy.nansum` or ``bottleneck.nansum``. scale_to : float or None, optional Scaling factor used in the sum combined image. If given, it overrides `scaling`. Defaults to ``None``. uncertainty_func : function, optional Function to calculate uncertainty. Defaults to `numpy.ma.std`. Returns ------- combined_image: `~astropy.nddata.CCDData` CCDData object based on the combined input of CCDData objects. """ data, masked_values, sum_func = self._combination_setup( sum_func, _default_sum(), scale_to ) if self.weights is not None: summed, weights = self._weighted_sum(data, sum_func) else: summed = sum_func(data, axis=0) # set up the mask mask = masked_values == len(self.data_arr) # set up the deviation uncertainty = uncertainty_func(data, axis=0) # Divide uncertainty by the number of pixel (#309) uncertainty /= np.sqrt(len(data) - masked_values) # Convert uncertainty to plain numpy array (#351) uncertainty = np.asarray(uncertainty) # Multiply uncertainty by square root of the number of images uncertainty *= len(data) - masked_values # create the combined image with a dtype that matches the combiner combined_image = CCDData( np.asarray(summed, dtype=self.dtype), mask=mask, unit=self.unit, uncertainty=StdDevUncertainty(uncertainty), ) # update the meta data combined_image.meta["NCOMBINE"] = len(self.data_arr) # return the combined image return combined_image def _calculate_step_sizes(x_size, y_size, num_chunks): """ Calculate the strides in x and y to achieve at least the ``num_chunks`` pieces. Parameters ---------- """ # First we try to split only along fast x axis xstep = max(1, int(x_size / num_chunks)) # More chunks are needed only if xstep gives us fewer chunks than # requested. x_chunks = int(x_size / xstep) if x_chunks >= num_chunks: ystep = y_size else: # The x and y loops are nested, so the number of chunks # is multiplicative, not additive. Calculate the number # of y chunks we need to get at num_chunks. y_chunks = int(num_chunks / x_chunks) + 1 ystep = max(1, int(y_size / y_chunks)) return xstep, ystep def _calculate_size_of_image(ccd, combine_uncertainty_function): # If uncertainty_func is given for combine this will create an uncertainty # even if the originals did not have one. In that case we need to create # an empty placeholder. if ccd.uncertainty is None and combine_uncertainty_function is not None: ccd.uncertainty = StdDevUncertainty(np.zeros(ccd.data.shape)) size_of_an_img = ccd.data.nbytes try: size_of_an_img += ccd.uncertainty.array.nbytes # In case uncertainty is None it has no "array" and in case the "array" is # not a numpy array: except AttributeError: pass # Mask is enforced to be a numpy.array across astropy versions if ccd.mask is not None: size_of_an_img += ccd.mask.nbytes # flags is not necessarily a numpy array so do not fail with an # AttributeError in case something was set! # TODO: Flags are not taken into account in Combiner. This number is added # nevertheless for future compatibility. try: size_of_an_img += ccd.flags.nbytes except AttributeError: pass return size_of_an_img def combine( img_list, output_file=None, method="average", weights=None, scale=None, mem_limit=16e9, clip_extrema=False, nlow=1, nhigh=1, minmax_clip=False, minmax_clip_min=None, minmax_clip_max=None, sigma_clip=False, sigma_clip_low_thresh=3, sigma_clip_high_thresh=3, sigma_clip_func=ma.mean, sigma_clip_dev_func=ma.std, dtype=None, combine_uncertainty_function=None, overwrite_output=False, **ccdkwargs, ): """ Convenience function for combining multiple images. Parameters ----------- img_list : `numpy.ndarray`, list or str A list of fits filenames or `~astropy.nddata.CCDData` objects that will be combined together. Or a string of fits filenames separated by comma ",". output_file : str or None, optional Optional output fits file-name to which the final output can be directly written. Default is ``None``. method : str, optional Method to combine images: - ``'average'`` : To combine by calculating the average. - ``'median'`` : To combine by calculating the median. - ``'sum'`` : To combine by calculating the sum. Default is ``'average'``. weights : `numpy.ndarray` or None, optional Weights to be used when combining images. An array with the weight values. The dimensions should match the the dimensions of the data arrays being combined. Default is ``None``. scale : function or `numpy.ndarray`-like or None, optional Scaling factor to be used when combining images. Images are multiplied by scaling prior to combining them. Scaling may be either a function, which will be applied to each image to determine the scaling factor, or a list or array whose length is the number of images in the `Combiner`. Default is ``None``. mem_limit : float, optional Maximum memory which should be used while combining (in bytes). Default is ``16e9``. clip_extrema : bool, optional Set to True if you want to mask pixels using an IRAF-like minmax clipping algorithm. The algorithm will mask the lowest nlow values and the highest nhigh values before combining the values to make up a single pixel in the resulting image. For example, the image will be a combination of Nimages-low-nhigh pixel values instead of the combination of Nimages. Parameters below are valid only when clip_extrema is set to True, see :meth:`Combiner.clip_extrema` for the parameter description: - ``nlow`` : int or None, optional - ``nhigh`` : int or None, optional minmax_clip : bool, optional Set to True if you want to mask all pixels that are below minmax_clip_min or above minmax_clip_max before combining. Default is ``False``. Parameters below are valid only when minmax_clip is set to True, see :meth:`Combiner.minmax_clipping` for the parameter description: - ``minmax_clip_min`` : float or None, optional - ``minmax_clip_max`` : float or None, optional sigma_clip : bool, optional Set to True if you want to reject pixels which have deviations greater than those set by the threshold values. The algorithm will first calculated a baseline value using the function specified in func and deviation based on sigma_clip_dev_func and the input data array. Any pixel with a deviation from the baseline value greater than that set by sigma_clip_high_thresh or lower than that set by sigma_clip_low_thresh will be rejected. Default is ``False``. Parameters below are valid only when sigma_clip is set to True. See :meth:`Combiner.sigma_clipping` for the parameter description. - ``sigma_clip_low_thresh`` : positive float or None, optional - ``sigma_clip_high_thresh`` : positive float or None, optional - ``sigma_clip_func`` : function, optional - ``sigma_clip_dev_func`` : function, optional dtype : str or `numpy.dtype` or None, optional The intermediate and resulting ``dtype`` for the combined CCDs. See `ccdproc.Combiner`. If ``None`` this is set to ``float64``. Default is ``None``. combine_uncertainty_function : callable, None, optional If ``None`` use the default uncertainty func when using average, median or sum combine, otherwise use the function provided. Default is ``None``. overwrite_output : bool, optional If ``output_file`` is specified, this is passed to the `astropy.nddata.fits_ccddata_writer` under the keyword ``overwrite``; has no effect otherwise. Default is ``False``. ccdkwargs : Other keyword arguments for `astropy.nddata.fits_ccddata_reader`. Returns ------- combined_image : `~astropy.nddata.CCDData` CCDData object based on the combined input of CCDData objects. """ if not isinstance(img_list, list): # If not a list, check whether it is a numpy ndarray or string of # filenames separated by comma if isinstance(img_list, np.ndarray): img_list = img_list.tolist() elif isinstance(img_list, str) and ("," in img_list): img_list = img_list.split(",") else: try: # Maybe the input can be made into a list, so try that img_list = list(img_list) except TypeError as err: raise ValueError( "unrecognised input for list of images to combine." ) from err # Select Combine function to call in Combiner if method == "average": combine_function = "average_combine" elif method == "median": combine_function = "median_combine" elif method == "sum": combine_function = "sum_combine" else: raise ValueError(f"unrecognised combine method : {method}.") # First we create a CCDObject from first image for storing output if isinstance(img_list[0], CCDData): ccd = img_list[0].copy() else: # User has provided fits filenames to read from ccd = CCDData.read(img_list[0], **ccdkwargs) if dtype is None: dtype = np.float64 # Convert the master image to the appropriate dtype so when overwriting it # later the data is not downcast and the memory consumption calculation # uses the internally used dtype instead of the original dtype. #391 if ccd.data.dtype != dtype: ccd.data = ccd.data.astype(dtype) # If the template image doesn't have an uncertainty, add one, because the # result always has an uncertainty. if ccd.uncertainty is None: ccd.uncertainty = StdDevUncertainty(np.zeros_like(ccd.data)) # If the template doesn't have a mask, add one, because the result may have # a mask if ccd.mask is None: ccd.mask = np.zeros_like(ccd.data, dtype=bool) size_of_an_img = _calculate_size_of_image(ccd, combine_uncertainty_function) no_of_img = len(img_list) # Set a memory use factor based on profiling if method == "median": memory_factor = 3 else: memory_factor = 2 memory_factor *= 1.3 # determine the number of chunks to split the images into no_chunks = int((memory_factor * size_of_an_img * no_of_img) / mem_limit) + 1 if no_chunks > 1: log.info( f"splitting each image into {no_chunks} chunks to limit memory usage " f"to {mem_limit} bytes." ) xs, ys = ccd.data.shape # Calculate strides for loop xstep, ystep = _calculate_step_sizes(xs, ys, no_chunks) # Dictionary of Combiner properties to set and methods to call before # combining to_set_in_combiner = {} to_call_in_combiner = {} # Define all the Combiner properties one wants to apply before combining # images if weights is not None: to_set_in_combiner["weights"] = weights if scale is not None: # If the scale is a function, then scaling function need to be applied # on full image to obtain scaling factor and create an array instead. if callable(scale): scalevalues = [] for image in img_list: if isinstance(image, CCDData): imgccd = image else: imgccd = CCDData.read(image, **ccdkwargs) scalevalues.append(scale(imgccd.data)) to_set_in_combiner["scaling"] = np.array(scalevalues) else: to_set_in_combiner["scaling"] = scale if clip_extrema: to_call_in_combiner["clip_extrema"] = {"nlow": nlow, "nhigh": nhigh} if minmax_clip: to_call_in_combiner["minmax_clipping"] = { "min_clip": minmax_clip_min, "max_clip": minmax_clip_max, } if sigma_clip: to_call_in_combiner["sigma_clipping"] = { "low_thresh": sigma_clip_low_thresh, "high_thresh": sigma_clip_high_thresh, "func": sigma_clip_func, "dev_func": sigma_clip_dev_func, } # Finally Run the input method on all the subsections of the image # and write final stitched image to ccd for x in range(0, xs, xstep): for y in range(0, ys, ystep): xend, yend = min(xs, x + xstep), min(ys, y + ystep) ccd_list = [] for image in img_list: if isinstance(image, CCDData): imgccd = image else: imgccd = CCDData.read(image, **ccdkwargs) # Trim image and copy # The copy is *essential* to avoid having a bunch # of unused file references around if the files # are memory-mapped. See this PR for details # https://github.com/astropy/ccdproc/pull/630 ccd_list.append(imgccd[x:xend, y:yend].copy()) # Create Combiner for tile tile_combiner = Combiner(ccd_list, dtype=dtype) # Set all properties and call all methods for to_set in to_set_in_combiner: setattr(tile_combiner, to_set, to_set_in_combiner[to_set]) for to_call in to_call_in_combiner: getattr(tile_combiner, to_call)(**to_call_in_combiner[to_call]) # Finally call the combine algorithm combine_kwds = {} if combine_uncertainty_function is not None: combine_kwds["uncertainty_func"] = combine_uncertainty_function comb_tile = getattr(tile_combiner, combine_function)(**combine_kwds) # add it back into the master image ccd.data[x:xend, y:yend] = comb_tile.data if ccd.mask is not None: ccd.mask[x:xend, y:yend] = comb_tile.mask if ccd.uncertainty is not None: ccd.uncertainty.array[x:xend, y:yend] = comb_tile.uncertainty.array # Free up memory to try to stay under user's limit del comb_tile del tile_combiner del ccd_list # Write fits file if filename was provided if output_file is not None: ccd.write(output_file, overwrite=overwrite_output) return ccd ccdproc-2.4.3/ccdproc/conftest.py0000644000000000000000000000227713615410400013701 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst # this contains imports plugins that configure py.test for astropy tests. # by importing them here in conftest.py they are discoverable by py.test # no matter how it is invoked within the source tree. import os try: # When the pytest_astropy_header package is installed from pytest_astropy_header.display import PYTEST_HEADER_MODULES, TESTED_VERSIONS def pytest_configure(config): config.option.astropy_header = True except ImportError: PYTEST_HEADER_MODULES = {} TESTED_VERSIONS = {} from .tests.pytest_fixtures import triage_setup # noqa: F401 this is used in tests # This is to figure out ccdproc version, rather than using Astropy's try: from .version import version except ImportError: version = "dev" packagename = os.path.basename(os.path.dirname(__file__)) TESTED_VERSIONS[packagename] = version # Add astropy to test header information and remove unused packages. try: PYTEST_HEADER_MODULES["Astropy"] = "astropy" PYTEST_HEADER_MODULES["astroscrappy"] = "astroscrappy" PYTEST_HEADER_MODULES["reproject"] = "reproject" del PYTEST_HEADER_MODULES["h5py"] except KeyError: pass ccdproc-2.4.3/ccdproc/core.py0000644000000000000000000022236113615410400013002 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst """This module implements the base CCDPROC functions""" import logging import math import numbers import warnings import numpy as np from astropy import nddata, stats from astropy import units as u from astropy.modeling import fitting from astropy.nddata import CCDData, StdDevUncertainty from astropy.units.quantity import Quantity from astropy.utils import deprecated, deprecated_renamed_argument from astropy.wcs.utils import proj_plane_pixel_area from packaging import version as pkgversion from scipy import ndimage from .extern.bitfield import bitfield_to_boolean_mask as _bitfield_to_boolean_mask from .log_meta import log_to_metadata from .utils.slices import slice_from_string logger = logging.getLogger(__name__) __all__ = [ "background_deviation_box", "background_deviation_filter", "ccd_process", "cosmicray_median", "cosmicray_lacosmic", "create_deviation", "flat_correct", "gain_correct", "rebin", "sigma_func", "subtract_bias", "subtract_dark", "subtract_overscan", "transform_image", "trim_image", "wcs_project", "Keyword", "median_filter", "ccdmask", "bitfield_to_boolean_mask", ] # The dictionary below is used to translate actual function names to names # that are FITS compliant, i.e. 8 characters or less. _short_names = { "background_deviation_box": "bakdevbx", "background_deviation_filter": "bakdfilt", "ccd_process": "ccdproc", "cosmicray_median": "crmedian", "create_deviation": "creatvar", "flat_correct": "flatcor", "gain_correct": "gaincor", "subtract_bias": "subbias", "subtract_dark": "subdark", "subtract_overscan": "suboscan", "trim_image": "trimim", "transform_image": "tranim", "wcs_project": "wcsproj", } @log_to_metadata def ccd_process( ccd, oscan=None, trim=None, error=False, master_bias=None, dark_frame=None, master_flat=None, bad_pixel_mask=None, gain=None, readnoise=None, oscan_median=True, oscan_model=None, min_value=None, dark_exposure=None, data_exposure=None, exposure_key=None, exposure_unit=None, dark_scale=False, gain_corrected=True, ): """Perform basic processing on ccd data. The following steps can be included: * overscan correction (:func:`subtract_overscan`) * trimming of the image (:func:`trim_image`) * create deviation frame (:func:`create_deviation`) * gain correction (:func:`gain_correct`) * add a mask to the data * subtraction of master bias (:func:`subtract_bias`) * subtraction of a dark frame (:func:`subtract_dark`) * correction of flat field (:func:`flat_correct`) The task returns a processed `~astropy.nddata.CCDData` object. Parameters ---------- ccd : `~astropy.nddata.CCDData` Frame to be reduced. oscan : `~astropy.nddata.CCDData`, str or None, optional For no overscan correction, set to None. Otherwise provide a region of ccd from which the overscan is extracted, using the FITS conventions for index order and index start, or a slice from ccd that contains the overscan. Default is ``None``. trim : str or None, optional For no trim correction, set to None. Otherwise provide a region of ccd from which the image should be trimmed, using the FITS conventions for index order and index start. Default is ``None``. error : bool, optional If True, create an uncertainty array for ccd. Default is ``False``. master_bias : `~astropy.nddata.CCDData` or None, optional A master bias frame to be subtracted from ccd. The unit of the master bias frame should match the unit of the image **after gain correction** if ``gain_corrected`` is True. Default is ``None``. dark_frame : `~astropy.nddata.CCDData` or None, optional A dark frame to be subtracted from the ccd. The unit of the master dark frame should match the unit of the image **after gain correction** if ``gain_corrected`` is True. Default is ``None``. master_flat : `~astropy.nddata.CCDData` or None, optional A master flat frame to be divided into ccd. The unit of the master flat frame should match the unit of the image **after gain correction** if ``gain_corrected`` is True. Default is ``None``. bad_pixel_mask : `numpy.ndarray` or None, optional A bad pixel mask for the data. The bad pixel mask should be in given such that bad pixels have a value of 1 and good pixels a value of 0. Default is ``None``. gain : `~astropy.units.Quantity` or None, optional Gain value to multiple the image by to convert to electrons. Default is ``None``. readnoise : `~astropy.units.Quantity` or None, optional Read noise for the observations. The read noise should be in electrons. Default is ``None``. oscan_median : bool, optional If true, takes the median of each line. Otherwise, uses the mean. Default is ``True``. oscan_model : `~astropy.modeling.Model` or None, optional Model to fit to the data. If None, returns the values calculated by the median or the mean. Default is ``None``. min_value : float or None, optional Minimum value for flat field. The value can either be None and no minimum value is applied to the flat or specified by a float which will replace all values in the flat by the min_value. Default is ``None``. dark_exposure : `~astropy.units.Quantity` or None, optional Exposure time of the dark image; if specified, must also provided ``data_exposure``. Default is ``None``. data_exposure : `~astropy.units.Quantity` or None, optional Exposure time of the science image; if specified, must also provided ``dark_exposure``. Default is ``None``. exposure_key : `~ccdproc.Keyword`, str or None, optional Name of key in image metadata that contains exposure time. Default is ``None``. exposure_unit : `~astropy.units.Unit` or None, optional Unit of the exposure time if the value in the meta data does not include a unit. Default is ``None``. dark_scale : bool, optional If True, scale the dark frame by the exposure times. Default is ``False``. gain_corrected : bool, optional If True, the ``master_bias``, ``master_flat``, and ``dark_frame`` have already been gain corrected. Default is ``True``. Returns ------- occd : `~astropy.nddata.CCDData` Reduded ccd. Examples -------- 1. To overscan, trim and gain correct a data set:: >>> import numpy as np >>> from astropy import units as u >>> from astropy.nddata import CCDData >>> from ccdproc import ccd_process >>> ccd = CCDData(np.ones([100, 100]), unit=u.adu) >>> nccd = ccd_process(ccd, oscan='[1:10,1:100]', ... trim='[10:100, 1:100]', error=False, ... gain=2.0*u.electron/u.adu) """ # make a copy of the object nccd = ccd.copy() # apply the overscan correction if isinstance(oscan, CCDData): nccd = subtract_overscan( nccd, overscan=oscan, median=oscan_median, model=oscan_model ) elif isinstance(oscan, str): nccd = subtract_overscan( nccd, fits_section=oscan, median=oscan_median, model=oscan_model ) elif oscan is None: pass else: raise TypeError("oscan is not None, a string, or CCDData object.") # apply the trim correction if isinstance(trim, str): nccd = trim_image(nccd, fits_section=trim) elif trim is None: pass else: raise TypeError("trim is not None or a string.") # create the error frame if error and gain is not None and readnoise is not None: nccd = create_deviation(nccd, gain=gain, readnoise=readnoise) elif error and (gain is None or readnoise is None): raise ValueError("gain and readnoise must be specified to create error frame.") # apply the bad pixel mask if isinstance(bad_pixel_mask, np.ndarray): nccd.mask = bad_pixel_mask elif bad_pixel_mask is None: pass else: raise TypeError("bad_pixel_mask is not None or numpy.ndarray.") # apply the gain correction if not (gain is None or isinstance(gain, Quantity)): raise TypeError("gain is not None or astropy.units.Quantity.") if gain is not None and gain_corrected: nccd = gain_correct(nccd, gain) # subtracting the master bias if isinstance(master_bias, CCDData): nccd = subtract_bias(nccd, master_bias) elif master_bias is None: pass else: raise TypeError("master_bias is not None or a CCDData object.") # subtract the dark frame if isinstance(dark_frame, CCDData): nccd = subtract_dark( nccd, dark_frame, dark_exposure=dark_exposure, data_exposure=data_exposure, exposure_time=exposure_key, exposure_unit=exposure_unit, scale=dark_scale, ) elif dark_frame is None: pass else: raise TypeError("dark_frame is not None or a CCDData object.") # test dividing the master flat if isinstance(master_flat, CCDData): nccd = flat_correct(nccd, master_flat, min_value=min_value) elif master_flat is None: pass else: raise TypeError("master_flat is not None or a CCDData object.") # apply the gain correction only at the end if gain_corrected is False if gain is not None and not gain_corrected: nccd = gain_correct(nccd, gain) return nccd @log_to_metadata def create_deviation(ccd_data, gain=None, readnoise=None, disregard_nan=False): """ Create a uncertainty frame. The function will update the uncertainty plane which gives the standard deviation for the data. Gain is used in this function only to scale the data in constructing the deviation; the data is not scaled. Parameters ---------- ccd_data : `~astropy.nddata.CCDData` Data whose deviation will be calculated. gain : `~astropy.units.Quantity` or None, optional Gain of the CCD; necessary only if ``ccd_data`` and ``readnoise`` are not in the same units. In that case, the units of ``gain`` should be those that convert ``ccd_data.data`` to the same units as ``readnoise``. Default is ``None``. readnoise : `~astropy.units.Quantity` or None, optional Read noise per pixel. Default is ``None``. disregard_nan: boolean If ``True``, any value of nan in the output array will be replaced by the readnoise. {log} Raises ------ UnitsError Raised if ``readnoise`` units are not equal to product of ``gain`` and ``ccd_data`` units. Returns ------- ccd : `~astropy.nddata.CCDData` CCDData object with uncertainty created; uncertainty is in the same units as the data in the parameter ``ccd_data``. """ if gain is not None and not isinstance(gain, Quantity): raise TypeError("gain must be a astropy.units.Quantity.") if readnoise is None: raise ValueError("must provide a readnoise.") if not isinstance(readnoise, Quantity): raise TypeError("readnoise must be a astropy.units.Quantity.") if gain is None: gain = 1.0 * u.dimensionless_unscaled if gain.unit * ccd_data.unit != readnoise.unit: raise u.UnitsError("units of data, gain and readnoise do not match.") # Need to convert Quantity to plain number because NDData data is not # a Quantity. All unit checking should happen prior to this point. gain_value = float(gain / gain.unit) readnoise_value = float(readnoise / readnoise.unit) # remove values that might be negative or treat as nan data = gain_value * ccd_data.data mask = data < 0 if disregard_nan: data[mask] = 0 else: data[mask] = np.nan logging.warning("Negative values in array will be replaced with nan") # calculate the deviation var = (data + readnoise_value**2) ** 0.5 # ensure uncertainty and image data have same unit ccd = ccd_data.copy() var /= gain_value ccd.uncertainty = StdDevUncertainty(var) return ccd @log_to_metadata def subtract_overscan( ccd, overscan=None, overscan_axis=1, fits_section=None, median=False, model=None ): """ Subtract the overscan region from an image. Parameters ---------- ccd : `~astropy.nddata.CCDData` Data to have overscan frame corrected. overscan : `~astropy.nddata.CCDData` or None, optional Slice from ``ccd`` that contains the overscan. Must provide either this argument or ``fits_section``, but not both. Default is ``None``. overscan_axis : 0, 1 or None, optional Axis along which overscan should combined with mean or median. Axis numbering follows the *python* convention for ordering, so 0 is the first axis and 1 is the second axis. If overscan_axis is explicitly set to None, the axis is set to the shortest dimension of the overscan section (or 1 in case of a square overscan). Default is ``1``. fits_section : str or None, optional Region of ``ccd`` from which the overscan is extracted, using the FITS conventions for index order and index start. See Notes and Examples below. Must provide either this argument or ``overscan``, but not both. Default is ``None``. median : bool, optional If true, takes the median of each line. Otherwise, uses the mean. Default is ``False``. model : `~astropy.modeling.Model` or None, optional Model to fit to the data. If None, returns the values calculated by the median or the mean. Default is ``None``. {log} Raises ------ TypeError A TypeError is raised if either ``ccd`` or ``overscan`` are not the correct objects. Returns ------- ccd : `~astropy.nddata.CCDData` CCDData object with overscan subtracted. Notes ----- The format of the ``fits_section`` string follow the rules for slices that are consistent with the FITS standard (v3) and IRAF usage of keywords like TRIMSEC and BIASSEC. Its indexes are one-based, instead of the python-standard zero-based, and the first index is the one that increases most rapidly as you move through the array in memory order, opposite the python ordering. The 'fits_section' argument is provided as a convenience for those who are processing files that contain TRIMSEC and BIASSEC. The preferred, more pythonic, way of specifying the overscan is to do it by indexing the data array directly with the ``overscan`` argument. Examples -------- Creating a 100x100 array containing ones just for demonstration purposes:: >>> import numpy as np >>> from astropy import units as u >>> arr1 = CCDData(np.ones([100, 100]), unit=u.adu) The statement below uses all rows of columns 90 through 99 as the overscan:: >>> no_scan = subtract_overscan(arr1, overscan=arr1[:, 90:100]) >>> assert (no_scan.data == 0).all() This statement does the same as the above, but with a FITS-style section:: >>> no_scan = subtract_overscan(arr1, fits_section='[91:100, :]') >>> assert (no_scan.data == 0).all() Spaces are stripped out of the ``fits_section`` string. """ if not (isinstance(ccd, CCDData) or isinstance(ccd, np.ndarray)): raise TypeError("ccddata is not a CCDData or ndarray object.") if (overscan is not None and fits_section is not None) or ( overscan is None and fits_section is None ): raise TypeError("specify either overscan or fits_section, but not " "both.") if (overscan is not None) and (not isinstance(overscan, CCDData)): raise TypeError("overscan is not a CCDData object.") if fits_section is not None and not isinstance(fits_section, str): raise TypeError("overscan is not a string.") if fits_section is not None: overscan = ccd[slice_from_string(fits_section, fits_convention=True)] if overscan_axis is None: overscan_axis = 0 if overscan.shape[1] > overscan.shape[0] else 1 if median: oscan = np.median(overscan.data, axis=overscan_axis) else: oscan = np.mean(overscan.data, axis=overscan_axis) if model is not None: of = fitting.LinearLSQFitter() yarr = np.arange(len(oscan)) oscan = of(model, yarr, oscan) oscan = oscan(yarr) if overscan_axis == 1: oscan = np.reshape(oscan, (oscan.size, 1)) else: oscan = np.reshape(oscan, (1, oscan.size)) else: if overscan_axis == 1: oscan = np.reshape(oscan, oscan.shape + (1,)) else: oscan = np.reshape(oscan, (1,) + oscan.shape) subtracted = ccd.copy() # subtract the overscan subtracted.data = ccd.data - oscan return subtracted @log_to_metadata def trim_image(ccd, fits_section=None): """ Trim the image to the dimensions indicated. Parameters ---------- ccd : `~astropy.nddata.CCDData` CCD image to be trimmed, sliced if desired. fits_section : str or None, optional Region of ``ccd`` from which the overscan is extracted; see `~ccdproc.subtract_overscan` for details. Default is ``None``. {log} Returns ------- trimmed_ccd : `~astropy.nddata.CCDData` Trimmed image. Examples -------- Given an array that is 100x100, >>> import numpy as np >>> from astropy import units as u >>> arr1 = CCDData(np.ones([100, 100]), unit=u.adu) the syntax for trimming this to keep all of the first index but only the first 90 rows of the second index is >>> trimmed = trim_image(arr1[:, :90]) >>> trimmed.shape (100, 90) >>> trimmed.data[0, 0] = 2 >>> float(arr1.data[0, 0]) 1.0 This both trims *and makes a copy* of the image. Indexing the image directly does *not* do the same thing, quite: >>> not_really_trimmed = arr1[:, :90] >>> not_really_trimmed.data[0, 0] = 2 >>> float(arr1.data[0, 0]) 2.0 In this case, ``not_really_trimmed`` is a view of the underlying array ``arr1``, not a copy. """ if fits_section is not None and not isinstance(fits_section, str): raise TypeError("fits_section must be a string.") trimmed = ccd.copy() if fits_section: python_slice = slice_from_string(fits_section, fits_convention=True) trimmed = trimmed[python_slice] return trimmed @log_to_metadata def subtract_bias(ccd, master): """ Subtract master bias from image. Parameters ---------- ccd : `~astropy.nddata.CCDData` Image from which bias will be subtracted. master : `~astropy.nddata.CCDData` Master image to be subtracted from ``ccd``. {log} Returns ------- result : `~astropy.nddata.CCDData` CCDData object with bias subtracted. """ try: result = ccd.subtract(master) except ValueError as err: if "operand units" in str(err): raise u.UnitsError( f"Unit '{ccd.unit}' of the uncalibrated image does not " f"match unit '{master.unit}' of the calibration " "image" ) from err else: raise err result.meta = ccd.meta.copy() return result @log_to_metadata def subtract_dark( ccd, master, dark_exposure=None, data_exposure=None, exposure_time=None, exposure_unit=None, scale=False, ): """ Subtract dark current from an image. Parameters ---------- ccd : `~astropy.nddata.CCDData` Image from which dark will be subtracted. master : `~astropy.nddata.CCDData` Dark image. dark_exposure : `~astropy.units.Quantity` or None, optional Exposure time of the dark image; if specified, must also provided ``data_exposure``. Default is ``None``. data_exposure : `~astropy.units.Quantity` or None, optional Exposure time of the science image; if specified, must also provided ``dark_exposure``. Default is ``None``. exposure_time : str or `~ccdproc.Keyword` or None, optional Name of key in image metadata that contains exposure time. Default is ``None``. exposure_unit : `~astropy.units.Unit` or None, optional Unit of the exposure time if the value in the meta data does not include a unit. Default is ``None``. scale: bool, optional If True, scale the dark frame by the exposure times. Default is ``False``. {log} Returns ------- result : `~astropy.nddata.CCDData` Dark-subtracted image. """ if ccd.shape != master.shape: err_str = ( f"operands could not be subtracted with " f"shapes {ccd.shape} {master.shape}" ) raise ValueError(err_str) if not (isinstance(ccd, CCDData) and isinstance(master, CCDData)): raise TypeError("ccd and master must both be CCDData objects.") if ( data_exposure is not None and dark_exposure is not None and exposure_time is not None ): raise TypeError( "specify either exposure_time or " "(dark_exposure and data_exposure), not both." ) if data_exposure is None and dark_exposure is None: if exposure_time is None: raise TypeError( "must specify either exposure_time or both " "dark_exposure and data_exposure." ) if isinstance(exposure_time, Keyword): data_exposure = exposure_time.value_from(ccd.header) dark_exposure = exposure_time.value_from(master.header) else: data_exposure = ccd.header[exposure_time] dark_exposure = master.header[exposure_time] if not ( isinstance(dark_exposure, Quantity) and isinstance(data_exposure, Quantity) ): if exposure_time: try: data_exposure *= exposure_unit dark_exposure *= exposure_unit except TypeError as err: raise TypeError("must provide unit for exposure time.") from err else: raise TypeError("exposure times must be astropy.units.Quantity objects.") try: if scale: master_scaled = master.copy() # data_exposure and dark_exposure are both quantities, # so we can just have subtract do the scaling master_scaled = master_scaled.multiply(data_exposure / dark_exposure) result = ccd.subtract(master_scaled) else: result = ccd.subtract(master) except (u.UnitsError, u.UnitConversionError, ValueError) as err: # Make the error message a little more explicit than what is returned # by default. raise u.UnitsError( f"Unit '{ccd.unit}' of the uncalibrated image does not " f"match unit '{master.unit}' of the calibration " "image" ) from err result.meta = ccd.meta.copy() return result @log_to_metadata def gain_correct(ccd, gain, gain_unit=None): """Correct the gain in the image. Parameters ---------- ccd : `~astropy.nddata.CCDData` Data to have gain corrected. gain : `~astropy.units.Quantity` or `~ccdproc.Keyword` gain value for the image expressed in electrons per adu. gain_unit : `~astropy.units.Unit` or None, optional Unit for the ``gain``; used only if ``gain`` itself does not provide units. Default is ``None``. {log} Returns ------- result : `~astropy.nddata.CCDData` CCDData object with gain corrected. """ if isinstance(gain, Keyword): gain_value = gain.value_from(ccd.header) elif isinstance(gain, numbers.Number) and gain_unit is not None: gain_value = gain * u.Unit(gain_unit) else: gain_value = gain result = ccd.multiply(gain_value) result.meta = ccd.meta.copy() return result @log_to_metadata def flat_correct(ccd, flat, min_value=None, norm_value=None): """Correct the image for flat fielding. The flat field image is normalized by its mean or a user-supplied value before flat correcting. Parameters ---------- ccd : `~astropy.nddata.CCDData` Data to be transformed. flat : `~astropy.nddata.CCDData` Flatfield to apply to the data. min_value : float or None, optional Minimum value for flat field. The value can either be None and no minimum value is applied to the flat or specified by a float which will replace all values in the flat by the min_value. Default is ``None``. norm_value : float or None, optional If not ``None``, normalize flat field by this argument rather than the mean of the image. This allows fixing several different flat fields to have the same scale. If this value is negative or 0, a ``ValueError`` is raised. Default is ``None``. {log} Returns ------- ccd : `~astropy.nddata.CCDData` CCDData object with flat corrected. """ # Use the min_value to replace any values in the flat use_flat = flat if min_value is not None: flat_min = flat.copy() flat_min.data[flat_min.data < min_value] = min_value use_flat = flat_min # If a norm_value was input and is positive, use it to scale the flat if norm_value is not None and norm_value > 0: flat_mean_val = norm_value elif norm_value is not None: # norm_value was set to a bad value raise ValueError("norm_value must be greater than zero.") else: # norm_value was not set, use mean of the image. flat_mean_val = use_flat.data.mean() # Normalize the flat. flat_mean = flat_mean_val * use_flat.unit flat_normed = use_flat.divide(flat_mean) # divide through the flat flat_corrected = ccd.divide(flat_normed) flat_corrected.meta = ccd.meta.copy() return flat_corrected @log_to_metadata def transform_image(ccd, transform_func, **kwargs): """Transform the image. Using the function specified by transform_func, the transform will be applied to data, uncertainty, and mask in ccd. Parameters ---------- ccd : `~astropy.nddata.CCDData` Data to be transformed. transform_func : callable Function to be used to transform the data, mask and uncertainty. kwargs : Additional keyword arguments to be used by the transform_func. {log} Returns ------- ccd : `~astropy.nddata.CCDData` A transformed CCDData object. Notes ----- At this time, transform will be applied to the uncertainty data but it will only transform the data. This will not properly handle uncertainties that arise due to correlation between the pixels. These should only be geometric transformations of the images. Other methods should be used if the units of ccd need to be changed. Examples -------- Given an array that is 100x100:: >>> import numpy as np >>> from astropy import units as u >>> arr1 = CCDData(np.ones([100, 100]), unit=u.adu) The syntax for transforming the array using `scipy.ndimage.shift`:: >>> from scipy.ndimage import shift >>> from ccdproc import transform_image >>> transformed = transform_image(arr1, shift, shift=(5.5, 8.1)) """ # check that it is a ccddata object if not isinstance(ccd, CCDData): raise TypeError("ccd is not a CCDData.") # make a copy of the object nccd = ccd.copy() # transform the image plane try: nccd.data = transform_func(nccd.data, **kwargs) except TypeError as exc: if "is not callable" in str(exc): raise TypeError("transform_func is not a callable.") from exc raise # transform the uncertainty plane if it exists if nccd.uncertainty is not None: nccd.uncertainty.array = transform_func(nccd.uncertainty.array, **kwargs) # transform the mask plane if nccd.mask is not None: mask = transform_func(nccd.mask, **kwargs) nccd.mask = mask > 0 if nccd.wcs is not None: warn = "WCS information may be incorrect as no transformation was applied to it" warnings.warn(warn, UserWarning, stacklevel=2) return nccd @log_to_metadata def wcs_project(ccd, target_wcs, target_shape=None, order="bilinear"): """ Given a CCDData image with WCS, project it onto a target WCS and return the reprojected data as a new CCDData image. Any flags, weight, or uncertainty are ignored in doing the reprojection. Parameters ---------- ccd : `~astropy.nddata.CCDData` Data to be projected. target_wcs : `~astropy.wcs.WCS` object WCS onto which all images should be projected. target_shape : two element list-like or None, optional Shape of the output image. If omitted, defaults to the shape of the input image. Default is ``None``. order : str, optional Interpolation order for re-projection. Must be one of: + 'nearest-neighbor' + 'bilinear' + 'biquadratic' + 'bicubic' Default is ``'bilinear'``. {log} Returns ------- ccd : `~astropy.nddata.CCDData` A transformed CCDData object. """ from astropy.nddata.ccddata import _generate_wcs_and_update_header from reproject import reproject_interp if not (ccd.wcs.is_celestial and target_wcs.is_celestial): raise ValueError("one or both WCS is not celestial.") if target_shape is None: target_shape = ccd.shape projected_image_raw, _ = reproject_interp( (ccd.data, ccd.wcs), target_wcs, shape_out=target_shape, order=order ) reprojected_mask = None if ccd.mask is not None: reprojected_mask, _ = reproject_interp( (ccd.mask, ccd.wcs), target_wcs, shape_out=target_shape, order=order ) # Make the mask 1 if the reprojected mask pixel value is non-zero. # A small threshold is included to allow for some rounding in # reproject_interp. reprojected_mask = reprojected_mask > 1e-8 # The reprojection will contain nan for any pixels for which the source # was outside the original image. Those should be masked also. output_mask = np.isnan(projected_image_raw) if reprojected_mask is not None: output_mask = output_mask | reprojected_mask # Need to scale counts by ratio of pixel areas area_ratio = proj_plane_pixel_area(target_wcs) / proj_plane_pixel_area(ccd.wcs) # If nothing ended up masked, don't create a mask. if not output_mask.any(): output_mask = None # If there are any wcs keywords in the header, remove them hdr, _ = _generate_wcs_and_update_header(ccd.header) nccd = CCDData( area_ratio * projected_image_raw, wcs=target_wcs, mask=output_mask, header=hdr, unit=ccd.unit, ) return nccd def sigma_func(arr, axis=None, ignore_nan=False): """ Robust method for calculating the deviation of an array. ``sigma_func`` uses the median absolute deviation to determine the standard deviation. Parameters ---------- arr : `~astropy.nddata.CCDData` or `numpy.ndarray` Array whose deviation is to be calculated. axis : int, tuple of ints or None, optional Axis or axes along which the function is performed. If ``None`` it is performed over all the dimensions of the input array. The axis argument can also be negative, in this case it counts from the last to the first axis. Default is ``None``. Returns ------- uncertainty : float uncertainty of array estimated from median absolute deviation. """ return ( stats.median_absolute_deviation(arr, axis=axis, ignore_nan=ignore_nan) * 1.482602218505602 ) def setbox(x, y, mbox, xmax, ymax): """ Create a box of length mbox around a position x,y. If the box will be out of [0,len] then reset the edges of the box to be within the boundaries. Parameters ---------- x : int Central x-position of box. y : int Central y-position of box. mbox : int Width of box. xmax : int Maximum x value. ymax : int Maximum y value. Returns ------- x1 : int Lower x corner of box. x2 : int Upper x corner of box. y1 : int Lower y corner of box. y2 : int Upper y corner of box. """ mbox = max(int(0.5 * mbox), 1) y1 = max(0, y - mbox) y2 = min(y + mbox + 1, ymax - 1) x1 = max(0, x - mbox) x2 = min(x + mbox + 1, xmax - 1) return x1, x2, y1, y2 def background_deviation_box(data, bbox): """ Determine the background deviation with a box size of bbox. The algorithm steps through the image and calculates the deviation within each box. It returns an array with the pixels in each box filled with the deviation value. Parameters ---------- data : `numpy.ndarray` or `numpy.ma.MaskedArray` Data to measure background deviation. bbox : int Box size for calculating background deviation. Raises ------ ValueError A value error is raised if bbox is less than 1. Returns ------- background : `numpy.ndarray` or `numpy.ma.MaskedArray` An array with the measured background deviation in each pixel. """ # Check to make sure the background box is an appropriate size # If it is too small, then insufficient statistics are generated if bbox < 1: raise ValueError("bbox must be greater than 1.") # make the background image barr = data * 0.0 + data.std() ylen, xlen = data.shape for i in range(int(0.5 * bbox), xlen, bbox): for j in range(int(0.5 * bbox), ylen, bbox): x1, x2, y1, y2 = setbox(i, j, bbox, xlen, ylen) barr[y1:y2, x1:x2] = sigma_func(data[y1:y2, x1:x2]) return barr def background_deviation_filter(data, bbox): """ Determine the background deviation for each pixel from a box with size of bbox. Parameters ---------- data : `numpy.ndarray` Data to measure background deviation. bbox : int Box size for calculating background deviation. Raises ------ ValueError A value error is raised if bbox is less than 1. Returns ------- background : `numpy.ndarray` or `numpy.ma.MaskedArray` An array with the measured background deviation in each pixel. """ # Check to make sure the background box is an appropriate size if bbox < 1: raise ValueError("bbox must be greater than 1.") return ndimage.generic_filter(data, sigma_func, size=(bbox, bbox)) @deprecated( "1.1", message="The rebin function will be removed in ccdproc 3.0 " "Use block_reduce or block_replicate instead.", ) def rebin(ccd, newshape): """ Rebin an array to have a new shape. Parameters ---------- ccd : `~astropy.nddata.CCDData` or `numpy.ndarray` Data to rebin. newshape : tuple Tuple containing the new shape for the array. Returns ------- output : `~astropy.nddata.CCDData` or `numpy.ndarray` An array with the new shape. It will have the same type as the input object. Raises ------ TypeError A type error is raised if data is not an `numpy.ndarray` or `~astropy.nddata.CCDData`. ValueError A value error is raised if the dimension of the new shape is not equal to the data's. Notes ----- This is based on the scipy cookbook for rebinning: http://wiki.scipy.org/Cookbook/Rebinning If rebinning a CCDData object to a smaller shape, the masking and uncertainty are not handled correctly. Examples -------- Given an array that is 100x100:: import numpy as np from astropy import units as u arr1 = CCDData(np.ones([10, 10]), unit=u.adu) The syntax for rebinning an array to a shape of (20,20) is:: rebin(arr1, (20,20)) """ # check to see that is in a nddata type if isinstance(ccd, np.ndarray): # check to see that the two arrays are going to be the same length if len(ccd.shape) != len(newshape): raise ValueError("newshape does not have the same dimensions as " "ccd.") slices = [slice(0, old, old / new) for old, new in zip(ccd.shape, newshape)] coordinates = np.mgrid[slices] indices = coordinates.astype("i") return ccd[tuple(indices)] elif isinstance(ccd, CCDData): # check to see that the two arrays are going to be the same length if len(ccd.shape) != len(newshape): raise ValueError("newshape does not have the same dimensions as ccd.") nccd = ccd.copy() # rebin the data plane nccd.data = rebin(nccd.data, newshape) # rebin the uncertainty plane if nccd.uncertainty is not None: nccd.uncertainty.array = rebin(nccd.uncertainty.array, newshape) # rebin the mask plane if nccd.mask is not None: nccd.mask = rebin(nccd.mask, newshape) return nccd else: raise TypeError("ccd is not an ndarray or a CCDData object.") def block_reduce(ccd, block_size, func=np.sum): """Thin wrapper around `astropy.nddata.block_reduce`.""" data = nddata.block_reduce(ccd, block_size, func) if isinstance(ccd, CCDData): # unit and meta "should" be unaffected by the change of shape and can # be copied. However wcs, mask, uncertainty should not be copied! data = CCDData(data, unit=ccd.unit, meta=ccd.meta.copy()) return data def block_average(ccd, block_size): """Like `block_reduce` but with predefined ``func=np.mean``.""" data = nddata.block_reduce(ccd, block_size, np.mean) # Like in block_reduce: if isinstance(ccd, CCDData): data = CCDData(data, unit=ccd.unit, meta=ccd.meta.copy()) return data def block_replicate(ccd, block_size, conserve_sum=True): """Thin wrapper around `astropy.nddata.block_replicate`.""" data = nddata.block_replicate(ccd, block_size, conserve_sum) # Like in block_reduce: if isinstance(ccd, CCDData): data = CCDData(data, unit=ccd.unit, meta=ccd.meta.copy()) return data try: # Append original docstring to docstrings of these functions block_reduce.__doc__ += nddata.block_reduce.__doc__ block_replicate.__doc__ += nddata.block_replicate.__doc__ __all__ += ["block_average", "block_reduce", "block_replicate"] except AttributeError: # Astropy 1.0 has no block_reduce, block_average del block_reduce, block_average, block_replicate def _blkavg(data, newshape): """ Block average an array such that it has the new shape. Parameters ---------- data : `numpy.ndarray` or `numpy.ma.MaskedArray` Data to average. newshape : tuple Tuple containing the new shape for the array. Returns ------- output : `numpy.ndarray` or `numpy.ma.MaskedArray` An array with the new shape and the average of the pixels. Raises ------ TypeError A type error is raised if data is not an `numpy.ndarray`. ValueError A value error is raised if the dimensions of new shape is not equal to data. Notes ----- This is based on the scipy cookbook for rebinning: http://wiki.scipy.org/Cookbook/Rebinning """ # check to see that is in a nddata type if not isinstance(data, np.ndarray): raise TypeError("data is not a ndarray object.") # check to see that the two arrays are going to be the same length if len(data.shape) != len(newshape): raise ValueError("newshape does not have the same dimensions as data.") shape = data.shape lenShape = len(shape) # fmt: off factor = np.asarray( # noqa: F841 factor is actually used in eval shape ) / np.asarray( newshape ) # fmt: on evList = ( ["data.reshape("] + [f"newshape[{i}],int(factor[{i}])," for i in range(lenShape)] + [")"] + [f".mean({i + 1})" for i in range(lenShape)] ) return eval("".join(evList)) def median_filter(data, *args, **kwargs): """See `scipy.ndimage.median_filter` for arguments. If the ``data`` is a `~astropy.nddata.CCDData` object the result will be another `~astropy.nddata.CCDData` object with the median filtered data as ``data`` and copied ``unit`` and ``meta``. """ if isinstance(data, CCDData): out_kwargs = {"meta": data.meta.copy(), "unit": data.unit} result = ndimage.median_filter(data.data, *args, **kwargs) return CCDData(result, **out_kwargs) else: return ndimage.median_filter(data, *args, **kwargs) # This originally used the "message" argument but that is not # supported until astropy 5, so use alternative instead. @deprecated_renamed_argument( "pssl", None, "2.3.0", arg_in_kwargs=True, alternative="The pssl keyword will be removed in " "ccdproc 3.0. Use inbkg instead to have astroscrappy temporarily remove the " "background during processing.", ) def cosmicray_lacosmic( ccd, sigclip=4.5, sigfrac=0.3, objlim=5.0, gain=1.0, readnoise=6.5, satlevel=65535.0, pssl=0.0, niter=4, sepmed=True, cleantype="meanmask", fsmode="median", psfmodel="gauss", psffwhm=2.5, psfsize=7, psfk=None, psfbeta=4.765, verbose=False, gain_apply=True, inbkg=None, invar=None, ): r""" Identify cosmic rays through the L.A. Cosmic technique. The L.A. Cosmic technique identifies cosmic rays by identifying pixels based on a variation of the Laplacian edge detection. The algorithm is an implementation of the code describe in van Dokkum (2001) [1]_ as implemented by McCully (2014) [2]_. If you use this algorithm, please cite these two works. Parameters ---------- ccd : `~astropy.nddata.CCDData` or `numpy.ndarray` Data to have cosmic ray cleaned. gain_apply : bool, optional If ``True``, **return gain-corrected data**, with correct units, otherwise do not gain-correct the data. Default is ``True`` to preserve backwards compatibility. sigclip : float, optional Laplacian-to-noise limit for cosmic ray detection. Lower values will flag more pixels as cosmic rays. Default: 4.5. sigfrac : float, optional Fractional detection limit for neighboring pixels. For cosmic ray neighbor pixels, a Laplacian-to-noise detection limit of sigfrac * sigclip will be used. Default: 0.3. objlim : float, optional Minimum contrast between Laplacian image and the fine structure image. Increase this value if cores of bright stars are flagged as cosmic rays. Default: 5.0. inbkg : float numpy array, optional A pre-determined background image, to be subtracted from ``indat`` before running the main detection algorithm. This is used primarily with spectroscopic data, to remove sky lines and the cross-section of an object continuum during iteration, "protecting" them from spurious rejection (see the above paper). This background is not removed from the final, cleaned output (``cleanarr``). This should be in units of "counts", the same units of indat. This inbkg should be free from cosmic rays. When estimating the cosmic-ray free noise of the image, we will treat ``inbkg`` as a constant Poisson contribution to the variance. invar : float numpy array, optional A pre-determined estimate of the data variance (ie. noise squared) in each pixel, generated by previous processing of ``indat``. If provided, this is used in place of an internal noise model based on ``indat``, ``gain`` and ``readnoise``. This still gets median filtered and cleaned internally, to estimate what the noise in each pixel *would* be in the absence of cosmic rays. This should be in units of "counts" squared. pssl : float, optional Previously subtracted sky level in ADU. We always need to work in electrons for cosmic ray detection, so we need to know the sky level that has been subtracted so we can add it back in. Default: 0.0. gain : float or `~astropy.units.Quantity`, optional Gain of the image (electrons / ADU). We always need to work in electrons for cosmic ray detection. Default: 1.0 readnoise : float, optional Read noise of the image (electrons). Used to generate the noise model of the image. Default: 6.5. satlevel : float, optional Saturation level of the image (electrons). This value is used to detect saturated stars and pixels at or above this level are added to the mask. Default: 65535.0. niter : int, optional Number of iterations of the LA Cosmic algorithm to perform. Default: 4. sepmed : bool, optional Use the separable median filter instead of the full median filter. The separable median is not identical to the full median filter, but they are approximately the same, the separable median filter is significantly faster, and still detects cosmic rays well. Note, this is a performance feature, and not part of the original L.A. Cosmic. Default: True cleantype : str, optional Set which clean algorithm is used: - ``"median"``: An unmasked 5x5 median filter. - ``"medmask"``: A masked 5x5 median filter. - ``"meanmask"``: A masked 5x5 mean filter. - ``"idw"``: A masked 5x5 inverse distance weighted interpolation. Default: ``"meanmask"``. fsmode : str, optional Method to build the fine structure image: - ``"median"``: Use the median filter in the standard LA Cosmic \ algorithm. - ``"convolve"``: Convolve the image with the psf kernel to calculate \ the fine structure image. Default: ``"median"``. psfmodel : str, optional Model to use to generate the psf kernel if fsmode == 'convolve' and psfk is None. The current choices are Gaussian and Moffat profiles: - ``"gauss"`` and ``"moffat"`` produce circular PSF kernels. - The ``"gaussx"`` and ``"gaussy"`` produce Gaussian kernels in the x \ and y directions respectively. Default: ``"gauss"``. psffwhm : float, optional Full Width Half Maximum of the PSF to use to generate the kernel. Default: 2.5. psfsize : int, optional Size of the kernel to calculate. Returned kernel will have size psfsize x psfsize. psfsize should be odd. Default: 7. psfk : `numpy.ndarray` (with float dtype) or None, optional PSF kernel array to use for the fine structure image if ``fsmode == 'convolve'``. If None and ``fsmode == 'convolve'``, we calculate the psf kernel using ``psfmodel``. Default: None. psfbeta : float, optional Moffat beta parameter. Only used if ``fsmode=='convolve'`` and ``psfmodel=='moffat'``. Default: 4.765. verbose : bool, optional Print to the screen or not. Default: False. Notes ----- Implementation of the cosmic ray identification L.A.Cosmic: http://www.astro.yale.edu/dokkum/lacosmic/ Returns ------- nccd : `~astropy.nddata.CCDData` or `numpy.ndarray` An object of the same type as ccd is returned. If it is a `~astropy.nddata.CCDData`, the mask attribute will also be updated with areas identified with cosmic rays masked. **By default, the image is multiplied by the gain.** You can control this behavior with the ``gain_apply`` argument. crmask : `numpy.ndarray` If an `numpy.ndarray` is provided as ccd, a boolean ndarray with the cosmic rays identified will also be returned. References ---------- .. [1] van Dokkum, P; 2001, "Cosmic-Ray Rejection by Laplacian Edge Detection". The Publications of the Astronomical Society of the Pacific, Volume 113, Issue 789, pp. 1420-1427. doi: 10.1086/323894 .. [2] McCully, C., 2014, "Astro-SCRAPPY", https://github.com/astropy/astroscrappy Examples -------- 1) Given an numpy.ndarray object, the syntax for running cosmicrar_lacosmic would be: >>> newdata, mask = cosmicray_lacosmic(data, sigclip=5) #doctest: +SKIP where the error is an array that is the same shape as data but includes the pixel error. This would return a data array, newdata, with the bad pixels replaced by the local median from a box of 11 pixels; and it would return a mask indicating the bad pixels. 2) Given an `~astropy.nddata.CCDData` object with an uncertainty frame, the syntax for running cosmicrar_lacosmic would be: >>> newccd = cosmicray_lacosmic(ccd, sigclip=5) # doctest: +SKIP The newccd object will have bad pixels in its data array replace and the mask of the object will be created if it did not previously exist or be updated with the detected cosmic rays. """ from astroscrappy import __version__ as asy_version from astroscrappy import detect_cosmics # If we didn't get a quantity, put them in, with unit specified by the # documentation above. if not isinstance(gain, u.Quantity): # Gain will change the value, so use the proper units gain = gain * u.electron / u.adu # Set the units of readnoise to electrons, as specified in the # documentation, if no unit is present. if not isinstance(readnoise, u.Quantity): readnoise = readnoise * u.electron # Handle transition from old astroscrappy interface to new old_astroscrappy_interface = pkgversion.parse(asy_version) < pkgversion.parse( "1.1.0" ) # Use this dictionary to define which keyword arguments are actually # passed to astroscrappy. asy_background_kwargs = {} # This is for handling the transition in astroscrappy versions data_offset = 0 # Handle setting up the keyword arguments for both interfaces if old_astroscrappy_interface: # pragma: no cover new_args = dict(inbkg=inbkg, invar=invar) bad_args = [] for k, v in new_args.items(): if v is not None: bad_args.append(k) if bad_args: s = "s" if len(bad_args) > 1 else "" bads = ", ".join(bad_args) raise TypeError( f"The argument{s} {bads} only valid for astroscrappy " "1.1.0 or higher." ) if pssl != 0: asy_background_kwargs = dict(pssl=pssl) else: if pssl != 0: if (inbkg is not None) or (invar is not None): raise ValueError("Cannot set both pssl and inbkg") # The old version of astroscrappy added the bkg back in # if pssl was provided. The new one does not, so set an offset # here that we later add in then take out. data_offset = pssl asy_background_kwargs = dict(inbkg=inbkg, invar=invar) if isinstance(ccd, np.ndarray): data = ccd crmask, cleanarr = detect_cosmics( data + data_offset, inmask=None, sigclip=sigclip, sigfrac=sigfrac, objlim=objlim, gain=gain.value, readnoise=readnoise.value, satlevel=satlevel, niter=niter, sepmed=sepmed, cleantype=cleantype, fsmode=fsmode, psfmodel=psfmodel, psffwhm=psffwhm, psfsize=psfsize, psfk=psfk, psfbeta=psfbeta, verbose=verbose, **asy_background_kwargs, ) cleanarr = cleanarr - data_offset cleanarr = _astroscrappy_gain_apply_helper( cleanarr, gain.value, gain_apply, old_astroscrappy_interface ) return cleanarr, crmask elif isinstance(ccd, CCDData): # Start with a check for a special case: ccd is in electron, and # gain and readnoise have no units. In that case we issue a warning # instead of raising an error to avoid crashing user's pipelines. if ccd.unit.is_equivalent(u.electron) and gain.value != 1.0: warnings.warn( "Image unit is electron but gain value " "is not 1.0. Data maybe end up being gain " "corrected twice.", stacklevel=2, ) else: if ( (readnoise.unit == u.electron) and (ccd.unit == u.electron) and (gain.value == 1.0) ): gain = gain.value * u.one # Check unit consistency before taking the time to check for # cosmic rays. if not (gain * ccd).unit.is_equivalent(readnoise.unit): raise ValueError( f"Inconsistent units for gain ({gain.unit}) " + f" ccd ({ccd.unit}) and readnoise ({readnoise.unit})." ) crmask, cleanarr = detect_cosmics( ccd.data + data_offset, inmask=ccd.mask, sigclip=sigclip, sigfrac=sigfrac, objlim=objlim, gain=gain.value, readnoise=readnoise.value, satlevel=satlevel, niter=niter, sepmed=sepmed, cleantype=cleantype, fsmode=fsmode, psfmodel=psfmodel, psffwhm=psffwhm, psfsize=psfsize, psfk=psfk, psfbeta=psfbeta, verbose=verbose, **asy_background_kwargs, ) # create the new ccd data object nccd = ccd.copy() cleanarr = cleanarr - data_offset cleanarr = _astroscrappy_gain_apply_helper( cleanarr, gain.value, gain_apply, old_astroscrappy_interface ) # Fix the units if the gain is being applied. nccd.unit = ccd.unit * gain.unit nccd.data = cleanarr if nccd.mask is None: nccd.mask = crmask else: nccd.mask = nccd.mask + crmask return nccd else: raise TypeError("ccd is not a CCDData or ndarray object.") def _astroscrappy_gain_apply_helper(cleaned_data, gain, gain_apply, old_interface): """ Helper function for logic determining how to apply gain to cleaned data. In the old astroscrappy interface cleaned data was always gain-corrected. In the new interface it is not. This function works out the Right Thing to do given the inputs. cleaned_data : `numpy.ndarray` The cleaned data. gain: float The gain to (maybe) be applied. gain_apply : bool If ``True``, the cleaned data should have the gain applied, otherwise the gain should be applied. old_interface : bool If ``True`` the old, i.e. pre-1.1.0, astroscrappy interface is being used. The old interface always gain corrected the cleaned data, the new one does not. """ if gain != 1.0: if gain_apply: if not old_interface: # New interface does not gain correct, old one did. return cleaned_data * gain else: # Do not want gain correct if old_interface: # pragma: no cover # Old interface gain corrected always so take it out return cleaned_data / gain return cleaned_data def cosmicray_median(ccd, error_image=None, thresh=5, mbox=11, gbox=0, rbox=0): """ Identify cosmic rays through median technique. The median technique identifies cosmic rays by identifying pixels by subtracting a median image from the initial data array. Parameters ---------- ccd : `~astropy.nddata.CCDData`, `numpy.ndarray` or `numpy.ma.MaskedArray` Data to have cosmic ray cleaned. thresh : float, optional Threshold for detecting cosmic rays. Default is ``5``. error_image : `numpy.ndarray`, float or None, optional Error level. If None, the task will use the standard deviation of the data. If an ndarray, it should have the same shape as data. Default is ``None``. mbox : int, optional Median box for detecting cosmic rays. Default is ``11``. gbox : int, optional Box size to grow cosmic rays. If zero, no growing will be done. Default is ``0``. rbox : int, optional Median box for calculating replacement values. If zero, no pixels will be replaced. Default is ``0``. Notes ----- Similar implementation to crmedian in iraf.imred.crutil.crmedian. Returns ------- nccd : `~astropy.nddata.CCDData` or `numpy.ndarray` An object of the same type as ccd is returned. If it is a `~astropy.nddata.CCDData`, the mask attribute will also be updated with areas identified with cosmic rays masked. nccd : `numpy.ndarray` If an `numpy.ndarray` is provided as ccd, a boolean ndarray with the cosmic rays identified will also be returned. Examples -------- 1) Given an numpy.ndarray object, the syntax for running cosmicray_median would be: >>> newdata, mask = cosmicray_median(data, error_image=error, ... thresh=5, mbox=11, ... rbox=11, gbox=5) # doctest: +SKIP where error is an array that is the same shape as data but includes the pixel error. This would return a data array, newdata, with the bad pixels replaced by the local median from a box of 11 pixels; and it would return a mask indicating the bad pixels. 2) Given an `~astropy.nddata.CCDData` object with an uncertainty frame, the syntax for running cosmicray_median would be: >>> newccd = cosmicray_median(ccd, thresh=5, mbox=11, ... rbox=11, gbox=5) # doctest: +SKIP The newccd object will have bad pixels in its data array replace and the mask of the object will be created if it did not previously exist or be updated with the detected cosmic rays. """ if isinstance(ccd, np.ndarray): data = ccd if error_image is None: error_image = data.std() else: if not isinstance(error_image, (float, np.ndarray)): raise TypeError("error_image is not a float or ndarray.") # create the median image marr = ndimage.median_filter(data, size=(mbox, mbox)) # Only look at the data array if isinstance(data, np.ma.MaskedArray): data = data.data # Find the residual image rarr = (data - marr) / error_image # identify all sources crarr = rarr > thresh # grow the pixels if gbox > 0: crarr = ndimage.maximum_filter(crarr, gbox) # replace bad pixels in the image ndata = data.copy() if rbox > 0: data = np.ma.masked_array(data, (crarr == 1)) mdata = ndimage.median_filter(data, rbox) ndata[crarr == 1] = mdata[crarr == 1] return ndata, crarr elif isinstance(ccd, CCDData): # set up the error image if error_image is None and ccd.uncertainty is not None: error_image = ccd.uncertainty.array if ccd.data.shape != error_image.shape: raise ValueError("error_image is not the same shape as data.") data, crarr = cosmicray_median( ccd.data, error_image=error_image, thresh=thresh, mbox=mbox, gbox=gbox, rbox=rbox, ) # create the new ccd data object nccd = ccd.copy() nccd.data = data if nccd.mask is None: nccd.mask = crarr else: nccd.mask = nccd.mask + crarr return nccd else: raise TypeError("ccd is not an numpy.ndarray or a CCDData object.") def ccdmask( ratio, findbadcolumns=False, byblocks=False, ncmed=7, nlmed=7, ncsig=15, nlsig=15, lsigma=9, hsigma=9, ngood=5, ): """ Uses method based on the IRAF ccdmask task to generate a mask based on the given input. .. note:: This function uses ``lines`` as synonym for the first axis and ``columns`` the second axis. Only two-dimensional ``ratio`` is currently supported. Parameters ---------- ratio : `~astropy.nddata.CCDData` Data to used to form mask. Typically this is the ratio of two flat field images. findbadcolumns : `bool`, optional If set to True, the code will search for bad column sections. Note that this treats columns as special and breaks symmetry between lines and columns and so is likely only appropriate for detectors which have readout directions. Default is ``False``. byblocks : `bool`, optional If set to true, the code will divide the image up in to blocks of size nlsig by ncsig and determine the standard deviation estimate in each block (as described in the original IRAF task, see Notes below). If set to False, then the code will use `scipy.ndimage.percentile_filter` to generate a running box version of the standard deviation estimate and use that value for the standard deviation at each pixel. Default is ``False``. ncmed, nlmed : `int`, optional The column and line size of the moving median rectangle used to estimate the uncontaminated local signal. The column median size should be at least 3 pixels to span single bad columns. Default is ``7``. ncsig, nlsig : `int`, optional The column and line size of regions used to estimate the uncontaminated local sigma using a percentile. The size of the box should contain of order 100 pixels or more. Default is ``15``. lsigma, hsigma : `float`, optional Positive sigma factors to use for selecting pixels below and above the median level based on the local percentile sigma. Default is ``9``. ngood : `int`, optional Gaps of undetected pixels along the column direction of length less than this amount are also flagged as bad pixels, if they are between pixels masked in that column. Default is ``5``. Returns ------- mask : `numpy.ndarray` A boolean ndarray where the bad pixels have a value of 1 (True) and valid pixels 0 (False), following the numpy.ma conventions. Notes ----- Similar implementation to IRAF's ccdmask task. The Following documentation is copied directly from: http://stsdas.stsci.edu/cgi-bin/gethelp.cgi?ccdmask The input image is first subtracted by a moving box median. The median is unaffected by bad pixels provided the median size is larger that twice the size of a bad region. Thus, if 3 pixel wide bad columns are present then the column median box size should be at least 7 pixels. The median box can be a single pixel wide along one dimension if needed. This may be appropriate for spectroscopic long slit data. The median subtracted image is then divided into blocks of size nclsig by nlsig. In each block the pixel values are sorted and the pixels nearest the 30.9 and 69.1 percentile points are found; this would be the one sigma points in a Gaussian noise distribution. The difference between the two count levels divided by two is then the local sigma estimate. This algorithm is used to avoid contamination by the bad pixel values. The block size must be at least 10 pixels in each dimension to provide sufficient pixels for a good estimate of the percentile sigma. The sigma uncertainty estimate of each pixel in the image is then the sigma from the nearest block. The deviant pixels are found by comparing the median subtracted residual to a specified sigma threshold factor times the local sigma above and below zero (the lsigma and hsigma parameters). This is done for individual pixels and then for column sums of pixels (excluding previously flagged bad pixels) from two to the number of lines in the image. The sigma of the sums is scaled by the square root of the number of pixels summed so that statistically low or high column regions may be detected even though individual pixels may not be statistically deviant. For the purpose of this task one would normally select large sigma threshold factors such as six or greater to detect only true bad pixels and not the extremes of the noise distribution. As a final step each column is examined to see if there are small segments of unflagged pixels between bad pixels. If the length of a segment is less than that given by the ngood parameter all the pixels in the segment are also marked as bad. """ try: nlines, ncols = ratio.data.shape except (TypeError, ValueError) as err: # shape is not iterable or has more or less than two values raise ValueError('"ratio" must be two-dimensional.') from err except AttributeError as err: # No data attribute or data has no shape attribute. raise ValueError('"ratio" should be a "CCDData".') from err def _sigma_mask(baseline, one_sigma_value, lower_sigma, upper_sigma): """Helper function to mask values outside of the specified sigma range.""" return (baseline < -lower_sigma * one_sigma_value) | ( baseline > upper_sigma * one_sigma_value ) mask = ~np.isfinite(ratio.data) medsub = ratio.data - ndimage.median_filter(ratio.data, size=(nlmed, ncmed)) if byblocks: nlinesblock = int(math.ceil(nlines / nlsig)) ncolsblock = int(math.ceil(ncols / ncsig)) for i in range(nlinesblock): for j in range(ncolsblock): l1 = i * nlsig l2 = min((i + 1) * nlsig, nlines) c1 = j * ncsig c2 = min((j + 1) * ncsig, ncols) block = medsub[l1:l2, c1:c2] high = np.percentile(block.ravel(), 69.1) low = np.percentile(block.ravel(), 30.9) block_sigma = (high - low) / 2.0 block_mask = _sigma_mask(block, block_sigma, lsigma, hsigma) mblock = np.ma.MaskedArray(block, mask=block_mask, copy=False) if findbadcolumns: csum = np.ma.sum(mblock, axis=0) csum[csum <= 0] = 0 csum_sigma = np.ma.MaskedArray(np.sqrt(c2 - c1 - csum)) colmask = _sigma_mask(csum.filled(1), csum_sigma, lsigma, hsigma) block_mask[:, :] |= colmask[np.newaxis, :] mask[l1:l2, c1:c2] = block_mask else: high = ndimage.percentile_filter(medsub, 69.1, size=(nlsig, ncsig)) low = ndimage.percentile_filter(medsub, 30.9, size=(nlsig, ncsig)) sigmas = (high - low) / 2.0 mask |= _sigma_mask(medsub, sigmas, lsigma, hsigma) if findbadcolumns: # Loop through columns and look for short segments (>> import ccdproc >>> import numpy as np >>> ccdproc.bitfield_to_boolean_mask(np.arange(8)) array([False, True, True, True, True, True, True, True]...) To ignore all bit flags ``ignore_bits=None`` can be used:: >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits=None) array([False, False, False, False, False, False, False, False]...) To ignore only specific bit flags one can use a ``list`` of bits flags to ignore:: >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits=[1, 4]) array([False, False, True, True, False, False, True, True]...) There are some equivalent ways:: >>> # pass in the sum of the "ignore_bits" directly >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits=5) # 1 + 4 array([False, False, True, True, False, False, True, True]...) >>> # use a comma seperated string of integers >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits='1, 4') array([False, False, True, True, False, False, True, True]...) >>> # use a + seperated string of integers >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits='1+4') array([False, False, True, True, False, False, True, True]...) Instead of directly specifying the **bits flags to ignore** one can also pass in the **only bits that shouldn't be ignored** by prepending a ``~`` to the string of ``ignore_bits`` (or if it's not a string in ``ignore_bits`` one can set ``flip_bits=True``):: >>> # ignore all bit flags except the one for 2. >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits='~(2)') array([False, False, True, True, False, False, True, True]...) >>> # ignore all bit flags except the one for 1, 8 and 32. >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits='~(1, 8, 32)') array([False, True, False, True, False, True, False, True]...) >>> # Equivalent for a list using flip_bits. >>> ccdproc.bitfield_to_boolean_mask(np.arange(8), ignore_bits=[1, 8, 32], ... flip_bits=True) array([False, True, False, True, False, True, False, True]...) """ return _bitfield_to_boolean_mask( bitfield, ignore_bits, flip_bits=flip_bits, good_mask_value=False, dtype=bool ) class Keyword: """ """ def __init__(self, name, unit=None, value=None): self._name = name self._unit = unit self.value = value @property def name(self): return self._name @property def unit(self): return self._unit @property def value(self): return self._value @value.setter def value(self, value): if value is None: self._value = value elif isinstance(value, Quantity): self._unit = value.unit self._value = value elif isinstance(value, str): if self.unit is not None: raise ValueError("keyword with a unit cannot have a " "string value.") else: self._value = value else: if self.unit is None: raise ValueError( "no unit provided. Set value with " "an astropy.units.Quantity." ) self._value = value * self.unit def value_from(self, header): """ Set value of keyword from FITS header. Parameters ---------- header : `~astropy.io.fits.Header` FITS header containing a value for this keyword. """ value_from_header = header[self.name] self.value = value_from_header return self.value ccdproc-2.4.3/ccdproc/image_collection.py0000644000000000000000000010721113615410400015343 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import fnmatch import logging import re import warnings from collections import OrderedDict from os import listdir, path import astropy.io.fits as fits import numpy as np import numpy.ma as ma from astropy.table import MaskedColumn, Table from astropy.utils.exceptions import AstropyUserWarning from .ccddata import _recognized_fits_file_extensions, fits_ccddata_reader logger = logging.getLogger(__name__) __all__ = ["ImageFileCollection"] __doctest_skip__ = ["*"] class ImageFileCollection: """ Representation of a collection of image files. The class offers a table summarizing values of keywords in the FITS headers of the files in the collection and offers convenient methods for iterating over the files in the collection. The generator methods use simple filtering syntax and can automate storage of any FITS files modified in the loop using the generator. Parameters ---------- location : str or None, optional Path to directory containing FITS files. Default is ``None``. keywords : list of str, '*' or None, optional Keywords that should be used as column headings in the summary table. If the value is or includes '*' then all keywords that appear in any of the FITS headers of the files in the collection become table columns. Default value is '*' unless ``info_file`` is specified. Default is ``None``. find_fits_by_reading: bool, optional If ``True``, read each file in location to check whether the file is a FITS file and include it in the collection based on that, rather than by file name. Compressed files, e.g. image.fits.gz, will **NOT** be properly detected. *Will be ignored if `filenames` is not ``None``.* filenames: str, list of str, or None, optional List of the names of FITS files which will be added to the collection. The filenames may either be in ``location`` or the name can be a relative or absolute path to the file. Default is ``None``. glob_include: str or None, optional Unix-style filename pattern to select filenames to include in the file collection. Can be used in conjunction with ``glob_exclude`` to easily select subsets of files in the target directory. Default is ``None``. glob_exclude: str or None, optional Unix-style filename pattern to select filenames to exclude from the file collection. Can be used in conjunction with ``glob_include`` to easily select subsets of files in the target directory. Default is ``None``. ext: str or int, optional The extension from which the header and data will be read in all files.Default is ``0``. Raises ------ ValueError Raised if keywords are set to a combination of '*' and any other value. """ def __init__( self, location=None, keywords=None, find_fits_by_reading=False, filenames=None, glob_include=None, glob_exclude=None, ext=0, ): # Include or exclude files from the collection based on glob pattern # matching - has to go above call to _get_files() if glob_exclude is not None: glob_exclude = str(glob_exclude) # some minimal validation self._glob_exclude = glob_exclude if glob_include is not None: glob_include = str(glob_include) self._glob_include = glob_include if location is not None: self._location = location else: self._location = "" self._find_fits_by_reading = find_fits_by_reading self._filenames = filenames self._files = [] self._files = self._get_files() if self._files == []: warnings.warn( "no FITS files in the collection.", AstropyUserWarning, stacklevel=2 ) self._summary = {} if keywords is None: # Use all keywords. keywords = "*" # Used internally to keep track of whether the user asked for all # keywords or a specific list. The keywords setter takes care of # actually setting the correct value, this just ensure that there # is always *some* value. self._all_keywords = False self._ext = ext if keywords: self.keywords = keywords def __repr__(self): if self.location is None: location = "" else: location = f"location={self.location!r}" if self._all_keywords: kw = "" else: kw = f"keywords={self.keywords[1:]!r}" if self.glob_exclude is None: glob_exclude = "" else: glob_exclude = f"glob_exclude={self.glob_exclude!r}" if self.glob_include is None: glob_include = "" else: glob_include = f"glob_include={self.glob_include!r}" if self.ext == 0: ext = "" else: ext = f"ext={self.ext}" if self._filenames is None: filenames = "" else: filenames = f"filenames={self._filenames}" params = [location, kw, filenames, glob_include, glob_exclude, ext] params = ", ".join([p for p in params if p]) str_repr = f"{self.__class__.__name__}({params})" return str_repr @property def summary(self): """ `~astropy.table.Table` of values of FITS keywords for files in the collection. Each keyword is a column heading. In addition, there is a column called ``file`` that contains the name of the FITS file. The directory is not included as part of that name. The first column is always named ``file``. The order of the remaining columns depends on how the summary was constructed. If a wildcard, ``*`` was used then the order is the order in which the keywords appear in the FITS files from which the summary is constructed. If an explicit list of keywords was supplied in setting up the collection then the order of the columns is the order of the keywords. """ return self._summary @property def location(self): """ str, Path name to directory containing FITS files. """ return self._location @property def keywords(self): """ list of str, Keywords currently in the summary table. Setting the keywords causes the summary table to be regenerated unless the new keywords are a subset of the old. .. versionchanged:: 1.3 Added ``deleter`` for ``keywords`` property. """ if self.summary: return self.summary.keys() else: return [] @keywords.setter def keywords(self, keywords): # since keywords are drawn from self.summary, setting # summary sets the keywords. if keywords is None: self._summary = [] return if keywords == "*": self._all_keywords = True else: self._all_keywords = False logging.debug("keywords in setter before pruning: %s.", keywords) # remove duplicates and force a copy so we can sort the items later # by their given position. new_keys_set = set(keywords) new_keys_lst = list(new_keys_set) new_keys_set.add("file") logging.debug("keywords after pruning %s.", new_keys_lst) current_set = set(self.keywords) if new_keys_set.issubset(current_set): logging.debug("table columns before trimming: %s.", " ".join(current_set)) cut_keys = current_set.difference(new_keys_set) logging.debug("will try removing columns: %s.", " ".join(cut_keys)) for key in cut_keys: self._summary.remove_column(key) logging.debug( "after removal column names are: %s.", " ".join(self.keywords) ) else: logging.debug("should be building new table...") # Reorder the keywords to match the initial ordering. new_keys_lst.sort(key=keywords.index) self._summary = self._fits_summary(new_keys_lst) @keywords.deleter def keywords(self): # since keywords are drawn from self._summary, setting # _summary = [] deletes the keywords. self._summary = [] @property def files(self): """ list of str, Unfiltered list of FITS files in location. """ return self._files @property def glob_include(self): """ str or None, Unix-style filename pattern to select filenames to include in the file collection. """ return self._glob_include @property def glob_exclude(self): """ str or None, Unix-style filename pattern to select filenames to exclude in the file collection. """ return self._glob_exclude @property def ext(self): """ str or int, The extension from which the header and data will be read in all files. """ return self._ext def values(self, keyword, unique=False): """ List of values for a keyword. Parameters ---------- keyword : str Keyword (i.e. table column) for which values are desired. unique : bool, optional If True, return only the unique values for the keyword. Default is ``False``. Returns ------- list Values as a list. """ if keyword not in self.keywords: raise ValueError(f"keyword {keyword} is not in the current summary") if unique: return list(set(self.summary[keyword].tolist())) else: return self.summary[keyword].tolist() def files_filtered(self, **kwd): """Determine files whose keywords have listed values. Parameters ---------- include_path : bool, keyword-only If the keyword ``include_path=True`` is set, the returned list contains not just the filename, but the full path to each file. Default is ``False``. regex_match : bool, keyword-only If ``True``, then string values in the ``**kwd`` dictionary are treated as regular expression patterns and matching is done by regular expression search. The search is always **case insensitive**. **kwd : ``**kwd`` is dict of keywords and values the files must have. The value '*' represents any value. A missing keyword is indicated by value ''. Returns ------- filenames : list The files that satisfy the keyword-value restrictions specified by the ``**kwd``. Examples -------- Some examples for filtering:: >>> keys = ['imagetyp','filter'] >>> collection = ImageFileCollection('test/data', keywords=keys) >>> collection.files_filtered(imagetyp='LIGHT', filter='R') >>> collection.files_filtered(imagetyp='*', filter='') In case you want to filter with keyword names that cannot be used as keyword argument name, you have to unpack them using a dictionary. For example if a keyword name contains a space or a ``-``:: >>> add_filters = {'exp-time': 20, 'ESO TPL ID': 1050} >>> collection.files_filtered(imagetyp='LIGHT', **add_filters) Notes ----- Value comparison is case *insensitive* for strings, whether matching exactly or matching with regular expressions. """ # If the collection is empty, self.summary == None; return empty list if self.summary is None: return [] # force a copy by explicitly converting to a list current_file_mask = self.summary["file"].mask.tolist() include_path = kwd.pop("include_path", False) self._find_keywords_by_values(**kwd) filtered_files = self.summary["file"].compressed() self.summary["file"].mask = current_file_mask if include_path: filtered_files = [ path.join(self._location, f) for f in filtered_files.tolist() ] return filtered_files def refresh(self): """ Refresh the collection by re-reading headers. """ keywords = "*" if self._all_keywords else self.keywords # Re-load list of files self._files = self._get_files() self._summary = self._fits_summary(header_keywords=keywords) def sort(self, keys): """Sort the list of files to determine the order of iteration. Sort the table of files according to one or more keys. This does not create a new object, instead is sorts in place. Parameters ---------- keys : str, list of str The key(s) to order the table by. """ if len(self._summary) > 0: self._summary.sort(keys) self._files = self.summary["file"].tolist() def filter(self, **kwd): """ Create a new collection by filtering the current collection. Parameters ---------- regex_match : bool, keyword-only If ``True``, then string values in the ``**kwd`` dictionary are treated as regular expression patterns and matching is done by regular expression search. The search is always **case insensitive**. **kwd : ``**kwd`` is dict of keywords and values the files must have. The value '*' represents any value. A missing keyword is indicated by value ''. Returns ------- `ImageFileCollection` A new collection with the files matched by the arguments to filter. """ files = self.files_filtered(include_path=True, **kwd) return ImageFileCollection(filenames=files, keywords=self.keywords) def _get_files(self): """Helper method which checks whether ``files`` should be set to a subset of file names or to all file names in a directory. Returns ------- files : list or str List of file names which will be added to the collection. """ files = [] if self._filenames: if isinstance(self._filenames, str): files.append(self._filenames) else: files = self._filenames else: # Check if self.location is set, otherwise proceed with empty list if self.location != "": files = self._fits_files_in_directory() if self.glob_include is not None: files = fnmatch.filter(files, self.glob_include) if self.glob_exclude is not None: files = [ file for file in files if not fnmatch.fnmatch(file, self.glob_exclude) ] return files def _dict_from_fits_header( self, file_name, input_summary=None, missing_marker=None ): """ Construct an ordered dictionary whose keys are the header keywords and values are a list of the values from this file and the input dictionary. If the input dictionary is ordered then that order is preserved. Parameters ---------- file_name : str Name of FITS file. input_summary : dict or None, optional Existing dictionary to which new values should be appended. Default is ``None``. missing_marker : any type, optional Fill value for missing header-keywords. Default is ``None``. Returns ------- file_table : `~astropy.table.Table` """ def _add_val_to_dict(key, value, tbl_dict, n_previous, missing_marker): try: tbl_dict[key].append(value) except KeyError: tbl_dict[key] = [missing_marker] * n_previous tbl_dict[key].append(value) if input_summary is None: summary = OrderedDict() n_previous = 0 else: summary = input_summary n_previous = len(summary["file"]) h = fits.getheader(file_name, self.ext) assert "file" not in h if self.location: # We have a location and can reconstruct the path using it name_for_file_column = path.basename(file_name) else: # No location, so use whatever path the user passed in name_for_file_column = file_name # Try opening header before this so that file name is only added if # file is valid FITS try: summary["file"].append(name_for_file_column) except KeyError: summary["file"] = [name_for_file_column] missing_in_this_file = [k for k in summary if (k not in h and k != "file")] multi_entry_keys = {"comment": [], "history": []} alreadyencountered = set() for k, v in h.items(): if k == "": continue k = k.lower() if k in ["comment", "history"]: multi_entry_keys[k].append(str(v)) # Accumulate these in a separate dictionary until the # end to avoid adding multiple entries to summary. continue elif k in alreadyencountered: # The "normal" multi-entries HISTORY, COMMENT and BLANK are # already processed so any further duplication is probably # a mistake. It would lead to problems in ImageFileCollection # to add it as well, so simply ignore those. warnings.warn( f'Header from file "{file_name}" contains multiple entries for ' f'"{k}", the pair "{k}={v}" will be ignored.', UserWarning, stacklevel=2, ) continue else: # Add the key to the already encountered keys so we don't add # it more than once. alreadyencountered.add(k) _add_val_to_dict(k, v, summary, n_previous, missing_marker) for k, v in multi_entry_keys.items(): if v: joined = ",".join(v) _add_val_to_dict(k, joined, summary, n_previous, missing_marker) for missing in missing_in_this_file: summary[missing].append(missing_marker) return summary def _set_column_name_case_to_match_keywords(self, header_keys, summary_table): for k in header_keys: k_lower = k.lower() if k_lower != k: try: summary_table.rename_column(k_lower, k) except KeyError: pass def _fits_summary(self, header_keywords): """ Generate a summary table of keywords from FITS headers. Parameters ---------- header_keywords : list of str or '*' Keywords whose value should be extracted from FITS headers or '*' to extract all. """ if not self.files: return None # Make sure we have a list...for example, in python 3, dict.keys() # is not a list. original_keywords = list(header_keywords) # Get rid of any duplicate keywords, also forces a copy. header_keys = set(original_keywords) header_keys.add("file") file_name_column = MaskedColumn(name="file", data=self.files) if not header_keys or (header_keys == {"file"}): summary_table = Table(masked=True) summary_table.add_column(file_name_column) return summary_table summary_dict = None missing_marker = None for file_name in file_name_column.tolist(): file_path = path.join(self.location, file_name) try: # Note: summary_dict is an OrderedDict, so should preserve # the order of the keywords in the FITS header. summary_dict = self._dict_from_fits_header( file_path, input_summary=summary_dict, missing_marker=missing_marker ) except OSError as e: logger.warning( "unable to get FITS header for file %s: %s.", file_path, e ) continue summary_table = Table(summary_dict, masked=True) for column in summary_table.colnames: summary_table[column].mask = [ v is missing_marker for v in summary_table[column].tolist() ] self._set_column_name_case_to_match_keywords(header_keys, summary_table) missing_columns = header_keys - set(summary_table.colnames) missing_columns -= {"*"} length = len(summary_table) for column in missing_columns: all_masked = MaskedColumn( name=column, data=np.zeros(length), mask=np.ones(length) ) summary_table.add_column(all_masked) if "*" not in header_keys: # Rearrange table columns to match order of keywords. # File always comes first. header_keys -= {"file"} original_order = ["file"] + sorted(header_keys, key=original_keywords.index) summary_table = summary_table[original_order] if not summary_table.masked: summary_table = Table(summary_table, masked=True) return summary_table def _find_keywords_by_values(self, **kwd): """ Find files whose keywords have given values. Parameters ---------- match_regex : bool, optional If ``True`` match string values by using a regular expression search instead of equality. Default value is ``False``. The remaining arguments are keyword/value pairs specifying the values to match. `**kwd` is list of keywords and values the files must have. The value '*' represents any value. A missing keyword is indicated by value '' Example:: >>> keys = ['imagetyp','filter'] >>> collection = ImageFileCollection('test/data', keywords=keys) >>> collection.files_filtered(imagetyp='LIGHT', filter='R') >>> collection.files_filtered(imagetyp='*', filter='') >>> collection.files_filtered(imagetyp='bias|filter', regex_match=True) NOTE: Value comparison is case *insensitive* for strings. """ regex_match = kwd.pop("regex_match", False) keywords = kwd.keys() values = kwd.values() if set(keywords).issubset(self.keywords): # we already have the information in memory use_info = self.summary else: # we need to load information about these keywords. use_info = self._fits_summary(header_keywords=keywords) matches = np.ones(len(use_info), dtype=bool) for key, value in zip(keywords, values): logger.debug("key %s, value %s", key, value) logger.debug("value in table %s", use_info[key]) value_missing = use_info[key].mask logger.debug("value missing: %s", value_missing) value_not_missing = np.logical_not(value_missing) if value == "*": have_this_value = value_not_missing elif value is not None: if isinstance(value, str): # need to loop explicitly over array rather than using # where to correctly do string comparison. have_this_value = np.zeros(len(use_info), dtype=bool) # We are going to do a regex match no matter what. if regex_match: pattern = re.compile(value, flags=re.IGNORECASE) else: # Escape all special characters that might be present value = re.escape(value) # This pattern matches the prior behavior. pattern = re.compile("^" + value + "$", flags=re.IGNORECASE) for idx, file_key_value in enumerate(use_info[key].tolist()): if value_not_missing[idx]: try: value_matches = ( pattern.search(file_key_value) is not None ) except TypeError: # In case we're dealing with an object column # there could be values other than strings in it # so it could fail with an TypeError. value_matches = False else: value_matches = False have_this_value[idx] = value_not_missing[idx] & value_matches else: have_this_value = value_not_missing tmp = use_info[key][value_not_missing] == value have_this_value[value_not_missing] = tmp have_this_value &= value_not_missing else: # this case--when value==None--is asking for the files which # are missing a value for this keyword have_this_value = value_missing matches &= have_this_value # the numpy convention is that the mask is True for values to # be omitted, hence use ~matches. logger.debug("Matches: %s", matches) self.summary["file"].mask = ma.nomask self.summary["file"].mask[~matches] = True def _fits_files_in_directory(self, extensions=None, compressed=True): """ Get names of FITS files in directory, based on filename extension. Parameters ---------- extensions : list of str or None, optional List of filename extensions that are FITS files. Default is ``['fit', 'fits', 'fts']``. Default is ``None``. compressed : bool, optional If ``True``, compressed files should be included in the list (e.g. `.fits.gz`). Default is ``True``. Returns ------- list *Names* of the files (with extension), not the full pathname. """ full_extensions = extensions or list(_recognized_fits_file_extensions) # The common compressed fits image .fz is supported using ext=1 when # calling ImageFileCollection if compressed: for comp in [".gz", ".bz2", ".Z", ".zip", ".fz"]: with_comp = [extension + comp for extension in full_extensions] full_extensions.extend(with_comp) all_files = listdir(self.location) files = [] if not self._find_fits_by_reading: for extension in full_extensions: files.extend(fnmatch.filter(all_files, "*" + extension)) else: for infile in all_files: inpath = path.join(self.location, infile) with open(inpath, "rb") as fp: # Hmm, first argument to is_fits is not actually used in # that function. *shrug* if fits.connect.is_fits("just some junk", infile, fp): files.append(infile) files.sort() return files def _generator( self, return_type, save_with_name="", save_location="", overwrite=False, do_not_scale_image_data=True, return_fname=False, ccd_kwargs=None, **kwd, ): """ Generator that yields each {name} in the collection. If any of the parameters ``save_with_name``, ``save_location`` or ``overwrite`` evaluates to ``True`` the generator will write a copy of each FITS file it is iterating over. In other words, if ``save_with_name`` and/or ``save_location`` is a string with non-zero length, and/or ``overwrite`` is ``True``, a copy of each FITS file will be made. Parameters ---------- save_with_name : str, optional string added to end of file name (before extension) if FITS file should be saved after iteration. Unless ``save_location`` is set, files will be saved to location of the source files ``self.location``. Default is ``''``. save_location : str, optional Directory in which to save FITS files; implies that FITS files will be saved. Note this provides an easy way to copy a directory of files--loop over the {name} with ``save_location`` set. Default is ``''``. overwrite : bool, optional If ``True``, overwrite input FITS files. Default is ``False``. do_not_scale_image_data : bool, optional If ``True``, prevents fits from scaling images. Default is ``{default_scaling}``. Default is ``True``. return_fname : bool, optional If True, return the tuple (header, file_name) instead of just header. The file name returned is the name of the file only, not the full path to the file. Default is ``False``. ccd_kwargs : dict, optional Dict with parameters for `~astropy.nddata.fits_ccddata_reader`. For instance, the key ``'unit'`` can be used to specify the unit of the data. If ``'unit'`` is not given then ``'adu'`` is used as the default unit. See `~astropy.nddata.fits_ccddata_reader` for a complete list of parameters that can be passed through ``ccd_kwargs``. regex_match : bool, keyword-only If ``True``, then string values in the ``**kwd`` dictionary are treated as regular expression patterns and matching is done by regular expression search. The search is always **case insensitive**. **kwd : Any additional keywords are used to filter the items returned; see `files_filtered` examples for details. Returns ------- `{return_type}` If ``return_fname`` is ``False``, yield the next {name} in the collection. (`{return_type}`, str) If ``return_fname`` is ``True``, yield a tuple of ({name}, ``file name``) for the next item in the collection. """ # store mask so we can reset at end--must COPY, otherwise # current_mask just points to the mask of summary if not self.summary: return current_mask = {} for col in self.summary.columns: current_mask[col] = self.summary[col].mask if kwd: self._find_keywords_by_values(**kwd) ccd_kwargs = ccd_kwargs or {} for full_path in self._paths(): add_kwargs = {"do_not_scale_image_data": do_not_scale_image_data} # We need to open the file here, get the appropriate values and then # close it again before it "yields" otherwise it's not garantueed # that the generator actually advances and closes the file again. # For example if one uses "next" on the generator manually the # file handle could "leak". if return_type == "header": return_thing = fits.getheader(full_path, self.ext) elif return_type == "data": return_thing = fits.getdata(full_path, self.ext, **add_kwargs) elif return_type == "ccd": return_thing = fits_ccddata_reader( full_path, hdu=self.ext, **ccd_kwargs ) elif return_type == "hdu": with fits.open(full_path, **add_kwargs) as hdulist: ext_index = hdulist.index_of(self.ext) # Need to copy the HDU to prevent lazy loading problems # and "IO operations on closed file" errors return_thing = hdulist[ext_index].copy() else: raise ValueError(f"no generator for {return_type}") file_name = path.basename(full_path) if return_fname: yield return_thing, file_name else: yield return_thing if save_location: destination_dir = save_location else: destination_dir = path.dirname(full_path) basename = path.basename(full_path) if save_with_name: base, ext = path.splitext(basename) basename = base + save_with_name + ext new_path = path.join(destination_dir, basename) if return_type == "ccd": pass elif (new_path != full_path) or overwrite: with fits.open(full_path, **add_kwargs) as hdulist: ext_index = hdulist.index_of(self.ext) if return_type == "hdu": hdulist[ext_index] = return_thing elif return_type == "data": hdulist[ext_index].data = return_thing elif return_type == "header": hdulist[ext_index].header = return_thing try: hdulist.writeto(new_path, overwrite=overwrite) except OSError: logger.error("error writing file %s", new_path) raise # reset mask for col in self.summary.columns: self.summary[col].mask = current_mask[col] def _paths(self): """ Full path to each file. """ unmasked_files = self.summary["file"].compressed().tolist() return [path.join(self.location, file_) for file_ in unmasked_files] def headers(self, do_not_scale_image_data=True, **kwd): return self._generator( "header", do_not_scale_image_data=do_not_scale_image_data, **kwd ) headers.__doc__ = _generator.__doc__.format( name="header", default_scaling="True", return_type="astropy.io.fits.Header" ) def hdus(self, do_not_scale_image_data=False, **kwd): return self._generator( "hdu", do_not_scale_image_data=do_not_scale_image_data, **kwd ) hdus.__doc__ = _generator.__doc__.format( name="HDU", default_scaling="False", return_type="`, ` ".join( ("astropy.io.fits.PrimaryHDU", "astropy.io.fits.ImageHDU") ), ) def data(self, do_not_scale_image_data=False, **kwd): return self._generator( "data", do_not_scale_image_data=do_not_scale_image_data, **kwd ) data.__doc__ = _generator.__doc__.format( name="image", default_scaling="False", return_type="numpy.ndarray" ) def ccds(self, ccd_kwargs=None, **kwd): if (clobber := kwd.get("clobber")) is not None: warnings.warn( "The 'clobber' keyword argument is a deprecated alias for 'overwrite'", category=DeprecationWarning, stacklevel=2, ) kwd["overwrite"] = clobber if kwd.get("overwrite"): raise NotImplementedError("overwrite=True is not supported for CCDs.") return self._generator("ccd", ccd_kwargs=ccd_kwargs, **kwd) ccds.__doc__ = _generator.__doc__.format( name="CCDData", default_scaling="True", return_type="astropy.nddata.CCDData" ) ccdproc-2.4.3/ccdproc/log_meta.py0000644000000000000000000001166513615410400013644 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import inspect from functools import wraps from itertools import chain import numpy as np from astropy import units as u from astropy.io import fits from astropy.nddata import NDData import ccdproc # Really only need Keyword from ccdproc __all__ = [] _LOG_ARGUMENT = "add_keyword" _LOG_ARG_HELP = f""" {_LOG_ARGUMENT} : str, `~ccdproc.Keyword` or dict-like, optional Item(s) to add to metadata of result. Set to False or None to completely disable logging. Default is to add a dictionary with a single item: The key is the name of this function and the value is a string containing the arguments the function was called with, except the value of this argument. """ def _insert_in_metadata_fits_safe(ccd, key, value): from .core import _short_names if key in _short_names: # This keyword was (hopefully) added by autologging but the # combination of it and its value not FITS-compliant in two # ways: the keyword name may be more than 8 characters and # the value may be too long. FITS cannot handle both of # those problems at once, so this fixes one of those # problems... # Shorten, sort of... short_name = _short_names[key] if isinstance(ccd.meta, fits.Header): ccd.meta[f"HIERARCH {key.upper()}"] = ( short_name, "Shortened name for ccdproc command", ) else: ccd.meta[key] = (short_name, "Shortened name for ccdproc command") ccd.meta[short_name] = value else: ccd.meta[key] = value def log_to_metadata(func): """ Decorator that adds logging to ccdproc functions. The decorator adds the optional argument _LOG_ARGUMENT to function signature and updates the function's docstring to reflect that. It also sets the default value of the argument to the name of the function and the arguments it was called with. """ func.__doc__ = func.__doc__.format(log=_LOG_ARG_HELP) argspec = inspect.getfullargspec(func) original_args, _, _, defaults = ( argspec.args, argspec.varargs, argspec.varkw, argspec.defaults, ) # Add logging keyword and its default value for docstring original_args.append(_LOG_ARGUMENT) try: defaults = list(defaults) except TypeError: defaults = [] defaults.append(True) signature_with_arg_added = inspect.signature(func) signature_with_arg_added = f"{func.__name__}{signature_with_arg_added}" func.__doc__ = "\n".join([signature_with_arg_added, func.__doc__]) @wraps(func) def wrapper(*args, **kwd): # Grab the logging keyword, if it is present. log_result = kwd.pop(_LOG_ARGUMENT, True) result = func(*args, **kwd) if not log_result: # No need to add metadata.... meta_dict = {} elif log_result is not True: meta_dict = _metadata_to_dict(log_result) else: # Logging is not turned off, but user did not provide a value # so construct one unless the config parameter auto_logging is set to False if ccdproc.conf.auto_logging: key = func.__name__ # Get names of arguments, which may or may not have # been called as keywords. positional_args = original_args[: len(args)] all_args = chain(zip(positional_args, args), kwd.items()) all_args = [ f"{name}={_replace_array_with_placeholder(val)}" for name, val in all_args ] log_val = ", ".join(all_args) log_val = log_val.replace("\n", "") meta_dict = {key: log_val} else: meta_dict = {} for k, v in meta_dict.items(): _insert_in_metadata_fits_safe(result, k, v) return result return wrapper def _metadata_to_dict(arg): if isinstance(arg, str): # add the key, no value return {arg: None} elif isinstance(arg, ccdproc.Keyword): return {arg.name: arg.value} else: return arg def _replace_array_with_placeholder(value): return_type_not_value = False if isinstance(value, u.Quantity): return_type_not_value = not value.isscalar elif isinstance(value, (NDData, np.ndarray)): try: length = len(value) except TypeError: # Value has no length... try: # ...but if it is NDData its .data will have a length length = len(value.data) except TypeError: # No idea what this data is, assume length is not 1 length = 42 return_type_not_value = length > 1 if return_type_not_value: return f"<{value.__class__.__name__}>" else: return value ccdproc-2.4.3/ccdproc/extern/__init__.py0000644000000000000000000000010013615410400015077 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst ccdproc-2.4.3/ccdproc/extern/bitfield.py0000644000000000000000000004510013615410400015133 0ustar00# External license! License can be found in "licenses/LICENSE_STSCI_TOOLS.txt". """ A module that provides functions for manipulating bitmasks and data quality (DQ) arrays. :Authors: Mihai Cara (contact: help@stsci.edu) :License: ``_ """ import sys import warnings import numpy as np __version__ = '1.1.1' __vdate__ = '30-January-2018' __author__ = 'Mihai Cara' __all__ = ['bitfield_to_boolean_mask', 'interpret_bit_flags', 'is_bit_flag'] # Revision history: # 0.1.0 (29-March-2015) - initial release based on code from stsci.skypac # 0.1.1 (21-February-2017) - documentation typo fix # 0.2.0 (23-February-2017) - performance and stability improvements. Changed # default output mask type from numpy.uint8 to numpy.bool_. # 1.0.0 (16-March-2017) - Multiple enhancements: # 1. Deprecated 'interpret_bits_value()'in favor of # 'interpret_bit_flags()' which now takes 'flip_bits' argument to flip # bits in (list of) integer flags. # 2. Deprecated 'bitmask2mask()' in favor of 'bitfield_to_boolean_mask()' # which now also takes 'flip_bits' argument. # 3. Renamed arguments of 'interpret_bit_flags()' and # 'bitfield_to_boolean_mask()' to be more technically correct. # 4. 'interpret_bit_flags()' and 'bitfield_to_boolean_mask()' now # accept Python lists of bit flags (in addition to integer bitmasks # and string comma- (or '+') separated lists of bit flags). # 5. Added 'is_bit_flag()' function to check if an integer number has # only one bit set (i.e., that it is a power of 2). # 1.1.0 (29-January-2018) - Multiple enhancements: # 1. Added support for long type in Python 2.7 in # `interpret_bit_flags()` and `bitfield_to_boolean_mask()`. # 2. `interpret_bit_flags()` now always returns `int` (or `int` or `long` # in Python 2.7). Previously when input was of integer-like type # (i.e., `numpy.uint64`), it was not converted to Python `int`. # 3. `bitfield_to_boolean_mask()` will no longer crash when # `ignore_flags` argument contains bit flags beyond what the type of # the argument `bitfield` can hold. # 1.1.1 (30-January-2018) - Improved filtering of high bits in flags. # INT_TYPE = (int, long,) if sys.version_info < (3,) else (int,) MAX_UINT_TYPE = np.uint64 SUPPORTED_FLAGS = int(np.bitwise_not( 0, dtype=MAX_UINT_TYPE, casting='unsafe' )) def is_bit_flag(n): """ Verifies if the input number is a bit flag (i.e., an integer number that is an integer power of 2). Parameters ---------- n : int A positive integer number. Non-positive integers are considered not to be "flags". Returns ------- bool ``True`` if input ``n`` is a bit flag and ``False`` if it is not. """ if n < 1: return False return bin(n).count('1') == 1 def _is_int(n): return ( (isinstance(n, INT_TYPE) and not isinstance(n, bool)) or (isinstance(n, np.generic) and np.issubdtype(n, np.integer)) ) def interpret_bit_flags(bit_flags, flip_bits=None): """ Converts input bit flags to a single integer value (bitmask) or `None`. When input is a list of flags (either a Python list of integer flags or a sting of comma- or '+'-separated list of flags), the returned bitmask is obtained by summing input flags. .. note:: In order to flip the bits of the returned bitmask, for input of `str` type, prepend '~' to the input string. '~' must be prepended to the *entire string* and not to each bit flag! For input that is already a bitmask or a Python list of bit flags, set `flip_bits` for `True` in order to flip the bits of the returned bitmask. Parameters ---------- bit_flags : int, str, list, None An integer bitmask or flag, `None`, a string of comma- or '+'-separated list of integer bit flags, or a Python list of integer bit flags. If `bit_flags` is a `str` and if it is prepended with '~', then the output bitmask will have its bits flipped (compared to simple sum of input flags). For input `bit_flags` that is already a bitmask or a Python list of bit flags, bit-flipping can be controlled through `flip_bits` parameter. flip_bits : bool, None Indicates whether or not to flip the bits of the returned bitmask obtained from input bit flags. This parameter must be set to `None` when input `bit_flags` is either `None` or a Python list of flags. Returns ------- bitmask : int or None Returns and integer bit mask formed from the input bit value or `None` if input `bit_flags` parameter is `None` or an empty string. If input string value was prepended with '~' (or `flip_bits` was set to `True`), then returned value will have its bits flipped (inverse mask). Examples -------- >>> from ccdproc.extern.bitfield import interpret_bit_flags >>> "{0:016b}".format(0xFFFF & interpret_bit_flags(28)) '0000000000011100' >>> "{0:016b}".format(0xFFFF & interpret_bit_flags('4,8,16')) '0000000000011100' >>> "{0:016b}".format(0xFFFF & interpret_bit_flags('~4,8,16')) '1111111111100011' >>> "{0:016b}".format(0xFFFF & interpret_bit_flags('~(4+8+16)')) '1111111111100011' >>> "{0:016b}".format(0xFFFF & interpret_bit_flags([4, 8, 16])) '0000000000011100' >>> "{0:016b}".format(0xFFFF & interpret_bit_flags([4, 8, 16], flip_bits=True)) '1111111111100011' """ has_flip_bits = flip_bits is not None flip_bits = bool(flip_bits) allow_non_flags = False if _is_int(bit_flags): return (~int(bit_flags) if flip_bits else int(bit_flags)) elif bit_flags is None: if has_flip_bits: raise TypeError( "Keyword argument 'flip_bits' must be set to 'None' when " "input 'bit_flags' is None." ) return None elif isinstance(bit_flags, str): if has_flip_bits: raise TypeError( "Keyword argument 'flip_bits' is not permitted for " "comma-separated string lists of bit flags. Prepend '~' to " "the string to indicate bit-flipping." ) bit_flags = str(bit_flags).strip() if bit_flags.upper() in ['', 'NONE', 'INDEF']: return None # check whether bitwise-NOT is present and if it is, check that it is # in the first position: bitflip_pos = bit_flags.find('~') if bitflip_pos == 0: flip_bits = True bit_flags = bit_flags[1:].lstrip() else: if bitflip_pos > 0: raise ValueError("Bitwise-NOT must precede bit flag list.") flip_bits = False # basic check for correct use of parenthesis: while True: nlpar = bit_flags.count('(') nrpar = bit_flags.count(')') if nlpar == 0 and nrpar == 0: break if nlpar != nrpar: raise ValueError("Unbalanced parantheses in bit flag list.") lpar_pos = bit_flags.find('(') rpar_pos = bit_flags.rfind(')') if lpar_pos > 0 or rpar_pos < (len(bit_flags) - 1): raise ValueError("Incorrect syntax (incorrect use of " "parenthesis) in bit flag list.") bit_flags = bit_flags[1:-1].strip() if ',' in bit_flags: bit_flags = bit_flags.split(',') elif '+' in bit_flags: bit_flags = bit_flags.split('+') else: if bit_flags == '': raise ValueError( "Empty bit flag lists not allowed when either bitwise-NOT " "or parenthesis are present." ) bit_flags = [bit_flags] allow_non_flags = len(bit_flags) == 1 elif hasattr(bit_flags, '__iter__'): if not all([_is_int(flag) for flag in bit_flags]): raise TypeError("Each bit flag in a list must be an integer.") else: raise TypeError("Unsupported type for argument 'bit_flags'.") bitset = set(map(int, bit_flags)) if len(bitset) != len(bit_flags): warnings.warn("Duplicate bit flags will be ignored") bitmask = 0 for v in bitset: if not is_bit_flag(v) and not allow_non_flags: raise ValueError("Input list contains invalid (not powers of two) " "bit flags") bitmask += v if flip_bits: bitmask = ~bitmask return bitmask def bitfield_to_boolean_mask(bitfield, ignore_flags=0, flip_bits=None, good_mask_value=True, dtype=np.bool_): r""" bitfield_to_boolean_mask(bitfield, ignore_flags=None, flip_bits=None, \ good_mask_value=True, dtype=numpy.bool\_) Converts an array of bit fields to a boolean (or integer) mask array according to a bitmask constructed from the supplied bit flags (see ``ignore_flags`` parameter). This function is particularly useful to convert data quality arrays to boolean masks with selective filtering of DQ flags. Parameters ---------- bitfield : numpy.ndarray An array of bit flags. By default, values different from zero are interpreted as "bad" values and values equal to zero are considered as "good" values. However, see ``ignore_flags`` parameter on how to selectively ignore some bits in the ``bitfield`` array data. ignore_flags : int, str, list, None (Default = 0) An integer bitmask, a Python list of bit flags, a comma- or '+'-separated string list of integer bit flags that indicate what bits in the input ``bitfield`` should be *ignored* (i.e., zeroed), or `None`. | Setting ``ignore_flags`` to `None` effectively will make `bitfield_to_boolean_mask` interpret all ``bitfield`` elements as "good" regardless of their value. | When ``ignore_flags`` argument is an integer bitmask, it will be combined using bitwise-NOT and bitwise-AND with each element of the input ``bitfield`` array (``~ignore_flags & bitfield``). If the resultant bitfield element is non-zero, that element will be interpreted as a "bad" in the output boolean mask and it will be interpreted as "good" otherwise. ``flip_bits`` parameter may be used to flip the bits (``bitwise-NOT``) of the bitmask thus effectively changing the meaning of the ``ignore_flags`` parameter from "ignore" to "use only" these flags. .. note:: Setting ``ignore_flags`` to 0 effectively will assume that all non-zero elements in the input ``bitfield`` array are to be interpreted as "bad". | When ``ignore_flags`` argument is an Python list of integer bit flags, these flags are added together to create an integer bitmask. Each item in the list must be a flag, i.e., an integer that is an integer power of 2. In order to flip the bits of the resultant bitmask, use ``flip_bits`` parameter. | Alternatively, ``ignore_flags`` may be a string of comma- or '+'-separated list of integer bit flags that should be added together to create an integer bitmask. For example, both ``'4,8'`` and ``'4+8'`` are equivalent and indicate that bit flags 4 and 8 in the input ``bitfield`` array should be ignored when generating boolean mask. .. note:: ``'None'``, ``'INDEF'``, and empty (or all white space) strings are special values of string ``ignore_flags`` that are interpreted as `None`. .. note:: Each item in the list must be a flag, i.e., an integer that is an integer power of 2. In addition, for convenience, an arbitrary **single** integer is allowed and it will be interpretted as an integer bitmask. For example, instead of ``'4,8'`` one could simply provide string ``'12'``. .. note:: When ``ignore_flags`` is a `str` and when it is prepended with '~', then the meaning of ``ignore_flags`` parameters will be reversed: now it will be interpreted as a list of bit flags to be *used* (or *not ignored*) when deciding which elements of the input ``bitfield`` array are "bad". Following this convention, an ``ignore_flags`` string value of ``'~0'`` would be equivalent to setting ``ignore_flags=None``. .. warning:: Because prepending '~' to a string ``ignore_flags`` is equivalent to setting ``flip_bits`` to `True`, ``flip_bits`` cannot be used with string ``ignore_flags`` and it must be set to `None`. flip_bits : bool, None (Default = None) Specifies whether or not to invert the bits of the bitmask either supplied directly through ``ignore_flags`` parameter or built from the bit flags passed through ``ignore_flags`` (only when bit flags are passed as Python lists of integer bit flags). Occasionally, it may be useful to *consider only specific bit flags* in the ``bitfield`` array when creating a boolean mask as opposite to *ignoring* specific bit flags as ``ignore_flags`` behaves by default. This can be achieved by inverting/flipping the bits of the bitmask created from ``ignore_flags`` flags which effectively changes the meaning of the ``ignore_flags`` parameter from "ignore" to "use only" these flags. Setting ``flip_bits`` to `None` means that no bit flipping will be performed. Bit flipping for string lists of bit flags must be specified by prepending '~' to string bit flag lists (see documentation for ``ignore_flags`` for more details). .. warning:: This parameter can be set to either `True` or `False` **ONLY** when ``ignore_flags`` is either an integer bitmask or a Python list of integer bit flags. When ``ignore_flags`` is either `None` or a string list of flags, ``flip_bits`` **MUST** be set to `None`. good_mask_value : int, bool (Default = True) This parameter is used to derive the values that will be assigned to the elements in the output boolean mask array that correspond to the "good" bit fields (that are 0 after zeroing bits specified by ``ignore_flags``) in the input ``bitfield`` array. When ``good_mask_value`` is non-zero or `True` then values in the output boolean mask array corresponding to "good" bit fields in ``bitfield`` will be `True` (if ``dtype`` is `numpy.bool_`) or 1 (if ``dtype`` is of numerical type) and values of corresponding to "bad" flags will be `False` (or 0). When ``good_mask_value`` is zero or `False` then the values in the output boolean mask array corresponding to "good" bit fields in ``bitfield`` will be `False` (if ``dtype`` is `numpy.bool_`) or 0 (if ``dtype`` is of numerical type) and values of corresponding to "bad" flags will be `True` (or 1). dtype : data-type (Default = numpy.bool\_) The desired data-type for the output binary mask array. Returns ------- mask : numpy.ndarray Returns an array of the same dimensionality as the input ``bitfield`` array whose elements can have two possible values, e.g., `True` or `False` (or 1 or 0 for integer ``dtype``) according to values of to the input ``bitfield`` elements, ``ignore_flags`` parameter, and the ``good_mask_value`` parameter. Examples -------- >>> from ccdproc.extern import bitfield >>> import numpy as np >>> dqbits = np.asarray([[0, 0, 1, 2, 0, 8, 12, 0], ... [10, 4, 0, 0, 0, 16, 6, 0]]) >>> bitfield.bitfield_to_boolean_mask(dqbits, ignore_flags=0, ... dtype=int) array([[1, 1, 0, 0, 1, 0, 0, 1], [0, 0, 1, 1, 1, 0, 0, 1]]) >>> bitfield.bitfield_to_boolean_mask(dqbits, ignore_flags=0, ... dtype=bool) array([[ True, True, False, False, True, False, False, True], [False, False, True, True, True, False, False, True]]...) >>> bitfield.bitfield_to_boolean_mask(dqbits, ignore_flags=6, ... good_mask_value=0, dtype=int) array([[0, 0, 1, 0, 0, 1, 1, 0], [1, 0, 0, 0, 0, 1, 0, 0]]) >>> bitfield.bitfield_to_boolean_mask(dqbits, ignore_flags=~6, ... good_mask_value=0, dtype=int) array([[0, 0, 0, 1, 0, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0]]) >>> bitfield.bitfield_to_boolean_mask(dqbits, ignore_flags=6, dtype=int, ... flip_bits=True, good_mask_value=0) array([[0, 0, 0, 1, 0, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0]]) >>> bitfield.bitfield_to_boolean_mask(dqbits, ignore_flags='~(2+4)', ... good_mask_value=0, dtype=int) array([[0, 0, 0, 1, 0, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0]]) >>> bitfield.bitfield_to_boolean_mask(dqbits, ignore_flags=[2, 4], ... flip_bits=True, good_mask_value=0, ... dtype=int) array([[0, 0, 0, 1, 0, 0, 1, 0], [1, 1, 0, 0, 0, 0, 1, 0]]) """ bitfield = np.asarray(bitfield) if not np.issubdtype(bitfield.dtype, np.integer): raise TypeError("Input bitfield array must be of integer type.") ignore_mask = interpret_bit_flags(ignore_flags, flip_bits=flip_bits) if ignore_mask is None: if good_mask_value: mask = np.ones_like(bitfield, dtype=dtype) else: mask = np.zeros_like(bitfield, dtype=dtype) return mask # filter out bits beyond the maximum supported by the data type: ignore_mask = ignore_mask & SUPPORTED_FLAGS # invert the "ignore" mask: ignore_mask = np.bitwise_not(ignore_mask, dtype=bitfield.dtype, casting='unsafe') mask = np.empty_like(bitfield, dtype=np.bool_) np.bitwise_and(bitfield, ignore_mask, out=mask, casting='unsafe') if good_mask_value: np.logical_not(mask, out=mask) return mask.astype(dtype=dtype, subok=False, copy=False) ccdproc-2.4.3/ccdproc/tests/__init__.py0000644000000000000000000000017113615410400014744 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst """ This packages contains affiliated package tests. """ ccdproc-2.4.3/ccdproc/tests/make_mef.py0000644000000000000000000000404713615410400014757 0ustar00import numpy as np from astropy.io import fits from astropy.utils.misc import NumpyRNGContext def make_sample_mef(science_name, flat_name, size=10, dtype="float32"): """ Make a multi-extension FITS image with random data and a MEF flat. Parameters ---------- science_name : str Name of the science image created by this function. flat_name : str Name of the flat image created by this function. size : int, optional Size of each dimension of the image; images created are square. dtype : str or numpy dtype, optional dtype of the generated images. """ with NumpyRNGContext(1234): number_of_image_extensions = 3 science_image = [fits.PrimaryHDU()] flat_image = [fits.PrimaryHDU()] for _ in range(number_of_image_extensions): # Simulate a cloudy night, average pixel # value of 100 with a read_noise of 1 electron. data = np.random.normal(100.0, 1.0, [size, size]).astype(dtype) hdu = fits.ImageHDU(data=data) # Make a header that is at least somewhat realistic hdu.header["unit"] = "electron" hdu.header["object"] = "clouds" hdu.header["exptime"] = 30.0 hdu.header["date-obs"] = "1928-07-23T21:03:27" hdu.header["filter"] = "B" hdu.header["imagetyp"] = "LIGHT" science_image.append(hdu) # Make a perfect flat flat = np.ones_like(data, dtype=dtype) flat_hdu = fits.ImageHDU(data=flat) flat_hdu.header["unit"] = "electron" flat_hdu.header["filter"] = "B" flat_hdu.header["imagetyp"] = "FLAT" flat_hdu.header["date-obs"] = "1928-07-23T21:03:27" flat_image.append(flat_hdu) science_image = fits.HDUList(science_image) science_image.writeto(science_name) flat_image = fits.HDUList(flat_image) flat_image.writeto(flat_name) if __name__ == "__main__": make_sample_mef("data/science-mef.fits", "data/flat-mef.fits") ccdproc-2.4.3/ccdproc/tests/pytest_fixtures.py0000644000000000000000000000432313615410400016471 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst from shutil import rmtree import numpy as np import pytest from astropy import units as u from astropy.nddata import CCDData from astropy.utils import NumpyRNGContext from ..utils.sample_directory import directory_for_testing # If additional pytest markers are defined the key in the dictionary below # should be the name of the marker. DEFAULTS = {"seed": 123, "data_size": 100, "data_scale": 1.0, "data_mean": 0.0} DEFAULT_SEED = 123 DEFAULT_DATA_SIZE = 100 DEFAULT_DATA_SCALE = 1.0 DEFAULT_DATA_MEAN = 0.0 def value_from_markers(key, request): m = request.node.get_closest_marker(key) if m is not None: return m.args[0] else: return DEFAULTS[key] def ccd_data( data_size=DEFAULT_DATA_SIZE, data_scale=DEFAULT_DATA_SCALE, data_mean=DEFAULT_DATA_MEAN, rng_seed=DEFAULT_SEED, ): """ Return a CCDData object with units of ADU. The size of the data array is 100x100 but can be changed using the marker @pytest.mark.data_size(N) on the test function, where N should be the desired dimension. Data values are initialized to random numbers drawn from a normal distribution with mean of 0 and scale 1. The scale can be changed with the marker @pytest.marker.scale(s) on the test function, where s is the desired scale. The mean can be changed with the marker @pytest.marker.scale(m) on the test function, where m is the desired mean. """ size = data_size scale = data_scale mean = data_mean with NumpyRNGContext(rng_seed): data = np.random.normal(loc=mean, size=[size, size], scale=scale) fake_meta = {"my_key": 42, "your_key": "not 42"} ccd = CCDData(data, unit=u.adu) ccd.header = fake_meta return ccd @pytest.fixture def triage_setup(request): n_test, test_dir = directory_for_testing() def teardown(): try: rmtree(test_dir) except OSError: # If we cannot clean up just keep going. pass request.addfinalizer(teardown) class Result: def __init__(self, n, directory): self.n_test = n self.test_dir = directory return Result(n_test, test_dir) ccdproc-2.4.3/ccdproc/tests/run_for_memory_profile.py0000644000000000000000000001263213615410400017774 0ustar00import pytest pytest.importorskip("memory_profiler") import gc import sys from argparse import ArgumentParser from pathlib import Path from tempfile import TemporaryDirectory import numpy as np import psutil from astropy.io import fits from astropy.nddata import CCDData from astropy.stats import median_absolute_deviation from memory_profiler import memory_usage # This bit of hackery ensures that we can see ccdproc from within # the test suite sys.path.append(str(Path().cwd())) from ccdproc import ImageFileCollection, combine from ccdproc.combiner import _calculate_size_of_image # Do not combine these into one statement. When all references are lost # to a TemporaryDirectory the directory is automatically deleted. _TMPDIR # creates a reference that will stick around. _TMPDIR = TemporaryDirectory() TMPPATH = Path(_TMPDIR.name) def generate_fits_files(n_images, size=None, seed=1523): if size is None: use_size = (2024, 2031) else: use_size = (size, size) np.random.seed(seed) base_name = "test-combine-{num:03d}.fits" for num in range(n_images): data = np.random.normal(size=use_size) # Now add some outlying pixels so there is something to clip n_bad = 50000 bad_x = np.random.randint(0, high=use_size[0] - 1, size=n_bad) bad_y = np.random.randint(0, high=use_size[1] - 1, size=n_bad) data[bad_x, bad_y] = np.random.choice([-1, 1], size=n_bad) * ( 10 + np.random.rand(n_bad) ) hdu = fits.PrimaryHDU(data=np.asarray(data, dtype="float32")) hdu.header["for_prof"] = "yes" hdu.header["bunit"] = "adu" path = TMPPATH.resolve() / base_name.format(num=num) hdu.writeto(path, overwrite=True) def run_memory_profile( n_files, sampling_interval, sigma_clip=False, combine_method=None, memory_limit=None, ): """ Try opening a bunch of files with a relatively low limit on the number of open files. Parameters ---------- n_files : int Number of files to combine. sampling_interval : float Time, in seconds, between memory samples. size : int, optional Size of one side of the image (the image is always square). sigma_clip : bool, optional If true, sigma clip the data before combining. combine_method : str, optional Should be one of the combine methods accepted by ccdproc.combine memory_limit : int, optional Cap on memory use during image combination. """ # Do a little input validation if n_files <= 0: raise ValueError("Argument 'n' must be a positive integer") proc = psutil.Process() print("Process ID is: ", proc.pid, flush=True) ic = ImageFileCollection(str(TMPPATH)) files = ic.files_filtered(for_prof="yes", include_path=True) kwargs = {"method": combine_method} if sigma_clip: kwargs.update( { "sigma_clip": True, "sigma_clip_low_thresh": 5, "sigma_clip_high_thresh": 5, "sigma_clip_func": np.ma.median, "sigma_clip_dev_func": median_absolute_deviation, } ) ccd = CCDData.read(files[0]) expected_img_size = _calculate_size_of_image(ccd, None) if memory_limit: kwargs["mem_limit"] = memory_limit pre_mem_use = memory_usage(-1, interval=sampling_interval, timeout=1) baseline = np.mean(pre_mem_use) print(f"Subtracting baseline memory before profile: {baseline}") mem_use = memory_usage( (combine, (files,), kwargs), interval=sampling_interval, timeout=None ) mem_use = [m - baseline for m in mem_use] return mem_use, expected_img_size if __name__ == "__main__": parser = ArgumentParser() parser.add_argument("number", type=int, help="Number of files to combine.") parser.add_argument( "--size", type=int, action="store", help="Size of one side of image to create. " "All images are square, so only give " "a single number for the size.", ) parser.add_argument( "--combine-method", "-c", choices=("average", "median"), help="Method to use to combine images.", ) parser.add_argument( "--memory-limit", type=int, help="Limit combination to this amount of memory" ) parser.add_argument( "--sigma-clip", action="store_true", help="If set, sigma-clip before combining. Clipping " "will be done with high/low limit of 5. " "The central function is the median, the " "deviation is the median_absolute_deviation.", ) parser.add_argument( "--sampling-freq", type=float, default=0.05, help="Time, in seconds, between memory samples.", ) parser.add_argument( "--frequent-gc", action="store_true", help="If set, perform garbage collection " "much more frequently than the default.", ) args = parser.parse_args() if args.frequent_gc: gc.set_threshold(10, 10, 10) print("Garbage collection thresholds: ", gc.get_threshold()) mem_use = run_memory_profile( args.number, args.sampling_freq, sigma_clip=args.sigma_clip, combine_method=args.combine_method, memory_limit=args.memory_limit, ) print("Max memory usage (MB): ", np.max(mem_use)) print("Baseline memory usage (MB): ", mem_use[0]) ccdproc-2.4.3/ccdproc/tests/run_profile.ipynb0000644000000000000000000001712313615410400016227 0ustar00{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import gc\n", "from copy import deepcopy\n", "\n", "%matplotlib inline \n", "from matplotlib import pyplot as plt\n", "import numpy as np\n", "\n", "try:\n", " from run_for_memory_profile import run_memory_profile, generate_fits_files\n", "except ImportError:\n", " raise ImportError('Please install memory_profiler before running this notebook.')\n", "\n", "from ccdproc.version import get_git_devstr\n", "from astropy import __version__ as apy_version" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('Astropy version: ', apy_version)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "image_size = 4000 # Square image, so 4000 x 4000\n", "num_files = 10\n", "sampling_interval = 0.01 # sec\n", "memory_limit = 1000000000 # bytes, roughly 1GB\n", "\n", "commit = get_git_devstr(sha=True)[:7]\n", "print(commit)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "generate_fits_files(num_files, size=image_size)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "runs = {\n", " 'average': {\n", " 'times': [],\n", " 'memory': [],\n", " 'image_size': 0.\n", " },\n", " 'median': {\n", " 'times': [],\n", " 'memory': [],\n", " 'image_size': 0.\n", " },\n", " 'sum': {\n", " 'times': [],\n", " 'memory': [],\n", " 'image_size': 0.\n", " }\n", "}\n", "runs_clip = deepcopy(runs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Seem to need to do one run before the profiling\n", "\n", "Every time the first run looks different than the rest, so we run one and throw it out." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "_, _ = run_memory_profile(num_files, sampling_interval, size=image_size, \n", " memory_limit=memory_limit, combine_method='average')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Memory profile without sigma clipping" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "n_repetitions = 4" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def run_them(runs, clipping=False):\n", " for combine_method in runs.keys():\n", " for _ in range(n_repetitions):\n", " mem_use, img_size = run_memory_profile(num_files, sampling_interval, size=image_size, \n", " memory_limit=memory_limit, combine_method=combine_method,\n", " sigma_clip=clipping)\n", " gc.collect()\n", " runs[combine_method]['times'].append(np.arange(len(mem_use)) * sampling_interval)\n", " runs[combine_method]['memory'].append(mem_use)\n", " runs[combine_method]['image_size'] = img_size\n", " runs[combine_method]['memory_limit'] = memory_limit\n", " runs[combine_method]['clipping'] = clipping" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "run_them(runs)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "styles = ['solid', 'dashed', 'dotted']" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(20, 10))\n", "\n", "for idx, method in enumerate(runs.keys()):\n", " style = styles[idx % len(styles)]\n", " for i, data in enumerate(zip(runs[method]['times'], runs[method]['memory'])):\n", " time, mem_use = data \n", " if i == 0:\n", " label = 'Memory use in {} combine (repeated runs same style)'.format(method)\n", " alpha = 1.0\n", " else:\n", " label = ''\n", " alpha = 0.4\n", " plt.plot(time, mem_use, linestyle=style, label=label, alpha=alpha)\n", "\n", "plt.vlines(-40 * sampling_interval, mem_use[0], mem_use[0] + memory_limit/1e6, colors='red', label='Memory use limit')\n", "plt.vlines(-20 * sampling_interval, mem_use[0], mem_use[0] + runs[method]['image_size']/1e6, label='size of one image')\n", "\n", "plt.grid()\n", "clipped = 'ON' if runs[method]['clipping'] else 'OFF'\n", "\n", "plt.title('ccdproc commit {}; {} repetitions per method; sigma_clip {}'.format(commit, n_repetitions, clipped),\n", " fontsize=20)\n", "plt.xlabel('Time (sec)', fontsize=20)\n", "plt.ylabel('Memory use (MB)', fontsize=20)\n", "\n", "plt.legend(fontsize=20)\n", "plt.savefig('commit_{}_reps_{}_clip_{}_memlim_{}GB.png'.format(commit, n_repetitions, clipped, memory_limit/1e9))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Memory profile with sigma clipping" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "run_them(runs_clip, clipping=True)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plt.figure(figsize=(20, 10))\n", "\n", "for idx, method in enumerate(runs_clip.keys()):\n", " style = styles[idx % len(styles)]\n", " for i, data in enumerate(zip(runs_clip[method]['times'], runs_clip[method]['memory'])):\n", " time, mem_use = data \n", " if i == 0:\n", " label = 'Memory use in {} combine (repeated runs same style)'.format(method)\n", " alpha = 1.0\n", " else:\n", " label = ''\n", " alpha = 0.4\n", " plt.plot(time, mem_use, linestyle=style, label=label, alpha=alpha)\n", "\n", "plt.vlines(-40 * sampling_interval, mem_use[0], mem_use[0] + memory_limit/1e6, colors='red', label='Memory use limit')\n", "plt.vlines(-20 * sampling_interval, mem_use[0], mem_use[0] + runs_clip[method]['image_size']/1e6, label='size of one image')\n", "\n", "plt.grid()\n", "clipped = 'ON' if runs_clip[method]['clipping'] else 'OFF'\n", "\n", "plt.title('ccdproc commit {}; {} repetitions per method; sigma_clip {}'.format(commit, n_repetitions, clipped),\n", " fontsize=20)\n", "plt.xlabel('Time (sec)', fontsize=20)\n", "plt.ylabel('Memory use (MB)', fontsize=20)\n", "\n", "plt.legend(fontsize=20)\n", "plt.savefig('commit_{}_reps_{}_clip_{}_memlim_{}GB.png'.format(commit, n_repetitions, clipped, memory_limit/1e9))" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.7" } }, "nbformat": 4, "nbformat_minor": 2 } ccdproc-2.4.3/ccdproc/tests/run_with_file_number_limit.py0000644000000000000000000002165113615410400020617 0ustar00import gc import mmap import sys from pathlib import Path from tempfile import TemporaryDirectory import numpy as np from astropy.io import fits # This bit of hackery ensures that we can see ccdproc from within # the test suite sys.path.append(str(Path().cwd())) from ccdproc import combine # Do not combine these into one statement. When all references are lost # to a TemporaryDirectory the directory is automatically deleted. _TMPDIR # creates a reference that will stick around. _TMPDIR = TemporaryDirectory() TMPPATH = Path(_TMPDIR.name) ALLOWED_EXTENSIONS = {"fits": "fits", "plain": "txt"} def generate_fits_files(number, size=None): if size is None: use_size = [250, 250] else: int_size = int(size) use_size = [int_size, int_size] base_name = "test-combine-{num:03d}." + ALLOWED_EXTENSIONS["fits"] for num in range(number): data = np.zeros(shape=use_size) hdu = fits.PrimaryHDU(data=data) hdu.header["bunit"] = "adu" name = base_name.format(num=num) path = TMPPATH / name hdu.writeto(path, overwrite=True) def generate_plain_files(number): for i in range(number): file = TMPPATH / (f"{i:03d}." + ALLOWED_EXTENSIONS["plain"]) file.write_bytes(np.random.random(100)) def open_files_with_open(kind): """ Open files with plain open. """ # Ensure the file references persist until end of script. Not really # necessary, but convenient while debugging the script. global fds fds = [] paths = TMPPATH.glob("**/*." + ALLOWED_EXTENSIONS[kind]) for p in paths: fds.append(p.open()) def open_files_as_mmap(kind): """ Open files as mmaps. """ # Ensure the file references persist until end of script. Not really # necessary, but convenient while debugging the script. global fds fds = [] paths = TMPPATH.glob("**/*." + ALLOWED_EXTENSIONS[kind]) for p in paths: with p.open() as f: fds.append(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_COPY)) def open_files_ccdproc_combine_chunk(kind): """ Open files indirectly as part of ccdproc.combine, ensuring that the task is broken into chunks. """ global combo paths = sorted(list(TMPPATH.glob("**/*." + ALLOWED_EXTENSIONS[kind]))) # We want to force combine to break the task into chunks even # if the task really would fit in memory; it is in that case that # we end up with too many open files. We'll open one file, determine # the size of the data in bytes, and set the memory limit to that. # That will mean lots of chunks (however many files there are plus one), # but lots of chunks is fine. with fits.open(paths[0]) as hdulist: array_size = hdulist[0].data.nbytes combo = combine(paths, mem_limit=array_size) def open_files_ccdproc_combine_nochunk(kind): """ Open files indirectly as part of ccdproc.combine, ensuring that the task is not broken into chunks. """ global combo paths = sorted(list(TMPPATH.glob("**/*." + ALLOWED_EXTENSIONS[kind]))) # We ensure there are no chunks by setting a memory limit large # enough to hold everything. with fits.open(paths[0]) as hdulist: array_size = hdulist[0].data.nbytes # Why 2x the number of files? To make absolutely sure we don't # end up chunking the job. array_size *= 2 * len(paths) combo = combine(paths) ALLOWED_OPENERS = { "open": open_files_with_open, "mmap": open_files_as_mmap, "combine-chunk": open_files_ccdproc_combine_chunk, "combine-nochunk": open_files_ccdproc_combine_nochunk, } def run_with_limit(n, kind="fits", size=None, overhead=6, open_method="mmap"): """ Try opening a bunch of files with a relatively low limit on the number of open files. Parameters ---------- n : int Limit on number of open files in this function. The number of files to create is calculated from this to be just below the maximum number of files controlled by this function that can be opened. kind : one of 'fits', 'plain', optional The type of file to generate. The plain files are intended mainly for testing this script, while the FITS files are for testing ccdproc.combine. size : int, optional Size of file to create. If the kind is 'plain; this is the size of the file, in bytes. If the kind is 'fits', this is the size of one side of the image (the image is always square). overhead : int, optional Number of open files to assume the OS is using for this process. The default value is chosen so that this succeeds on MacOS or Linux. Setting it to a value lower than default should cause a SystemExit exception to be raised because of too many open files. This is meant for testing that this script is actually testing something. Notes ----- .. warning:: You should run this in a subprocess. Running as part of a larger python process will lower the limit on the number of open files for that **entire python process** which will almost certainly lead to nasty side effects. """ # Keep the resource import here so that it is skipped on windows import resource # Do a little input validation if n <= 0: raise ValueError("Argument 'n' must be a positive integer") if kind not in ALLOWED_EXTENSIONS.keys(): raise ValueError( "Argument 'kind' must be one of " f"{ALLOWED_EXTENSIONS.keys()}" ) # Set the limit on the number of open files to n. The try/except # is the catch the case where this change would *increase*, rather than # decrease, the limit. That apparently can only be done by a superuser. try: resource.setrlimit(resource.RLIMIT_NOFILE, (n, n)) except ValueError as e: if "not allowed to raise maximum limit" not in str(e): raise max_n_this_process = resource.getrlimit(resource.RLIMIT_NOFILE) raise ValueError( "Maximum number of open " f"files is {max_n_this_process}" ) from e # The "-1" is to leave a little wiggle room. overhead is based on the # the number of open files that a process running on linux has open. # These typically include stdin and stout, and apparently others. n_files = n - 1 - overhead proc = psutil.Process() print("Process ID is: ", proc.pid, flush=True) print(f"Making {n_files} files") if kind == "plain": generate_plain_files(n_files) elif kind == "fits": generate_fits_files(n_files, size=size) # Print number of open files before we try opening anything for debugging # purposes. print(f"Before opening, files open is {len(proc.open_files())}", flush=True) print(" Note well: this number is different than what lsof reports.") try: ALLOWED_OPENERS[open_method](kind) # fds.append(p.open()) except OSError as e: # Capture the error and re-raise as a SystemExit because this is # run in a subprocess. This ensures that the original error message # is reported back to the calling process; we add on the number of # open files. raise SystemExit( str(e) + "; number of open files: " + f"{len(proc.open_files())}, with target {n_files}" ) from e else: print( "Opens succeeded, files currently open:", len(proc.open_files()), flush=True ) if __name__ == "__main__": from argparse import ArgumentParser import psutil parser = ArgumentParser() parser.add_argument("number", type=int, help="Limit on number of open files.") parser.add_argument( "--kind", action="store", default="plain", choices=ALLOWED_EXTENSIONS.keys(), help="Kind of file to generate for test; " "default is plain", ) parser.add_argument( "--overhead", type=int, action="store", help="Number of files to assume the OS is using.", default=6, ) parser.add_argument( "--open-by", action="store", default="mmap", choices=ALLOWED_OPENERS.keys(), help="How to open the files. Default is mmap", ) parser.add_argument( "--size", type=int, action="store", help="Size of one side of image to create. " "All images are square, so only give " "a single number for the size.", ) parser.add_argument( "--frequent-gc", action="store_true", help="If set, perform garbage collection " "much more frequently than the default.", ) args = parser.parse_args() if args.frequent_gc: gc.set_threshold(10, 10, 10) print("Garbage collection thresholds: ", gc.get_threshold()) run_with_limit( args.number, kind=args.kind, overhead=args.overhead, open_method=args.open_by, size=args.size, ) ccdproc-2.4.3/ccdproc/tests/test_bitfield.py0000644000000000000000000000431513615410400016032 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np import pytest from ccdproc.core import bitfield_to_boolean_mask def test_bitfield_not_integer(): with pytest.raises(TypeError): bitfield_to_boolean_mask(np.random.random((10, 10))) def test_bitfield_negative_flags(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(ValueError): bitfield_to_boolean_mask(bm, [-1]) def test_bitfield_non_poweroftwo_flags(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(ValueError): bitfield_to_boolean_mask(bm, [3]) def test_bitfield_flipbits_when_no_bits(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(TypeError): bitfield_to_boolean_mask(bm, None, flip_bits=1) def test_bitfield_flipbits_when_stringbits(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(TypeError): bitfield_to_boolean_mask(bm, "3", flip_bits=1) def test_bitfield_string_flag_flip_not_start_of_string(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(ValueError): bitfield_to_boolean_mask(bm, "1, ~4") def test_bitfield_string_flag_unbalanced_parens(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(ValueError): bitfield_to_boolean_mask(bm, "(1, 4))") def test_bitfield_string_flag_wrong_positioned_parens(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(ValueError): bitfield_to_boolean_mask(bm, "((1, )4)") def test_bitfield_string_flag_empty(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(ValueError): bitfield_to_boolean_mask(bm, "~") def test_bitfield_flag_non_integer(): bm = np.random.randint(0, 10, (10, 10)) with pytest.raises(TypeError): bitfield_to_boolean_mask(bm, [1.3]) def test_bitfield_duplicate_flag_throws_warning(): bm = np.random.randint(0, 10, (10, 10)) with pytest.warns(UserWarning): bitfield_to_boolean_mask(bm, [1, 1]) def test_bitfield_none_identical_to_strNone(): bm = np.random.randint(0, 10, (10, 10)) m1 = bitfield_to_boolean_mask(bm, None) m2 = bitfield_to_boolean_mask(bm, "None") np.testing.assert_array_equal(m1, m2) ccdproc-2.4.3/ccdproc/tests/test_ccdmask.py0000644000000000000000000002522513615410400015660 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np import pytest from astropy.nddata import CCDData from numpy.testing import assert_array_equal from ccdproc.core import ccdmask def test_ccdmask_no_ccddata(): # Fails when a simple list is given. with pytest.raises(ValueError): ccdmask([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) def test_ccdmask_not_2d(): # Fails when a CCDData has less than 2 dimensions with pytest.raises(ValueError): ccdmask(CCDData(np.ones(3), unit="adu")) # Fails when scalar with pytest.raises(ValueError): ccdmask(CCDData(np.array(10), unit="adu")) # Fails when more than 2d with pytest.raises(ValueError): ccdmask(CCDData(np.ones((3, 3, 3)), unit="adu")) def test_ccdmask_pixels(): # fmt: off flat1 = CCDData(np.array([[ 20044, 19829, 19936, 20162, 19948, 19965, 19919, 20004, 19951, 20002, 19926, 20151, 19886, 20014, 19928, 20025, 19921, 19996, 19912, 20017, 19969, 20103, 20161, 20110, 19977, 19922, 20004, 19802, 20079, 19981, 20083, 19871], [20068, 20204, 20085, 20027, 20103, 19866, 20089, 19914, 20160, 19884, 19956, 20095, 20004, 20075, 19899, 20016, 19995, 20178, 19963, 20030, 20055, 20005, 20073, 19969, 19958, 20040, 19979, 19938, 19986, 19957, 20172, 20054], [20099, 20180, 19912, 20050, 19930, 19930, 20036, 20006, 19833, 19984, 19879, 19815, 20105, 20011, 19949, 20062, 19837, 20070, 20047, 19855, 19956, 19928, 19878, 20102, 19940, 20001, 20082, 20080, 20019, 19991, 19919, 20121], [20014, 20262, 19953, 20077, 19928, 20271, 19962, 20048, 20011, 20054, 20112, 19931, 20125, 19899, 19993, 19939, 19916, 19998, 19921, 19949, 20246, 20160, 19881, 19863, 19874, 19979, 19989, 19901, 19850, 19931, 20001, 20167], [20131, 19991, 20073, 19945, 19980, 20021, 19938, 19964, 20002, 20177, 19888, 19901, 19919, 19977, 20280, 20035, 20045, 19849, 20169, 20074, 20113, 19993, 19965, 20026, 20018, 19966, 20023, 19965, 19962, 20082, 20027, 20145], [20106, 20025, 19846, 19865, 19913, 20046, 19998, 20037, 19986, 20048, 20005, 19790, 20011, 19985, 19959, 19882, 20085, 19978, 19881, 19960, 20111, 19936, 19983, 19863, 19819, 19896, 19968, 20134, 19824, 19990, 20146, 19886], [20162, 19997, 19966, 20110, 19822, 19923, 20029, 20129, 19936, 19882, 20077, 20112, 20040, 20051, 20177, 19763, 20097, 19898, 19832, 20061, 19919, 20056, 20010, 19929, 20010, 19995, 20124, 19965, 19922, 19860, 20021, 19989], [20088, 20104, 19956, 19959, 20018, 19948, 19836, 20107, 19920, 20117, 19882, 20039, 20206, 20067, 19784, 20087, 20117, 19990, 20242, 19861, 19923, 19779, 20024, 20024, 19981, 19915, 20017, 20053, 19932, 20179, 20062, 19908], [19993, 20047, 20008, 20172, 19977, 20054, 19980, 19952, 20138, 19940, 19995, 20029, 19888, 20191, 19958, 20007, 19938, 19959, 19933, 20139, 20069, 19905, 20101, 20086, 19904, 19807, 20131, 20048, 19927, 19905, 19939, 20030], [20040, 20051, 19997, 20013, 19942, 20130, 19983, 19603, 19934, 19944, 19961, 19979, 20164, 19855, 20157, 20010, 20020, 19902, 20134, 19971, 20228, 19967, 19879, 20022, 19915, 20063, 19768, 19976, 19860, 20041, 19955, 19984], [19807, 20066, 19986, 19999, 19975, 20115, 19998, 20056, 20059, 20016, 19970, 19964, 20053, 19975, 19985, 19973, 20041, 19918, 19875, 19997, 19954, 19777, 20117, 20248, 20034, 20019, 20018, 20058, 20027, 20121, 19909, 20094], [19890, 20018, 20032, 20058, 19909, 19906, 19812, 20206, 19908, 19767, 20127, 20015, 19959, 20026, 20021, 19964, 19824, 19934, 20147, 19984, 20026, 20168, 19992, 20175, 20040, 20208, 20077, 19897, 20037, 19996, 19998, 20019], [19966, 19897, 20062, 19914, 19780, 20004, 20029, 20140, 20057, 20134, 20125, 19973, 19894, 19929, 19876, 20135, 19981, 20057, 20015, 20113, 20107, 20115, 19924, 19987, 19926, 19885, 20013, 20058, 19950, 20155, 19825, 20092], [19889, 20046, 20113, 19991, 19829, 20180, 19949, 20011, 20014, 20123, 19980, 19770, 20086, 20041, 19957, 19949, 20026, 19918, 19777, 20062, 19862, 20085, 20090, 20122, 19692, 19937, 19897, 20018, 19935, 20037, 19946, 19998], [20001, 19940, 19994, 19835, 19959, 19895, 20017, 20002, 20007, 19851, 19900, 20044, 20354, 19814, 19869, 20148, 20001, 20143, 19778, 20146, 19975, 19859, 20008, 20041, 19937, 20072, 20203, 19778, 20027, 20075, 19877, 19999], [19753, 19866, 20037, 20149, 20020, 20071, 19955, 20164, 19837, 19967, 19959, 20163, 20003, 20127, 20065, 20118, 20104, 19839, 20124, 20057, 19943, 20023, 20138, 19996, 19910, 20048, 20070, 19833, 19913, 20012, 19897, 19983]]), unit='adu') flat2 = CCDData(np.array([[ 20129, 20027, 19945, 20085, 19951, 20015, 20102, 19957, 20100, 19865, 19878, 20111, 20047, 19882, 19929, 20079, 19937, 19999, 20109, 19929, 19985, 19970, 19941, 19868, 20191, 20142, 19948, 20079, 19975, 19949, 19972, 20053], [20075, 19980, 20035, 20014, 19865, 20058, 20091, 20030, 19931, 19806, 19990, 19902, 19895, 19789, 20079, 20048, 20040, 19968, 20049, 19946, 19982, 19865, 19766, 19903, 20025, 19916, 19904, 20128, 19865, 20103, 19864, 19832], [20008, 19989, 20032, 19891, 20063, 20061, 20179, 19920, 19960, 19655, 19897, 19943, 20015, 20123, 20009, 19940, 19876, 19964, 20097, 19814, 20086, 20096, 20030, 20140, 19903, 19858, 19978, 19817, 20107, 19893, 19988, 19956], [20105, 19873, 20003, 19671, 19993, 19981, 20234, 19976, 20079, 19882, 19982, 19959, 19882, 20103, 20008, 19960, 20084, 20025, 19864, 19969, 19945, 19979, 19937, 19965, 19981, 19957, 19906, 19959, 19839, 19679, 19988, 20154], [20053, 20152, 19858, 20134, 19867, 20027, 20024, 19884, 20015, 19904, 19992, 20137, 19981, 20147, 19814, 20035, 19992, 19921, 20007, 20103, 19920, 19889, 20182, 19964, 19859, 20016, 20011, 20203, 19761, 19954, 20151, 19973], [20029, 19863, 20217, 19819, 19984, 19950, 19914, 20028, 19980, 20033, 20016, 19796, 19901, 20027, 20078, 20136, 19995, 19915, 20014, 19920, 19996, 20216, 19939, 19967, 19949, 20023, 20024, 19949, 19949, 19902, 19980, 19895], [19962, 19872, 19926, 20047, 20136, 19944, 20151, 19956, 19958, 20054, 19942, 20010, 19972, 19936, 20062, 20259, 20230, 19927, 20004, 19963, 20095, 19866, 19942, 19958, 20149, 19956, 20000, 19979, 19949, 19892, 20249, 20050], [20019, 19999, 19954, 20095, 20045, 20002, 19761, 20187, 20113, 20048, 20117, 20002, 19938, 19968, 19993, 19995, 20094, 19913, 19963, 19813, 20040, 19950, 19992, 19958, 20043, 19925, 20036, 19930, 20057, 20055, 20040, 19937], [19958, 19984, 19842, 19990, 19985, 19958, 20070, 19850, 20026, 20047, 20081, 20094, 20048, 20048, 19917, 19893, 19766, 19765, 20109, 20067, 19905, 19870, 19832, 20019, 19868, 20075, 20132, 19916, 19944, 19840, 20140, 20117], [19995, 20122, 19998, 20039, 20125, 19879, 19911, 20010, 19944, 19994, 19903, 20057, 20021, 20139, 19972, 20026, 19922, 20132, 19976, 20025, 19948, 20038, 19807, 19809, 20145, 20003, 20090, 19848, 19884, 19936, 19997, 19944], [19839, 19990, 20005, 19826, 20070, 19987, 20015, 19835, 20083, 19908, 19910, 20218, 19960, 19937, 19987, 19808, 19893, 19929, 20004, 20055, 19973, 19794, 20242, 20082, 20110, 20058, 19876, 20042, 20064, 19966, 20041, 20015], [20048, 20203, 19855, 20011, 19888, 19926, 19973, 19893, 19986, 20152, 20030, 19880, 20012, 19848, 19959, 20002, 20027, 19935, 19975, 19905, 19932, 20190, 20188, 19903, 20012, 19943, 19954, 19891, 19947, 19939, 19974, 19808], [20102, 20041, 20013, 20097, 20101, 19859, 20011, 20144, 19920, 19880, 20134, 19963, 19980, 20090, 20027, 19822, 20051, 19903, 19784, 19845, 20014, 19974, 20043, 20141, 19968, 20055, 20066, 20045, 20182, 20104, 20008, 19999], [19932, 20023, 20042, 19894, 20070, 20015, 20172, 20024, 19988, 20181, 20180, 20023, 19978, 19989, 19976, 19870, 20152, 20003, 19984, 19903, 19904, 19940, 19990, 19922, 19911, 19976, 19841, 19946, 20273, 20085, 20142, 20122], [19959, 20071, 20020, 20037, 20024, 19967, 20044, 20009, 19997, 20045, 19995, 19831, 20035, 19976, 20049, 19958, 20021, 19887, 19961, 19928, 19805, 20173, 19928, 19939, 19826, 20096, 20078, 20100, 19935, 19942, 19969, 19941], [19876, 20056, 20071, 19886, 19979, 20174, 19978, 20037, 19933, 20184, 19948, 20034, 19896, 19905, 20138, 19870, 19936, 20085, 19971, 20063, 19936, 19941, 19928, 19937, 19970, 19931, 20036, 19965, 19855, 19949, 19965, 19821]]), unit='adu') # fmt: on target_mask = np.zeros(flat1.shape, dtype=bool) # No bad pixels in this scenario ratio = flat1.divide(flat2) mask = ccdmask(ratio, ncsig=9, nlsig=11) assert mask.shape == ratio.shape assert_array_equal(mask, target_mask) # Check again with different ncsig and nlsig ratio = flat1.divide(flat2) mask = ccdmask(ratio, ncsig=11, nlsig=15) assert mask.shape == ratio.shape assert_array_equal(mask, target_mask) # Add single bad pixel flat1.data[14][3] = 65535 flat2.data[14][3] = 1 ratio = flat1.divide(flat2) mask = ccdmask(ratio, ncsig=11, nlsig=15) target_mask[14][3] = True assert_array_equal(mask, target_mask) # Add single bad column flat1.data[:, 7] = 65535 flat2.data[:, 7] = 1 ratio = flat1.divide(flat2) target_mask[:, 7] = True mask = ccdmask(ratio, ncsig=11, nlsig=15) assert_array_equal(mask, target_mask) mask = ccdmask(ratio, ncsig=11, nlsig=15, byblocks=True) assert_array_equal(mask, target_mask) mask = ccdmask(ratio, ncsig=11, nlsig=15, findbadcolumns=True) assert_array_equal(mask, target_mask) mask = ccdmask(ratio, ncsig=11, nlsig=15, findbadcolumns=True, byblocks=True) assert_array_equal(mask, target_mask) # Add bad column with gaps flat1.data[0:8, 2] = 65535 flat1.data[11:, 2] = 65535 flat2.data[0:8, 2] = 1 flat2.data[11:, 2] = 1 ratio = flat1.divide(flat2) mask = ccdmask(ratio, ncsig=11, nlsig=15, findbadcolumns=False) target_mask[0:8, 2] = True target_mask[11:, 2] = True assert_array_equal(mask, target_mask) mask = ccdmask(ratio, ncsig=11, nlsig=15, findbadcolumns=True) target_mask[:, 2] = True assert_array_equal(mask, target_mask) ccdproc-2.4.3/ccdproc/tests/test_ccdproc.py0000644000000000000000000012134413615410400015667 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import astropy import astropy.units as u import numpy as np import pytest import skimage from astropy.io import fits from astropy.modeling import models from astropy.nddata import CCDData, StdDevUncertainty from astropy.units.quantity import Quantity from astropy.utils.exceptions import AstropyUserWarning from astropy.wcs import WCS from numpy.testing import assert_array_equal from ccdproc.core import ( Keyword, _blkavg, ccd_process, cosmicray_lacosmic, cosmicray_median, create_deviation, flat_correct, gain_correct, subtract_bias, subtract_dark, subtract_overscan, transform_image, trim_image, wcs_project, ) from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func try: from ..core import block_average, block_reduce, block_replicate HAS_BLOCK_X_FUNCS = True except ImportError: HAS_BLOCK_X_FUNCS = False _NUMPY_COPY_IF_NEEDED = False if np.__version__.startswith("1.") else None # Test creating deviation # Success expected if u_image * u_gain = u_readnoise @pytest.mark.parametrize( "u_image,u_gain,u_readnoise,expect_success", [ (u.electron, None, u.electron, True), (u.electron, u.electron, u.electron, False), (u.adu, u.electron / u.adu, u.electron, True), (u.electron, None, u.dimensionless_unscaled, False), (u.electron, u.dimensionless_unscaled, u.electron, True), (u.adu, u.dimensionless_unscaled, u.electron, False), (u.adu, u.photon / u.adu, u.electron, False), ], ) def test_create_deviation(u_image, u_gain, u_readnoise, expect_success): ccd_data = ccd_data_func(data_size=10, data_mean=100) ccd_data.unit = u_image if u_gain is not None: gain = 2.0 * u_gain else: gain = None readnoise = 5 * u_readnoise if expect_success: ccd_var = create_deviation(ccd_data, gain=gain, readnoise=readnoise) assert ccd_var.uncertainty.array.shape == (10, 10) assert ccd_var.uncertainty.array.size == 100 assert ccd_var.uncertainty.array.dtype == np.dtype(float) if gain is not None: expected_var = np.sqrt(2 * ccd_data.data + 5**2) / 2 else: expected_var = np.sqrt(ccd_data.data + 5**2) np.testing.assert_array_equal(ccd_var.uncertainty.array, expected_var) assert ccd_var.unit == ccd_data.unit # Uncertainty should *not* have any units -- does it? with pytest.raises(AttributeError): ccd_var.uncertainty.array.unit # noqa B018 useless expression else: with pytest.raises(u.UnitsError): ccd_var = create_deviation(ccd_data, gain=gain, readnoise=readnoise) def test_create_deviation_from_negative(): ccd_data = ccd_data_func(data_mean=0, data_scale=10) ccd_data.unit = u.electron readnoise = 5 * u.electron ccd_var = create_deviation( ccd_data, gain=None, readnoise=readnoise, disregard_nan=False ) np.testing.assert_array_equal( ccd_data.data < 0, np.isnan(ccd_var.uncertainty.array) ) def test_create_deviation_from_negative_2(): ccd_data = ccd_data_func(data_mean=0, data_scale=10) ccd_data.unit = u.electron readnoise = 5 * u.electron ccd_var = create_deviation( ccd_data, gain=None, readnoise=readnoise, disregard_nan=True ) mask = ccd_data.data < 0 ccd_data.data[mask] = 0 expected_var = np.sqrt(ccd_data.data + readnoise.value**2) np.testing.assert_array_equal(ccd_var.uncertainty.array, expected_var) def test_create_deviation_keywords_must_have_unit(): ccd_data = ccd_data_func() # Gain must have units if provided with pytest.raises(TypeError): create_deviation(ccd_data, gain=3) # Readnoise must have units with pytest.raises(TypeError): create_deviation(ccd_data, readnoise=5) # Readnoise must be provided with pytest.raises(ValueError): create_deviation(ccd_data) # Tests for overscan @pytest.mark.parametrize("data_rectangle", [False, True]) @pytest.mark.parametrize( "median,transpose", [ (False, False), (False, True), (True, False), ], ) def test_subtract_overscan(median, transpose, data_rectangle): ccd_data = ccd_data_func() # Make data non-square if desired if data_rectangle: ccd_data.data = ccd_data.data[:, :-30] # Create the overscan region oscan = 300.0 oscan_region = (slice(None), slice(0, 10)) # Indices 0 through 9 fits_section = "[1:10, :]" science_region = (slice(None), slice(10, None)) overscan_axis = 1 if transpose: # Put overscan in first axis, not second, a test for #70 oscan_region = oscan_region[::-1] fits_section = "[:, 1:10]" science_region = science_region[::-1] overscan_axis = 0 ccd_data.data[oscan_region] = oscan # Add a fake sky background so the "science" part of the image has a # different average than the "overscan" part. sky = 10.0 original_mean = ccd_data.data[science_region].mean() ccd_data.data[science_region] += oscan + sky # Test once using the overscan argument to specify the overscan region ccd_data_overscan = subtract_overscan( ccd_data, overscan=ccd_data[oscan_region], overscan_axis=overscan_axis, median=median, model=None, ) # Is the mean of the "science" region the sum of sky and the mean the # "science" section had before backgrounds were added? np.testing.assert_almost_equal( ccd_data_overscan.data[science_region].mean(), sky + original_mean ) # Is the overscan region zero? assert (ccd_data_overscan.data[oscan_region] == 0).all() # Now do what should be the same subtraction, with the overscan specified # with the fits_section ccd_data_fits_section = subtract_overscan( ccd_data, overscan_axis=overscan_axis, fits_section=fits_section, median=median, model=None, ) # Is the mean of the "science" region the sum of sky and the mean the # "science" section had before backgrounds were added? np.testing.assert_almost_equal( ccd_data_fits_section.data[science_region].mean(), sky + original_mean ) # Is the overscan region zero? assert (ccd_data_fits_section.data[oscan_region] == 0).all() # Do both ways of subtracting overscan give exactly the same result? np.testing.assert_array_equal( ccd_data_overscan[science_region], ccd_data_fits_section[science_region] ) # Set overscan_axis to None, and let the routine figure out the axis. # This should lead to the same results as before. ccd_data_overscan_auto = subtract_overscan( ccd_data, overscan_axis=None, overscan=ccd_data[oscan_region], median=median, model=None, ) np.testing.assert_almost_equal( ccd_data_overscan_auto.data[science_region].mean(), sky + original_mean ) # Use overscan_axis=None with a FITS section ccd_data_fits_section_overscan_auto = subtract_overscan( ccd_data, overscan_axis=None, fits_section=fits_section, median=median, model=None, ) np.testing.assert_almost_equal( ccd_data_fits_section_overscan_auto.data[science_region].mean(), sky + original_mean, ) # Overscan_axis should be 1 for a square overscan region # This test only works for a non-square data region, but the # default has the wrong axis. if data_rectangle: ccd_data.data = ccd_data.data.T oscan_region = (slice(None), slice(0, -30)) science_region = (slice(None), slice(-30, None)) ccd_data_square_overscan_auto = subtract_overscan( ccd_data, overscan_axis=None, overscan=ccd_data[oscan_region], median=median, model=None, ) ccd_data_square = subtract_overscan( ccd_data, overscan_axis=1, overscan=ccd_data[oscan_region], median=median, model=None, ) np.testing.assert_allclose(ccd_data_square_overscan_auto, ccd_data_square) # A more substantial test of overscan modeling @pytest.mark.parametrize("transpose", [True, False]) def test_subtract_overscan_model(transpose): ccd_data = ccd_data_func() # Create the overscan region size = ccd_data.shape[0] oscan_region = (slice(None), slice(0, 10)) science_region = (slice(None), slice(10, None)) yscan, xscan = np.mgrid[0:size, 0:size] / 10.0 + 300.0 if transpose: oscan_region = oscan_region[::-1] science_region = science_region[::-1] scan = xscan overscan_axis = 0 else: overscan_axis = 1 scan = yscan original_mean = ccd_data.data[science_region].mean() ccd_data.data[oscan_region] = 0.0 # Only want overscan in that region ccd_data.data = ccd_data.data + scan ccd_data = subtract_overscan( ccd_data, overscan=ccd_data[oscan_region], overscan_axis=overscan_axis, median=False, model=models.Polynomial1D(2), ) np.testing.assert_almost_equal(ccd_data.data[science_region].mean(), original_mean) # Set the overscan_axis explicitly to None, and let the routine # figure it out. ccd_data = subtract_overscan( ccd_data, overscan=ccd_data[oscan_region], overscan_axis=None, median=False, model=models.Polynomial1D(2), ) np.testing.assert_almost_equal(ccd_data.data[science_region].mean(), original_mean) def test_subtract_overscan_fails(): ccd_data = ccd_data_func() # Do we get an error if the *image* is neither CCDData nor an array? with pytest.raises(TypeError): subtract_overscan(3, np.zeros((5, 5))) # Do we get an error if the *overscan* is not an image or an array? with pytest.raises(TypeError): subtract_overscan(np.zeros((10, 10)), 3, median=False, model=None) # Do we get an error if we specify both overscan and fits_section? with pytest.raises(TypeError): subtract_overscan(ccd_data, overscan=ccd_data[0:10], fits_section="[1:10]") # Do we raise an error if we specify neither overscan nor fits_section? with pytest.raises(TypeError): subtract_overscan(ccd_data) # Does a fits_section which is not a string raise an error? with pytest.raises(TypeError): subtract_overscan(ccd_data, fits_section=5) def test_trim_image_fits_section_requires_string(): ccd_data = ccd_data_func() with pytest.raises(TypeError): trim_image(ccd_data, fits_section=5) @pytest.mark.parametrize("mask_data, uncertainty", [(False, False), (True, True)]) def test_trim_image_fits_section(mask_data, uncertainty): ccd_data = ccd_data_func(data_size=50) if mask_data: ccd_data.mask = np.zeros_like(ccd_data) if uncertainty: err = np.random.normal(size=ccd_data.shape) ccd_data.uncertainty = StdDevUncertainty(err) trimmed = trim_image(ccd_data, fits_section="[20:40,:]") # FITS reverse order, bounds are inclusive and starting index is 1-based assert trimmed.shape == (50, 21) np.testing.assert_array_equal(trimmed.data, ccd_data[:, 19:40]) if mask_data: assert trimmed.shape == trimmed.mask.shape if uncertainty: assert trimmed.shape == trimmed.uncertainty.array.shape def test_trim_image_no_section(): ccd_data = ccd_data_func(data_size=50) trimmed = trim_image(ccd_data[:, 19:40]) assert trimmed.shape == (50, 21) np.testing.assert_array_equal(trimmed.data, ccd_data[:, 19:40]) def test_trim_with_wcs_alters_wcs(): ccd_data = ccd_data_func() # WCS construction example pulled form astropy.wcs docs wcs = WCS(naxis=2) wcs.wcs.crpix = np.array(ccd_data.shape) / 2 wcs.wcs.cdelt = np.array([-0.066667, 0.066667]) wcs.wcs.crval = [0, -90] wcs.wcs.ctype = ["RA---AIR", "DEC--AIR"] wcs.wcs.set_pv([(2, 1, 45.0)]) ccd_wcs = CCDData(ccd_data, wcs=wcs) # The trim below should subtract 10 from the 2nd element of crpix. # (Second element because the FITS convention for index ordering is # opposite that of python) trimmed = trim_image(ccd_wcs[10:, :]) assert trimmed.wcs.wcs.crpix[1] == wcs.wcs.crpix[1] - 10 def test_subtract_bias(): ccd_data = ccd_data_func() data_avg = ccd_data.data.mean() bias_level = 5.0 ccd_data.data = ccd_data.data + bias_level ccd_data.header["key"] = "value" master_bias_array = np.zeros_like(ccd_data.data) + bias_level master_bias = CCDData(master_bias_array, unit=ccd_data.unit) no_bias = subtract_bias(ccd_data, master_bias, add_keyword=None) # Does the data we are left with have the correct average? np.testing.assert_almost_equal(no_bias.data.mean(), data_avg) # With logging turned off, metadata should not change assert no_bias.header == ccd_data.header del no_bias.header["key"] assert "key" in ccd_data.header assert no_bias.header is not ccd_data.header def test_subtract_bias_fails(): ccd_data = ccd_data_func(data_size=50) # Should fail if shapes don't match bias = CCDData(np.array([200, 200]), unit=u.adu) with pytest.raises(ValueError): subtract_bias(ccd_data, bias) # Should fail because units don't match bias = CCDData(np.zeros_like(ccd_data), unit=u.meter) with pytest.raises(u.UnitsError): subtract_bias(ccd_data, bias) @pytest.mark.parametrize("exposure_keyword", [True, False]) @pytest.mark.parametrize("explicit_times", [True, False]) @pytest.mark.parametrize("scale", [True, False]) def test_subtract_dark(explicit_times, scale, exposure_keyword): ccd_data = ccd_data_func() exptime = 30.0 exptime_key = "exposure" exposure_unit = u.second dark_level = 1.7 master_dark_data = np.zeros_like(ccd_data.data) + dark_level master_dark = CCDData(master_dark_data, unit=u.adu) master_dark.header[exptime_key] = 2 * exptime dark_exptime = master_dark.header[exptime_key] ccd_data.header[exptime_key] = exptime dark_exposure_unit = exposure_unit if explicit_times: # Test case when units of dark and data exposures are different dark_exposure_unit = u.minute dark_sub = subtract_dark( ccd_data, master_dark, dark_exposure=dark_exptime * dark_exposure_unit, data_exposure=exptime * exposure_unit, scale=scale, add_keyword=None, ) elif exposure_keyword: key = Keyword(exptime_key, unit=u.second) dark_sub = subtract_dark( ccd_data, master_dark, exposure_time=key, scale=scale, add_keyword=None ) else: dark_sub = subtract_dark( ccd_data, master_dark, exposure_time=exptime_key, exposure_unit=u.second, scale=scale, add_keyword=None, ) dark_scale = 1.0 if scale: dark_scale = float( (exptime / dark_exptime) * (exposure_unit / dark_exposure_unit) ) np.testing.assert_array_equal( ccd_data.data - dark_scale * dark_level, dark_sub.data ) # Headers should have the same content...do they? assert dark_sub.header == ccd_data.header # But the headers should not be the same object -- a copy was made assert dark_sub.header is not ccd_data.header def test_subtract_dark_fails(): ccd_data = ccd_data_func() # None of these tests check a result so the content of the master # can be anything. ccd_data.header["exptime"] = 30.0 master = ccd_data.copy() # Do we fail if we give one of dark_exposure, data_exposure but not both? with pytest.raises(TypeError): subtract_dark(ccd_data, master, dark_exposure=30 * u.second) with pytest.raises(TypeError): subtract_dark(ccd_data, master, data_exposure=30 * u.second) # Do we fail if we supply dark_exposure and data_exposure and exposure_time with pytest.raises(TypeError): subtract_dark( ccd_data, master, dark_exposure=10 * u.second, data_exposure=10 * u.second, exposure_time="exptime", ) # Fail if we supply none of the exposure-related arguments? with pytest.raises(TypeError): subtract_dark(ccd_data, master) # Fail if we supply exposure time but not a unit? with pytest.raises(TypeError): subtract_dark(ccd_data, master, exposure_time="exptime") # Fail if ccd_data or master are not CCDData objects? with pytest.raises(TypeError): subtract_dark(ccd_data.data, master, exposure_time="exptime") with pytest.raises(TypeError): subtract_dark(ccd_data, master.data, exposure_time="exptime") # Fail if units do not match... # ...when there is no scaling? master = CCDData(ccd_data) master.unit = u.meter with pytest.raises(u.UnitsError) as e: subtract_dark(ccd_data, master, exposure_time="exptime", exposure_unit=u.second) assert "uncalibrated image" in str(e.value) # Fail when the arrays are not the same size with pytest.raises(ValueError): small_master = CCDData(ccd_data) small_master.data = np.zeros((1, 1)) subtract_dark(ccd_data, small_master) def test_unit_mismatch_behaves_as_expected(): ccd_data = ccd_data_func() """ Test to alert us to any changes in how errors are raised in astropy when units do not match. """ bad_unit = ccd_data.copy() bad_unit.unit = u.meter if astropy.__version__.startswith("1.0"): expected_error = ValueError expected_message = "operand units" else: expected_error = u.UnitConversionError # Make this an empty string, which always matches. In this case # we are really only checking by the type of error raised. expected_message = "" # Did we raise the right error? with pytest.raises(expected_error) as e: ccd_data.subtract(bad_unit) # Was the error message as expected? assert expected_message in str(e.value) # Test for flat correction def test_flat_correct(): ccd_data = ccd_data_func(data_scale=10) # Add metadata to header for a test below... ccd_data.header["my_key"] = 42 size = ccd_data.shape[0] # create the flat, with some scatter data = 2 * np.random.normal(loc=1.0, scale=0.05, size=(size, size)) flat = CCDData(data, meta=fits.header.Header(), unit=ccd_data.unit) flat_data = flat_correct(ccd_data, flat, add_keyword=None) # Check that the flat was normalized # Should be the case that flat * flat_data = ccd_data * flat.data.mean # if the normalization was done correctly. np.testing.assert_almost_equal( (flat_data.data * flat.data).mean(), ccd_data.data.mean() * flat.data.mean() ) np.testing.assert_allclose( ccd_data.data / flat_data.data, flat.data / flat.data.mean() ) # Check that metadata is unchanged (since logging is turned off) assert flat_data.header == ccd_data.header # Test for flat correction with min_value def test_flat_correct_min_value(): ccd_data = ccd_data_func() size = ccd_data.shape[0] # Create the flat data = 2 * np.random.normal(loc=1.0, scale=0.05, size=(size, size)) flat = CCDData(data, meta=fits.header.Header(), unit=ccd_data.unit) flat_orig_data = flat.data.copy() min_value = 2.1 # Should replace some, but not all, values flat_corrected_data = flat_correct(ccd_data, flat, min_value=min_value) flat_with_min = flat.copy() flat_with_min.data[flat_with_min.data < min_value] = min_value # Check that the flat was normalized. The asserts below, which look a # little odd, are correctly testing that # flat_corrected_data = ccd_data / (flat_with_min / mean(flat_with_min)) np.testing.assert_almost_equal( (flat_corrected_data.data * flat_with_min.data).mean(), (ccd_data.data * flat_with_min.data.mean()).mean(), ) np.testing.assert_allclose( ccd_data.data / flat_corrected_data.data, flat_with_min.data / flat_with_min.data.mean(), ) # Test that flat is not modified. assert (flat_orig_data == flat.data).all() assert flat_orig_data is not flat.data def test_flat_correct_norm_value(): ccd_data = ccd_data_func(data_scale=10) # Test flat correction with mean value that is different than # the mean of the flat frame. # Create the flat, with some scatter # Note that mean value of flat is set below and is different than # the mean of the flat data. flat_mean = 5.0 data = np.random.normal(loc=1.0, scale=0.05, size=ccd_data.shape) flat = CCDData(data, meta=fits.Header(), unit=ccd_data.unit) flat_data = flat_correct(ccd_data, flat, add_keyword=None, norm_value=flat_mean) # Check that the flat was normalized # Should be the case that flat * flat_data = ccd_data * flat_mean # if the normalization was done correctly. np.testing.assert_almost_equal( (flat_data.data * flat.data).mean(), ccd_data.data.mean() * flat_mean ) np.testing.assert_allclose(ccd_data.data / flat_data.data, flat.data / flat_mean) def test_flat_correct_norm_value_bad_value(): ccd_data = ccd_data_func() # Test that flat_correct raises the appropriate error if # it is given a bad norm_value. Bad means <=0. # Create the flat, with some scatter data = np.random.normal(loc=1.0, scale=0.05, size=ccd_data.shape) flat = CCDData(data, meta=fits.Header(), unit=ccd_data.unit) with pytest.raises(ValueError) as e: flat_correct(ccd_data, flat, add_keyword=None, norm_value=-7) assert "norm_value must be" in str(e.value) # Test for deviation and for flat correction def test_flat_correct_deviation(): ccd_data = ccd_data_func(data_scale=10, data_mean=300) size = ccd_data.shape[0] ccd_data.unit = u.electron ccd_data = create_deviation(ccd_data, readnoise=5 * u.electron) # Create the flat data = 2 * np.ones((size, size)) flat = CCDData(data, meta=fits.header.Header(), unit=ccd_data.unit) flat = create_deviation(flat, readnoise=0.5 * u.electron) ccd_data = flat_correct(ccd_data, flat) # Test the uncertainty on the data after flat correction def test_flat_correct_data_uncertainty(): # Regression test for #345 dat = CCDData(np.ones([100, 100]), unit="adu", uncertainty=np.ones([100, 100])) # Note flat is set to 10, error, if present, is set to one. flat = CCDData(10 * np.ones([100, 100]), unit="adu") res = flat_correct(dat, flat) assert (res.data == dat.data).all() assert (res.uncertainty.array == dat.uncertainty.array).all() # Tests for gain correction def test_gain_correct(): ccd_data = ccd_data_func() init_data = ccd_data.data gain_data = gain_correct(ccd_data, gain=3, add_keyword=None) assert_array_equal(gain_data.data, 3 * init_data) assert ccd_data.meta == gain_data.meta def test_gain_correct_quantity(): ccd_data = ccd_data_func() init_data = ccd_data.data g = Quantity(3, u.electron / u.adu) ccd_data = gain_correct(ccd_data, gain=g) assert_array_equal(ccd_data.data, 3 * init_data) assert ccd_data.unit == u.electron # Test transform is ccd def test_transform_isccd(): with pytest.raises(TypeError): transform_image(1, 1) # Test function is callable def test_transform_isfunc(): ccd_data = ccd_data_func() with pytest.raises(TypeError): transform_image(ccd_data, 1) # Test warning is issue if WCS information is available def test_catch_transform_wcs_warning(): ccd_data = ccd_data_func() def tran(arr): return 10 * arr # No warning. transform_image(ccd_data, tran) # Issue warning when data has WCS. ccd_data.wcs = wcs_for_testing(ccd_data.shape) with pytest.warns(UserWarning, match="WCS information may be incorrect"): transform_image(ccd_data, tran) @pytest.mark.parametrize("mask_data, uncertainty", [(False, False), (True, True)]) def test_transform_image(mask_data, uncertainty): ccd_data = ccd_data_func(data_size=50) if mask_data: ccd_data.mask = np.zeros_like(ccd_data) ccd_data.mask[10, 10] = 1 if uncertainty: err = np.random.normal(size=ccd_data.shape) ccd_data.uncertainty = StdDevUncertainty(err) def tran(arr): return 10 * arr tran = transform_image(ccd_data, tran) assert_array_equal(10 * ccd_data.data, tran.data) if mask_data: assert tran.shape == tran.mask.shape assert_array_equal(ccd_data.mask, tran.mask) if uncertainty: assert tran.shape == tran.uncertainty.array.shape assert_array_equal(10 * ccd_data.uncertainty.array, tran.uncertainty.array) # Test block_reduce and block_replicate wrapper @pytest.mark.skipif(not HAS_BLOCK_X_FUNCS, reason="needs astropy >= 1.1.x") @pytest.mark.skipif( (skimage.__version__ < "0.14.2") and ("dev" in np.__version__), reason="Incompatibility between scikit-image " "and numpy 1.16", ) def test_block_reduce(): ccd = CCDData( np.ones((4, 4)), unit="adu", meta={"testkw": 1}, mask=np.zeros((4, 4), dtype=bool), uncertainty=StdDevUncertainty(np.ones((4, 4))), ) with pytest.warns(AstropyUserWarning) as w: ccd_summed = block_reduce(ccd, (2, 2)) assert len(w) == 1 assert "following attributes were set" in str(w[0].message) assert isinstance(ccd_summed, CCDData) assert np.all(ccd_summed.data == 4) assert ccd_summed.data.shape == (2, 2) assert ccd_summed.unit == u.adu # Other attributes are set to None. In case the function is modified to # work on these attributes correctly those tests need to be updated! assert ccd_summed.meta == {"testkw": 1} assert ccd_summed.mask is None assert ccd_summed.uncertainty is None # Make sure meta is copied ccd_summed.meta["testkw2"] = 10 assert "testkw2" not in ccd.meta @pytest.mark.skipif(not HAS_BLOCK_X_FUNCS, reason="needs astropy >= 1.1.x") @pytest.mark.skipif( (skimage.__version__ < "0.14.2") and ("dev" in np.__version__), reason="Incompatibility between scikit-image " "and numpy 1.16", ) def test_block_average(): ccd = CCDData( np.ones((4, 4)), unit="adu", meta={"testkw": 1}, mask=np.zeros((4, 4), dtype=bool), uncertainty=StdDevUncertainty(np.ones((4, 4))), ) ccd.data[::2, ::2] = 2 with pytest.warns(AstropyUserWarning) as w: ccd_avgd = block_average(ccd, (2, 2)) assert len(w) == 1 assert "following attributes were set" in str(w[0].message) assert isinstance(ccd_avgd, CCDData) assert np.all(ccd_avgd.data == 1.25) assert ccd_avgd.data.shape == (2, 2) assert ccd_avgd.unit == u.adu # Other attributes are set to None. In case the function is modified to # work on these attributes correctly those tests need to be updated! assert ccd_avgd.meta == {"testkw": 1} assert ccd_avgd.mask is None assert ccd_avgd.wcs is None assert ccd_avgd.uncertainty is None # Make sure meta is copied ccd_avgd.meta["testkw2"] = 10 assert "testkw2" not in ccd.meta @pytest.mark.skipif(not HAS_BLOCK_X_FUNCS, reason="needs astropy >= 1.1.x") def test_block_replicate(): ccd = CCDData( np.ones((4, 4)), unit="adu", meta={"testkw": 1}, mask=np.zeros((4, 4), dtype=bool), uncertainty=StdDevUncertainty(np.ones((4, 4))), ) with pytest.warns(AstropyUserWarning) as w: ccd_repl = block_replicate(ccd, (2, 2)) assert len(w) == 1 assert "following attributes were set" in str(w[0].message) assert isinstance(ccd_repl, CCDData) assert np.all(ccd_repl.data == 0.25) assert ccd_repl.data.shape == (8, 8) assert ccd_repl.unit == u.adu # Other attributes are set to None. In case the function is modified to # work on these attributes correctly those tests need to be updated! assert ccd_repl.meta == {"testkw": 1} assert ccd_repl.mask is None assert ccd_repl.wcs is None assert ccd_repl.uncertainty is None # Make sure meta is copied ccd_repl.meta["testkw2"] = 10 assert "testkw2" not in ccd.meta # Test blockaveraging ndarray def test__blkavg_ndarray(): with pytest.raises(TypeError): _blkavg(1, (5, 5)) # Test rebinning dimensions def test__blkavg_dimensions(): ccd_data = ccd_data_func(data_size=10) with pytest.raises(ValueError): _blkavg(ccd_data.data, (5,)) # Test blkavg works def test__blkavg_larger(): ccd_data = ccd_data_func(data_size=20) a = ccd_data.data b = _blkavg(a, (10, 10)) assert b.shape == (10, 10) np.testing.assert_almost_equal(b.sum(), 0.25 * a.sum()) # Test overscan changes def test__overscan_schange(): ccd_data = ccd_data_func() old_data = ccd_data.copy() new_data = subtract_overscan(ccd_data, overscan=ccd_data[:, 1], overscan_axis=0) assert not np.allclose(old_data.data, new_data.data) np.testing.assert_array_equal(old_data.data, ccd_data.data) def test_create_deviation_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() _ = create_deviation( ccd_data, gain=5 * u.electron / u.adu, readnoise=10 * u.electron ) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_cosmicray_median_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() error = np.zeros_like(ccd_data) with np.errstate(invalid="ignore", divide="ignore"): _ = cosmicray_median( ccd_data, error_image=error, thresh=5, mbox=11, gbox=0, rbox=0 ) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_cosmicray_lacosmic_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() _ = cosmicray_lacosmic(ccd_data) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_flat_correct_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() flat = CCDData(np.zeros_like(ccd_data), unit=ccd_data.unit) with np.errstate(invalid="ignore"): _ = flat_correct(ccd_data, flat=flat) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_gain_correct_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() _ = gain_correct(ccd_data, gain=1, gain_unit=ccd_data.unit) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_subtract_bias_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() master_frame = CCDData(np.zeros_like(ccd_data), unit=ccd_data.unit) _ = subtract_bias(ccd_data, master=master_frame) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_trim_image_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() _ = trim_image(ccd_data, fits_section=None) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_transform_image_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() with np.errstate(invalid="ignore"): _ = transform_image(ccd_data, np.sqrt) np.testing.assert_array_equal(original.data, ccd_data) assert original.unit == ccd_data.unit def wcs_for_testing(shape): # Set up a simple WCS, details are cut/pasted from astropy WCS docs, # mostly. CRPIX is set to the center of shape, rounded down. # Create a new WCS object. The number of axes must be set # from the start w = WCS(naxis=2) # Set up an "Airy's zenithal" projection # Vector properties may be set with Python lists, or Numpy arrays w.wcs.crpix = [shape[0] // 2, shape[1] // 2] w.wcs.cdelt = np.array([-0.066667, 0.066667]) w.wcs.crval = [0, -90] w.wcs.ctype = ["RA---AIR", "DEC--AIR"] w.wcs.set_pv([(2, 1, 45.0)]) return w def test_wcs_project_onto_same_wcs(): ccd_data = ccd_data_func() # The trivial case, same WCS, no mask. target_wcs = wcs_for_testing(ccd_data.shape) ccd_data.wcs = wcs_for_testing(ccd_data.shape) new_ccd = wcs_project(ccd_data, target_wcs) # Make sure new image has correct WCS. assert new_ccd.wcs.wcs.compare(target_wcs.wcs) # Make sure data matches within some reasonable tolerance. np.testing.assert_allclose(ccd_data.data, new_ccd.data, rtol=1e-5) def test_wcs_project_onto_same_wcs_remove_headers(): ccd_data = ccd_data_func() # Remove an example WCS keyword from the header target_wcs = wcs_for_testing(ccd_data.shape) ccd_data.wcs = wcs_for_testing(ccd_data.shape) ccd_data.header = ccd_data.wcs.to_header() new_ccd = wcs_project(ccd_data, target_wcs) for k in ccd_data.wcs.to_header(): assert k not in new_ccd.header def test_wcs_project_onto_shifted_wcs(): ccd_data = ccd_data_func() # Just make the target WCS the same as the initial with the center # pixel shifted by 1 in x and y. ccd_data.wcs = wcs_for_testing(ccd_data.shape) target_wcs = wcs_for_testing(ccd_data.shape) target_wcs.wcs.crpix += [1, 1] ccd_data.mask = np.random.choice([0, 1], size=ccd_data.shape) new_ccd = wcs_project(ccd_data, target_wcs) # Make sure new image has correct WCS. assert new_ccd.wcs.wcs.compare(target_wcs.wcs) # Make sure data matches within some reasonable tolerance, keeping in mind # that the pixels should all be shifted. masked_input = np.ma.array(ccd_data.data, mask=ccd_data.mask) masked_output = np.ma.array(new_ccd.data, mask=new_ccd.mask) np.testing.assert_allclose(masked_input[:-1, :-1], masked_output[1:, 1:], rtol=1e-5) # The masks should all be shifted too. np.testing.assert_array_equal(ccd_data.mask[:-1, :-1], new_ccd.mask[1:, 1:]) # We should have more values that are masked in the output array # than on input because some on output were not in the footprint # of the original array. # In the case of a shift, one row and one column should be nan, and they # will share one common nan where they intersect, so we know how many nan # there should be. assert np.isnan(new_ccd.data).sum() == np.sum(new_ccd.shape) - 1 # Use an odd number of pixels to make a well-defined center pixel def test_wcs_project_onto_scale_wcs(): # Make the target WCS with half the pixel scale and number of pixels # and the values should drop by a factor of 4. ccd_data = ccd_data_func(data_size=31) ccd_data.wcs = wcs_for_testing(ccd_data.shape) # Make sure wcs is centered at the center of the center pixel. ccd_data.wcs.wcs.crpix += 0.5 # Use uniform input data value for simplicity. ccd_data.data = np.ones_like(ccd_data.data) # Make mask zero... ccd_data.mask = np.zeros_like(ccd_data.data) # ...except the center pixel, which is one. ccd_data.mask[int(ccd_data.wcs.wcs.crpix[0]), int(ccd_data.wcs.wcs.crpix[1])] = 1 target_wcs = wcs_for_testing(ccd_data.shape) target_wcs.wcs.cdelt /= 2 # Choice below ensures we are really at the center pixel of an odd range. target_shape = 2 * np.array(ccd_data.shape) + 1 target_wcs.wcs.crpix = 2 * target_wcs.wcs.crpix + 1 + 0.5 # Explicitly set the interpolation method so we know what to # expect for the mass. new_ccd = wcs_project( ccd_data, target_wcs, target_shape=target_shape, order="nearest-neighbor" ) # Make sure new image has correct WCS. assert new_ccd.wcs.wcs.compare(target_wcs.wcs) # Define a cutout from the new array that should match the old. new_lower_bound = (np.array(new_ccd.shape) - np.array(ccd_data.shape)) // 2 new_upper_bound = (np.array(new_ccd.shape) + np.array(ccd_data.shape)) // 2 data_cutout = new_ccd.data[ new_lower_bound[0] : new_upper_bound[0], new_lower_bound[1] : new_upper_bound[1] ] # Make sure data matches within some reasonable tolerance, keeping in mind # that the pixels have been scaled. np.testing.assert_allclose(ccd_data.data / 4, data_cutout, rtol=1e-5) # Mask should be true for four pixels (all nearest neighbors) # of the single pixel we masked initially. new_center = np.array(new_ccd.wcs.wcs.crpix, dtype=int, copy=_NUMPY_COPY_IF_NEEDED) assert np.all( new_ccd.mask[ new_center[0] : new_center[0] + 2, new_center[1] : new_center[1] + 2 ] ) # Those four, and any that reproject made nan because they draw on # pixels outside the footprint of the original image, are the only # pixels that should be masked. assert new_ccd.mask.sum() == 4 + np.isnan(new_ccd.data).sum() def test_ccd_process_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() _ = ccd_process(ccd_data, gain=5 * u.electron / u.adu, readnoise=10 * u.electron) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit def test_ccd_process_parameters_are_appropriate(): ccd_data = ccd_data_func() # oscan check with pytest.raises(TypeError): ccd_process(ccd_data, oscan=True) # Trim section check with pytest.raises(TypeError): ccd_process(ccd_data, trim=True) # Error frame check # gain and readnoise must be specified with pytest.raises(ValueError): ccd_process(ccd_data, error=True) # gain must be specified with pytest.raises(ValueError): ccd_process(ccd_data, error=True, gain=None, readnoise=5) # Mask check with pytest.raises(TypeError): ccd_process(ccd_data, bad_pixel_mask=3) # master bias check with pytest.raises(TypeError): ccd_process(ccd_data, master_bias=3) # Master flat check with pytest.raises(TypeError): ccd_process(ccd_data, master_flat=3) def test_ccd_process(): # Test the through ccd_process ccd_data = CCDData(10.0 * np.ones((100, 100)), unit=u.adu) ccd_data.data[:, -10:] = 2 ccd_data.meta["testkw"] = 100 mask = np.zeros((100, 90)) masterbias = CCDData(2.0 * np.ones((100, 90)), unit=u.electron) masterbias.uncertainty = StdDevUncertainty(np.zeros((100, 90))) dark_frame = CCDData(0.0 * np.ones((100, 90)), unit=u.electron) dark_frame.uncertainty = StdDevUncertainty(np.zeros((100, 90))) masterflat = CCDData(10.0 * np.ones((100, 90)), unit=u.electron) masterflat.uncertainty = StdDevUncertainty(np.zeros((100, 90))) occd = ccd_process( ccd_data, oscan=ccd_data[:, -10:], trim="[1:90,1:100]", error=True, master_bias=masterbias, master_flat=masterflat, dark_frame=dark_frame, bad_pixel_mask=mask, gain=0.5 * u.electron / u.adu, readnoise=5**0.5 * u.electron, oscan_median=True, dark_scale=False, dark_exposure=1.0 * u.s, data_exposure=1.0 * u.s, ) # Final results should be (10 - 2) / 2.0 - 2 = 2 # Error should be (4 + 5)**0.5 / 0.5 = 3.0 np.testing.assert_array_equal(2.0 * np.ones((100, 90)), occd.data) np.testing.assert_almost_equal(3.0 * np.ones((100, 90)), occd.uncertainty.array) np.testing.assert_array_equal(mask, occd.mask) assert occd.unit == u.electron # Make sure the original keyword is still present. Regression test for #401 assert occd.meta["testkw"] == 100 def test_ccd_process_gain_corrected(): # Test the through ccd_process with gain_corrected as False ccd_data = CCDData(10.0 * np.ones((100, 100)), unit=u.adu) ccd_data.data[:, -10:] = 2 ccd_data.meta["testkw"] = 100 mask = np.zeros((100, 90)) masterbias = CCDData(4.0 * np.ones((100, 90)), unit=u.adu) masterbias.uncertainty = StdDevUncertainty(np.zeros((100, 90))) dark_frame = CCDData(0.0 * np.ones((100, 90)), unit=u.adu) dark_frame.uncertainty = StdDevUncertainty(np.zeros((100, 90))) masterflat = CCDData(5.0 * np.ones((100, 90)), unit=u.adu) masterflat.uncertainty = StdDevUncertainty(np.zeros((100, 90))) occd = ccd_process( ccd_data, oscan=ccd_data[:, -10:], trim="[1:90,1:100]", error=True, master_bias=masterbias, master_flat=masterflat, dark_frame=dark_frame, bad_pixel_mask=mask, gain=0.5 * u.electron / u.adu, readnoise=5**0.5 * u.electron, oscan_median=True, dark_scale=False, dark_exposure=1.0 * u.s, data_exposure=1.0 * u.s, gain_corrected=False, ) # Final results should be (10 - 2) / 2.0 - 2 = 2 # Error should be (4 + 5)**0.5 / 0.5 = 3.0 np.testing.assert_array_equal(2.0 * np.ones((100, 90)), occd.data) np.testing.assert_almost_equal(3.0 * np.ones((100, 90)), occd.uncertainty.array) np.testing.assert_array_equal(mask, occd.mask) assert occd.unit == u.electron # Make sure the original keyword is still present. Regression test for #401 assert occd.meta["testkw"] == 100 ccdproc-2.4.3/ccdproc/tests/test_ccdproc_logging.py0000644000000000000000000000761413615410400017400 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np import pytest from astropy.nddata import CCDData from ccdproc import Keyword, create_deviation, subtract_bias, trim_image from ccdproc.core import _short_names from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func @pytest.mark.parametrize("key", ["short", "toolongforfits"]) def test_log_string(key): ccd_data = ccd_data_func() add_key = key new = create_deviation(ccd_data, readnoise=3 * ccd_data.unit, add_keyword=add_key) # Keys should be added to new but not to ccd_data and should have # no value. assert add_key in new.meta assert add_key not in ccd_data.meta # Long keyword names should be accessible with just the keyword name # without HIERARCH -- is it? assert new.meta[add_key] is None def test_log_keyword(): ccd_data = ccd_data_func() key = "filter" key_val = "V" kwd = Keyword(key, value=key_val) new = create_deviation(ccd_data, readnoise=3 * ccd_data.unit, add_keyword=kwd) # Was the Keyword added with the correct value? assert kwd.name in new.meta assert kwd.name not in ccd_data.meta assert new.meta[kwd.name] == key_val def test_log_dict(): ccd_data = ccd_data_func() keys_to_add = { "process": "Added deviation", "n_images_input": 1, "current_temp": 42.9, } new = create_deviation( ccd_data, readnoise=3 * ccd_data.unit, add_keyword=keys_to_add ) for k, v in keys_to_add.items(): # Were all dictionary items added? assert k in new.meta assert k not in ccd_data.meta assert new.meta[k] == v def test_log_bad_type_fails(): ccd_data = ccd_data_func() add_key = 15 # anything not string and not dict-like will work here # Do we fail with non-string, non-Keyword, non-dict-like value? with pytest.raises(AttributeError): create_deviation(ccd_data, readnoise=3 * ccd_data.unit, add_keyword=add_key) def test_log_set_to_None_does_not_change_header(): ccd_data = ccd_data_func() new = create_deviation(ccd_data, readnoise=3 * ccd_data.unit, add_keyword=None) assert new.meta.keys() == ccd_data.header.keys() def test_implicit_logging(): ccd_data = ccd_data_func() # If nothing is supplied for the add_keyword argument then the following # should happen: # + A key named func.__name__ is created, with # + value that is the list of arguments the function was called with. bias = CCDData(np.zeros_like(ccd_data.data), unit="adu") result = subtract_bias(ccd_data, bias) assert "subtract_bias" in result.header assert result.header["subtract_bias"] == ( "subbias", "Shortened name for ccdproc command", ) assert result.header["subbias"] == "ccd=, master=" result = create_deviation(ccd_data, readnoise=3 * ccd_data.unit) assert result.header["create_deviation"] == ( "creatvar", "Shortened name for ccdproc command", ) assert "readnoise=" + str(3 * ccd_data.unit) in result.header["creatvar"] def test_loggin_without_keyword_args(): # Regression test for the first failure in #704, which fails because # there is no "fits_section" keyword in the call to trim_image. ccd = CCDData(data=np.arange(1000).reshape(20, 50), header=None, unit="adu") section = "[10:20, 10:20]" trim_1 = trim_image(ccd, "[10:20, 10:20]") assert section in trim_1.header[_short_names["trim_image"]] def test_logging_with_really_long_parameter_value(): # Another regression test for the trim_3 case in #704 ccd = CCDData(data=np.arange(1000).reshape(20, 50), header=None, unit="adu") section = ( "[10:2000000000000000000000000000000000000000000000000000000, " "10:2000000000000000000000000000000]" ) trim_3 = trim_image(ccd, fits_section=section) assert section in trim_3.header[_short_names["trim_image"]] ccdproc-2.4.3/ccdproc/tests/test_combine_open_files.py0000644000000000000000000000333713615410400020072 0ustar00import os import subprocess import sys from pathlib import Path import pytest run_dir = Path(__file__).parent # Why? So that we get up to the file above ccdproc, so that in the # subprocess we can add that direction to sys.path. subprocess_dir = run_dir.parent.parent OVERHEAD = "4" NUM_FILE_LIMIT = "20" common_args = [ sys.executable, str(run_dir / "run_with_file_number_limit.py"), "--kind", "fits", "--overhead", OVERHEAD, ] # Regression test for #629 @pytest.mark.skipif( os.environ.get("APPVEYOR") or os.sys.platform == "win32", reason="Test relies on linux/osx features of psutil", ) def test_open_files_combine_no_chunks(): """ Test that we are not opening (much) more than the number of files we are processing. """ # Make a copy args = list(common_args) args.extend(["--open-by", "combine-nochunk", NUM_FILE_LIMIT]) p = subprocess.run(args=args, cwd=str(subprocess_dir)) # If we have succeeded the test passes. We are only checking that # we don't have too many files open. assert p.returncode == 0 # Regression test for #629 @pytest.mark.skipif( os.environ.get("APPVEYOR") or os.sys.platform == "win32", reason="Test relies on linux/osx features of psutil", ) def test_open_files_combine_chunks(): """ Test that we are not opening (much) more than the number of files we are processing when combination is broken into chunks. """ # Make a copy args = list(common_args) args.extend(["--open-by", "combine-chunk", NUM_FILE_LIMIT]) p = subprocess.run(args=args, cwd=str(subprocess_dir)) # If we have succeeded the test passes. We are only checking that # we don't have too many files open. assert p.returncode == 0 ccdproc-2.4.3/ccdproc/tests/test_combiner.py0000644000000000000000000010374313615410400016053 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import astropy import astropy.units as u import numpy as np import pytest from astropy.nddata import CCDData from astropy.stats import median_absolute_deviation as mad from astropy.utils.data import get_pkg_data_filename from packaging.version import Version, parse from ccdproc.combiner import ( Combiner, _calculate_step_sizes, _default_std, combine, sigma_func, ) from ccdproc.image_collection import ImageFileCollection from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func SUPER_OLD_ASTROPY = parse(astropy.__version__) < Version("4.3.0") # Several tests have many more NaNs in them than real data. numpy generates # lots of warnings in those cases and it makes more sense to suppress them # than to generate them. pytestmark = pytest.mark.filterwarnings( "ignore:All-NaN slice encountered:RuntimeWarning" ) def _make_mean_scaler(ccd_data): def scale_by_mean(x): # scale each array to the mean of the first image return ccd_data.data.mean() / np.ma.average(x) return scale_by_mean # test that the Combiner raises error if empty def test_combiner_empty(): with pytest.raises(TypeError): Combiner() # empty initializer should fail # test that the Combiner raises error if empty if ccd_list is None def test_combiner_init_with_none(): with pytest.raises(TypeError): Combiner(None) # empty initializer should fail # test that Combiner throws an error if input # objects are not ccddata objects def test_ccddata_combiner_objects(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, None] with pytest.raises(TypeError): Combiner(ccd_list) # different objects should fail # test that Combiner throws an error if input # objects do not have the same size def test_ccddata_combiner_size(): ccd_data = ccd_data_func() ccd_large = CCDData(np.zeros((200, 100)), unit=u.adu) ccd_list = [ccd_data, ccd_data, ccd_large] with pytest.raises(TypeError): Combiner(ccd_list) # arrays of different sizes should fail # test that Combiner throws an error if input # objects do not have the same units def test_ccddata_combiner_units(): ccd_data = ccd_data_func() ccd_large = CCDData(np.zeros((100, 100)), unit=u.second) ccd_list = [ccd_data, ccd_data, ccd_large] with pytest.raises(TypeError): Combiner(ccd_list) # test if mask and data array are created def test_combiner_create(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) assert c.data_arr.shape == (3, 100, 100) assert c.data_arr.mask.shape == (3, 100, 100) # test if dtype matches the value that is passed def test_combiner_dtype(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list, dtype=np.float32) assert c.data_arr.dtype == np.float32 avg = c.average_combine() # dtype of average should match input dtype assert avg.dtype == c.dtype med = c.median_combine() # dtype of median should match dtype of input assert med.dtype == c.dtype result_sum = c.sum_combine() # dtype of sum should match dtype of input assert result_sum.dtype == c.dtype # test mask is created from ccd.data def test_combiner_mask(): data = np.zeros((10, 10)) data[5, 5] = 1 mask = data == 0 ccd = CCDData(data, unit=u.adu, mask=mask) ccd_list = [ccd, ccd, ccd] c = Combiner(ccd_list) assert c.data_arr.shape == (3, 10, 10) assert c.data_arr.mask.shape == (3, 10, 10) assert not c.data_arr.mask[0, 5, 5] def test_weights(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) with pytest.raises(TypeError): c.weights = 1 def test_weights_shape(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) with pytest.raises(ValueError): c.weights = ccd_data.data def test_1Dweights(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 1000, unit=u.adu), CCDData(np.zeros((10, 10)) + 1000, unit=u.adu), ] c = Combiner(ccd_list) c.weights = np.array([1, 5, 10]) ccd = c.average_combine() np.testing.assert_almost_equal(ccd.data, 312.5) with pytest.raises(ValueError): c.weights = np.array([1, 5, 10, 20]) def test_pixelwise_weights(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 1000, unit=u.adu), CCDData(np.zeros((10, 10)) + 1000, unit=u.adu), ] c = Combiner(ccd_list) c.weights = np.ones_like(c.data_arr) c.weights[:, 5, 5] = [1, 5, 10] ccd = c.average_combine() np.testing.assert_almost_equal(ccd.data[5, 5], 312.5) np.testing.assert_almost_equal(ccd.data[0, 0], 0) # test the min-max rejection def test_combiner_minmax(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 1000, unit=u.adu), CCDData(np.zeros((10, 10)) + 1000, unit=u.adu), ] c = Combiner(ccd_list) c.minmax_clipping(min_clip=-500, max_clip=500) ccd = c.median_combine() assert ccd.data.mean() == 0 def test_combiner_minmax_max(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 1000, unit=u.adu), CCDData(np.zeros((10, 10)) + 1000, unit=u.adu), ] c = Combiner(ccd_list) c.minmax_clipping(min_clip=None, max_clip=500) assert c.data_arr[2].mask.all() def test_combiner_minmax_min(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 1000, unit=u.adu), CCDData(np.zeros((10, 10)) + 1000, unit=u.adu), ] c = Combiner(ccd_list) c.minmax_clipping(min_clip=-500, max_clip=None) assert c.data_arr[1].mask.all() def test_combiner_sigmaclip_high(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 10, unit=u.adu), CCDData(np.zeros((10, 10)) + 10, unit=u.adu), CCDData(np.zeros((10, 10)) - 10, unit=u.adu), CCDData(np.zeros((10, 10)) + 10, unit=u.adu), CCDData(np.zeros((10, 10)) + 1000, unit=u.adu), ] c = Combiner(ccd_list) # using mad for more robust statistics vs. std c.sigma_clipping(high_thresh=3, low_thresh=None, func=np.ma.median, dev_func=mad) assert c.data_arr[5].mask.all() def test_combiner_sigmaclip_single_pix(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 10, unit=u.adu), CCDData(np.zeros((10, 10)) + 10, unit=u.adu), CCDData(np.zeros((10, 10)) - 10, unit=u.adu), CCDData(np.zeros((10, 10)) + 10, unit=u.adu), CCDData(np.zeros((10, 10)) - 10, unit=u.adu), ] c = Combiner(ccd_list) # add a single pixel in another array to check that # that one gets rejected c.data_arr[0, 5, 5] = 0 c.data_arr[1, 5, 5] = -5 c.data_arr[2, 5, 5] = 5 c.data_arr[3, 5, 5] = -5 c.data_arr[4, 5, 5] = 25 c.sigma_clipping(high_thresh=3, low_thresh=None, func=np.ma.median, dev_func=mad) assert c.data_arr.mask[4, 5, 5] def test_combiner_sigmaclip_low(): ccd_list = [ CCDData(np.zeros((10, 10)), unit=u.adu), CCDData(np.zeros((10, 10)) - 10, unit=u.adu), CCDData(np.zeros((10, 10)) + 10, unit=u.adu), CCDData(np.zeros((10, 10)) - 10, unit=u.adu), CCDData(np.zeros((10, 10)) + 10, unit=u.adu), CCDData(np.zeros((10, 10)) - 1000, unit=u.adu), ] c = Combiner(ccd_list) # using mad for more robust statistics vs. std c.sigma_clipping(high_thresh=None, low_thresh=3, func=np.ma.median, dev_func=mad) assert c.data_arr[5].mask.all() # test that the median combination works and returns a ccddata object def test_combiner_median(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) ccd = c.median_combine() assert isinstance(ccd, CCDData) assert ccd.shape == (100, 100) assert ccd.unit == u.adu assert ccd.meta["NCOMBINE"] == len(ccd_list) # test that the average combination works and returns a ccddata object def test_combiner_average(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) ccd = c.average_combine() assert isinstance(ccd, CCDData) assert ccd.shape == (100, 100) assert ccd.unit == u.adu assert ccd.meta["NCOMBINE"] == len(ccd_list) # test that the sum combination works and returns a ccddata object def test_combiner_sum(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) ccd = c.sum_combine() assert isinstance(ccd, CCDData) assert ccd.shape == (100, 100) assert ccd.unit == u.adu assert ccd.meta["NCOMBINE"] == len(ccd_list) # test weighted sum def test_combiner_sum_weighted(): ccd_data = CCDData(data=[[0, 1], [2, 3]], unit="adu") ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) c.weights = np.array([1, 2, 3]) ccd = c.sum_combine() expected_result = sum(w * d.data for w, d in zip(c.weights, ccd_list)) np.testing.assert_almost_equal(ccd, expected_result) # test weighted sum def test_combiner_sum_weighted_by_pixel(): ccd_data = CCDData(data=[[1, 2], [4, 8]], unit="adu") ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) # Weights below are chosen so that every entry in weights_pixel = [[8, 4], [2, 1]] c.weights = np.array([weights_pixel] * 3) ccd = c.sum_combine() expected_result = [[24, 24], [24, 24]] np.testing.assert_almost_equal(ccd, expected_result) # This warning is generated by numpy and is expected when # many pixels are masked. @pytest.mark.filterwarnings( "ignore:Mean of empty slice:RuntimeWarning", "ignore:Degrees of freedom <= 0:RuntimeWarning", ) def test_combiner_mask_average(): # test data combined with mask is created correctly data = np.zeros((10, 10)) data[5, 5] = 1 mask = data == 0 ccd = CCDData(data, unit=u.adu, mask=mask) ccd_list = [ccd, ccd, ccd] c = Combiner(ccd_list) ccd = c.average_combine() # How can we assert anything about the data if all values # are masked?! # assert ccd.data[0, 0] == 0 assert ccd.data[5, 5] == 1 assert ccd.mask[0, 0] assert not ccd.mask[5, 5] def test_combiner_with_scaling(): ccd_data = ccd_data_func() # The factors below are not particularly important; just avoid anything # whose average is 1. ccd_data_lower = ccd_data.multiply(3) ccd_data_higher = ccd_data.multiply(0.9) combiner = Combiner([ccd_data, ccd_data_higher, ccd_data_lower]) scale_by_mean = _make_mean_scaler(ccd_data) combiner.scaling = scale_by_mean avg_ccd = combiner.average_combine() # Does the mean of the scaled arrays match the value to which it was # scaled? np.testing.assert_almost_equal(avg_ccd.data.mean(), ccd_data.data.mean()) assert avg_ccd.shape == ccd_data.shape median_ccd = combiner.median_combine() # Does median also scale to the correct value? np.testing.assert_almost_equal(np.median(median_ccd.data), np.median(ccd_data.data)) # Set the scaling manually... combiner.scaling = [scale_by_mean(combiner.data_arr[i]) for i in range(3)] avg_ccd = combiner.average_combine() np.testing.assert_almost_equal(avg_ccd.data.mean(), ccd_data.data.mean()) assert avg_ccd.shape == ccd_data.shape def test_combiner_scaling_fails(): ccd_data = ccd_data_func() combiner = Combiner([ccd_data, ccd_data.copy()]) # Should fail unless scaling is set to a function or list-like with pytest.raises(TypeError): combiner.scaling = 5 # Should calendar because the scaling function is not the right shape with pytest.raises(ValueError): combiner.scaling = [5, 5, 5] # test data combined with mask is created correctly def test_combiner_mask_median(): data = np.zeros((10, 10)) data[5, 5] = 1 mask = data == 0 ccd = CCDData(data, unit=u.adu, mask=mask) ccd_list = [ccd, ccd, ccd] c = Combiner(ccd_list) ccd = c.median_combine() # We should not check the data value for masked entries. # Instead, just check that entries are masked appropriately. assert ccd.mask[0, 0] assert ccd.data[5, 5] == 1 assert not ccd.mask[5, 5] # Ignore warnings generated because most values are masked @pytest.mark.filterwarnings("ignore:Degrees of freedom <= 0:RuntimeWarning") def test_combiner_mask_sum(): # test data combined with mask is created correctly data = np.zeros((10, 10)) data[5, 5] = 1 mask = data == 0 ccd = CCDData(data, unit=u.adu, mask=mask) ccd_list = [ccd, ccd, ccd] c = Combiner(ccd_list) ccd = c.sum_combine() assert ccd.data[0, 0] == 0 assert ccd.data[5, 5] == 3 assert ccd.mask[0, 0] assert not ccd.mask[5, 5] # Test that calling combine with a bad input raises an error def test_combine_bad_input(): with pytest.raises(ValueError, match="unrecognised input for list of images"): combine(1) with pytest.raises(ValueError, match="unrecognised combine method"): combine([1, 2, 3], method="bad_method") # test combiner convenience function reads fits file and combine as expected def test_combine_average_fitsimages(): fitsfile = get_pkg_data_filename("data/a8280271.fits", package="ccdproc.tests") ccd = CCDData.read(fitsfile, unit=u.adu) ccd_list = [ccd] * 3 c = Combiner(ccd_list) ccd_by_combiner = c.average_combine() fitsfilename_list = [fitsfile] * 3 avgccd = combine(fitsfilename_list, output_file=None, method="average", unit=u.adu) # averaging same fits images should give back same fits image np.testing.assert_array_almost_equal(avgccd.data, ccd_by_combiner.data) def test_combine_numpyndarray(): """Test of numpy ndarray implementation: #493 Test the average combine using ``Combiner`` and ``combine`` with input ``img_list`` in the format of ``numpy.ndarray``. """ fitsfile = get_pkg_data_filename("data/a8280271.fits") ccd = CCDData.read(fitsfile, unit=u.adu) ccd_list = [ccd] * 3 c = Combiner(ccd_list) ccd_by_combiner = c.average_combine() fitsfilename_list = np.array([fitsfile] * 3) avgccd = combine(fitsfilename_list, output_file=None, method="average", unit=u.adu) # averaging same fits images should give back same fits image np.testing.assert_array_almost_equal(avgccd.data, ccd_by_combiner.data) def test_combiner_result_dtype(): """Regression test: #391 The result should have the appropriate dtype not the dtype of the first input.""" ccd = CCDData(np.ones((3, 3), dtype=np.uint16), unit="adu") res = combine([ccd, ccd.multiply(2)]) # The default dtype of Combiner is float64 assert res.data.dtype == np.float64 ref = np.ones((3, 3)) * 1.5 np.testing.assert_array_almost_equal(res.data, ref) res = combine([ccd, ccd.multiply(2), ccd.multiply(3)], dtype=int) # The result dtype should be integer: assert res.data.dtype == np.int_ ref = np.ones((3, 3)) * 2 np.testing.assert_array_almost_equal(res.data, ref) def test_combiner_image_file_collection_input(tmp_path): # Regression check for #754 ccd = ccd_data_func() for i in range(3): ccd.write(tmp_path / f"ccd-{i}.fits") ifc = ImageFileCollection(tmp_path) comb = Combiner(ifc.ccds()) np.testing.assert_array_almost_equal(ccd.data, comb.average_combine().data) def test_combine_image_file_collection_input(tmp_path): # Another regression check for #754 but this time with the # combine function instead of Combiner ccd = ccd_data_func() for i in range(3): ccd.write(tmp_path / f"ccd-{i}.fits") ifc = ImageFileCollection(tmp_path) comb_files = combine(ifc.files_filtered(include_path=True), method="average") comb_ccds = combine(ifc.ccds(), method="average") np.testing.assert_array_almost_equal(ccd.data, comb_files.data) np.testing.assert_array_almost_equal(ccd.data, comb_ccds.data) with pytest.raises(FileNotFoundError): # This should fail because the test is not running in the # folder where the images are. _ = combine(ifc.files_filtered()) # test combiner convenience function works with list of ccddata objects def test_combine_average_ccddata(): fitsfile = get_pkg_data_filename("data/a8280271.fits") ccd = CCDData.read(fitsfile, unit=u.adu) ccd_list = [ccd] * 3 c = Combiner(ccd_list) ccd_by_combiner = c.average_combine() avgccd = combine(ccd_list, output_file=None, method="average", unit=u.adu) # averaging same ccdData should give back same images np.testing.assert_array_almost_equal(avgccd.data, ccd_by_combiner.data) # test combiner convenience function reads fits file and # and combine as expected when asked to run in limited memory def test_combine_limitedmem_fitsimages(): fitsfile = get_pkg_data_filename("data/a8280271.fits") ccd = CCDData.read(fitsfile, unit=u.adu) ccd_list = [ccd] * 5 c = Combiner(ccd_list) ccd_by_combiner = c.average_combine() fitsfilename_list = [fitsfile] * 5 avgccd = combine( fitsfilename_list, output_file=None, method="average", mem_limit=1e6, unit=u.adu ) # averaging same ccdData should give back same images np.testing.assert_array_almost_equal(avgccd.data, ccd_by_combiner.data) # test combiner convenience function reads fits file and # and combine as expected when asked to run in limited memory with scaling def test_combine_limitedmem_scale_fitsimages(): fitsfile = get_pkg_data_filename("data/a8280271.fits") ccd = CCDData.read(fitsfile, unit=u.adu) ccd_list = [ccd] * 5 c = Combiner(ccd_list) # scale each array to the mean of the first image scale_by_mean = _make_mean_scaler(ccd) c.scaling = scale_by_mean ccd_by_combiner = c.average_combine() fitsfilename_list = [fitsfile] * 5 avgccd = combine( fitsfilename_list, output_file=None, method="average", mem_limit=1e6, scale=scale_by_mean, unit=u.adu, ) np.testing.assert_array_almost_equal(avgccd.data, ccd_by_combiner.data, decimal=4) # test the optional uncertainty function in average_combine def test_average_combine_uncertainty(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) ccd = c.average_combine(uncertainty_func=np.sum) uncert_ref = np.sum(c.data_arr, 0) / np.sqrt(3) np.testing.assert_array_equal(ccd.uncertainty.array, uncert_ref) # Compare this also to the "combine" call ccd2 = combine(ccd_list, method="average", combine_uncertainty_function=np.sum) np.testing.assert_array_equal(ccd.data, ccd2.data) np.testing.assert_array_equal(ccd.uncertainty.array, ccd2.uncertainty.array) # test the optional uncertainty function in median_combine def test_median_combine_uncertainty(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) ccd = c.median_combine(uncertainty_func=np.sum) uncert_ref = np.sum(c.data_arr, 0) / np.sqrt(3) np.testing.assert_array_equal(ccd.uncertainty.array, uncert_ref) # Compare this also to the "combine" call ccd2 = combine(ccd_list, method="median", combine_uncertainty_function=np.sum) np.testing.assert_array_equal(ccd.data, ccd2.data) np.testing.assert_array_equal(ccd.uncertainty.array, ccd2.uncertainty.array) # test the optional uncertainty function in sum_combine def test_sum_combine_uncertainty(): ccd_data = ccd_data_func() ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) ccd = c.sum_combine(uncertainty_func=np.sum) uncert_ref = np.sum(c.data_arr, 0) * np.sqrt(3) np.testing.assert_almost_equal(ccd.uncertainty.array, uncert_ref) # Compare this also to the "combine" call ccd2 = combine(ccd_list, method="sum", combine_uncertainty_function=np.sum) np.testing.assert_array_equal(ccd.data, ccd2.data) np.testing.assert_array_equal(ccd.uncertainty.array, ccd2.uncertainty.array) # Ignore warnings generated because most values are masked @pytest.mark.filterwarnings( "ignore:Mean of empty slice:RuntimeWarning", "ignore:Degrees of freedom <= 0:RuntimeWarning", ) @pytest.mark.parametrize("mask_point", [True, False]) @pytest.mark.parametrize( "comb_func", ["average_combine", "median_combine", "sum_combine"] ) def test_combine_result_uncertainty_and_mask(comb_func, mask_point): # Regression test for #774 # Turns out combine does not return an uncertainty or mask if the input # CCDData has no uncertainty or mask, which makes very little sense. ccd_data = ccd_data_func() # Make sure the initial ccd_data has no uncertainty, which was the condition that # led to no uncertainty being returned. assert ccd_data.uncertainty is None if mask_point: # Make one pixel really negative so we can clip it and guarantee a resulting # pixel is masked. ccd_data.data[0, 0] = -1000 ccd_list = [ccd_data, ccd_data, ccd_data] c = Combiner(ccd_list) c.minmax_clipping(min_clip=-100) expected_result = getattr(c, comb_func)() # Just need the first part of the name for the combine function combine_method_name = comb_func.split("_")[0] ccd_comb = combine( ccd_list, method=combine_method_name, minmax_clip=True, minmax_clip_min=-100 ) np.testing.assert_array_almost_equal( ccd_comb.uncertainty.array, expected_result.uncertainty.array ) # Check that the right point is masked, and only one point is # masked assert expected_result.mask[0, 0] == mask_point assert expected_result.mask.sum() == mask_point assert ccd_comb.mask[0, 0] == mask_point assert ccd_comb.mask.sum() == mask_point def test_combine_overwrite_output(tmp_path): """ The combiune function should *not* overwrite the result file unless the overwrite_output argument is True """ output_file = tmp_path / "fake.fits" ccd = CCDData(np.ones((3, 3)), unit="adu") # Make sure we have a file to overwrite ccd.write(output_file) # Test that overwrite does NOT happen by default with pytest.raises(OSError, match="fake.fits already exists"): res = combine([ccd, ccd.multiply(2)], output_file=str(output_file)) # Should be no error here... # The default dtype of Combiner is float64 res = combine( [ccd, ccd.multiply(2)], output_file=output_file, overwrite_output=True ) res_from_disk = CCDData.read(output_file) # Data should be the same np.testing.assert_allclose(res.data, res_from_disk.data) # test resulting uncertainty is corrected for the number of images def test_combiner_uncertainty_average(): ccd_list = [ CCDData(np.ones((10, 10)), unit=u.adu), CCDData(np.ones((10, 10)) * 2, unit=u.adu), ] c = Combiner(ccd_list) ccd = c.average_combine() # Just the standard deviation of ccd data. ref_uncertainty = np.ones((10, 10)) / 2 # Correction because we combined two images. ref_uncertainty /= np.sqrt(2) np.testing.assert_array_almost_equal(ccd.uncertainty.array, ref_uncertainty) # test resulting uncertainty is corrected for the number of images (with mask) def test_combiner_uncertainty_average_mask(): mask = np.zeros((10, 10), dtype=np.bool_) mask[5, 5] = True ccd_with_mask = CCDData(np.ones((10, 10)), unit=u.adu, mask=mask) ccd_list = [ ccd_with_mask, CCDData(np.ones((10, 10)) * 2, unit=u.adu), CCDData(np.ones((10, 10)) * 3, unit=u.adu), ] c = Combiner(ccd_list) ccd = c.average_combine() # Just the standard deviation of ccd data. ref_uncertainty = np.ones((10, 10)) * np.std([1, 2, 3]) # Correction because we combined two images. ref_uncertainty /= np.sqrt(3) ref_uncertainty[5, 5] = np.std([2, 3]) / np.sqrt(2) np.testing.assert_array_almost_equal(ccd.uncertainty.array, ref_uncertainty) # test resulting uncertainty is corrected for the number of images (with mask) def test_combiner_uncertainty_median_mask(): mad_to_sigma = 1.482602218505602 mask = np.zeros((10, 10), dtype=np.bool_) mask[5, 5] = True ccd_with_mask = CCDData(np.ones((10, 10)), unit=u.adu, mask=mask) ccd_list = [ ccd_with_mask, CCDData(np.ones((10, 10)) * 2, unit=u.adu), CCDData(np.ones((10, 10)) * 3, unit=u.adu), ] c = Combiner(ccd_list) ccd = c.median_combine() # Just the standard deviation of ccd data. ref_uncertainty = np.ones((10, 10)) * mad_to_sigma * mad([1, 2, 3]) # Correction because we combined two images. ref_uncertainty /= np.sqrt(3) # 0.855980789955 ref_uncertainty[5, 5] = mad_to_sigma * mad([2, 3]) / np.sqrt(2) # 0.524179041254 np.testing.assert_array_almost_equal(ccd.uncertainty.array, ref_uncertainty) # test resulting uncertainty is corrected for the number of images (with mask) def test_combiner_uncertainty_sum_mask(): mask = np.zeros((10, 10), dtype=np.bool_) mask[5, 5] = True ccd_with_mask = CCDData(np.ones((10, 10)), unit=u.adu, mask=mask) ccd_list = [ ccd_with_mask, CCDData(np.ones((10, 10)) * 2, unit=u.adu), CCDData(np.ones((10, 10)) * 3, unit=u.adu), ] c = Combiner(ccd_list) ccd = c.sum_combine() # Just the standard deviation of ccd data. ref_uncertainty = np.ones((10, 10)) * np.std([1, 2, 3]) ref_uncertainty *= np.sqrt(3) ref_uncertainty[5, 5] = np.std([2, 3]) * np.sqrt(2) np.testing.assert_array_almost_equal(ccd.uncertainty.array, ref_uncertainty) def test_combiner_3d(): data1 = CCDData(3 * np.ones((5, 5, 5)), unit=u.adu) data2 = CCDData(2 * np.ones((5, 5, 5)), unit=u.adu) data3 = CCDData(4 * np.ones((5, 5, 5)), unit=u.adu) ccd_list = [data1, data2, data3] c = Combiner(ccd_list) assert c.data_arr.shape == (3, 5, 5, 5) assert c.data_arr.mask.shape == (3, 5, 5, 5) ccd = c.average_combine() assert ccd.shape == (5, 5, 5) np.testing.assert_array_almost_equal(ccd.data, data1, decimal=4) def test_3d_combiner_with_scaling(): ccd_data = ccd_data_func() # The factors below are not particularly important; just avoid anything # whose average is 1. ccd_data = CCDData(np.ones((5, 5, 5)), unit=u.adu) ccd_data_lower = CCDData(3 * np.ones((5, 5, 5)), unit=u.adu) ccd_data_higher = CCDData(0.9 * np.ones((5, 5, 5)), unit=u.adu) combiner = Combiner([ccd_data, ccd_data_higher, ccd_data_lower]) scale_by_mean = _make_mean_scaler(ccd_data) combiner.scaling = scale_by_mean avg_ccd = combiner.average_combine() # Does the mean of the scaled arrays match the value to which it was # scaled? np.testing.assert_almost_equal(avg_ccd.data.mean(), ccd_data.data.mean()) assert avg_ccd.shape == ccd_data.shape median_ccd = combiner.median_combine() # Does median also scale to the correct value? np.testing.assert_almost_equal(np.median(median_ccd.data), np.median(ccd_data.data)) # Set the scaling manually... combiner.scaling = [scale_by_mean(combiner.data_arr[i]) for i in range(3)] avg_ccd = combiner.average_combine() np.testing.assert_almost_equal(avg_ccd.data.mean(), ccd_data.data.mean()) assert avg_ccd.shape == ccd_data.shape def test_clip_extrema_3d(): ccdlist = [ CCDData(np.ones((3, 3, 3)) * 90.0, unit="adu"), CCDData(np.ones((3, 3, 3)) * 20.0, unit="adu"), CCDData(np.ones((3, 3, 3)) * 10.0, unit="adu"), CCDData(np.ones((3, 3, 3)) * 40.0, unit="adu"), CCDData(np.ones((3, 3, 3)) * 25.0, unit="adu"), CCDData(np.ones((3, 3, 3)) * 35.0, unit="adu"), ] c = Combiner(ccdlist) c.clip_extrema(nlow=1, nhigh=1) result = c.average_combine() expected = CCDData(np.ones((3, 3, 3)) * 30, unit="adu") np.testing.assert_array_equal(result, expected) @pytest.mark.parametrize( "comb_func", ["average_combine", "median_combine", "sum_combine"] ) def test_writeable_after_combine(tmpdir, comb_func): ccd_data = ccd_data_func() tmp_file = tmpdir.join("tmp.fits") from ..combiner import Combiner combined = Combiner([ccd_data for _ in range(3)]) ccd2 = getattr(combined, comb_func)() # This should not fail because the resulting uncertainty has a mask ccd2.write(tmp_file.strpath) def test_clip_extrema(): ccdlist = [ CCDData(np.ones((3, 5)) * 90.0, unit="adu"), CCDData(np.ones((3, 5)) * 20.0, unit="adu"), CCDData(np.ones((3, 5)) * 10.0, unit="adu"), CCDData(np.ones((3, 5)) * 40.0, unit="adu"), CCDData(np.ones((3, 5)) * 25.0, unit="adu"), CCDData(np.ones((3, 5)) * 35.0, unit="adu"), ] ccdlist[0].data[0, 1] = 3.1 ccdlist[1].data[1, 2] = 100.1 ccdlist[1].data[2, 0] = 100.1 c = Combiner(ccdlist) c.clip_extrema(nlow=1, nhigh=1) result = c.average_combine() expected = [ [30.0, 22.5, 30.0, 30.0, 30.0], [30.0, 30.0, 47.5, 30.0, 30.0], [47.5, 30.0, 30.0, 30.0, 30.0], ] np.testing.assert_array_equal(result, expected) def test_clip_extrema_via_combine(): ccdlist = [ CCDData(np.ones((3, 5)) * 90.0, unit="adu"), CCDData(np.ones((3, 5)) * 20.0, unit="adu"), CCDData(np.ones((3, 5)) * 10.0, unit="adu"), CCDData(np.ones((3, 5)) * 40.0, unit="adu"), CCDData(np.ones((3, 5)) * 25.0, unit="adu"), CCDData(np.ones((3, 5)) * 35.0, unit="adu"), ] ccdlist[0].data[0, 1] = 3.1 ccdlist[1].data[1, 2] = 100.1 ccdlist[1].data[2, 0] = 100.1 result = combine( ccdlist, clip_extrema=True, nlow=1, nhigh=1, ) expected = [ [30.0, 22.5, 30.0, 30.0, 30.0], [30.0, 30.0, 47.5, 30.0, 30.0], [47.5, 30.0, 30.0, 30.0, 30.0], ] np.testing.assert_array_equal(result, expected) def test_clip_extrema_with_other_rejection(): ccdlist = [ CCDData(np.ones((3, 5)) * 90.0, unit="adu"), CCDData(np.ones((3, 5)) * 20.0, unit="adu"), CCDData(np.ones((3, 5)) * 10.0, unit="adu"), CCDData(np.ones((3, 5)) * 40.0, unit="adu"), CCDData(np.ones((3, 5)) * 25.0, unit="adu"), CCDData(np.ones((3, 5)) * 35.0, unit="adu"), ] ccdlist[0].data[0, 1] = 3.1 ccdlist[1].data[1, 2] = 100.1 ccdlist[1].data[2, 0] = 100.1 c = Combiner(ccdlist) # Reject ccdlist[1].data[1,2] by other means c.data_arr.mask[1, 1, 2] = True # Reject ccdlist[1].data[1,2] by other means c.data_arr.mask[3, 0, 0] = True c.clip_extrema(nlow=1, nhigh=1) result = c.average_combine() expected = [ [80.0 / 3.0, 22.5, 30.0, 30.0, 30.0], [30.0, 30.0, 47.5, 30.0, 30.0], [47.5, 30.0, 30.0, 30.0, 30.0], ] np.testing.assert_array_equal(result, expected) # The expected values below assume an image that is 2000x2000 @pytest.mark.parametrize( "num_chunks, expected", [ (53, (37, 2000)), (1500, (1, 2000)), (2001, (1, 1000)), (2999, (1, 1000)), (10000, (1, 333)), ], ) def test_ystep_calculation(num_chunks, expected): # Regression test for # https://github.com/astropy/ccdproc/issues/639 # See that issue for the motivation for the choice of # image size and number of chunks in the test below. xstep, ystep = _calculate_step_sizes(2000, 2000, num_chunks) assert xstep == expected[0] and ystep == expected[1] def test_combiner_gen(): ccd_data = ccd_data_func() def create_gen(): yield ccd_data yield ccd_data yield ccd_data c = Combiner(create_gen()) assert c.data_arr.shape == (3, 100, 100) assert c.data_arr.mask.shape == (3, 100, 100) @pytest.mark.parametrize( "comb_func", ["average_combine", "median_combine", "sum_combine"] ) def test_combiner_with_scaling_uncertainty(comb_func): # A regression test for #719, in which it was pointed out that the # uncertainty was not properly calculated from scaled data in # median_combine ccd_data = ccd_data_func() # The factors below are not particularly important; just avoid anything # whose average is 1. ccd_data_lower = ccd_data.multiply(3) ccd_data_higher = ccd_data.multiply(0.9) combiner = Combiner([ccd_data, ccd_data_higher, ccd_data_lower]) # scale each array to the mean of the first image scale_by_mean = _make_mean_scaler(ccd_data) combiner.scaling = scale_by_mean scaled_ccds = np.array( [ ccd_data.data * scale_by_mean(ccd_data.data), ccd_data_lower.data * scale_by_mean(ccd_data_lower.data), ccd_data_higher.data * scale_by_mean(ccd_data_higher.data), ] ) avg_ccd = getattr(combiner, comb_func)() if comb_func != "median_combine": uncertainty_func = _default_std() else: uncertainty_func = sigma_func expected_unc = uncertainty_func(scaled_ccds, axis=0) np.testing.assert_almost_equal(avg_ccd.uncertainty.array, expected_unc) @pytest.mark.parametrize( "comb_func", ["average_combine", "median_combine", "sum_combine"] ) def test_user_supplied_combine_func_that_relies_on_masks(comb_func): # Test to make sure that setting some values to NaN internally # does not affect results when the user supplies a function that # uses masks to screen out bad data. data = np.ones((10, 10)) data[5, 5] = 2 mask = data == 2 ccd = CCDData(data, unit=u.adu, mask=mask) # Same, but no mask ccd2 = CCDData(data, unit=u.adu) ccd_list = [ccd, ccd, ccd2] c = Combiner(ccd_list) if comb_func == "sum_combine": expected_result = 3 * data actual_result = c.sum_combine(sum_func=np.ma.sum) elif comb_func == "average_combine": expected_result = data actual_result = c.average_combine(scale_func=np.ma.mean) elif comb_func == "median_combine": expected_result = data actual_result = c.median_combine(median_func=np.ma.median) # Two of the three values are masked, so no matter what the combination # method is the result in this pixel should be 2. expected_result[5, 5] = 2 np.testing.assert_almost_equal(expected_result, actual_result) ccdproc-2.4.3/ccdproc/tests/test_cosmicray.py0000644000000000000000000003037713615410400016250 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np import pytest from astropy import units as u from astropy.utils import NumpyRNGContext from astropy.utils.exceptions import AstropyDeprecationWarning from ccdproc.core import ( background_deviation_box, background_deviation_filter, cosmicray_lacosmic, cosmicray_median, ) from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func pytest.importorskip("astroscrappy", reason="astroscrappy not installed") DATA_SCALE = 5.3 NCRAYS = 30 def add_cosmicrays(data, scale, threshold, ncrays=NCRAYS): size = data.shape[0] with NumpyRNGContext(125): crrays = np.random.randint(0, size, size=(ncrays, 2)) # use (threshold + 15) below to make sure cosmic ray is well above the # threshold no matter what the random number generator returns crflux = 10 * scale * np.random.random(NCRAYS) + (threshold + 15) * scale for i in range(ncrays): y, x = crrays[i] data.data[y, x] = crflux[i] def test_cosmicray_lacosmic(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 10 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) _, crarr = cosmicray_lacosmic(ccd_data.data, sigclip=5.9) # check the number of cosmic rays detected # Note that to get this to succeed reliably meant tuning # both sigclip and the threshold assert crarr.sum() == NCRAYS def test_cosmicray_lacosmic_ccddata(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise nccd_data = cosmicray_lacosmic(ccd_data, sigclip=5.9) # check the number of cosmic rays detected # Note that to get this to succeed reliably meant tuning # both sigclip and the threshold assert nccd_data.mask.sum() == NCRAYS def test_cosmicray_lacosmic_check_data(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) with pytest.raises(TypeError): noise = DATA_SCALE * np.ones_like(ccd_data.data) cosmicray_lacosmic(10, noise) @pytest.mark.parametrize("array_input", [True, False]) @pytest.mark.parametrize("gain_correct_data", [True, False]) def test_cosmicray_gain_correct(array_input, gain_correct_data): # Add regression check for #705 and for the new gain_correct # argument. # The issue is that cosmicray_lacosmic gain-corrects the # data and returns that gain corrected data. That is not the # intent... ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise # No units here on purpose. gain = 2.0 if array_input: new_data, cr_mask = cosmicray_lacosmic( ccd_data.data, gain=gain, gain_apply=gain_correct_data ) else: new_ccd = cosmicray_lacosmic(ccd_data, gain=gain, gain_apply=gain_correct_data) new_data = new_ccd.data cr_mask = new_ccd.mask # Fill masked locations with 0 since there is no simple relationship # between the original value and the corrected value. orig_data = np.ma.array(ccd_data.data, mask=cr_mask).filled(0) new_data = np.ma.array(new_data.data, mask=cr_mask).filled(0) if gain_correct_data: gain_for_test = gain else: gain_for_test = 1.0 np.testing.assert_allclose(gain_for_test * orig_data, new_data) def test_cosmicray_lacosmic_accepts_quantity_gain(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise # The units below are the point of the test gain = 2.0 * u.electron / u.adu _ = cosmicray_lacosmic(ccd_data, gain=gain, gain_apply=True) def test_cosmicray_lacosmic_accepts_quantity_readnoise(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise gain = 2.0 * u.electron / u.adu # The units below are the point of this test readnoise = 6.5 * u.electron _ = cosmicray_lacosmic(ccd_data, gain=gain, gain_apply=True, readnoise=readnoise) def test_cosmicray_lacosmic_detects_inconsistent_units(): # This is intended to detect cases like a ccd with units # of adu, a readnoise in electrons and a gain in adu / electron. # That is not internally inconsistent. ccd_data = ccd_data_func(data_scale=DATA_SCALE) ccd_data.unit = "adu" threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise readnoise = 6.5 * u.electron # The units below are deliberately incorrect. gain = 2.0 * u.adu / u.electron with pytest.raises(ValueError) as e: cosmicray_lacosmic(ccd_data, gain=gain, gain_apply=True, readnoise=readnoise) assert "Inconsistent units" in str(e.value) def test_cosmicray_lacosmic_warns_on_ccd_in_electrons(): # Check that an input ccd in electrons raises a warning. ccd_data = ccd_data_func(data_scale=DATA_SCALE) # The unit below is important for the test; this unit on # input is supposed to raise an error. ccd_data.unit = u.electron threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise # No units here on purpose. gain = 2.0 # Don't really need to set this (6.5 is the default value) but want to # make lack of units explicit. readnoise = 6.5 with pytest.warns(UserWarning, match="Image unit is electron"): cosmicray_lacosmic(ccd_data, gain=gain, gain_apply=True, readnoise=readnoise) # The values for inbkg and invar are DELIBERATELY BAD. They are supposed to be # arrays, so if detect_cosmics is called with these bad values a ValueError # will be raised, which we can check for. @pytest.mark.parametrize( "new_args", [dict(inbkg=5), dict(invar=5), dict(inbkg=5, invar=5)] ) def test_cosmicray_lacosmic_invar_inbkg(new_args): # This IS NOT TESTING FUNCTIONALITY it is simply testing # that calling with the new keyword arguments to astroscrappy # 1.1.0 raises no error. ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise with pytest.raises(TypeError): cosmicray_lacosmic(ccd_data, sigclip=5.9, **new_args) def test_cosmicray_median_check_data(): with pytest.raises(TypeError): ndata, crarr = cosmicray_median(10, thresh=5, mbox=11, error_image=DATA_SCALE) def test_cosmicray_median(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) ndata, crarr = cosmicray_median( ccd_data.data, thresh=5, mbox=11, error_image=DATA_SCALE ) # check the number of cosmic rays detected assert crarr.sum() == NCRAYS def test_cosmicray_median_ccddata(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) ccd_data.uncertainty = ccd_data.data * 0.0 + DATA_SCALE nccd = cosmicray_median(ccd_data, thresh=5, mbox=11, error_image=None) # check the number of cosmic rays detected assert nccd.mask.sum() == NCRAYS def test_cosmicray_median_masked(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) data = np.ma.masked_array(ccd_data.data, (ccd_data.data > -1e6)) ndata, crarr = cosmicray_median(data, thresh=5, mbox=11, error_image=DATA_SCALE) # check the number of cosmic rays detected assert crarr.sum() == NCRAYS def test_cosmicray_median_background_None(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) data, crarr = cosmicray_median(ccd_data.data, thresh=5, mbox=11, error_image=None) # check the number of cosmic rays detected assert crarr.sum() == NCRAYS def test_cosmicray_median_gbox(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) scale = DATA_SCALE # yuck. Maybe use pytest.parametrize? threshold = 5 add_cosmicrays(ccd_data, scale, threshold, ncrays=NCRAYS) error = ccd_data.data * 0.0 + DATA_SCALE data, crarr = cosmicray_median( ccd_data.data, error_image=error, thresh=5, mbox=11, rbox=0, gbox=5 ) data = np.ma.masked_array(data, crarr) assert crarr.sum() > NCRAYS assert abs(data.std() - scale) < 0.1 def test_cosmicray_median_rbox(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) scale = DATA_SCALE # yuck. Maybe use pytest.parametrize? threshold = 5 add_cosmicrays(ccd_data, scale, threshold, ncrays=NCRAYS) error = ccd_data.data * 0.0 + DATA_SCALE data, crarr = cosmicray_median( ccd_data.data, error_image=error, thresh=5, mbox=11, rbox=21, gbox=5 ) assert data[crarr].mean() < ccd_data.data[crarr].mean() assert crarr.sum() > NCRAYS def test_cosmicray_median_background_deviation(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) with pytest.raises(TypeError): cosmicray_median(ccd_data.data, thresh=5, mbox=11, error_image="blank") def test_background_deviation_box(): with NumpyRNGContext(123): scale = 5.3 cd = np.random.normal(loc=0, size=(100, 100), scale=scale) bd = background_deviation_box(cd, 25) assert abs(bd.mean() - scale) < 0.10 def test_background_deviation_box_fail(): with NumpyRNGContext(123): scale = 5.3 cd = np.random.normal(loc=0, size=(100, 100), scale=scale) with pytest.raises(ValueError): background_deviation_box(cd, 0.5) def test_background_deviation_filter(): with NumpyRNGContext(123): scale = 5.3 cd = np.random.normal(loc=0, size=(100, 100), scale=scale) bd = background_deviation_filter(cd, 25) assert abs(bd.mean() - scale) < 0.10 def test_background_deviation_filter_fail(): with NumpyRNGContext(123): scale = 5.3 cd = np.random.normal(loc=0, size=(100, 100), scale=scale) with pytest.raises(ValueError): background_deviation_filter(cd, 0.5) # This test can be removed in ccdproc 3.0 when support for old # astroscrappy is removed. def test_cosmicray_lacosmic_pssl_deprecation_warning(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) with pytest.warns(AstropyDeprecationWarning): cosmicray_lacosmic(ccd_data, pssl=1.0) def test_cosmicray_lacosmic_pssl_and_inbkg_fails(): ccd_data = ccd_data_func(data_scale=DATA_SCALE) with pytest.raises(ValueError) as err: # An error should be raised if both pssl and inbkg are provided with pytest.warns(AstropyDeprecationWarning): # The deprecation warning is expected and should be captured cosmicray_lacosmic(ccd_data, pssl=3, inbkg=ccd_data.data) assert "pssl and inbkg" in str(err) def test_cosmicray_lacosmic_pssl_does_not_fail(): # This test is a copy/paste of test_cosmicray_lacosmic_ccddata # except with pssl=0.0001 as an argument. Subtracting nearly zero from # the background should have no effect. The test is really # to make sure that passing in pssl does not lead to an error # since the new interface does not include pssl. ccd_data = ccd_data_func(data_scale=DATA_SCALE) threshold = 5 add_cosmicrays(ccd_data, DATA_SCALE, threshold, ncrays=NCRAYS) noise = DATA_SCALE * np.ones_like(ccd_data.data) ccd_data.uncertainty = noise with pytest.warns(AstropyDeprecationWarning): # The deprecation warning is expected and should be captured nccd_data = cosmicray_lacosmic(ccd_data, sigclip=5.9, pssl=0.0001) # check the number of cosmic rays detected # Note that to get this to succeed reliably meant tuning # both sigclip and the threshold assert nccd_data.mask.sum() == NCRAYS ccdproc-2.4.3/ccdproc/tests/test_gain.py0000644000000000000000000000405113615410400015163 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import astropy.units as u import numpy as np import pytest from ccdproc.core import Keyword, create_deviation, gain_correct from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func # tests for gain @pytest.mark.parametrize( "gain", [ 3.0, 3.0 * u.photon / u.adu, 3.0 * u.electron / u.adu, Keyword("gainval", unit=u.electron / u.adu), ], ) def test_linear_gain_correct(gain): ccd_data = ccd_data_func() # The data values should be positive, so the poisson noise calculation # works without throwing warnings ccd_data.data = np.absolute(ccd_data.data) ccd_data = create_deviation(ccd_data, readnoise=1.0 * u.adu) ccd_data.meta["gainval"] = 3.0 orig_data = ccd_data.data ccd = gain_correct(ccd_data, gain) if isinstance(gain, Keyword): gain = gain.value # convert to Quantity... try: gain_value = gain.value except AttributeError: gain_value = gain np.testing.assert_array_almost_equal_nulp(ccd.data, gain_value * orig_data) np.testing.assert_array_almost_equal_nulp( ccd.uncertainty.array, gain_value * ccd_data.uncertainty.array ) if isinstance(gain, u.Quantity): assert ccd.unit == ccd_data.unit * gain.unit else: assert ccd.unit == ccd_data.unit # test gain with gain_unit def test_linear_gain_unit_keyword(): ccd_data = ccd_data_func() # The data values should be positive, so the poisson noise calculation # works without throwing warnings ccd_data.data = np.absolute(ccd_data.data) ccd_data = create_deviation(ccd_data, readnoise=1.0 * u.adu) orig_data = ccd_data.data gain = 3.0 gain_unit = u.electron / u.adu ccd = gain_correct(ccd_data, gain, gain_unit=gain_unit) np.testing.assert_array_almost_equal_nulp(ccd.data, gain * orig_data) np.testing.assert_array_almost_equal_nulp( ccd.uncertainty.array, gain * ccd_data.uncertainty.array ) assert ccd.unit == ccd_data.unit * gain_unit ccdproc-2.4.3/ccdproc/tests/test_image_collection.py0000644000000000000000000013174213615410400017552 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import logging import os from glob import iglob from pathlib import Path from shutil import rmtree from tempfile import NamedTemporaryFile, TemporaryDirectory, mkdtemp import astropy.io.fits as fits import numpy as np import pytest from astropy.io.fits.verify import VerifyWarning from astropy.nddata import CCDData from astropy.table import Table from astropy.utils.data import get_pkg_data_filename from astropy.utils.exceptions import AstropyUserWarning from ccdproc.image_collection import ImageFileCollection _filters = [] _original_dir = "" def test_fits_summary(triage_setup): keywords = ["imagetyp", "filter"] ic = ImageFileCollection(triage_setup.test_dir, keywords=keywords) summary = ic._fits_summary(header_keywords=keywords) assert len(summary["file"]) == triage_setup.n_test["files"] for keyword in keywords: assert len(summary[keyword]) == triage_setup.n_test["files"] # Explicit conversion to array is needed to avoid astropy Table bug in # 0.2.4 no_filter_no_object_row = np.array( summary["file"] == "no_filter_no_object_bias.fit" ) # There should be no filter keyword in the bias file assert summary["filter"][no_filter_no_object_row].mask class TestImageFileCollectionRepresentation: def test_repr_location(self, triage_setup): ic = ImageFileCollection(location=triage_setup.test_dir) assert repr(ic) == f"ImageFileCollection(location={triage_setup.test_dir!r})" def test_repr_keywords(self, triage_setup): ic = ImageFileCollection(location=triage_setup.test_dir, keywords=["imagetyp"]) ref = ( f"ImageFileCollection(location={triage_setup.test_dir!r}, " "keywords=['imagetyp'])" ) assert repr(ic) == ref def test_repr_globs(self, triage_setup): ic = ImageFileCollection( location=triage_setup.test_dir, glob_exclude="*no_filter*", glob_include="*object_light*", ) ref = ( f"ImageFileCollection(location={triage_setup.test_dir!r}, " "glob_include='*object_light*', " "glob_exclude='*no_filter*')" ) assert repr(ic) == ref def test_repr_files(self, triage_setup): ic = ImageFileCollection( location=triage_setup.test_dir, filenames=["no_filter_no_object_light.fit", "no_filter_no_object_bias.fit"], ) ref = ( f"ImageFileCollection(location={triage_setup.test_dir!r}, " "filenames=['no_filter_no_object_light.fit', " "'no_filter_no_object_bias.fit'])" ) assert repr(ic) == ref def test_repr_ext(self, triage_setup): hdul = fits.HDUList( [fits.PrimaryHDU(np.ones((10, 10))), fits.ImageHDU(np.ones((10, 10)))] ) hdul.writeto(os.path.join(triage_setup.test_dir, "mef.fits")) ic = ImageFileCollection( location=triage_setup.test_dir, filenames=["mef.fits"], ext=1 ) ref = ( f"ImageFileCollection(location={triage_setup.test_dir!r}, " "filenames=['mef.fits'], " "ext=1)" ) assert repr(ic) == ref # This should work mark all test methods as using the triage_setup # fixture, but it doesn't, so the fixture is given explicitly as an # argument to each method. # @pytest.mark.usefixtures("triage_setup") class TestImageFileCollection: def _basenames(self, paths): return set([os.path.basename(file) for file in paths]) def _setup_logger(self, path, level=logging.WARN): """ Set up file logger at the path. """ logger = logging.getLogger() logger.setLevel(level) logger.addHandler(logging.FileHandler(path)) return logger def test_filter_files(self, triage_setup): img_collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp", "filter"] ) assert ( len(img_collection.files_filtered(imagetyp="bias")) == triage_setup.n_test["bias"] ) assert len(img_collection.files) == triage_setup.n_test["files"] assert "filter" in img_collection.keywords assert "flying monkeys" not in img_collection.keywords assert len(img_collection.values("imagetyp", unique=True)) == 2 def test_filter_files_whitespace_keys(self, triage_setup): hdr = fits.Header([("HIERARCH a b", 2)]) hdul = fits.HDUList([fits.PrimaryHDU(np.ones((10, 10)), header=hdr)]) hdul.writeto(os.path.join(triage_setup.test_dir, "hdr_with_whitespace.fits")) ic = ImageFileCollection(location=triage_setup.test_dir) # Using a dictionary and unpacking it should work filtered = ic.files_filtered(**{"a b": 2}) assert len(filtered) == 1 assert "hdr_with_whitespace.fits" in filtered # Also check it's working with generators: for _, filename in ic.data(a_b=2, replace_="_", return_fname=True): assert filename == "hdr_with_whitespace.fits" def test_filter_files_with_str_on_nonstr_column(self, triage_setup): ic = ImageFileCollection(location=triage_setup.test_dir) # Filtering an integer column with a string filtered = ic.files_filtered(naxis="2") assert len(filtered) == 0 def test_filter_fz_files(self, triage_setup): fn = "test.fits.fz" ic = ImageFileCollection(location=triage_setup.test_dir, filenames=fn) # Get a subset of files with a specific header value filtered = ic.files_filtered(exposure=15.0) assert len(filtered) == 1 def test_filtered_files_have_proper_path(self, triage_setup): ic = ImageFileCollection(location=triage_setup.test_dir, keywords="*") # Get a subset of the files. plain_biases = ic.files_filtered(imagetyp="bias") # Force a copy... plain_biases = list(plain_biases) # Same subset, but with full path. path_biases = ic.files_filtered(imagetyp="bias", include_path=True) for path_b, plain_b in zip(path_biases, plain_biases): # If the path munging has been done properly, this will succeed. assert os.path.basename(path_b) == plain_b def test_filenames_are_set_properly(self, triage_setup): fn = ["filter_no_object_bias.fit", "filter_object_light_foo.fit"] img_collection = ImageFileCollection( location=triage_setup.test_dir, filenames=fn, keywords=["filter"] ) assert img_collection.files == fn img_collection.refresh() assert img_collection.files == fn fn = "filter_no_object_bias.fit" img_collection = ImageFileCollection( location=triage_setup.test_dir, filenames=fn, keywords=["filter"] ) assert img_collection.files == [fn] def test_keywords_deleter(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords="*") assert ic.keywords != [] del ic.keywords assert ic.keywords == [] def test_files_with_compressed(self, triage_setup): collection = ImageFileCollection(location=triage_setup.test_dir) assert ( len(collection._fits_files_in_directory(compressed=True)) == triage_setup.n_test["files"] ) def test_files_with_no_compressed(self, triage_setup): collection = ImageFileCollection(location=triage_setup.test_dir) n_files_found = len(collection._fits_files_in_directory(compressed=False)) n_uncompressed = ( triage_setup.n_test["files"] - triage_setup.n_test["compressed"] ) assert n_files_found == n_uncompressed def test_generator_full_path(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) for path, file_name in zip(collection._paths(), collection.files): assert path == os.path.join(triage_setup.test_dir, file_name) def test_hdus(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) n_hdus = 0 for hdu in collection.hdus(): assert isinstance(hdu, fits.PrimaryHDU) data = hdu.data # Must access the data to force scaling # pre-astropy 1.1 unsigned data was changed to float32 and BZERO # removed. In 1.1 and later, BZERO stays but the data type is # unsigned int. assert ("BZERO" not in hdu.header) or (data.dtype is np.dtype(np.uint16)) n_hdus += 1 assert n_hdus == triage_setup.n_test["files"] def test_hdus_masking(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp", "exposure"] ) old_data = np.array(collection.summary) for _ in collection.hdus(imagetyp="bias"): pass new_data = np.array(collection.summary) assert (new_data == old_data).all() @pytest.mark.parametrize("extension", ["TESTEXT", 1, ("TESTEXT", 1)]) def test_multiple_extensions(self, triage_setup, extension): ext1 = fits.PrimaryHDU() ext1.data = np.arange(1, 5) # It is important than the name used for this test extension # NOT be MASK or UNCERT because both are treated in a special # way by the FITS reader. test_ext_name = "TESTEXT" ext2 = fits.ImageHDU(name=test_ext_name) ext2.data = np.arange(6, 10) hdulist = fits.hdu.hdulist.HDUList([ext1, ext2]) hdulist.writeto(os.path.join(triage_setup.test_dir, "multi-extension.fits")) ic2 = ImageFileCollection( triage_setup.test_dir, keywords="*", filenames=["multi-extension.fits"], ext=extension, ) ic1 = ImageFileCollection( triage_setup.test_dir, keywords="*", filenames=["multi-extension.fits"], ext=0, ) assert ic1.ext == 0 assert ic2.ext == extension column2 = ic2.summary.colnames column1 = ic1.summary.colnames assert column1 != column2 list1 = [key.lower() for key in ext2.header] list2 = ic2.summary.colnames[1:] assert list1 == list2 ccd_kwargs = {"unit": "adu"} for data, hdr, hdu, ccd in zip( ic2.data(), ic2.headers(), ic2.hdus(), ic2.ccds(ccd_kwargs) ): np.testing.assert_array_equal(data, ext2.data) assert hdr == ext2.header # Now compare that the generators each give the same stuff np.testing.assert_array_equal(data, ccd.data) np.testing.assert_array_equal(data, hdu.data) assert hdr == hdu.header assert hdr == ccd.meta def test_headers(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) n_headers = 0 for header in collection.headers(): assert isinstance(header, fits.Header) assert "bzero" in header n_headers += 1 assert n_headers == triage_setup.n_test["files"] def test_headers_save_location(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) destination = mkdtemp() for _ in collection.headers(save_location=destination): pass new_collection = ImageFileCollection( location=destination, keywords=["imagetyp"] ) assert ( len( self._basenames(collection._paths()) - self._basenames(new_collection._paths()) ) == 0 ) rmtree(destination) def test_headers_with_filter(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) cnt = 0 for header in collection.headers(imagetyp="light"): assert header["imagetyp"].lower() == "light" cnt += 1 assert cnt == triage_setup.n_test["light"] def test_headers_with_multiple_filters(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) cnt = 0 for header in collection.headers(imagetyp="light", filter="R"): assert header["imagetyp"].lower() == "light" assert header["filter"].lower() == "r" cnt += 1 assert cnt == ( triage_setup.n_test["light"] - triage_setup.n_test["missing_filter_value"] ) def test_headers_with_filter_wildcard(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) cnt = 0 for _ in collection.headers(imagetyp="*"): cnt += 1 assert cnt == triage_setup.n_test["files"] def test_headers_with_filter_missing_keyword(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) for header in collection.headers(imagetyp="light", object=""): assert header["imagetyp"].lower() == "light" with pytest.raises(KeyError): header["object"] def test_generator_headers_save_with_name(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) for header in collection.headers(save_with_name="_new"): assert isinstance(header, fits.Header) new_collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) assert ( len(new_collection._paths()) == 2 * (triage_setup.n_test["files"]) - triage_setup.n_test["compressed"] ) [os.remove(fil) for fil in iglob(triage_setup.test_dir + "/*_new*")] def test_generator_data(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) for img in collection.data(): assert isinstance(img, np.ndarray) def test_generator_ccds_without_unit(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) with pytest.raises(ValueError): _ = next(collection.ccds()) def test_generator_ccds(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) ccd_kwargs = {"unit": "adu"} for ccd in collection.ccds(ccd_kwargs=ccd_kwargs): assert isinstance(ccd, CCDData) def test_consecutive_fiilters(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp", "filter", "object"] ) no_files_match = collection.files_filtered(object="fdsafs") assert len(no_files_match) == 0 some_files_should_match = collection.files_filtered( object=None, imagetyp="light" ) assert len(some_files_should_match) == triage_setup.n_test["light"] def test_filter_does_not_not_permanently_change_file_mask(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) # Ensure all files are originally unmasked assert not collection.summary["file"].mask.any() # Generate list that will match NO files collection.files_filtered(imagetyp="foisajfoisaj") # If the code works, this should have no permanent effect assert not collection.summary["file"].mask.any() @pytest.mark.parametrize( "new_keywords,collection_keys", [ (["imagetyp", "object"], ["imagetyp", "filter"]), (["imagetyp"], ["imagetyp", "filter"]), ], ) def test_keyword_setting(self, new_keywords, collection_keys, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=collection_keys ) tbl_orig = collection.summary collection.keywords = new_keywords tbl_new = collection.summary if set(new_keywords).issubset(collection_keys): # Should just delete columns without rebuilding table assert tbl_orig is tbl_new else: # We need new keywords so must rebuild assert tbl_orig is not tbl_new for key in new_keywords: assert key in tbl_new.keys() assert (tbl_orig["file"] == tbl_new["file"]).all() assert (tbl_orig["imagetyp"] == tbl_new["imagetyp"]).all() assert "filter" not in tbl_new.keys() assert "object" not in tbl_orig.keys() def test_keyword_setting_to_empty_list(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir) ic.keywords = [] assert ["file"] == ic.keywords def test_header_and_filename(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp"] ) for header, fname in collection.headers(return_fname=True): assert fname in collection.summary["file"] assert isinstance(header, fits.Header) def test_dir_with_no_fits_files(self, tmpdir): empty_dir = tmpdir.mkdtemp() some_file = empty_dir.join("some_file.txt") some_file.dump("words") with pytest.warns(Warning) as w: collection = ImageFileCollection( location=empty_dir.strpath, keywords=["imagetyp"] ) assert len(w) == 1 assert str(w[0].message) == "no FITS files in the collection." assert collection.summary is None for _ in collection.headers(): # This statement should not be reached if there are no FITS files assert 0 def test_dir_with_no_keys(self, tmpdir): # This test should fail if the FITS files in the directory # are actually read. bad_dir = tmpdir.mkdtemp() not_really_fits = bad_dir.join("not_fits.fit") not_really_fits.dump("I am not really a FITS file") # Make sure an error will be generated if the FITS file is read with pytest.raises(IOError): fits.getheader(not_really_fits.strpath) log = tmpdir.join("tmp.log") self._setup_logger(log.strpath) _ = ImageFileCollection(location=bad_dir.strpath, keywords=[]) with open(log.strpath) as f: warnings = f.read() # ImageFileCollection will suppress the IOError but log a warning # so check that the log has no warnings in it. assert len(warnings) == 0 def test_fits_summary_when_keywords_are_not_subset(self, triage_setup): """ Catch case when there is overlap between keyword list passed to the ImageFileCollection and to files_filtered but the latter is not a subset of the former. """ ic = ImageFileCollection( triage_setup.test_dir, keywords=["imagetyp", "exposure"] ) n_files = len(ic.files) files_missing_this_key = ic.files_filtered(imagetyp="*", monkeys=None) assert n_files > 0 assert n_files == len(files_missing_this_key) def test_duplicate_keywords_in_setting(self, triage_setup): keywords_in = ["imagetyp", "a", "a"] ic = ImageFileCollection(triage_setup.test_dir, keywords=keywords_in) for key in set(keywords_in): assert key in ic.keywords # One keyword gets added: file assert len(ic.keywords) < len(keywords_in) + 1 def test_keyword_includes_file(self, triage_setup): keywords_in = ["file", "imagetyp"] ic = ImageFileCollection(triage_setup.test_dir, keywords=keywords_in) assert "file" in ic.keywords file_keywords = [key for key in ic.keywords if key == "file"] assert len(file_keywords) == 1 def test_setting_keywords_to_none(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords=["imagetyp"]) ic.keywords = None assert ic.summary == [] def test_getting_value_for_keyword(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords=["imagetyp"]) # Does it fail if the keyword is not in the summary? with pytest.raises(ValueError): ic.values("filter") # If I ask for unique values do I get them? values = ic.values("imagetyp", unique=True) assert values == list(set(ic.summary["imagetyp"])) assert len(values) < len(ic.summary["imagetyp"]) # Does the list of non-unique values match the raw column? values = ic.values("imagetyp", unique=False) assert values == list(ic.summary["imagetyp"]) # Does unique actually default to false? values2 = ic.values("imagetyp") assert values == values2 def test_collection_when_one_file_not_fits(self, triage_setup): not_fits = "foo.fit" path_bad = os.path.join(triage_setup.test_dir, not_fits) # Create an empty file... with open(path_bad, "w"): pass ic = ImageFileCollection(triage_setup.test_dir, keywords=["imagetyp"]) assert not_fits not in ic.summary["file"] os.remove(path_bad) def test_data_type_mismatch_in_fits_keyword_values(self, triage_setup): # If one keyword has an unexpected type, do we notice? img = np.uint16(np.arange(100)) bad_filter = fits.PrimaryHDU(img) bad_filter.header["imagetyp"] = "LIGHT" bad_filter.header["filter"] = 15.0 path_bad = os.path.join(triage_setup.test_dir, "bad_filter.fit") bad_filter.writeto(path_bad) ic = ImageFileCollection(triage_setup.test_dir, keywords=["filter"]) # dtype is object when there is a mix of types assert ic.summary["filter"].dtype == np.dtype("O") os.remove(path_bad) def test_filter_by_numerical_value(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords=["naxis"]) should_be_zero = ic.files_filtered(naxis=2) assert len(should_be_zero) == 0 should_not_be_zero = ic.files_filtered(naxis=1) assert len(should_not_be_zero) == triage_setup.n_test["files"] def test_files_filtered_with_full_path(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords=["naxis"]) files = ic.files_filtered(naxis=1, include_path=True) for f in files: assert f.startswith(triage_setup.test_dir) def test_unknown_generator_type_raises_error(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords=["naxis"]) with pytest.raises(ValueError): for _ in ic._generator("not a real generator"): pass def test_setting_write_location_to_bad_dest_raises_error( self, tmpdir, triage_setup ): new_tmp = tmpdir.mkdtemp() bad_directory = new_tmp.join("foo") ic = ImageFileCollection(triage_setup.test_dir, keywords=["naxis"]) with pytest.raises(IOError): for _ in ic.headers(save_location=bad_directory.strpath): pass def test_initialization_with_no_keywords(self, triage_setup): # This test is primarily historical -- the old default for # keywords was an empty list (it is now the wildcard '*'). ic = ImageFileCollection(location=triage_setup.test_dir, keywords=[]) # Iteration below failed before bugfix... execs = 0 for _ in ic.headers(): execs += 1 assert not execs def check_all_keywords_in_collection(self, image_collection): lower_case_columns = [c.lower() for c in image_collection.summary.colnames] for h in image_collection.headers(): for k in h: assert k.lower() in lower_case_columns def test_tabulate_all_keywords(self, triage_setup): ic = ImageFileCollection(location=triage_setup.test_dir, keywords="*") self.check_all_keywords_in_collection(ic) def test_summary_table_is_always_masked(self, triage_setup): # First, try grabbing all of the keywords ic = ImageFileCollection(location=triage_setup.test_dir, keywords="*") assert ic.summary.masked # Now, try keywords that every file will have ic.keywords = ["bitpix"] assert ic.summary.masked # What about keywords that include some that will surely be missing? ic.keywords = ["bitpix", "dsafui"] assert ic.summary.masked def test_case_of_keywords_respected(self, triage_setup): keywords_in = ["BitPix", "instrume", "NAXIS"] ic = ImageFileCollection(location=triage_setup.test_dir, keywords=keywords_in) for key in keywords_in: assert key in ic.summary.colnames def test_grabbing_all_keywords_and_specific_keywords(self, triage_setup): keyword_not_in_headers = "OIdn89!@" ic = ImageFileCollection( triage_setup.test_dir, keywords=["*", keyword_not_in_headers] ) assert keyword_not_in_headers in ic.summary.colnames self.check_all_keywords_in_collection(ic) def test_grabbing_all_keywords_excludes_empty_key(self, triage_setup): # This test needs a file with a blank keyword in it to ensure # that case is handled correctly. blank_keyword = fits.PrimaryHDU() blank_keyword.data = np.zeros((100, 100)) blank_keyword.header[""] = "blank" blank_keyword.writeto(os.path.join(triage_setup.test_dir, "blank.fits")) ic = ImageFileCollection(triage_setup.test_dir, keywords="*") assert "col0" not in ic.summary.colnames @pytest.mark.skipif( "os.environ.get('APPVEYOR') or os.sys.platform == 'win32'", reason="fails on Windows because of file permissions.", ) def test_header_with_long_history_roundtrips_to_disk(self, triage_setup): # I tried combing several history comments into one table entry with # '\n'.join(history), which resulted in a table that couldn't # round trip to disk because on read the newline character was # interpreted as...a new line! This test is a check against future # foolishness. from astropy.table import Table img = np.uint16(np.arange(100)) long_history = fits.PrimaryHDU(img) long_history.header["imagetyp"] = "BIAS" long_history.header["history"] = "Something happened" long_history.header["history"] = "Then something else happened" long_history.header["history"] = "And then something odd happened" path_history = os.path.join(triage_setup.test_dir, "long_history.fit") long_history.writeto(path_history) ic = ImageFileCollection(triage_setup.test_dir, keywords="*") with NamedTemporaryFile() as test_table: ic.summary.write(test_table.name, format="ascii.csv", overwrite=True) table_disk = Table.read(test_table.name, format="ascii.csv") assert len(table_disk) == len(ic.summary) @pytest.mark.skipif( "os.environ.get('APPVEYOR') or os.sys.platform == 'win32'", reason="fails on Windows because file " "overwriting fails", ) def test_refresh_method_sees_added_keywords(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords="*") # Add a keyword I know isn't already in the header to each file. not_in_header = "BARKARK" for h in ic.headers(overwrite=True): h[not_in_header] = True assert not_in_header not in ic.summary.colnames ic.refresh() # After refreshing the odd keyword should be present. assert not_in_header.lower() in ic.summary.colnames def test_refresh_method_sees_added_files(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir, keywords="*") # Compressed files don't get copied. Not sure why... original_len = len(ic.summary) - triage_setup.n_test["compressed"] # Generate additional files in this directory for _ in ic.headers(save_with_name="_foo"): pass ic.refresh() new_len = len(ic.summary) - triage_setup.n_test["compressed"] assert new_len == 2 * original_len def test_keyword_order_is_preserved(self, triage_setup): keywords = ["imagetyp", "exposure", "filter"] ic = ImageFileCollection(triage_setup.test_dir, keywords=keywords) assert ic.keywords == ["file"] + keywords def test_sorting(self, triage_setup): collection = ImageFileCollection( location=triage_setup.test_dir, keywords=["imagetyp", "filter", "object"] ) all_elements = [] for hdu, fname in collection.hdus(return_fname=True): all_elements.append((str(hdu.header), fname)) # Now sort collection.sort(keys=["imagetyp", "object"]) # and check it's all still right for hdu, fname in collection.hdus(return_fname=True): assert (str(hdu.header), fname) in all_elements for i in range(len(collection.summary)): assert collection.summary["file"][i] == collection.files[i] def test_sorting_without_key_fails(self, triage_setup): ic = ImageFileCollection(location=triage_setup.test_dir) with pytest.raises(ValueError): ic.sort(keys=None) def test_duplicate_keywords(self, triage_setup): # Make sure duplicated keywords don't make the imagefilecollection # fail. hdu = fits.PrimaryHDU() hdu.data = np.zeros((5, 5)) hdu.header["stupid"] = "fun" hdu.header.append(("stupid", "nofun")) hdu.writeto(os.path.join(triage_setup.test_dir, "duplicated.fits")) with pytest.warns(UserWarning) as w: ic = ImageFileCollection(triage_setup.test_dir, keywords="*") assert len(w) == 1 assert "stupid" in str(w[0].message) assert "stupid" in ic.summary.colnames assert "fun" in ic.summary["stupid"] assert "nofun" not in ic.summary["stupid"] def test_ccds_generator_in_different_directory(self, triage_setup, tmpdir): """ Regression test for https://github.com/astropy/ccdproc/issues/421 in which the ccds generator fails if the current working directory is not the location of the ImageFileCollection. """ coll = ImageFileCollection(triage_setup.test_dir) # The temporary directory below should be different that the collection # location. os.chdir(tmpdir.strpath) # Let's make sure it is. assert not os.path.samefile(os.getcwd(), coll.location) # This generated an IOError before the issue was fixed. for _ in coll.ccds(ccd_kwargs={"unit": "adu"}): pass def test_ccds_generator_does_not_support_overwrite(self, triage_setup): """ CCDData objects have several attributes that make it hard to reliably support overwriting. For example in what extension should mask, uncertainty be written? Also CCDData doesn't explicitly support in-place operations so it's to easy to create a new CCDData object inadvertantly and all modifications might be lost. """ ic = ImageFileCollection(triage_setup.test_dir) with pytest.raises(NotImplementedError): ic.ccds(overwrite=True) with pytest.deprecated_call(), pytest.raises(NotImplementedError): ic.ccds(clobber=True) def test_glob_matching(self, triage_setup): # We'll create two files with strange names to test glob # includes / excludes one = fits.PrimaryHDU() one.data = np.zeros((5, 5)) one.header[""] = "whatever" one.writeto(os.path.join(triage_setup.test_dir, "SPAM_stuff.fits")) one.writeto(os.path.join(triage_setup.test_dir, "SPAM_other_stuff.fits")) coll = ImageFileCollection(triage_setup.test_dir, glob_include="SPAM*") assert len(coll.files) == 2 coll = ImageFileCollection( triage_setup.test_dir, glob_include="SPAM*", glob_exclude="*other*" ) assert len(coll.files) == 1 # The glob attributes are readonly, so setting them raises an Exception. with pytest.raises(AttributeError): coll.glob_exclude = "*stuff*" with pytest.raises(AttributeError): coll.glob_include = "*stuff*" def test_that_test_files_have_expected_properties(self, triage_setup): expected_name = get_pkg_data_filename("data/expected_ifc_file_properties.csv") expected = Table.read(expected_name) # Make the comparison more reliable by sorting expected.sort("file") ic = ImageFileCollection(triage_setup.test_dir) actual = ic.summary # Write the actual IFC summary out to disk to turn bool into strings of # "True" and "False", and any other non-essential differences between # the tables. tmp_file = "actual.csv" actual.write(tmp_file) actual = Table.read(tmp_file) # Make the comparison more reliable by sorting actual.sort("file") assert len(ic.summary) == len(expected) for column in expected.colnames: assert np.all(actual[column] == expected[column]) def test_image_collection_with_no_location(self, triage_setup): # Test for a feature requested in # # https://github.com/astropy/ccdproc/issues/374 # # and fix the bug reported in # # https://github.com/astropy/ccdproc/issues/662 # # Create a collection from a list of file names (which can include # path as needed) source_path = Path(triage_setup.test_dir) # Put the first three files in source_path into temp_path below # then create the image collection out of the three in temp_path and # the rest in source_path. source_files = [p for p in source_path.iterdir()] move_to_temp = source_files[:3] keep_in_source = source_files[3:] with TemporaryDirectory() as td: temp_dir = Path(td) file_paths = [] for source in move_to_temp: temp_path = temp_dir / source.name temp_path.write_bytes(source.read_bytes()) file_paths.append(str(temp_path)) file_paths.extend(str(p) for p in keep_in_source) # Move up a level to make sure we are not accidentally # pulling in files from the current working directory, # which includes everythin in source. os.chdir("..") ic = ImageFileCollection(filenames=file_paths) assert len(ic.summary) == len(file_paths) expected_name = get_pkg_data_filename( "data/expected_ifc_file_properties.csv" ) expected = Table.read(expected_name) # Make the comparison more reliable by sorting expected.sort("file") actual = ic.summary # Write the actual IFC summary out to disk to turn bool into strings # of"True" and "False", and any other non-essential differences # between the tables. tmp_file = "actual.csv" actual.write(tmp_file) actual = Table.read(tmp_file) # Make the comparison more reliable by sorting...but the actual # in this case includes paths, so we really want to sort by the # base name of the file. bases = np.array([Path(f).name for f in actual["file"]]) sort_order = np.argsort(bases) actual = actual[sort_order] bases = bases[sort_order] assert all(Path(f).exists() for f in actual["file"]) for column in expected.colnames: if column == "file": assert np.all(bases == expected[column]) else: assert np.all(actual[column] == expected[column]) # Set comparisons don't care about order :) # Check several of the ways we can get file names from the # collection and ensure all of them include the path. assert set(file_paths) == set(ic.summary["file"]) assert set(file_paths) == set(ic.files) assert set(file_paths) == set(ic.files_filtered(include_path=True)) # Spot check a couple of dtypes as a test for # https://github.com/astropy/ccdproc/issues/662 assert ic.summary["extend"].dtype == "bool" # Of course, default dtypes on Windows are different. So instead # of comparing to something sensible like int64, compare to the # default int dtype. assert ic.summary["naxis1"].dtype == np.array([5]).dtype # and the default float dtype assert ic.summary["exposure"].dtype == np.array([5.0]).dtype expected_heads = (actual["imagetyp"] == "LIGHT").sum() n_heads = 0 # Try one of the generators for h in ic.headers(imagetyp="light"): assert h["imagetyp"].lower() == "light" n_heads += 1 assert n_heads == expected_heads def test_force_detect_fits_files_finds_fits_files(self, triage_setup): # Tests for new feature # # https://github.com/astropy/ccdproc/issues/620 # # which supports adding all of the FITS files in a location based on # their *contents* instead of their *extension*. # Grab files from the default collection and make a copy with a new name # (and with no fits-like extension) # # Making a copy of *every* file means we can just double the expected # number of files as part of the tests. path = Path(triage_setup.test_dir) for idx, p in enumerate(path.iterdir()): new_name = f"no_extension{idx}" new_path = path / new_name new_path.write_bytes(p.read_bytes()) ic = ImageFileCollection(location=str(path), find_fits_by_reading=True) # Compressed files won't be automagically detected by reading the # first few bytes. expected_number = ( 2 * triage_setup.n_test["files"] - triage_setup.n_test["compressed"] ) assert len(ic.summary) == expected_number n_bias = (ic.summary["imagetyp"] == "BIAS").sum() assert n_bias == 2 * triage_setup.n_test["bias"] # Only one file in the original set of test files has exposure time # 15, so there should be two now. assert len(ic.files_filtered(exposure=15.0)) == 2 # Try one of the generators expected_heads = ( 2 * triage_setup.n_test["light"] - triage_setup.n_test["compressed"] ) n_heads = 0 for h in ic.headers(imagetyp="light"): assert h["imagetyp"].lower() == "light" n_heads += 1 assert n_heads == expected_heads @pytest.mark.filterwarnings( "ignore:The following header keyword is invalid:UserWarning" ) def test_less_strict_verify_option(self, triage_setup): # Tests for feature request # # https://github.com/astropy/ccdproc/issues/607 # # which would allow reading of otherwise invalid FITS headers. bad_header = """ NAXIS1 = 10 / length of data axis 1 NAXIS2 = 10 / length of data axis 2 TESTVERI= '2017/02/13-16:51:38 / Test VerifyWarning """ with pytest.warns(VerifyWarning): testh = fits.Header.fromstring(bad_header) testfits = fits.PrimaryHDU(data=np.ones((10, 10)), header=testh) path = Path(triage_setup.test_dir) bad_fits_name = "test_warnA.fits" testfits.writeto(path / bad_fits_name, output_verify="warn", overwrite=True) ic = ImageFileCollection(location=str(path)) assert bad_fits_name in ic.files # Turns out this sample header is so messed up that TESTVERI does not # end up as a keyword. assert "TESTVERI" not in ic.summary.colnames # This does end up as a key some how *shrug*. assert "17/02/13" in ic.summary.colnames # Try making the summary as in the original bug report with pytest.warns( AstropyUserWarning, match="The following header keyword is invalid" ): ic = ImageFileCollection(location=str(path), glob_include="*warnA*") def test_type_of_empty_collection(self, triage_setup): # Test for implementation of the suggestion in # # https://github.com/astropy/ccdproc/issues/601 # # in which an empty collection with no keys has but with files # returns a summary table with one column, but an empty collection # with no keys and no files returns None. # Make a dummy keyword that we then delete. ic = ImageFileCollection(triage_setup.test_dir, keywords=["fafa"]) ic.keywords = [] assert set(ic.summary.colnames) == set(["file"]) # Remove all of the fits files path = Path(triage_setup.test_dir) for p in path.iterdir(): p.unlink() # Now the summary should be none with pytest.warns(AstropyUserWarning, match="no FITS files in the collection"): ic = ImageFileCollection(triage_setup.test_dir) assert ic.summary is None assert ic.keywords == [] assert ic.files_filtered() == [] def test_regex_match_for_search(self, triage_setup): # Test regex matching in searches ic = ImageFileCollection(triage_setup.test_dir) files = ic.files_filtered(regex_match=True, imagetyp="b.*s") assert len(files) == triage_setup.n_test["bias"] # This should return all of the files in the test set all_files = ic.files_filtered(regex_match=True, imagetyp="bias|light") assert len(all_files) == triage_setup.n_test["files"] # Add a column with more interesting content and see whether we # match that. ic.summary["match_me"] = [ "hello", "goodbye", "bye", "byte", "good bye hello", "dog", ] hello_anywhere = ic.files_filtered(regex_match=True, match_me="hello") assert len(hello_anywhere) == 2 hello_start = ic.files_filtered(regex_match=True, match_me="^hello") assert len(hello_start) == 1 # Is it really a case-insensitive match? hello_start = ic.files_filtered(regex_match=True, match_me="^HeLlo") assert len(hello_start) == 1 any_bye = ic.files_filtered(regex_match=True, match_me="by.*e") assert len(any_bye) == 4 def test_generator_with_regex(self, triage_setup): ic = ImageFileCollection(triage_setup.test_dir) n_light = 0 for h in ic.headers(regex_match=True, imagetyp="li.*t"): assert h["imagetyp"].lower() == "light" n_light += 1 assert n_light == triage_setup.n_test["light"] def test_make_collection_by_filtering(self, triage_setup): # Test for implementation of feature at # # https://github.com/astropy/ccdproc/issues/596 # # which adds the ability to create a new collection by filtering # an existing ImageFileCollection. ic = ImageFileCollection(location=triage_setup.test_dir) new_ic = ic.filter(imagetyp="light") assert len(new_ic.summary) == triage_setup.n_test["light"] for header in new_ic.headers(): assert header["imagetyp"].lower() == "light" def test_filtered_collection_with_no_files(self, triage_setup): ifc = ImageFileCollection(triage_setup.test_dir) with pytest.warns(AstropyUserWarning, match="no FITS files"): _ = ifc.filter(object="really fake object") def test_filter_on_regex_escape_characters(self, triage_setup): # Test for implementation of bugfix at # # https://github.com/astropy/ccdproc/issues/770 # # which escapes regex special characters in keyword values used for # filtering a collection, when option `regex_match=False`. # For a few different special characters, make test files with FILTER # keyword containing these special_kwds = ["CO+", "GG420 (1)", "V|R|I", "O[III]", "NaD^2"] for i, kw in enumerate(special_kwds, 1): hdu = fits.PrimaryHDU() hdu.data = np.zeros((5, 5)) hdu.header["REGEX_FL"] = kw hdu.writeto( os.path.join(triage_setup.test_dir, f"regex_special_{i:d}.fits") ) ic = ImageFileCollection(triage_setup.test_dir) for kw in special_kwds: new_ic = ic.filter(regex_fl=kw) assert len(new_ic.files) == 1 assert kw in new_ic.summary["regex_fl"] ccdproc-2.4.3/ccdproc/tests/test_keyword.py0000644000000000000000000000377213615410400015742 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import pytest from astropy import units as u from astropy.io import fits from ccdproc.core import Keyword def test_keyword_init(): key_name = "some_key" key = Keyword(key_name, unit=u.second) assert key.name == key_name assert key.unit == u.second def test_keyword_properties_read_only(): key = Keyword("observer") with pytest.raises(AttributeError): key.name = "error" with pytest.raises(AttributeError): key.unit = u.hour unit = u.second numerical_value = 30 # The variable "expected" below is # True if the expected result is key.value == numerical_value * key.unit # Name of an error if an error is expected # A string if the expected value is a string @pytest.mark.parametrize( "value,unit,expected", [ (numerical_value, unit, True), (numerical_value, None, ValueError), (numerical_value * unit, None, True), (numerical_value * unit, unit, True), (numerical_value * unit, u.km, True), ("some string", None, "some string"), ("no strings with unit", unit, ValueError), ], ) def test_value_setting(value, unit, expected): name = "exposure" # Setting at initialization time with try: expected_is_error = issubclass(expected, Exception) except TypeError: expected_is_error = False if expected_is_error: with pytest.raises(expected): key = Keyword(name, unit=unit, value=value) else: key = Keyword(name, unit=unit, value=value) if isinstance(expected, str): assert key.value == expected else: assert key.value == numerical_value * key.unit def test_keyword_value_from_header(): name = "exposure" numerical_value = 30 unit = u.second h = fits.Header() h[name] = numerical_value key = Keyword(name, unit=unit) assert key.value_from(h) == numerical_value * unit assert key.value == numerical_value * unit ccdproc-2.4.3/ccdproc/tests/test_memory_use.py0000644000000000000000000000513513615410400016435 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst from sys import platform import numpy as np import pytest try: from ccdproc.tests.run_for_memory_profile import ( TMPPATH, generate_fits_files, run_memory_profile, ) except ImportError: memory_profile_present = False else: memory_profile_present = True image_size = 2000 # Square image, so 4000 x 4000 num_files = 10 def setup_module(): if memory_profile_present: generate_fits_files(num_files, size=image_size) def teardown_module(): if memory_profile_present: for fil in TMPPATH.glob("*.fit"): fil.unlink() @pytest.mark.skipif( not platform.startswith("linux"), reason="memory tests only work on linux" ) @pytest.mark.skipif(not memory_profile_present, reason="memory_profiler not installed") @pytest.mark.parametrize("combine_method", ["average", "sum", "median"]) def test_memory_use_in_combine(combine_method): # This is essentially a regression test for # https://github.com/astropy/ccdproc/issues/638 # sampling_interval = 0.01 # sec memory_limit = 500000000 # bytes, roughly 0.5GB mem_use, _ = run_memory_profile( num_files, sampling_interval, memory_limit=memory_limit, combine_method=combine_method, ) mem_use = np.array(mem_use) # We do not expect memory use to be strictly less than memory_limit # throughout the combination. The factor below allows for that. # It may need to be raised in the future...that is fine, there is a # separate test for average memory use. overhead_allowance = 1.75 # memory_profile reports in MB (no, this is not the correct conversion) memory_limit_mb = memory_limit / 1e6 # Checks for TOO MUCH MEMORY USED # Check peak memory use assert np.max(mem_use) <= overhead_allowance * memory_limit_mb # Also check average, which gets no allowance assert np.mean(mem_use) < memory_limit_mb # Checks for NOT ENOUGH MEMORY USED; if these fail it means that # memory_factor in the combine function should perhaps be modified # DROPPED THESE TESTS -- it isn't clear they were actually useful and # in any event the important thing to guarantee is that we don't # exceed the memory limit. # If the peak is coming in under the limit something need to be fixed # assert np.max(mem_use) >= 0.95 * memory_limit_mb # If the average is really low perhaps we should look at reducing peak # usage. Nothing special, really, about the factor 0.4 below. # assert np.mean(mem_use[mem_use > 0]) > 0.4 * memory_limit_mb ccdproc-2.4.3/ccdproc/tests/test_rebin.py0000644000000000000000000000457413615410400015356 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np import pytest from astropy.nddata import StdDevUncertainty from astropy.utils.exceptions import AstropyDeprecationWarning from ccdproc.core import rebin from ccdproc.tests.pytest_fixtures import ccd_data as ccd_data_func # test rebinning ndarray def test_rebin_ndarray(): with pytest.raises(TypeError), pytest.warns(AstropyDeprecationWarning): rebin(1, (5, 5)) # test rebinning dimensions def test_rebin_dimensions(): ccd_data = ccd_data_func(data_size=10) with pytest.raises(ValueError), pytest.warns(AstropyDeprecationWarning): rebin(ccd_data.data, (5,)) # test rebinning dimensions def test_rebin_ccddata_dimensions(): ccd_data = ccd_data_func(data_size=10) with pytest.raises(ValueError), pytest.warns(AstropyDeprecationWarning): rebin(ccd_data, (5,)) # test rebinning works def test_rebin_larger(): ccd_data = ccd_data_func(data_size=10) a = ccd_data.data with pytest.warns(AstropyDeprecationWarning): b = rebin(a, (20, 20)) assert b.shape == (20, 20) np.testing.assert_almost_equal(b.sum(), 4 * a.sum()) # test rebinning is invariant def test_rebin_smaller(): ccd_data = ccd_data_func(data_size=10) a = ccd_data.data with pytest.warns(AstropyDeprecationWarning): b = rebin(a, (20, 20)) c = rebin(b, (10, 10)) assert c.shape == (10, 10) assert (c - a).sum() == 0 # test rebinning with ccddata object @pytest.mark.parametrize("mask_data, uncertainty", [(False, False), (True, True)]) def test_rebin_ccddata(mask_data, uncertainty): ccd_data = ccd_data_func(data_size=10) if mask_data: ccd_data.mask = np.zeros_like(ccd_data) if uncertainty: err = np.random.normal(size=ccd_data.shape) ccd_data.uncertainty = StdDevUncertainty(err) with pytest.warns(AstropyDeprecationWarning): b = rebin(ccd_data, (20, 20)) assert b.shape == (20, 20) if mask_data: assert b.mask.shape == (20, 20) if uncertainty: assert b.uncertainty.array.shape == (20, 20) def test_rebin_does_not_change_input(): ccd_data = ccd_data_func() original = ccd_data.copy() with pytest.warns(AstropyDeprecationWarning): _ = rebin(ccd_data, (20, 20)) np.testing.assert_array_equal(original.data, ccd_data.data) assert original.unit == ccd_data.unit ccdproc-2.4.3/ccdproc/tests/test_wrapped_external_funcs.py0000644000000000000000000000456013615410400021014 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np from astropy.nddata import CCDData, StdDevUncertainty from scipy import ndimage from ccdproc import core def test_medianfilter_correct(): ccd = CCDData( [ [2, 6, 6, 1, 7, 2, 4, 5, 9, 1], [10, 10, 9, 0, 2, 10, 8, 3, 9, 7], [2, 4, 0, 4, 4, 10, 0, 5, 6, 5], [7, 10, 8, 7, 7, 0, 5, 3, 5, 9], [9, 6, 3, 8, 6, 9, 2, 8, 10, 10], [6, 5, 1, 7, 8, 0, 8, 2, 9, 3], [0, 6, 0, 6, 3, 10, 8, 9, 7, 8], [5, 8, 3, 2, 3, 0, 2, 0, 3, 5], [9, 6, 3, 7, 1, 0, 5, 4, 8, 3], [5, 6, 9, 9, 0, 4, 9, 1, 7, 8], ], unit="adu", ) result = core.median_filter(ccd, 3) assert isinstance(result, CCDData) assert np.all( result.data == [ [6, 6, 6, 6, 2, 4, 4, 5, 5, 7], [4, 6, 4, 4, 4, 4, 5, 5, 5, 6], [7, 8, 7, 4, 4, 5, 5, 5, 5, 7], [7, 6, 6, 6, 7, 5, 5, 5, 6, 9], [7, 6, 7, 7, 7, 6, 3, 5, 8, 9], [6, 5, 6, 6, 7, 8, 8, 8, 8, 8], [5, 5, 5, 3, 3, 3, 2, 7, 5, 5], [6, 5, 6, 3, 3, 3, 4, 5, 5, 5], [6, 6, 6, 3, 2, 2, 2, 4, 4, 5], [6, 6, 7, 7, 4, 4, 4, 7, 7, 8], ] ) assert result.unit == "adu" assert all( getattr(result, attr) is None for attr in ["mask", "uncertainty", "wcs", "flags"] ) # The following test could be deleted if log_to_metadata is also applied. assert not result.meta def test_medianfilter_unusued(): ccd = CCDData( np.ones((3, 3)), unit="adu", mask=np.ones((3, 3)), uncertainty=StdDevUncertainty(np.ones((3, 3))), flags=np.ones((3, 3)), ) result = core.median_filter(ccd, 3) assert isinstance(result, CCDData) assert result.unit == "adu" assert all( getattr(result, attr) is None for attr in ["mask", "uncertainty", "wcs", "flags"] ) # The following test could be deleted if log_to_metadata is also applied. assert not result.meta def test_medianfilter_ndarray(): arr = np.random.random((5, 5)) result = core.median_filter(arr, 3) reference = ndimage.median_filter(arr, 3) # It's a wrapped function so we can use the equal comparison. np.testing.assert_array_equal(result, reference) ccdproc-2.4.3/ccdproc/tests/data/README.rst0000644000000000000000000000040013615410400015226 0ustar00Data directory ============== This directory contains data files included with the affiliated package source code distribution. Note that this is intended only for relatively small files - large files should be externally hosted and downloaded as needed. ccdproc-2.4.3/ccdproc/tests/data/a8280271.fits0000644000000000000000000211070013615410400015431 0ustar00SIMPLE = T / BITPIX = 16 / NAXIS = 2 / NAXIS1 = 536 / columns NAXIS2 = 520 / rows OBSERVAT= 'SAAO ' / observatory source TELESCOP= 'SAAO 1.0m' / OBSERVER= 'crawford ' / observer INSTRUME= 'STE3 CCD' / DATE-OBS= '2013-07-13' / UT date OBJECT = 'rf0420 ' / IMAGETYP= 'object ' / IRAF image type FILTERS = 48 / Filters 10*A+B RA = ' 22:04:08' / ra DEC = '-00:55:31' / dec RA_OBS = 331.0334 / ra in degrees DEC_OBS = -0.9253 / dec in degrees EPOCH = 2000.0 / Equinox of RA,Dec UT = '00:57:33' / UT at Exposure start ST = '21:45:06' / Siderial time at Start exp EXPTIME = 150.040 / integration time in secs SECZ = 1.176 / Air mass at start exp TRIMSEC = '[ 17: 528, 1: 520]' / Useful part of data BIASSEC = '[ 4: 13, 1: 520]' / Overscan region BSCALE = 1 / DATA=BSCALE*INT+BZERO BZERO = 32768 / GAIN = 1.9 / e-/ADU RDNOISE = 5.0 / e-(rms) read noise CCD-TEMP= 180.2 / CCD Cu block temperature MJD-OBS = 56486.0 COMMENT = END €»€Æ€Ó€Ô€Ñ€Ô€Ù€Ñ€Ö€Ö€Ò€×€×€Ö€×€Ò$&/%+%,$/$#$'7 )4 &&/)+5 $'%, *&(%/#'+! '*8/&,-&04)&*1!+$'##%!7"*'(-%4) $-5#71&''&4%#&' ),'!5(,%'&','%)"-.( -*$&1-" '!'5"#"(($+-5'';('*,1,,.*8#3)'%!/4+1;(/ /$(#'%.&%!%!2%*!$1())' %5/%,2")% &'7!0*!''&!)/-%*%%.+%.('.*)+#,$*)2.,-*+) ,&#%%#>.0-#"//)&-+/$-(**/+*$!)*523,)",&4!!/+'0')()++/ $&3&1-9%#'#1-.!/ &*)%"13#621/%-3($%$!($) +1-"$(0,,*$ &0,5:6*;2(0$$/,(( %1'3**0$4"$1+('(1(*(",+)*-1&8 $+*.$,)(.5*&$-+2*-#',%#'#).'()0&'*"**2. 089&/#1$"-%.!/)#0#-2€Ø€Ô€Ü€Ù€Ø€Ò€Ù€×€Õ€Æ€Ú€Õ€Ý€Þ€Ö€Ô€×€Ù€Ú€Õ€Ö€Ú€Ö€Û;9.-65$32.53-8<08()&15.75/+#/$%2.4"89+2:'853)3-/>+"&3.*-*--(!(!9/.2,*),*,!-)719+/56@#*5,*3(2,5/+/%.-10%(,0-&*-44/$%2,0&).*+22$#%-3(0.1C4-*+1%%*%/87"+5'7,;*-$/&5:%(1)*)423512*/,'),70+*&9+,62,72/'1B>+7/3867++:50+-*4.$!/6!&01:28+37%',6;1).)9+*.157+'2531.)+1++&(5%$)/23.+&4# /;66-4.0)//*:3')*,92/5;''75.+-/20%%0/',4*3*$-7+3&.-'56/*3-:(,-41)435/+3!0+)?+46;(@6,2+):660.,)17&B'8%3470%0*23+4/-(/8 ?7+9/6253./4*:-;)37,A/%17*7A ++=(2"29,69'*=57,52$23'6(11.69754,)8=(.0505'=2=73.'365-91/1'6:/.3)&75/356;5,76.€Ù€Ù€Ü€Ö€Ø€Ý€Ó€Û€Ú€Ê€×€Ø€Ú€Ö€Ù€Ù€Û€Û€Ø€Ô€Ù€ß€Ö€Ö0*466+,7)62-005.7(&1-*C&#,*;+(/%*5.3-7:2.+3*,=*'0$)19(.-%7?+6+6952..52;,.2-+(7334,>0=/'5."0%"%828.0/03/-9:9.43(A7%'9:/238+444.-6)'+)$+-*01;55/2/9)"$,63-*7:8/-/+6(9'0;1',+5*&%-0+-,061$66/'+,1,62-7870!726,+/*)-:1$/6-543/55/-58,>+.(1/)853"*20283//0)*.:<+/943(8/11),€Ù€Ý€Ù€Ú€Ù€Ø€Ó€Ü€Û€È€Ú€Û€Ù€Ù€Ô€Ú€ß€Ó€×€Ù€Û€Û€Ø€×(03(,73<31':30''8,5;7;#3.@9+(79$5%$3''!)/02;3+3-#'/2/63/+"3 ,/17;1,163*.365)40210639/.2/29*1).)())0#@8$'%2, 4,+:7.4;/43*2--.,-+2;**4+1)7&63'1.7+77('(3.,#1,211@/(-66:44'48%/25*;-*224.5*(.46*,27-../2*+3F9.65-92'5 >09):/7>251+/+4*4-3$1/7,(#-(3..2/-/8/-=3/,$+@-#-8/.=2$0$%.+2*,10"'*'-*6)49;58$*%'&(.0:!:',7'1#-$*2'/9(7)()01,5'0<.'/ *3;#-(#1#*.(00+($11,.%$$4--5A&0(.74)0%--2*(<'+%558922-42/72E880$//=:7;5728'€Ö€×€Û€Ý€Ö€Ú€Ù€Ô€Û€Ê€Ý€Ù€Û€Ú€Ö€Ò€Ù€Ú€Ú€Ø€Ô€×€×€Ü5?4.$",51,'41.75!//7+55!1450+9-21' -)546#,&.0//-&>&:.90,19#1($%:,585,3.-,/+%16090 -45,"!7'+(1-.'1-*3.-671/&)&/05'&+$3&+7-2(+%)61(&;:#-2-41$-+(-$26+/60!-69,,2"/(4/+.5%-;/(2/3.+6+(.*/=)52"0*42#.**32@);#,+/9!($(6%4=,18)&'%2/%3*12:2.73.*3/83/*78*:*A!(-.4=50;%3(20.,(-3-7/5/153*C71,-/''*$<0&62?7*%4.4D218&)+,2,/.52-<+8+(..,/4*-8/.1+48.#3+/5:9-/<+/)2519,3.5&++":!045!*@5<"$.7.3+34!0@<'6-73,,+-8,)€Ú€Þ€Ú€Ù€Õ€Ù€Ô€Ù€Õ€Ç€Ý€Ù€×€×€×€Ó€Ú€Õ€Ô€Ô€×€Ö€×€×&/6(-:/6/3/835+2**5'*/>2$950:>)%''6'1*'1-,3/ %,#>017/+&.11,/$**07+("&-*#/+%(30'-(>#$+5-/+,$22(6+32+%,/,7%3,)+'0/$=,4#(/,1.5-,+5)%'81/ 27C#4)(& ))%3&/+:$4-&//2.)3/,:)5//5220)1,/)-+51/..'26*!#*%<-1'-6#-*6,1<49*(17+%546;3%/,0%/38,99$-*(-/%81)/9>1&4%$3+(/2)+-1.#581."*2--$-5(*8+))/*%,7++'-%#<,+))9"*,<67&;3+*(*+*(/;,0514,:130=,)(4-+,/7%"+$1-)74017')<+6%*,)94#//(22,+04)!&(1.).-;.9(+)./1671041,,*,3"9--112;%5//1(-;')153.+/1+3-0'+&7,"/6'%2++156" 1(6*('/3(*4)7)(,330"-325!)&5;33,/)14-&%760/#,71%-*-2&&+,,-','5>€Ö€Û€Ô€Ø€Û€×€Ø€Ü€Ö€Ê€Ô€×€Ö€Ù€Ó€Ö€Û€Ó€×€Ö€Ø€Þ€Ö€á+34.,)3$+(/"++;00$0(".,&/,@$2/981+%,.+!218,&1(2(21-,13%(9'+! '3#')2;)(+-+-+9*-7&.%7/81553')1++051101;+)-7/$+30'-+%!.)#/.,(%-)'2#.5'$*-&42!-*6,5*3/$2-015'9(.91+.;52/*%-91/25.(6&)735('/&&/+")%#&/33'(B4*+0,$:,"6%)50)/-,'027-)F%;-"00).2&0.:310--),(5%)$2(5)4+18)(#232.(,1&+3).93)'160%:--/+(08%-)*4//!1,+(;.$=9:+1$&#+4)15*(!+;@1+-/!5*#<&0"#.4%2//4'+4+,82',-*(9/*611(/''6%#%/4/'*$0)&3()!7$/ *9#,*1,/(--626*$6',.-/*1":,,"55!###!)922)/%(/67;.,7*/)/-)-31641-7+-$7"-@1/071*(+)1' 4)-0)3/3.24:4%36*(3&-81=1/5"0€×€Ñ€Ù€Õ€×€Ú€Ú€Ù€Ù€Ì€Ù€Û€×€Ù€Ý€Û€Ø€×€Ó€Ò€Ö€×€Ü€Ù'?&$(+75-0134*$,//:3%/+.#+!5,61!++.*23)&F(-50(=-.30*79$-2)=,*8)3%%39+)>49*,&)!0%-45#/31/42')2,302--,)(*644/03'/#$+&!)-/07'$/$/83,(,.)-/?6@+&+%"42$%(0*%5$.+""5%6'*6)+-?3,)(012+7 !1(+-("47822,;3.)3%.2,'/+//-41-.6+%%1.*3$4'54+2-'#//7/4<)!42C/ &5$%%+05/2)%&59*-()/($15',&,(*2$%7-2111"-8-!!53-+.)?(5/$*;--0-2-!&',5--7.528(+"04/!743/901'4/983/1!#%*0*01/641*0'&7+/=**1++'5!-(#:(3(+24;)/446#6)/5)/71!8-,,#,4.%-&/-,#7/-&(5($0*2#'2)/%1$/*0(---4)1/2%$4.-+&*$524-((&/:)+'/+&+900).< 41,:"/10>223-.23+2.:+1+;€Ü€Ö€ß€Ú€Ø€×€×€Ö€Ú€Å€Ö€Õ€Ú€Ú€Ù€Õ€Õ€Ú€×€Ö€Ü€Ù€×€Ö.4)>, 3')6*-.,(1775*&)13)90$01 /"#/,*&,1%)),/*()+5,02441781,,9)#265)+72(6&//-004/)%"802-3/--. )975.,03#&.%.6/+5+4-',571.70',&5,&2+/2.&/(*-!0"8.#2, 4 <00/;1$),*/ )230:.ƒ.Y/+%-#0**/+74&7/('4).*'*/ */2-?*5$"/$21/17-*=$ +00,0)467)3&'B340/0*(*!-;,4.*).+/*21(+2',*)227).1@*<+*/-,)2/-%/=/!$+.'/21*12&,1*1'&&:+,3,,23 ,729&!;%#9*2*+ 7'()%. :2#.9((,+9%-(((-014/2$9!(4-)+%4/+.*,%3,6-2+')>+03%.$5%&%)..=2(4--2431),&)+/*/948',1."*"4*-3'-/3*&$ ='#41+,,,9,-6!12&%1.6$'%1'25+(1,/ )2!-3*4:13/,42.'-422:/+&14-)-24=€Ù€Ú€×€Ù€×€×€×€×€Õ€Æ€Ô€Û€Ú€Ø€Õ€Ö€Ò€Ó€×€Ö€Õ€Ú€Ú€Ö733)703/&3" .84+0//,&6*/!/+:2")-*-.,/!)2$0,A+3:.-/)"1/2(/-+$$13/#/)8&6%#+34/",./2/85#3,?73*-=)0+0),%;+2&,)3'>3,/23.5/&03 8+5 0/%'2)/(9.#&/./.4"092*%/%5/(8*7$-(0!.%5/1.%C&=0+#2+-*(1;(!(%3+'*.#2,(,/(2%49&',*72*2/721,"*7--6&32'=22+* 0-,"7+38*%'"&213./0((48'58(1*0)!45'8-<3/*'0%+'1-/&#-0*3(&-4-&,&0>!&)1?3,$0$)'2(*%243%("#/41&'.0&1%$"7+4)/$"9-+%+0//..1?&2G,(6./E6-0-09,<4+-0$+7&-&6,'4+#+(/,,-$'2)+>&<55+,.3(21.(1*/0=-%'4,.')-1$,!60'),4$359$*5#(,))"++,6'5+&0",2*-%&8,-(.,2'.(*2;->)46$1%3#-.&-/"<12/€Ð€Ø€Ö€Ð€Ý€Ø€Ö€Ö€Ò€Å€Ö€Ù€Ø€Û€Ø€×€Ù€×€Ö€Ø€×€Ñ€Ù€Ó17///+*+&+.)57&441-0(B1)/'#5( /8<+.(074;82%,50$':6('/4--/+-#%# 13.$:0(()2-*+021(+=(-+/3!';*&"3"- #/"3/&,#8%/1.!%/-'))31))-+/.)''0 1/%,*1/-5*4#8(8.%"2,-'1+/$0*$.+%.//'34!/4.4($0)!1*36-!)(-6,;74**50%&+01&.)57(-052,=//&.6;%!0%8)&!--/0)-,,"7)#/-/,102-*#'2'!/.5'/4%/+'',.2&..1+/$".,.) )-, ',2)@/94+$/4))0(+&-.,%3//6%0%2(%)-(/'+%>.D:,1#*++405,)*35+0.)-77$%20/:))1+*(03.$3):>9#2*$624)-!)+'-+354$*,(/+*0#')(/.2./�%)-.&552&)/+.,&+68/2(13*"5'(+*+(*=/471+! 1+("#(+)2,B/13%803!(&*.+.(.1,7('!*()?*+3<".%2"7,,*2€Ù€Ø€Õ€Ù€Õ€Û€Ó€Ò€Ñ€Ç€Ó€Ý€×€Ø€Ø€Ó€Ø€Ý€Ö€Ö€Ð€Ú€Õ€Ú4.*2+-"-/5*'90-.')'(5+111*4(6-+,'!%%4%-/.#/:10+.0)+)76*,2 //&?)-5.&-2$*'4//4#$#/()0%31/)2'E/0&$+'"",$%74'.%.+$ .'&1+)8/#.$.-'9105-.#7)!00-.5))&%#(-.5!)*)(+:/%'*+6-+&2/$!/1/%/,.,-&-.5%-3@#17'4!,-..(#*#$>-7)"+')%-(.!#&1=(*14)/3,5, &'3'"%5,.-#&-,/+%%5(1)/..8%9(+".5$7%+/$+,#&3*#%1($)&=*(.87#*(15--+-22+/9!&%25(1&'51#0/.0(+$/!34(-2/3,'(/4.40&)+/(/,0+-%,416$;5'3 0),<-(550::%"-79*2$',142/7/721,-=4%+1936,06499/.1'$/70?0-3/21*278&4713391,,5€Õ€Ù€Ü€Ø€Û€Ô€Ö€Ó€×€Î€Ô€Û€Ö€Õ€Ú€Ö€Ù€Ö€Ó€×€Ô€Ö€Õ€Õ !0/41"/)116)%;1(.*!04.",*-%2/6+12. )&+3*/(7965:8+**1'/*,3"1/.-!-'(9(<$9&,0&5'#2.1.,:&6)6+)0%!,-2- 5+#-+))".(#.!-"-!5-*'+06+$+5#*706%7%-6/0/02"),84+8!'<533'21;3=6-44./&2016(2$'3*1#$2/:!1%&) 0-/%**-114(6'%/!1.):%-+/&(1(:7*#*+/<,'.:($.0+.($*#1.1$=9///,+%)+%-+)+&./ )-&//(,1$;,'4/"32)#-0 ,4.+5/0%-+57,'(//95(*-242!7"'21(,5'*-)5#*+33+?"%-00!4&, .(1(//-0;2.&- $.&%'%&4&;-)54!1+05-3/##(/-+-'"97/(29029;/*1.%(4.&*&0,0'2/%000*%/K1&%.)'9-73-42 08+24%"/'-&"#)+-.+0!3)()-3#4,<+$!@8-$:08.))*//+2$#*&(135;?C€Ö€Ó€Ú€Õ€Õ€Ò€×€Ù€Õ€Æ€×€Ó€Ø€Ù€×€Õ€×€Ò€Ù€Ú€×€Ô€Ñ€×%;$5',41/%3$',21+&+.04:#7#4(7143$)1**32&-4=/*/6(#%0-.%.(+:,.8#)$1&9>/'$#!2*,%*2(9$.9"'+"5+(1!)*.19&)*'3,)%'042 +&65&%4.6*-.5)6,.3,7&-5+-7()2*0013/.30.--%+3246,)+'(3&/0)2='2%*.3+.+0&.4+1)./0/0++0&006/4 ;/#&;.;%-02'1)2,,(35-&#&,0.,*-514&' 6-,;+%5*'/%*+)1'50-,#01&9/?&5'-)&)(! %;(">!-)%/55)(/%--417 &1'(-/+'/--*2"-')02/++/1,%1$61)*2&.5+*.60)&//95&&,-+%4--"./1!//-/$'1084+),.-(+74.+)&%7'5&%28*1.)(3%+,6/12&1-6"+//*# 5''2+4))=17( +0.34(+&.*").3:)-3,, 6++&&")-+&")/'$)-63072+*503$22404(&(0+,(1*/3+12+%76-.€×€Õ€Ù€Ô€×€×€Ó€×€×€È€Ø€Ø€Ñ€×€Ú€Õ€Ú€Õ€Ø€Ó€Ö€Ô€Ù€Ù/03'9(6233);#-1"/$#,(/3)"2# '&.#&$))(*82$)1-(%/6('%4+/90(&,,&+(/;4 /4)45%,5/*+!* )*!,(%*?#&+)&"*3(/'9(/*4'/*48)+.-*5/%'+/0/$&524))79(/+--08-120*172%)).(+);8%"4/(/%,)#++*"#*34+-&+2>(*1:,34'.'&;"'91%903.*<'"251.+/'4&1*4+/)3&)4+&3)63,(;'24/1,-)/)).27*(4&1#0,/705&%2&&/507+"0(/$54,(41(.32+,($.)+(.&) /!&6-1"/(73* 7-50+)2-*.8725/#'$(),*(!,(("*)''(';!4))-!'(-=:/%+09(,((:$15%34*! 52'9(7)#--'.((.2-)-( ,1=27#6'1/.311-12>,$-7*&,42-.-"/%3-&3592-+)0*-204,1'%,&."&+/3.3-3057.5)0+'$'0'-1.!$4-'%'$.'29,85€×€×€Õ€×€Ø€Ù€Ö€×€Õ€Ç€Ò€Õ€×€Ó€Ó€Ô€Õ€Õ€×€Ü€×€Ø€Ó€Ù&%461/:)?11*(!')). (4-1#*+'$(7'$('*).),4(*'&'+.20/-(, 0; +,--"66$+,%,3+45'"313**,+,920+,'/-#4%-,.&.**/A%!')$9)%(4()1D*)/$)1-:0(.,07'%/11,9%+:",7.225"*)05-+*'1*",-.''*-3%.*4-,&6'46+#,'!1#,$1./-/6##*%1+1/*1"/1./37/'462)4)5/3:3%*/""2!.'+1&0./9-*4..?65.$'*/-6%7+/*,/9$'2,/-,%1#*4*'<./#&(82&2$.3/5%*%#32&(&$)*,->$>-)-+&,.#+)/%001('71*9&($)/9,02+1%'**&(4/2#&4.)**'!+",)@$/1+59&,&$*+.%$&").4.2$).)6.4.:'-69-!)-+."-!344#1-#4+1#+2%.2-0+(+'3"&,6/;//.-4$.<)*2(-*6/- 1)2+-'%3"&,+,7!9-1-4#1(2&&'3(0/%/7€Õ€Ô€Ü€Ó€Ö€Ù€Ù€Õ€×€È€Ú€Õ€Ò€Ú€Ö€Û€Ö€Ô€×€Ø€Ù€×€Ö€Õ,&,1#*2-((:-(7--%5$,02.&5'"(!.28&/33"'(%65 '+2"8%3/(,'+77!!/-051,8-*0-., #+(-5(%*35$*0"4*%+%1-23%&3$)4/&*),+41.'0/1&+3/306(&2(-17/")31)(3(2/3**/$1&0/4 //0(+#*!$0,-#)031)1.!)%($#.#2415&)%1/$/''.$"/,%%-11>4'*&*'+%+.37,%3''/)8&%/43*7(,.$4-,%2,6/0.0%8.%0-1&+%C(%./#%*/,65)&(/4.(+$5(21. -%=)'/,32"&9$)'"3'&.((%3&&&!4"75)&24./4(3&5)0')"5%-)3/#/+-(/>8+-!*+/''.3(€Õ€×€Ö€Ô€Ð€Ó€Ø€Ü€Û€Ï€×€Õ€Ø€Ô€×€Ö€Ö€×€Ø€Ö€ß€Ñ€Ó€Ô:2(/*!/++."(+-4':'-$)/6* 3-$&5#*#.40+4"<(*0#$21-'4.%)&2$)5>!6!070(5$3%&*,',-0)5!7-/./#'010;+!*1+.0#2,+*(*)2/8"9-+3;5-/**)"/.0'/,5,(5*72131$8*+.3/':-"02++((,-%+(+-3//2-'5/),#/ +,$;#!)/-3(-7 %5#'*3$&+/71#,50)+%/#"/.+2*+A(20,/,*"/4&./,((.5:/+%-,+/5<"6113#&'$&58,5+0!(,+ -#1 .(,((* :/#4,,.('/ ///)'$)#+4/'&+",'3 ='+((.).01' D'(!$$+/+,;(0.*/ :!-/3.371$5-1+(3''-06#,1830262%6''2'04.6)$0*.#"+'8*+&05&&.5416)1$(*#*/-:*/2-7!(,.+&*/3(2*5/-+5/2)0.!/0&&1.%0#3/++&*/*-01'103*67./#-AG'02(8/5.&0381'"2+/-/)€Ø€Ù€Ð€Ô€Õ€Ô€Ø€×€Ò€É€×€×€Ô€×€Û€Ø€Ñ€Ô€Ö€Õ€Ö€Ö€Ô€Ô/-%-,((+28+#96"%&*,,45$-(%.8#..036-7*0(0+/,//)00'"'$-%/!.,#'20-30*-2 ;")':-.#/+6*4!*2%,-#"3.%-9/4(4:!+)#(9)-(0 0'-%!(, )",/3.:(',('32(-(2,0$!&*.):555,3%13$455##4- *)/3&)*.-6).1<8%# )3%)&.(6+:/1 #)+'-+/%,/4$.9$+%:.))0.*)(#40/%.,7,%*0+"(3'(%#&/1/510#)3'0524)-2"*5('&123/*2)/%/,6:33) '(/+-)2,-(')7&1,-51*1-%7*=*1.#,40;-*/',,802*'7/$/'&(, %1&(=-)*-#)/'.+4,&'3.+(#$#7((@)/16-+&,!)(-//*-+-B'*$%*)10:4)+)#*$*+(//4$&-+8;$(6!+&*.1-377*).*-.-(!(7#6..**1.+1&$94+9"3?+5%.5#$(2,%//30#//0/*+1-3+#06/'2.',/(8€Ó€Û€Ü€Õ€Ñ€Ù€×€×€Õ€Æ€Ò€×€×€Ù€Ô€Ö€Ö€Ù€Ò€×€Ø€×€Ô€Õ,:(-73(*/--0- 8)0.5.'$%.'-'7+"/"$%1%%)#1)-")10/$*((*-5,?-5)-3**)(($18#.+-0'-+93/,6"#$0#&7/8''+,)/().4 0.3.-*!&-7*'5#&6,620*32":// $)&%4/,6.(/D,(65, 22( 20+-/5+734(),,%'3&61.5'22#%/,)25'-% +.14)%-%32* $+$.,-*&%#//251-%#.!%%70"43),8*-6(/-5-'0(6("0.1$.58-08-(3:+,429#6'-%7#/=+74?'*#.3/972$-'9)**3,8!0*#%-6,*#/# +%(&"1../2-%%.52,*-*.- 1 /424-2*'',2/8+,',(/#,1.*+'5.-/%'&*/,36).&%,4&62(#%/00'2/&2//7'5+4*++.=,)1313,<2&21.&+&'(-*='13+,+"))2)6+1(2+%//1)),2'5# ),);(5!!2,/256/'(0-"B0(7+'/+3234#B-€Ø€Ð€×€Ü€Ù€Ô€Õ€Ú€Õ€Ê€Ò€Ø€Ù€Ô€Û€×€Ö€×€×€Ø€Ü€×€Û€Ô&,6"/.-/$/&.),+/0'%,97'4.,*!,&;&%+0)('*4)+)3).));.0%!16 %1 .4/1&,*,(/#3,'"!/H9'+:&2,/&+$*/1/&3!2(),3')25/+'&.70(/,2!,&$/-2,/424"%#,2/41,7++9')-%'1!42-1739@+-.&,-50#+,.450,4=,!#*8'*7-6.&8%!(1&.+0.'60.)/=)/--"7'(%!/)(!'' ($.+0()3#2.(0.5+;/*+&(.&#-2'&+1-('%)#-)))4$)!<1/34)2(%(95$(03)+#(.#/6,6,(21,312'+&5.6+('18-,1248)+6#/*.39"&-&*,5-",="%7/1/))#-)7-:!,-(* .1/%07,66/*4$4902045'22)7 ,$!!2&8!0?&+>)%73+,")-"*.)25.&,>$#32/7/5+.*'")--%,."/%)/.-&40,2//2-*(&"4% 57'1$+70'$30+063*",85.*=,,7416-1€Ù€Ö€Ô€Ó€Ô€Õ€Ò€Ù€Ñ€Â€Ø€Õ€Ð€Ð€Û€Ù€×€Ô€Ô€Ô€×€Ò€Ö€Ó91"%;,,&&.4.(04,-/#,/91&78*%+-8+.%+0!"*+%3#'6+%)#4#/712+$&!2"&-3-8!'302 9!+$0 -#&--%2,9-.5(,4 .0)+31*02/342(/0!)9-3/'6.(97: )3'15&("()+/6=/7+.8.%251,1(**,82./778/''9-&),-:-)1+(%'9),/.%."/3!11'2/)'(&/*&(3.(.*30&6(,3)0.8(#%+69+'@/ 2(,%6&"*,.$),(!'060&.-(+8)42,#1#1)'')5!.&1-"+.+./')-0/+1&(&.&%*4(03:)*$'-((4'+'+4'*3(#*)(*,--+&6,4))6),3<;7/#250%11"%*'/&,)/+ 14-6'-*+:/)."+%.-5+4,"60+-6+&--'3&!)1&,!3.+;-*//,!>9$ >42,46,79-(..3*./- 3')''*00.+%%65%5300-4$+/*6($&*6+.+2 /7-)&"*+/#7&<14"1'3*+5-2&7;#2*%?€×€×€×€Ø€Ö€×€Õ€×€Ó€Ç€Ø€Ù€Ù€Ù€Ø€Þ€Ó€Ø€Ü€Û€×€Ó€Ò€Ù&/*23+)25%1;%0///6%102&(.++.&:+&&1(7%/%)&3) &,6(,)4&1,2!/".-0#2./"0%=*<5!8'-,0752+1(5/+435!&3.70/)1,!2%//;"(,-+/&!0$#,+/-5!463/*6+(.%3+);.%,&)/+@+6958'*20<*"%!9/;/*-3((,"23&%=(%;+,4)#/51#*$)A%/*600,%/0$*#1'712.%(//,3,%*0'62%5#'0.*(9--=+3%.3001!':%%/*//'#'(23,4-//.#$#&$'&- *0%++-%(,73**<1<#7/--$(11-("0',,*194/23)%0&4*%,+&%5),.-*1&5(4%4.46%-5/%($)1"714.'$8/'!'-#"34+!(".+7/(% <+B3*(+7$$1/123/2-+8*8'/?/1(-3$)0*&/)'//-'',#89(,",4-+-3)'((,=*(0-&77.4--/671275)%;?24/!*3%/502,*0/"1+)+3'8/39,6'?1€Ñ€Ö€Õ€Ú€Ù€Ø€Õ€Õ€Ú€É€Õ€Ñ€Ö€Ò€Ú€Õ€×€Ú€Ó€Ü€Ñ€Ö€Ö€×#1.3+!#$9,&.:!/0%,' /8)5,),75".7#,''#'&,51.2$(.2.87''0#/($;0(%0 7##(+4,1&+/)321 ,$2 $+++.*35$/,46)"&0662%+//2!/%*-/2'4"64-3+1)4&-+'5=.5/$2/-+++-'8@-J31.,?35:9)1&/31A5+)0)?$"*88./+-!%%('%)-*,5.=$ 0270%(/*0'//9+(5-/-#,&(-1()%)!8.3!3.+4#3.$)28;':.&+/;#6 3)!:$%1)&0,,540*-.$+2!)*1-&;1.133.%)/4$D*//!3&24"5'2./"')&.%#8/6+1'!&/.)% 6/9&&'/$. #%/#(':&*&2+!&4$.1300"")-%13!2420J,;2)*3 ,))*./(!!'#,$/$18/-2-*,$.)5,'&%&)/,:%3&11/%+-"$/-1$(>#/0.4)&')*(%(7(-+(0--,<-&61"7.:„!-'.:)/-1+*(-'/*0/"6;,.-"/&$262,€Ø€Õ€×€Ö€Ø€Ô€Ñ€Ñ€Ò€Ã€Ù€Ñ€Ö€Ö€Ò€Ö€Ú€Ø€Ô€Ö€Õ€Ó€Û€Ö24-&-#(!56./+))(5+;#'475*'.-)."#(-$1*31/44)#/#>39&/5"6((3))#4$+%&4;-#/7*%+42'2'7!925-&%*-&.,*/+(&,&'7-//)*7-/++,C.&&; 1."(*)(/*30"7,0")(:)59?/<@>=:@E6)3'@+%3><.%*13'2#"..$0+!1-(//// %--.+0,+*(/. -))+ ='58(',+)'3-;.3%/'1''1.;"/4%5;-* '30&04'1/-2'02)/,&0'7&@,.#)'./*.'*,*"-)1*#"'$')(-"!+/'/.%1((0/0"9%/7(0/$/&/'#)/$12"&3'-1**//,5#-/0+#.151*' *+(165/%/*')&.&1-2*'1-')2"('98*(,$-0:493))7/(.3&1/7+/2':*1..4<#708+'" 2-&0*6,9,.*5#%1)23+6)1%*26/.)+1.0!.2,#/,+".*,/,K%(3-;30(*&,&11)+56*/-5#'$*#+)€Õ€Õ€Ô€Õ€Ô€Õ€Ø€Ñ€Õ€Ç€Ô€Ö€Ó€Ö€Ú€Ø€Ô€Ý€Õ€×€Ø€Ó€Ô€Ò5)9"42#.0%-,,2/3&4(#.*/*&'3-%1.*2!,1.+'(*8&%)/+($)$/'2)".4':!*6+,*')(115.+2211"-3$-3127,.'6&"3;2'//$(,4-A+-''*64'('7-5*'5&4(#.;,7)-,#.,,1854.84;8;193866L81+8/2&10!*1()34!)*)'$+24%-3, ..>-%$-#*,$..""$*')45"&-12$(,+/-1)$25-,-3&)+0+45)#9-&,'(33+29-!7"./.+&-.#)/,30-(2&-''(/*%-3/',%07*35%&3,-+7--80*6)#5 - (,/*$#+$3,)&0&*/+3&%+($'3*$+$&'+%$'.&(3&4&'0$5-+4&2,&11(*'""/1**0.2,(1(31/. &-+)/&.&;)<383**/,..)8-+%"!<2'*.<2.+"5$*-(+)9%(7:+/9*"-@.2+.)1&(3;)254'0+-.95915A/+#-7,.-/18&''$0--*.112-:€Ó€Ù€Ô€Õ€×€Ö€Ñ€Ù€Ñ€Ç€×€Ü€Û€Ù€Ø€×€Õ€Ø€Ö€Õ€Ø€Õ€Ó€Ñ./($'+4#2-2.$--/56-*,.,63-)&.1#3(1$-$03,#)<)-).2 7(-##-%5-%1)#,,+/)("+"/5.%)509*+*-<)'4!!".)7(2/4#!%1,';+%-++/35.47,8(11*+,$9.*1:/1:8+2-<74+;.=@WG7F:=93/24&.2&("1*!4+18.:"(*----!"0*6.95&2%**%&&"06"%13'-&3'5,.//3,"//((66*'&4)+'+'24"2+&%%7(*$'33,+'&0'$2#3(.'-&5-')+-7*05241 $+.23.&2)/+>*+/&")+#/)0/-$*3 ,)(54!!#1.11/0 %&+'%.%+5$*2)&1)2".6*+(05&09B208%,+23!+-*+0--((%5(0*+ ,1B,-/.5'+'3/&1,3:+&&$+06%1*,7,$'&)7((.(,#+).+0$*8 *-90'/ -/%4 +)!4=(.-:*4*0('2@2+-&+-!-+$.&5.&2+$1!:.%3%/,2,%3/'/'4/40*'95€Ö€×€Û€Ø€Ö€Õ€Õ€Ô€Û€Ë€Ú€Ô€Ü€Ó€Ð€Ù€Ö€×€Ù€Ò€Ú€Ú€Ö€Ò,25@#&,'0!,&82+%/12%"%!6*2'35,%:01//2)(#++'8-.4/"/*0*!*0/ )/4'$'1-&-,%3*/$3)1'-&&3--0+./'%+00'1)%&.2/=.:-."7.0*/&5 21)!+89//5+1/-'0&1'<49>@:JMOFN9JFIM48F:B)%)27&=+--.2(*2%.-+4:!%)+*/2$-,1+/.,8*+#7!"-+,(#&/9%-(5%2)197''(:.%+.2-2%&1*2637$.&*,%#&+)+(@+1 7&*30''3(/*!1-&--+(&/%)2,+2'+24)3)3/%,3%./7$))5)0(1'(..)3-2?/3"+'-07/(,/$:#2=/+/343*>,/%03'#/.&&+1(7-/+) *8'2)/*4$.."(.4/3.*(*41'313++0.+=.*+&2-+.+!1-240/)0!$'-)$/ %*$,1!+4.*)/+@."'%.&0,"!/104%#/(,)-/+&.7$*.(-)*"&9.%0,):4'15+#;#0($#&9!000=8€Ù€Õ€Ô€×€×€×€Ö€Ø€×€Ç€×€Ú€Ö€Ô€Ø€Ô€×€Ö€Ö€Õ€Ò€Ô€Ú€Ó/#0"<2$4-.%*85"2%+$,311-,0:6/.3%)",,)%!/.(5&/6/'4!&1 "/#0-*4)*+*&*'6).01%&6%4%;+03'2+$.:*<$'049+#1/*'+0!161%-&1%0.'95.4'/ 0/-3-:% 3C/GF?;E[P^Sgk[Q]TGCC9/99780.-2 /&%(01,/"9;;3+/)* 0&*++0613)4'+"-'./(2$6'%0(/++#&7%0)'",=0+*-#)//&21)"''%* ;9,#- 0 -,1!%2%-98)(313/.)1((-5'*-(*('$1.3+3 !%9#%-,4()-0$1,0+ )),6.),*!%'& 3:)2(&'-$E@6*''#4*<(412+8',);'7*,*,$2%..&,+'#);*'((7+1*E+$.00($1 5.2+$0'12.177&(8'4/+(4,,2..+-.0$2./+59((9.1%2&.6,&#*0#1*+*5.?4-"51'#13$/&$,9/6;0(385(',$ *42&(-,6:-+-6-+25=2€Û€×€×€Õ€Û€Û€Ø€Ô€Ð€Ã€Õ€Ö€Ï€Ð€Ø€Õ€Û€Ö€×€Ô€Ò€Õ€Ð€×/,(++,23-$&+'12#63,)*1%'/.#659.430'(1,' /**75)47-0-01&"!#$),#(.*)285&(/"-(%(2)+1'30!-$+,*%"+*-",'+!1/#?$0-03+$%&,',/<%).*&'4',. 1*31*3.*389IKQYeXktowzlTeYE;=4G,229:2?9%-/-+*(F/7)3*)@1/)*6#+#/"',-3/'' "+,(*6,+0&3$/)#*0/72.!'8)106+3!(/"/&1."'-8,/(!%-*'(1'-,-#*'2.813'3+%*(#(-75,+"%//*)''*7%**.+.1 1+2/,61,()1''#2*,/2/2#($('%1+()4(+)-+6)) 3(*&231!,*+-!)$5#2(2)'*/%%)-#)02*,,+, 01*."!* 11)'(6*2$'//%7-6445'<-4.7*%/.&'*.*1-'&5.&&./ -,7..(+5+.&:.+.))-!%8/-)/,.232*7*%#(**7(*&2"0.70,8/0/8*240/*9*83+6#6.:2*"*€Ò€Ø€Õ€Ù€Õ€Õ€Õ€Ø€Ð€Æ€Ñ€Ó€Ö€Û€Ù€Ó€Õ€Ö€Ö€Ù€Ô€Ö€Ö€Ö63.$/*%4$)+#2-*'333(659,8#4-9./%1%%3*')83#!).%5! &17,13+' '(:'/'%'0(,1*,1$)608/())*122$$'.-1*(#(3*6(-054!(-+$*#%:/*&)"%"4+3/3,-*)'.5$=1?HFX]o… ¥´ÝÜßÔ·•‰p_^S?;A763;$1/>+)&/+ 4'$+%++,(* 53+$4-"*0--"+$%+-&,*:)5-)2%%#."#+.+702)*&'/ %')15'1,*3"#*90)4*7,8,54743)$./,+"-/**1-$-.+%1+%!',1.4-++2650%.'"#0('"2+.8)/1/#) *+%!,)$:*79'+#+)0.'*'$02)/+.30-)#,'3&3 &1+#(7'-6/,+'/",;016.')083&,$2"-2,012*)8"$#:59*,1+)+3<850)0'-2+0711,1,"7)#%3)!)%/% 30'5$:6"/;3+(./,)/).42#1'*,/'5//%($+$%,()8/'.?+2$9,2(-/03€Ó€Ð€Õ€Ù€×€Õ€Ø€Ü€Õ€Ç€Ù€Ô€Õ€Ñ€Õ€Ò€Ù€Õ€Ð€Ï€Ú€Ô€Õ€Ð0%8#<#$09$1",(.5*/)62&0*102%4(*0&"+;9/(/4'+9(374"*%4,.3"# 3(.)7-%,42-+4+"0() +)7;#,!3%&,30*/)0(04/"1/';&0&-%-*'*/+!1*%..+0:0#%.:9,//%5=/\kSlx´Áã‚‚/‚O‚;‚‚Û«‹fW\M=A729/:,'+')%3+806) )(/2&1-):'0#..8+,,,/+16()+4*,+*).)01+&&0/.*0J-&'-*+&)3&%03 016/C&,4?/.-(#(5#+3.+)+.+# (-.(,('&)30+/01./!7* .3=1#*#+,-1-*1/+-).&'/#%)..()2(.(-1/"/3+6+'$6/5$**31"2)(# -3,+$%** +(-.03+<(%'!:,3#."1" -($,4 &8,=--1/-6!3(!%1&-$5-&+90:4#.':!+)+!1$,)9&.)8)9""4*%."(+"7%++-+10',.#4)++&-4%5/,(.$3&.-'-;++;+0.'.111'*+/-5-4,232349€×€×€Î€Ó€Ù€Ö€×€Ú€Ò€Í€Ö€Ú€Õ€Í€Ñ€Û€Ð€×€Ö€Ø€Ø€Ö€×€Ù4701$+-":!5*5/$4':):/- '+2>1.254*$-&,1&5= /84/(271,%#!%)1.)0$+/%145,'57",--%191**5+5&-/44) **41+230)-5$6$/0++!/5/-3((: #,"%5.").-,'1=5BCMYrˆ°Å‚‚U‚x‚”‚±‚é‚»‚p‚åÛ]_NH:5612%//6'A1/3=,%'/.#?*+.'0*,7 /%&1/#8#,+1$)B -* .*/(,1,/1=),;34-%%..7,"($)*),&$0,3"!,,"$(.2('>!'643/$;/11>22-%)3'&-%+""5+$+#2.2.0!)2%()3$!'*(1+12))9-3""95&4+2'3'&-0/'0&9 ''++*%0)5%3**$*)' )%+1()',&)0#9#-%2-074-A+&'4#1!)$(.2.'9-/#&'145%#,(37'1.45%610:,,,3'##541/2"1+,0!#>/0&2+3'.5,%1*38).,&$1*)/&'-1.862&5++%+)#'&2;3,9&%*2+1*#-45%9,4&--,/32€Ø€Ø€Ù€×€Ô€Õ€Ô€×€Ù€Å€Õ€Ö€×€Ù€Õ€Ó€×€Ø€Ö€×€Ó€Ó€Ø€Ô2-&-4/',-)0/!'"$3882&(1-2 )'.5'"$*+..+/4..:43!3)'0"45-%2- (3#*).#-++0+%3!1+ +57++,-.(!1)0''!52-<64)84&**%%*."0"6+++*))+71(.4-6-0(321)+-(=9OQWyˆ¯à‚P‚ ƒƒ¡„„F„3ƒñƒo‚þ‚Š‚é—qaJ5225?/>-1' 2)073*0/46+:1%)'20(6/('(+0-3$+/#(,;+'114-'+../2'-)4+5" 02&1"?--+(,!'6)'4')%%',++,(.$5)&!144('838-*)+;+()--**90&)03'*.7,&'4,,()4.$2'",/.%/&&)72/11%".$$+2F0#/#+'"9$8#$')&-2#=".)2%2/-,2,&2!'+%+/(!7(&#&(';/)3!9)&$%2/.1.,*5/((.%/+*!:&86'6*%90/++1-(*(%!+7,'3,5/!(,$8-%:#+ *$).1,(./-7&0)13#$-:&4,'05G#;&&&(-'1+*/('042$!/+-(&8€Ñ€Û€Ï€Û€Ô€Ó€Ø€Ó€Û€Æ€Ò€Ö€Ô€Ð€ß€×€Ñ€Ò€Ú€Ö€Õ€Õ€Ø€Õ%0%'.+.74)042%*'"-60"+$!..%),8''2(.!+'/26,>/($3+1('+%-,/*153(/4%3+-//4 +!:(4(8'#/**0.*5-@"3A'%1/!3.( )--3$#/&) /)/1'$!5++$/14'#0+/;31-ND9S}’‚n‚Ûƒs„„¶„ï„Ó„‚ƒóƒY‚é‚Mã¡}^PBFD17*)5$'+61"-+):)01.2171!./#+-++&-84+45+/81512+,.034%31.-/%)*!+#$()*55/"#*-,/+'(5(/()+((-1$#3-+'+/-"/&+1&$1*:%2"&(4/'6%%-)/$3%&4&(,+&&:"/,%0))/2'-/.!%,50+$8/-'6$$'".*+$%982)!0'9+"(9%(3 1*()1(10(7)./%.+10+,*#2&%.*)'$ %2,/,#(+4)>0-++7:95,%4"()544,.',(2)1(.1'/*/*3)1,+**'$2@*6)&&.1&"$"*!9,2#$/)(,/34 %49/,4+778'4-42839'&+*4##4&'.24---€Ô€Ö€Ó€Õ€×€Ó€Ö€Ù€Ó€Å€×€×€Ò€Ô€Ñ€×€Ö€Ö€Ú€Õ€Ö€×€Õ€Õ&694*$060/-)(/>47(/!425//4/.'!-1',)7,'.93)#1%26,,*'2/)4'(-'4--,)'.+7'#/%()($((##3*58/1&046-)--58+).$)-/0&)"&6)6)+(18&/-6')20=774,RWiq¯Ô‚'‚«ƒƒ¥„c„Ã…„â„ü„Qƒ}‚á‚J‚ªxqEQ?9218323'$)'/&/93:/A,2$!.111.,),3-(*9#5(15*2//(+/3,*"1#)(6.1/.#4'%$-+2601/*7-+*%*..0@,+/$&-.':4(.'7*&5(4.).0/!,,&8/1),*-/.(./'#*%.4,-$)%4)+")*--6$1*5.*+)2!000%-'-4'3*,"; /)."0.)"#'+ 16.--*5*%-/''")"(;!91,/3.-4//,',-C;7+-+-2**=(&-##6/01,,&*3%/8*503$(/*-0./60+='00'/)/'.),/ %"/30+/11&-&6%+/*82.1)-0/0:*)),;((*3'*()(%<.5&7,4/86/,€×€Ú€×€×€×€Ö€Ò€Ø€×€Ç€Ó€Ô€Ô€Ø€Õ€Õ€Ü€Ó€Ñ€Ú€Ø€Ø€Õ€Ó-#18:.:%-.165,#-,0:+8$5..5)4&-($5*,1 3!9)(-3+4%/+(/)'-3#%<")')"1*+)7.&(01)('22)6)-#-6*(-)52-3)&,71%38&./01+;''-1&80)/73$.9*0%+'31+8::96IZ_‡˜Í‚ ‚‹ƒƒŒ„„„á„Ó„iƒñƒ‚ã‚Gå¶ykTEC6EB76>13:1(/:08.%1$#0*63)4/'8#.51'/2&05!("1$.=-+('02&-$&621)#,.!.4(4.#+/0"-1-&"*,-*=$-0/%++(+-(2")./,7'1*)4/'$/4-6--//./&6 7#7($+<2'00*-2.1.&0++0*>((,01:,1.%-/4265')*$%5+25$(2/'"2,"+5"3+$&3',,/1)*%&3"(20( $/'5.,7)1+1#%6#*++22!$63+-&&3+1"*!( 2-''-.5-/,3#-95,).5"5""+',5.)'34$-*#66./#(0*7'/7%*#5&:5*17&%13%0,,.12/1#6%-2*"*+3%4+ +/7(%5#+€Ù€Ó€Ð€Õ€Õ€×€Ø€Ù€Ô€É€Ï€Ù€×€Ù€Ø€Õ€Ö€Ö€×€Ô€Ö€×€Ú€Ù0*'#-085('# .*-/-#1+/57)-98$02+"7+6""'*('50&3$)40%*%3016-0&2.,&-.*3$5 )&/3(0+2*'&"27,2#-1%&+-#3-0 3-(0&:/($? % ++2/2533:.B%#/-6"87(ABDPahšÒð‚q‚¶ƒ5ƒ«„-„@„S„ƒªƒ‚†‚=믉bF;A,8=723./6(-3//$2(7+ *110/'%6)7+&"%(.--+,'-/$!6/+%4+'&4%4!1%&//,-*/#+8!-,4/'*&'1&67'%39&0%3$2(!)(5'0'0,-+)#13!0$)/2-*4(+21)(!5$$/8(6#&+,$1.)0*32)-.&$)(-1-#=!/&,3/(%121&(4)*3"8+4,-,050-7;73%/"0/-**,'"("-*5.-%#"*!3/(/.&1)%,*-3(31(6.+*524!.)-3(2'%35,:&&+&)&5,&5!*+-/&).3(*;#-)7,*,* -$-%%3/,-&,/$+*&00(%/2*)"*3-+/)134'$-77),+42/6*)'+€Ø€Ò€Ù€Ð€Ø€Ù€Õ€Ñ€×€È€Û€Ö€Ð€Ü€Ö€Ý€Ù€Û€Õ€Ð€Ú€Ö€Ø€×6#/)1)<'5/0*,-7.1/94"2$("(%)+3-..>"'4;->%.(&%*:)**+#35+,331(.1&9'%!+++-2%.''-.-7+/+5* 6-&'$)%#%:"'.0-%(3'4,+8.''5365,,#;/4!+;+*&-@5274AKMZ…”Ãò‚C‚x‚˃ƒvƒ}ƒmƒPƒ‚΂k‚ ¹„ˆXbA3C;11;A#5#A,85*4-(&3*12' !.()$2,+$(5/#!32+29//(58,(/3.5.%$+8750&22&+1'!-+5,//3/$/%#+79*#(9+#,2%5.---,)%%+0)0,"(1$7.0'545,(!5B0%+1)((#2//"$'4(2)"+(%#&":,(3"4@/:''(9.-3/!/&*,.+#$-0'(%8+-.$%#0/#/1. /4+))#-6!,5'8075+*/.;*#5;4*":3/3$;+)305+*%/+0*"/2)-1',.&5)))!'4&'55)2<"'/"91$-/1*521702&%/01+3>35)*50;5.5:&.)746(#,+2/<*'0/-1008,+4/7"€Ø€Ø€Ø€Ý€Ø€Ø€×€×€Ó€Å€Ó€Ø€Û€Ú€Ó€Ò€Ö€Ü€Ö€Ö€Õ€Ú€×€Ó,70($0<(C3-6/36"/<7)(.()**-/1*5"7=,.)*(%'.+)(&-/) -$5/"9+.:,,#+,$/,'."/+.7#0,%0%4,/7*-8/.1$0&'0%*,+)&22%+,4()16+2,&%%..//)**4/)6#?5(+<7+OK>V_w²Êú‚/‚|‚Ÿ‚Æ‚à‚ã‚«‚‘‚`‚,Ú¯sNO:A>=:A8043.))"-21;1/;.3)5#%!6,/)#,004/%%($+)!0+'%43!&&7*.'"'5//=6&+$,-),!3",)&%-*$/2."$7*/,&,125$.2'322/+4("('+02#,/,'"!(1 051%)0(/.*+" ,"1'(*0/ /:'+/''. )%*/3!*1$%+1+*0/57',-*(3#,2+"3&@0&3/21,$8$)0/*.)'6(90#51/11%('(/,1+)&"0-+2-'%/22"+38-!().,#2502++1>(=4&2'(&/10)(35&'/+*!!+740)5,42/++5%+/*(2)''74.4*-&2/$/'*69:2"0352)/*.0 )€Û€Ù€Ú€Ó€Ø€Ó€Ù€Ú€Ô€Ë€Ú€Õ€Ú€Õ€×€Ø€Ö€Õ€Ù€Ó€Ò€Ö€Ö€Ô$+-*)%#&-+66&/*'' ).(**/0*$).,3*&6)#//-!,- /,%-*/81*2:.+3#.20432+-,* (1*'80*1.*650<&///1+.#3+-2(11-1,%'"&-,+)(#&#15+5.&4,3-$,""6I=//16=F@dnƒŸ½è‚‚@‚F‚E‚e‚\‚5ëÙ­‰w^KS5I/E7154('4=+),!068.)/)(%)2)+66*+.&%0''4&+090&%'1&4..2&(30479/4%.:30#*374(,2")/2*+#%5/ -+#/-%'*$/2)/,)/%*%)-2($+/1&+/)+/00-3)'0/ 19-,/*/551(=*&-/)*$+2/1"/"4("*%3#+5+%)5&0,--#*%)/'2)0%5'(A2-,)40'/&5.,!,/*0!(/&5,'1-%*)(.#2''* ://!$'1--,(%7,8(1-#,/'#)0*"0)-#6(3/:*+%6*6, %/&)0!2'//'$-(2&35813&,#$*01+,*)&'1/&,#5+5+&**",./+')3G".1!822"1-5€×€Õ€Ò€Ø€Ø€×€Ö€Ú€×€Æ€Ý€Ð€Ò€Ö€Ò€Ü€Õ€Ð€Ø€Ö€Ï€Õ€Ö€Ô3&*4*.1)-'/'$ 2%0(6&3*//$:6-96-.1.%06$+'%(,7'"(5&%-A3260<3+ /+')/6$6-(14;*%&&*+8)3)&,.!+17-'%/!,./7%-.:)-)5/4++0/4268*/3-26,-*0397*()1,:=HMTdv‹¶ÅæéçõÔȤ‡uvJJGCD0*-%())?&$*(*#-(.1/"2&'+%&0''/3$&3%.4!)($&/#(*9)!.0%*4+6,.2+$))2&+€×€Ó€Ö€Û€Ô€×€Ù€Ó€Õ€È€Ù€Ò€Ó€Ó€×€×€Õ€×€Ö€Õ€×€Ö€Ü€Ö132&0:6,2'$;*00&/"0-#$*#)')&2.4+&,#'".**,*+.%0'*45-%/%5,/--&+/+0"(4#*/84.42-'5./'&-#1'-&7)+*+*5(///03+,%3',,0--7*(+*'!.6="5+(;),:1..';B<61/-27!%9053"7-3#)37"(+--26$0/#*$% #-&!-,-) 5-5/#(%(#$6,(&*."&"$1#!*57)60(14*5&,50)6+//7;.2/+308()/3)-,*,./1,* 5#/!01147",,*038'"11+.5*'+.9-2,;+(6&2',,18(1(&53'!%&2+#'.!7*%"4.*1*+!/2#01++/$'$3-37/%5&%909.*22.,-#-!3$.*'+/10.*3*''--6)93%6*..&-/0#,2-/',/%&2+-)#2=.%$(");4$($(6/ -'"06(1//."+-$.)-*%0 :60)"$*9+(@53%6##/4$,060.//.47€Ò€Õ€Ó€Ó€Ò€Ö€×€Õ€×€È€Ù€Ø€×€Ù€Ô€Õ€Ó€Ø€Ö€Ö€×€Ö€Ö€Ò"11*(241,*!6%70*"2*4'1237$217+622!8";(%50/()*+2*"10, +*$+33#/2)**/*A)(!0#41/(%*.,,:$312%42=26:)))%4%1';#%5,0+,2)+)52'%+#-$.2*5%(0*0%(77;5->BFR[SXjjzˆ…’…ƒmpkQV59;/A&-574+>50/+27&4((,*&7,%0()%-+&%<5/+67*-+5*%/7&,!<7)".+/'22./()$1#114)()%+'1'#+&3*,1-,&,'#-+$%.3,#*&,30#;/*(/%='&,+!.. *56"1)$1'5'*"'5"!,#.%3+12-+*,7%+1'&1'')('-./3%*(&)*'#17*&85'42#+)0$*6%*)"0%-)-/))#&)+$,0/!)9.!)6.-%,#+( $/0. $0+,/*)6*#*')$&-#$)-0&/*#)/709,24$+*38,91/ *")4&&)-*!4.17*8)300)5-16#.)3'73/'(+35;4*63-3)0.1.4;'(577€Ô€×€Õ€Ø€Ú€Ò€Ò€Õ€×€É€Ø€Ú€Ö€Ú€×€×€Ô€Ø€Ô€Ö€Ö€Ø€Ó€Ó0!*1.&0&-%'-$/1*1&64*)//2)(-'):(*+":54&/#10())"33%"%.0*#((,-3*2)0($-1(/"&7&5,$2-*2*&8%&.863)#)*3#//"622*14:(730,>7)-'),,*+9(3#+'".$') ).;9BKIZT]cgmfi[`\C^LJ<41=3)4&-*5>%5/#456621+'5(9-'.13"1!(/ ..!(/,1 -"./)3% (+1')'-/9'%-,305&-?5$"#,:$#2//=-0$ "-#6'(.(&.;/$*)!&/(+/(#%)('(.,4&00)7/:(,$/5-"% 4.1&##5..7'13./*',1%'()&0'!$7$)&%(+;1):03+4$38*00/.++*/("'<./0',/5-&(4247+$,-&%3(&-*)/8 ,-5!'0&))2+/1,%'43$. +8(-)3$'+-+..3&'*-+54)./0(+)(((1$%2)./#14)7226B/&7+1.83*2*%3/1)'4180%*+)/73/>-)25/9-&.51,,€Ô€Ø€Ò€Ó€Ö€Ø€Ô€Õ€Ò€È€Û€×€Ù€Ö€Ú€×€Ò€Ú€Ó€Ø€Ò€×€Ó€Ô2'-6/290)2/+,(-%54(#. ''+)/*2.$.,7(042!713&.'6$1*-1$.'1+',0,-/1"'1&7% /90(%!*52,9)3!&7-&%.''.000-',/81+-+*%).=)-(,'(/33.1)+02-/5(/42198BFFC=DXcSWbiS@XIHC5F163:.+)70*,84))&/$$)7.7*('71*,%5%0'51+011".',1$11*)5),5415,5)50**"+D')"<)(5/*.-),.(2%.;30)&291,.0-*&+#4,33:#/-(+5)4(3+'6'<1+%$25$:2,-18&(5)+.-5+5#14#70/27'30(9(+)-)%(3(0'$)*-1&,)2--,2,$*-:"306+/&%($-)3!('"5+/ 1'!)9-0$+:(*)3),5+2411&+(%+6(3.'%2($(**)(#:'(,/$0 42'<,'&%),&489-69;/<.-)22/)>3;//.".12:$0-&4-1&)73,/7./)%)8'(D+&/87:-8:/2€Ö€Õ€Õ€Ü€Ó€Õ€Ñ€Õ€Ó€Æ€×€Ò€Ô€Õ€Ù€Ô€Ò€Ô€Ó€Ú€Ó€Õ€Ó€Ö..#-)#++(%*5%-41.(,1,>.+.,-/('3+!754+!$-+4#)$#$)'B4)--0-(/%3-"7/"%%--..-0,,7,+()(//2+:0"$+/&"/'7-/*()--6,0/*%-0'7)"/)>/603'-3.2'(13163*>54ONRU9L9EE=6C20%)40-4+!#,#.+&)'3#-+,5,3+0(.0-"'-6+.+<*.533'0'#%2%:&-!';>#+(1'*+++,340:/6*.#+/:5,!<)'+* %,5,//4%)':+,4*,>0%$0#-*--0#7(+)+(0 3=,$3.1),,3(4)),*!//,/'')(=)7)/,-5*.( 05,0'3.!/(65;1,*#%1&53&!.) 1+%)(($1-/'(1%130#/( '("5#),"!'"/&33-(1$5.$2&/$/."-")):/..',)%>0(4'01-+/.-&/0'3$85(1+/-20-/*0;3#&:2$& / $-7/1&--7*/,2')+ ,!)8#0%*+#*.€Ø€Õ€Ó€Ó€×€Ò€Û€Ö€×€É€Û€Ù€Õ€Ô€Õ€Õ€Ó€Ö€×€×€×€Ó€Õ€×7-+!),$,,;;(-+-5+*.(&7,4#,43/0**#*!/7(,"-)4/+!-4-,*+=6$%!(".!'*(/0,'*=3(-(414%*.)!-'00-24 *(-3/:"5+.",'$*/(&).&$'7*,#0+2.(<.33&*/.,)0#-)6=.$-@;:5ID=FBK[<=D./=1).:$+( )+&2',(*03&'+.'5,%3'--7!$.'+-7)"("4*'.*,$*-+90&0*(!(2%571,/232'&+/-/)1(8 "/9.5$+2'-)7 /B(910, )05>,77,*,(+/-609%!,)$)&%%.03-7+,:&(#.**2(6$#!&2+!3*!1')%('(,'6%".$(23',34,4)20))24!$00-'1%(1%#2)/).&'>$1+//&')#/"))#7-4$3)/%1-!&4%77)((0#$/.'+(.(:1'(3 &)+5,//6'2=-2/(.8$$,-.4,./6:1#'-&2)/'++$((05)/'"'-6*+1(0-('2/!-1/?,#,5% 6(',,#+€Ô€×€Ô€Ñ€Ø€Ò€Ó€Ö€Ó€È€Ô€×€Ø€Ú€Õ€Ö€Ö€Ö€Ô€Ú€Ó€Ó€Ô€Õ3+2)2+')!*!1515+/%+3-2-&1030-28//6 '+5')08#+$/7-+--#(+++0%5( 7/)3+2).".2%7%#''.//(*+/*,"*36%/28*.+).' (,"4'*'&-%0'&*#$0&-+#)1%4?0*@+8D764<93+D@79A'I;E24AC9+/<844&./'(7*/&*)1'#--/)(12"'-%-2+)(/&&)-9$(&(0!30-+?53)-4!'-&,.%,2/)2#$*&*+(7,='&-3(9/-%#((45$=!, '7-'4!(=)(9'-+.3-3-,.'/("+%/"*'-'3--22%.% .2-,.-$2-+2/-)8/,%,--'&*0!)*,$ % !*'/(:!0#//:/7+"10' %%'4+//77%!$4(,1+-**6+"+4!$.&&8,'*$-"&/%$2/,/21)*(#.8,5,/,&"9-9() +0)*'"--0<0+,7-*/&1/!/0$*-/!"&)$0.,0+ ()/B$'+-4: ,51.1 7-2+$5'1#*2€×€Ô€Õ€×€×€Ø€×€Ö€Û€Ä€Ñ€Ö€Ï€Û€Õ€Ð€Ó€×€Ú€Ö€Ú€Õ€Ô€Ø,$./2.4'05(($+-&)1.*-./)63!(0)"*/6'7/16*9.;*<%"-/%0) &84,+$7%$2* ',(.$5"9&8!&*)'21**/+"*,4$1.+.,69.1')4'&'*..*4&7-%-1-+%/*)38:#6&1"2#-:*0.29<0<,8=7981I562@47)-+/&/26+3;38*(()*'-7+3#*(955*/&8)-@/#(&+*/6 -#,/!+2/'741"/+63-(+.3)(**//&"3&3.<..'+'&,3-(5.&3-4,1"#*0).6+41"*')*(//#*1!++/*-4%-$7'6)!)0*.,-)*#/$6*-03- )(!'-65%+#*()&,.0/&-'&,%(017!"64#*'(2-3>$'+30,&/<-'#64,03#*.$.*0+3-)*5,)#+1"2.,/,15/!4*1-.5%),4)43*+;3"&"".5.5 0//%+) 4"1+6*/146*)5*-./7-$..,2)4**95"0"*".$1<1/##1,+'(.6)/'.!07+8,(€×€Ô€Û€Ð€Ù€Û€×€Ô€Û€Ä€×€Õ€Ñ€Ó€Ø€Ø€Ù€Ù€Ý€Ù€Õ€Ö€Õ€Ð#%*.&(1.* &(+0.-@12+$,&<+)(.7,-&!+'')-0.2-#5-0#)-)(#-2!&A@*>+0*0,!%&'1%6#�)'22*.&/+)/,/829%3&!/1)!;*.3,1.17%"*$,04.$+76"'&2#2((.&C*--,%,*:));5/*>7@2.=<$38-+%%/22*/&,7.$/''5+2/&%7 ()."+/=.*&",+)-/3-!(* *.7)% 6.1/%.5"2%*.&%+.,1%32.!0 1(3,/"5.+/+36*-*/).,+07/,/40*-,!(/6%('"#(3*+&""/37.,+0).-2&/21.7)/$..#$2(/3)!(/+2)(%$1/!.(+3%-/'.8/(&+.+%1.%-415(+/$)*3&+2;-2#&%127#,1")3.= %5/,882$.-1/$(3&/;3&"':-&@&2)5%#+,-.#/%,0*53'++-&.+'*!.3&1+-#(5,+%1%/*(3+,5% '720'&)+2*0,1=!'*3.3*5/.775)24.+=6&45€Ù€Ô€Ü€Ø€Ô€Ô€Õ€Õ€Õ€Ç€Õ€×€Û€Ù€Ù€Õ€Ò€Ö€Ó€Ö€Ö€Ô€Ú€Ø+-96/-+94#=/,'(&!)(,.!-$2042&-011//20/)060'.1*(+0&'*-+,031;*%//)A%'2*5#%%%6-2#'&(%*3(3&(*21&-/1/*&.0-09$//6.3 3!-7#227&@321&'4#.6--9)4)/.78205<136;/$2264-%<8%<349--(:15*3=&18(0+-/+*"/)&&-)=+-9)-%6%%)(-& ($$.,/(,3-&19.-(1!1((%%74+,(2+06'$():7!&71&2)3-3.,(.40.*+7$#-+#*(/65)!5+4++5/-"&) )224/,6)((1/401*B)3-5,','(7$+-4%-&,-% .&.)/'/"2-$,'*+131%2+)2+3%<!/.)%28/6/)&45-.) !/4.4/#%/. '&,*3"*(2//5,3-52(('/'4$'))8,' !2,/1)2.(%3($)!%057--(-(/)!23+"%!(+.2;(,/2,*@3#*#82 .##8*,("C-)/3-+)#-/%1$(.51%4€Ù€Ð€Ó€Ø€Ð€Ü€×€Õ€×€Ê€×€×€Õ€Ý€Ó€Ñ€Ø€×€×€Ñ€Ó€Õ€Ú€Ö0 ),:1*')(2)%/+))285%'/>1,+&$*$6/2+2"./-*($",,2'%.'3)05.*&3<-1-(.&8+7**8*<-$0/)":%'4$4+$,2&-1%052A/;/()+*,,(&+$8>(%)1%*)-203-/!+.;4)!""()-%,*%B22(//=0-((0 38*(;.,+*" 2!)4.7-(#%2! 6-('&16,'1"4+.-#(153)#'3/0;.7+%-2+1$3'-/-!),+' ;/. !( &'0(.*- 318 ,86%3"*#*+23-(6;"2 -"+5-.1',&%/32.+,+:/+-+3*0))'&,-)%!'3)(1:5-1. ".') 7.,$.0 -$.3077&'(0&%'*'/+742%0)/1,1:4'0'1"..3($8*22$)1$/#(%3!(.7/&!32!3'!%./(.23*-3*0,(%-,+!67,%(-1*2,$!(*+,"83(9. (&$+&(=/=!0/%9)"+0!9+&6-/+/.#%# 7*82/135&453<')(€Ø€Ô€Ñ€Û€×€Ö€Ó€Ø€Ó€Ã€Ò€Ô€Ó€Û€Ú€Ù€Ò€Ø€×€Õ€Ó€Ü€Ó€Ñ'&0.21)1,*&15&&-4-))-)-4%-+'%!)+#*# 20',*/3%)41*(*1!1&0-(2+"*''(0'0*.!-%"-%9."+*,.)*1!)' 55"''A 2521!./-(9&/3+,&87.647.*!21+%.22))1%+0%/'$8/"2+)'>),+"0*96,#,!)&().&0)*%3 .5**3+$-8%-&($5,.49!09%&'/4%+"'"*$/.'$85$/#0$!.0*-*2+ $01,)//2,"50/+*-%/1/6*5)($2.41(5') 735'30 %'113,#2#+3(+.311,12.0",&3. /2+41,&%%%,%10)9.3+2)34(*(/(1$-/,4% *(!+/,%:$4()$-#+*5*43/9/74+)0+!(,/.%/).11#!/).0%2 *1/4')7++'1% ,+9-47/46,6.*3-$:93+/!/4#8.7-6/0/1;:+#$<%%,1+'/+'&/4/70%1€×€Ö€Ö€Õ€×€Ö€Ò€×€Õ€È€×€Ô€Ó€×€Ù€×€Ó€Ó€×€Ö€Ö€Ó€Ö€Ó:#;2+,$((&4-)**'8;1)+3",0%2'+&))06'*01//'.'+1--'2!4'5*21(*1)#412*'/./44)%/'3'$3'(<4)/00-0%%7**:* ,*##-#+$-+&%*,#5*+'19,))+&/,.!97)3'%')797*5(/($/<69/::,,0*("+&7-*-)12/42$!01',0(&,0/(!.,'!&6*%0!,1/*4%/3)02**,1'?,/0+,7$,$ .,3%+,13(-/05,2,2#$)-/*')&-/%. &9):)58,=/2)'+*/;-0$/8!';..:+.---$-30"-,%&6+4:0+2+' 5(-:0-+-(-'((475*+3/33.3*)'/�//9,+(+3.,#!),'$7()# +32,%,,4(@"! 7*,+"+$"6/3#')*3'!2/,"(-0.&-&*.%)'+-3/6( )%'1+-87/&,+)-'.$'+%***/2/+'-01;$15./+1,*'1')*)."-6)$0'(;+/9"&(9(%53*=3#€Û€Û€×€Ø€Õ€Ý€Õ€×€Ö€Ç€Ô€Ú€×€Ó€Õ€Û€×€×€Ù€×€Õ€Õ€Ø€×7!(.7'50%;&)$%+(,-,6++1')&9!"#'#20#81++6#+%*#;$)"*0,"')*/). (+$-**0!&&)#'19#3)-)04-'4(2),,%$,!0-))'.:&,)(03/, %1)&+/-1+0'+*+6&+1*1//3&+4%*-?*1):7)+(&*%(-(2*+'-3,' /"#&./3,1*$&#%*09&$+,4"/7*'-(.-+61.## &2+',(4(//,(/(#/&*2-47.5)+',+'&&1*$23/3/1"!/"(+-(#0)24, )/.+1&,&8"*, +()$%1/ ,(4:/!1$%=:;$/$&*/-<16*0%)%/7/%&/4)(&)6)++((&,.1&/''2,:1*#/+%$*'/*!*23#,! $0'!&2+!/&(9 '.#.&-"#,()"-$()370&)'('0-*+-+&11,%1%&31-.0#()/)2*(.,<1/()2))0!...-6,432'+%05'1&**1+*!0$2'.%1*1+"237+9'&97(/7663,)>)€Õ€Ó€Ô€×€Õ€Ó€Ù€Û€Õ€Ä€Õ€Û€Ú€Ò€Ó€Õ€×€Ñ€Ö€Ö€Ö€Õ€Õ€Ò(.+3<7//'05(.3--@%$#5$3''."!.2*$0))"/)*$,"<23(0+,/(./2)2$0,1#1*,)2)#%.!,+0."-'(;2+*+&!'/#&0+1( &2#-9,-5//73+$.1(.+5':9+&7-'7#.*3.2(+, )&*'+*3-+;&+2&:2/8'-24+.$5&12 :0$+2+4)>!%,".9/+*+'!)3-6)&1&02!'('+/68!19!-0*-2)+&.+,-56).#-/<*4824%314,6)1/%%+(#"1!*''+)/.6*1%/!'*/.360'"'/'0('+)B$,2403/+,661*0#$/5'-+3.:1%"*4,%(#/(;12,+*'/)3!"15/8$*)'*/17+0),(''.863482)+&'7#1'*(!!.,--!2(+&2-)*"326--(5,5!'1*.8),&:*-2%$),67-*"#)2,"(0(&/,7+0'"*0115*! )&)-*-&6*/*6+0#&'+.1/((;%351*6;=+,7.-*24'../2,."+547'/!5€Ô€Ý€Û€Ô€×€×€Ò€Ó€Ô€Â€Ø€Ó€Ò€Ó€Ý€Ø€×€Ø€Ø€×€Ù€Ô€Ø€Ò'$53$'2(,/,(7)1(%.%-%83+-&%1/.)3#/(-$.15+)'+(()$/.)672-0//,!21.*/:((<./)(&;*,%1$'/"*+")4+'69//.+3%&&19*4($/-2(&"'.,4)''&.*)*'2-+,1"@024%) 6',-01'(+8).,#'%01+&1'67+*0B'1:)&40$&9$-+/(;$3,.'.%/123)=,+$%/,-1*354*$-B&#)0+9/'!%/4,.,#-*/8--1'-15/.-!.2-+'5"($**0%+2+5-8€Õ€Õ€Ó€Ô€Ø€Ò€Ô€Ñ€Ñ€Ç€Ú€Ò€Ò€Ö€Ó€×€Ö€Ù€Ô€Ö€Ô€Õ€Ø€Ô)%+)-,+!0#%#-2,%*(1&8.+7&1 66),-7/")"2 $/$.+-'"!.( !.1 ,,5$%-1'5'3!5.(2&!3,1,%/%(.7)0.1#(+%48/+2.)1./)"5%+-+&,,#,0%2,(,:/3(,+0/'12"-/8)0/%-10!-(*$6 /0#*7+5'*)**'#6)#! +(-13+3#+/11-(*+"4!+%-,!':.;+*."!*)-/,"*+,)( 13%#39/3#''%'+/+*,2+!1$51=),02,9+(0/()1$8*50/*',//631.$!3!3.+0%(,29'.*73,/'0,2!*2(*12"".)1%(2'-()/&4%!,(&,79'$/:3),$*#(3'((,,4*2)+,+"+)"#+70)/+4#+-*"%!*&*/#(%0,!+50(7"0+/"('")((2#(,-+!1'=0*,-8(%.(25(055+#5%&+**/., //,3*60+.-(+43%52-$=/00/'-+%&835>#.3%"7-9.)>$*831.'&1',.€Ö€Ñ€Ó€Ô€Ñ€×€Ô€×€Ñ€Ç€Ø€Ó€Ø€Ø€Ø€Ú€Ú€Ô€Ò€Ñ€Ö€Ö€Ò€Ö/*3(*,#,7/'*'9!!26!',,+$ .,7$%7$'*"#!"./%&%%$/(2+!$%'43#3//)2%!/#)1*+94*&)(-/&*B)+3+1)#04%"15++(4#1+/*0/$-8+*,!237*#,(&''&'$05+)),$3'753)'$).3$-,4-,(()))9+72!/);")3(*+6-'8!/)*-2:0".0.%(+(/(&11-(*',7)5# 124(/**--(95/3',&736(*9()%&13% 9(/+)*&#%3&,'$-;3+,,#/+!))&)+1"48.'*"+,4,*2*.38;*802/*-# )*+7)*/./%6-/(*#"")(*(4*'&1)#6 %*(..&4-3,&-)+>02-/& -)%*)-$2)./71)/3!)+.7%(6"1.+/ (2/3$)2-*!71'(-,+""1-53)-;.**.(=)"2"+2+.+$'=5*1"#'.0.*.+$!$',"-15+<4+,'&+&&-(/$/*$0-/,.2)2./5-*4&%--4/7:./0,41/=4&$€Ó€Ñ€Ô€Ü€Õ€×€Õ€Ö€Õ€È€Õ€Õ€Ö€Ô€Ú€Ñ€×€Ü€Ý€Ñ€Ö€×€Ð€Ô)"01,/5,.7)".--4/2& "./%&%>*)16+)"2/,42-0++-(&7'/2%$4$&&!+)/% !4'(72--47"*7'+//5,'667%',) '311&1+3/.$+-+*+*-,!-1%1-('37(;6.-('+,8(.&(*0(.9/").:)("0+%4/5$)*$760''/&$'%'$9/ ),03&2+//---4..*-,%%+&/%)71!/7'+()$)*1%(.1# +'$&*7%/'+3$1'6$0#30,:-4:93""221#93)31+*/!3/-8-!.(*!('/,0(-#$%-2,.-'*678"62-05)*-"(%%&/-.(+" !612$%,,(0+*%)$+%'1!5+%&0,3-1$"+&45" $/* (-11-'+/+7!+1'1%0-.)(,#>%)-$/*(*)'('%,&$,%;*033'0.),'$('')#$" /('+/$/')0-$&)'26-6'/-2/(4#&210--2(.'1)$/0390+70.&4!+"-.,!*(!3+-2%/(-&3530%#40€Ô€Ñ€Ñ€Ù€Õ€Ö€Õ€×€Ô€É€Ò€Ù€Õ€Ø€Ù€Ó€×€×€Ö€×€×€Ú€Ö€Õ'!@*6'+'-3."/4'3-0;(('4(5/+.-%+003-$*%22,)1+)&-.$-1)2.1)4+'&.*2 $2((92-*(@-."1-$#/"1/0)$(*0//24+%/42#(79+.*,!;-.0!2(+,("-3%-1&$/!:/(('7(/0*%5&&#)!&-5?+/*2(07272"/2%0.1(<'##*($,-(/&))(' '%"+43+-*17#'-)*--*-4"#-*2)(&/+&-3%+5)4()5 )561*1'+"3$+6..''(- *!*-+3030-.2.))"#/ *,;0,%31.5-. .6(92.)43#";,,(#.9*+2'0(-(/-*'%%*=,2)-/3'!* '.+..#.5('2)$-07.'''1,2)1*/'1*(/,%/)3 % )+'-+0!0*6((/)'".#!(004+'&3'//",,%10*!-*'"2*%020/51&* '+$,1*#,'7-'1$*+/20#,+%,)'<"17-<+1(+(6/'(3&&-/86-5,.'A!$&6&+/.'-9€Õ€Ù€Ô€Ú€Ô€Ô€Ô€×€Ó€Å€×€Ø€Ù€Ï€Ö€×€Ù€×€Õ€Û€Õ€Ø€Ó€Õ1*3%..3/'#!58/6+ 24@,)+/$+8*+/+9)-'.%&5$://...2.)4-2%):'&+"*%,/3$ &"=02*6(-%&840&%- "/"&)+3144$+(!$-%4?*!--'1/3*1$5&)66%'.('--56/#".(/-,**9%%* /'+-8$+()"#2&0/30+28.) '/+0>=.(//7+ B2(3-2.,#('%/$''1/+7/),'4$1./-'A!%*,;9', 4&)367/7)/-/$-/).."+0+""**&<60$2 +!0#++'3*./41)0*2*'#%'$5%61*(!=*:/*0 ;$&3,03.$/(/380($0%)('2#12(9.'.**(",&6.+).'*5+"*;66 7--%'&+710(0+7$/+/0"'-%'&& /9*,116"40*//%)55,/!,.'3+3.'%(2/))+)'&&'*-,*'14!7%3/,=101/,.90(,95+1%%)+6/B<24&.5&*1/-0;'$-702%*1)'0.9'€Ø€Ù€Û€Þ€Ù€Ø€Ù€×€Ö€Ê€Ó€Õ€Ó€Ø€Ø€Ô€Ø€Ó€Ø€Ð€Ù€Ó€Ú€Ù/.$%-0)- .53@+,)*44/"5"$*.)%2-./1+)+1.*12-6(#&), *('72,.*(-&&,$.-4%++)"*."/'3*,%6%5*-2%)'32;11'2($7&-73+($#''1A+2-(**,#*//,!36&$B30/)43-.#-0 '<-/<#$72+2++00,'0:)*%6./7-+5//(,%'"*+)/%&(:*.-* -1(4(8%)+0*)*/,#./2/,&/+,8/*&65)+")3%)3$+$-8,*#%'4-,!( '#50&3'901531(/"+3>'(&&-3-!!.-'2*"-1+&3+!#92.'*,*0'#%()3.-1#'.5'2&5+,,#0*0)"/(30&-)'033&)25&"&)+#.#0'(/+1&)8)$85$1'$)*%("9*%-..+$(0(./+#*/'(,,.28,'(1:.-,6 ".#%/&%07+7(.0/#'8-3(5%.4,5-1*7+%)0=/)+81)'!?,+46%6))8-3.+:./4-!1,&/.4(!3*-)/!A%,)< -$?€Ú€Ó€Ô€×€×€Ø€Õ€Ô€Ó€Ë€Õ€Õ€Õ€×€×€Ò€Ñ€Õ€Ó€Ñ€Ó€×€Ò€×2..000+0+373(,4*61.51426/.+3# -!&#/6730,*/6%12%2%)0)("!152 &)1+30%%+2+/1$+(.‚!{*,+(,(&'(/""/-&&)2+01#"10&*-,'+-/$,#69!-&14/3'/%+3%"!1,/7*$;&+(%23&(/ 453$ +)((6;,5/*4+48/1+3)$.%#//056*&)8)!0!$9'A,,,(-8+0(-1+*%&%+)3)#1/'#/%)&/&+1-&''3/-!.#":;% (7/0.,%)+*'02+/32'%'&$34-''+0/.(+ ,.;.2$/5#&+.9-'<.5'680*5!!22++%(-,+/%./+!%*-8'+/'&0&,;$*'/ /&',!(+4/4%4)),:)/)0*0*)2#34&&/&@2''"1'#,#-'"7-*0"&%.,7$(&&,$0..(3:"/1-#&(4".+5*0)58,--,:0&$-)$2065).3/7&)$*&6%0#3!436, ).("!2)2+)/,/1*2..>'&-.5*%-+& 4!,16-€Ô€Ø€Ù€Ñ€Ù€Õ€Ó€Ö€Õ€Ê€Ó€×€Ú€Ü€Ú€Ú€Õ€Õ€Ð€Ï€Ù€Ù€Ô€×13*&0+$,':$(,+2!)++/).5$%?('(%7*+!30/-!13+%,''/-'0&!*0#(7*)00*(0+'#*++%&/+6"%:‚2O +#*$*,4.%)12. /'&7)4/.11%)+&$(*-%%$4))0('10(1.5))( (-/8*"-*$0'(%.''(.++-9+'/(,.-1-().13(.01(2*//6))(25*%/0+36455*+/")"+*#,*#*'9':,22+6-"!&+":')-+5++#$*)-->4.-+#03!$/( $*+$3!+135%+6,1#++3712 8)2!!&5+8/!'-3!3*)..%,*4'75'67&(#1&>+2+.(4.("-7!:78(".)-+&&&):5-'/1))**.2./*, *(22( .>+.$2&0(0,#"/-0/9/7?#*1+,+$+4%-5)5/&-+ 4211( 6*&+,+0?()+/0''64-,('*%,:")%.3%4!-, /36*&1+9;/('*216?%(-/8$1,/*/7+,-$#%,@,/'&7)* (€Ù€Ø€×€Ò€Ö€×€Ù€Ò€Õ€Ç€Ò€Ó€Õ€Ñ€Õ€Ó€Ö€Û€×€Ó€Ö€Õ€Ö€Ò8$-.-')&+5$))3),2*0&1"1#+0.)(1%/+,*'52-4-84.).0&$%%/+3.&'///29'4(%/*0* #$(*-%&*:,,&*0$1'- .))&#.)&+5 3&&)-2''++9&$1)+()6/4.(.6$4(''(%4*0")-08;*"#.4'+%%!!%& 1).)*&/&.%40%.*#-,%&,%)!13$2-$**=*')(,+*8-$6 '1(8#&#**/'-&*$'/4/4-*2+.)#*&$(6$./*1->*0%#$)'';++&3$.3-/ ,1),3()+&*47"+6,-5-2&(%:'/1,**!3*,?)$997@-(&/'+((-&.).47-%. %$(.8/"2',.'$"+):),2+!/'5-('(/5+&16 )./+/#,.-+(/110'/3&2/$',+.%0(+$&232%)<'()4+&2"+'*$.3&4>'*%&-2'%2+/;003"'#)3.//(&.#,'*/)'1'%1-19!<0$9-+%0,)"5-/)(3 19$+2512)&249,6%6(-.*€Ú€Ó€×€Ð€×€Ð€Ô€×€Õ€Ë€Ñ€Ø€Û€Õ€Õ€Ð€Õ€Ô€Ù€Ö€Ö€Õ€Ò€×8$:/# *%-.0.0//,'8.+,07#!1 7*+-/)*,55/+&$(*00,'3!-$#%'(2+&, $,.,',11273+/(%'/0"*.338-242!'51**.)'/,%("0/3'+'<'#0%8+#<'+)5%1/.1#+41+%)+,1-$650/-')2-($*%5*:($-!4&*0''.)$2%+$5# .7!-!/+-4 /$)*+.+!)+'-*,<)(-36/!,*#)/.-85&0+*'-8-%->;93*-)5)3 91)+(-.)1#2/,,*-+)"+4-5;7&%)(=&%:&1!-'/")/02/-'& A(-4+&!(.#'%-72'::3/#2+*%3-2(%#''4),&(-&<4.'+:/&$062*2"%,!/2#/1%"'-&03,-)/2%+.-++%*4-!% (,%4+')!,4;)%'&/9,'5,46*,,)'--(0!)4+(),"2&0*!?.*@3/'(-!/9*'=2/3-+/&:/"'&',0.1!/'!22;67*5(/*)=+:/,.%/$(789$3-)(6/='#€Ù€Ö€Õ€Ö€Ù€Õ€Õ€Õ€Ø€Æ€Õ€Û€Ô€Ñ€Ù€Õ€×€Û€Ö€Ø€Ò€×€Ñ€Õ&5.#%0&+++5/435(,/1/--7+-#0%%3&..$:(/2,1%15:#+')!,-,;3) 31/3(6--%#-6(0*10-%1 ,"10-6.1(29"(1(*8+(/+%5.+5(7"5')++!$*4)4/*2(/8::+#**(.103&&+3-)4+*0 $6/)-.)+05283+.-,8+#)')444)''>">!&)/$:.845 (1(%!3-!--"7--)#..$,)0(+3)12',).-0%&() 7#)25//!/#+.4()/+,+&*-/*52!.)'%+%'0+ *7%0"(5-+15-'0#,57./++''-6''%-0;+%6/6,3%4+*3)€×€Ö€×€Õ€Ý€Ô€Ù€×€Ú€Â€×€Ô€Ñ€Ò€Û€Ö€Ö€Ó€×€Ø€Õ€Ô€Õ€Ö(3&4-+2+1'.4!8-*//-'%4!+.--$+$).92*,*,3 )"&7.20-,)/*29(5%5-8):,(-+3'0(-*5-67&6,//+0-$(50#3#"!!*+17&.#('#,+$0%(*,*4-/..36'8-*)//#&*&&.#,%-2-1)/"./)+7)0.&.'"0&&.1//8'-$1$;&.:2-2,6%%2.-(/1/2&*,-,,(,1(00/)'*",.6.-())*-6(($)&+'.%'&+'3+(,**!'7&&+$&"+-1)/-6274<,#9*3/-(2%*#'1 "%.'86,/%-0)'0"6!-&/()'#$((+)%+&/* (02.++0(*!%3272()6)* 70#$0/,,#)%#"%38+(!$+#<.'3+ 2*-'<'*2#'&'/440*%2/-$#*7-'-7/--3)4%.-#(0&/,%*6(',@&4.1/&0000.+&"-84/"*-)%++0()0$)%0#0$%+7 6($5<44&4!"*#',*5,47;5A+)+2)6&+//.,,01/0€×€Ù€×€Ó€Ø€Ö€×€Ó€Ô€Â€Ø€Ù€Ù€Ó€Õ€Ö€Ú€Ó€×€Ó€Ò€Ø€Ó€Û>;"4'-/0/7,753%=&)&'/)0#5#;2/'100)+#/1&27(,8".2:*.+/(**-/+'4"!2/$/33&'),$%)7$*%%-00()+/&1+3+#% 4/02.*2!8,).%'/3).;%$.-6.%,#1,.-!)&9).)%/#3% 12%/ 3#%).0-/(),&#%").+/*%+34*%,$!$.+/!*,847"+%,*,04,'4..%.*!.?-&.3-*'+/.1#+(*7&'"+#9'#:+*1+$71/.-*)"0)-,#%$%:$4,,>3/0)%,',',.(3./(7)(&*'&4 #+-*4)"*--(*72$>6<)3,!2-*+,-*-2/*.,9(.2.,36-)-("#10.-'12-&*61)-*("&/-6.*!8-5&.25-,/%8+-,*$23 )#(*/))0&2-'0)7%#(,2*02%3&1))&-.%*(),/1.5%+.3('8)1.5'.*978-,.6-01*$'5-.***>8* $.)-///1)8)+()03",-1/6&613*6-/€×€Ó€Û€Ô€Ö€Ò€Õ€Ô€Õ€Æ€Ø€×€Ú€Õ€Ú€Ô€Ð€Ô€×€×€Ô€Ø€Ü€Õ81*!2*3#/73/45/-/*5-3'.$-7 6:'+7643+1(6*+08()+(/.7,*17*.*-89-&")-&*-%.1"%1-#*2/0%%:-* '.*2-/5 "#$;(4(&.0 +'7.(=/5+#',3:,/)-/.1+($4)/2.(,,,,/9'*)*+.& /%-.+*,/'21*'-0:.-/$28',3%6,)/''# ''4-3+***8#)5'3,(!'1:)") 6/$ ")25/-&0*.2$(&+,/)1&'>(0,2$+&34)0)4+)9 #3(;)9(.#/'2*+)-.*2.//#($*'-&%0).$0-6*%&-13.02*0'0'%*0*0)**%/:,160,'/3..1.)6//&0<+7(12''/ 5'+#-%-,+2#*7.0%+.3(/07,#&!.%'#-(.6!&),)"9'1'0(,$.)))3 2"""/'))((,)"0,4-*(%'-'*3/!;. '1*)+.,/-,9.17.+(*4'2/,'$.5%7#%346-41-'3%1.)-&('3*71#(4+$,.+€Ü€Ø€Ò€×€Õ€×€Ö€×€Õ€Á€×€×€Ò€Ù€Ñ€Ó€Ð€Ñ€×€Ð€Û€Ù€Ø€Ò5)/E/0,1!+;"#4#2&)(202/6#"1+%!+/,).25%0.-*%41)","!)*-(';0'.&'*7)5+#:$-!21+9-*9%&3($/6)25)"4+3)/1!" 0+8."'"0*+/,* 3/201$/.0&@#-%2&%1121(097.8*8$-402/+.+65-'-.$3./5))>.'2#4 1/+' '"'5/'1%/,'%0',/5$3++/+.$!&.)#8# 4"+).0$-5&"(,+6!!+*#0-?+&.83)'&'+).,( 3,"54(,/*,+/41%5')'&+!&,-/1(/3/!',/-(!$/!)3"&/'(#)+*&5&*3-%),6/+)*)'%/75++' /2)'2(3#("/,%,5.)2.-2"(4&1*//.,3$51,)/!"/.+!+&5*)#/%4&'2") &.'!+'&7,&."$.&&'776&6/$&($031-,',*-=-7?1 75)+-43/1(11*'$-##(*).'/*(9,"&/205>/(%61$5&-,!,-1+%('/0,+34'€×€×€Ò€Ó€Ò€Ò€Ú€Ù€×€Ç€Ù€Ü€Ö€Ø€×€Ô€Õ€Ô€×€Õ€Õ€Ú€Û€Ö7&6$2.(4/5+)9,'?393+99*/* 077&4.(:'5*3)+4.!4. -(*020#'1*!6$++#'!>136<(*-# /4*+/6/ '41.$%12%*&)((8/*-"1+-'3,/#&0)$2%'(2%#1-!%+&.,.%9-+63'5'"-).'%<60+*'*720)'2+3171+-.$/($))#*83#)1-31((.&-&-'&0++-&)+-0+$!**+)52 /+1;$-00,0*(/--'//$#0(-")%1+0$:7 ,'8(1(%6!*5*" &,+7$(513*-,*-%% '02#+)1&%22*#1$0,*3'%%.%2#&!3#'-"$.)#3<5)/1%1*.+6'+"(%*44+-368""'$(061 2$','"0.%*4/3!#+),4#8.32+$6$1&#*$%4%2/-/#1!08'/5124&2),)5,$,)#$56*),0&,"+*.",5--/(,5-''/2.%.,+* //,D&-////)08(-",'/)43..+30*143%/.!3$)(&+,C6!%"4-€Û€Õ€×€Ò€Õ€Ú€Ó€Ô€Ø€È€Ñ€Ö€Ð€Ù€×€×€Ñ€Ö€Ô€Õ€Ù€Ù€Ö€Ò('-/61(,74*1"023,2/()0',%(17+#4037+31,;:'%&-7/380,'"+&-& #;'1-##0+--*;-)%1/.%)*5(')321.4*")(&8$7*)*$%0,-'#%30/"*6&-'03&+$!(90./,./$,#'0*(#!/"2'/,16")&'207 5)(*0(-$'.%%1,,)2<*9)+#6,,,8%40#2$ (3))-4*'2")(.15+(+%+,='+'.+",(794+-3'$&90-)-/4**+*-','.'2'%48!24',1!11)(.#/4/-/3)*3'.,&-6;5%.-#3$0%55/,'1,9#*$"+!++.#(),3"1,&'*5-/"&'/-+4 )($!#(,1/(!,5&0!(,1/20"/*/&.-%;,38*7+035&+&-01/$.')103++.;3'!.,&,3/*'!104$+2"/.-!6-''4,.03$'*7%- 2/+%''$*%'-8&"0(*))2&6&#'A.-"'.//6:+)4300&$3--04:+#22")/?3,;€Ô€Ñ€Õ€Õ€×€Ô€×€×€Õ€Æ€Õ€Õ€×€Ó€Õ€Ú€Õ€Õ€Ø€Ô€Ò€Ù€Õ€Ô#3'@1?:.-+3/1:'!#/2',!,#(,#0.+7(.4)/,,0/"(.)+%'(*12/$ (#0/%(/%!*7,6$7."68',*.'&&.00''2!&D672,(6 0/2+!2#*+/#!)07%6/&%16).($+63/.$"#(534*/)1,'-.)/2"*"'/.!2.22*3%.'(1'(((#*2,-0#+&/!#6&*'/)/$2 - !"5-*1(%)+, +!+(310,/*3/6#3'2/-,*57-(!>,38.+93:,,+#64)*!(21$;/+3-',$3!.)./###1.0&/'$%*0-#7'1*+,%)'92,2'%'2.-)--!-*01"+,/#1$0---.+1.-3$+01 *'7&*'-7'%(3-%6(/0)547(*,,/G%//'-$$* (4":&1) "&&00*/%*-& /(",.$//0+.%%'5,)-%.+,.,#5-!#/$$!-1. 7)6"%3//+#(*-3,#)/91,1+&$/-:.4*%+/+/5:-+(0(6/3*&1'334(€Ø€Ó€Ú€Ú€Ø€Ø€Û€Õ€Ò€È€×€Ô€Ô€Ò€×€Ú€Ó€Ó€Õ€Õ€Õ€Ø€Ü€×&%3(2.--75&5.+7$)*( *%)4)#14(401,3->#*5+/",4,('0#6(:5-0('*,$*#!2+5*%7#'3/+-1**,($!1-(-$4*'6*2/$42/)3*5)&1+*$1'(,',2$"+/$2,)+%(1%5"&-0/,+5(+%-"(&5*/&11//!(6'*81-/&&&'/$);,3 863+6+4347/-*38-3=8"5,$3(-5*''%%$(,2+0-;1+9*++(&+!((1%!"#)6/)764(+.*"'+437*,%B*/<2;''(+(+.2*23')5** #!'/&*/,2-;&"1/'++-)2*')#"766#'3#'++3-'1+:3614>+,1-0!&,/,);#29250--/7%;(+.',&**,(*2'''+%-*'+&0'/%'3.2(&$"0"-3.3665*!-(4+/(8('5'8,.42)(+)(,7)**/-*)2*+3'/5(#2,*'05.!-!)9' *-17355.+7*073,*)+015!/'76'')'+621'(!2$+2+*8€Ô€Ö€Ö€Ý€Ó€Ó€Û€Ù€Ú€É€Õ€Ô€Ñ€Õ€Ù€Ø€×€Ó€Õ€Ô€×€Ó€Õ€Ô)<')/0..(-25-,*%+(&('.4.,$*1 <.6,!&4%%(1(/4*&++3%"# ,%83#:2)$*3!1-21,/,'-/(>!003'5/-1(,'%,6--#()'$/&,/*'*&/5+ *04.&#:!"-%0/1*"%2(,'.<*(4&/&2.(**3'$1--2%'&%+<5-&'//(/-2-36%$+(4-,)-0'%#2.%222/03(*+-/&%.(5+,'/'%-"1!/.*3%6/+*3+5.),$,%&'5((/!++$22&%)7/)' /+0%'&"(/!-'(11),).8'./(-%/#,&6'/+$51(-,)(5,1/,(*%.8$1%/*.4$$+*!**)/.97 (.(!$%2 '/;8#+*!#/!/,'!81()%//&07+*/"4*)0+2$%37(-&0&'5!'+/9)3/##5/)15 .: ,&"%&&26*).%/!'(%)+,.+*!8 2/+-$/)+-/3,-'.%'(/34/& 5%$-5)+1:%<-"*+6&/6-.,%1%051/(1-*&/+9%('4/€×€×€Ô€Õ€Ù€×€Õ€Ó€Ô€Æ€Ö€×€Û€Ó€Ù€Ô€Ø€Ò€Ò€Ú€Þ€Ö€Ù€×:*.0'$6'06 *5)..4 15''0')0--1)"'' %1/1*1-).0-," 1)2/+$/,1!&(6)(%+.+-(-807,37!5+2#0'*24+ 2#+.-0&&+2*,&,-)0!%%(3*+))3,&--)0++*6*/;9&*/* (152(,+0)&-*4;/9.(0,=.0)6*($+652,2-(0'*,'1 )!6.'= +4#1&#('$+2*-/%-6!/6:2(3@'>,*&,033!5/80''&+,0'*1)4)&-4*64$..+0+$.)1/1!#%+5'*0 01'/./+*)&7(2').(4)0.2.;#+5%A<<'..1/'))*1"#*F3(/0,6)4,%-2.%(*%6/'#+(($&0-%+81,($*$)*/'"#1&-'&)"*/-7)25$*('*./%$(/8/(2.+)!220.12#'03")/+%(*(0-"%":"+-1#3).1/*"+((.// *( )1//;33'1306.+.9..2$1'*+.*(5-1':-*,&8>7.7.2$2(((1).-2(/1)'0-*(€Ú€Û€×€Ó€Õ€Õ€Õ€Ö€×€Ç€Ö€Ï€Ø€Ô€×€Õ€Ó€Ó€Ø€Ð€×€Ò€Ù€Ò",4.*#8-#$13,'91&1/1*(.%)/-'((2(24*.#,098/+*4& (+5(')$-5/(71/)+/%((#2)*3212/2+0+'-$..*&&#/#(/,2!+<$+2()0+(%/2.'0%4,320,( 3+!03.*2<'0*-42/'$/(4/'5%$(.&)112-35 2+4*:3=4>#$')-+((+%),/#$/$#.25:05.0#-9)126$/(6*(*-#(1.,73)#61+4%3+1<9"7(#%-&&1%&&3.%042;)2'(/)*49!#(2*3/1.(."*(,1+"9,++*/',4!302-/)1-.(+11.*+#9)* )/*3% .+4/+,-":)4.*80((3++/ ,5)!%*.!(-)4'*.'/+,$,)31))1 +3++1**(0!&&(* &5-1&*'1+$,&$3,$)1+&#(#&03//)01:(18-)9+/())++%(%&*+).4)-",3/'6#14<<6$&7+/*+#4A9+-"1-3'&/,-*(/%/?2-0E%06++0'&€Û€Ù€Ô€Û€Ó€Ó€Ø€Ö€Ô€Å€Õ€Ô€Ù€Ô€Ó€×€Ó€Ø€Ù€Þ€Ö€Õ€Ñ€Ø.)68',,73(6-,(9)402+1&+-)/(#-+)'*-*",,013(8&5,)9)8($'2!3.'/&*-'%#3,*3'#5*,$/++-(4-3-%$)62)+0+.0"6+=,*0/%%*'32./0,)!7-(6"!,(01$.#+/($',,0--76"0.3/)-6/..%,5%2'&.'-&,$'!02  */))<,$)#*',/%+/0'-/0,%7',3-3 ', )*2$%/''-)/),)1%8)))(2/#!'*) #)5*.)'321-1$+5 --(2) 8+& ' %2.+"$ 6 %+%90+#-&*$-0%-+*)@/.9'-*-('-/9%'5-**%(4+-*./@/,(+40//(3 7.)2+-.0(9:2/B0+*0$6/7/*5+#%031!,82-(4-61-/%,*&62):-!729(;08+1#%)(*2(/'2627&!&;,(4<#"2%-+1,/11'!3)7 4'3,)/()0''(!';)19'''%- ,$3%&&1.5'14<*'.3+7;+,,'95-0/)'7,%C/€Û€Ù€Ô€×€Õ€Ô€×€Ö€Õ€Ç€Ú€Ò€Ò€Ô€Ø€Ö€Û€×€Ó€Ø€Ô€Ù€Õ€Û""(,-)/$'3)/-#.-%-13$5$275(0'-/*)23,,7215''@&/3,-5-:)-((/"33(%826*/!.1* 11! 700')-->2$!' 0%#(&-',.2.$&/ -(&%5%").5)4#)-,%/%$(0+95/$-!-2-+-)%6 .**3',3;,99*&%)0(+%!4+% ,.0'&!.$-'')+- .+!603(03*4$0,&) ,-,-$<(3/))-%(3&(' 0,2$(%8*+%#(:)/,?&*"%%*-11$ %5%"'6&$?,$#,55)+80!7,1"04,0+6+'*/%-)!!0'7,3*&/!4(*!050$87$(,$*1%%+'//)-*$>),)5/-1/-'+5% -!-//1 (-+;(2/(1*3/'(0:*-(-0+0)."21/<-(0&))'+/+%!*.2%&")) 21)&1 -$7-(10)$-&,#) (+3&2*&**.89*<1*%,1.'--//*-'0')0/);/*7)1+'$/&;'73#/*%-+"$*&-%()+9.'0=+6))+23"-2!#=(/((2.$+/#5%"')'#%.//63#&.$)40<(8*/@.-"./'&-+:/)/#/$01"(--*,.)'+.(6+24/4+7#13':*&-/22/+2%)-#%€×€Ñ€×€Ô€Ù€×€Ô€Ð€Ñ€Á€Ó€Õ€Ô€Ù€Ö€Û€Ò€Ò€Ó€Ù€Ó€×€Ò€Ò?(<15"+62).'&.$&(8+6%-/"/'**'5,'8,),*),37(1*)(),-+5 +2%**,*3-5/%,35+')4%(4'1&*3/,518'630/(.2)7#.$+1//'),+*2."332+-+)*(9)/(%0"+9."#$0/('$,5+1-40%&,*4/%2*)8,+451..1-$+.0-/2) 12;&&&'7&1(71/4/)"31/&%#/'+%+(1)$*):* $));0#)41%,.5*1.%0("'6'2"-"5$8&0+#'5*5)%*2(95//32((1)#+81>$+/)/0/#/(*8..2,0)5-)!-;(%%9!61.)2$.%-)*3+-4%4$-0+40-++4$)-')1"%%-0$/,+5&3,+%(,<,&,)*#$(#+/=)*,(5!)-7)2!9+1/-9$&4,/#*/= -5-2'7. *3/#"(0)(*"0(&702+%(*"/-(.-/++-.2'4/)%'--.3.7 ((0- +/'/&&/3*.&/+)- -$(9)1.,)-30,6,352.+2-€Ü€Ø€Ø€Ö€Ö€Ù€Ò€Ð€Ø€Ã€Ô€Ö€Ò€×€×€Ú€Ô€Õ€Ó€Ô€Õ€Ü€Ö€×#:)5'&#.*72*1 +&70/='3*60''$.'+2-"8."((.-7 -4(..*+('1./'/5*,. / #','*4735+--/1%6/#-'0+&"#1%0%04+/2/+$)*//65/7,13'))1%'/+)5*)'4(-!:,, 71$21,*4+%%+:)/+2&+#412 ++"-13-(9.03/3--&<,)$4$:)!"4)!0+22)+#222&)4'&/.5+$ '+!#&""2'&2*.,*3B'/%)2)))4, ;&#/%'-+6;+"*)'!( $+#0).21!35#).'-,5+6).-"#&,$/183*$/64=+!4%00)'-1-'34,-*)$'))5&-+/,'18&3/#*3(/(/(+.%'(%+:&44(/-/5*"3/53%%)/32&9 /'.-&7#.!+$(%$)***5&**((1?51+2.,5:"()9&03+/!)&38111:%7$%,.(3(!-$5;/-.9."(;+'-7/.,)5@++)-))+&582(('+510423,%./>*#2&)2€Õ€Ø€×€Ó€Ö€Õ€Ø€Õ€×€Ã€Ù€Ó€Ó€Ú€Ù€Ú€Ó€Ò€Ó€Ô€Ù€Ó€Ö€Ø.)'2-*%,)1595,1/ #:-)%.1<306*./4)%+/115/29--'+**3.$/*/-6+*(70*2+*(210(%;.;#10*0."@#2.+/+'5%3,!4"+(7+&$.))9#+)))*#*(%/# *1%++'0$22*%,*,( #1!!3(+.,+C$&4,+0'5*15+5/'10&2..-"$33**31(!7!!';%48+)/0+/% &-+1#!)0 9'45'7&"/35.)/&6(0&0&2&2 11*2'1&.8'/4-(%.#,%,&0--,0341.)'4+.0;'2//*5-&/73(%$1')#+-32*#6-*')3+ .,-.00$4#*(.'3@5)#2+.45"0/65+*:*(08=+#()6&--20"*#3!*.9&%1*4(2*4*%*&2-5))65'/2.:9003&$",-&+<,#+,+0;,!#""".&(%/6,-3+&#&-$0+/++)%2'&/1#80'2'=&+72+470'&5:,+,'/.*,+4730.$-02$2+/+ /%+%2)8*/*€Û€Ó€Ò€Ù€Ù€Ù€×€Ø€Õ€Ä€×€Ø€Û€×€Ø€Ü€Õ€Ó€Û€Û€Ö€Ö€Ö€Ñ/),/*+0'/2$.2'-91'*'/-58*%$-"3!2,1*%4)%3!"((74$8)"(4/1'51-/**0&1(#)0-:.312. -#&*&*+-&5*'')"3.%+6/ &&>4":+( '.+,1/0905/#/,0$*;?2-4'*615/#5#$)/$(/$0#0)(-/(&(()(&*5/&+/"3%.=42%5$(8+:"-,'/#12'+8%"26'('1#&31&$-**,)*07#42'.)='/;8)7,)-#,("'@24%)*4"'0!.%/06,%-3(9*)%,/,5026()54",&3*'%1:$3+(1+.(.*2-1.630$&$-1*0 .$$,!6' '"%(2-''&.11)*'4+'%!(/0%++#//*) 5,"3#,&/?++:8:/(('$"0/.7(*/.<+#7 "59/*296&0&(*&:/)+/"+1;- %5/- )*0.-04(1+#-14 *./%%3+$,4-'2-3,'6+1$7,++-+=.632+-3+54/235,3''/76323+,3%2)B'-2&&-€Õ€×€Ù€Û€Ö€Ö€Ù€Û€Ö€Ã€Ð€Õ€Ü€Õ€Ö€Ö€Ü€Ñ€×€Ú€×€Ó€Ó€Ñ$+%*)2<6()**-&4&,0"0G-(/245+(1243,.8/%)*/-$20,'7%/%'+&29#2%.7+43,7$7/./',#(-))).4,+4-->*6(:18($1+,%(+"-.#.*($='/)3!#1E)2"$27=% #))/#B")+','%!05&-*".'5#%1)0#.=.%3/1)))!&3*$2/'&.:-/$6/*011-0&-+,3$''0')/(8,3+#,!"74()( %7$+-$#(* -*&;2,,+-/-"-"0&&3)'1;#3:3*.2+#,#$+%,+.4/6%&'E(/-#4#-$9$3'/,:/,)"?1,,5#,"&*$()*!,,(02'8.'$-''2#"'1",,3&4.+(#1/)&*).) *!)#1/8)*"3+/00*"0';1.+'"/,,"$-2*"'".)%&(*&5!:+).,#&.6$*-4).')+0%(,*9/+4-)(-*"/%),40(%1)1$*4.#.-))(++-5<2-<‹290*0%%0,)+3.&(%2/?C-1-:.+;7!.@<3,+<#:,181€Ø€Ô€×€Ò€Ò€×€Ò€Õ€Ò€Ä€Ó€Ú€Ü€Ø€Ô€×€Õ€Ö€Õ€×€Ø€Ö€Û€Õ1-*#10&)/5/357/3',%8)3%53.)%(''4&$)+/4#&8'2/3.+2$5#21*3%3*&/+.'-''%5'1>5'&9)';,2(0(81/)%29(++4#(.4!*.'(*1))#5&-&((4"/**38-09+&,(*/+$($/++)3%04'0$73++$-(-.%#76/+*!1-3/$(%*2),,-,1()1.$.(2.#+1/2(%..(,/.+(6.03#3')48*$+$"(51*+%1&0."?&'!&0$3,,&!*&2'+)30!<#,.3)(")/,'$(=+!,:,'37.16*9.-*-2*'"5',)6& !($)*%&4/+0/.+*/0*1#?/9//6--/. '/6!'4$,'1#309(+11.+-3..0/5'53146-)5.#-+&0!3.+3-,('0(8/74-29')1/+23#$%/-16)(+-.+-/*5+)*2+7+*.2/+$3-/$+*&(+$+""+,'6(!1-0$)0'#-67):ƒï:-./5747*3*'%"3-$*%6-++)(<)!4//35/"&/+%$.66€Ù€Ò€×€Û€Ò€Õ€Þ€×€Ø€Å€Ö€ß€Ú€Ø€Ø€Õ€Ö€Ô€Ò€Ò€Ö€Ø€Ñ€Ý&++1/'2!+9%'324+F2-*-:2&&7;$3/-&2,/),"1">-%)3 !%5&9-3'%(9001!.0&-/'.2'/3+1(.-E//1)%'33-*&/1.&,E,0&31/+5+'3.7::.+-572-)&5&--4$/#!#/*3*&./,&1.+*$%-+/<' 7$-(%'&)#4#@** 1(2,-:))24$5#,0%""55%3,&21/301$./)/2$0.)02/6-1(,)#"!/+-1&$.*#1/*#)343/'6-#7':0$.24#'%,;1&&.'+*$.+-5%-4'*.(*()!3.23&)0$03,,25-.*6-)2)(,,+,6%#'.(%+$,21,#*27-/!+!'..2/,".<*%))',/.>5'! *-1230%4,,:05+=(5/0(&3!3),%0%0 $2+)&&/%<.,*4'%',/,- -.(%0;#.3**(,(7#:..10('0--,-) 40+&)/7+"+&-,:33,.6+)""7205$&+&()5-6!08+1+1 .%45$!5&/$%))%/8%+2*7/€×€Ô€Ø€Ó€Ù€Ö€×€Î€Ô€È€Ñ€Ñ€Ô€Õ€Ù€Õ€Ô€Ö€Õ€Ö€×€Õ€Õ€Ó/!()&5/4/9!'+*%%,2+&%''41:(,-%/87384(/+/2:5310&26#**#8+7--%,/-%01-#+0-!11))-*(3+/833.).#)$$50/'1/:$'!-0&<8)3/23*#$/&"#/2)&!4+4-0' "+"+#2/15.1-)&"*2.,,(*+. ,&7-,14!#-*3.*%-6-$) 5*).,$%/&0'4,%/#(2!%*%81)'<*',/27%/2((*!9+*6'(*/@5)&!*%/,,<5("2*1'%0"5&1**-*8$1*0+'"+--11-'6'57)&-$&0#;3../-'*0)+*<+15,5/%%),#8;*0!,-*%/19$'/"$0C.%'1/$+24.$/)&+04(-),4+5-#-*'-9-.*(*)5+#+2)3+(3+#"(4:04&2/"&2+*++0(%( 3 #!//5.#'/4#664'/7,+'1'.1'+18)%+0% ".5&1%9?)4'32)%#81732 '#%!'02#(4(83201+3..*9).(,,)+5.$*#/#153307',:2€Ô€Ù€Ó€Ö€Ò€Õ€Õ€Ó€Ö€Á€Ô€Ò€Ö€Ø€×€Ó€Û€Ö€Ù€Ô€Ô€×€Ó€Õ+').(&3>,#,-/440(+,.(-.006/18),!1(0/--#5%/3/,10!+*0/&2'-+8'*!,/0/*1*/1%2-'0%03%7*,//9(&* *7'/%!.'-4+-+("$)$%)6--%*'(5#*)3%2!';$6"<&#);(*'$(/37))9%%-$93/+-08,31*-%2..;4'+!2(211/(-*+ ).--04/065,%0) 4('.%1,2",#$16"46,804(€×€Ù€Ò€Ö€Ó€Ö€×€Õ€Ú€É€Ö€×€×€Ò€×€Ñ€Ï€×€Ó€Õ€Ñ€Û€Ö€Ð,*0,4/05-.//16%4$"4*'2(<43$"/,"31*5*%;/)01*(* 45-0*/46,(0/.1A!4+#&#(;--'-8-7&&+")6!&-$2)2*#=#//.9"0&#!$-).%+)'15@$1#7'',.)16 &2$/*44&,'.+.-3+$86/9/4'-3,.,#3,-3+4,+#, -3+%"$*32"+#(/ -'!*$$0/&.F.*&! "+"3 *$,#8&,*(,528$/((./)'*!),!<7'(40.49)21+'&+00304.(-2!1''-1-632.-90 21-+2,+6)+..+)+$1>, ."#2.2)$)(*>%+-(. 3$(,/(6*/////)%(.'/8&7)31%'*(5!/(),!&4,*-*)4,.+/)$50*-=#5+&./#15+5/' /486*8'*)0*+;))+1+96//%&0(37-+7.0'*'%'1('=9&$+0+#;32-%*2$.'&7)"%9%)*8/"03-&9(+()4)0/0,*,,(1=7/*,%'.*71/3%,=07/+6€Ò€Ö€×€×€Ù€Õ€Ö€Ö€Õ€È€Ñ€Ó€Ô€Ö€Ú€Ø€Ò€Ú€Õ€Ô€×€Õ€Ó€Õ0*%+%35%1.(#,) /+3<:.$15/+1,(-2"0!3+3 "8.!6)$'.-/'!-'&.+*))4"/2.(&*-,)2/%(=%3,2(-.!'-.!&.(.!/-,:--83%/((#*//)1*..+&+10+.-+0"('++8++2;*(-04)(2-+*#5!1#-2*/%7+66)-/%%3.%'.215,2#*+;3'(*6+:)?3:534$"#+#-#.+-0":15,"4,=; )2*5/),?8"1&$+-+1#/02!%$*/-)6,-<23-6%+0'1-2$-/+-2/,&"36)4/%1-'=%2*0+0*+0":!95/+, *2(+51,%.411)./),(/<"27!!:2*),$%/'65-/+.$,3*+%,20-7$+ +/)+/)-#8/031%6//+90*"()-1/'.-(*-)$(8'0.$.(360$#)29# 1$8A7/./7+./-%.1+(*.-<$;'6)*3!+))4#.).--,*+#2'602%2/623:/'1*)62+"1*#46/243'+%#53083-3€Ñ€Ñ€Ò€×€Ú€Ù€Ö€Ï€Ô€Æ€×€Õ€Ö€Ù€Û€Ù€Ú€Ú€Ù€Ð€Ô€Ó€Ø€Ô))1'/-2#/9.;/9'&-/0(,%.0#2'$-'4-,&+1+%#3',.&-:*-'*$*,%+-6/76,!--,)-$$*67$40-*+)05)+6172,,84%0$#203&)#!%&91 +%+&*.&+.$:&3!(.+%"0$-+-""/'.,+&1%(13)#&04 #5%'++#/2,#'8601&$)&46&1/436.,()03&*$#<,3.'!/-''1/81,/"+*-6.*%)2)(2,)32%;0'5-3';-5))."+!2+5#&& +)14+)*-"'%%)'./.*!1!%#0%*9&/$2%4%4)$012*/+%2+3,*=1!1$(4.'/%421--5)0/'.(.*,-2--)!/6@/$'2*"'*0.21.3'/2+%;4/02&(*+G%/'()61%38.4 "+),).$)".3*1.,*(#..-(%,%220$)'/!'"&9++,/.%356---4-"',,#3*3,('#0:*+/)0*)7%4%<+3):%28+7*,622*/0*(-//1&-*;$0" .24.#(-,))8-)€Ù€Ý€Ô€Î€Ñ€Ø€×€Ö€Õ€É€Ö€Õ€Ó€Õ€Ù€×€Ô€×€Ú€Õ€Ó€Ö€Ó€Ô-1)620@.44:A,-5(4(/1,'-43)0'01'2!$/-.2.-3*/<02/6#,%(3):&$)(!(-&,&&*+2-03%.11-03#+++1230"):.2(#&.412<#+'37(093/&.0-+!*1+2.1.2-4-(-<:%17/1#1)*2."7'*%!2&'368(#"#0,0!%7*+@/4*!//2+-6-'#*56$,&+3400,*2/);+2-&$1/)&%,,1(1-&:'4)(#6(6*.%!,.(-/&"-$ $-$.,3/211:$.-/+0,&-*/"(+0).7$'-7)"/!51/'3"/5-,'',%- 51:240&$5423&%/+1/ +' 50&)(&5,7-#'&2/*)',/5'-)-/+0*$!$%-%('$$'6*15-(0)40600')3'2'''# +$6-.-%7)''4-*&'*,1,,%/%/+!8 1)8*+0<4035!/#)-2+(1$%75."%6&0*;$'+=&*(/00%*):,)/!.60%7##&/";=0'*&*139-5135/(53!%(/3*5-316€Ù€Ô€Ö€Ù€Õ€Ó€Õ€Ø€Ð€Ä€Õ€Õ€Ø€Ò€Ø€Ò€Ò€Ø€Û€Ø€×€Ò€Ò€×%)6;63<6/,*-+30,+%#/%6*65'+86+-)/9*2*,035++ !/*(2)#%-//)$)$*< #,(50 +09+(,,,.+1(>:=*0%3&/)%4.;+/"* 21-1/-&&,),(/2(/)*% 4 2.*71'%$*(&-'#"()8 )#%(0-.35"()/**)!4*.0(";#**+/&!+7(+1%3'*.1,)!1)-&+,&0%2%'0-'8(%.,)+",%$,'*% 24!".,($3!5'&%-$&/=/-)/<+!%$'*/."++,2/!24.7$,-).(00"0.*-/'+5&0$+)29;'*3('1048'-257"',.3)/+*.,.&5+)/)1+# *0*,&.,!+.$041:,0"51.)**"'#$7-;0)42'%'3./42**.)'$27,3%,*)+!+)+33;>,2,3201*(-))(-''97&,/3%*4)++* 6#65("1(9,12"9/30.-5#+.2//€Õ€Ó€×€Ô€Ù€Õ€Ô€Ù€Ö€Ã€Ø€Ö€×€×€Ó€Ø€Ó€×€Õ€Ò€Ü€Ô€Ô€Ú1.-3%5142/,*7$-7/+//')+-1+-*'!/0+2.1!201!5*-+7'6*7&*))%-*$5.4/(!- +#-6(-+.%+!(2 0& 4-+/1&(.-*07(--)40#(&77!,.#0/$)1+,)$%84+"%!%,*%$+-$4)''!4'!)!452$'.0!+01+.+#71+)'01//)5-+/!,./6">$((.*#*5$ 6.6+>457'/.$7-!,/$2"!#)&/70.$22,/#6(-&)(*0,##;#&0*..,240+,$"0+1!,*.1.!'*%'11/+%(&/)/ '&6(/!51///(%67)/2!2';''%(-+ '&,-- ($,&:)/,(3.# +:20,#/21.%')*>)+3#9++,'-/,202.(2*(*8.1-705) *4&)(!(-.3("-&7'1#(#+/**)3"2"/)7-&/,''.,-'/$+&((!'(''$#(*%0#4$/9/5''&))0=/#=/ "0,#(&>5&,/)(*//9+/(!A11>2+*72,0&3,)<@(/B€Õ€Ø€Ù€Ö€Ô€Ù€Ù€Ð€Ú€Ä€×€Û€Ö€Ø€Ó€Ù€Ò€Ø€×€Ô€Õ€Ø€Ù€×794)".65.*1-.5*)9*,%)66-)*)0,(3'-,/$%+&52*+!-+!/)(6++/10%()#"7;(%%2)','--+"1,#(%82/$8$'-&.1&/&&#(-,"%0.,//11",52$/25((730'(1/(0"$3*%7$'2&*/.-&86!$01.-')$.!' 70//1*(0/-$*)))..)&-%*'*5,3(*%'?9+79,'#(0144/$1 50!&1)8*1(*/5/4',2+3((+&1,(4$-24&1!.1'(.#*%-'=883*(!'%-'9'0,$#/*'-&3,22$ %1-( +$)')1-3)*+7:#("71:+5#%4492+.63-*!,5'//##132&$4.!%3(/2-*/14&+'5,300!5'/ **.$$&87(!"6#8,#*!!31)#=9-*()31/!,)#/:*%.$-$&'(&$1!-#-+!/8)($1++4"*04%&3'.,"1)#29014%-$1-(4!0" %/4171)&*.6)<0>-)$+2(-1*9,1 5--&'63*1B€Ú€×€Ù€Õ€Ö€Õ€×€×€×€Æ€Ù€Ó€Ó€Ö€Õ€Ó€×€Û€Õ€Ö€Ñ€×€Ù€Ô)%+#&A ,.7*+/+8%-+'/32'&2#(1+1+'",+-0*'1/.=)'/'-//50+../-$+.!*'-+6+8$1%2%9)-0+-(*#'!(%,%"0-,%-+".1+5 4+*' 12/-5'1 /.)2.#+0&4*-5-!%3.70/02'(3&**5/!//52#""/#53$&//60% *)//6#&*-2($$$=3$#$3/*%(,$-# .1'8%.44%/5.<0+((#.0#5.)&/'-%.()$.14'#(4*/4#"+(.05*.')#(+/-,2&4-%2':)8/'0*-143+$)2!1*0,3$433(% 5*:+).)''' -8*7$0!!/6&/.)'#5**%/&'3 '/.1*2/1*2-%2+,/*-9=.10+4,&$)+,-&6.%3'1*-#(12+..(!.(0,00.).-!%-) 0,3219/'$2-*(.)(.0+!$-/,/(''($.04,12423--/497%/*%+52"#,3-3+/3145/9&+0- "0"-(:5)/3--)5*'')1.+,&%23,+€Ñ€Ò€Ö€Ø€Ò€Û€×€Ø€×€Ã€Ó€Ö€Ô€Õ€Ö€Ò€Ú€Ø€×€×€Ö€Ô€Ô€×;42)>+573*6-+)03,8-:607)4'3+-+C2>A#2+(5.- %)(+/%+8/*32(*+-1)/#,+#/3)03 .)/(+657!/-/ 1+9)!0*1%5.)+ +$3,,,&"#48+/%'5!(4*7%%/&/*%.2"-+4(/-/,+*)-.$0 "./*,%/%:+3."'82*#%"*&!)"*/5+*-.'#1-)+%)0&-.'30'"3)1?/2.14-58//#'$+$0,5&.-+/111+)/$/41/+(0+$*(-'/5'-)*)))1.&6)'7('.'9+.);-2,#.&+#/0,)*+#3&&)24*.*-8,//3$+'6)+(+#.%(+%+02'.)-%4!0,$0E-,,&/10*4$+3-$:*#$+$ %6%&0,*%7+2!++-/5#(9-+,1'+)!$4+)/##)+30'2&+6'"((.+()5##,0*-&-,+&00'''05%-+6'-#%*+,&20'?+3-2!,9-%59%.'#(,+5-2-7(.$)+&%5&"&)(6<>€×€Ö€Ú€Õ€Ó€Õ€×€Ö€Ö€È€Õ€Ù€Ò€Ô€Ó€×€×€×€Õ€×€Ö€Ù€×€Ô *60++220'+/-",.1++1.&&)'0//3(35+/(4+$9+,,)+,916"*++'&4))+#-+4*-*+26')4+-':2%/171505(*'6%#$9'+1'3,(,0%&:9-%=.1*0-3/.2'((3-/-!2 4!,*%+(*+0-*7)9<&0)#+8'--1+2110,422/-;.1$(4-60$-70''&/-"13//764(/(4//$%/&63-"-2").+*.6?*3.+*#/$*7*-/ .%)*9!0-#+,#,*$'):'+1/)2&15%-*2!+.#*7)*6/&/+.1--&,#-"*3!.2(1-3/-#)"):"5.+*.043/-/$'**3&'4)366(766))7#%*$"92-%!,,)/,11)*'+,!*,#2/(/4-7,8.8./''#-)09$%9+2().(4"%-)97/*:)/%1*04)+. ,713.70;+14'+4*$-46!!')"3%$/&&'*0-1:!.'1&+,#$'/"/0$.725(/:*&(/73,"/13+ (-+=:,/&4€Ù€Ù€Ó€Õ€×€Ò€Ò€Õ€Ô€É€Û€Ú€Ö€Ó€Ï€Ö€Ô€Ø€×€Ñ€Õ€Ö€Ö€Ö8*3,*#%,#'*(//:42/0,5%/1/*5#,')9%406./+)).(1,-/*$+$//-+66131#)/1")+0*2>31)+%'$. 2#1(/-&/()0+.4-*)%(';-."#,+1!*0/*#&3'?,%',-%:'".&.--1,#'$1+-*0/41*(2%+..--40!$%!$1'21..,(8-/.3#1 ,)*.'4(,=6+/4 72/5*,//6&&),-*/,1&*3(7,)#(0%(*";*4*4(- 1...&8+3:-(5/%+%(.9+)1,'+..,*%)/54+0*.(?((*&5/3(.$':'.:,,<'14-.+1,1&+3;4,0/1176+)'/(/,4).4!+.*-!%-58*.'+,.+5&#, 2(2)&/4,',)(/-/%,0$9(*+-<+1&5/9,&;5/5!&"&55$'-*02&.45;#.4-3(-="1$&$.&"(%1*'&139$'*-*/0!$0+0!,:+-=)9/-#0)#,,2+ //,0,/&-'19#,4)#%")90&+/!2,,-";6€Ù€Ö€Ò€Õ€Ù€Ô€Ó€Ô€×€Æ€Õ€Û€×€Ö€Ù€Ô€Ñ€Û€Ö€Ú€Í€Ô€×€Ø(+#)-?1*#%/0"&,03.-)-'2,)!2*31$/(.)6?2/--/)%#470+&0.)++%A.$.,3$'1<+/40,2($31'*10) /),:+5.#-*0$*-15#/'/)!#())6 !&! #0$#%,!/-#,$3,%1"%4,)8'8+(&*) ,$ 2+%931%4,,#$.:%01--)/-)0*!/')(%/:*.3#()2.)&+$(()A0(&-$#1%7./%:1+13)'("$$&)+$+172<$1)%* $35)2.&)( )+(,52'3/.$'7/.0$*+/! 6'%9%%38+6/*34'/.-77*/'*./)$/#7- ,7,%20,+$.%6+'1./%70'*"'%,(0/-.!'.&2=*-%/(4--;'2%(/"/*8:+$%+"') +:62$.)++4),0*/0+!7"()20-.51-/!96'0.8+&.'*,#(3,0**5%5#!0&''.!3+)*'6+./#"+,.7.))0&20# (20-52'95-&!;%)4=%-)26(07.-+,,+,'-6.400-#€Ó€Ô€Ó€×€Ø€Ù€×€Ñ€Ö€È€Ó€Ú€Ú€Ñ€Û€Õ€Ö€Ò€Ò€×€Ú€Ó€Ô€Ö2#<-03( 8-2%,/(/)$/+ ')-(0).&'3$/')!",'&0!0+!#%8&49$,,*-3&(,-7(<8--,$%%*+&%1+/-)0#$)+'('&1*8&.(21'6&4)!,0%5)'$,) &.(,0(/4&-* 4.-'.3-0,5 57#8/,/01.;+4+)0&-57!%.1,')79$-'.!/')1*='$ !(-).8#5*'/-(4"!0")(5+&%5.-(- %2(++0%/!9%54,,4)#('5+-'-(02730))'+,-05*0,,,02++&-C4+111-%*,=$50,,)7:0'4,**&5%)- .%!#''&//!%2"2*,:2"-$:-4$/-$2/&& -&3&+4'+.6$#/(2"$)*(&*+(-$-/+ #(*&/,+1;05312''*&B*+1.#.6.11*!+4,!03)//+)31,' )-!.'/--5((),'"+3---'/%*00,()0*3%1(-%+$.+)++)-"-64-945*) )/,4/ #3:!2/6*)6/&0)*/0%#2?$3(€×€Õ€Ø€Ø€Õ€Õ€Ù€Ø€×€Ç€Ó€Ù€×€Ò€Ù€×€×€Ö€Ù€Õ€Ó€Ó€Ó€Ù7!6$-')3!0(*36/*01'/'/1%*+&+.(.(:0)'0848''+'")3)0/.*5%&$*'*-,).7+-&/)3&!#-3,$+%%++7!"0$+5694(,,/(2!)/:).+,%:1&+-%/+$"0-/()95,/'32 %,//)#01'502'%#2$$*'0-0!)1#0'2.-/-0"0//'&,5'(4 '/'7.*1(-%%&/#"/+6$.',+(6(:(%)23+%$)/0!71(0(+'*31*+&*)-636''132+'(+&+1)4%5)/(!*$$&19(*%++ "/$$3(,:'%67.&*+./&!',$&/%+-("052 0#3$-++1.43/)(-*1+2!,"*+;5*,$3."4/*29#443$0-,$&*5-.7&0'''20.&*1*#'0 +- 5('15*&543@-$1,+3)5#0/942-"-*"+!>65%--42.%)-($45+#!6)&' ,-+,&/:97%,/48,!2%*6166*/-)6.(/'51'%6201,2-3.+/-./)/0&/3!8#/ 1%-'2",€Ö€×€Ö€Ï€Õ€Ú€Ò€Ù€×€Æ€Ö€Ø€Ò€Ð€Ö€Õ€×€Û€Û€Ò€Ù€×€Ö€Ø9'-3)28+2%*-.,().05&$'/%6 2,4&$<1,03)%).-+;-+1$(),!)3 (;-4*1%05!*',--<#0--,8+,7#+,*1$-3('+)(6,(8*'01, %')4%+-,'-*451-0'*5'&-(-+16./* 54-8//*6))-33#%-4#/.9*3+#&+$"',$.11)'/,2)*6,-)(?".'4!0%38'#1*#*--10,2/,"/2*','502')4./2$/71,')2&/&*(!+-.5#6(/,45%/-#-"%09&&+2"/&-7&224+25 +3/0'*/,,/)*)! .5/+(%4+61!.4-,&847+*0/0 %;+%+./%0*4',09!,,$--4&+1(.*(-3(%4%:1+$-%)0.-4-.5/2+6.(/$+0*./$/,'-*7)/72/*(',/**/).."/+-" -(.$7"2+#57 (+&!2'$404+'$("3&20+--%,-++4#7 41.0)*!2#'/6'%),"4'<(0$1/(6-5:'=,.&)8€Ú€Ö€Ø€Õ€Õ€×€Ý€Ù€Õ€Ã€Ù€Ó€Ò€Õ€Õ€Ú€Ó€×€Õ€Ú€×€Ö€Ó€Ô+(),2/+-6%$.5%0%*0,912)14*1)/'0:*#$&7*7)6")383'18,'/:*--+1+#5-!$.,.+-%0(#,/31/+582.32/+ 74/0-'/$&:/&0.54')4)+,**&,-*-.,(.1--)**2/,-19.6*$, &8+/#=/4-5#+'+-/(2"./4(0%-(6$*%7**-,3/+1=220)0.-#12./-6*'2.0(3%3',) 04#6/&.$*)..(%$,('),/3(8303&**6'*4-5&.*$)6.+,/"$6( #)?/ ,8.-,3*(29&.--,#,@5)')2,.27$'5-)2)5%)7('/#%,-/$%/5,+"*"/#/7'.2+'.500+("2:2*-*%-.//'#%!%(4(".,74-,*&(+..+/$(),!8/0/$&+046)8%,,##!.-1!0 :$#0()'4*#9/(+05-/%#2/6'2%2+34#$./*$--,7)+*$0-7-2;9%3','(2'€Ø€Ô€Õ€Ö€Ù€×€×€Ø€Õ€Ç€Õ€Ø€×€Õ€Ð€Ö€Ö€Ô€Ò€Ö€Ô€Ô€Õ€Õ-6"./(:.&*,+1%//09(#?-:..#-!-$.$55! '016-+*))6(+* -&0(.(8,0)+"2)/,-*.,,/-"0'$+%,+<%")$7**--79$0//4+?$3*#.2*.(2)7.%0&4'+.,.;9-2%('/"%')%1-"%/125%7-/74,41+'0!*%"))+-$:0.-1)!/3'7#1/29-/ 1#!(+"&/ 722#0$,15*#2:'$/:2!,**/1)'0#5)+0&!$%*("!3)0*0/!4**-6!"')#),&26>+.13.70+3*,*(24*+-)&((5) .B+!3/3&'?+*>#+#/$+)(-2!/% 1'$8'%-3%)11-$++*++'21-02>+51%&'4:0275+!.(".2%.,4!)'%+30:*$'2"5,7*:*&(/9,9?93+6-4/3€×€Ø€Ö€Ó€Ï€Ø€×€Ù€Õ€Ë€Ô€×€Ó€Ø€Ø€Û€Ó€Ù€Ö€Ú€Ó€Ò€Ù€Õ230/0"2&/.2,=')3+2&0,2-(3/*6#:17%5+70/465()#5 /&)!1/&-"-/*/+--('*9%$/6*'0+'8312'/#5'%(/,-+ 16%;$,,%-:#(&/9.%0+6+'*1+1&-4/+(+*,..)$'*-*)'6 (/+%9,''2,1;42)8. )1*-7$'05'80%&(&&-/+&&8-&,)54'.!0,1/+'"#.*,4''.53&--'"&% 24-/"50( 5)-+ *215)24,0!#,%28 4.(-02((*')$%#/%1)(7 &,'$!4%1/(,9 #,)1'.4"*.)-8*2"1*3$''2.-%4$/1$+")/&0*,(/1/.)042#&/$%/%-'.$'/0.G9*0.1/1=0)(01*%"" 2+10"/95;'((11'2'*-,-)5/-+51-.#-;/%'/-.,/!-.,+*!)2-40(./"/"-/*%%1#1:0&#'!+ $+/%)0(0++/5*#-*+9/-*5*,,5/("2,+/6,+,44"+171 !32+%5%'(1)/24*#8/4.0$*/156+(",+*+0/-/$()5+1"!-*(1,--$1/)+&%%-6--/*(3*',"839*"0.,1)&.+2.(8*:(-1#1)-/--#7,2/+-#+"*9,.%+*37+(&004173+<)1,92*--#52:5)(%0.-+&7..3/$'"5$2430 %7#(6:.10)34!5.%')!'*-12'-(/-&-:!#' 2€Ö€Ö€Ñ€Ô€Ô€×€×€Ù€Ó€Ç€Ö€Ú€Ó€Ö€Ô€Ø€×€Ö€×€Û€Ù€×€Ò€Û)+-%/,)/,.$##:)4$0#).+%3!1,*/(01#&6711'0//1&)(+2;&,8 0*1*%$2-4A'&)- +)'1:'54-'+3'/5/.'!0)/%*--3:4/,%.*&%1$8(&5%.1'*+ 2:1.+7***%1**,+2!;3$35,-,+4,,+320&&.$)(-.-8):(0+%J'/).&'4-/):-04/0';$",+,+*)-$++*#-$)#/'/- )/4% &2)9$-+-!*./1*30(70'"&-',7+$+81+",.,34)#0,1)30=-2)!'&1%%2"4.84(,&-&+,"#"?)+2!;(*#%1*(*0 1"(/.:272/#,8).!)2-*./',$/(#&/8*,!)8%*)*8?'-%!4."***1*2%9" )";!(+1)&.2*3,)(!"4(+,2#'*((&-0"+*- *.&&)+-.)*&.380,%5) 3;/)2/98&7%+),/&'3#'/'2 342)%1&(603"3*/-*;,)**074(%/5,/+2',025-,$-2,**€Õ€×€Õ€Ù€Û€Õ€Ù€Õ€Ò€É€×€Ò€Ú€Õ€Ö€Ñ€Ù€Ü€Õ€Ö€×€Ó€Ú€Ô 2>5(&49$).1%-*.0/0-7,0-5!1//3):&,5+1.'+4*2&04-",/%$+10+0(+0,*,09."08,*/3+4 /)**,'*5+3.2+%!,):'&,&-18%/:#21 -$-((#&.6?2:+%1$3(&+/+,**/.-'&11$4*/$94-$.,$+2'8(64+$!(!//3/!0523*/"061'0?2"12);0/$4 '%91"#%$-(-'$54*)/8/..57$-,$3%'.62+%+&/+7*$& '++*+34/1"-"52*).0+244$.*'),6'(40+/+#/)0*)+%#/-.2)-)2)02,.24&% %20:(0)-&0=$,#6 %-.!*(/$*%+!),+409)03.%-2();+-0+,-H0*((3-))-2#+1..3"0!(*&4/'0%$1110*"56)%&',!4",/)2*,0%-)*(-.!.(:4)*%)+3-*04/&%)48-6'%/%&4&3,'$.28(:.+#'(#+13,(.*4& *5375+(++'('15,5-*$€Ù€Ø€Ù€Ô€Õ€Ú€Ø€Ö€Ö€Ç€Ô€Õ€Ø€Ô€Ù€Ö€Ö€Ö€Ó€Õ€Ö€Ù€Õ€Õ4++62'-35>7),(8$#/,$(/->':)372&)/%%)71-10..,#,0-+$02!8/+:#*0-.04 $%0 /+#/+1-2,/ 3(#236+=7"50(-$'-*,*7*(+47/)&*!20)%1'(3).+)"2%2/(;&58*%,+&1.*0:-/3-***12+0&578!/%/)-A(;)1&-!$$&'-,&,/&(.'+)5%(/&.+5*3##:!#(,&)&6)//#"'!60().('(1+ +"$.)/+';@1#-#($.)./)&(*65. ,'+%-/+/(,'3%'4/1.'(%02.'0()0,(+/*&&.'/:.'-!'((0.*++4$,5*/4$4,'-9,"2,;((#910,01,5-4'*(4--&2&.7$2#P„ø+',*$$1!,(;),.'&7%#)*-/-((1+/"+-%:231.&*2(50;&!1 *%01+&1(7.2',10"/5.-+)!-/;&/$1"/-<31(,( ) 7$50,7"1(0.#&0#+!2+'4*&.5.34/%$..%.5<9%0#06€Õ€Ù€×€Ö€Ô€Õ€Ð€Ï€×€Á€Ú€Õ€Ö€Î€×€Ò€Ø€Ø€Ú€Ô€Ù€Ù€Ò€Ú(;-.6()&$'-.13$7)1+'*0 %::28-758+++3(%1-)(/1+(/=)'0601#"(**#!).* )#& 56,-='%1362(!'*)3*21&7/(,+,',/.9'*-6/(+2-* 333,5:/,/.)-,(056)+1-,1747.2-20"/,5&*60%0**'+/#(""1'5'21-!)/-"00)101 $30+('),=*3:/+5(0.221..6$4(%,2*/82,) /6)*,3)4%$43/+7*:57"$(0*.+8#>,%0,,-#/811*0*#21'*+'3751/(+8..!-,))!((+8,&,"5/$*17]‚oh2- ,)3!30',#*&(+*$2.3326/!-*&1/#+.7,!),(7+6-=&("),*(*04,*;"&+'2/+7&&-8&-9$(#'6"/.-0%3$./,.8()2)*4/,+16/06(&82/ :.*!7*#&2,--#'.26/-7)$>54;0/.1#/,!70+'+*3:+5)!*4 4 3259($'**-&.C0€Ò€Ø€Ò€Ó€×€Õ€Û€Ú€Ø€Ã€Ò€Ñ€Ö€Ó€Ó€Ú€Ò€Õ€Ø€Ø€Õ€×€Ò€Ó;5')#+18./57+414':*#/2-4(/!(7":2/(%1(7+*%%*-)&($-&-2:.,118&**")/)-3).$)9/'75#1%4&!1+2-,$*)$+.)4/!3/ <1&%%/5/"+ $,1-)7.57&24)$2+&)1(#,8'*# .5820#6(,0#!1'./,/3%($-=*:*/,970()&/*-$&3+*/4'"'+##-3#-3;)$3%*('$(+#&)2+ $0,*1/%** 7?%+3+)56&27*51610/()(/+*75,))$04&5=1-%/1(.,/2=$2'8+.&.-,3%)3>#3*2).& )*"0.$ !+''2*9‚eƒ²Ž-/!;''.$+0'(!0(,/.*1(134 9+3(4$;6/6.+$'*-+566./52463-*+'0$2"&1)("141(++1/-'.*0.2&:@..>,.&!,)*-/,($4($!.0/,3313*3/+80-2'-%&&3,*4 '-2,)/062$).%0-2)53')8-1'+5/*(3$ +5*06)0&/42'%7.%+#!/,226$4#.0-/0&(-'38+&#,%)2/4-5'-8400"!4.1+3&5/39/')00)-2-(,!&$)!27#(3*.!2+21)+:)+4)./(&*2/(-0#)%3&01-91))5+.+4&),(*(',#*4,0'4-)/9)0/1,75-/.*82'3'%(&4 #./(./4-1".-(.$1,-/',+0'-4=$&()6!(50248-0,),'4%#+3+,1A,.+,&0,;%'+-*#/ &>-/-3!1#+-1"'4/!0-*0Fƒ5‚A3-*88.*')!((0'.&6?2&4#0077-,(5(02--1/1+0,1%8*##3%'(/)).%$$))($,/$21,0&!)%:'5-3=%*%91()))(17(6.0..&*4*,4$;!(-!+1+/%%3."'./=6#013,%=.3---110.233'2.;.7A'1('/1#.)"!9126*1%33'1/-8'7/€Ð€×€Ó€Ú€×€Ú€Ö€×€Ï€Ã€Ô€Ò€×€Ñ€Ø€Ò€×€Ñ€×€Ó€Ò€Ó€×€Ø81(+3=+(3'0-$,=**+4."#,)(!*(!*&5-16324&/4 *472,1,53(&%<''221*-6$$,<7(",1))314*5(9/"+-18/,2)6"(.((,4(:,<=/ 3+.7)/#2()+*+. /**,,2+,&,+>+),.,0**#%.&%$/85'2'0#3+(+,9)*(-)'*02604)!001-0-#53(-'($3.1'(26+(;&*-&4$%*0;=9)1:*% *)/05'"/&%."/,4-/,&%/(55)(/'30% )+("9-4/ ,--3+1*<2#$*#/.)1/$15%&216+%!20,0-!0*%&0*0'**$3,(3#*)3&1<&2)//"0.'2%%3+#90+.!#3(*'#+ %2.,%%.*546597(-,-4+7(7*(9/'*,:+18+4(#! 6010%'09#/*(.=/!/3;$%((/1''5%./022-6#83'#1'2"3"'+$+)'&*"*/*6*0'(+'144")2'23#&/3*6(%)%)+'154**./$.*#!,6.142()''6"+€Ô€Ó€Ö€Õ€Ö€Ø€Ö€Ú€×€Ç€Ù€Ú€Ñ€Ö€Ö€Ü€Ó€Ó€Ü€Õ€Ö€Ö€Ó€Ñ='6?$>,55+%,-3.(#5,2/20*/+4&)3-,16(*$##3./ (-*(4&%7(*$/0/&.-,)2-4*,5&-%4*-&**,*3@(--7,/((0-'1!1)*6%,4#%''+/,,+"-+%&*#3 /%/47&0'%/0++&/,4(&0,616"70,!-/2 #&,)( )3#=%))1':41*'72-#+++$(--(!','/6.2,2()+'('1-+,($*10$'.---,5&('&&1)*))+6*$)#(+.!9-$('17#,30/*#.'/)*'$B!1*04 5*1*70-/-,"'1682#+*.30,$%654, 26"&,1 +2-423/3%331,/!%*3 -'($+3$$-(-,39+/'-#%/60()+8(4-,#&*#+30!3/8)&%/!"4=14)'.&/16,&(*9-160&/.4'7,)9)8((1)(%0-'20$,3%-%2+-)0+(&.'*/+ #7+2+*51;6"+:2*')6*95.+,77))8*$442,1,0 ,).-+#%%13)1.9=4.(0:5€Õ€Ò€Õ€Ú€Ð€Ô€Ñ€Ù€Ö€Ä€Ø€Ó€Ñ€Õ€Ô€Ó€Ó€Ø€Ø€Ó€Ó€Õ€Ø€Ô+,(.&'*0-271.5*&+0+*2$4.$$'5*& '/37.'4-'02(&6753-/.,''++.7$1%".&37/:*#-.:7$)/$1/.!)*.$#&,3/10+4'+$"'/;--@"*9*40-*"*-,' /&$)2(+!61.+#;$/A2//&1)5320(."$7)&0,2%4/# 7)6%)/6#,2('&5-..(*!&&.5(-3+//+3')*",1(+3-)/%-/#/"5131*.+)')!$.!(6'+/+/(061$.531%+/+()'.#!/-(2,3)0)2472 $&1.-,0),'-'&3)+-+34/+09,+-0+)17/.#-&)"*483:1&!'%#&'1'!&%%,-'4(/=0*51%"4&+'"%++!&$-.%((>%'61,",#"@& +%)&.> &+)7%*.;.)&()**'8%3+/1)0+,'&9(".95%, /+=8#/7++=-%*1/4$#28+3>+&5=$51/1/57#>&/!/,!8-,''$+*84.;1,3;&**3'€Ú€Ó€Ú€Ó€Ö€Ô€Ó€×€Ö€Ä€Ò€Î€Ï€Û€Ü€Ø€Õ€Ñ€Ø€Ó€Ö€Û€Ö€Õ!03(&3)20&+-*38/+1 41+7,.*/++4 (#"&/,(-&%&/.&2(&61%62&!24*:,.-+-0+&)(%0*/(4'0&+)('36'7,07=/%70(0'1%5;"&,.86/-/4( # *+9!#$&-5-(. /0'%%,.768'3-.( / .#+082)'/!,.739-"++""!254+1#(4'!.%7-*5(:2/,+,*0-246,&$07)*/'-,2.,"+.-4%*,0+-.0-0*%-,&'-*0,++'%&)!*9,-+8(''"#&,. .-0+%$-$)+,(/ $9*/*$1,.++!"*63D-$%01*/#/(+$",$3B0$0.$1$1.<, $1"B3''715,'(-1,/2+$(6+$'+5%&+($,)(%6 +#& 1*3650**1.%/0162#/!'+9,,,+13,26+'=5--,)(9,'("=2%.4 1+2 5,0,!5%-(,%)4,"7/,0+(C(4"/2.&%+2,)$-6,())(.))8+14.**.*/+'":%.0)+"5+-$+"'.%2'(+€Ó€Ô€×€×€Õ€Ù€Ø€Ö€Ò€Î€Ô€Ø€Ù€Ò€×€Ó€Ó€×€Û€Ô€Ô€Ô€Ô€Õ0+(0$(1+))-&6""+0!22,)..2-.8)5'/.&1"**2+*4$%.)-*7'&/4+3/ #:-,/&%+2=-'/)(5')+'*7-*+))&(4/64''%$'#/=37#52//@503-+.&')3/+=/-$41<"-6)/$5-1&-,!'0+(3!-.+,'%5(.#++.$*5"5)7'=+0*4)*.,*1$(.,-20'0.&&!-0,)+'*0%,($+/#$&$'.(&,+6&.(& 5'?; *-&#)*!2%,6.5$$&:5#.,#+/($$&2&%$),--/6(2"%0*1.,5,',38('3,%*%/$7*6$/#13+ "( %/0/*-.))8$-2++D')0(!4)-((1*(%))/('/21%,)&'!,,1<10(47115-%*&:#2,/%$$)/5%+$-$5*+11-!,10#/##$?0%*1"-!7?6404+202:&.5*3%,.21'A)&(6*8&!)-3'&+,>0.$)7$'5:,=0&%2*-#20--+"+7.2/(#,0'3(1"'10493,/€Ø€Õ€Ø€Ò€Ö€Ø€Ö€Ó€×€È€Ø€Ù€Þ€×€×€Ô€Ø€Ú€Õ€Õ€Ö€Õ€Ù€Ø.,:.:8+0-1''3'*169>%*36*+/'1$,!'*'$&!()1-.*-&;71,(*-(+"".7*0-+.&,"3$-//,%-/+).$04'*'*/12+('-,--214 *#''---#1(043+-,%')-"04%#2'5:/.*0,1.+$9!4.&-6%&.( .& %2/9("*/,/%8*'%13/9'+%*)51//)37*+!)**,1/' +03((#'3')().)-.0-.!-"/' 13,)9*5/$0*0&!.)*$#,(!;(*#/5+$9'/69=#*&.+3(22+7+(#(-+0,'"./'1(.3$010$31*(&/'*0702"!/$>7)3&:"&)32#*&'-%2#(+%*'550,/?-270!0519*+(/7.$4%+-)4"0!-/) &'/&.-''*33,0'1%./*#4.)%!+ )#*1/&('4.-+"!)-$*"4((/)8-+$7)&257515+,"+*%/2+0/4//#/80)<0)(3)%0" 875&,:)/7:8*.*='76,/(3€Ø€×€Ø€Ò€Ô€Õ€Ô€Ö€Õ€È€Î€Ö€Ö€Ó€Ù€Ù€Ô€×€Ù€Ö€Õ€Ñ€Ó€Ò+*$31;,%,-/ ..-.3$.!+.),3++$$0&')2-33/?ý"'!4#-48"$*/"-+0-%//*%&&/*1-'4)*+,359%/80%25+%/)-,"@3$26 4*.+9(34+*&%/3$+1(/$&--,&,,..,(45(-&';'2,- 0#&*.:'3"3'-2-2&&#."D+:$8/.!0%6,/*%31/(')''-/&(%,5.21--*"11!%.3/).'"-./+'%3"$/*# 4-,/4205(&'3#'2( ,.'/42<.%40-#(.1/*A,#)#/4-%'-*2/*4$,9)+7,1)-#/*34+$61 /&:.2;*/+5%%3&3+-"916(?.):),/72.7-'%*.0,,,(&)$3/-#%(-%,6205+'26!''!02+0!-7(/,2((-'43)211"$)1-/3&6-'+%.21/6!'#%)$+ 3%3'#,,+,11)'/(/-6'/++!$1"//+;!1+",+/1/1$2"/)$),1*+ .0,).5&9.+92+)'2+5)2€Ö€×€Û€×€×€Ø€×€Ü€Ó€È€Ò€Ñ€×€Ò€Ù€Ó€Ø€×€Õ€Õ€Ø€Õ€Ö€Û5)*3"*?9& $*&61/2(+/-+*-:*3-7-)&*4<(6Rƒý83/3-/%*0/'%'),02+9,/-/2+/+27>0/$+%#%.-#+ 3.*"(/-','1, 04#3-%##/5//9##?/+4613*!+,#$#)4,.!0**0")"+-(#$'&.'(3/&)/?-2);%-/#/'"0($,*./&+,2$"7('. .1'/)3','13*'/8..(0.&.'3,//%"%.#$/..#/.7*'"+4$,)6-&-%/'&+%.,:',#+.,2*$)*%7F(*0/:*2*,(+&(/'+**#("%/*7,-5+.&7,*<'21)2$$31&$03**8($-#(0-5(&16.; *&-2=3,"$)=+')%*&23#1.-- :/212,,/+/,%9/)1)//%,!.+"#'&,3$05/+"/1)/-0#&/> -0'#9&'$.--/**..),,-/&.+*!%5/%+& + ,(,-5.0)+&'"+%&'7'1;%'8+/)/+05*!8+;8&2):"(#&!/.18$34-0,.24€Ø€Õ€Ö€Ö€Ñ€Ø€×€Ó€Ò€É€×€Ø€Ö€Ý€Ù€Ó€Ñ€Ô€Ø€Ù€Ù€Ö€Ú€×**6.5=0#=-3&"*;)4''(.-013*:-/;,4-'(.,*/&/!+(&7*/0;-8+5&2',7%-/%$,7*).).',' 1,3%/&$7&1+,'%*'*!-'-,,%*'%160*%%3#*3-+%$$1),,$#,&+;('39'!5)!4>&,&(&/14)3.66925)!#+)221.,2%4207(,,*3-'!3+(,0&',-&/)%$02#->2.98*570$**('#)4#%*)04%0%%;61"35954,1135 .*(720.(.-"4%5 3$3$7,)'&()'+#%635.)(%%.%,((,/0*"+-*'3$.*'/%()*-/$9./0536+(')("#'//0$$4)3%+8(3+,.+./*,9+(6)-*;&1-&!3/*-&(2-!$./'61.,2&4,),*<,6--?#1€Ù€×€Ö€Ù€Ø€Ó€Ô€Ù€Ô€Ê€Õ€Ù€Û€Ö€×€Ñ€Ö€Ù€Ú€Ò€Ö€Õ€Ó€Ð,/)9%74'+('*1*--/,*-/.(#1)+4,.(,%/58.)504*-%----'#:'4&&9*)/3.*/(!%''&!5(0-->3%4/*-#.),)+3;/6,200?23.(<)!%*(8%3/306*3/*"*,94%'//+>!)2.+15/--)0(1$$/%$#"'(+12"$./&#%'%5+'2+#+.5"+ '*7,0/.!41.&60)/5'&*&/,+'',*/,*-"%-#,4.2 /'0+)(!*%(+/*-355$4(+$8.),(+4%:+*5/17-!/<13"$,1+..).(Ù)0,456 %'*4+/&+(,/-1*=%'*0%,&+(46/?-+3/*(&'')5'*2!'!<2+0&/+5"#3+((1-$0%.+%2(9$&54'5!,%%%.*1-+)-1$1")5/"+1-3-+&(2'%%(/'.#%740(##"!861*2)'- -,*/!'2,,/,50$7'(7-%6'-.4%%))5.1+8.++.$%,)4."1-*4+91-+)22/10-6,-4)-,/#-'(.%642.12€Õ€Õ€×€×€Ó€Ü€Ø€Ø€Ò€Ä€Ý€Ú€Û€×€Ò€Ó€Ó€×€Û€Ô€Ù€Ø€Ú€Õ16+2#0()#).-D'.'/1#?"+(+?2.2/&.%%")$&-7-@2),;470&')&71/1+#01&8)'7:&5"/)/,'#12%-%3)0<(.1*.11""/##!3.*461' 91+#,/','2$,)2++2 <70//:14+.',*5%$"#(*+/)' +##./#%(+2$)9+*34($+'' ,)/ +/"..+:)+90'2"7*/+2#0 4-32&0/().$*/-2"09/'+1(.*&.).3()&7- .#C(0$1+)$%%0%0,'3&80)*4‚9%&'&$1)!52,%//!$(3(,)707-+4)"01+(!&!%/!#6/-/)".)1#2*'+''+$&-&.(+:--%,%00,8./*+.,'+%/2(=++3)/)&32(,.B&=2**31/),,-..04))'&4233:0!) (073$0%2(*-5*&$%')&!6..5$-3/,3#%'#*.+"6)2%42$'*,,0!045 B,/<'9-&/2659;&:'+30./#3+01€Ò€Ô€Õ€Ó€Ô€×€Ú€Ô€Ö€Ç€Û€Ö€Õ€Ô€Õ€Ù€Ø€Ò€Õ€×€Ó€×€Ó€×!&'.:/+*))"(3!',)")',&+)(,2 4&!++6-2-!(1160+&=))-7/)+1*,+(-)*)/5,1833%2%*4&352,)0%/$1#1/-6)4"0*,(5&'++##/2'$ (9+- 34.7%*)&!:5+&$27#&$-'4*,#+-%+00-5 ())+1%)-)+'.3"2-*7.0'-,"1((+!0$=5$,/.(+'092)(',*0"#3-"'&.&($*/*+(5'3#)!(!(%-:4-)#($#+.-+%/.#+-.1(5$B)0&>,3/)%(%.F$./'(%/5.').'0"(6,250(-+/6/&$&"2).1'*50#93%*,!')3//3'8)/)#(43118$ (1-7%8,+,13*)*'//-../102'+'61(/#&'"/)-5")(k#&$*'")#1*4*+)&%%-5"1"(%35''73&,4/(,3,9'3?'+#2.4*- 2'&-2$/62)/10,)..075/%&%*6*7*29'-,7(+/+(&0-#/1!:0*-41.+%(22'/'./ 52€Ø€Û€Õ€Ø€Ô€Ò€Ù€Ñ€Ô€É€Û€×€Ò€Ø€×€Ö€Ù€×€Ö€×€Ö€Ú€Ù€Ó505+<563&49+&'3*30,,(+#0%)-1.(8/$+*>/+/)8&3+*,(',.*("14-#4)2$5+.4%*$#2. '&.23$0.)6$*8--3"/#+0/--&/*-!-=1"7,-+,,-/2&()7(1-+/C+(.01!2?%',(#",'0$22 ).7+'$2).,(1*%1,1=.3(.50+!-.44&-#0#.*,23+( 0=&41(,/.*.-**.:%&",1!-'+$1/%<3/(+(/&%++&!+,2+9+"./.'1(('/1&,'$50. '1**"1 *$+:'3.(71",'&4)&!$7+,&5(1*',**+&!& .83&04,/9#1'.%#5.)'"(-.'+*+,+-($/)#/$$,42&*7,.+3*&,+-D)/&)4/.0.'+*/%!++/$'3.,.#)*-,!.!/'##!71-3..%"%717$,(/01,)*(()4)/('/$,.8!,+4-%*,8-"+3-6(15+704)*!3D+1+!1/&&.3*189/87!')/*7(3"*14C23.A+€Ð€Ò€Ù€Ö€Ø€Ò€Ó€Ö€Ö€È€Ü€Õ€×€Ù€Ô€Ø€Ú€Õ€Ö€Ö€Ù€Ó€Ô€×/,$+6217"*-*.#61"1(90,A,-/#5,.)85'-1$/(=67/0)/, #&B )%#*2 -5) .,/)*8-),.%+.6&*.,.+1")*$%:01$"+.'&+'"#).-#00-30-"/*.%2/2+*(/83.7/('.#:2-(&*-/.+-)$++*+4,!/4--.-1! #7-%<",-(%4+;&'(0%-*$13)&'/,")5*526'24$&-,&'!/# (22'.-.//"#,/2#9%!"16&$5'1,$53;3-"0-3(%1(.'&030,)6-+/&'9(-3#..!1)+.!"8+)$,+')/-*$0-'!.0(<(!.!-)+"'"))%!,*2+--+(2'!//(* "0-*2&.3!+)&.%)(+1"+4(./-%"/5$!15,),,5/$1*.02& .&' '8"5053$).)2-1;2-)1'-1"#8%05+6',(#"(7-@(04$-+)-('>,"#.(&$3.,.''%)/(-%?*##+$6'<+!6.+3'%'/4:&2'4*/)4.,6+€à€Ý€Ø€Ñ€Ð€Ó€Ô€Ö€×€Ç€×€Ì€Õ€Ñ€Ñ€×€Õ€Õ€Õ€×€Ø€Õ€×€Õ-'+/4&,0+/1()''2$5*+6.*6*%3-(23(('1-<2?42=&%A/1.-3'&1,/0#$,2$'(!.).+<7+#*8"(-'"%/"411+103.((61$/1D/*,))0+1-*'%1 ./2+#.'!(,&'-(7'40(&*%%",$/#1(-% $-(7 /%/4.'+3%,#$-#2/"5&2%+/,8(!0&2!#&%*/0))/,*3(+!522+*$/%3,-/#2%*+%8',33$ #/%.0#(&$*,&+-'+,4..')4*3.- 345+)"2-263 '.)&33778*#+)'.+&$-6/#;('(,1,$*))2'14*('42+33)''35/('5/";*81$&6-2..+'.()4&1+-3*'*9&4'9"1,4,*3-$+**)(%" .%#,$&&/+&+#"32)/*(. $%-':42%$-+),0032!'7/ '$%)/,*/7&2,'! ='/*4,22549-'*%&,*1."=0+=7!32-($4<.)(()-30*1325/&.*5-0,6+081&€×€Ô€Ô€Ô€Ô€Ö€Ú€Õ€Õ€Æ€Ø€Ó€Ô€Ò€×€Ú€Õ€Ô€Õ€Ñ€Ñ€×€Ð€Ò00)2-#1,'(0/0$+)15,)51-, -03+="))4&31),-6,(9'3.#%%-*0&--.&-&!/6&)*,11&$%121,+#,//-&6',":%/1'.,$0*0".5081-2)+0;"*++0$5.(-+*$1)!9+.'-.$')#((':-*&#//(+51",+*1$-/5*-+++0,-,6-71()0,!$)+://%+6'(+-."(&% ++#/+4*..23)+)0=0#.%*$'+//;""+,605,'9.#':(&6+*&,$.:,#3)9//++'#!/5.(/!6%#3)2(/+),/-&%->+-.$7!2&+!1..5(7402)(* #/:)11&6(& ""4/>*&&)7+!+$.8;'33-1,".(-(-.#$*(%"/) 7*)-6$"!"%5***2)0+)$'(2%+$9,'<$':#16+*/9&@ .',$9%%--/!%41-.'3&4/16*$5=#'%$/)3(1*01+%/ -82('-#/'5/&2'.'7.+$(.9&.00-*(:*!+))9(.&-1&24:6*:%7-/=€Û€Õ€Ú€Ú€Û€Ö€Ñ€Ù€Õ€Ë€Ù€Ö€×€Ö€Ô€Ó€Ù€Ö€Ö€×€×€Ó€Ù€×&')+15/)2/.'(.7+'0.!'-,/,*:38:(*0.5+#.*#9"-'(#$+(%4/ '1&2/$)/&62,C+#%,6+#//'/5"/-1#+8#6./4&6*(("%/20#)3)--#1&%$1-/%+1)*(%3)>2.(9)&:;'+ .,-1&'.(2*'$,5*/.*/('+%,)(-"73!53(/%80%0-+*.$-.73(73::.: +0/331-8,3)26+%2+2#.4,-&&7)"(1-!3/$!!-7,,14(,%/0*"--2)/"/-/3$1<%-7&.3(56&",/'=-874*!)*/'%,/$//%%12$0&$+/*0/&0./0!25&'+#33#5((-3-$()51%/5#)/'403%!&($4)-/8%*/)!)/8%=',-73), $5&,&5/-.+$1.<.,+0,/-<(264/"*$)/(0'60"-.-1),5'*+/6-3.+!#""-(7&C*6&/0%<(*(10,"0+/10#.$*)1%$/*0%)"8+%)&03+-$&'3&8.*%+.&*,&-/4,.3++,+./239&3)- (8,#..$(%,)'2*%.-)./,/7!2)*3,)1%*2*/'"+*':/,2++;34#&%6*1+1,!.$#<27/)#-?167"/#(&#,<"5/2'0)+3+,6+",<)/=,.,)/%2.*%7&(9:+1+6-0'$,4#-1. 34#%/)10)(3.(--)41-(.+-)8(7%),-5&"#+$'+7"06%%/70+/#(>+ ;%/.0 .*!'6'#,02)0,*,0!93*6./-'&290#4#'(/*7>//,-- 4%06/4.-+('/- "!2*/%*6 /,34&%/62,.0++-*1,2)3-3",9-4;+(1<5+#++2.€Ò€Ó€Õ€×€Ø€×€Ô€Ó€Õ€Ç€Ù€Ò€×€×€Õ€Ù€Ø€×€Ð€Ö€×€×€Ó€Ö$7(6%,7//:-.'%/'A)2/8"4070-/%'$3*..9,.*).1- ,6**+;18#)2,1-,'( (00)&.*'5.'4*!0(-4;:($";75,4)*-.!,#5'43-*/+57&&.&0*/"405%&5+;2/# *3&(8$)!5/62,*4*/+/0.##%40'"((')+5(+-01! 112)&'<5"4&%6%8)'/0&!-%'-,(3&+''7)5'-12%3.5)+ 2271 2 )&-041''56/!)-2!%- 149$*&2"+(6! ) *#()2.33/6,6'0-,*4(:"/+$,&%;.2%*(4/"!.!577)00%3# #57),*)9143%/#2-8%/2)518,23+&(72,%.*4)%-()21)>/=6'',"'(&*+0.(2$'7%& 1&,(!).& /+(0@)+4837/2-'*)<*,?:.).,€Ò€Ò€Û€Ð€Ô€Ø€Ú€Ö€Õ€Ä€Ö€Ú€Ô€Ü€Ñ€Ó€Õ€Ñ€Ö€Ö€Ù€Ô€á€Ø1/.5366%3+&6./66-2-1346'*#6 5<*)4"(.7$-48)3&-*8)14%'634$6, "($/&!4,))$1('4,3-0###'-7)6.+419/$',))-,..2-!,%:2.2,+#&3.*.0 '71G4(@2$4$-35!/-,4'1@-3,-11*"(-<)')5((+.11/,"3351+;2!&1/6--*,%$2,,,.+5)'9*1'+)7$,0,+-,-**)5 /(%/"+-/9#<623(1(9,&0**-10)1'+(-:%2.)!((;%223$/6218(.)%*)0.0$#4(33:'-.&.&9-50.+,.4*.5<-+,=&,3/,$15 841?%+++,.'4%"9/0$)+&'/+44'%2'2$#2,#+--2,$)05.0./.,*/+6,41*$/0%9!96'*&#?#(1%4'+++2:).)'*$.5*6)/8!*0.$3&$*$0(!&18$6//54,&-";#)6..'0",,!#0+2(27&,,--I&)/-5 $1&)!*12,20,@DB95)1€Ô€Û€Õ€Ø€Ó€Ò€×€à€Ü€Å€Ú€×€Ù€Ò€Ø€×€Ö€Ö€Ø€Ö€Ñ€Õ€Ò€Ø+13*";%"*7.)2/0):5(6/8!)/1%+*:.).+1-2C#)++#3%)*0&$$14*5%*#6*-.#31:,*335//)4)02435#/')4)+#&-(-2*&+6)*457*(**33"-,&/-"++%!"/$()+"/=/2*.$32'-#%*;&+#43%")4!(*3)'-%*%&/51 !.)4'%,%-'-./)#')/)-1,%2$%/!2&!1)3%).-2$(1/)#,23)1()!4*85*24*.,/-&);82/) /(!--2$//(...'2(-+.2*&'(;3&8 2-07-*'*0'3#--(/&"*$!, 9(%74#)0),5,44(1-#./+)+2"+-8'/"1&-&$'/E!/',.'2$/%-&+% 4(.4)(/;.%42-'&$")&(#,-+)0-7+4&6"+/"3+'&*!(122:&/7.*1$-+#5&%&%(-+%*/;1+$!8:1//&!./.!#.-*3+./24/ 2*,+(1,%.#!(2/12-( %:(/(/+$()918-0,/.1*/-7:(0/$5,%€Ö€Ò€Ù€Ô€Ù€Ú€Õ€Ô€Õ€Å€Ø€×€Ô€Õ€Ø€Ò€Õ€Õ€Ö€Û€Ó€Ø€Þ€×32.%&.:25*&!(-;%H!.51.#/3(2)**,6)".44,)5,%4 )'2('.'3')$#(1(4'&$,$), 3%1/+'*.(9#3,7#&.(%.)#,)'/'=9!01!#++12))*5'@".2%4 (80.614+.+/0+*-0!#2/(-%+) $++8(-2*(+3'&+/#'3&"'/25"+)&'!+5+,#,34**-5+/(=..$-&.12#&%.+D4%5,!..,9&(2$)):**%)*")&/4/(*043#+6+$%!7'$B)5(3+ )700)0(1"$.)17,..(%05-5%#..$#(&##3'+-)-#&.2/0-=3%&-.0#00%25*D02+6+"&/$,#()&!%)/0410#-0.)-5%/&(-$&,*5),48,)'$0. ,+/3.24.(,-")"(5'$-,(0',((',(+..!#'8+8*/--(4(:,/)*+%-/.-(2"2,4302.)#,&/($.31-):'9556(+ '5753" #7/.,$5$8&)160+7%4.0-7-"%14773.:3€Ó€Ö€Ó€Ù€Ô€Ô€×€Ö€Ô€Æ€Ñ€Ñ€Õ€×€×€×€Ô€Ö€×€Ô€Ö€Ò€Ï€Ù&.6,&2)0*+5%00-*'-/+9,-2:1/ .6<4+11960(4350<58/5.69(."9/):5%42(/2./3!,*?2--(7+74/3(23.,/+;30,2(?(*-0/*3,8%+2.08%6'&?*&**'-+-!)6")61,&*$,)$#*)!--,)1*621*1%+.,$%5,/)')2/#!(#(+'!?.4-03+"(#96+#1+$4)(1'.**7'88$(*1/-%%.42/9*+1'!)&!/+-(*,,'*".>+0:",4:30)#6%/?!(*)#*-/")!(.'/+)-2# ##',*%)#@++)*)! *0%*3%2"5'17-#$01/624#/.3)0//-(-4736*/:#778.#*,*+)06,$:/)&6-"('>/4:@0!/#0(.)$/'+6(2-&;*$/(,++&245)%&.+%!$$1-!$-(/)+-,.8')/$1+2,+&/3',2-4#!>69#)5#*.($).,(1*&*(<"%>,)((<',52',3.."*95&7-8.%1"-0$2.#207 .%*++980&%$/)56+3#"4#-1" 9*'4(*++0$"2//"6'2.02,/'?7$*.,!%*"+*)0,&.(*-#()/&/$!2+-3,.(+,&,"-/<1/*<)+-,+6',+7'-5#$*0')!)+,-'# 12/*#-&/%(-/+-4-3C*011-0&A3$')%-%)5*'*&3!#--+8,+3$*-,>#3+0/;" *0.<7+'(./!&8+:*%€Ø€Ö€Õ€Ó€Õ€Ï€×€Ô€Ï€Å€Ø€Ö€Ô€×€×€Ø€×€Ú€×€Ö€Ó€×€Ú€Õ6/5;(05$70-$&,"#'6 .4,-2-$/'/#8/(3.%831+..20)$$03%.80$8%'-326)$=-4'43)"#<1!7,'/-%2 #%#:!!/2+*')'.0#-,.1!2-$#(6/2-4#%/6*+.%/1 /'"=2,/#*%/"5-6.(#9'/03'$',37.+!%*,'/133)6.&7+ .**3-18$'"'15;3/4'.2/@2#+/16'%&8,,&'%)+/+91(80/)+"&&6*'*-((".(5''-.+/.6<$6-48$4-14)4<#+)9'12))2/)'#0-+&'/+((20*2+5-1*++*7'$.-(=-/-.**%0$4+3024-*,*07 ."2,/19)*//!'/"'+/7,/('&((.$+!'-+$-/+(.1=$661-(.*)%%!4%)- +1!0+8+;(,;+*2%01$)/+',4%($-+*!,&,*<(1%'/*2$),;3/3/$&/$/,.%;,)5.&/ <&+'3+'#3)*08 1( %10//,+*2/(7&1(*,*-'&5 €Õ€Ù€×€Ö€Ú€×€Ô€×€×€Ä€Ù€Ø€Ô€Ü€Ô€Ù€Ø€Ò€Ù€Ô€Ö€Õ€Ú€Ñ262<5).%9)$%,'.172)#1.9(13$#0+')&+$+)18'+*+,*15))):(.%8'&,'%030#)//).,92-";/&$-%+%-42(+7&#'.+#61"#+"#4%#21(*-'(0-'A(3('13,3$+4((3%8%'(3/ -%)%#"#'#*$)/!1)(:#)"#%#"32*/6*.&((#(3-83423/& 956*'2#*"1:*,(%'*)-*&%/&'(3.%6$),,,":'30+12)(2'.=!'9!%%.'161.37$)34+)/0:/+'%&''3&&0",262*-..)3$&.5"00+!1%6(-&"#2)+&)( ( 2''&(%104)-+',(15+/."/*'&((&0'.7"$0, )).*?&*)# 08"0+,-*5-)*%.8-":+11+5")2&"50)%%-*+.&2$3)#"/=6(,!%+%#>#14(&).'(%(-%'95/3(!,4-.581)$+/)'!05*6- 02.,4<12170+363'&%.$4,.(3%=2#3./'!%--//)5+-(*,&'#€Ö€Ú€Ò€Ö€Ö€Õ€Ó€×€Õ€À€Ô€Õ€×€Ù€Û€Ù€×€Ô€×€Õ€Ù€Ï€Ú€Ó1,7+',&--)74")).!9).*'*/%.1+3+,)51.(6,##76$7',&1,,'20/3+,+3''07-'/%.+6!04/<.%/-.0>;)>31/212#52=*0*&%0,/)<)$.&(.5*2-!&$6)&25&)."-,*&.*0"4*5'&-/4,$$&-1%&-%33'63+.)53(63-/'80/=-,:+2'7!+*)9+>"4)&)(/).)2)&/4-50,1 -,$4)4+,0),.3%7;+,(1-!(&1+-)-+,-)+,-,*(%+* &/3'#23/8-%*4.&$8!2/'%)',&3-3<3)+&4/-#(+%/-3$8$1)!&.(*?33*()/1'*,$0.81921/(3/'/*,-$+" !)*.,%7-4"(75/9$1)*,*,$/-#2 )/11%'+0/-(9/2)75,,:$+5/)+.$/0%"*/.$"*1&34755*/+-56(85(,&9',+%.,+/,+!;8$(-2(/8+3.4*0+-4)*1&)063&6,)9/22++(-.*)3(/$(++0!:%62:-€Ù€Ó€×€Ö€Û€Ô€Ù€Ï€Ô€É€Ú€Ù€Ý€Ô€Ò€Ò€Ø€Ó€Ñ€Ñ€Õ€Ü€×€Õ,/7."&2#;$-//263.!/6-/'1')-$(7<%%:)/922$/(-:!(.2/"/ 36!2(3,+520-&.$"'%'%.0352<0/*3+1&-%/$*%052/-8,-22%.*%9*=*'')3$) 7'/)-%+#5'*'.,!&(+5/ =%:+'0--43106!:-0-(!'1/(!-.9*&15#,+*.6!1$)-5+),+7*5!%)5+$/2).+/,))%2%%'*+3-(+'%-' .#*.&0(&!)4,/-.1)=04.+/ %3%1.*(#(;+'0&)',:-&!++0"+(++;//2))0,"8!*)!).10.4&$. '&+%)-*/.2!)) % 4$0$'.'%%$0%./*"!-*8.8"8+(*')5.-*6* (,4/"0-$")0*!*)*-6+'31&2/)'#",7-/+(4-03109-'".4:*0-&&. &&/,%(%"*0*)).4,',+)&/2!%/% 13--4/02+/'8,.3$262-$%+0=&93>.)9*0%.4+33(/,(7+.%-€Ú€×€Ö€Ö€×€Ú€Õ€Û€Ô€Ç€Ö€Ô€Ö€Ñ€Ø€Û€Ø€Ù€Ô€×€Ô€Ú€Ú€Ù+11?047(;/74*191-0&5)1/69.&,$*0,0+4.&36&-++"(,;0$!5(23;.2&.#-()+&3)$3%''+7*,#!*,13.,+5&*2)-)-E73+#0,--925)*-7 //-/#6+155*/+*+)3++65**+/9#.#7.,0&)58(53& ,.)**)'.@-.'!14+8"5.!'83''(/"*06.%'1%+*)6",*>)7!4/*,"0$-"+2007.4*+/)5!)#:2/+5+/6$''*$6+74(%;1'4.()(2+)/7%*3$%22)40--+1/%'&)*)8 8)&79-!/(B0"26"2+ +7;%&)%**7.2(+8=#15!4/&,(83,2/&)/2,,+,,,0.' )2>2++ 536'+&4!*$(-(2($)/$@/$/1.)/%$>20(--)-(8' (!/$66+%.-$=64-@-*-05"(,)--//*&1)7.!+&,.4'.)9*&'(*$$4',3.'5*/&5(./6+3+3/#'!+$."70-07#G-+&/003,$.,%1$9)€×€Õ€Ø€Ó€Ø€Ù€Ô€Û€Ö€Ç€Ö€Ò€Õ€Þ€Ô€Õ€×€Ö€Ù€×€Ò€×€Ò€Ö%.+ 2+35+4/&805$)#$/-0*,4'/0#1)'.-$6/!15&24"51+62!17(3/)96)&!*#+')|ÝF%#0>:&#)=+&,),.+'! -!#*2,,+-!*.)).')3'3$6,.;.<1:--/-)&(&:3=0.()*&&+,,58"$0()58;7D64-+!1#*$5#-+.0"/!$0/ '7.)21+1!/+>,-0'+2)2&).-5-%%%,"%!,2#-/(. ;8+"#'()0.0* *.+(2,+' 3)90.3/'30-.-/+4')"/,2&,$,-"%/($",#-2/)$,/***%+!7'$-#*&0-/&.'-&+'"(*2.720%&023"+&-"'0*# +/)&*/30/5836.'/,1/&:<-./--%5%%.2 //.*9'%;,0!',0,)0,6)$$)))'*&$#$-5%.""*+;1(:3,!()/"'-65-(8+)1)/4€Ò€×€Ô€×€Õ€Ñ€Ö€Ñ€Ó€Æ€Ù€Ö€Ö€Ù€Ò€Õ€Ï€Ó€Ø€Ö€Ö€Ú€Õ€×+*15.1>:&(.;$-+#6**+*(9++04$'-5..+*$60+'/##0-6$/%,B(!:'&".,#,+$(,.2.#8*7'(/-/*22.!273%'1&"5+112:#.53+1*041''6/0"/ ++)931/5'3('.%2,QR6/*/+/"3).-*()"//#/&,-&* $(*/$,.*' -)/.)( ")%*#(-(2+4!+=.4/1A/ 1.65%22./1.10.,,3'150$02$3./$ ''1",.#0+1&%1(#,!2)$2,5=,;%'(+7./3*%:,6 $,+"0/)-%*#),*?+&%1$-#.*+)7#015 20&%0.#)*+%(.#3$2*-+/&.20I-5$)7++,2(--%*1-:34/23, /)%/+7+,3=(&!("% ,/&&5*(-,.1+!&%+/*''0-+*2$&;($+5.4)2)!;$3*4)80--/1 *0-1(,)-* 2%0&"/42-&()+6-17*1,:6/*,+-$!!'-75,5)%'2&)5/4"0-8):5(.;,6,1(%/€Ò€Õ€Ò€×€Û€Ù€×€Õ€Õ€Å€Ö€Õ€Ó€Ú€Ö€Ö€×€Ú€Õ€Ø€Õ€Ø€Ö€Ï)+35'?5%.':-*,3C-+!%,9"&17-2+24.%2/%.)+32(9'%,8/(5/>?+'!;/))(#49/%*#12())&-5$'2'7//!6*00+&'/*--+)+/&%3++,$/$$,6(0".+/++/1-'7*%,1-/+7 #14#"1#)%%+"")+,+(6/(1,&/09*&3)/&8+;/.8,"(0#0:7066//4&,-7'/3+43.2+/(* 6-)4#1$14&0*7+%2#&'246#'3(+,%&)*J-/8%&-.0(*23*/31+'71-"+&.*)(-%#2*4;+./)5%51(17)"###501'*%')+( :#<&/5#2$!-6"+#/$/045#(+(1(-/0*62'?'*2&(.46%!($)/'+/&-'*655+(*-39-% +/&-*'2')3$-%+("#"++-%)8,>&(9!:*?/,3''"--'!-9-+'&*<(%2).+"1$.##""!$0+%--;(%6-3*)&?%/&)7(>#242;/+/#7B4*0*+!.7$5%*=%;4)&:93-1+-)€Ô€Õ€Ú€Õ€Û€Ø€Ø€×€Ö€Å€Ð€Õ€Õ€Ô€Ú€Ó€Ù€Ó€Ú€Ö€Ï€Õ€Ó€Ú 5+3'.&2)/2.+3,($.$7(#+'(6,.23%15D4'..766)%-'4%*%<9'"-%"76#+2/6(2&(,4'()*#$&*&.6.2/%<(.0',*/"(#*/-14(7/*%(%'%'&0@*65-/""(!)-4%2119)-1$&+9%$'%"*1$55C13,&)8%#'1+'.$,5-',/0('-$-1'+.)-%-.()&452%!5/5(6 &0-)/3-/"&1+%+'!!8*!*-!4+0)*/++'3'$&(*"$$--'#)!+20/!6/2&/8$(++&#&++;)24',0.&89)#,9-,.6-+>26.'($);3(%51&./-,,+3077€×€Ô€×€Ù€Ø€Õ€×€Ö€Ø€Ê€Ô€Ú€Ö€Õ€Ñ€Ó€Ö€Ö€Û€Ò€Û€×€Ý€Õ*369 )'$4-/10/7,)./2/2!7'"!5.%/+-.7?1+(*'5"9%4&/&*048+''%(/-..1"/(1307421+(+)(+2 &6%$#).%+,1(-230'. )+:&0)8/)4!<)3*$/1:9+7+1#,/4#,#')#-!148.'%000:(((*>'6"'1(#4-,2-#!'&1&)!.9)# 6)' -*&2.3..2+)$/014*'8&#,)+2)(#,#2*3%(&''.&14<4&6$* ,6&0+&3%(&31*$*+!%%:28.$;-3&0/35'2&0+4%4-2$-+%31%,%+0.)/31//36;**($1+C+5!*"(:1*".%36&#$$+#0"&3//)#"*-:%:.*#%0&-80&'*4).9(+"0("5+)625-.22*42<,((+0(75"$>&"1! +<3,+&/,("+;!,-922*/),"5'!8+-6,0,(+)*+3/,.(..6+3(&4-*6+&/-9#".'-#;/*1(+859,#(:+3.'-& *(,*)0)!('@7-,*,5(16%3$6€Ö€Ù€Ó€×€Ü€Ø€Ö€Õ€Ò€Å€Ñ€×€Ñ€Ö€×€Ô€×€Ú€Ø€Ô€Õ€Ð€Ù€Ú20 335,//.;&=2/#/*,&'-+6 ((--&@);5()/++)111(18'(-',.+(.2(#(03+&4+52+-)/* 1)<0#$D33*0I%))<,)0&15+)%'%!(!:-'?,.-%)/,8"3%)'$!..:.#7%((%4&)).-+-%)10"-13$&:,+%6-14/(+& 42+86$,.//+!"%+0$'0:/-)(%62&$,+/&&))0(.+#%$&.22'652&-%6(/)/+!&.62*.4+-90,2-$2/% 75%!,./90<-%-31.!&%3(,.,+.'25"#./!'-6+-*./3$$-"-%2+#05$2/&-%-,4%5@//)&;+7/$%/,**497%/7*.;#4"(:23.-&+.-$2,+;#*#2#)'2%7'*0-$,83#,.9-/&D".')+*.%&&--4&*)6)'"$(&-2./,2--(+/-0-7'7(0/4'3$04'(.&-&/5&1$"3'-2?-% /5-)%$3'+/%#$,0/$7:-.,/*+160/)=5(3!0.5&€Õ€Ð€Ù€Ö€×€Ð€Ö€×€Ö€É€Ó€Ø€Õ€×€Õ€×€Û€×€Õ€Ó€Õ€Õ€Ò€Õ/;.2&&&%*'2/6-+/9%$)++4%);<$61'+,,+= .*.'!'338+2/!*$*#/.#)4+,$0/&/:*')"2(&(")'*3-(*),75+5,*/.&-1!,3,#.&.$+"0%+7-17.(283*-6.-.! &-2$-1#,$&(( *' *,)0( "-*+2/54*-202)/.)74+-3/",--)/)*','6(3)**--#)-'&#+-17/+$*/470"53'0(%3,69-$-/'*3+.)%2&/.3/%-/-%55#)0>(3-5'16$%*50/(()0&4:-5-8),'($$,+6#&61*2:%#,)/6!&,%).4.)1##*,*&06/**3%&H%*-(7=#+6*6(55,%%#1+/.0'--15693,7-),+%'1/3'(6((.(,4($/ ',+/.'9$$-6&*(/2*;.3(2/.3*,-1'65**!1/ &#.(#..$6):+-0/.#-05//'A-4%)..*%%7/'15"/*&6+36,4.'.92"/2!+/"(+;0+5/, ; (5€Ü€Ö€Ö€Ñ€Ö€Ó€Ó€Õ€Õ€Ë€Ö€Ò€Ð€Ù€Ú€Ø€Õ€Ó€Ô€Û€Ó€×€Ô€×."9:1-+ &,-"-)-3!'.)%(1",6/0',.0:'#&2)%,('-3,*=60-2+*3-6#3815*,-7211'"-")+0+:87.3"*+0,8 +.=+$.'/815#'*1+,97;-$(&'!71"")01.$0'/.27‚o7) './$0).#)#/#+7,2!$**=-*$/+/1.1*."2&7,&7/11+392(#)+1)#0+4(3,;5/)*1('&+*10+-(0*12/5/*-0-%**' 8-*)(3%7.&+0+1*'33++*92-+41)+1#..10-211.'.5.,#'/'4)/.."5,1'+') ,:.30**10,**+(-:4)&*,+'-;..5!1%3'62B6/1/3(7',)!!5%#$*3+'5.$6'13#%.)+."3#/*&''313&3-(50'+0*-/2-6,7))*="#/ ,)$%22"2,5').3&)$6/0+#$-31.-8 52''7#.'1:.'">&+-&3406/3),)/+$,)1/.+-/!3*,&1+)+$-,/0>%3+-*200$/-61&€Õ€Ú€×€Õ€Ö€Ø€Ñ€Ö€×€Â€Ú€Ö€Ø€Ò€Ú€Õ€Ù€Õ€Õ€Ö€×€Ö€Ò€Õ/<6(&?3)72;!)1/)2(311+6)2(+5(9.7+.=44)#**=*2&+#0#;$(((.&( -+',':-+32$+8*"'$-/$.-20%.0#(.32 '/-$*#(33-6,+)*+2,13-+4),'--6&%$+'5!#'%'. ;&,'*)(+3.$-70.&%2'1*#4')#/&$#52-).,53') 5-7#0'70,1+.5>.+3&13$(.,&9;'2%-.4205 */+0---41$/ '3).&).;1-3%1(8$*/8/-01/*'12/;-!-!0)&('23/.,./0+;20*55/&7"5+)5'')/*.($&#**-9*5';/5)/".)*7('9)-/@116+)3%+-;#.:*0/-**.'4),7%/),)-,-=+:$$)034//.3*+)-*&1*,/%&/0*/+,#+, ,5.+.%1.;/)-.5(.%1-2'%/&*&/32+/47-*0%*&"*9-7.%2#*71-D+"&/''8/),)3"?19(2742594+(&1&>-*32#%"@190-#11-8-:0;/+€Ø€Ú€Õ€Ó€Ô€Ù€×€Ð€Ù€Ä€Ù€Ô€Û€Ú€Ô€Ø€Ö€Ò€Ú€Ò€Ø€Ø€Ù€Ó*6/-).6%*2/#.474'*,.,-24'*&%6#)8$-+6)2+?000+43//,F174"/%, @1/'-)--'228)&)-<.- $','C/!8+5(*.%-5-2)54(-///$-#3-&.&4 $3+2'2(/*6&12,766().+6-*$%.++*4$@+);)+,('#0/%)+(-/-'!<3'32(##./+"-!%46--4.&&-01,&&,/',!&09/$-+.2(1..*,+-2450>#,/$3&-(11$'0*#+,8-&,1"1 )'275+(/0,7+)435($63/#.!1)&*-+0/-4/*2&'&3&%.".,++,,.2*#-)($()-",2'"1/)%)$', */",%/##3>12= +%,+5,.;'0+33-)+.&1-#-.'-0)-*3."15(#&&%.)6.10#-%+'$*!9+',+(+*--%9'/0**&3'>5#<(&5""0+,+-%)%$(+<5)1$40+')/4,!.)2(/'&-/29/-6)#12".1:.('2./(+69:-%-.1*&€Ú€Ù€Û€Ù€Ó€Ú€Õ€Ï€Ò€É€Ù€Ù€Ú€Ô€Ú€Ú€Ø€Õ€Õ€Ò€Ö€Ö€Ù€Õ5$'+.#*5/4*)/.%&,),!!7(2+1,(,#11/2/3!6,/-%0'7/%+*.%43#/-5).,(,%;/+#"1+72>3--4'+0),;-,6)-'(++ (,"36;*( */#%'-&.+1$3)6'&;,2#"9)/-"(+&98()+1-8/)'.!"+,//%!,.>$*/:@:5,/41/(&%4$&,/*())+(-6&*(-".$'31="0.*(/=).'*,06$72+*1%$&5,2-9-0-+!*+'2'-&+0#18('7.'2)+)6.*,/(5#:4,7)+!6-%0/'4%'#5)$41:25+0015&.9",9(5+1(4(.3.!02>:2(0(F*$2*07(&672).'+*(.47. $/'&/01%*,'5++2++,9$(#.*+'208'"90$)!+$+$-/6(+*'#'70.#!&'4 %20())83 3/"32;+13+'0)1+*(3,*!+&'/1,6())633/+6),%+6)('/=)31)$'&1/1*-&+$1,*/+/'*/&!!1'%'6'/#4%10/'.<, 0-#//"5),%#/0+#7-33,%&($#-2,'-*#!4+-#*0'#1#. ")"*9/--.;%+,&#/$?7-&##8!&1)-5/6*A'%0)+%%0+&261%&,$=/6*"7(,!-1-$/&6)5#,+//=(# 94&') #1?$# (7,%";-"1/$".076.3.&&/.21),#,*&$,4.1&71-.0&53+1+#.#*/9&.*4-1&8.(92) 1!+2)*(60'#)/,!)320*%8#8*--/-*,)/+3-&+(3)/.8/5&2*%*+'2*('-2.%) .,+2#2(1)-+/0 0- .#3&J'*+;),$3'-++/("$"772,+>45--$3A43+.,8#7/3'".,) (1914*12,5%')&$/.%627*%9;,<&€Ö€Ò€Ô€×€×€Ò€Õ€Õ€Õ€Å€Ö€Ô€Ò€Ö€Ð€Î€Õ€Õ€Õ€Ø€Ô€Ö€×€Ó.+):3#549(D/-03%%$%5'-(2%+'':$%/422'017019+0)/'0(2(0&'*9+0#)45'*$&-%+#$)+(#2/&:0*(,*--**),0%*/,%,0*&+1,- +.,7(/$.+!/*0',-)/81)/).)3($##+'.3**24$0'$*%+%7-/1).##"/"#..+)0/07$21'1,/-#4#7:-4$%/$175'+%,&(&1.2--3'1)C*,))(#0&%2$.+&60%(,%$23-"&(35-$7- .:-(%5$&/),$)**(0./(/(--#5*$!-%"*4/C-,0""&!)'/6126*)84#51(1-&%17)"')!3( 1/+'*("/5$81 =00.+/!012*6)%'?/0++3B&6')*546)22#- &5C+/'5,*($)$+<*9#,;2(6 =3(%*))&("%9$1&=#/!,&;.065-7(*,7#00/604/1-/*80),, #,"-3C#*.40;6 50%+./0&+&*. +8/25(70;>$1/1/%(67&2<3€×€Ô€Û€Ò€Ú€Ó€Ö€Õ€Ô€È€Ú€Ù€×€×€×€×€Ö€×€Ö€Õ€Ò€×€×€Ó//5,**&24;'.37-45#5'/1/1+/)''.7."&6-.)3 /*2+=);0*,3:,$(-(6)=7,/>1$*(2(/%+6').02*.!5$)*,)(064(13/*#&5/.-/.#+%#,')%99(-./)7-)++-*7.*/+#03(/+6/(%+!6>-&6+2%",/0-411!"$0%$+/0#.**/"($8)1+&9*+36, *)/-&/+&/&&)6 -*" )#4.2&,&'/!/2+'2('/+/01--'5%1%'#7B*)//)3 0%/,1/,0+.(-%)**(-0*.'5*4/('0(&/-)2$00-&*0&2..&).*+)+'-/0/7)%521.=#0+-'7?&.1#1%$%*+4+),&+&37*01)<"+<)21.4$-1#" 0 %!%81+4)<*.-23)0-4/*,#/+51%11,,$)3,7#")#-2.1)4670 01('.5C**1)23-"),4* ,0+1/;-)8''.3"+)+75.*)7,9/#*&51%---))*73/0.'--%*(/$4*:€Ô€Õ€Ó€×€Ù€Ò€Ó€Ö€Õ€Â€×€Õ€Ò€×€Ñ€Õ€Ö€Ú€Ñ€Õ€Õ€Ò€Ð€Ø-)1/<+1&2#(1*5,(,-()'13+%..+,/(4'5!-6##4&9+8(6,-+%30/>$53$2-&+" /,5&3),+'1/#74-*!,)00$.%,$&-&)&$)*)1:$--0!6$2"-+"7+1-(5(/9+74$04"1(30,%9.2*',)%&)('$9#4땠",30$/ ?8/4&#/)+ 3%)#+/)2**(-"6)'&*"'-0-7),)5) ')%#6'04'+.$'6'."2((//(/*(8-&-* -*9*-&0+#/3.,);)141'$,)(9/%431$!1/)+!9*;%+7."1%*-$4.")'0781"(.0')#3,02:,*0.3:#.%&7'20'!*/.$$0.-+2$(:#"+ .)(*6++0*,*<3./&'&-7,'7#+#+3'))308/0.@&/($+9'23$76+-62$59(0$!/;& 3/1.-3-,1(%&2"$ :/))(302&'%/+0'*/)4'(3)56. *2*F)--..'(028.81-.-$5426,(06*()6(*€×€Ö€Ô€Õ€Ù€Ø€Ô€Ú€Õ€Ç€Ò€Ù€Ø€Ñ€Ó€Ò€Õ€Ö€Ñ€Õ€Ô€Ö€Ô€Ö4(2!#1"5)67,**(4=),3)/2/(+'0+)'6,(67'1(0?+%;()$!,*+8'&.40)!*%0<1-5/=#<+.'1/&#$9+4".;<4:5*2+*!-!;"+/'(:28 &17''-+2(0,+%+4$$)2-2/,('+#*(+1/%+0#)3*:.)*.0&-(1'$/)1--".,83)/%*1%#'8!2€Ð€Ò€Þ€Ï€Ð€Ù€Ñ€Ù€Õ€È€Ø€Þ€×€Ø€Ü€Ü€Ö€Ù€Ô€×€Ö€Ð€Ö€Ø/012*23$+29-&29*&2)41+178&/"58&0*,43/-"#$,().4,$(7%1,'8-$4.)22%.*****///,1%(%(/*#2/0&0*"2,(5#/+"+.)/;%+-$5&+)/%&--#,)2(&089,(1:02,'2$'-.85,*/")$$'-*&&6,(**1(6.M6/1+5- ,45&0,!&4,6/09*)'&+*./##1 &.&')$/12)0+- &&!(+7)-!,).#&)**$+*-*8'$2$2*4--()3"-&60/&'81#))44'#'0,8&#&,-.!$2%46 0+;-2,1!+(211/"*-21/93/*A/,)(=0$3%.$("!1(,",2)')*02#71&0)423*(1-&37)4+-%,/+-*&0-1"0&-3&#$-6,-00-+8$85/.+,.2%'3+0-,3(5&)0$)34*/2"+"//'*.+-.-./$/.1,40010&3.-)+"*0$6/-$:2+(&00.2&+1(*$-/*)/63"5223.#$.1+/7*9/./.€Ö€Ù€Ö€Ó€×€×€Û€Õ€Õ€Æ€×€Ô€Ù€×€Ï€Ô€Ò€Ò€Ð€Ô€×€×€×€Ú2:"=3#3"%6:&5,*)6'6&(B7*5865+*0/-',"*/29,613%*-+'0)*.))&1%3$#7.62()%',%,*;;+%2'$.''/*,.?(?0'*.,!1$")+4+* 5@5")%0,/0--5()/-/!%+*1)++%+)0!(-+)"+/&00,*)155'5/%*//$")-$*/"#+001'0!.*++*(+.&%&4%10%2-)5*)4.,2.(%301)%.-%381,2'3(!%'02-3".%/#((0 &!',++7'+3,,8"/,,+8"(&-2-1-0)(+"(/&8.!'*22*1)/3/).6%%%0-23.3++-%/'.5-3$-/+/,/*")"73+92)02+315.1+81/$),*&.63%7*0*++4//16274*€Õ€Ò€Þ€Õ€Ó€Ú€Ô€Õ€Õ€É€Ö€Ð€Ú€Õ€×€Õ€Ö€Ó€Õ€Ð€×€Ô€Ö€Õ3.';-%.73.%*0:0,-018(0&1(60/*72,)&12/%5<+0%>-4/0+6;8--5:%&-.+1*.$2+*."-584&!-'/,043-?(/3#=11.%(((&!("6.,20"*$"%&(-3#-,,,+,'&*435 )'/./!,-!#&(1452(.(,/)!-$23./)%+1.(24!)+#(-*%2)2/',+*20&'+*+()51;0(+()52)(2$*10*3/32!.(#50�.&05('+,/+.4&1%*-.,'-*:078&/*0/$&.+6&22%-0'0+%+'9(2(*!%:'#(,6,/*12)-*+.$'&*//'5*-+/17//"6;23%.(0(- $/"0.!)+)-7")*0-)#*$<-*1&334(%4,%%#+;/+(0*&*$50'+:)5!&$91"8'/-).&*/+)&562$6%5".,7+6(#)/',.(.*(-2//5,.!/30)&970%81,-.$%6)4'%,0)6(6$*6%&58+";5&(4*'((&9-4$=.,,1#'-0*(/%;€Õ€Ù€×€×€Ù€Ù€Õ€Ò€×€Æ€Ö€Ù€Þ€Ò€Ô€Õ€Ö€Ø€Ø€ß€Ö€×€Ù€Ô503+3!%.&)-2''+*2),&2+5,(+/.0'+1(+,-8.)6:/%*49',001.72('5-4,+/$&%*%$,06- 6,)/!!.1//&0/%&/'/4(0./6*/,-.) -50!'+/"/-0,#0+(+#5#60"96*1%7".-222$3 (*$4.)+)7*/)$(0+'/*'',(.#?*!60*-)*,'%*/#%.-2/.-"&&3((&,5C*2 ),)5'$7&,+$4!%'-2"#5+%&%>%9-*4:'636/"'314;/#+%268.)&6-&&+*.;0$+'30#21,(1+/0 3 )1(1$2":9)73#/)4/*#/&(.%%34&)#649.,*%('(0''02%0.')(+))).);--9&53&'(!/*/3*-.,$2-#,*, *.%1)-()&%)(*!!.)2-*)'+// 1&,$,(2).()&#+,#/-,*&**.,6#$%*0,):1$-7(-<(.8$ *3/0"215*+101*+; ('4;31$&.4=92/*€Ö€Õ€×€Ú€Ñ€Ú€Ò€Ð€Ø€Æ€Ô€Ú€Ñ€Ý€Ñ€Õ€Ó€Ø€Ó€Õ€Õ€Ú€Ò€Ø-'(+11$1+*7D/*+*0#.40**2(4'7$/522"+%,(+A8?6,)2.==/2B$7/*5&4"*,/#-&$2.(52&)%%./38$"-.--(33)-#&%+)/494-3)3+2'-/,1(-$))#&,0%)-,+56(2'0')&3-*/#%*(&%&'*(3&*23**-1"%35'))5#08!-,!'-)),.(**00/81 (93>,-&,8")(*.-&5#&#)-4(+/$&/2- *!.%.+.+14&+51%.$-401)!+%,8713+--6!*(11,-3$$1-&3 3***4-)-*-09, )0*+%$1-+-.(."038).'(8+'+-/07(-!-051%!+1,*-440!+2+01('6,7302&0'+<0./:%/#%(-"2/!(/.4%1,*((;%"-1.&0.(-,*//+)5*8+*&5/05+/& 0)* /'.*),-(.?#+*%/!"%3)--0;(3'-.%%3*64+-+9-%!%+27(&23$'63'!2<++''6$"0331(1*-3(-,14 74##$.$€Õ€Û€Õ€Ù€Ù€Ò€×€Ú€Ö€Æ€Ó€Ö€Ö€Ñ€×€Ñ€Ö€Ð€×€Ö€Ú€×€Ò€Ô,)/-($$+)>49*4-*)02./2&,(#77)#"21&/4/(%-'7' "+54*"9-.(0+*&,#-6/-*/+2')514--'*646/.2#3,4#"1-'8/*0/.<(4)1+&*(3%"(!)&'+#()(9()*%4A1/.)(/2""#!,4#*#,+/'*4'&'-"3/"-(7';"1/9):(.'%;+8(. &2-'-/-7%//3-(%>:*%'4$-$/2-&-%0'!&%='(#7/$&,,(33/,-2-$+/5'%$31/'4"/4$2/-+,/03/,.++8(0&-/26*+,6//*-"$#/3&+#&8'B&3+*);29/!.*2$..!+1$%4+'/''+/% '#/(&,6&.3#,,9 0*4!52/**-/("(3'-/%/',#),"2%*2+' *=)#3&2.+*5#%4*0.,0*)()-$33)0+1,2+,(<$86.*,47/#),(&;-/()+)-*'(0*-+3,,+<,)'!1141-#%./03(%*04%%:'(6-**"":&9,";1:#4-)*-+*2'*06€Ô€×€Ù€Ø€Ñ€×€×€Ö€Ñ€¿€Ö€Û€Õ€Ü€Ò€Ý€Ö€Ö€Ö€×€Ó€Ö€Õ€Ö$945(#%3+"784'*2,1(+)-+."/<-1 @.:3/,1+1#,(*7&001+)4. +:2+&2#1#03.)#1&--(%/-&/,52/3*0*<5+,*. ,0/.*/.((/*&%/55.*+*--&*,,-4$3+,/5,42+:+%1-32$+)+/,7. (,, +' /"2+&'(/"1,-111%+)'/$35-5$",%3),&(-)9-),('%,0$'-@*-1'4/$3.+-.4''-%7%*1+94*()..61//&%/,0!()/,##'/!*+.&/(.5*4.2'5'#.*;$&,)4323'0-),!3;(".2/4-#*),7:/04* 8-*2!-5'+-+%3#7(&+ )1'-'1!-+2.,*)*'*11-.7%%'&(0#.55225*0-2'8)"0',-*+(2)2.,#$*"0280(/=3"6'.%12'+-+73-/5%#'*60<%!! 3&7*'-'"/0!98)1.63/.+++*4(-4/./"%$+<**.1:+102$*#*032!%6!1*3'/,€Ö€Ô€Ô€Ö€Ï€Ö€Õ€Ó€Ü€Ä€×€Ù€Ø€Ø€Ö€Ô€×€×€Ù€Ö€Ï€Õ€×€×,$$'7;-5,"9<1(%9".+5%(/+8$)043.1:)(,,$''#-1)(D*((!$('*23+6,#3$&#)234'"1,0/7. #<*-.&7'4*-((-$ ,"./",%,2-$,#(/%+-/,',)(')! /''*$-2%-)2/0!1%&3$)$1%-#--*&1.++'0***5'8-12"/"0'")1 0',-/=5)#21*"/(3,%6143/,("65+,/%/2')0);8+$#231)2/5,"&'$3$*+*:6+!.3;//#@(%2#,/82&(9',"!00034 +$)1*%1%3.$7%"+7-!,-'-"&<#*4&5,)*(.+//#)66$504$)5/%(-+23/.!6%5,$7&*6/*.+25'/*)-2++!/)+ ,!'2,-/$/'4+/-)(3)-$'0+5*1325.<0&5# )3"$2"*()$.1,,*",2*)$/ #+7++%+''1/7.50 6**3.''6#0.)5*2"20*-&*%&-2+2*52+27.+/53+.+4-%/€Ö€Ù€Õ€Ø€Ó€Ö€Ô€×€Õ€Ç€Õ€Ü€Ò€Ø€Û€Ø€×€Ü€Õ€Ô€×€Ø€Ù€Ù580*31*06--&*+28((*)/+5/4?$372 *-<'+1%%.5()"1+&1 =+//-1,.--,4!-"56&+5,,+/*3-'+;'--$*)++/#.+"?2$805-)%-#&62-**+-*-2?-. 1%&1+,+.31/$$*00)&!,0*'-,(0-)5 8 --%*/+4(*.#H-+' -!%+*7)4,3!./!*$$$8$"'09!-1(-!042/$'(-/,)(12$*22+*0/",<'3)-*!4*,&.5.*,<.(0/5**/%+$+$2':2'/%#-/,.,/$2,)+$/"//*/.4(.140*,#4,)+)5')0-#*0,9/=(/*-#%;'#%')8<)'*(&3&+.+--'&:5$(&$1&1#-,,3+-*/)6"/-4*6/0$,=.'"!() )7#.0")5((+.:3)*$"!), *7"3".!.#2007)2$/1.1%'3*+./31,@:(.3-/3+)(()'**+%/&/(/-,0*'40.)- +-*+,=1"<%*802<'+:.1/&;,.0-&%4€Ó€Ù€Ò€Ò€Ù€Ø€Û€Ú€Ò€É€×€Û€×€×€Ö€Ú€×€Õ€×€Õ€Ý€Õ€Ü€×09+*-*%/:91)<-/!6+206',45*#81*6+/-21(5*#;+.(,'76%*'+)'$#(.-*./#-(/$7#363+-*),)2)%,@)41&7).)1"-(2/,. )02)8((-32/+-/2)%-&)-1%.%12%/0-/'#&%,%./,/501'#)%1-+&$3#56#8527+$/$43(12*5(/""*).91"$5$5&)$/2,:$(*/00.1+3%)$./+*(/13*.)1,+4%'%-2,60#"*., ;-!%,11.,0 .'#3(++4+2!/$*)=,.)+#*>'-3&2*/+$/)%$-9-*1)2-6*&++,7;$'%4+1+*+#:16*44:4€Õ€Ö€Ò€Ð€Õ€Õ€Ø€Ú€Õ€Å€Ó€Õ€Ô€×€Ù€Ö€Õ€Û€×€×€Õ€×€Ô€Ú)&$/(7%0)*/1../-/'&/,#,$>-.',*$1#5*++.-&1<,+'0(,)+2/& 94*,,>!%#!&(/;*4!;(2,6*!#*0(6$&11.*.0,$%*#$27<'1+3%'?3'+0017'+,@"/9#-$1*)-/0./?8('6*&/0%+ +1-*"2+/4*+91)*($1;('*)".56:5//%.5/,3%4&+/-+0(*#(3(.5*$<06-'%)-'4"1$)%$.-4..5&3)-*,/#+2%/3!#+5*,*15%3)(+!#*-*-!*6%+,.)%5,-1+0,-:+,/$5'-2,%'+*,$&!+..&//+*.$+0!,.'B500:<'.')*'.)()1('14/0$1+/++$&=( !1)308/*6,32/*)523,/$(6%.5#0#-2.&((4.--:.%..)&("/(4&2 (#3%7,2)/%80/41"7'17-,,$.&'",*)*!%/1%,015$#7"()(,/2)&.2.>+318#%8.637)*/+/55%#7$ <2/!2)+@0,,*2%"30*€Ù€Õ€Õ€Ó€Ù€Ö€Õ€Ó€Ø€É€×€Õ€Õ€×€×€Ò€Õ€Ô€Ò€×€Ø€Ó€Ý€Ö"532"(+637(.14$1*01%4!4E&2*.0:*.%.-77)$ !!0((#(6" %+*/;&6,,(+00#((5+712-5#?"14I+-"204&*&5++*.>+!-6/3-*+( 2745*"76$-&-.',23/+*)3.4/4,%1#&$)-&5 !%72&".0++8-4-,02-) A,,*,(.6.' '2"2.*,-,"/%3.&&5&)%#/-/;-%1+51**5+-+001*&/-+/.(403-#03+2/041€×€Ø€Ö€Ô€×€Ö€Ø€Ù€Ú€É€Ó€Ó€Ö€×€Ø€×€Ö€Ö€Ö€Ö€Þ€Ð€Ò€Ú0.=*1,1'/'+-22,+&.6 )+1,5&-7/ ',&%*,/@4,!/419*1$#*@,*- -<%)(.,&06'/('+&#*+;'(/ 0"#7(8/#)/-(%.0#27#+/15+-,&2"-#/C)(64&1%/38.06'25,%1-%43!&.97+5*"*'+)!0))&"%#'2%90/<+3,'/+'9,#,(:/011((-1+3+1)9,.3"3!/)0)0%-#./-$#5@0+7(*,4%0/ '410(/,/)0*.$.'43-+(2()16/%1+09/3+*2" 200-1"*!#!%-*./.0)?(,)23'/46,&8&""2#*-+16%-,96('%4).0-5036039:--/$*,+%$*+115-'07'-%%2/05-)*&$..'3/',0=/*4.),%$6 +/"'5!./0#/6':+%8*)/!#$"2++).6)-,/*7/!;)+%+=9(8#1()-3)1/3'.4(& (2,"0/**',8&%',3)&*)(0)2+%4<*/+)0*6/<-+8) ,4+,%$)/4%*-%<&5+&2)0€×€Ó€Ð€Ù€Ø€Ô€×€Ù€Ö€Ë€Ø€Ø€Ï€Ò€Ù€Ð€×€Ø€Ù€Õ€Ú€Ø€Ó€×(*/.,=*9:&+35)$"#,+!$''.'"+)!-)//((9*/3 2*9:57-68$.)*/8+**'(-.3/0$26)&4#5#+-(+:2-%(/90/))20+1/#3!)14,7(-,(*9*%0&-E0*%-'!,2/#),"304%*")2/9*03"5+&337/&/)-.'-/++=%(5.+6;'<(&,""210-7++-.-/5%252).($$*-(2+./<,9151%5+*+-/.+&4-'%!5*,(127&&.8"0-$2)')=:3&%.<3!/-(----97-:, 73.-/-%+4//#()7'.1<3''23**$),!,)4"1*/&8)/.*/-:&13)/'*4981-.'&/#('+6A*/&1$65+#1)++!/(&/(-2,7'0/.')#1.+#-(*9-32!.4/17+%'&6%.7**3',&1++7!-*&5*-1.)28.40,+)'*-(97+3+*7$"# 0%2,"3044!*#+/3,.)"(+5+.24$K/'0.45-(,//(#5)1"52-0:,,5(3&€Û€Ö€Ñ€Ø€Û€×€Õ€Ô€Ù€Î€Ô€Ô€Ô€Ø€Õ€Ø€Ù€Õ€Ú€Ö€Ö€Ò€Ù€Ô6)!/&.-8 (./ +2&/0570:&9---/*,810.-+,,/"+22%<9'-%+-(*%+;$(++-*1&+&!!%("/**!(*&+-&7-1, %,-1,%,($2-54<753130.9-'*1)%()/6<0&/>1738+".()8()/.-.,$1&0!"./-,-,+,60*0'5#-%(*-.9*0&.&-%!2/3(/1"/9%/.+*'-3A00-03//2';'.2#1"+0:$0.1*+-2-%07/'.1"3%4/5.3( %+%) (2(8(/$'%'#,48)783,,&)67%&.&#-#&13+-&+.-),+8)1):02'31*'!%.;)*72),5>(7,:-!%.#0/'91'4 .&75/(+4.)0.+=04) /'#/2%02"/ ++$4&+'0#%2/'078&++-(.4< 58,+,./&30),738"5(%)1#.!%060,&6--&+%'9$*2/ *,7,8,5(/6'/3.**1"36*''1-/,.11"*2*)+'*$5&.%4**90,/6, '.:=+46€Ô€Ñ€Ö€Ó€Ù€Ó€Ò€Ð€Ó€¾€Ñ€Ù€Ù€×€Õ€Õ€Ò€Õ€Ú€Ñ€Ú€Ú€Ø€Ö<#');)-&='0/;/"-)-+)356&40+245,(4'!.4+*+.-7=14/?.12%./4'-4),6*'.)20'+9-(&.&>1*3,&*3)-7'9"!3","+!'+6.& 32+51+,'-<#);3"&3+01.!*"7%&((,3*/%(21&7((-23/22'&)4(,++)''2!(3/17%(%.0)%0+%21,*5>,&/*13))/"++*!'2(1/'/+&2+/.!)'0/021%1*-2/$5,(+"()2(#"-4&&3 * #/+'-3./,/3):*-1 )4(.8',.-*/$*(0&25)$++*-653;+%/%,,2/5-11-7"4512,*'**0+,44/-.,613,.?1!;78''+-3$12,;2 /''1-*) %17*3%225,)-&!1!&%,**(+%#/74)1$,-&.,0!,*,!)'-,'0630&$+2')2,&6/'**')/1*!,)20/0620'.-#<-443$0#.)#%0!($*'#.5 .9-3%,/+)22/ 967%-/+(441%-3.14,(1€Ò€Ô€Ô€Û€Ñ€Õ€×€×€Ó€È€Ù€×€Ô€Ô€Ú€Ú€Ö€Ø€Ò€Õ€Õ€Ó€Ó€Ø6#(=!82(+=-A4!+-*/,+)9,/3'1+-5!'/)).*-9(7(.)A.,$(+2..>2(6803&*-(0,.+/..)7.%(1-")'(.41&:0/3$$,.1.5#)/431(46*.#*')"*"!5".--(054'$'53+<14,$-.5->2,+)'/'#',0+%.3)-*3++#7423%5/',-519-!(03'+134#%6+)',:/++))@6&%,%7+*1'*55).3/. &+/1++1/96"9.&/.-:(=#,(%/0,-$,$4-&5)!7)0)+'0$%0#/*'!>)0*0.,.+022.?/##5:0+3+0+)#'/(&2*+/0*)(!&'+4((*%01++(/('/.2304-,!(2/040/(+&1'0''=0%((0)$$%&+1;.,&5-"*&"2)-5-1+<'#B1/1-@/+$#C';%'26&)-33-2&&,214(0760*.+*!*/5)'56)&*,61(+&%)B'-)32,7,-/"50&',#5#*59-65$@/5(/)(-#180,1*+/ +5(4€Ï€Ü€Ô€×€Ù€Ù€×€Ø€Ö€Ä€×€Ö€Ó€Ø€Ó€Õ€Ü€Ô€Û€Õ€Ó€Ô€Ó€Ò+%(14&6/&*8./(5+*5)#&7+>: :('-,1/%62)7$-0026320(5!,,;*5!4'%*(27-0)*9'4+9);2=C+'4(/13) %0.)+.&05:*4%.7#+8,2->1+-.,60='/'2/-4'+8,#5'5/ --8.'(6*1%*-(&2/)%/-.-23$(/-3+/5<'6$7"5-68!C$ ,&.?%((!&<.'-(#+5%0*+%3263-4'6 %'5('295)(01,/,#9)''<-)+,+*/&.'%'*6(+./6-/,0)*50-25.*,-.*,/0.26',13(-88+/.)/+2% #..-/-0#&+3&&)!*:.5,(/-,($+2((/,)%) &%)/2-+-:"*)),3' 8#%1'/1657#$$32$1*3*.))6&+$-'"//$+,9&'*(-:*).4)*- *#,)(%+21.2(<0*343&/2,9#')-F/,%"6)1-+/,-+7",* -+-)'-'+'70;7936%%0$,+$7$""33/0/-+/:9),/7&<,€Ô€×€×€Ý€Õ€×€Ö€×€Õ€Ç€Ü€Ù€Ú€Û€Ñ€Ô€Ø€Ï€Ù€Ø€Ö€Ø€×€Ó:#8'(&.,-6(+1+1+'9(65!8#12./$+/4+(.72=4,0(32( "+/1.)"40(0#!3,,$1)0)9B1172%'' .*''$6&2#,%(!9-($).5/$,%8(-*.'%1&+245*")-/,!,./(*0%(/+$ '(#&:-*$10% -.&."((:0%,*("(#'$8)>3!6.($5#/6'&((,##%0. ,7&.$0(1*5,'6+)'1%*-,)"93)%#/&.;+-2&'./*%'*3/2--- $;(8"%),*9((10-(%)63>'4&D2*#'"3//2/4/'$-;%.,*$//")+&)*)00/'#+.%/#.0,242,.87 /!<19<$7*).2%),'; )/!+3 %-*:%//!4,1/!0"+'"4&3#!01)/0*)+"'*;6)&)1&&**#")8/-6077!%-.-%3(#40(/"9/%.0-,/5"6 10-/'*)*/7), $2$-44*&:.8&+()6+(-'")- -$#*4)-,55-/.+3/&,--#5(4-97€Ø€Õ€Õ€×€Ø€Õ€Ö€Ø€Ô€Æ€Ö€Ú€Ù€Ö€×€Ø€Ò€Õ€Õ€×€Ò€Ø€Ó€Ù6<5-#+!7+7337-0-$ 1#-4),3/(-(3-<)#..+2'2/$(%)4=/23*(#2)4&%+&#://3#-14)53)(*.-"$1"/:*/6+500)33&%#81/281/*0).!7.9)**$+-35.14%0&%)9'(.+.2/)*)#11-./,,#)8+11(8.&/**6'(5*.1 '/)..-+$#-'"!//-5(%(59/+-58-12=0&);"/(6-54'0,-/&04*)5*"0"+&#+3!,0$8('%66(8!.)6(&+ *),321%&0(4.'%,*62'(,)40+2.)1,/)-666%02-6.1$),0)"+,&),/$97""-/))21#4-*2/4(0#($'39(7)0+41$2/-7.#-01!*7/6-'+(2$/1*$-2/>/+-.,%.3('-*+"4'#/.03$-,+/)1+03+(, 0+273-2'%,':3-!84$:*#*2.++/%+/$-.4()%1*(*(.)*))908+*01,.41(!6,/1,,)05+++0%81"(4*7€Ú€Ú€×€Ö€Û€×€Ò€Ô€Ù€Ç€Ò€Û€Ó€Ö€Ú€Ú€Ö€Ò€Ö€Ó€Ö€Ô€Õ€Ó9,430/5$-0&/-%+&0#37-*'/,%51#4=3>'/..$./3$0(120(+,--/"/+"('7*-*"%2 &4*$ "1*,.)3&328(6**#*1323)(,''*'%(+**)++/&(+&-3)<9+$7/ 4*(*,$(&-)98.)'/*3($) &9/5/6(''0) *(0-0-+(%11.-"9+/0*;,,#-*9€×€Ó€Ó€×€Ø€Ò€Ø€Ô€Õ€Ä€Ù€Ú€Õ€Ò€Ø€Õ€Û€Ó€Ó€Ø€Ö€Õ€Ð€Ù)-/),'.0&-,*3)(/8$.34.2A)/")%7$,8+/0.+%/*$211#-2)$#34+$5&0/1, ,++56+.'035,.-/9$,"01/%;-#..') /".,&,'#,'&+,0!/;,.44,3,,,96!()7;/*.012*$0.'-01=--'62-,&->.2&8/+(0("/3-53**)#+3',&*+!!#!-'82()-)70.29*)%2&+'5. +3./8)-*-',9+3,;0')'%+1.2%!%1,(,."))&+20'-+7&" *1%2))2',,-$"#'1-*,%-*(=+()61+)--/0#7203:.*8('$6+-) *.2*)-0,77'7:*+!4.6!/)<322,%/&87&1*.29)$-,!)/7#-$*:5-5!4.*'('%-/%(.+0/.),,(6#1 >046!&1"4 /(/2&#/?*'1+ 6-3$+2"!--26+%3'"40 &,5+!1,)4,-6.(+!-,'*17)/*5,28+43/(6/%9/=,+75%(-,2+41$*(4!/,)@9/&4€Ð€Ø€Õ€Ö€Ø€Ð€×€Ö€Ú€Å€Õ€Ù€Ö€Ù€Ô€Õ€Ô€Ù€Ø€Ô€Ù€Ú€Ó€Ù52*1*=2*--*/%+9*06'7+8924,#'7&1%=3#750'*4)$=!0+.)/!&1*)#-&6,!9! $,/20$1'2".&*, 7'((4& #,(//#.&"./$/($55227*/1;'$%%8'6/+(/, *,/#5%*-)&5-$-!,27',(%&85/02,)()#:(%497&1#3%+-11-.4#)3-(3&3'3+-4$$,/#6-"32-%.-'&(+#+1&5+01'05%,+.0!%54$2/%.6<0% /&,-%-2:%#2%1-,*.2-,-$2,!"*((,,?-.(6=()"*2/+-*2.%#*4'$"((("%"(#).))06-!2#(%611(%*&.)*&*-2+5)1&+4.1/3,*.(!7!/2)-3#&,0'/121#'(/5'!3)'9-)#<$4&"39;+/'0,/%!$62'--27-.1&33',3%'(20%/5!,+-*)G)5--#)6*'-3$:,<*)(%-3?4/0**,'3-2-%-%4.355&#%.<)6$(*15*+/.1'-*6+6<,)6'28**€Ö€Ù€Ö€Ò€Ø€Ó€×€Ø€Ð€Æ€×€Ó€Ø€Ø€Ñ€Û€Ö€×€Ô€Ò€Ø€Ú€×€Ú-//./,(/+(014119'3,0527 4*@+%+"=0,0* %&2,*+#:1,,((5$+/10(4))(.'+1#.(&*/$"-5;#610 -1**-.(/ 2.20/'6+(./)1.(&.*1)9'+33#5%%(0)',.$%2('*1,0*/##3)1)(2'%:-'/3/"%-,%3+*&&,,(32/&.#,#/?05 65,;*.(/"&+%-5/%2$1&$$33+)))+(,04%$/3#.31033*&1,4+&!)21&)3"' ../%,.+&+'.0-9&*0%1+"3)65)/,+%1+2#4-+)#-)/#,74'*020.+24*341'/($#$) 4'1(&- ,$%28$++"'23.*"21,$"2++(2%),3(%+/"+%0-7,&0+%.*8,;+)*1')(%,&:*%,'"*),3,$%31-3.+006/3*".*-'+&+25&,(/,25%,,+"&2!""4(%+55//"0)%$(0 .0'*))$07%(/63=+(+18-/59#+$-+5#&+-0-6("&(-=$€Ó€×€Õ€Ô€Û€Ú€Ù€Ô€×€Å€Ù€×€Û€Ù€Û€Ô€Ø€Û€Ö€Ú€Ñ€×€Ö€Õ:28=!!;/*20&61%414(8 (*8=(%0*-921249)$0= (+!(%8#+&2+*""6&'#+(03(/ %5&!1//.)/"'&+&219//2*%%&$';!78%/<2'*,%&+.)),&")/$#-$$2.)/4'&)7)836(''(-&++*,)%(2&5*0'+9'*+#3(4((-,$'--*+*&'"6,!-,%+0"3/-3 -,33-::*53-&#(1%%+'+*&+4/,*'. '/(&+)+1%%95(*!-!"8/ --203"&(.)#%;.714:;2'$-4F1.+=(.2;-*012*+*, 3-9!4))=#&5*01'-4 )'/-*0&'+*.+*"01413"/&*+-:+&;0,%%/-+5*'/-25& /3;$%$301/3!0*2(*'55-2**(6/6215*'$*290''3$)."3(+((/ /0:*+?$$)=)<-+%,3'-/)2+0-#6+&'#$"%7,".:0*/-,%6*#)/*00%.*/,/(-.4"177%():!/,'//231/&,2+//:$/)7+&€Ó€Ø€Ù€Ð€Ò€Ô€Ö€Ó€Õ€Ç€Ó€Õ€Ø€Õ€Ø€Ð€Õ€Ö€Ó€Ó€Ü€Ð€Õ€Ò/5-5351,.92*)+,%0-:.%3,1(#)1.(/.'"/()/$*3),1.1$-*:-+10-8,)-1%1023.+/-0*./<,",)'%./20./22+'1//) )&874:-2(1#3,'1-#*"*#+-!!'.&1215-2:(2$+ 7'.*1*/'&0$7,*!.);84"""-*(11-2-1.'1!"48'0! 64*)1#3/$5-(#!$-%92,%(0 ($.!"0'+'.%4+))',,-*!0)'13/342+9):',21$/63!&7$'-'1,4177+2/$:+)-2/'0;"5++5%%'#$/")(&%,);-3 )%3-*%.%-%"#4)*)*-(%)',-'2?)+&2%/-,,390&!1!&)((187. 10*/;6(00(%$-5+/.00)"-,+/@,)("+3.'(3%,9$*//<$*+."3++3!0*'-0&.8+13-''1%!' %-'7/2.5"0*"/+5%%-&*'2+5-5-210-6@")5*7&3.8+7+'7'5&7+$9-).6'( +@2-'+€Û€Ó€Ï€×€×€Ó€Ù€×€Ù€Å€Ñ€Ø€×€Õ€Ò€Ø€Ô€×€Õ€Õ€Ö€Õ€Ü€Ó8*5/%52'*-41%0!-+2&!3++'$4//#)4).+7&*+31*1'0*+1&/#043//)0 5&/-/*/81(/2""7&4'703*41*0.))/!).(//#)24((/10 3((0<++:.>1,3+7'81"+4(''3%0/ 0,',)"+&3$$#6*(9!""(.&"0$/'-////&/2')/5%51!&"-#5&.&"&2$:7&"&+&+()7""2*"(+''068$"1<*#2/.+>0)-)()!+0)%+1)3)+%.(43/$)2#.&6)8'-'91).*/()"2''-*4&/+,"3+$)).)70)"0%29%84(.(/+'+45,)(,(1&+/*:**)7.7+)+4+34/3,1+,46#'&.*) 31?)-)./3%2.+,'.3$+%05)/.(,'14(4+/-6%:.175'/0.!4*/7/+)(2%2)('<9-+*+/*%9)-%82,2&+.*!++5 "&8&>/'9)5/"'/&),8*/#'-81:+/$/)1"-71/'--#+'+-*.(14212%14):&/+)2€Ö€×€Õ€Ü€Ö€Õ€Õ€Ø€Ñ€Ã€Ö€Ó€×€Ó€×€Õ€×€×€Û€Ø€Ô€Ü€Ö€×,7#4-1)-?"80/$!.<27#(/!0%+)-88=1#54-33$;8./;4+0'--3011232"#$-1&76/,..853!%%-75( /+6 ,/1$.5*//"-586+.0"2"5%16#)0#,-!910'-20*$3%+,)/2+&&4+5/&3,,'4=1!"/&294++(*/!.'0"+"/4(17'3$)"(6(.!$$&2-)%-.$+)(04)*10*5+!#&-'&.$%/.&1205(78*2..5-2+%1&"'9'+53(/;',..-6/+3&8"7',$(0*-%76(,-651//1+/--)-%2201*4-+/4/(+&8'%0,)2/-',404.$29,':8)/*-%.4(,+3(1+#'$*&$:/+"%1.$+34(/70/1%*)#6"101206"2.:=$-'+'47+*30708%5-&!+"2)&)3%-25)/&!).1++4-& 5"4*B+/;35+'2)/(1()?*((/,6'.7>87)18*/2*1/03# $/*.4,041.9+,3)110-+7&*6.5700$&€Ú€×€Õ€Ó€Õ€Õ€Ò€Ü€Ó€È€Ö€Ø€Ù€Õ€×€Ö€Õ€Ù€×€Ò€Ò€Û€Ô€Õ.+56#+/0,!-./!'6-%0'/9!7&2-'1/0%/7)/+ 1%1#-'4.+.)4/!6/8$.3 ,;/(#'3/-*510(1,*(.%6046*:)&5,)+++8(6");*4-/*)&+#.2$/,0*2((&-6#5)65*!#+-+"#!6(,%'.40@,1%+)3/-:.(&%0%.;.+%%/&$&,0)>).33+*,-.#*(&"(+(4+0"(!(**-5;*0#*,) 4-'.0%#%"19-*+($22*5(:)(*)03)5)##$1.+2-,;%."-0)52'2$6*&>!=0;%7)),6&,/3+11(02")"&6/9&%-.-(25,8&3/+-53*+'8+).)4*(11:2.(&'5*28 .&&'$-4.*$ <'*+'/A8+/#"1)'2572*'+-.7054'*241/"=21,+30'8!**'%*.+57/.1!,)&):-/%/(*(-*7"#9+/+5!--,7./29&0*5%4#%2/)*-)/+-(3/86>5.6-)42;0*$0 3:*-/&'/)2+3267€Ü€Ö€Ú€Ó€Ø€×€Ù€×€Ó€Ç€Ö€Ù€Û€Ô€Ù€×€Ó€Ó€Ò€×€Û€Ù€×€Ù!6(/#,8':/(+(5).&/>53+*B?&,0*!8&-,.*%-$3*&'17/.+))$,,"*#-&3(-#2&22()/1180#(.)2 /0--..'%((C7-#+5-?)1')9 %*+*22$/+2;.3"$!-*+0*5$(7,$:4/',B2*7'2)))54)" ,1.%$$'7/22-(!,5+3 4+2/*7213)'0++ (+-''(.,(01'''17)+./%*&/&/)+9)5-.+."$%5.9&-=-0/-)*./**1#(1("D1#-.1*6#3.0$-3.++)"6 $1#-*-4A(5.-$%1/-*&+4''+-8*(8.+/6"6!:%44&2"3($:3 *,+(2/-*(#-&&&.-.(6*'6)0-"-(15%%4'3-!0,-))3-(,!683$%3$)$#=+1%&7285(3'%++00?+*.)5.,'//6'((0,&(./,06-5-96=)0.%*2)311.$-,),),'/+,0%+!3%0+,!%)2%4=,//73344"0,4//,+/%''#7*&6& $);2€Õ€Ø€Ú€Ö€×€Ô€Ú€Ö€Ù€Ê€Ó€Õ€Ø€Ø€×€Ö€Ó€Ð€×€Ø€Õ€×€Ô€×8'7,*70*5)253,+0+A0-,*:+$-!5'&7:-,%75'+/.%//)26($)0 $5/3'4#!?3-&+-+..2 1(/%3.30+052-"2#&05/(&,2+*$*./+'),.((,)63//43%..7-(350 ,',$!+1,,-+6*,,$4*+1-3.('#12#0403/!2#(,)-'+-).'4% */$&(()2.'"0-!'%&-1)+)7+.'"+)-!4#.+0#$)4$+'/()16?%+*03'4*.)+*008%/--,,7+19'20-0:$/-+(.1!.2$+8+7#!&-2.;%2'3 #(8*--+-1*//0*-+2;-"!246+0-12$ 1,3'4+'0*./03%1$10(09$-1)-3147()-.0/",&2*,(<1)0%25.83.)C622+133!/-3(--0(/3.2&3&,&'+)&8-,972(71)60)&*40(06)*."+5'8'"$%+++20./ *++%2/%+),&%)-(,3 -,.(4,*+24%#0(-03#,0/0!1%",!0 /8),!*&+9,$''60''9+3--1&?,0-&4/0$/'.=.&5)+/(-!8.)+ -;#&*,1,-+"&05))+/8(#416?(4 713&!2"')'&#+"+A3/-*15/81/*#*:'++/"(.+3,,8*.)',-)&)'/,, +84/13('-0/11,4&2+93#2//(,'?/-6%&0'06=+714,%.-.3+6:(2,%':5'5(##5)+0(,020,4")$)1+/+,.(5(/€Ï€Ø€Ö€Ð€Ý€Õ€Ò€Ö€Ó€Ç€Ô€Ó€×€Ò€Õ€Ù€×€Ó€Ö€Ù€Ö€Ü€Ö€Û1<-3=$+15323/.62&)3-/)2>8-!.1*#5(933:2.!1*32"(/(:2".%(!)(+'.-7<)(.511,424%).#3+*%/;5",!)&%//'!%'/+5845$(&0&))9(9+70+'('#) /,'3&25/%,))5/*."/-'4(!/72<%/-&#)17*++&/,&",83/,,%%+4&,)3*0/!4./..(1!#)-3*-'(("*+(%+.0-1,%%+7/(3(-.5"!3#%%0*33+5()(50*%9)8 /(''*.:05(C/1/+,#721,((+*0'/1*.(%+0'-<24%*1*2(70-/$4')20**'/'2/)(05',(7 4"04+5+"(1#$'#'(+<.0,4-%++60*#3,+* (8%$*,%-03(/(*+/8%5&+''/'*+0.'12)+/%',"=*7*+,2)"%2*19&0&)&)/-*9$-+*%+ *64#1(23,+./).%/,<#7(&',".'):-$-!-/27$- -*"3-54%+7%#00.,')1)+(,39//-85)€Õ€Ó€Ö€Ò€×€Ù€Ø€Õ€Õ€Æ€Ô€Ó€Õ€Û€Ô€Ò€Ó€Õ€Ø€Ú€×€Ù€Ó€Ö1'#,-*,+/20.)-59)/%%,%5(!0'/(%3)(=$17459/18-(/8;)&2(+#6&58.0&+4(*$(-/.1&. +'34*!&7(*"5+.&+3$)$+#$2+.- #45%44'++$'+$/70-%(2<+,&&,:)/00!1/(5))*4"0.-,11115/2,++*.!/9+!<-*.(&,'1.27++)0),/)*093,4$-($-1,1&&&*75./'&+&.'++'5"$0:.0),''/12(+2+,/5&1%$,/+()0+/1414,"45 "4,),.1%'-*/"03(,+$1+(:"&##'7)93:+632$$0:.6%%16#9(2' &323"--&3,4/.1'6)#',%2'1.(39,"(32)')- 62-:#+(0)7910-*'/!=+E)14.-,)0:%+2*5,%.+$.*%10'$4/)('.-.&&-*5'"=*)6+!6*''6)*4<(,/$)/,0)+71.,#-2,&%$$19))/$.#-;.(;06/-#3*0!*)(1 +0*:-( 0 /€Ø€Ú€Ó€Õ€Ö€Ø€Õ€Û€Ù€É€Ó€×€Õ€Ó€Ö€Ñ€Ô€Ò€Ù€Ù€Ö€Ø€×€Ý02+,.6/!'65.)%:++0./0'34.-(4.83/'*/522/$+%*+'4+21-+% 1$7%32)(&'70&(63"%-*-)"$3,:(4*2#(-7/!#*.-".0,1* *!+)$32*.(41-/%10/#,&+)+)/,+.#*/8!:/1'.4%/5+*(*4"%*2$-*%,&/%*;*6+#)((&7@013!1/!,66"4<'&*(40'*+0%/"2&%*8,2')%5-89+A"+*/)/7&129&5- &%80,D,032-'(+'2%37"/+518./25.30;%!(*)*016.&::4/03@:*/.'//+21&0+'+--,1#.!.(6+6%-.'(=*&,2/%1)!&&4@41$))&/3120*%'/*(6(0108',/)''++/'">/.;1%2(*0%*.+5+''%'!&)!)88/-$2"-#.''$6.2"-)!/&-"6%4,1) 2&%0*0/.;*-2'51.-*'))0/(+'3"+'7$,.1)00'23)-(!2.)'(31(;6."8%2!&)'2!29)(49)€Ó€Ø€Ö€Ù€Õ€Ö€×€Ø€Ö€É€Ô€Ø€Ó€Ù€Ù€Ü€Ù€Ö€Õ€Ù€Ù€Ó€Õ€Ò>?)00)2 6-+=37+&&//(#:+( 1-,$/#-)1.'$>7$5../0*.#%8,)1##1*20,'#/0+302(66-.6*!..-&,%):.-"/-0.('$(4#0-+*+(=91$ )6=0+4'5,3%.#2((%'"%+'$'.6.5!+///+'4%))(+,1-$8/"3*.)'#)%2'',$/-+ 3, -!1.("".9++-"!!>!.)5/%&%)'/$7-1&31(,)2)-&&0,2+,)4++-)++2,)2.23/#-/+0-$#C,31*08/(1.*-/./+)+.55//(1/:.'( ( ;3'41(.,2%8*/'/9((((+),)%+**/7*/),*,5/'&,*43009;(&-0$0+/%#4&/-. ./-(7+2,&+6)2 0")),-/-/1(2&"%+"'%,%,9"9<+(.+2,1&,(+%*2,./4+)*3+-6/#3,2.$$!-5.3,2+,)71,+/&403$(&$",$-?%)!+#'1$31 +#6&$/"*)(7-<'!/#20/2+-,/3)<3)2€Ö€Õ€Û€Ò€×€Ó€Ó€Ó€Ò€Â€Ô€Ô€Î€×€Ò€Ô€Ò€Ý€Ö€Ô€Ñ€Ø€Ø€Ö2.-(9#%/#+7*/.8/ /$"16''/019+08(##@6%'27'1,//2)1&(&3(&3'5#$64.4=)(-;#)*2.%&3.6-&7/,-8#,1.$*2* 0" "5?,,<--(/'7-+)(=0-3-)')0 -'1B:*'1+))$02+484,,",(1!,#62-<(/*0/)$163./(&)4,62.3&*%)(/2':36$4++",-/)?G).562',1/+*#=03/'+/'*(3/23(:%-210*6%,,*):)B.//0--/83$(%&04*%0&%(,++("%046)&+*/3'+*&$.2%,-:+ ;+%7#-'0"#-1*(-.:.,.3/).&3'3)&+7'($$'51C%1&6%'*,'9,7',.203*/)+"7'(/-3$25"2+'("2'2)$.!&(+/+&1,+.,+!* (',-(**7/ &,'(+'(-.+'11'),6!.+#,!4/&)"*9/1/'(,"#7*/%$1./%"1/./$97%&?='3#/392 '2<"%000&75#(+3&6#2,(*7.9-"&-)54),*+(511@-+&".*2-)4(6.//-&57,3+2,+0.,1+0(7*+45'()2+&)1'44: +, ,/52,' /;-//1:./)77,-3)*;')+4)4.2%/+*0+1)+-)-'*./-265.3-,0-5+$$*6)# 6#(.#(6$'3'&+1&+%-:60$9*$(&4-9',%((1-/*'4/9+.%(.%%0,"54$%-.)(0'7,,/*41(4*%.#+):9,6320*.7.8/+.8%%:2++'.#&,+4(:*5*371/2)1.7-%93(/%0$--+2%#/'&4,!.#,%%-!+72.,)2$(0)292('(.'%32!7)221*223.7)-1%."1 .,,")-)/.%3(1&-1./%(*&/-&.90,)7-!$1%4)/,00(8*)#/"4,-45/%.(6#(,,++"/4,-()-/0*&,"-1'2*'/C.10(17)567.-€Ò€Õ€Ù€Õ€Ö€Ù€Ò€Ø€Õ€Å€Ò€Ñ€Ô€Ö€Ù€Ö€Ø€×€Ú€Ò€Ô€Ô€Ò€×-'*#0(=*00%3)-A*43"54721+21.-4+6'8-*,//!3,-''5/-3 $$,$7*0<10..>",-.'/++/6)*,"+*/1,18*.02&3,2/*%)%(A!(+*3$-85=0.)%1%'#24)5//!3-9)(-*/*/3'+*2!?/7*&,/11*.'+$4!654555.5(+'14).$$8)&0:*38%5,.3,&:-,,/)&&(!$*97*/-$38.8$%$.5&+/*$),/&/4*2:3-*&+'"322-0.)-4-1*4/#,;$,7 -!4*,*6!.).&$6%.!6***)1/0**)/%03)5%-%(-74/#+.+%'.21,.4/!('8$0/-10!/00>4./"/$.'?1/,&;!+'3",/'/6/20./'%2(5+&$(+ ),)!4."5$,,,$.71%4+,6%17*#//'&+*')+)!$,)1(+/#.-9-1+0)'%- 7(#.2(3*)&!$.%//,&+1*4(#3(&;'5#!*80(54-+..0+%(--8@*051>01%,.€×€×€Û€Ñ€Õ€Ñ€Û€Ô€×€Å€×€Ò€Ü€Õ€Ð€Ô€Ö€Ð€Ø€Ø€Õ€Ö€Õ€Ö/+9)/&&<)-&330>(-7 '+#,('/3(:&1.7)/#))/!(2%1'$.+*47(1(9/)&$- (-;#5'35.51&-&+*/>*4&307/@0.(2*+6)(1'82%,"(1#%-*))62#0(2>9(.8--"&/+(,/74+3++(*.0#.>1&79%)730;#/,2%$#+&+$#*9$7,,*,-4+*6.3).(0¢¢#72'%%-,*'.?'6;1*4'&,.6*.+41&,+%0+%(%+/31-8:&$3#207-0+#*,(6%$$+56"-2?+/*6(("$?48:72%-&*!3<5"-3/,9$/20'8)+(',0*3+>$,0#)11%!243<2-:.*,9/.-%-"$2-043$2336!-#"+$*30-*0-- 1.&*#/-)((+4$&)=1/-& 2$4'+&)-""5*,$7/"*!:3/-)59!-2*2,/'00)$=*'+&'.(.,('2:70.)#//92.'$/+.('&:33+0&-/:+2(/','0=5&&82+)0,15.#//.//2:0+0,++/&0.€Ø€Ú€Ú€Ú€×€Ù€Ö€Ù€Ó€Æ€Ñ€Ø€Ô€Ý€Ú€Ô€Õ€Ù€Û€Ù€Ù€Ø€Ú€Ö(:,%-&+2%%7//2+15&5+12+&!*%7/2-*)#)4.*-*$823"7/%))!-2%4/"-030"''60,++%"5(,'57$#) (22/%)-0,,5).4%%/&/)'-'*(1'3/*5+-1/+$'-%#/(%,1'%'#79.1-;,+*$)+?&'.)+.)*+/;)1*+)!-*4"62#.&(#%5$-/-)*82*/:2‚z‚ *#:.)=#%)&$%*$(*&(0(.#'/126*,9/)1),)/,*,//<)',.'% +27+-4#1.*(+;+5$'+//*4+2)/-+>*/+-#14,0,$0-2.-04-(/,+.'*.4910!5'#*1)'8:4%,)1),').1*%!+-0113,'3(;$,* /$2./+4-',+.3 3+0,+5+44+@*1,(<#/#+&/420/59'- .%603-0'+'2:*$6/.*20+75#.29*7(%-%+(21$;.().*.$4,.01(A%/-"-),-/-"-*/2.(2%(+,,'&")-0+ .*0/3+,0B/-€Ü€Û€Ò€×€Ú€Ò€Ö€Õ€×€Ê€Ø€×€Ô€Ø€Ø€Ô€Ö€×€Ò€Ú€Ð€Ó€Ø€Õ-)4 7&-02$&.'',-2&/')5,,9++171@+.-'3228-/+,+',43,&7*,!*/1758.0*5.)22-(0%/5*!/) +082%+*.1-':.&,0$.%66,3-+7;3,*0/./*!/#"*8+,+;2)25,1!(. !*%*/01-3$/&/(/!/7A*4*+(($42,7$**% +7)1%# *4,%1+--*.%'+,3"&/0&+5(1)1/''(*0+35/ ..++6+,--%,88*/,,)<3$5#)/1)-,(/1-0",12+1,('*(5*.+'3.,0@<#)+0)3%33)20/.%&2073*!'&)*)(-&''*-+3 3+).-@..&.%'&(44*)5+--+)5!+)-1(*"3&*.4".*1.)6"4%25*%*#(*'8.%321&))"-5'42(410%$)*3#*%*&$5(&&3*&4'++2.# %=)&1)*;%&27+2+/&2://%&-%6*+))-%:(%!2'$8;52"('.3(/"+1))9+)0$2(*5+.+'0..!1#'7,,2&/3+€Ò€Ó€Ø€Ô€Ö€Ô€Ø€Ó€Ø€É€Ñ€Ö€Ò€×€Ò€×€Ù€×€Ý€Ù€Õ€Ø€Ö€Ó2&7(43&2/*-1041''=32)26+"4.///./0:/-,$/5,/7$3((,0'/13'63/$#%$%1&5!)#2/(4&/!01%0&%","=-&0-&!(1!##'$),20.36')*$ 3/5*406,3-8#-."*/*#*,'0%. 73%-&!+91/%"*(24+,02#&/$*&,%6'.'4-+0-'/+2/.'$+24*//; $3"&)0,#-..7)/)*9&"0%;1=?+3(!15,-) 7(&4&/$*2$!/52/(&*0+0.2)&0:4**#02$',0*203$%!+,9/%771/")'),+%(*&%*$'#&8*&3141$+-'))/3%(('0-)"154&3')+1/22/%)/)22(2!'=.)1'/*/21-/59#+05'%5--1).4,0+'7,*4!&,/&.-!,/)&"2-411.%9$#+".+'7').561+5*-1,&)?/7-+-#"&4#.*;!10*#32&!58/+-!90("*+08.33#:/>7 3)+/9%0/,)*6,+))/+'$3:$48/# <3!,3150%5;1&,68+#54.2-4$0(+)=)/))&-2-/*$/-.'/,/5-36+,24&.-'/,41*;6,'+2'*+6./&4)&(&)*1/548.)'3&2'**98396(((&.+',744)!"6+220 &-./*-0!$)<,"'/-0>/1*,%1%')5&.$ &0"++7)$%5)#%#%/,7#:1+*2,..*3,&30%1/!(*)2#-1#%$,+.)).8?02 3$,+,*.4)&$23&,**%"&50%)4.40 -&4++41%"-3$*+/./%.8,0+2$(6-3-,,:$2*:('+509-(31(5-1/*4#$4"4&$) $!3,*0%-(3)/*'&*,4+,+432-,90%,;;00-05-<(+8-+4''2&%.5-%2,/)%5&4-(3)4+*+.$1)*)1-2/17/"6( )&/25!,-,:,*/!5)+3! '.5$++*,"14//63))7**2%73%*-1,'-/,+2.€Ù€Ú€Õ€Ô€Õ€Ó€Ô€×€Ó€Ç€Ô€×€Ù€Ñ€Û€Õ€Õ€Ö€×€Þ€Ö€×€×€Ú+318+(08)"+$)*"/'0$/.#!+7/.* -3%)"95-"')%-- 7!.02*"+&51+;+(+5=.1.&-*6+,-$9%6(.7/5(.521*+ +0/)+%),%*#.&0,?)7+6<,7%2+0)"1&())!/!5+1'&'(0"4+,3+/.#.%/0+)0/)&,+ ./"2(<&(7/,(').3)(&-'">485))4('4=)"+3$0%%.%.0!%.&03 6'&$$3-(!%6))*"5) 6**/$530,*(5,&+13%+/./;+43%/"+/+-/%,6.03'-12"-")9%!&(/$(,()()1,1,,$,5*0:3/90"0, /"16?'1>.*+,%3*35%1#.$3)*&07%&#+02-'%E,21&)%!*-92(;/( !$0.'//21,+*06"8 ,33*'7')0+3, +')#-45$1,)'+9(3 )+3.//04*/((+.#(5)8-:./)-/!/+/.(/-+-)(,$11:4,,*+%-A,/"50-&///8/:86.$**.1-6621#0-"€Ò€×€Õ€Ó€×€×€Ô€Ö€Ó€Ä€Ò€Õ€Ö€×€Ò€Ø€Ø€Õ€Ñ€×€Ú€Õ€Ö€×,)0'73++&#-,+1*%5://;266++7,),*1/)1#''-*'*#:'.,<4'02."3"$4-C*+3@(# &#:3(&+&3(4'$*91=3/0*+-14(+!%!2.-3(*,88,)<4-./*1-():%%+!1+9'(9&#/-#7;-)(.2.102-5-'))()13/*!/',+(#-,3**"/--,/--3<&11+0&4)6)# (#.+0.!/&)0,%!*'4,!(&.*/003 . .5*611,+6-&&/.-)4#* #$#49*-05:+''0+5/443*%.#!+3,5&%/'*B:13&)<. .50-!#86,0/2&&433",7%)51*3(/,-;1+%)4,%/+/(9,:2/&$*/'!2%0%%&/51+-40--42%(1!'+9="-2>%+0!2"-";(3"#1,(-&5+)7-5!3',0,+-'(4 &73062:44)/=//25#3("*--/$3'&+,,* 40)3/:7/'%*)2)1--.(*52#'8>1-*0+$).+%91/-,3)7-2884$'-€Ú€Ù€Ó€Ö€×€Ñ€Ø€Ø€Ö€Í€Ø€Ù€Õ€Ñ€Ô€×€Ù€Ô€×€Ò€×€Ü€Ú€Ø2(%3':6)-,1)4$(-3,2)4.&&4(4-%*7.1-0.3'-+,30/'44.+6&)3#0(6+&2773+)&*,;)#-0'*)/-1,47&&4* /)$.$'*1/7(#+$(5118-!+.3!+,&(".# .'*%()2&$95!',+$-'*3>,86#&1+.'!2(%.!$"-,+')&','"(!"++!',%''-##(#1/*0,,'*7 .'$D0*')2'5(+542":6. ,'/0,A#*$/,,,#8#30.&3017/& ##39+.'39(4+)/13+&'--08--5,-(,-''00(7/.*/22",-,9&!$,+,,1/')0$1+=.0'0+#!.$$-).4<"5/+'#3".,32.*'7,0*'%4 2*(9&*-456'%.,+'-)7#0+"3(++- ..9(.2%5%?2/-#0(*)3%- !&"-;)62&3+*)."%2;':3,5/2(.3#.7-"6)-.2.2/(2/.3,"-<03.'+-'6#,'5+"(2:(-3+.'+/'52&..,7/923-*2:1*%& €Û€Ö€Õ€×€Ø€Õ€Ó€Ò€Ô€È€Ú€Ñ€Ø€Õ€Õ€×€Ó€Õ€Ø€Õ€×€Ö€Ô€×4"/).)5+/.,-(7*!+23.3,&2%37.6"'+2-'36211/' 0-$02/5&&'-,+/1+$($/9"-',,)%.,/,7,43*=/)/"64.&8-)-%4%(0:.2%0"*362&4+#;*2+26%(+0",)/3)*3*<*5(%26(:'A,+34/"#3;11/+,'../(,4/0*( +(2,/)*&&,!*()...'+ ))/*8, *.*).+'%&21;)+/*&1/41'5',69)7#4(/(*('"$315:0"*%31*243(#2&/!)#//)'.-5 -$ )&'-)1('*+3'%*16,$(0-==3/!.74'0)'1/(&)'-.$5@$)*%0$9./%%).5/)'&0)$$2!7)+':+/ 6*36-*070$+,/(-*6-'''%*(10'%($&+.#,.3)0,39.8/& '-75/20*+$272$&',&1."-)6)-*,80,"7'/772"/.)7/-"=$-&A)-,26(&?2'€Ô€Ö€Ø€Ô€Ô€Ø€Ð€Õ€Ø€Æ€Ø€×€Ø€×€Õ€ß€Ö€Ö€×€Ò€Ø€Ð€Ö€Õ)2(/,%2$1(27 1*=)6-6-9+%'1,7#.")1-;?1;%!;3-'()5,25%*:$40&1)*(3%()4)*+ 5 '-+(-)*".)0:*.%1? +(7"$ (#2(+/-%-$.% 2)*2-%5"'&.-/9-(*62%# 3 ,'5+("1))4'0%,0+!0)>-=;0+ 0-';2#(65*$.(*1*(+!+/-+&$)/+,.)'((3(/*-10)/&-.34**0,7-..1,5)&($713,$,+0'+3'()".&)',+/7#.5&*((-.)4 40 *)$&(-(-'.7!9$6("4'&'"34 &/%*)#(10%6*/"&56%-/+%'(1;)))")/1+3-1/%8.$-1'(3*+)-,.#),+/.**5/'#4 *13->+5 (+*-.3';)"%%/()'.,#6+,&>.6!!!$/2/'6%5&579-6)&'$%+#,9(0/'*;.//#;0041&,(-//+'):74&/!-''#+.034%$:!+7*$,*'))1*&33(4-*!(1%4&+++:27&(,, '&#€×€Ü€Ñ€×€Ù€Ô€Ø€Ô€Ö€Å€×€Ö€Ü€Ñ€Ù€×€Ô€×€Ù€Ó€Ú€Ô€Õ€Ö%*'.,4.2A)!+" .1%1-3$/0),+/+$)1=18-2-+++((+&/4)7)2 ,'/+424#*+)#)1%.)%'$/,A51,/-4-%='):)$912);/('!4!3)2+*%3/:)&).+40+4+(113-/;1#7:.#0%$,/,#,++"'+#+,-'-)+8'73"&1'&;*# #%4+,&0%0/)$'-25/.(%),-*+2"+(",0--$<<,,&+6,54$81&"0.$1#+$+1'''503-+'*"6&*,9#%''+5% 4*-8%83#"$/0-8)*'&011*9*>*.-//,+()13++('79=##)'=4'+;-9-70--%%)-+). '(!0'($9:%%+)0)).&,)$$5,1)(/1(#/.520)4%15*A0;4$#/(0-0/+4':#7 '2'0+0.)/5,&&3!&!0%#0339/9::$*,7!.-*/<+&)(7*40$+*'/++)1$5..5+#0-"'$!&)6,%/),5!+5(8/@'*3./1*,521(?-<50,1-+!-4!/.,@*+'@€Ø€Ñ€Õ€Û€Ö€Ö€Û€Ø€Ö€¿€Õ€×€Ò€Ò€Õ€Ô€Ù€Ò€Ú€Ù€Ò€Ö€Õ€Ú5"&!34"-+*')"95,60)*1'+*!>5#20$-67#-$,..&*3/'%*(+.&9)+3*'(!*+49,-+7*,!3(&020-%!8-,5(5,*%)/0.+(,",+7A-(3-.,-7%++2%0-#4;&.()#1."'1(),)/9*727,-/6'"-$7("/6529*'/$5,*,2!)*5(-=%+/-#,-%(+(-+&%-+ 6-#$2(5&*'4-'5$"-&%%--6+3,#.$"+$,5-96#!/8*4+,)0.482*#-#(()81%,2,*.#!$=*-'.:&,5',#)#(52%&%..&3(6)B*8-4*-/=+!'*1$-/$7*"*#,-3'&++))/$1)0-*'+,.% :'##.(5..0@<3!/-1!+-<'++)1*/&*/0'0220///+++5'(*#)(+!(?0 ,3&(0+)5#3 (+)3$.+'/050(6")&/2*+(0-2*'+1-((4*))0/2-5180'-.$/3$";%<*5%#%*6"0/0,/&!3)1.51!($..'8€Û€Ø€Ô€Ô€Ó€Ô€Õ€Ô€Õ€Å€Ò€Ö€Î€Ù€Û€Ó€Ô€Ö€Ô€Õ€Õ€Ù€Ò€Ö$')03)1-1:,/,8.&36:/0+*;/31*$%4$.')-'$%8$&$'1A/0.23-2/1)(%)&!3(/232)0,,4/.E$).#0),,%,)*'>,*.%&)/+*-0,')%8)+/&,(, ./1,#2* ++3)2)'$62)+0(%)5),.9+5>'//)*.7+'&(6$1/*",0/(*+/;(#'&21)%((,)2)&&,*4/$)?9/# *'22)).//.$.-'2!4&+.(.//%#) "%24,/1/1)) 24.%+(5%++. -#9*+)+4,/*((50;.!+)&#+(-.%)(=,%.' (+2 11370/Ÿb1)3932&*#'*11%&),*.)!%#)(,1$."2$%,.),&13+++01//%.)-/2/"0'-)/$81,4(.)33#$5'*!1.,/#4'$($/%67&/#4.1//7. 44+.8)*/2#,3&%))3;730 23/4%*,-'&23."-.*--%;5/,110!1%.4/>9&#:9(3>'&*'//'/1,5)5015-,0'5/-8€×€Ö€Õ€Ô€Ñ€Ø€Ú€Û€×€Å€Ó€Ó€Ð€Õ€Ø€Ö€Ö€×€Û€×€Ô€Ù€Ó€Ñ'&1$)(4'44%/.% +&(&!-3&/(+(- +#!3(<*52+$*&8/#)*(6-;$3/2%&)3'76%/2&7+)7."(,+*8"*("('.4%%1'-461.()%@:/695((*+*$ $)7-,"#01/53&& 2*+7))..,#.(4(*+787.*--+).#'-),(-". //27*.$'+&.,,/*62(4: +"--)%1>#(17/'7-0(9')34)32=#27/);/( !%0)7+/2+*1,%78.175%4/#!19#,*".1%.".&/-/7,)#1+1.1*'$2(,,5%+)$83)-/%)1*‚2t&(*-3;)4-6+)5011-&- +*5/:5'*/&,-7,)*/.+/%!",)#)%0%)"&//)*+#40%.#4,(!''%:.4$&+'%$*-.(51./,+50-51,37112-/$'&5)0 )//1>+)"-)).*2;5+.,'39-3/&3&,@-()+251+0()")"-3*-,=.0!3485&'4$.),$0<.)*3,2+*;.3&,7/,(€Õ€Ø€ß€Õ€Ó€Ò€×€Ù€Õ€Å€Ý€×€Ø€Ò€Õ€×€Ò€×€Ü€Õ€Ø€×€×€Ú4.13:1//'630##7&#*7$.!#++00(+-'+(2/,8,1,2.#4;;6:/.6*0-.(-+60$/%-1-042(7/2)0')('17.--00("/(;$/7 /(*%+(%%5(A3$11-*"17803=/(&4,1"*//.-*2-45(-"%+4'#$520+$'+--('.1'$*.8+-/1'$*5)�+'10,A3)1353!%:)"(/-$*(/:$2(.-4 :0#"*.,&.)!")$&23#6)2&*1622'-.*./$,1)33)2&)#(2,/).-;#)>,*.1252+)+$%/3'+6':"#,5'*&5'"-,#$//+/(%/+07$+,625+'-,()')1+%+-&(+('0&€Õ€×€Ö€Ó€Õ€Ô€Û€Ô€Ô€Å€Ô€Ô€×€×€Ù€Ò€Ú€Ö€Ô€Û€×€Ò€Õ€Õ%/-'5??/*'/&)($2#)5&/69++*%4*743**);//9'!0% '%725+32..+.4-1)8 15&)(0&'/%/"**-%==,22,&&./:/./)"*:.)$* 68/$8/&/)*&%*,#*(8(+.5/4)%,,&16&,3&*20)+*))".((+0+ )%-(7/,/**%*8/%,-'>!8"&!+%/"'00+ (/9)")+)/0'+&4/#+10**.,--,,8-,'2)1/,/$/'5*&8#7P&)+0-'+3+++3$0$,%+,,+8+',%,'5/1(-/)+0,+,-**4)$':13*.41*(/ +1),)).+/<:&"&()+.',!13/*,;'.#6.'3'5+"5&40,C5&++2&,12!---2'/2106'(400+,8$)#%5/%3*1&+!1+','6(0-..!%$1!/2,$)5)*<+!+#!))/%'!4+.%)&+2*"'#! % 72&-+3&(20 (-6(,(33#"64261',,4-5+%1++)-->(72-%;6()27-';56&%334#02.81.€Ô€×€Ó€×€Õ€Ó€Ö€Ó€Ø€Ë€Ô€×€×€Ñ€×€Ø€Ô€Ò€Û€Ü€Ó€Ø€×€Ó&*$F+944>"35.$+73,-2+3%5&<6+-)..*("',4(333.*6#!/4.&(3**55#/:)3+-'&(*/#:*0>$5+&+'(2,1-*%7(.(+)-3+&%/133(2$/)*/**3).+)-79#*%.14%5*6%?-0;0.-,'2,(.$2(,).5,-3%%)1+*+"32(.)53C+(4/)(!%04.+8,#(.2*>,05-!-;4--1+,4#(2).6:,)-)9--%'.-*/'#*&2(*.%)*/3**91&833=.11)'020464324,-"1%)006'38J.,?//.7&*12&-//-2+423@,-40'8?-(.+/'1/&4.!%1-./"* -0'&+2*'(8%,9.(439/5&&+,/2%:0/ 3:*0-,#/606-)#-++%/$&$43%&--/5(-(*/,%32//)$*%2.,$(2*1.1"&3&3,)+1.))*,(#03*2&)6,+.1<8+.)3"#:*8&,)+*2)// /0(('07'33,"749-#3+*)&1€Þ€Ö€Ù€Ö€Ú€Ô€Ö€Ø€×€Å€Ø€Ö€Ö€Ó€×€Û€Ø€Ø€Ï€Ù€Ø€Ö€Ø€Ó',/'6997%$*/,(?$.$.'#/-5*:!:#67(0+&%/%37')3/((048)+31%+#./')-,8*-*8!41+4',1832,-.!0(0&781#++'7&-#(+&-*/6/0,!-/(%C+"5-'4(32$+)0-9&-$//#%3(*$&!'+*3&5/4)%,+(-/,.,-215!8.-//00+(!$/1+'0)(7!$2%;&6%9%,(#3#-!",8+&'/,02)+,"+'#,/(.,)/2*+1/5='':-0,)/2*)!'6)9/4&4&/-3*2%#.)4$,&'(04./1'+(;1)*7#6*4"+,4-/,%)-/ /" 8/-04*/:"0/=,2,-(82"2'9''$.8":+62=#&/,3/'&/2':)&!**,,"4'/:0+230&9&/",7($.$'.05.),1,1+/'(#/*=(/25)1(17(")4+*#0+4%*-+2-+))$+2='!,.+8,.035>#- 3);2$9#(93.;&"0,-),-)))1,;8!/.(58* 6,0/*.7#)'../$6'€Õ€Ù€×€Ó€Ø€×€×€×€Ù€Æ€Ù€Õ€Ò€Õ€Ð€Ô€Ö€Ú€Ñ€Ø€Ö€Ö€Ô€Õ:,3&/**4,%"/+/1.9,'92'+.92&=)0037'3.F*-.3%2#$-4/74#530 72>)+/>&1%'++6+/"+'("-//",/)7+%#)1''"+9#5-4%+,'(2/'7$-)+++,,.++-7.&(%8$1/'(-* 0'&&;#/$0:%!/,8')1-+(/0+.,'(0))3/,,%?(,)(9'+-/*/"+(07"6)-(:921,&1'3,,;-$.#6.9%"(,+0,983.30-02+#%,%%*>'$-0'/#2''%+)0**(8%'+**$2&,-#2-&71(0$$&5-(/$'1&3&3.8/&/3+%!*0#3)46<&-*3#$-6/('+;+*51(/*3)+0(/&5-(1/'8%)>2)1821%/"3..+(/1/2+3/!907(/'70#?,'38.218I.2)4%&*=/.'07!-'5# &((*./22;?+&1'(+%)''",+420*08/(,.5#(7#*$% !/8.+2.*5(,(-(!-&*004%7&7%&#)2/(-'*5'/,,--;)+30"&,+'*)9&0,//*'!0)8!#+5&4-)01/)(-#/04'/2+%5,8,(9)'%,0'1///,.//,..*.!;- 22'+*7/:*1",€Õ€Õ€Ù€×€Ü€Ó€Ö€Ò€Ó€Æ€×€Û€Õ€Ø€Ú€×€Ò€Õ€Õ€Õ€Ù€Õ€Õ€Õ<#*%3)%+1**%.,'1)*,$-/'")///##%+,-(*18*$'1!3+0*$+8 *,+-.5#.2&='65.9'1:.*+'=0$-+/1'&/,)4:$ 2,,5(--.2/51%%)$(/(1.+!&3587%'+*215&!#45'*.$4-!(+)+'1%32,+8))$5$'')+-8-($4%.72',6*),!$%%/%2$4(2'0,-1-1)2+)!1.$/05%-*03,-,&"%'#'"-'(&)/4$711'&4/!//51&*245)3" ' )&.*5%3.>5(9 5//.710,$/+4"5)$$.6(+.&%#:)(&.)2('$3(,2%7)+)&57&%3('='4.13&(+%7(.7!+/1&4-..)$ (C,-)*3%./630-$'"%.).0/0*4*1C-,(-5 %,1+">*(0*63$(-',4/!)3*)%31$6(/.5*/)8%/'43,/2--.3/"1$$0)'-/1+3*.-4$3+)"&6#'%.+"-9*+3(9450+'/0/0!/*1<&+1*0)&-'-€×€Ù€Û€Ú€Ø€Ù€Ø€Ò€Ù€È€Ô€×€Õ€Ñ€Ø€Õ€Ö€Ô€×€Ô€Ú€Õ€Ø€Õ1',.9;-),,$-48&8/%/&1:82'4.>(035+0/+,A(4#+(/',--5-)/03$&-)A$"))02!#+/**%(3/-#4(9/*75/).9#1/551,$-04).).7&,-.)+$&)&?,/0*'<;>5&%,$&0+($+:%;0.7'+8+%*6-2!%2-5-*3,333$"+784+*&)/ ,,,0/,) -)#,%:/,,/12*/3/&8)%,#-&(=,*@+%!),)&+1/+)2/'# 0%!$/,+-.3#,$&)0/210.,!#&&'#' 4%(*)4%'52(" 36<1)"+,*0*:(-!!(04('2);)--'2220.-''"0/3%-)45'0//=+'5(5<)%)$)*,.5+;':-*-0,4840,2,042.47/1 /6-43/*/0"-#)1%+'/",$))/)#*1&+,5( +('8*.(*4')07#,(+!*89%((/+,'-0 *++$0",=2#%4)5"%%0,8.5//+(/*()&,/+&'1+(,#,6+B+/14+/0:5'€Ó€Ù€Ö€Õ€Ø€Õ€Õ€×€Ï€Æ€Ü€Ö€Ò€Ú€Ö€×€Ó€Õ€×€Õ€Ø€Ð€×€Ô)9411./*2*-"10-7'"<@)B554)1*6-=/&*(+&/';%.*2*!)&+)*-+0&-*-43602,22'-.!',"-( 745'4!***&,/!'.3&)/(%+')4#.((/-+(-8&0/3(1!$*%&8"+'-'$-2,$(+-2.1)-#4,&"085. "' 2'44.=)-%(-&&)'+(%,#0310)1+30$'0,$-+',6+*!,')-" ().3,!($ %<7.25.)&%(&.!+5*(($+)-)06//+8#((&6)-*/+!'A)-%-!2%'';&,4)%+15=.'-+-'**1!#,3 5&/7,-+"+.4$+$-!'30#)2'20,/+;/-(1'+28'6&7)/%.,5-#/-,),1:*3-<--)% +2//4-*+3&4'"3:),30)/4514)#,-+#*15,!$0./., /$4*9+1222&4(3+%.**/1!,0+57"'2(6)*).'""$5/53%**')(-2/9).57-9@.4/.+,-*<-7*(%:%9$6#0<7-/#+,);092€Ñ€Õ€Ñ€Ô€Ú€Ú€Û€Ò€Ö€Æ€×€Ö€Ô€×€Ö€×€Ú€Ö€Õ€Ú€Ñ€Ø€Õ€Õ#72'>C'120%++>+ 9+'-.-"2(8.-)542-%//,2,1#*.#6*,5)+-1/2%(%9".-1/-)7&/5(!26-**////(26.)5)-(500+-4-4"0,'-",(2)%(-2-"/-'.8').1%,$+)..2/3"&%')%4.#%&/73*8.* 1/.6(1/()+#))**3+/)1&61.984229*4%'.++)'+,.!+).,*4&/+1,*/0(!.++1$#&1,4++,/*%%/1'3,3).4>-/!-.&=6+1/.4($3<)6 ,:2,)"-%&+4-/*$$&5(+.!+&4 .&(427.)4"2=6%.0./,!114+*6*(5#(-6-(.3++%)'))/;,4!'2/721'3:/"%)"/7$-6; <)!/+%1*4*5!4+&$**52-.)%%+37*-7',&-.++,(4(-&0)*.3,+:%%,*((26,#/+!&/+36%*1,,%(%"7#C///-2*72550%6'3#/.)/24%*63,+3;+-014!2<7('.,806'-+*/9/-€Ó€Ö€Õ€Ô€Ö€Ø€Ú€Ô€Ú€Ç€Ö€Ô€Õ€Ò€Ó€×€Ö€Ý€Ù€Ø€×€Ü€×€Õ.8'85:67#4$4/;0.#:%&)3;.< +5'70-'-3/& *@+-,/-!!/'-.0-+*'&/&4,--.4''$++%',%1&"*0(.06+".*,0,."2&*(<5%$<%&.5%*##'2-,-%(0/(:9/+)9))=.0002*5.+0%$1)5'8#3,/--7F/).0'7*6($0//316,")-%%44+-(02)&/*)$-$/5"1,.,6-324*;+%3*'(4'0/*?'.- 314/03+9*+.%.'0,?/#'++1<1,5.&2//-.-3+))-'.+,5&.%,2-/5!)+5A?48+/&%<)):+(.)75-1.'--(###9)1+2*.$40,+'7"/+,-+$/5+1+*/#6( '5:7#+-@$,'#.))./3/+++2!(3".%$ )).'1513/*3)%1+3,;.(-31',$**/))4,3)/*0?4:24'*,5-1/50,,,+<(7--/*,+.*,&618103)+&7$:83#,.&425+#1%-.+1,,'0++6?00.1+16)00-/,"*7+;03$1(€Ó€Ö€Ø€Õ€Ù€Ð€Ô€Ú€Õ€Ë€Ú€Ö€Ø€Ö€×€Ï€Ø€Ø€Õ€×€Ô€Ø€×€Ö.-'+3-6.)%1%)!13//,/-)1+2,'*.02,('"3+2'812$4+)'+,+*3&-11)+*+'6;#(<2/%(*),.0.,"+15411-(*)1/ !-)#%1,$'*%-9&1,((292// 7"%32#+/:-.&8+'!(*+5*$4(%((-!%)*5!-892-3%-4 5*#90-73)318.-!$0',-1,9--*6*(&/2&.& !)1%)1&$,**%-/-0*'48%@)-43..4%?-0,'$-;&*206+206%"68%.' +,-4+''204*$.8'+&(/)/*5/'11 &(--4A3(!C('=+.0198,7'3*)4'562(33%* 2#9.(5)0-&0/ 8":,$/&&$313-+)'4,"+)%,&-4&/7-);*$00 )3&/63*0/10(,20!2+#"5:+203/2:(/*/4)+5&&33,!)1,2/;04#6,1*&'/3'5&3092&7,!)*9'11*,/0/+4%:'&0#-(70:3(.,)#".'!/," ,=*9.,:7-,1$5>,4€×€Ð€Ö€Ò€Õ€Ô€Ò€Ö€Ö€É€Ð€Ó€Ù€Ô€×€×€Ù€Ö€Ñ€Õ€Ó€Ô€Ö€Õ<,)36,.+(@-77#,+%4%*)$)%/'?.%3=2+,##+)=+%+3'/1%0-.3!42+#.-(=3-'96-2-5%$75<1-+(6&*B3",:/(!)*(0*,1#+/.&,2151<)0%!*-3).&*/% .5)*(//*2+-1/7'(-%.$69!--2.+!/*5$0)(*0*-+**1**)%(*!$2!5,--.&&%*63+%&,).$&&100-'1*./*4+:++8,33%2356$&3$1+2-%,/%#0,31+!6*4+(/);/$/'%,'%2,3,*!.-%2+#./1*,/*-#!-+($,1'&+54-/8'6*+)*("%3.1*("%65'++&1'70/7($#+)4&!0+*0/1(1(%#1)+0&;'0(<+'1.;'/(/$0('1.(0/2.)(.#0(-.'0.,63%!+&-1-7'1%:#*0>#/'++3C+$0-3*/,7"0,($40$0/."**%#,-(5!#7,2**$&3%30(%%&:.-*5"$6B),6720(.%#22+2)<)-.97,=-.*/3;57€×€ß€×€×€Ú€Ò€Ú€Ô€Õ€È€Ö€Ø€Ù€Û€Ð€×€Ü€Ö€Õ€Ø€Ú€Û€×€Ø/-+"'+0+5(;3)+0')81+'06('&23,4433/8/*,.-3'-+20"%&)+-'-'.$#.3,)6&1+,&4(58,2%65'3/'+)02"()),/%#3'+=,%4&&+"%117),*($$)+%12*0247#//)*-(01)# 7G*/(,9%-$&:-4.8)%9%*()77'555)3+/%$%>+=$$./!6()2/.4#*-6'), ,0*+3-- *3*62302,1',#,.,+1+43@*/+3&&,5(02260-$05")+(4+*))A$5"6",9++.+)'*)%&651))4$/2((".+31-(3&07*$**&",<)-4.+:#$'%%7'!5? +,,.+&,%,-%(4%+037+2.-&-54'4$ 3473'+.)%='3"--.<23.+.'- &'6(*)*&3&(/&&*12'9'26&/&59.5/.52*)&+'%20)!$&!03'-1#=822+54-*7:&)(*'/0"+;*-))(16(-6($&4*)+37)*/''7)23.<-/.(50*7'84(*+-&€Ò€Ö€Õ€Ö€×€Ô€Ú€×€Ò€Á€Ï€Ó€Ò€Ó€Ö€Õ€Ú€×€×€Õ€Ø€×€Ô€ÒB2'#4)1/$&"8.5'3/(/& 0!..!1+5/2.&&.'#121*+2&+)6,//)/(7&6$+/4(%56.+34-08)5.7))-/*=*3('50(/*&42!//%$'#0$%8#3)30)1!6/(1"-+&'(+*2,66+1*,&,(,04'&+'/%4'/27'&&2$-(+6!3.'/!+='$'%&(8/,*4+&-9#&'1-*%+&,1,#1*(;4$ .%3+*&*"&(#/##.%5*0!-%2 )0!!' 5+/#&- 2%11(6'0)-'+)&#./$"(-.**524-$8%1&*@,4/#,*37*-1*$,')%)/))/ B5/*//%5"+56$.'00+( 37+4%)$7/*#)!0*8#)%2-".0"9$#)7(18.7/-')+4#/9&2-527'+(-(!!909+(-).-+2!)02/'%/560'*)2%0*?9-2:3-:'4*)-A*.0-02-)'1.-.02/++(>',1/+#-*621629%)"*21:(7)*!,*1+,$55;:./*1*1+7++/03$*:.28."0€Ö€Û€Ø€×€Ò€Ñ€Ù€Ô€Ö€¿€Ñ€Ô€Õ€Ø€Ô€Ö€×€Ô€Ò€Ô€Î€Ö€Õ€Õ)).1304-?.63(-2-/8+9++-'30&)4).1()%6.37303026=+(%/5/<2/7'+0,.'#)615,.(5)!'/=((*)*, 0)'$5*$$3-)%),4+,82/430!.,23**4'.2-%4$5 ',.2+:97#3'.) 1,$-'.6,5!*.,+)(+2$/0,#+.3$#,"3+$'/+A626!27,,6"$$&,--/! *3!#;)4/#(*0/%=*1,',.9#%//2.((*41+'!.7'&1.*)//>'%%" 4$"+&$)(.&)/"+()-0%0'+/4$ ).=).-/4;+)'$%2!)4'(( ,+029")(/4 *7+;7-')*'1.&!%*011**'-2/2#2)!(%&,,#-*%/1,'/:/10*"/4(/3,%*9"22),1454/---!010*/+.50'.5%-:(2*+6+/+&57-/&-*)(-):%-&-$/#-/. '.2%0. .$%)#*-5+;;#/43)7& &,)6*)'4'/(%+//2$28/<20'*'.9/+30€Ð€Ñ€×€Ö€Ö€×€Ô€Õ€Ó€Ë€Ú€Ó€Ô€Ñ€Ò€Ù€×€Ó€Ú€Õ€Ö€Ô€Ú€×/#+.,8:.5)5/1812,*.'2.00.*1++!25+#4/.=+$.0+;)(&-(&'%')7%*%60'?2) 1.2+-'%#3!%,%'/0,/#"'*-%.+9#5/0-#*/(3&*),0+/)%-/' 1#/,*+9*),-/&-3$%,41)3<07*"$:.2--6%%'+%.+4'#((&2#/+5*9+1$,%($.&'-%6)*',/6.*,/%"2(/7--*(-5+.(,1+./( =*00799--<*52)7+/%(*#!)87+/1/6+./)74!*5,.,:)"-02*1&?&20#/*1. #/*/9%/*7%0*-.+;-#),/ 2-527+3(3)-3"*4)+.7-*+3&)'&%-1",30$(,//4.-#2%6%!+<&50(.1,%-)!) (/!'$1*2--*/!;1.)))/27+6)2@35 !'0)357,34++(0'"$%7.-*.("-// %/,545*11048#-5&2&#$7!*',(!+4,+(05!0-9/63 =2,7/35/&59-0)22"#73'4:A0(8)')€Ô€×€×€×€Õ€Ø€Ø€Õ€Ñ€Æ€Ø€×€Õ€Ö€Ô€Ö€Ö€×€Õ€Õ€Ú€Ö€Ö€Ø1,234)'5+,7/3+)-/631,:2(4075*-".0(%2'(1/,#1-():2'+/)'+,*())12'* 0$9((():,-8,+-"041))(4:-*.$"'()"2*7)-1-0).7(((27.".=550,%.#%!/;*+5 &,2(14*($./&&/3#!4%-$13()0+2+-),*10!+&3"+867#2.*90+&%&#*?--('7%2)*(,,&*?/055-,'0>-5#=#5/,.*(/1+"2&0)1,&0*77*7/&.28,)#)",-/1'***/+/-34-0454.5()*#"+9/(/6=.0(6--,(%/0(0* +A&)-'0*).:),!-6*&5+*%,2-*'1&6"!D+(+(9:"#-8'+/".)3/,4(4*<&5#$2)%+4--1-<)).//'+254#$#$/7+-6))3!/),55/1)".%#(*0/)4.2%)-/6+%!,*,4)$)/)015)# /&*2)*-,(2'1*.)+////3/1/1&1/**54 7:#+"8+%578-24%,;37)6€×€Ô€Ò€Õ€Ó€Ö€Ù€×€Ò€Â€Ö€Õ€Ù€Ð€Õ€Ü€Ò€×€Ô€Ù€Ø€Ï€Ö€Õ.1-01* .20+6#-(5-+*'1*-1%%?)4+073!*.&2):!:/+/*''.0'(+-:6/-#//3+..$5"*0+/0851/-1,10/*5,'0/--%*3)%0,-(0%'-++08>&+6).6!9+)/*/&#:%(1$+$"/.3/.6*4#%#2#(($'&/! 93/8*#'+)'2&/6@*/&&/)&8/-%(%, (+0 1!0)6.&&$.#+5%/9<+('%+?-(5)/1+-&/.)9&(* ,-,/&!"92,+')%3*8/()*+).*/*3--21,(52"49,.%!413"33/5",/6),5(3..0-'($1,505*4)(3/,$5,%9(&+/23)25*6(00()0(,%)%5/$"0/2-'-*2#5'.(+..6*1,2&).5!.*()('-+)*.+4>-8--0:*-&,5$(%*,,*/!6'++'*4!%&*/'%+5-973',%+)*)+-'7+5.2'/B+&(8*(8*-31.1/2,244*4+*-5(.:!;33/..$-530/8+3#14/568.(1-2€Ø€Ð€×€Ù€Ü€Ô€Ö€Ø€×€Å€×€Ö€Ø€Ø€Ô€Ú€Ù€Õ€×€Ô€×€Ö€Ò€Ö*$(3)+1$(2&7$3*2)8/0/0"/6!*10*020&9 -3*-71-0-+-+9.,3).:%26#(91//!'0.4-'(9/%')3%%-%*55*(+#+++'4.*+,("5+*(*.43<1&*-*"-/!/-#/&.5#3*/&031-'3-(13(',!515"*6%0%1"5*/-8.,('4/.+&( .77-2--, 6#,&')0.(.(0/(32(4-'5.*36&7+.$$)"7(1!= 3&.781" $('(-.,2+3$&+/&*(&*!)+. >8<701/$35:,0$#-/*"/,(-(6<22+#**254'448(,*-0(0*4&*/-'+7)=+3%&0&'$2,!3$ -3;)'+,",%131./3(#8/>(/.5//2&1+21963"21'*&€Ò€Ñ€×€Ó€Ö€Ô€Õ€Ù€Ó€Ã€Ñ€Ú€Õ€Ò€×€Ô€Û€Ö€×€Ô€Ù€Ú€Ñ€Ô,>,1*,3+14%/373.) +5%#).7.'46'(():113,$5:*+(4%,0*+"$2*6/2,.2+6/149+82%.1#-!#//0(15#73/(* *8'4//,0!-+,*!*),0&**%+2,4/1"-/#$)4*2(41,#!4/7)2=%7.-,(&.9,7+(60.-202,%.','$4-%#,++.0''5491#&-$()+(/1(%-(3672#*396-526:5$=*(*1,6&/'7%"!*-/,6&+7(*%+$%+6+(;%('.)/2.)-40+:.+#-4%8451;0303)3415" *+/,/"'-5/&('+++$01+(&;,'-./%/,2$=/)4;(3*9,%:%%*333)&%) 8+ .03)!-')) '5<./+'('&4+,'..1/(#4$,?+7((-."+/&3+,1(+',0*+7%*-+$2.6*&+64*(3%/%.('(+&!("0/8/,7)$0.30-63)1#%'5:/.,2%(%1/4*0/1,327.)2;0,,!2,#)6,0+0+3!(=+@62€Õ€Õ€Ï€Ù€Ô€Õ€Ï€Ó€Ö€Í€Ô€×€Ò€Ö€×€Õ€Î€Ø€Õ€Ý€×€Ø€Ó€Ô;7/0('-*&)4.,3'%(#(<#723.$+!1*06,4"'1+5:*0(+'-, 12%/8-.%*+.4'&4$/20&63#200'.1D5$)(.1"//+-3%/'.-$.3/&8'6,0! (.4763'!71; 7,(.)$"32(4!**-,*(5%+)&,)*)21"7-%'/(2-.(3-+**-!-#2(.)&2)(7+($3-++*&*&-2*+'$3),/*..$0C',$;04+36(/02>+"308(2,/%-%9)881&.*/%2/.+"$.5$0*/! (0.)$#!/-44((048!6#''$63&"-89%3*+2(&*1,*3/'').-#.-%/.12.)()70')84/1!'A(066&1#4)&%#))4+)0?*%1$2/ #+%*),*$45%&'2057(#763*/!7/4*$2/ 6+,,&.0**(4%(/!* -/,4$(&!"!4)+7+.'.0/8 +2#*$&+/7-+1#6-B)50/5)-/&'3)*)1#...,'(76 */212,'1+03.322$/84;9."€Ö€Û€Ó€×€Õ€Ô€Õ€Õ€Ö€É€Ò€Ò€Ø€Ø€Ô€Ò€Ô€Ô€Ô€Õ€Ò€Ø€Ó€Ù.$'&*!(/%%4/1+6/.!5(-.60*+:*-1,-4/!/()/*-2-3."/*'*003&2/(0*"*9;* $-'0(.+-1)41+7%#-5&(!(%'$,(%':0/'$6))7,.40*6<&7((-%-"-622)"%/44,(&7561%5.(&$;'-(4%/**31."#6%"05/&#$&/+"+10+2+9/23%.-*-!3+($,%".39*'(54)9)37 )-&$1//+.7(2!+#3,-%/** 1-7)',3&.+-&2$)0#3+105((-/+.1$15*-%%5/.,614(--/8+' ,!<3$#3+/56)=+;/9##0&/$!31*7!1/4!,4*1=((5(/+7.'/&#-+.$+#7,'*.'-%+54/1(0,252---)/.8(*>&)*73$%4.20!&##.1+66+00*&41(4&@:&-,+,8**)//,'%4%48"&,'96&4,&+5#41-#-%*0)!*.3!&9'*29#-,>(!.*+/-'$)/17#&*7*-1:'-7.++(265+*+69*-€Ô€×€Û€Ø€Ø€×€Ô€×€×€Ç€Ö€Õ€Ù€Ò€Ú€Õ€×€Û€Ò€Ù€Ó€×€Ù€Õ.)7,521.,2#"'4&.2('6.0$0-7-,'+*4/+3)&&%'?/5590!,+*++1' +)4-)15.%-))'0(*0)+&-$11)'$(;.94)5,1&4'&1#./4(5)+9&,)#-2+6'-(%!',& 2/6)/21613, )0.2+)52&*++7%'))+&/#2/4+-5(.&'%&//./"),))(7/0%7((13+%-'+(9,!4./%2$,1"%(-,8#(/#%.':.=/7%*$.<2)%243+./3-!5)*&*7'.&)/8:20-39'0+1#/'232,*=*0%))(/-*1'1"'5*+)9/)+'!,/7/%0%*/8) .'*,-!($%(5&&/'&79!+&2),,2(&/0+-614--*1+2#,$)!5.+/'1%)(-+1),+!(",/7501$*0)1,"9+3-,0-0+.$-+(0-0&5-5.4"%.(+.*))A+4$/'4&-&,4(-5*??>+#19.241-)+-+!*12210+2*3(>'97--.?1)6$84,0,5'2)&-+=(!2+22/;-/;€Ø€Ø€Ô€Ò€Õ€Ö€Ù€Ù€Ô€È€Õ€Ò€Þ€Ø€Ô€Ô€Õ€Ù€Ú€Ò€Ö€Ò€Ú€Ö&:+08$0/&1,0"-#:4"+$5+/-<+4>*3'2--"0'6/;+-+1.,.%)2*,13-1;/-$,(&'.2%')&447*(52-. +'/%$#/&),+3./)56;#.),1%$2!$%+/5(+,3-'4+/2+/'1$-.$)2/)1.)7)!)#'(,'"0&*+0)'74%")#,@23%5$30/*53..+"2(+<,+<,-4-34@33/2%'!!+./21 +*-&%*,44')-&:'))(.4)'*(#143-,'++,!"+)9%$-,(':;5$40),/*-!3:.&.4.<, $:6?$+!")/ //*"(-)*0,!-&.+*34$;()$,6-2/9-:81=7*=2 ),7*);'-6+,/#:€Ý€Ô€×€Ô€Ö€Ö€Ô€Ú€Ó€É€Ó€Û€Ô€Ü€Ö€Ù€Ö€×€Ô€Û€Ø€Ó€Ó€Ð-18%2!-72-5&(4)/-),2$(0*$'44!11+72-*24-+7$"1&3 )2?3/1&$"'8%. &3$0%!.,7,)3.-,1-,4,-"6)030%/;1'&)8(+6'5+./*&(.(04/23')'/3(.2)3,(-'*'33&)+!1,&''5'$5&-*230/3#0%-!5#%(')4/(/,4+$3735(&&!&,1)44#&),-'%&0(*35'/70#)01((+$/1/$),,6%",.,67$0)/+4"-105-4<(2.*$1+44/%-.%00!$(/$)()+/#+%* '-4&/822+.-0(,*.,#!*&).61*#25.'421--/*')A'$-,8+.+.&*/ //+6(/4,06!&*/-# %2-+.!1*6/%"1!"+& 7--0:) '*/+.*7'--//5-0-08/> '130*(-#7+'%0/%(!''1/,6"#/')+"+*%1&6#%1,%+22-)2"(%)&/,>43/5&!!6(*$)'&)9)'.)63-*#(/34")87/07172=-32;9€Ø€Ý€Ù€Ö€Ñ€Ò€Ø€Ù€Ñ€Å€Ï€Ô€Ó€Ø€Ú€×€Ù€Ó€Û€Ú€Õ€×€Ú€Ð687+'/;11(+7)-),9&).71&").$;)*?"&#(6#%3,&+-86.,-%'533/./:7-2 0,%1,(*2/!)$3(&7!-0($7 $1-6+&".(%!'/!+%)23$'/$&'%6)3?$'*)%*7‚ Ú:0)(24"$'+,4/$/6),"1/+40:$25+..3+=(;'71+$.-$3',450/2&:+#3*5"!8"+)0-,*'<56#&8+00!47<70/)*6547/)%3(,'(20#3+#4458.+"+-''&%&&'/(&93(51,-7 &<&+%--9/,+-'/5+/&,-0#;./))*'*/*'/5' -,5*-00"/-*58435%3)8(%)&2700E2-)*-077-080,+''!,93)3%+0&3#(/@/// ((//'3&/,,2'416/5(*25'+244-/32<6+/(*0"'*)%%7 2&+2*231'5/+3)#"6;---B3/8-*&9-)(%**.+.)&',6+3(% /1)/1,)",(()$(9)& %,/*'.391!%/&%)/1%/,14.&2!-5-/'312-'&.+$'+/&3&-75)3&"+$%3%0(&73. ,%/'0/)&),+%8&+<6).,/(06()+6.6+*(/++)%1'15 2''4'&-09*)-//17.1-)0&$(0+6%$%+#)0*/0',0,&+/*%%)2.+5!8$3"94%.-)26-5/*'=36&$+,).&3$*69*$(#'3"-%)3-%,2#(,*/2-)+(7%0-,:()1$-0*15"&$ ///,8:,.2-,-21)%%+&*#)+)-9'&%)*%(1-718---!,-#5.+4>")0$2&&(,6%)1#1*%0+1(;%730%3++@)1=€Ó€×€Û€Õ€Ù€×€Ó€×€Õ€É€Ó€Ö€Õ€Ù€Ù€Õ€Ò€Ø€Ù€Ø€Ø€×€Ø€Ò4/"> /@)-/84./3*'-$=)/((/0<.:1,4+4/.-.6/!-1-.*,*$:''8+&/1(<+.F.&$33,//)%+(%0-<,(5&1101(9+1/6;#.$+,88$-7)+* /534)6,,((6(14/()-:7/2+*74&5+&*)-.1=')-5*75,0'$26.-$/.'*6+-$5)9#'#0.*,7/-)&,-"&!&(1 ++ -1/'<.'%)/.*-3***(352)7&3*-@.44*&")12(7.+753-//&323))%0+"-,,): )0/+9(/./."9-70(!+-%',(/4.",<.0$+!)3124"-)%'-8&-6$&'0'77*).)/+#%(!6%(+'$143'(+.-+++3(7+%/$*-" )/2+-.1)$63=&().*0$) ++,-*&,304+1315+$*",*&-.0(@-+4+()+-'$1.)*);+''3+$0.!.,--0(312 /10=!#../>02&'/++(360&1-+7=(16-7(4 3616&"4;5-81#*+)#€Ü€Õ€Ú€Ö€×€Ú€Ô€Õ€Ö€Å€×€Õ€Ô€Ò€Ý€Ö€Ô€Ñ€Ö€Õ€Ó€Ô€Õ€Ô'095'$)8,;#513"@1,+1,'4"1-22,/1/(7="051)$':.$(24,4,,2246)0(5*!;(!,33(5,.+!,/)*1),&4*+20,$1+$0*/&0'5!2-)/*0%9.(;7.22%+(#+./%+..57%2&' 1&;0'*&$-/-,!,% (*3/53-"+%2&'3)) %&,&)-#%$*(&&(701+-1*)+&51-.%'>$,8+")%1((7*(('%#/&(*+(5+%*"'83*>1?/%.+7'4$/,.>%!..22/()&.%60805,3$%%4)%+-+.5**(.,,'2?.-4+'*1)4)%):)!'3$-/8+(/-"0-*+72/>-./:9#(- 0/1%/,-26*0#(/10+8)&5!-*,.2'<.,&.()*1/7*%/'(,(-:5/3(.73'#9-%16(50-)(,*-"))4,60-$(+2411/)'/4#)2/$%*/#3(.<+=,84(23$*2$*51*7+*%7.'!-"#2*&2,/+()590-61+/6+(1-(('-0#*./%*%77*;+&.(1,%.))6&:.33)'7€×€Û€Ø€Ó€×€Õ€×€×€Ù€Ä€×€×€Ù€Ó€×€Ù€Ù€Õ€Ö€Õ€Õ€Ó€×€×%%*")5'2;6;205*)"',%1<'*4387)5<&+.0)(,%*205&220+&((1%%47%*=#6/)1*.0)&*@%+0()0+)(+04/'-/'3)(.-43'//2-62)3!&+"1-',&/$3$ 0)-+###/&,&$6%0.-2/2+!$(+)56-*+8,5%/92.(6,%.-,4)1%:'-%90-113/0*$#!7 (0.,,*%"4?;/(-4<;"("1&2,/&+!/-'762(*,25.0%%((., >%!+3/5235$**/1(3#.3,%2*+1.-+11(1-,'#$$,'31*5$.'*#2%4=)-(83./+'.//',))1-))%18: 9!!"0.3%;.",/&&-.(?1,,+.+459-.23)(*'*+06'1520$-4)(1./7-3650-8483(4%3)6/790!1+'0-29)(81.'%+5.1931-.'%0* .10+*4%36#.82/7)3>934€Õ€Ø€Ó€Ô€Ù€Î€Ô€×€Ñ€É€×€Ø€Õ€Õ€Ô€Ð€Ö€Ù€×€Ô€Û€Ø€Ø€×.(38+97%--,97(+06)#1.,-21).,)6:/+,,%2'(2.(:#+/&*,%&+/)#*2*37%(*")+5!/%0+6+./,-0)27(+- '&2+!H//2860*7#-',781%20,1#33=%11@$%*),+)('2&";(#2)#'30**44()*-'20,0.+!*!*4+8+0%*$'(.(3///)C)#1/*34:%%))22.(*)-6,&3#5%3 2%1$.5-+40%)-&&3&7/021'25"/##4#)'46//5850'./(.$,*:34#+*+,0)'+"1#3)7,'&3 %(+/0+/8432'/+/0310-+)7-5&&7$+' )')&)*/*3*%-)(.% ,1%$- /*,0--2@#&4$,1+-/03.6-+/*!$$1''96.3(14.8%8+23)-4/&2#.*+)0%,$11(7('202,)55/7(16#-+# 657)&2&&+5.(%90-""-$352'(71&0)&,1&/'$.3,5) -*+("1-.-5-42,&<&(33$'-&,/#'3%)3%€Ð€Ò€×€Ô€Ô€Õ€Ø€Ó€Ö€Æ€Ü€Ó€×€Ù€Ù€Ú€Ò€Û€Ï€Ï€Ô€Ï€×€Ò*/33 %$+0+:80:.%))..85.+19*/$:)4.;#/%)+7+&/-,4%%-+;.%5))+.'1$-$/#36,'+*&#*/ 72#%,/-")(09(/1?5512#)#/#'/#,*+0'+>24/*0:510($)/2)+2*!(40%- +8,5$,*.-,%2'+'*'- )26'3-3*+"4#79 %(!'"(:6!#-//.!#2&)94+66%!*39/0,1=+*--&74/*6()=?)+),.'30*;.<+./6!-%7/16%$5,+%4;806+-4$'#!2-*,** *%!30#1230.'&3*& .$<+'+*5/+5 )0<0*,.0(-.(/5/71.#.4-+(9<82;6.1)/(574'4-*+$.;8.063+(9(%2+690.3)'.*$-695%./'-$+++#)!(# -3112%-6,'*1-&-1:")"++%"3)+*227)3%+#/*+"*'&/':-9!<(0(2/--#(16#&*%+:/3"5('#)'778701"-)"'%$/$9*1,/<,)*51,)/-146)*$#%%.+)+"136;+*%.€Ð€Ó€Õ€Ñ€Ø€Ò€Ø€Ó€Ô€Ç€Ò€Ô€Ø€Õ€Ö€Ó€Ô€×€Ú€Ð€×€Õ€Ø€Õ-+!15/400++4+1(82*2'!8;=*&*>70%&:$ 0#%*1--(%+0&"* +3-))&&))-$.*9/.0-+<'0#--53#);,#/)!671+)/0*271/5*#<"40/0-,4>%, 12,6",,%/+5)/-(1,7*)3&,+$)3/*((2;4'.3$"18'5+$33(761/0&&9%*4&1,0(,+0-,!'#43#*1,64445/3+.+7/3.-+5, (5+8-!= (=2&'#2$),*8%/."+./-#117"42'&)/"16+#2 #0%)#*%8%5 ,2+/3*%*2)4=%/$+22)&&8)/(1%.$&"8)4(91$1&").)*/1,#*4-/0#.,.23(()2--3(+/%24/0'. 6,/)51)&'%'4@*(/')-*&+('4+4+'6,&$',-,%4!.(-,).'/(/*/ 27*4%$6.+24../0'2%1#&&-(0'*"-.,1'(/(.6&',.!(/)3(43-!1'71+!42$'7$8-+.:*/-.84/)1''$-08%3*40-6€Ñ€Ù€Ô€×€Ò€Û€Ò€Ö€Ô€Ç€Õ€Ò€Û€Û€Ø€×€Ô€Ö€Ù€×€×€Ö€Ø€×,-+:23%63-)"42;8,3320'+(*3$$./,B)/&);$.%7@-7("'/ --8&%926-0.0 A#73948.115!)'$@0!0%$**=)0*$)2,0%+>*-299/&%-'"-1+!*0$$"%A1?-+2$/K%:2*/#-$ /#20$%/0 %-5+61+-%-(+12-9.>' 142#+079'(/0+ %,-+&/'5&'-).#20. )02,+!+%/(/(+"60-.'7!)&,,*#/).$111-.&)7&4(0/!*7';.4*#18/--$-/70',#-)721*!5()+'"+,*0,25//3)58)-%'7 + 6".''-&3$.,&+,/3(6())&%20**#'')09,0**$,3*03.%9+&.0425&-'%334"&',/,*-%',=+!(*-.')1,'%*-$&'0/831.8$(-)'0(6.+*#*)*1%6632*)..,21*+45#*(4:0##(1+,),+#2!(21++7*(':.0%-%0!%<7#..,+0+#;&+**./6+$'&;-+1/$#500(%&+7)&$)0#*5(#.6)7"62;,-3'&/-755*88&5%.+07)3€Õ€Ñ€Ö€Ö€×€Ó€Ù€Ú€Ò€Ä€Ô€Ý€Û€Ò€Ú€Ö€×€×€Ø€Ú€×€Ù€Õ€Ô/$0$0-%602/20"2-:(/#214+'&)'&+/%C%962.-+'3""&(/"4/3+8&001(9.8)"6+559/+&*B*,47&-/&3)*0%2(*(5)3(06&/?)0*%9/'.6'(0*/&71'*'+)7:2'&0-,-/*4(*/&&.'/+&80 '+01.)8()35 :%&,/,65+&+)5%!+/.7"*&%+../,-:"$-.#!3+)/*1(*2.3 /&+%)*/0+7/-2#*-,#17(7416*//(-/37/A-*7(-)+"'*$%/+:.8690%1#.3+&-%2=!7/)2",&0%+=6%&//'040/324(3 &,.%,6.)&(/+4%'**&26/-(- ,0.'$3)8-)-62##5/+!)/-/159??'/+*08(-0+!922.),%5%/$+35.5/$'+/ *"(('(2*0;,))-10*+ ,&0+%)'9)'3'6-+(+8**./,/4-&")/69/%),08'"$!*1%'9)0)))*;2.3420-$+/5&2#)0#;2/,883/836016"-6+0€×€Ó€×€Û€Ù€Ù€Ü€Ó€×€Å€Ï€Ú€Ú€Ú€Ø€Ò€Ò€Ú€Ñ€Ó€Ù€Õ€Õ€×1562.,$'36..,--/:53600,2#%/$3,441+5,1+13.B9/*.7062##/#01/( )1&(:4/,43(--+'7/(7-74%)1.!'#863:0)2(//6'1*67+**%&'.-++))"(@70*).&"!<,,,/5/ 15$'&/,'&0)+(96)%-"')4 .4##!-1*$6 */*)27/"%%',$&/0353(,&/<(5>&-'06+)*(',+50('-!)2$)'(-035(1%'/'613-&'1#$)01182.$6/%'*/'&4*,70*'(01*73**- <32*3++-6#4+'2%+/+'83.'-5:4 +)()-(,(5-*0'*,&,& '+)#5/;*))/78)/407"1+(9%%./*.*)9:(/)/(-.-"951&4!6);-($+/$+-&1/,%'30*43*3' %%-3)'/%"+.9/%!/!.+5(.-+/.--2(+3*.)1',,--&3*-11%05',$-(!0#!-)92.)7"&2#..02"$.-80",+)*)5-1€×€Ö€Ñ€Ô€Ö€Õ€Ô€Ó€Ô€Ç€Ù€Õ€Í€Ö€Õ€Ú€×€Ö€Ý€Ö€Ù€×€Ò€×/&)/.6#+-=0*2)0=81/)7'32-2,+)&&$31#1-&)%$/-+#!$3-(,0)%+4'$1"(/2-086<8"$%+-)80+*?.12"'++/(..,44&1(0;03'1/'ƒM1-30(0(%0%,+#'7/'!*0/'&(0(7-.$:,<%.,1 '1')(&:++1*+2/2%)1&4;5'19'-).288++0.'+('2*$2()/2/-/.-2037/.7.'),'0$6%(54(#,.2""+3-%86-)*+(+!#()('*40)&%("+-5*,.0(/)5&!)*,'+(%'$;*%-+&/3!+%+ )/),0,84/./<'+#*'/"!/&,*1,#%/137=8042*9$8),)180.,3(&+-(+*-)3451/*-/4/).))0*%5+.-# +&0#.#,9'!/0% ))/+&*1%,,,'%!(88>($&1+$ "3-4)->" *'-35*,*$".)3)%'3*21*%;,"*'(*.31)-"&@&A-.,)0&+0;&%:$4%/,.-%%.01#:,' %0:30*%0608 4€Ø€Ú€Ö€Ö€×€Ö€Ø€×€Ò€Ä€Ô€Ô€Õ€Õ€Ô€Ø€Ö€Õ€Ø€Ø€×€Û€Ø€Ô&;0-2%6($"%'31*)165+-82&&.-,<$8%0;&164+ ,%0%,/)0 2 3$20'.+07$.$--"'8"*.,84**+2)#3*$2+*',0."53-11/1'+)2&-$.:1)&-19;!'& 7'*,,*:%..%7(%"),$''-"-'./73'3$*'1+ 6#+6''-*'%/%&15./3"##&1.,.',*$%!*%$)/**%'+&%-3(!1#$"1*/-)'849/4+)>(-40';/%)<-3"5//)"+'4(5-7(.,-6.(,&() **23"/03'3 -../')*%686'*.44('20:)%!3'-+7$'547-%(00-,,+3')9%//0+,*&-13/(?#*%'-,* '+8".-*60@/,,.+ &%*2/1'-"-4812*-*+5+4".5'-%*/3!'0)*+*+1/.,&'/.'30.+;#2#94$)</+%-'-,,1'.'((+0(".,)0$53(1'7+20*%$,3)"8*&)(*'4%0.>*&1*(23(73-1:)*+%8&1,%--->);3+5€Õ€Ø€Û€×€Ö€×€×€×€×€Ê€Þ€Ù€Õ€Ô€Ù€Ô€Ù€Ô€Ù€Ó€Ø€×€Õ€Ø-9('.1.+-)?0';//$)&06/'0-9+&4':'-+-#0/,0*))#.32),#/3)+%6/.0:#4-6)6075,*19$%)$,67.9/30,*7*--4#0)*+)&/$3- (1+'+0),1&1&!/%&3'2)7*.*4$-$/282!>4'&)1*-4'/&&0-*)(10%-4&53(-!.*/8)/7*+0/,++&")%1(-63*0$=!6-36*!--1*/+&'0"*/")!..+'),#/430-'('6/&#))+$)5+7-,#.&!%!3'&9'3++1&-) !1*/ -18#-+..3+=/-%9&-4-0'!@(822' ';3*01*.(+%,-7( &.'"(>+/.83B&'8/,5,3*&()(##*30'$3+)!.(*(/(%8721-/(/$6)$ '"*(7'+$%!$(+17--#<4<"'12* -* )'%*++"'/0:9('4%$,3): *0'%))+(+.5)((,*$//),&4<-01'.1+-0/.5;%3%+*3/+629#-,7&40@/+)5<-5:/+**€Ô€Ô€×€Û€×€Õ€Ö€Õ€Ó€É€Õ€Ô€Ó€Ô€Ô€×€×€Õ€Õ€Ó€×€Ó€Õ€Ó5,'G$+1*/)14-'& )<.A036,+)*%-)33.+9)3347)$%(* #1)5*;7)+*'* 0--$(&&9+-)3'212,*&(%3+/+-//('"-/.,()%0(3"1/ "",#!'$'323(,;..*,0&&+#8'+'%%/%!20-&4+/,;*0!').&3'*$,4,&$72;03&$#'%:'0(;0,/.3#2;-)<(1248#2#'9.3&'/*10%)7)/2)&/-/83*9*$5.)+.-%#!,'(0/ ,!9(33'*/"4)&2-++2-#++(*'3(/6'((/!-2/((,%1,)*$36+/"2/62:4-&1)&%47-!+**7/'3./,50&-) 6+(6//(+0/+<-'7)*1-)/134..%,/.$/ '(,7*8$/943/1/0*1*-#0/17'1!1 1'474(#)'2%&5/.!.:.")%)*-'(-'$3#/)'(-!*$++ )/$"7,8.,;7%*/1 %2(1!5.$;-,%"6/"// *0+'55/.*9+.0&36+*&)5*%53,.5,&&.%2/'8€Ö€Ú€Ù€Ø€×€×€Ò€×€Õ€Ê€Ó€Ù€×€Ó€à€Ò€Ø€×€Ù€Ù€Õ€Ò€Õ€Õ(67'%;,)-3-#-'1*!&9%.4%4,))%)/45%3(,'2.6$.>&'0#,(,31$-,;20511(/-"3*-,/,''427/*5/*90*/(76./#*/,.4)(%;#,+0$=;&*+*>'.%?&#!24(+(-372(+*)*.:00-3%)())0'+*0&0626'(!-#'3-3-,'++(/'-/4%:)4(*,527")()/*./-7/****/-#22*2&6/(+1/4323$'#$9+)!((/+--#-+*-%(51)$5)6/%21*,''1+%10.14+1( :-51())0 '121)50/0(?/) .151*)-)&'+6(3:$31#/7,1( #2-#$0-,5,'*0':!&)!2/ 06..# .0.5+5=)(5)047&(++0.%#.-9!*,5+1:'6$-)01)0.#.,-6"+*/,-/2(6+>(8&/+6<4,5-4+'- 01& $1#-)2/'23+7/'.-$'#31$5/10<*(.20$*:'/!29'!4"/10'!&+,:2*$/1.03(5,.--,5€Ö€Ú€Ö€Ð€Õ€Õ€Õ€Ö€Ô€Å€Û€Ô€×€Ó€×€Ö€Ò€Ô€Ö€Ô€Õ€×€Ò€Ø3-.)*5%7;, >.2-*079!0+%/(&0%&1),+1-/#;%#1665+,,))+.0)%(*%&'3+'(/.(%.(+!+21-++"$*(10-.$,.,1#/6/)*!+/#7)(&,7"&5,&+(3%3(+*40*&,5+/.9.-:+'16&+$24>-360/9.=2*+24.%&) 1<%"/+/2/8;-&,59**24-'*0++206"#/1,&/+$.+/-#(:<)#0.+$*30&%-'.+4+.*25,(7&08%!822+-++5)87-'=80.7,&+6"*++$)'&)#333 **2-(/5.8)',':,'/3+#5- &+("/..'-+$9.3!(<&(3%/+!51(03+38-,0>/$-€Ô€Ó€Ú€Ö€×€Õ€Ú€×€Ö€È€Ò€Ø€Ð€Õ€Õ€Ò€×€Õ€Ø€Ö€Ô€Ù€Ô€Ò/.4($$17/5'';+07.1:*$4,1%C&!79+-5*7$+.',,7+-$;/!#$53&%1$"/7.5+,1$-&/7)*0/''-.6 6.)#,#2*,/(,#2)(#.)(02/%1).*$1:25-19601)#'+,++22-,6-/3/%,9-3)%*-",3,1(2'-&-,1%)8*4(; &+:-),('*11*&5&:")#')$40(4+7/)61420%*+(),'8,2$4(5$%)&>4%1/3/+1"(-.,8%+2 $$%-++/!*'(2&)+3&)+(.-'13#*$;:0+$%=30(E+*/(3)/)2$)5'0+-0-'*'-$/'7&2.7.2/(14-#4)2"&-70;#+,)+5$-0713-/.8'/%!%,*,<.++=$.24)03!1")4'%4:)$)0''''9-&-(7,1/( '$,//* 5((%(/,-1*.+*/$/%/1(&"+-'1'*4'+*-*)01)!##!&/3*/6'*7..$*)/'-'/2"61-%07 " %&842*+/!&%)36$8$+10&'&$8€Ó€Ü€Ù€×€Ù€Õ€Ó€Ö€Ø€Å€Õ€Ö€Ó€Ù€Ù€×€Ø€Ú€Ö€Û€Ô€Ú€Ó€×#).&:!-8,*'..% *(&6/'/1'44,)+%1/,).)-81*(,.'4,+13+7)310')//0%*"5134.;)/-'"9/2+374(,'0)05$1")+.02+%170$("8!%%+.*,/20(,.#2',0-,+1$&4/"),$071**':'1&&2//!)1% ,$$-(&2'8"%1'..".+'*414)+//+)1.+/&!'&//40( .'#+*&&;(-%-*(%&3,210)-+2=20#"'4'-#.+#4)%.6,)33.//+-2-9+$$?*%.*-)7/3#+1(+&5+4$/#-567'5"/*//''';,=-/%-.22*'#)+"2;3)+$*0*6A)'*!+"##/@##345&%+/#>-)-,.%2)--#)(1,25*,//-+%/02&?14)-%-3 %18110+$!5)1;.&6(.92/)24/3-"*,/;(&):4,,1",$.;---C'#1$*$5/)3+%/$1'211%#2 (57'1-7!A32653/?+'.+.//$/**6(2($@*&,2-,31-/%€Ô€Ö€×€Ó€Õ€Ø€Õ€×€Ø€Í€×€Ü€Ñ€Õ€Ô€Ó€Ð€Ï€Ú€Õ€×€Õ€Ô€Ö@46+',3-443/6&!:'281./!,57,).6-2>3<+%8+/01->56#).>(7(&1+2*-'",%(*73,..&1'/**"%('+/*('91($*-22,)4'%-3(-&/."/*/"*52('01, +* %'33'32.+,)-6+<.#5()+>+34/0-)>55+#1$6-86,()3!&=5+5)"+%7(1$2"69(11-:26!.<-/04&(%,&*--%1) *./))*'/+1*!+*,'-0$;%/%-$'*+$4%"+.+5**20,1//)0.35$"!<24-002$$$*!/+0//++0(''/'7+,).(6#+,%7, 8!#,165-@/-4%!155"*'*:..'1+%68.'$("/,(4,!,20:(/))+9'17#24%-+1#?1'-0.-/)9)42*.)&(7!1.34-/+1,/$4,).*/6*-3!$3*2(+-%+3&'0=/5/'$60.010*-)#-+/(9*,7,:%+&)/5;032/$36;5;5'3('',/2,:/9:.=112(*$21&€Ô€Ô€Ø€Ô€Ô€Ø€Ñ€Ó€Õ€Å€Ù€Ô€Ð€Õ€Ô€Õ€×€Ô€×€Ð€Ø€Ø€Ñ€Ó59133'2?)-*3-5070%/05**3''=,+*7%%+-! 821**3(-'53/%(%7'/3$28(5>E")'/#.(-*+,*.%2 ! $/#4,(.*1.!-:)=*,00(0)4.)-*'($)&4*+'+14+6<+)2"3*0/-#7% )5#:/++/>5./,720-51+1!7/"*%&.++1*0+%3!,#+0 +%'%75(0,,#./.*&#$#;%/&&3'5&F1'*%-1/&4*/1)202()+1*4/),&01*&1-)1*/+72%31-5,C-1-8.%4'0/A?9+<7)/*1+)%'-:20,*94(/.!/.-/*./&-:+'')'* )<,#,5-$-,>& 10+*9)01/36$+6/+%(..+.$&1$/./+&*2'+"(91+%02,)-*(/) 3.3+2.$=7((./3$4*%-%%**5 +'-(/09/6.-1,'.:(.-41&5$-30',5%*,,)*314328)( /-1 ()450%50$!/!50.0)+)45'&;157&0+1;&2.-6%')$(7/€Ñ€Ô€Õ€Ú€Õ€Ô€Ð€Õ€Ó€Ä€Ó€Ô€Ó€Ø€Ö€Ñ€×€×€Ô€Ñ€Ö€×€Ö€Õ--4*+$.- 31;,5&2&'*03,+/''0.&1$76)1*',2+/%3%*"))+5.(")74%+''3'&2.&/-,+3&63)4-(1/43"2/29"(0#:%8.3'9710,*8-+* #4(8 -3-5-.4+0'1"((/-6.(/&)/2: /.1(2)/0"1$(&-44,.%+'&30-2.+7!+$2).5#(1%#.,)*--,./1&1(,-0.($9,21$)!*/4*.-+-:'&3)(--/(7)200"(1//-"2%91/32/':(5)2)-$.'6-2)#;'4-+6+)'**0/1%&3- '4+2&)*+ 3#.&3.$$25!4'?';-%$1%'B#9*011$92.(.3-2%...600*(+883*)++18(+&3182:)'.0**4'&%/0/*,(6/)0$0)/!*',6$=14&))16)04//$07*)"-/-6.)4(#/; 51&&3%,0-0+50.*"4+2)5)3.'/*3&,/-4*&(.(%(/,0-(3-3+7(#97E'+/3&32>/-,-&)+/€Ö€Ð€Ú€Ù€Ñ€Ö€×€Ô€Ö€¿€Ü€Ö€Ø€Ô€Ô€Ö€×€Õ€×€Ø€×€Ø€Õ€Ñ..3.,6)-,.5.$"(( >/);1)&(.(/8'3-:-+80/7*25-*#%1.,'4%)(%0,#-6/-'))+#-/0,&))41+/%&(',1(-8+<,#$<2)+.''.'20%'' (-.,),1.3)83(89/-.+.3%+//43//-'6+17 /1/117'7421'"%'/-22.29+3-/#.9,#&3 "5*84-150('&'((%+(&+#,*.1.,03&+00(-4/##,6*(432($7+3,#-'(:6190!'?7"/(3*-$!0,$,07$.4.,)!'((#/202/--,0753+*07*+'-'+54+,#%(/&)-'.*)/,11&$05)">+16.4+0-;:(43'.<-+&0)'4, ;+=$4"/ +;'(/*-*938 %3'/#) 3#3/0%8$$1!/06:0-'&*)2(*+1,'' 0)0,&2005#!.2306#.,)6*,/(52!)#)740'61 +4-54(,':+'),++#02++*-!3(")+1-*-,'//):9+?-3%/4,,)* *0&€Ø€Õ€Ù€Ó€Ó€Ò€Ô€Ö€×€Ç€Õ€Ñ€Ø€Ö€Ó€Ù€Ò€Ö€Ò€Ø€Ó€Ø€Ô€Ô6*",):(185.+>!/+/.$0.0(,.%6*3.131-./-'9$-36%#0,.).00".(6,+)108!17411%$='51+1220($$#&%4$+3/8'0.%#0+"0*8*%&6*#;+*%'$',%"/+(73,:$.-'(1,)724/1/"-40-)9>%37%<,)*)"3.$+.-7/752)*41&)%=-++$43,8).4.#* 0$2*4&&+,$*"$%)6,/+'*1#-+))/80$,2$++71((03(2=,8, #-8((5++%2.:/"*7#++,8'/ *(-$'<#1%5,;;+-6#'3/#/%#3,$6;(;,5*04/&/*(%3#5(0510$/2,%) '*2+%"$-%,2/2=#1$/71296)+/5/)+/-9+8'$312'*#.6)'-*#3=/3!,*1.13)1$(++)+)1*%'&&/-/,%/+5?)-**2+)$+/-,/300& 1(/%2/%',,92.2-(0-*+6%9(;&5+&&3+%%2.32*&2,?77+71%#+'3+-/*4+;'0=.+$%.//3217/€Û€×€Ó€Õ€Ù€Ô€Õ€Ø€×€È€Ó€Ù€Ø€Ö€Ý€Ô€Ù€Ñ€×€Ò€Ö€Ø€Ô€Ø/&91-6/0*.$-(?/'+'/#36)3*,3(%0..--//(7#3 4*02+114!'&$0/54 :'$.-$-,+3/,5#0 02''6+/-4(++3/4.&3'(-'(*+#:-""00&(#* '7&,/'>"092/ .),-33",%1().9-,+*)#(% +#.-4!)('*2,$0//$*1)3-#5+,(-++!%*(4).%12--"''//6)/,)-63+0.3/+37,.93"(0'3)/54'-!1*/+,)$+%&/0/)@.+*'-':.)3,2%%../;3#*,#$%+'91.4)+/&1=+(/,-1()2/=-%/,+)!/(9'/7*.,*-&.21('41%0!0-(5/%+#/**..2"-1-*303378)75'712,%5*)42"2//3;%'&(1(*,2+6;))- '!&51/*3(2+13(,"9+/."$/-%(/!'$'(-6347-4077/+6,!6!.00;2#*.#&9)()8,*0*67%%> *6,71(27(0?-.'$#(7"%8///78,'9$€Ð€Ö€Õ€×€Û€Ø€Û€Ú€Ó€Ë€×€Ñ€Ù€Ô€Õ€Ö€Ú€×€Ô€×€×€Õ€Ó€Ò/61+.:6+1/142)1+'/!0/3-04+/6:*/'%)"3,*,2D)1*-+**@!%21,/>2227'3/) %6-6#1+)4+0!5&+%2& 5/:)++'*)$.( *#$$//!08%#.!247/6)#/.#.*1''**-2*"6*%25-C2'+-) .)(7+1,/+&*-00$.4)%'*":'*.8*,#--'.0-'(1&3),.373/*)'&5*)0)"$5)+),5*()*5%,5$*+*,3)'/32)-#/+"7//!)2,#.!7/,63$*-7)0,1'$/1+&%.-%"*(&3(-*6/'23%)050/21#2'&'&3-%/8*3-%"+.4/'%--0#)2,'/$+#-#,$6'&**'8.0)+)2-@1.07/,90/+4+1$+."+"%6),,,/-.$''%$!2+.)"'+(%$/,"5/(1&/*0+#(.#+*+$1%=/')(,4".-7"#',")),65(.)#)+./?0.92%1+;2/(,)/+!2./'.7*/1;7-1&4-2("-%).6%'*=,-2#0*1,-#'&66/,.8€Ø€Ô€Õ€Ö€Ö€Ø€Ó€×€Ô€É€Õ€Ô€Ö€Ø€Ø€Ó€×€Ó€Ö€Ø€Õ€Û€Õ€Õ/:-,)409-4*..#,/4&0+12##(1*)1'/0-'675324$00+&,'/*7"30 ,5+0/4++/270)6+)'',('1(-(*7-7*:&.1"(/,&2(.#;'/'%)& 667"/3$/5%*..$(+//*/1((1+5!7+%+#)..!/*('%5,-(.<>/1#2,%,3!0+/74*4*(!#+#31$-%'-0038%(/$-'+'-(,0+*"(1(/6//42.7&/-&27+)%1%)+*83@+(')% ')&+*'+*!,-,*2'5-:+!61,$1/.#+4'&1*)0)337@3=*+=$0253"2."%2,301*22'/.1#0"3+,7;$%4.3$<4€×€Ö€×€Ø€×€Ö€Ô€Ö€Ù€È€Ù€Ö€×€Ö€Ô€×€×€Ô€Ó€×€Ú€×€Ø€Ù/(.3)$3(1/,...)+,362+:183%499$" &)-%')&0+19-500+# -!%-)1)##1#6,9)-(3+,12*"")0*&/.'++4?0*-//"406.-#3 0%+(1*//-')/'+'%:.)(/3*,0*)+721-7(+*!'30'0-)**+(%;*-15'-*3&1,#12-//440!11/&+-;33&-5$+1.(#-/)(92-7)(0+2#-/6.+601*.$01%.#4=9*0-,53$1,3*'$-2)$!4%'!++*"21;)#1(0;06+! +130&)(=#*/,+>4,'4.+/.0"8$91-%+./-*0*(-$--*!54)(.390)5%+/*<6**!22 ((7@1+,$)./%*-$9'5)'83,/+2)//'/)*'-3'&'%3--#%-(.(17$+.&#,0.5).'..-9 ,/*(&/.'3'3$($%131(/-5/(0,!29-*/2.! +/(/'#4&&/-.'!3,*&.-50%/ /(-/#-/&F4.-1.(70'*5.;('-+";*-':€Ö€Ú€Ø€Ö€Ø€Ñ€×€Õ€Ñ€Å€Ô€Ù€Ô€×€Õ€×€Ù€Ó€Ò€Ø€×€Ù€Ó€Ù;").$-09'".'1 00487*$&*)(31()+%($1%/2#449'3*%+/,#%,;(.&9+"#&%/-./'4*&+1/2,;+%3+/$+,/%"+/.3-+:. 3#)="$4/)+0*4+.-<"0&',- &"*02320!)',*2'8*52259+5)4*&5#&(%,04.%(-5/)(,!(--$+-6+//#)-/,*./0*,/;$'.402001(.,1.-;00%/&.7:250*;&3%!5&9#9'*<09$*&+"'(4/1&7,().3.,4250/4***+'1"#'/&**'$130+'$70*>4+),6=>&%#2=(8,*')1;&#()'28(+'()-'50*/'(1"&$'1 ;%*.#31;*((6+@$"/+"-//-/&&&%407-'/'5.'(3)2-**%)*015,#/'''$11 ( 0.&".<5'0;(21*-(-2'*$)%'"2+$-"-+;$-*/**(#-2/42'2$)/0),'!@10)/,--'(()(, 7#,565.,77/-'2'6$:!;€Ø€Ö€Õ€Ý€Ù€Ó€×€Ú€Û€Ç€Õ€Ø€Ø€Õ€×€Ö€Ó€Ó€Ö€Ó€Û€×€Ù€Õ0*&4-1!/12>)/54&1.161(%34*5,02;4*2%#/;&+2(.03*1*=0!+,)"6'0)##'-0%:-+(',*0+/'42-4/4)+%)/69.*)2B/'./0/45/34+3)&,'('#%*1-(%+6-9%-.$%&&2!09(3$%0(-'1+"-/-17+5&**//1$!)7(&3!/)&"3&+5+0'.1(09 +-/#63*&3*)41-4/8+.&'(4%(+.,4.-,!('-'&-78- +/0//-3,16/*)+420*+:)1*!3,5((('5/(--.$'338/5'88*0!("*,$,(.+2$*,333-.&/*<'6%0&:.3"2%&#(!4"+@0352/('&%(3028%++?9-)5/5*)&%#(5.3.%+:6132*//3&+%)+/#+%*+9*'3",+<**29.".5-4,12'70/5%5('- <3'%3&7*--/52(,*<%2%'/,1+6:(.%((9%-1383112;-0+0*)'.)+*()+95/.37).&"02-?%,/,;:1//1.(:++25,€Ó€Ú€Û€Õ€Õ€Ó€×€Õ€×€È€Ñ€×€Ú€Ø€Ò€Õ€Ô€Ó€Ò€Ö€×€Õ€Ô€Ñ2.1047/-070)+4$-1)-$=&,**50-0<"&-*#9*,1(/#:("$).%2/+21/".040,1'0.022(&02$)2-,,+.2444433..2/)&'6')4**6:$,3)8,-5%4#+")3.1"%%1($78/6..'2-'*-!-7.)+?/- 5-)&36*%*$1/"!.?(#:*%35.."-+,:7-+."/66'%"47*2"%46(*%.0$.(+8,7/-7..-5-.4;,.3#6'+"2,%&$,%7/,323/%10+5$&2 )&3+)/<4!13)7((/$+)('1&,6&(3!4C+22++0..+(', ,(-&;**/(3.-"(*4/6?88)"3,+??81&4+20 %'*!)&)+'3:#)677+(10)6;5%1:;3-#(*209A/--)"-,+08,.!2,<4(1.&(#-,6&* 60/71,#%7)*7:7'(/%(#*-"#245.1=/..41+6,-%/6.1&--'/&0#7€Ú€Ø€Õ€×€Õ€Ó€Ö€Ö€Ö€Ã€Ø€Õ€Ô€Ö€Ó€×€Ö€Ó€×€Û€Û€Ú€Ø€Û08?!+/@570&-7)%3-1+,1%(,+-'!/1%#79+.&-8-%+3.4(482&/*!('3//(;,,3)7-)5&7/--)-/,+*82*6&(5''$--70-$&7*'!'8//*64&&*.)&**+/%5-&*-338,4%&!6!?4%)"(1+4*#$!),"*=0/"-#/56)'7.&&1)$39,3#+&=34)61<"8!((0,)81/$1*-&1*4+),3%.2 -0-5.0.$)"*%B068:+-,%3(,14;0)%+(1,=-?(;(*%%!)4/24$%&$),;++'(**(%230,*1*"*+09**03/9- &.,%,-//#%)4"/+'65**)5&+5)%.%1#$235@#!%('3"14/$%%1#/%0,5623+6'#'&/+/-''6; @-1((+"2.-!,604)'1+$(**-"/-+.9*;1/(+1/13#.2)1(:,/:21),/-.))9€Ö€Õ€Ö€Ò€Ô€Ô€Ó€Ö€Ð€Å€Û€Ô€Ô€Ý€Ô€Ù€Ú€Ò€Ô€Ú€Õ€Ò€Ö€Ù'(5+I*%'0,!2(,#+#0*,$'-."$//'-+.''!'7,:'!,'.'8$@4';8!03*5.1 +*3 /(0!--6 ,5".!5 /3,.3#:5(9'#&3(%.9)+/&3!/,&6.9$&--<"'(*'/)')+,13#+,/"/)0*0.+. #(..+%%.%-//053%&8+'+4*+..)!,,$(1/,>-3&&-+,3%&"'6-*+9,'4+'(+",4.!3+ /-(.#7,-/#1+2%70+/0++,;143'*9+3)4(/04"/&-#(#*0- '6-,-$!;3,$=,-?'@+'!-+4'22'*(-,*3:#.*5'B1.-4/*+3/(69-.+/ -90&21/1' %1,%+)9/0,&0)%*8#)A8:).#42(&3)&,.)-&&%,(,410<11-"1*0'7. &"(;(&-(:/#0/'-1++2+8%)(%*.0//%!(463(&!#134.+./'/;/-/9(.1255'*'8+39-02+'3(02%2)576+0-B6/,&36-:(//1*.(%*&-56:-;+?€Ö€Õ€Õ€Ù€×€Õ€Ù€Ö€Ø€Ê€Ü€Ö€Õ€Ø€Ó€Û€Ó€Ó€Ð€Ö€Û€Ö€Ö€Ú7-'/-)/+#&40&/;303"%/-80/*--8#1&&/.6'2+) ;47,', /34* 9-2(/,D,%,-!,:4$'1;.;*-.51/2+3*!&)(/+.'$,;452(,2+)/,/*,$'/('*;'* 8*/&2?57%51)9,#-0)*$512'%6%3942) -$%-)+((1A3-/,&1,!)$ 3#5)(%7*%$$-A+*$/B(:.)5,2?".)3-11$!36/-,4%-%1+;-//(5%(#-#0'1/67%,9':"///5,-+)$!. 40&/1./'&)7/%34),"-&:.3*(,"381'/,//!-+''121( &$.02*8& .8%/&22'.,-4'&;587$)&37;$(:58;2/ &08'-/)4((-#6*2$+4)-'..=-4-/'7/?+'1,))#5*061,+$'/('("-923.0.*/3,*10%+ +&.:)"),(*8*3&!(//8/1/("<$24)(1)$'+!/*'A/*752&%32'0))/+.%(01,**4231€Ô€×€Ó€Õ€Ø€Ó€Ñ€×€Ú€È€Ð€Ó€Ò€Û€Û€×€Ù€Õ€Ú€Ð€Ò€Ø€Ø€Ò.( )%+-1++"+7.&4131-#.-H/-32%+#.!-4/2%#')-5*3 953-.,/+*<) !10/.'$'/2,&.40)8+&.(2()6.+7/ 57".*$*(6/(%=*+.6"6,66.**+-(/0+(0'/217-*(-6,0)+*3++-/4+&&1:/4!*"1--350%!,+1&-/5!-)(046.%)'0;,+36:20+.*'0611A,/*3+&5-&.-+,%8$3#2(030-( /.5.,13(+)(5.&3A/+"4+-/%$7'1.*%"+/-)&7)% 1*+$),1/4-4)% .() -3$%+.)43/! 0" '.#,%'0#/1)*),+*/3$+ 26&+,%!/<8-+,4+!,/+(*-2((:&3,+$/".01-"()">*0!$114(+%F$&/2713#2!- -)/35&%)$-'%$0*-1'!/+-()-/$(."/06+$.5'#'),24"!8++$+=2952?#2'(024),/9 ,;*(#4-7"!-,)82*5%3)'&-$:77!68+.),7,.@0,7€Ò€Ô€Ó€×€Ô€Ò€Ú€Ù€Ï€Æ€Ó€Õ€×€Ö€×€Ô€Ó€Ò€Ö€Ù€Ö€Ñ€Û€Ú,-'-//-*51,$$(4./&,1+.(8 1;)#'80,*0N+8%/)!/$=,+71/%!4(2-&((.6/17'13*)*.;.>=$%+/2&)$*0./-(/#'/(/+4'#4%)55**+++4; 3%*2-0+2++)/--9%+&/46%�/.),54,.2!:+4"0,+2?+#-#0../+".''9.0'!(-#:"28'5)!<1%+)#70$.(%)"-2/8:,)4 %,)++/'5-:#&2',1+ $! %632#+-1,()0&.#%-)#&1/.#3#.*.-2/0'+5'4A),/0-'.0.$05?3*&',.-2<$+.'3#-0,-.$*54;&4*4,90*/),)0,3(/-+)9))-42)-2+4 %&0%&%7,1.:.5&*;€Ù€Ó€Ï€Õ€Õ€Ü€Ô€Ù€Ö€Æ€Õ€Ø€Ñ€Ù€Ú€Ø€Û€Ù€Õ€×€Ö€Ó€Ö€Õ*04&.8'&8///0-++,34-<)8%,*.".*1"4%++/.")&*5:7&1+.,4(,*<,1-,3&74)75:6+4/*4€Ó€Ó€Ö€Õ€Ú€Ö€×€Ò€Ö€Á€Õ€Ö€Ø€Ø€×€Ñ€Ø€Õ€Ù€Ó€Ú€Ö€Ø€Ù%5<5,*1>%*;9/+/+.$%(6)*4+#1,-6(,123'%/-D-86#+)6*2/+.)),'22%#15+*512'./0-&6*#"%!)*5%23.%0"+=&$>$%7+52%)/7/13/2,'!#$*../)9/#3*+$&$-'6!#+172#!)51'+*4%2+2-+5)9105>29,%06,39243+34*!3"/+3%5+6''"++$'< 220=+)+-2).-75(&))121%) $/.4>-11,-+%)+23./*"(/7:2%(%*,,(8/-*/)01'41,(1<0/-2#(0*5*5)(5(*1((%,'#(+,/+-.*050B43(.0**7-*'"&6=18#+1/#/+8(!*/*7:'+20(-.0'++'0'=0"2$++07,."-),1"*%,!'%6!4*/.$)951!5:7+/,+)&#:+$*-)#&0%&'+'3)#,8-7/.+8/(4)$/)0,-1/(4,)1&&+0+/&.45&&" (2,(6!')++"7%:&+""$--$*6).-'3(+0&0.-&;5A/@,€Û€×€×€×€Ö€Ö€×€Ô€Ó€Ç€Ô€×€Ú€Ñ€Ô€Ù€×€Ö€Ó€Õ€Ø€×€Õ€Ô&)()&>/1?*+%,,(4763';//$/)7..;*%//*2-+.5%./,-/&/6-3:"+ ./)$(1-),2111($)*0&&1$8<-1##./+/*/6#7+ -)+&*;62&//-57-0+'0.*05)'90++-%/$&0'0(&4*5##*+,-$+&&)).6)7-,+&*(1+))#/"47&4%$,',!,/424.&%*,13'57:0/03C4%4%*%-(%#,-9'%0!1-,20.5/$/0&,%.+/#(-.15..++3+"*;7&00%1,4%+..)471--/(-!*<)/%4)35,/,,6%8/;7&'*"/)A1)(-,+>'$--31.0(*",54''($=*:.(5(2'!%(-0,./2&553+ *1@"*23#/+)@%'%:*47"-).4)'/()-0+-,)0/)01005/31)8*'&/3-'--u8'(2)+!.(%.1,'7%3)3.171,<'(24/(-5***(3/#'('1*/143&-(($&,,:1%5/-93+:51,*5'./2.'$#3,%(3'/6.")!%-+2*1*0(7$75(3€Û€Ø€Õ€Ø€Ó€Û€Ù€×€Õ€Ç€Ö€Ø€Ø€Ú€Ø€Ð€Ú€Ó€Ú€Õ€Õ€Ù€Ð€Û%($41&,.//4#% 0,3# .$'/0&+--$46/;3,6//A#/ (%($%+7-5)$)38)(".7%#:)73%,$-$))&/3,$(&/-&*;)'+ (+;)+,!(6*2/%'(%'+%#*/2.$'+1(+'40, :+.,02/72%%)5-5))"1--76>10 6/&-$-'4.)3+6'":%$#+6!9%8,.,0-&12" *-)2.9)032/8+*3)5290/-0+'.+.,--5(++(%-#50+.0!2+)+)-$ ,/)%*%+11I!"*%)'+'!6++>2"$7/*;$%*,,'54+4'/%.;510$4*570+()5-=45'7!(+$14.(%:„-M*(-'/%30D%7/)41*9''03%%,1*%0."-&=+%0*>./(+%0$$$*4557832.1$&.13..0))-%,(5*2'0&32>441&5712.:65-/#-03*1:-0€Ö€Ö€Ñ€Ø€Ï€Õ€Õ€Ö€Ò€Å€×€Ð€Õ€Ñ€×€Ô€Ú€Ô€Ø€Ú€Ò€Ñ€×€Î,)05&56* "2(+7($,//,.63,+)/ &&'+0/+!*.746(.-1)/-(0-//*1;-9*%'3(3,2()E5'+3/>8-9&#-(7/ $'-4-053!(.647)*%$)+'-2**.+321+&8/*=+*#'-0((4",-!*%+3 -7/1,$& (+1#2+/7,-0%2&300),+*/@<79!+#)//)0,&3"/.6=92)/1*,/1,*+2;/,&#!26-7+48--3 (5%-?#59268++/60,)*#3/-3+72'!$."5-(&;/#+%0-(((-"3955('!)1'27-)-6*%1*#%,3+;1'7*-(9,0)/*.255(1)-/#+*!0!'&!!*'28(.64,2*#103&2($*.*&/+39++))'(%-,/+3/+*,.-95"65(223.,,+%.1/ƒr:08(.*+#+02(.-"$8'+$2%:"0/+*+*1*',,"%)*),703-$-'.4#+*&$1+''9()4>(/6.((.7.'9)23, #,+$-2+'%1&,7-%%*-+2%)-;7€Ö€Õ€Õ€Ú€Ö€Ö€Õ€Ö€Õ€Ç€Ö€Ô€Î€Ò€Ø€Ô€Ú€Õ€Ó€Ø€Ù€Û€Õ€Ó*/-0/44+%1."$200/+*+$4,23842;+!*."!,).#3/63914..3(+',+* (3-70-32'+2++%))4-31&1/.,)7* 0%7,3.(%6&.(#)>)*5870&.+20-)(*)0(#)'*/(&6#.2*#*,'G/.+#+!&,%/0!(2%*'(%&$-+/&())$-0";32+''0)+)')(#-0/*.",%!'3)(+6.1/&())+ ,))#3&7/0,+5,:1$-('1/62+:9),7*3##+4'./')-0"*4-+>8,(10/+-.-.,.&)+*2*%035'+0#1%-(+6*3=30;(/75,/-,':'33%")5".83).%,*'58+..)A.#0--)2(#64,2,1'(#+-332$25/:/-1.-0/69+-01-9'3//-!'(37+9.*(21-<4%/-#10010) %$)9#-3-)+,)(1*59 ;/%7## ;**$(%5.; /)/3&2..&)+8.#".)6(4$705540%+63'8161<2%"53**#.-20./3=1-6*03)€×€Õ€×€×€Õ€×€Ò€Ù€Î€Í€Ò€Ð€×€Ô€Ö€Ó€Ú€Õ€Õ€Ô€Õ€Ú€Õ€Ò81/-(23)7,92+4)7+*/)7-4=-(%*)'3-/7//.!-++#'%4*9,21+/*9,2(*%6%3+1*.0$'#0'',7//#*04)*&<'4.- 424+&/&+2#3>2*;2*,./("#*)'#"*.55-0='2!+42/<'%)!'1( ).2/!1)!0*'76)/2#7'&;-0')!#+*+4&&-()/.7'*#2%51'3/)46(&(+. /+7+632"')(8*.234+*,$*1,30''>)&.#+7/$"01'&.$"+.!0,+,-5)()(/:7/@'$(#&-/-'2,("-142+5*2!,622;%G0.;:/097*4*)0-**10$-*9!%) 3)5+3+,#$ )+.1405!*+B4&+'*,306+-;/$27./$")/''0)20(167(17+**,/2-*9#17-23""3(.1#4#3+).)'0*!27,<.+-+'-.6+€Õ€Ø€Ó€Ø€Û€Û€Ö€Ø€Ú€Ã€Ø€Ù€Ö€Ö€×€Ø€Ñ€Ì€×€Ô€Ó€Ö€Ó€Ù8)-/)0#0+(,./)3#%(--..2/&)).(910(2026(3".70)#.6/3(1A .)1%3).'3;$@(-/&3.'<0+/;*6)'+/2#0/,+##4//5$-37%(/(((3"-+326(/+3)!-'75/&60)1/7&2'3)2,1-$+)%,1*2/1001//,+*-.2'%,30+/6*/3.+447-*7204.8((>./)402*))10-/.!"23(8' 3**(-*<)+$-*1826172;,+73-+%* 24#3$35/.'+30/')39'/),1-170'",%/6,.,%3160-260/-'87.-/(-2)/-,622/*%+213$+556* .7%-)'5%*9# 8+#(:4%8;)9)+/$%%%#+)(',&56'+>,/-'):;85@257<9-.%/7-8+/:.%*34)5;3.4),&7:)*#&1#04/)=;"')'01-0)!0$>'+#",2'(&5()('12"/+&/.;./&%8;(,0)./*% (*/+1)156.,'41=32&.0823*1'8,€Ñ€Ò€Ø€×€Õ€×€Ù€Ñ€×€Æ€Ù€Ö€Ú€Õ€Ö€Ò€Ù€Ö€Ö€×€Ò€Ù€Ø€Ú1/.,=*3+11('7%&%*+*&-))*(0'%0$4'664-6+.()+/-4*+*+/0'5#- 3'##0-,,(0B-8&7'/. *0#B94902/(6+''+1+61-/003!&*0*4.',*:3("(&=*0'",+5+3)217-$.*)'#.'#)-)& &9-,7))4,0*,1&*"-$/%B#*+95%-)7*33.4500/#4<*),2*.-2!)'%314*)#)-$+4+*#1&/32*-+36*/&,*2#!146*)244+3>%-.0/ %!1!6&++,%,&.4121.15/.)'.%31%&,-+)!=3/-3&5.1$%310* 7*&$&*714//!*'D,"/-(2.,/?/ *".1/))+/,0($#0%)1,/7.' -#4+./'-/.=7&.27246B87+512.6+0!.-3-+$(+!&/56 /'2./@/+*.#85+)3-#>+,/'61.2-0-#/1*0,$36'+(%'*$&8#0+2**28(+275*-26-3/#2'11060+*8+(2,:+'1;+0**#+€Ö€Ú€Õ€×€Ú€Õ€Ø€×€Ù€Ã€Ö€Õ€Ò€Ù€×€Ó€Ú€×€Ó€Ú€×€Ù€Ö€Ú+.,5+9%25;)-0(-8;.2/<&&13,')/.)-/&*/1(?*9+*3/,+',%(.8/)825*#1+(-,*,2+<'+4*/")131,2.+*(26,-/1'100)208***)(/8*71.2&6++2./(+$$(&)5"**.,9)8(+(.,4$(1#)/,.&1#/#,2-*1,0,0#.)*!,!-5025563+8):+$2*@%)&%('%4,5)1,%+!4),"$4.&37-67,.17,&.4,#32'8-$+*,01/-.!(:/9%/./,3/357$'*.(,6/#3)(,152"4!0)6.+,6;30'1)3+/+$ 65+#=&+(5+#0.F("$+&'((",+'2)(&7%/).0 9&/8$('$,1C*0'1(329.#+''/)'&*$+-,<%(('3'/2$!.0#,./1+'(**/%(9+%$+,' .%'0&2&'"-,,)# )6.06+7%7.01##./*5/%*..4.40-,3/13/3)79'%',''-,./-2!*"+-7'8490/,- +(+),)-&80'-#'./(7,&+49)*6/3?#/$41%%/3,/ *515)1 0./(275'-32,)(6'*3,#&80#*0+''/'-*&?*34.&&#"#,,#,**()/'3*,.9-.*) 2+/$%02/(?'6'7$/3<:&):--/1<;PETKBG^G871(=(/(C&$>.+/13$)&,11&/''(,%*+1+"/&'!+'#'.+//!%+)#4+)8/."1,--/6'+3/6*+81#4=+$51!/411'11,(&%!-=23,:/,/'12(-9,'1($2(4€Û€Û€Ú€Õ€Ö€Û€Ù€Ð€Û€Â€Ö€Ù€Õ€Ö€Õ€Ø€Ó€Ö€Ô€Û€×€Õ€Ð€Ù*5&3(1,;&#*.39(/)5&0(-/142. -1&+-&3/"1*+&$))(-2)%#*$3/6,('0$+$./4.5/)+'*(,,+2%-)3+6'+.*1,/+.5+'3<4!.'0))7'1-)1/(0-/,' +,(,.*2*)0##*'311. %#(')* -/:##)0+$+#-*/'+%3+>"#*(7'$.7**1!6$>,3///*#=1/-*8#13*71 1+,&$1%48!0'-12/)''6,,,7(3.15#+,#90!'%)/0.,09%%(- '%&3:1/.3//59)26-)"2*5*!+'/..)44-+!+08.;1-2-/-)''7%-*+/9/'#(410"'-(0*!+3"$-.0.052%.+$:'3)--&")./*,804*8+,.1"$5((%+16=AEMIaA]O\HK5=1+//=.1*+%3-.$$)3" .0.'2 2/("+7:/25-%&-(: 70+/7-12, 001-%9&/0,$-/*7$ ./<63!'1%7-+!##0)+,5651+#:629-+.(*+4.&/!&2,2€Ó€Ù€Õ€Ö€Ñ€Ó€Ó€×€Ñ€É€Ó€Ò€Ò€×€Ø€Þ€Ö€Ø€Ý€×€Ö€×€×€Ô".:-=-!,@12+'6&/;0-0#-/(' 252(+=.2"3>1))'*!'2+@-.4-(// &)36.-(!9'$839350)#:/,(+$//4'/$+"//)3,*)+32*&*+#/500!3%%#!)4$10.3=1"+()33&)$&"*$/&)%+5'&)*)+*;*'*4'#*$4'*/%,29+6+50:3)$6$,/.+/)%6(*(#/2!9#;"$%-3'-+..*:,7'8:00-(,/8%.$ "(2%&./,5%0.%(+*6(*3,6')/))-2*1#2/*+7'/50'02!"):41)/9/'3'3(/!(/11/,*0:"-&/%0**$/0,7 ##-)$2,,+/$/9*(#,.(/%/*#)*5/%*6&2- 8(&?)2'2-4.9*;46-.2+0;(-6)/D@X[\NYNE5Q272335%/%75.+)24;'*'221%5=%% -/1%1+"2),)24-2.$C%'*,0+&(*%4-&2(*-+4&*&-(-+72,?:$%9#-1("%2.+'%42 .1*/++0,5<)1$6/<€Ù€Ö€Ø€Ó€Ù€×€ß€×€Ò€Ë€Ö€×€Ó€Ó€Ø€Ø€Ô€Ó€Ö€Ø€Ú€Ø€Ù€Õ6%/54*&44/(.1)51!3718-"5#*%8,%(*)71,(!-4207(# -(-+- 1(-))4,6*31)(!+01)8"-;*,#4,,'#06/#9#0!47(%*,!!*-%3!6!#6)./')"2,/+%*55.42'47,&&-5 ))5&"#$486&0'895-%).3.87#(,$(.)3<.@'6%&,/#,-'3250)--4"%'/1..6*1"!/9-#'824&/3(%3$17*#,*//#4-",/(2%?- 8-!)#+#042)+#0+2?&5-5//)6'A.+:%. ..../04))29&6)/0*,66/,,/()+- 3*,2*)+*,.,/1/1),+/.%)'0"*)$2&/23"2)(*"03)393):-2 /%-6.7%00'-2-6486C:E@RONbT`QNR6;3&1" >7+++016%$'2%2815*+)'1*52,--00+,.4'1(6(#)7'0!! -77*-2#8,+0.+/+1$,"*!8!/+"..,-3(46.%.."0,/(27$/,-..3505)2+(€Ý€Ô€Ð€Ú€Ñ€Õ€Õ€×€Õ€Ä€Ô€×€Ö€Õ€Ï€×€Ø€Ù€Ö€Õ€×€Ø€Ô€Ó+B2!#=6*/'*(2.8(%**8)7."/8 *'$."&98)32+.3 6+-/5!5,2!--6)'//#/%/+)##1%3##+0/5*)'8!.20/*9-&91#/12"&'%4,-+<#:)((*0',9$9%(/'*7)&1!+(.014+70'&11&2*#6,+$2,,'(%'!/(/)1*+-(2*!*16'1--/)-#.-*".&02-)/&,;,*'("/.2-6*).&05%51$.)35;/,) -34%#,:,$2-,3*-,+%0-&-4'*00!*032(24)'*)-3-"53*41'/('-0'22)26*+#)&).-=6'5(-6*&,/05'#%1'7"*!/*$,$/(+/."%-+-,.0()/&!6%0#'38.(8&6$$*&%.,6.&&-/./%73))4+F>4:;[OWMKTF4>51,/*8,7+,*5&)/-06,?*)(+/&93,92/*.)70&9-%+35!-(+*405/3$,.2--.,,-0:$2/),-/720,(.2"/ #%.#/)-)*$./%1018+)@3/2-7/3&.383€Ø€Ñ€Ø€Ò€Ñ€Õ€×€Ù€Õ€Ã€Õ€Ö€Ñ€Õ€Õ€Ó€Ñ€Õ€Ó€Ö€Ù€Ö€Ö€Ï.1:>1(+02649&02-&6-+1186(88/'9)/!5"')8#2*,(*''-1$.)00!$9+%20'.500+0#)&'&.2-2)+#++0'&2"%)5$&&2!(18#7'(&,*:--'0*1!(5,,/'*(*-%."2.3-'/!*(',(-)/15*/*+#!.*0'(644"+,&.$(6*)6)33+*.6('9;(-,.+&2&4"04)(-//87%*".,1-0,<(&1.2#8% ;*:32*26%%*36$)4.+& 3(%41+797!%&14+6,:/.68%!,+)/*$-55*-2-('2' 0'.-2%61!'2.**,6,/)90)1+.2''*4(+7#25/+8+20(5"4")*-+/23--/,/%,4&*!3(!%/&2/&('*6%0-40:1/2"(:/8,701CAbII>>=+9*&=%45(5.$'9%/',*#.(0(4848,,,$1-**+-'%;.;)'*4'1/'%'6'5- ,1#,53++3/0+.05/'/1%5%5+,,6)),.-/1*01/2--7,-&*,*09&2;3)/)":*'€Ó€Ô€Ð€Ö€Ý€Ö€Õ€Ù€Ö€É€Ø€Ú€Ö€Ö€Ø€Ö€Ö€Ø€Ø€Õ€×€Ó€×€Ó-7,25,!+6+1$+3&/+%0.0/((*19:*-3'+./&0/ -.0228(1*012%#7+)$8/+<,0:./5!27-5/5)-):3,/236(5'!$+.6,1'38*/8+/+2&+((1&7+1%$,4%$/($-,72221"')*)'.*1%,+%*+2&)&2".(,,"&/-.-.3(4(/*."+-13,$%2 !"' -((+//4'/5;./),3*9/&://'$(),.2/,=-0!&04/40!/11+,)/:/33)+"2*881644/+<9+''#:5(/)&)%*)-+3:03&4&,&=++&'"(:.'"+49 /#+!1(24!7+0#&*2.04$'./*'0+(8%-+*/#)#',)!,7 *:)2,4+264%---+%0$7./2,/$/'%366/4I# AJTE=@9C069(/#(*=A'757*-0.)--2&*5240"2,%.61/-*+0/*-59.0*7$*8#;(7*-6.(01'7B+.4:)5B1"511(52%2€×€Ù€Ö€Ó€Ü€Ú€Ö€Ô€×€Æ€Û€Ö€Ù€×€×€Õ€Ò€Ô€Ø€Ú€×€Û€Ù€×=%04<)2!-15+)C/3&1$(%!%-+(6!8;!90)$6%%(-,3(-3'7(7-8$4(*2!-&63-+20&"&.(+-(++1;.4+.+%2-,7)'0&1-&%''".5!.2' ,,/.!(+$&(/*9/)-(-'3&(/6/))3*(450///'*%-5/.&2.2;(-,2*5*'3*.0/+*,%85-1<,,/-)!! -/9#6/14&1'/,!$+-&-A,)7,1/1.(96+/;+/* (&33);0+-.2&-(5)&4"-.:%%#.0:8# ")2'%7-"(5%.4/%2"+$2+*<-5/.32(.(9/ $%>)/.%,$+@/-/3.*747--,<*2)-,+8/ +)+)2&(!%3-0 /.-"*.$05),- 3((+'&.(+*'+29(+52157/8;:C*>F?;,@'5146(!4*+0#(!. &*7'3* /#3/%5:&%+<(/&,1*.,1$12&!1*55-/)55)&')'9..5+-'(-5''7#'4"+9%7090%3"'/&"1(65;1;A 4*)%)3(-:(4*6€Û€Û€Ù€Ø€Ó€Ù€Ò€Ø€Ó€Ç€Ô€Û€Ñ€Ú€Ø€Û€Õ€Ù€Ô€Ú€Ù€×€×€×-4..)*:=)04#%'3791+,'*3/87&*&5:+--!2-%5%+570/'4/*-")"*=/)!68)$*%+',9*0 6*$$09+%3*- 0'5%)2) $++%'#.,$6(+931('/3')/+3">-)-2$$1/1./8#0/+/- 1)$5/98/%)+62#.5/*35-03/*49-,%7$1#4)2:+.,2*/-2/-/)/+*.-1#13(-//'(%/)#1>'2..6&01@(-7!06&/+6/1/*$#(&'#%5*(6.-401*0341,22,1*$(4&,1&-'.5*,/5.2->+(76/1+"*&2#/14+* ,/:*21.-+&71/%..,,.2*/3+/,&5,&,-''&%/*$$;6+/,15 */)$)'&#8"3,1%+3(,12316, (284,6.33"52!":5+)0,').-!-:;3,!#33(-983"25+4'5//1)1-9813',0'+*(#0&0'0102$7!/!*)8/46$%(''%4''5&),(39,*"17"4" )-2/1(+(&.1;*4;*/.+8:7&*'181$/,.7*+6+&131(%"*/()/4*.;,10,$"1-0)2/7!.2'27%)1"7+$).,))*.1+3.(-2'"()$14994*-07#,0!0&,'(,"0-7(!2((&*3 0$0);/<#&+)(+(2)2#1+'-, ..,(*+#)3///3#+$"0',3#)#-,'+'0/'*-./,,3!3#08.+%)/7673<(-:--+%:(61//22-((&4,714,$"0.3/'/#2&0/)##)).#/6-:7;1:(;9%(5/37#5-00/3'61'0(:/.2+'&32- +'+:+/.*,220'+-'6"0*%,0.32/2$()2(;,1:%27(,4€Ö€Ï€Ö€Ô€Ù€×€Ò€Ø€Ó€Ë€Ò€Ò€Õ€Õ€×€Ø€Ó€Ù€Ü€×€Õ€×€×€Õ5$--#9*'+)/0"33,/*-$0(320,,/7'71'3--"-1.#€ÿ21)2-"<$2-9+/()++67(,+9,1(/::(""+3&*(:0+#1**7074'-#+&&5"*+0>(2-8(,+',$.24.67-&.1.>$1#!5()%&11+,1",15411.+6!(,'00*(1+0.?/+5,.%%/($',*1-*#-/+0*-",//6)3-+"6'4=,(%;*'%4,6'*:3"2.#7()0%')128/*7(52',(,0"-6*,06+/2+:-)%(-+'0,-0-8.314-/ 34.&*0= +'H'24')!%+.=&-).$, '+41$8=-5#2%.--.*.-)+,17.#-20/'4&*',)"1-4+&#&5''.6514"&6.3/)7-+*+56*&/36:4/5%1,6=2),46)7&3%7*////-#,0'.''%$)7/:$"0':1(%1+."'4#'905&;5-5!7'#)02 /505"6'1/4+(/099='/-3%23,65"0$*/-*&(=,;07%-2)%3&/./(€Ô€×€Õ€Ù€Ô€×€Ù€Ô€Ò€Ë€×€Ô€Õ€Ù€×€Ð€Ù€Ý€×€×€Õ€×€Õ€Ï.4)(-1*377!$41/11),4177--*1)$)75;-7+=0()'%-3'12,+030%/6&!#,2%3%+%9$/ ,/,!2(.%5$5)-9)$:,,":-/%%?'2-+)#.3&5-%-3%5%-,&6),7&0/2,4*#'.+36).>6;$),+,,-)),90%$"//2+&%(.052+%0%*-'-'5:433*/-1#$/,<%).04+-0,'*5(,/!5*2&*8, /(/)+1!,0(*05%/55$0;8/*1. %8;(3+.8>,/6,#/,*.3),2)=='$..2,+"43(*),57&*%.,%9,-2--%3*1/&.7+/'3-(5$4!#0523(+/4;&11/20.6"417374(%*/32/0)(%$1$6+3.'&-/,128)$+-/133*=0)%)&4%51"8%,/"'')$11"/")*#3+2,* 1",**+41(& )%'7%"(1+7&#/.5.47'+/",*2../-4*-#-!(-',*-?""104"'6,33*1&36#,33(46720..1!7-*7.'01-,0.2#)6/%47/)/:>)*-",).,)%00,11+*8#10/,;/+*"$725%*21*2*$41(.3:6(4355$00/*2+.$$/&2,6$7,.3-*11/9&*.'3-)+61.%/%%7+€Õ€Ø€Õ€Ø€Ú€Ù€Ï€Õ€Õ€Ç€Õ€Ú€Ô€Î€Ö€Ï€Ó€Ù€Ñ€Ö€Ø€Ó€Ö€Ò/,*A/*,8*4-0)0--6>10.7,30$55/-;0.*6 8,$0+"66,/1#+8/$+'8'(&/.((2(4*/(3-7+)/!27(-(=%+(/-2*";.%*.$5%/'24'/0--!7/!&;&4& **-+/',+")*5)/.!'.@.15-))-,/A#0',-$0-##0#151!5/%,1 -346+"%*247*,.3#9-*,/#/*:',2(%4-4!.,..,02613/ )-)%0!$3,36&)**'/*3(."'/')/9))*/$+ 1 ,:/ -$4%12,84++%.%"06$'3521&?&).&/%-""&$,-'+.3/. /,+-'.333/--&+.72,$$"/-&402124;+22.8 $%+(0++<2",13(-(2=14.54/+(0)-05$'0**$-)($26-)&4($%6,(+.!/&1, 7%,;4.-1!/13!.(-*)(+-713-%*;-+0-+-'/!&/+#.+3&1'(2,351'!0-5%*+&$,%&+*+/1-.',..2'D'62<-+/12745!*&2+0!/.!3#<10 !1*-.32/$#5&),# !4/7$%57'1$-/&872''%4$:6#%$5#&$&,+,'//(07*#/"*2+0/(*,3,7)&3&'5/5375-22-/521,7&0#*":08,1,;&(*+3'1:-7"*794%;)*%31.$24/!+$3,%)*:5.)&%%--9,7.#)84)(38:0!4+(%5$(+3'%%4,3/.6-';+%.(!53'%(8".%$3'#79%.9,6&)"1(2C+"%+*1/021?*),558;9&2+'+*,%1:0-()/&3+#.6%%)01),0$3/%*++#'%2.)'586%,%;)25*'-%/"48)$-&//)+!))/(0)0891.40+!+.7,517,*,+9.63'5:&3%/1#1.)%)3/=+)$ €Ø€Õ€Ø€Õ€Ù€Ö€Ø€Ú€Ù€Å€à€Ð€Ó€Ø€Õ€×€Ó€Ô€Ñ€Ø€×€Ý€Ù€Ø)-49":+,$$.*$)*0+6%1'=%$3&)&.2'2-'3*+0-5'14-.:<,/-/.8!%7'.)0-.,#')11&/0;4'+:.4)$0$(%+7&%*/&#:,59$ %2%0620:-0+81'<%1%0*1##++5())"&-*$ 002'&5/4*(#.)-%!.23.).&0-,'."#9"&1!5/7#*21%7*?'("6)28#-0/'=2#5*/+*10"./35'-#'#+0&*1(8 /(82,-A/&9"1)+-/)(#,/,#0'62$ 8=820125-*€Ý€Ó€Õ€×€Ò€Ñ€Õ€Õ€Ô€Å€Õ€Ø€Ö€Ò€Ò€×€Ø€Ö€×€Ú€Ô€Ø€Ú€Ø"738(!,.3;'!-/.-1(110321+&%$27('')%*%01&3,+!$01'%'+'/'$&%%;+0#) ,5.=44,("(+/*/",7 )):'(+%/1-(+)6)&)5230!797(",)0'"9&5,, +..#-#/)(- 5$/,4(%5)%-+-/?+)"2(1'*>$%*#23*!( ))7/'(1#(*(*22$'%.29.(-5%,+'3 $14*!735,167/0&0$#&75(1%-%2)-3+'"+-9%.1*-.3.!&)$$'.(1026:3-%3*#9&'- 68),*/9+0/8/4,%*%/!3)4-4.(+)/*-1,,)#$/!-'&3",!4&%;,*())//)):1&00&--,1(+5'#)-3&.).%)/$0%$)/&%/53,'%2,,/+,",)3 *27 *)',!,(0,--7,'4(.03!./:54+1'**-!)7#+259 7)/4A2352.-246+5553(.&09 3€Õ€Ó€Ú€Ò€Õ€Ô€×€×€Ö€È€Ø€Ø€Ö€Ó€Ï€Û€Ò€Ó€Ò€Ö€Õ€Ø€×€Õ-*5-0$)403$+A,.*)6,0().('..9 (22*.1,+-0,'0*7%*./)(7+/!'////.+&4))$1+1$5/'16$*!+*5-H+8).6#('/("'+"+'9():&/(2"1,"'+-5/-1'+!''#/'4$20;;*&"/%3&5#'""-2:''6-%,#('(2$'03)%44>&$)1'>73(+23)-*,/%%)'"C)),&8*:<)'%0-,)0;,2.0%,39-4++5"-0/(++)'/&:&/46)'=0,/5-02*70-(0#2-/,-;>.0 --+1,+3%,3'/+1+80/3!-6,$1(/$/-)+'&-"*),/)&!(9,#1)%&*,8#&,/&2,3)%)-%4&(5+&/4(213+$2*-3&60.-6.)(//3$'(:+/3/(;%.&7+/(.;8003$/,!*5-,/0&1D/0+2%$)3"16: 053400*07,8-5/--!;+!'&"1-)*(5//$5$$( 3)7.0&243(*/)$;'*"7-)0((C5)5."56+6<2%+473+)$8'+'€Ø€Ö€×€×€Þ€Ô€Ö€Ø€×€È€Ø€×€×€Ø€Ú€×€Ò€Ù€Ô€×€Ü€Ø€Ò€Ó.&+>*#-*+5..,&002.).:0.9%--<-+-$; )/.'22',.9/5,'.$%95%- ,(.*5=2(/!42?2#'&/,'+,5#5'',&922.3,'4$+29.:=.&1'0;)','/$/,&-'/1&$.."''.(!)/'.*(5-,(*3< '1%-9/)2#/+4=-'/1-;',.78%3''-5%+;,-426'.5*)-3*'!4.*57+.13*..(#"/$(,()4(0:2( --).%3,#"/*,)()1/2".,5:16((/-441-'#4+9..2;)+33&-(*$%&+0,90)/&.+,#,*-/'./)$1<,-#;(+%$#+-%,;45, ,*+'+>$%)&3)<.)4/<%($-3#,'2.**5+5++$+&/!%$.-&.(-/3"&"&!4881+.4..,/-5#2,"7*389%"5402(-'*'*%+6-%+527!8"+#+2'+/5(,'/3&..%+*&-&0&,((/5/64347=.3,'1"%%')0/+7(%$3+**)%5?*"/,*,34*"$%)0&5(2(&#)'0*)7,"/''(17*")/63)('--',5*(,'0.+).'#)&,/,$!74>/2,4+53'/.$7:26# '-1 21$34.?">/'-3$-='1(.-)4-*)16!./932&)854."6%(,*)/'+,1(.2$%*)&,1+8/+/86'+2-'.&+.404*€Ô€×€Ô€Ø€Ô€Ó€×€Ü€×€É€Ô€Ò€Ø€Ð€Ù€×€Ó€Ú€Ù€×€×€Ö€Õ€ÔG+%8+*6#-*'0/41501/9&-14<2-4)!14)%6+3:0*1/ !%#5(*2+,&-/#*)$/443-9?,/*%E41&,,#5+),!:)&-,,01''4,/%)+'$8*44/+/-=/)/+0.& -1-$2 1,47(2)*6 61B'1/$3*52!,0*53&+"8"!(-3$/7.470;)%%(1+/#3$4/"''/8*&7-4*-"*41'',$'.3)1+0.%&651"2(+7,:&4*0*)>8'4.)%76/!-4"")&1,/<6')(03()-%(''*'53)-'1./:1)--8%.+6333(%9/.%'%:#.0.('+;))%-9+-(8-*=2//14/,2++/+$/'/0*473&#;/%%4.*1.)-)3%1 (2#),7-:4:02:+-2*(;/3-!-1.6..2)+%('+9&),(%/'#1)0-(.3-5/1(*, ")336,0+(-/7/0427+70*(',/'61-5##17325."'+4:1(%#05C2903-0, 64.*&1+7,91):'9&(0(,€Ú€Ô€Ô€Ü€Ô€Ø€Ó€Ù€Ï€Ã€Ô€Õ€Ó€Ú€Ø€Ó€×€Õ€Õ€Ö€Ö€Ó€Õ€Ø338)'$,'9?+&'0))'3(*&;/+*:)4.'!0!5//%*(1/#,)6&*327.:+#//2%-$,(4%-5$8.(,&**-+#2)*.0915/2(4%%(4&*./(44+4+'0$%%4C..<),-*(20 &%//*./+1+&9% ##,)),-%94'140-!7+,# )$),7.494/8+-))%,')')9-'2,0+#%-0-+)#/(-$.1/20'.-?5,'""/%*.0%&2('!0'3", 3/"/6)4*.) 2#<))1,.+-/*/)8"/+1/+4+."1$))2)/3(+A+1+.'.-.+//,8(5$3*07!*/2)'/-!+:#),-#5,,$*."&)10.-03!+//,(5&-+.-/03,877&13&*(+6$4+.160-(5%44" /"0'++#'-%%( "/?7-.&27.-/(.7(-,++2.*:(-*,4%).=#+#3'$9---"$)2<-!&/%2407,16>/'-/+':;.1 /.%+93*%0&)7B/:%1:*).48)*/9/+78,&,60+€×€Ö€×€Ø€Ö€Ö€Ó€Ù€Ò€Å€Ó€Ù€Õ€×€Õ€Ü€Õ€Õ€Ò€Ø€Õ€Û€Ö€Õ47*//3/*<-7./)('32).74%3$ +0/**2 2&)6%'*90##,$97 001''#-%.1%#0*1*.$2)&+(1"15/)""86+(+!1%,:4'('60$+2:01,'+*#)(A,5((+,#4'.1%<,+*0'2%',#+/#>1+6#+,.&%32$,4-.-') ='026.1#2'.)13%!"1.&('(%;#'6%:/"+0(142*&565#&-/8&&/-.2%4&/H'32+++"285+(2/<88.+)847(/0*/)8&2186*1+%*5+1'6.&+'@+;7):/7-4%6$76./&2'$".25-:**4#%/935!.+-*+3.&A%1!31%B$1,%"('3/%'8+453=,93,390/(1*+.?#(1*21+&+*,32&74)&,2'0-(&,,(/--1.4'13,(#=../.+(,(-+0/9(,0"1!51/$$3.$(431+-@'(,(&-+ *&.*-0 /7,(1+- ,)&-;)6.7=7.57/)'1%;+") (/7(.0-'3$+,/!5%-1.-/€Ô€Ù€Û€Ù€Ø€Ñ€Ô€Ó€Ó€Á€Ù€Ú€Ó€Ð€Õ€Ø€Û€Ñ€Ý€Ô€×€Ð€Ù€Ú0116/8'1(.7)0(*9;$)53)/##*01023!31/) ?/-)6%(8,&+',/-2/-8*$+&-0/)'3+=,$1223,72/+7+&9 !55$/.0.+394+,9++611*%#-/($5++)!(%6(24"2(-(7+/&-747*(147,.*-!6*))('(5,)*2-/"3$..(;:/)"6/+-1#%%+-&,,-%-$,$'- '#$+2'*/262*53E-(+)4/-0/'+/(((5.116*).13(3:+)*..0'%$*4+2%2&+'9?($++1,/#)/(). 2(+.,-#313'#78/:")08&1#3.):*(*++&+8(8/73(1'+))-3(3+/'-5$//8),.;,03%0.'-2,$):-"@!3-)%.%6"0--341&26$86%)6:7)27;0:/))2-3#+/3/(&!%'+%632*2& ",$+$/'')047,+3')*2'##+#.86*-#-.2#2/*',+(D'0(3+5%*-.2'+,?6,*2,08':24-'-1'-0/=)+-),'&73€Ô€Ò€Ø€Ü€Õ€Ö€×€×€Ù€Ë€Õ€×€Ô€×€×€×€Ô€Ú€Ù€Ú€Ñ€Õ€Õ€Ò%':()4+3112&%"21,)(;2-305(/,(5%':%%-3,-10+=',#.>-,!57-&12-/(-++98451*(359%524+6&%),9+34#50'+&*26-+"59).3(/6.%6&/'.$-/$0)?:*2.)320%.&0-/(2625++.(/)03/$+%-3-*+0%5/$-+6$)"2.1"*-'%7$-+'0)))-")#& 4(41/60//5)6',/40'#&0':*7.)4-12<14.))&&'*704)-9)6)341 /4&$.1139-..-+*3%/1+6'';(/")/#$8 (137.=*+3$30"1++./3*103A4%&+4.-%5)'9.81,'52-*#,/1,4;,13, ,)#0')4'-+0)4#9'$&;/:.9(58))(*-$&,+*2.,)#4!$%-"5*7',/++-')+33/+#60E' 5$6'4+/ 24;C'((%5+/1$+0/&5,$"=3/C,/4<3)-#4 7+858/4%€Õ€Ó€Ö€Ñ€Ñ€Ø€Ò€Ô€Õ€Å€×€Ö€Õ€×€Î€Ï€Ü€Ö€×€Ó€Ý€Ñ€Ô€Ú562,#66%*.!0%#)+)/64152+0/))=)+++16*6+1'.(465,-5;1%,,26-%*'.-.&&//*6#910&1+-%&$'/%8)5.1*&1,)$2'+-$607#53#;5.3@3*,--&)01/*/02.3&;A%+)0.0$($1*'#*8!(+*0+.-+($+)*9)+1./9)+6+6(/ 233+4$*",4++$-$,5'$12/. *3.,9-//7$6 03/,$+&#%01.24/20,!*&80(,28*0(3--&%!+&.89#,*(#/8.&<3%-1/0%$)*+(,0), 6(3/)+52+*:'$')%5,;'#%&&)&+--$1*(6;)'"$+/"1#)7=$32,&#'&(.-!".'//'.,'/!)02-.03*%+12>)&(+.0''+,-85-(.$"*&')")'+*1*&.&3(-**&)+(18$-1$(1/3"(-(*0-//!+0'4#0-10*3.0$>#;,,*')&'%/3&(-(+8.(&0);2/)1/)7*2:)//;-( 5419&19)142-16$5€Ð€Ô€×€Ô€Õ€Õ€Û€Õ€Ü€Å€Ñ€Ò€Ø€Ó€Ø€Ù€Ù€Û€Þ€Ð€Õ€Õ€Ö€Ò/,//<&#"51(;.)1.'E/:--/.3)6(-#$"1-1(4//+59/7-/'*/%."*,((7.+/,25;/):1(3-)%)$)0/2012)+*+23('(%.0-&-) (%45&,&*"93-4;1,+4. $'835.#&//17)--&*7%+(26%1-3*-&+094.:$%(#!7/-''$#-241)-0"+7(/2*21"./+&:%.*"+'&(#*)/5&)*+1$:2%&-06554'#*--)/*%*$()&05%.%)%&1&&:+3+-20%*/50(4.7(8)-.#4(&,,&*/!-**'5,&81*3..%"):*('%4(4'54*-1&+*7!3!#)$-300'&910+#7*&&32'%+'1((1=-6/B/0$2'710))(2 -+,$2+9,4%<29-7;&& $2)+(*/60*0.! #+,+/84.'(%6032-&*6//%3*-1!4&++$")3)1&'9 .3/++.-0/6+1$/955+'2/*701,.+-3)#8,85*-0<3$*1-0-6;.2-9(10'€Ö€Ö€×€×€Ö€Õ€Ï€Ï€Ú€Æ€Ô€Ø€Ö€Ù€Ð€Ø€×€Ü€Ð€Õ€Ô€Ù€Ú€Û7.+-%--,7-,24))(&60511#)/23'&-08&('2)-'9069).)52+-*27+70/-#1/#/8+2,.'1/+5'%./0*./(0:*#*5(,)#!&.3+/ )1/2+,*/,%6'+/9.-/*1:"06/-((2--&)*, (76+.:+#''$#+/ '+?+/!1"%)$)+' +++'/5+$'/0/!0$"""*-.&46/9)(6:.&0-9)+).-1,1-5'+.$;/74+ 1&0+3)7)(/),''-(#1)!3(2$*'1 :1&,">)(2"#,&*/45"'(0%*5)!8'.2'.4235&&(4,2->)4'&4(-2+#92.-:')"/625),/3(264(-*)095)-).5,",%7)!*("0-16%/"5)80/43.*/%04%4').1*+<,'/7#"!4%%4)+.83<)&'*,,%);),/3-2.()/7(!&%).2'+$(57229!13&#.%7'+5..40- 8055")**-.10)4*"'+763*18"'.$7.37/'1!&/.84(-#',8-1)€×€×€Ï€Ü€Ð€Ø€Ó€Ø€Ó€Ç€Õ€Ô€Ö€Ö€×€Ï€Ú€×€Ø€×€Ö€Õ€Ô€Ù2'76)#+$(*1+.)/-,8.-+.+-&34#-#6+5-&&;'%;4*/$/30;=.6"2):-+)5&./9)$+1'9(&6410'';&)')7#(/%(1)%#)++2,,4133("33&!)2')!./#40-+)5,+#++5'37,1+*!..21!,($*1-7*..5&--,/&&0;-:)7%5(';)&23//4'*E1+74,%A3,-,5/, >+&)0)D+1(.44%&!.(./A%-*:#;7 ' &"6+12'2*+86!(+-*'+,)27.-+(+1?"5'200-4&-&%":.60049&93.,.)*0#5B59./3,)-3"+$((!.%'")*3$$790*,+(/$0-*(%55)42,@./-3)"/+/"'>5)(<-%%*-&1$//')/*-/$./&53((+)!3.9(6804'# ,%10$7,9,*/(25+,'+*2@12/2(.'6"01-2=*0**)+*53/-.,/ 35."(3/+4;9-<*,,)*)7/"/.1.2%*422.3'(95;/&21-0.1*3&50-€Ò€Ø€Ù€×€×€Õ€Ú€Ú€Û€Ê€×€×€Ø€Ò€Ó€Û€Ö€Ù€Ò€Õ€×€Ð€Ô€Ù,%0/(4#0583%."&20,%&1+0/'"&337+1+#746%3+,1,8'#%1;"*-$1(0+/;();)+%+2+9$11+&(-.*10,&<&3'-"%-&!"15-%.3%1&%6+6!83..&3%32(7'2)2&(*52*)6&4/6'/,6')#(1%&/&*-73+ &'/3*)4-(-25*'%#/%,'34($.-%#2342+6&*3*")1/1(/3+74&)1/-))1(-(-5+-0*2323*( -!+1+-57.22-#/+/1#),5%/-,/)%%(15(""&1*'13%-,)1.07(-.++68A0%*-75+-, )4'(0- + (#00( 0.537*7/16(+.,1+501*,4.6;21&A-,.$1 14&&#"->.**2,711$5,.1+,&027*77%$;)),+2-/85+"1'%-7%/,3'1% +!6(7(4$/1512,€×€Ò€×€×€Ù€Ø€Ö€Ó€Ô€Â€Ô€Ó€Ô€Õ€Ú€Ö€Ó€Ô€Ù€×€Þ€Ô€Ú€Ù+2/*,+-(,*:$13,/36?)55'/-%&')%&/-2--94)-**.$/,($1+)3+'-).+2%$+264)#4)8'-1**0:5-,,3('#2)1+-8'7&-7,-)!)823.2!-47,/.,5--=5,'6/(%'/=!5@/4&.49&+(*$-?!)""!&))46 )$-.(+%&/%@2/2,/,"%1,%7.1<-+-.+'+$-%(5..-!)$'3/'2+61$(.3.#$,")&,*+":&,*5*:'&#)5$-+&$6)' -(01/1/5.0,'0$'**2)-&/)3-+")"2-2#9129'*74/'&*(,+40--2%%+9#(<+!-, 8+A<53%02.-5('50,9/823102,%%;"$*-.*,0(66%,&1,<),/08(*( +"'-/)0'3)2409..7/>8".)(7(!.+$+)''!0)*1-$40-40-'+("**2.-1;1%/-3'2($+(, -2+'02/4.)/'.45)+4.-(4-+:).0.'".+3(-)5.$)&5&73&&1#"*-,$3,*7/9+-4€Õ€Ú€Ô€Ø€Ø€Ù€Ö€Ó€Ú€É€×€Ú€×€ß€Ô€×€Ô€Ù€Û€Ô€Ñ€Û€Ù€Õ0'*13)#*&)+2.),<26-;6'3+,12+().-"47#+642-(39<13,%$2-*-&'5@9&+('6/5"/0706!3 08&!+3,&+#&63,0&'#2,(*7#+23$.%(/2)-.1/5.+*6&1"(2/",.4$-40-../+2+!1,-" .*1)(/+5!.,-/.50)-..,/(!2B%92.'5$;!'.5),5-'(3%1/6.1,/5/'6206.5-"%83.,()*'4/431,0#,/*'2% ,$-12*+33.("#+.='*8.0*4.)**-6'/84)3/1--&//0/5.24(2*-,2 %'*-6")2/1--302,"/3$1--*+59.'/027%:./*./'/-.#((&5:3&$8D/1(-)51.49856'"2,'+/!7+)/9**&(:+()+%*/)&(0'--+4+84!,).&5+2**-,%5./.,&.*)*,5+31+:2%*--,#*(1)9+4*/-�/=.)/68++9)0+6233-*40-.532A610/* 3/.>21(2.-2-93€Õ€Ö€Û€Ø€Ñ€Ó€Ò€Ü€Ò€Ç€Ô€Ö€Ñ€Ý€Ù€Ö€×€Ó€Û€Ô€Õ€Ö€Õ€Ø?+(,'444?/+*#/5&,77,)11!/5&2!**5 4-57+.A///27,61+=+3&-17+&+'*'-))5&-.7&3/(2;474*/6&5,1(66-=/2,89.6:0,'1/.3'&*, //*$(+. 2*'1/.5#)9+3*9+<'20;*F9+30/*75+,5$,0)*11! +;+,4)),(0%.'$"5/2)+:7167/5%&#%(8!'2#9*).+.3631/1*/&:75#1B"+'357H3!!5&3%-.#,.7-$&$%4',@1%"2/*7%01?.!.#0))./&+1$(3225.5',>+0420#/'.70+*+'&3/01(*6%-*8!/.3-' 1.1,1%505:-#47%)+'-,4/#2.+1*7.-&4-136/42.+*(-+16+/,<9-0$28*-*.$/+,$# ,*,.+.+=/+%#1,(7+538&-* &6#/1+8.3)1%528+($4( 6%'0)<4,;'3,').24/!2((2/!-+433+/-./3...+3,(-$4*9.41..5660.)532:*;.8+#€×€Ö€Õ€Ù€Ô€Ö€Õ€Ù€Ö€Ç€Ò€Ù€Ô€×€Ú€Ô€Ø€Ý€Ö€Ú€Ø€×€Õ€Ú=4//64*4/,=6/,7-*';4(*7$0*2//771)3$)06,57'0'502+/8&2:'=/3-'9,5++,*;-300/,*8 ,01/*. :/6-%+0((.5>/9)**0#32/+1((;1,//9-!/%.28*//)/.20(+/# %(2.()+)'31=8(!;-)>5,.,,;-6&0$22),92%.*-',0)='/,+&&-2$5-(&,&$.)$)#!#&**+4&%5(,1'$:6-1..'2(#6(#(*;04?1+4);1-$ .56-% )!/-0+% ,255,' %1120.#*)?4#,9,1#0$+,/38,+-:./:2/ 2-:$/6/!2/-)+.+/#<+'3;0.4 '8.'1/!-+-&!(#>3-//9:-6-+2)(/(29*()&2++2(-&-/*.'0-''"(1%+#986)"2+0#13/+-)+?%289)(0-7)/*>-;.;2:7593.0&2#1%*'0&',*/4$1(,,#7'4,0.61+5 (*8.5-'0:*.'.,+'&.531/302&*2%./*/&€Õ€Ö€×€Õ€×€Ù€Ö€Ø€Ù€Ä€Ù€Õ€Ö€Ù€Ú€Ô€Ó€Ô€Ö€Ö€Ð€Ö€Ù€Õ2/>//.;-4+0$/:+%14%)/208>56611-(%"/5<*+)0+,)6764(&/'$.!>%"/!-'-'-#5/1,'/+(.#0&47;,(/('&5+)4(,!&/++,%*.&./5*&/0/9"(:'(3&95(-(2*+- *%-)./*/61(&00(5)'!(1-"(*$#.99.-%+,+-4#*0+"3)/,#41%./+0*2 1$&#./)0&).+2 )7($!%,#051"/52:+0-&)('#63)"5'//(+,0&-+&29.&+);9)*'45)6+0$%2'(-12$<)1*#,/$2#$7 '/#/-.*2++-?++"1(06")1&*.,(4-**(."#8%%-*40544*3*(53-+))(4:'///0)*))/2-28*&83,:*''-*)&-0>!+4-"(91(/2(*5B*5//;.2:10)"1.*($3%-#/+-'- 75&''//8##%0%1!D.:/8961+A#086230'/824-86-*13(0)3' ;((4'++(($,--656(%)*'+3#=-5,#7$#,0%/,'35*:011(9+/,/=&28-,-/8(&/0).:+#&&*%301)'#3"#'%401&)=2((9*!(/C-)9)0:/54,5*33'73-9)7/9655€×€Ö€Õ€×€Ú€Ö€Ú€Õ€Õ€Ã€×€Õ€Ð€Õ€Ô€Ö€Ú€Ô€Ù€Ú€Ò€×€Ó€Ô.,?72F61654!,+/$.(*/+%(17)B1.#*+%12-=*3../-+-4.561/1*"!093//8+0/1<,/='1)42=-/'#)%."*&.=@//2%9+'*/(5%7/*.'++%-9&-85-443/2**)"*+4590.256;-24%=),4&'+/)+0%-<#/ ++:./85,-)3,21(312032,(%244/'4*$)!034.&*.&.8 4=%+8"73,'2,#3/*&. '3&"+4:6)0-0&53=(#*7/'1$#*,869$/:4')$/42&%)A,$#&1)A))"(/*-+-/&)/%/04,6;:**13 #)/(/+.99:1%+$5*-')/*)2@,+)3/ %,)@(-7,,&09>4&'/$#15$/7::+3(,0)/&'35#:853+,0*%138(?1#")%45..&)%:--)/&..5*0*/&5.#,!0.1-7,'%35+1"0&2!+/&6-))/34+6$':,(/5"15+$&.'(11B/*20$0*+3%0#8(482.!114?-(1(9+1(89€Ô€Õ€Ö€Õ€Ü€Ö€Ö€Ð€Ô€É€Ñ€Ó€Ó€Õ€Ï€Õ€Ý€Õ€Ù€Ø€Ù€Ó€Ù€Ñ15%23(+@$69:1/@7,-$-(-..-')'744/+"/+56>%++*-+!..4+-+9.,*,71*(,9!,,+0-&!-)0'#&93;2D 72/3+,"6. 52//*45,+4+&+!20-/&/5'5,*!5%39(50%7*26+-*1531//*1.55')3%0 #&0,"+/)<%24'--2'0*,,-915208.%#3'-1&$%0$5/++*3&*$'!60-(*'/)))2*.5)*6-*5$%,53'&/*/2$!9/-!4-(3(1'(1,-*2$5(##0,-,/%(1*/!2*&#/'6-+$()9./+35650"+'**.=*///5+,-")'42*&.7",*1#$.&0..-0".!/'5/&)33(.+.3*$4---(2E-5537-5)('50"/)A+(#(*2.#41/2&28$3>+-6*+2'-'.0*7-.*-221<.,.#2"(&#-0 )4*+,/+261$-:##*!/'0,./153320/,/03'*4,!+!2;-13./)+83/6.3+8&-60(.6**6=)3;*2,81€Ô€Û€Õ€Ù€Ú€Ó€Ú€×€Ð€Ç€Ó€Ù€Ù€Õ€Ú€×€Ï€Ù€Ó€Û€Ü€Ü€Ú€Õ53/6*5+906/':%9//;.>*92)1<(*6.!*/!)++,%(/;%-:)%)#-1+"":&)%0!-61*/)&&+.(((.34/3--./*-(4%167//1,1)1#3*5+8#1&')/3' /.)1)13*3.1!:'.4&+/$'%+/++*2-/5',',%(/%)!-)0,!(*/2"*+,!5.(%52)//-1,2!/;,3(0%''*.)62'6173%),*%%-..*&+"(*+:".04#1 2($%16%$-63&/(52/005&(6-/*-8":+19*5/'#6,%'-('/&3*(4'(3+6!660-)%/2#.05*!!,(-(;20$,+#%6'26,'.*+)%''-8:@" /%12/-.':)276.,*/2=223%77>207,9,&4.4*3(+)*/1,/1% &;# +;-(-.;-0/"452*16;;+%&%4#55!07$3*,")-6&52$.5..$3+3-55+02'6*-0#.+&3%1*5%<5"1.#.% /**+*9-930!.&7+.2()2&,,&2)-#69#/2;,78€Ù€Ò€Ï€Ò€Ö€Ö€×€Õ€Ö€È€×€Ú€Ù€Õ€×€×€Ò€Ñ€Ö€Ô€Ô€Ú€Ö€×&0%/3300,)?,600.&('/+%,#/6// #62&63)-,1-$)9:3%!$31@,(2"-"<3*.,8&/0,2/;1/"+622&63%B+)-/,5/0)97-(4;*!9*5,&72*-64-.#,&7/2;3/4365!/)/&22&3-2%=#.+(,2,#&,(4/,0/'#)5$,$960,, 2)&*284@,36%1636>"+//+(+!2;0&+-2'32/+ ,)%-'+9&,."#0%)3'5"4(/#6-)9537+:'+-$+$&./*('1(5++," )+5.!#"4//,3+(3+1/#)-**/84"*+-07//'0@)()*0'0=8&,*))71$#:4-2#+6..)$'*))3.'+%,%/%4/,(((-86+%)*-*!(+23./33)(5 +*&(/85%'.#(;.),).+*,42,'"%.2#.$!/%$40&/)+0)-(8&1'91()3/-8/*6*9*)#%.'-,06/6-*22,1 =3,18+*(&4<--*-)1.3+&9>">.-910*.,.*64-+&'322+388&€Û€Õ€Ø€Ö€×€Ù€Ò€Û€Ñ€Æ€×€Ù€Ö€Ö€×€×€Ø€Ò€×€Ø€Ù€Ý€Ó€Ó91#**=//41(/50/39.(041)2%+)25.0%5**4)5)-.6+0;/5(",*%'$106910')%=,10+1*5%5'(/'1'/37%0/*,+ +-/-%6%'.-3;%*3&99=113&,(.366#01*'0>=*,%/$++/$&&!"2,-&84/7>%+$*$331/(+)4'+7&."(&(1?.2++!((0'&"0.+1*,0-*+1#:':$-7-'0'&"2"+1,/2 6(&1&-/-80:.'1/+$3/5+5*:)**%%*460/1@/9)13--0// %-662+*5*,1(!)%.6%-72=*,1(1.*'+5/,:#--01)+0(+19/,#)1/+& ''-3:1!'1&,(9//$6--,.(05332')+'1.2>2+(%,0+%%+-,6'94/25%!2;&-(%5/%&(>4(71/+-7;&-92.#+&*(#/33/57/-+0-/)(0'&.03/%&A2--)&:*!.7+&!.+,0+,'*=519,,0,)"*2$&9+,=3;+4. -1,8,&2*+'2./ 3'.,"/1€Ô€Ú€Ö€Þ€Ô€×€Ú€Ò€Ù€Æ€Ö€Ö€Ø€Ó€Ý€×€Þ€×€×€Ö€Ø€Ô€Ó€Ñ)856-:::9)5# 3/93/'*!-/+%*1'13@)"5)('&1%1=.1#(2/1.55)0)&*2 '',0,/=+!&;4%/&5,=)+C0+C$+&++(-/'7*1++)5(/(4#'&1,"*5725,%/-&=(*(*)&.*#%3"..+001*58"60/&4/5-*,,'3',%)32',8/''3'22'"&-!0&5$00%/%8%+,*3".)/0)1"1( ,+80#-+'.(8#/38(+)#/.4)%,0 ",13-"9',.+-*/441&/-,&+13&-5--4"///+"!( #31,1-/++)-/110%/$/$'%51!9%35,"0($84(/71((#"40)+*0.%-*6-(46492*&:-%1<6-00!/$&"*"+882$-0.+<)-%-5:$)62!-7+,'%+,///.-*0>18.'++$ )2,''.1'3-5#/+-..617$4+!,(,1*61+79"+:#!A2,.$%84,0*#*2:2.64,%+..573+.1132+0*,41.73>4*49-/..(*.31,€Ô€Ò€Ù€Ó€Ò€Ô€Ù€Õ€Ù€Â€Ú€Û€Ï€Ö€Ø€Ù€Ù€Ñ€Ü€Ù€Ø€Ó€Ñ€×2!):$/)&/* .&*$,&.'.6**4,2"(.+(#7'.&**,3*&31%4.('1'7'.02'%,'<,8*+1('.(+*),+2$%.'0%"5-352,,,/'=-+///)6%.,%&)3-41111335*/1.,,'5*2-#"*'&/1#.&++&/1#(-'6,%/(347/(4:,4)(%0/9,2("6,*-.0*)*,(1-":4-0()*2/5,<1.-5.)$/$+5+6+/)/'7/0(02-0.1-+/+ 23(/$,+1"%1)1+15",0)7%5*2+!.)"3D0€Õ€Ø€Ó€Ô€×€Ö€Ù€Ù€Ö€Ä€Õ€Ù€Ô€Ø€Ô€Þ€Ø€×€×€Ð€Ô€Ö€Ù€×3053:!@/2)5*3)'-#7-(20)%2%/6)/*&-!83B'-:3$%;,4(%(-*8.-%%4-(&2*7/(3,40+.1.+3+/7:!'$(5.4).?0';81")-&.()=9*7*2D30$.+.$+3)*6)"(73&65#0%.744#'$*(3*.+%64,*.*'+(("3(-#1(/( **12' +231)9>(/)05/6%.$)2'2/:0.*4+4!24 (*45*&//.17/-/- #/+(.0/81%&83'6+1C!*(30))$+2*%*6-02(*#.1+),$5-)7*+)*0")&&5&/.--)#+*0'6!;*00,(!-"556'57,+:ȉ++$,/$730(&3!9'-/1*02+*..10!/2<,,-.$5:/8-7)+/+/,15&4*'4.+1#%/"(4,-,(*$.(0%,20,2..9+$03.!.1-1.*"2A/%3/4%1,-;-#'2+20:#)*3.(+6///3.2-#=/*!)5-<3-4-,19:$501(7-$(&'*433-(47./3!8!?) *-1&,2€Ö€Ù€Ø€Ö€×€Õ€Ô€Ü€Õ€È€Û€Ú€Ø€Ö€Ô€Û€Õ€Ù€Ø€Ó€Ø€Ñ€Ï€Ú7*5;-5,/1-,?%,56)".%.7'+)(F1+,+%42+=/3,"=74-+'/5):(33*,+/(7%9)/'3"/&.#3&+921!3+1+=/.4'9(1*+-)'/693$7(/,&:/?%08/&+9*+$03+)?264+%0%7/0'"1D+1334/+1--'(3 *0$-)/3:"-$4#-.44 +/.--/5-:**6-"-(="%/'1+*3,*-.+--*,()5/*+/595 +-8'$$."*$,3+!!.16/-9--'8#.%.&71&..'+(3*/./++1.+)#3.)-22/141,,,/+505(.8"$3'#((7 *%220),7)4$)# .1&&-,81*/1),&:)(13;/%/+/482".-:(/-*7/'A4/ ,+0,3)58/(2201--3#(-A&11!:0 B711$36-*..,#@,?(5)813"*2:04&13-'/7:.2B+*54*&.)'8--41$0057/(0.%,(+()2+33&4:?2,341&7)/"$70/&*!5'".83,15")#"0.00--#€Ö€×€Ù€Ð€Ó€Ô€Û€Ö€Ú€Ã€Ö€Ø€Ò€Ð€Ú€Ñ€Ø€×€Õ€×€Õ€Ó€Õ€Ù+/>;%0?%6%+9.2;:$'+3*",'8*+7644,#/=,''0'1*8'+4040-0077,.!-;'-(---3.+0--5,"+/'6+3/-%30%02+6'1$/..($)34'4)#7-14..(/")!"0/$$(?0?*2)(2!,(-"841=%%.2, *)%8/,*(%4*5A(1*'/6%$%4.-4>%04/1%/1(*-),0-!8*22>&2+"<.6)+%+05'-'*.*(262*3/'"5-/$'-<&36-3-+/+7#3)0&.1%",()/./#/%-('!2 +,#,53+117#4+/1+'09,01).*&*1+<1%*/0*,7)%$"%,")$-$1(-2.!.#"6/(*02*&.,)52'$3&*&-4$,%-2/#;(-"3(7)+8+(.1812-!)?2*8-,3"04'++9-1%),#*0')*2,&%4#91+620-;)%#%6+$/5.--424"&,2'-)8:64*4%0,'(29+'2! $634,2#0:*<3,+)5C-#%47'*(,/5 *1572(3(1/4€Õ€Ô€×€Ú€Ó€Õ€Õ€Ô€Õ€Å€×€Ô€Ø€×€Ù€Ô€Ð€Ö€Ó€Õ€Õ€Ö€Ö€Ô$7>33(&+)+'1-)2-5<&-:#-)+&1-=:,*3)5).3/6,7&-6+-(:45$ 4:;-/?7(=5(/$1"-.-,+%:"&"13"7.,3445)1*3'"6/0'.8";;-%-%35*+(+4**'$2&*+'/"!3,!2$/'!/',+16,*5,,+%+"")-1#01)3-/)01,(,/(+6-#/!.$/.7*-*.1$/".#0+2-('&(9-*6A%":))0 #5')B(*/'12/).,08*.1.&$-'*#7.58)-$?*0%2%+>-./(0*.21-(1,/832%!/6+& !,-?-#'+112)%)/>+7'.6*.1)$9#+,*+/-59-+'/3)0$-,'39.*',/7*+1,.'%)528!'/&)1/-;!!9/4')&6&"( ./*(-/(5#/5%%+-(58()2$'7*(3*82,%1*)-*+32%.25D!.%((:0'8,$$",02%&33&!1./ '"&1$.-$/3,;$)(!,!.;5$B!%<7+00+3,3167.(-"!/-21);33,14<)5%,&+,€Ö€Ö€Ø€Ù€Ø€Ó€Ú€Ò€Ô€É€Ù€Ø€Ù€Ú€â€Ö€Õ€Ô€Ó€Ó€Ø€Ø€Ò€Ô30)61I,9)-1;,<206$/4-<,0-$).1)2/21-%7$-/#-(-+)( 28(9-#*1.-,*)@#1*#9.,&**7 ) **1/-09""5(.4/),'30&)1")&..+1+'*%3' -/1,1+')&1;1#.8(15%62)*/')%)%)+)$.1)-%(.*3'',+61&;,)4'1*>(-&6/(.9$,2/'88$<00.*.6*,+)'-40,1$+'/46-6,0,$$)%3-10.5-)%%2*0+.76B0&,($7:'&2'#)&).-.0*". +9)1+7117'%2-&'.&3&)+$0(;%&'/)6$-) $361+89-&0$4.-32'%2/3!'81140/&)-+8!/(&*)2)2 '6!$%&)+0"6=,*6732,+*%,15'1+7(&)*069-2'3,)42*'5,/'(2>-.7)1"2%5.2$)/23'+-/3*- 30''34--/65(@&'//*0/*'-$5)&4!-0,)-0/3&3).#1;4.#./'-24700,$-/--!*9#?.1&47*/%0€Ù€Ò€Ó€Ñ€Ò€Ô€Ò€×€Õ€É€Ó€Ô€Ò€Õ€×€Ø€Ù€Ö€Ö€Ô€Ù€Õ€Ü€Ù/+82%2;5%.)-I/8$(//2/:/4)#1):"51)/4,$,&9+&.*2,)3541/'$+")1$,:/;7#"+&*//3**5-&/),2%3',/' +1+'%46*39')61;%+121,#+&/55526',+/6+5.=10%93#%+%25+./<7,-$))"%#2 ,>"2%+!11%7%303.-/13")%2-%7!'+52-/$?(,)%,)-013)):&"8450=#-7'),**/)280,/%16!'055&3#("(2,(@*61$4%.).'%0&%!(2/&55*//3"3(43,8#8!$/-,+%5643+,<$&'%6-2+*";,8'+6!1($%/1716.*(4%**$)&94)2/- ,%,)./2(4!$/43%-;'.14'0;22%(*-/31*-(**.&7.11/+/:&*%/53"-31%0#,3&&1&& .'),%!)6+ ,-.8(/./,53'--++3 #5**-%$24346+)4&#(5%.7.6'#;7.'+42#(B5.*9&%90'%#<31)66-3)/+5'!€Õ€Ù€Ú€Ô€Ò€Ù€Ë€Ù€Ô€Ë€Õ€Ü€Õ€×€Ù€Ö€Ô€Ö€Ù€Ö€Ô€Ø€Õ€Ô545/-'(5/,,9051=.=&*)1/*373.+<-*&+3-+*391%+":'/)$$"6+80,7&-2+'):0'44/!4+)13/*3#"4+-)--=/-,7#2/8!)0/1/5%2!.)*')5,1./8$),45%>%$4/+1-+6/'+-/'2+),=(51#3/13'')"2+&.#,!#1,5.5#+(<+41- .)913*/0-2)4,-(74*2),-'@5%)'330<%#.2&<*&3(!3&/.3%?*47;+/0 0:&')$$+/77(--2)&%=&/357'%'*"'-!''/#,<*2.3&*"%, 0&21"0"/.;.051",-7+)+02",/#"*4+*.1*/4(*$/2--*//-)(//((/*,).,-('1/1&1.+/4*&-%00+,=!8)4--165-./3,.0,33+!(0!*((,//+,76%5, 4.*+*%&)&05;*)--!-.730(($41%7%0/.,)-4+++%9++,+%.:/*-51.*9'!$), -'+4@7,//))+-,51$#:?9/393,/€Ú€Ó€Ù€Ø€×€Ô€Þ€Ï€Ù€É€Ö€Ö€Û€Ù€Ô€Û€Û€Ø€Ó€Û€Õ€Õ€Õ€Ø-05:+&)6+9/(3$5%5/,'%)A)00 5'+*#2-6'!"0(. /(2&:3+$#9))6*;()*-*.)+/03+2-A/%+!4*)<"-,*-&.*8/!.(7(/&* .,3+-545.".,3:*)(/+-'7&412*-2<105/3&)(4-$7,./02-: +/53-1%::5#+,%3.,2(87*-,1$*+8:*/&/1&*)24'+'/0@-+'(;..!C()7,)%2./):!4"-&--8+0*'10)':4228914-1/'+-:+:7)/*A0.9/0&'+!071.%-30)-%17#-+&+05.(&/+,,+-#+5)-6&7+2. .&*.3"-/-87&#3*21'431%52,$//.#),3+02.(#0(,5*/ #-77-(60120;%.,/103(1(*/-#.59/=)0.,+(-,60/(&03$! -&.!-*.',&%53.&6':%*1640-*)34!1)+,3!;'474',)--(<'5/ *2&4%&.'-3,!8*&(2,=46""4,;--7>*@537&.8,€Ü€Ô€Ö€Ù€×€Ý€Ò€×€Ò€¿€Ô€Ö€Ô€Ð€Ù€Õ€Ø€Ø€Ù€Õ€Ò€×€Ô€Ö3./((,%0(.6)>),;%&%41=)'K5""/6'5/7)));&10(1"&/((+35%3.:)--<+/)3)1)$,0'-'.'*+&-*1*1),))1"-1+$--#0(+(/<.443,)+*0'&!$36)%.5&:,<4+*58/542239$%(0-#"7($$$'5/)21.7)%2//9%,1,,0&.1(3'(%+C)()!)')-,,*'"%%1/'3)72++,'7'#:/.-%-1..'3,*554'-,0+1.31:$".-&0.2+*)3'.* +-:*%*&,3(+'("4(/.-)@32 %1-++/8-/+*$;1(%'(*7./,-.+,+'&3%-+&2)2)!))+0,+'-(<)')4*,,5'/,23'6&9+4/+*;*33+2440&:)#845A.3'.)')**1)573#$6+2-+$8.36-0 )-+)*4#*/2"46!;&.%(5/1++&!0.#/(**37%-%0.4860&)5012"3))++,516$5@63-+5&1:3-1+5&=293/,3+/41$63=(;&'-'8,-2%#4€Ó€Õ€Ö€Ô€Ù€×€×€Ù€Ñ€Ç€Ú€Ú€Ø€Õ€×€Ö€Ô€Ö€×€Ö€Ï€Ô€Õ€Ö"4<52=0+&-40,/%4,' 7&#+-$*%'3/+,2>('8$1,'>:30*21*-/3240--6.-0 50=(@4-4. ,00.1/2$36!'1121-'%!.5<'+5*!/5+/+/&-.'7(,58*,)-,*63'1#12$0&.11!1)'* %&&,0,5,6+/+)* 9.&7,&:)-'-+0;$./,+!2' 0).4'+416/4.%@$/%%-1'4)1/-+70--59,-63#1,23+)4/.,(2&!2-@/-9)1-''() %;(2")2)&0$0'5.-5$1).'))-3*1%=1#8./+-6%#9#624$/+,*&,0+6'4+(6//211).5$+?*,")5&//(+4+%:'(22:.3.7%7$#/5: 3-+21,(1-+/+/*$/,/9)/3A923*6-1+/*9+'/B!/1+4++)2:'"/*0),'%4+"*3/!,--7*)1%)"4,29/"40%&",++4+)/68+/1-1$4:7/5.)30(&&-:7/33=+7+/4++()3.'1.6.#8*/630D327(€Ô€Ù€Ú€Ö€à€Õ€×€Õ€Ú€Ä€×€Õ€Õ€Ù€Õ€Õ€Ö€×€Õ€Ò€Û€×€Ù€×(#;3:73-?,101/<,',..53'#B)!*)32474'8& ,-*6$)5('.86"-/*)7'9$--+15-*50&+()"#&-,)44+-.4/4((05/#562).3))$'(.*<,%3-18)5)6(-'+-*9(63)-0#"5-42+,.4$/'/0(7-)-'(',B6+0+63%/(!/'(+&*$'.>(-0/6,!+)),)1/(+-'.+128,0&3"% %-5(,;/(,5#(%&+)#*3//%*27+)&/*0*/)',%72(9($4//#6'*,)'(1'/ 9&2-%528.)0->*589(-.37)4%0$-#3.443(,-( 3&-3<#3%/87%)17'2'*,"*!./+()#4%..'%+$+*$!.711)!'()-).'%+)7;-'6(+,-&%'7-.$*/-60.&)-5114$0*4-$5('(-)37**18/.*%/-,+#(1#=+.#40 ""*+.1,8088(/,/$4/,-*(75/3.$,!,,'33#0,/$''03-&&+*./%)0'+%/-*)/(14€Ý€Û€Õ€Ñ€Ü€Ø€Ö€Û€Ó€Å€Ñ€Ó€Ô€Ð€Õ€Ô€Ù€×€Ø€Ù€Ô€×€Ö€Ù7+%:%)3./(+67)%0!-3(+3,7'&+*0()...3<2/7//1--'&&3&''/,,%7(#)=070/-#-/+'')1+7=*'*C*//+* :/3&)&/1'+/.,+3>+(=-,,20)/.0401$,+.&.3$/7.3/#',0/#1340>8%)//!%.&)3&%-**4#0/&2!7)% 4/.#%)*(87043>*84==1-2,60177*36..*'-+3+15(/%*//,#+5#83*9,)8.%)(5>#*52$1 /.+*/6,4(+,+,1$+!*$4'-)(,33*$-(,-+5: ++./(0).,1,!20*/'0)4*"33-*!* 1463%*/+-B8+4,,%$-3%(9,7.4..3%#%$A-/&4')!1#+'-)(*/6*#5061*,-&.+3/6&7)*)31/6&+?--+*28*,(3'*7/$/+'):2,;#/#+,38'+&642&9(522%#4/#7'.)1$%,%/-,-''")-43+9:-60+<)09/1#&9&='<%=.5410/>:$"$42.5:<0(1)5€×€Ô€Ø€Ô€Ó€×€Ô€Ö€Ò€Å€×€Õ€Õ€Ó€Ù€Ö€Ø€Õ€Ó€Ú€Ø€Õ€Ö€Ò:-*3-*6,.(5%14+7+01.8+1*C,0,%''+4*(,,/.)0'-'<,('#.#,%27"798/57#9 2",-2'$73,/161951)+*(3/06)'*3,2."60&'10/90.)1/1)3*6'5+.40*& 63-/333,&312"7+3432.%.1(,/(4(4%:+:*8:)5*+(0+*8+-2,)9.3-*,)/7%' /+,--7 (4*:.21,?1.,(69F+"-,$+:.;8&;;2"+,):2(7'2+/."3%3;39)I-!%-/!'*'#-/$<2&+."*-!+B&.4-,(#)(-)3'.413+(3//1',/"'((/6*.03 %0-,*10%1"'+/*+)%*.5*53/172/5(B-(,%039,3',91:/-4 8'3,37)$10;%3024."",-2,5)'/)/1+'=.&+++- /2%*##6%''+*+(7'%)(43&=+1+,20-11*90)02'3-/),),+-&; +9$,(377*+(82---2*=1/.4/*-/'9!7+/0/&02B"&2+,<€×€Û€Õ€Õ€Ò€Ü€Ñ€Ø€Ð€Å€Ó€Ý€Õ€Õ€Õ€Ù€Ú€Û€Ú€Ö€Ø€Ô€Ô€Ý,/&7$0+.24+:./7/144&)44/6-30.++116%-)(+$;).0*(/+'#'-75'+-(""!02/&,6&+$1/1,,/1+/10-+.+3313>B+,/';.0**#&-2#, 46.-5(/8$<-4$+.8*6&1)+*+2)-/3?1'6&1,=/!'+*2/+))2,/5%1:(')(2'33,3/*0,,&!00+-,8+/4'5,$%6+4+%!)8/ &+*4.7#*+)$*%3)/0//$3%,32/##)04+"'02&)<:(;4(.'546(+4)5*-%'*7)5)!$(2(4"5*8","+23*//.-5*4%+$:+622/#,&&-&21"'%+-:".*&'),/#1(8' -0).8*-1&//5,0 3'-44)7/- '#-:,(',/*/,&5')3'*!6,%-,/&)7*+0( "/&3.")4*<1#2/.&(-6&(/'/&.@4%180,&'-!#-(52*)8%+.-."10;).#46//)/-1*4*4-16*(''-0510(7&/(),.7.-0=%+,+644$<;-4€Ö€×€×€Ù€Ó€Ö€Û€Ù€Ó€Ä€Ö€Û€Ø€Õ€Ö€×€×€Ø€Ù€×€Ú€Õ€Ù€Õ8++-()>&7+:,29;1-80,56.#257146-29'/<-+#2..2,-/#1+-)-18()5/-5+30(%.#*)(>*4#)(1.:>/4)-7+)"/2$#3,#-(5//62*'5$'.*%((/+(3?)+$-7*/.0.B-+':58'02.140*3/(+'$-&,)./.80)1*09#%30-$1#='.$63%1#**-.)-/184+#(%77##/)1+0))--,30)9)24&"5'83&*1-'/;/)$+(*&+(*9&+0,3,8-(.8//!8)+,833/1)%7/-2084);,$0<-''/3((.+(.4-+*)2+'/"'-+*,E*%2*8*%$!5#*C45'%+&'')2$)49(43(0-5$1'+-(3!(4!,/325'''-.#*+;(3:5-)"/3-%7!)22')*"$+&8<2/,"0151324''6,,3-'() +2*1700.-++5;--+3-"&,9*(3;, +*01%50-&2!50.03+<"+.-*32&0+5!/*)/,+06:%/$,*2)3%-):<3-8)1%.,€Ö€×€Ø€Ö€×€Ø€Ô€Ô€Ò€Ä€Õ€Ö€Ø€×€Ò€×€Ù€Ø€Ô€Ø€Ø€Õ€Ø€Ù*7005',5-00*/33'5(*+,+60112)0,561*.()7$/6.305L%43-1!6)7*,+)77#2(-2."2-.(/-(5-"-'3#)$5:59-,##*3*<6".13/./1,2).*#$+.,43/'/' "+"0!1')!!(%%)5+1,..0(),0'/(<,2/""/!!16*0,20,7325'.+-+&'5$,2725"31-,)3(5 12%-/,/6%032$.#36&:#-3+*.),)/C?'$.(%,/6105>%/(52"#.16'+A-3+/$.(0-$7-+'//(&&">/$)?*/*.'!',5'.3/**.(&.',-7(,5127028&(%+'364/,*#&&52&7$11*0+.1%%2!0+/,&27*.(.5!'6-&,);*(,-(7,5"(&(.,.--750'5786)2%3A+%. ()-6&$-#'0/9'.);3++/1$*,;4!),')*01;/)%;.23)7!+-,&(%/+30)#0'(%!624*- :*%>%5.(!-(15(+2A-8:*/&).7)=(,+:H,5.3--3*028€Ò€Õ€Ó€Õ€Ù€Ù€Ü€Ú€Ò€Ã€Ó€Û€Ø€Û€Ó€Õ€Ö€Ø€×€Ù€Ø€Ó€Ð€Ú//4,,.5$52(;9/,''0&),,)+&//#0,*=6,(96116%'++%'/*/,),+)//15))5 +%0$054$.)%()=#/.,-&/$(1,;%+(<05&*4*-+?&*+5/*$0+7'*,4*&,=51%./)*21.)///#,3)-(#2'3,!#23*1/.+0.!+1$5+%60;2-+'*&-3,3+%(0.*6.))&0'#&*+14-'&$460%+0*/$/)0)1#,&)02""6(*!4-)--@6.2$%5+(.1+*()#,-!'C$440/./(2C+(!1,+,*6)0';-.06,.)(+6.002"*141?)./-1.%0(%56,-6 $),73F3%*12/0-451#,072)-6''*+//%3-3%*%(243/&";)4/-(%*03.))"-..0#"//+4(+6)/,$-%1&/+6.-).+'')(*4.6+'*+'=%,%(2(+1/(%0+1.(/#+01%50)/ 533$)&/--)-*0.*187&)401>%-3+A-9=1'.%1--4/1+'-.6+921'34+€Ö€Ï€×€×€Ú€Õ€Ò€Ù€Ö€Í€Ö€Õ€Ó€Ü€Î€Ö€Õ€Õ€Ô€Ú€Ô€Õ€×€Ñ#;='94%1*05'+-/*121#942/-./1*+0+6"&./-*.&*+"6$('/%138'47%'&#/"563(*38*+7((;.*$)'-%+2/6?,/(37'9%;06!8'#3+'0,.%/#&."/(*),,,')123.*0),+,,%&',2%**1+./.#3,0,9?/0'1?"8(/&'+2+(+*.:)9/&,72(3=6+*1!*.+%#'/'*!0:0)&*!0"-"&+.+=4.*+1)/'4+,+;)..,!2=#%%$4+&//+##14!(:4.+3'.""3!#(.,).-(*#1=01&&104&5/-510%2+527+7*.-8!(*+#*$.#+2/0:?95 ,<2%,4/1"('6;8.)++ ,9--+C.(/$843+= ,/*7/)71."+*(-3+5,*0)1,&'*!.'&%, 7:/.8)%%60(&06-20/,)(*2-&/0.(5&-61&)7+(".43=/9&3)<).)36/&((!7+..+$661/%!/1D$('3&9.+$/5*6)*-4)*(23)#72.%;,+170;€Ô€Ö€Õ€×€Û€Ö€Ù€Ó€Ø€Ç€Ù€Ú€Þ€Õ€×€Ø€×€Ð€Õ€Ò€Ú€Õ€Ö€Ö !'1*27"/.5-2"!*7!+''0/A,*(1)/;;2/#/.5C2(//323,3<21 (.,33(/(--7'/'.*5*&2(0./.+*3)"D1(.$:-147)!+1,)&/"'"+$&4*43""(%=+9115"+1/+010%-1(#2-4B4+'44*#")&.$1;.03 -..!4.3%))(8().3/$88'#36%()5"!-&*3)5/*0& )'&2(%*!.05"$';5 )-/'(B5,-/2''2!/00!.,*/+.,3903&.'-, "+-2,-/*"*./&**+*.- .0:-0++4+&<1)2'#'/+4046,2#$$%/0*063,$0+$06)-()/.. (/-5-(9953(=12F&&6.+/3+1132+5$%.)(!,;/-+(!06"!-5-,-$!.1()&2*)2*')1/!#*5+4/*(2*/7:3*2*$*('*$()"'5*)-0'-9/+3#0-'4%01:3!"*03.,(&)#%.5*50(/,!9/"7-.53+68.(,/'0/.&=&-61,21>;:1*1("/0€Ö€×€Ü€Û€Õ€Ø€Ù€à€Ò€É€Ö€Ô€Ô€Ø€Õ€Ù€Ø€Õ€Ó€Ö€Õ€Ú€Õ€Ô:-.60+537+//++2/&176.(/6@74*.73+01,93#/81',*-4-&)1*<'*#'5)-3,2--&7,0)2-91(2554/.8+0$)*, :4)-)#2+0.(7+4"070/+/)""%.,&,,++1360&"'0'0,1(.*271#479(/1,$,/14-"59/'1-.'0%0#/+,,46)4*4#)$-1%*#+ -/.))*+&1(-&,&*+,>('.8.&2*#+/%+,12&$),0*),)+-)#<-$',%7!%,0A#;42)-*:#,5)8.+$2/3-+),60?).3-,-014-.,*",.&&-)'4$,.*$1+,''?/7/,(+1,5-(6,+.#611(4+01:+,0//&#!'#!/,5)..0&.").;03)91./)/3))%(%//+*,34+1.+8,49'/*--,,...',#95:/-7($3'%)/+(0-&'70*-61;.*/3%'5.)3(-620).728(*'.=-1845($"(/)-3>4:+(25')/*)/;95/7-7=1)+;-&4*;'1€Ø€Ö€Ö€Ú€Î€Ò€×€Ó€Õ€Í€×€Ú€×€×€×€Õ€Ò€Ô€×€Ò€Ö€Õ€Ù€Û5-).+#2-0-0'6%*!/7(+')!'0.3;3.-6$2--)*/'/-2%/ )0(<67-.--+74/-+0)1*A017>7.+"'(1)1#41110"0+3;,*-$.@;,=+3%9%9&2110:$-'-+1$& +30#$+-1-// 3"7$+#-3&-:"+;.0')1%"'4.2+)0;5",*.05#*">(".0'2-'+3"3*-"7'/1.#+13)-2!+)*3!',%&*:/>3%&,*,%'0/(+-0;*1'***%&/53)+/24.74+&-$&+-5)1&!-0'-=1=4*58-26*!)<'-;,0/*4+28-'(6&27/,)/1*$2$&%)#/)/'+/-)&+614&0-/,/7"( 4+'&+#-/0-%,/!)--;63:**,#8<(&.?--9-%6-,5*.!(#2'-12/2,*6-1,91.09&2# 2%&'/2+#$.)$81$1%** )(>(!)4)34 &5'$5('6.'')--2%+ )4*/'%'()37)+'")1'7%6A.)#%1 7* *-*5()/1#2&!04*'€Õ€×€Ù€Ó€×€Ö€Õ€Ò€Õ€Ê€Ô€Ö€Ó€Ü€Ò€×€×€Õ€Ñ€Õ€Ò€Ö€Ù€Ü26-12-%/'=7+'8$)49*'%+3,,279.+"/5"712$*0?100&/'7$423=25'35$94+ 7-)%:--59>%/571#-$.*:/.!$.+/)30&4.)(#37'0'17&10%3+&0&2-00&&'0+;%/6*//2#+&%"+-#*'1/$;#25$/0!/1,./17%#,("@%.+62(,#//+-:3',.-6+'*71)' 008'&-2;)7%#33+#.7)0)*5#$( /*#'(-5%C*+:.&-5/85$.$,3)98&*!)+'8&-$42&*2--**"/20-//-*1*:,$0*,+,''--28%#-$0,&2&70,()-/)3+--'81&.#.&&B6-','-$69,+&(&/-).2A!#"1(/).#-0*$&7,(&+4("))(*06-'&0:)!13'*8--+*/89,425#&0/%).,+22.**%"&,#)("641&1+56(,#!%+-(;)/:,$:'&2/2//',*-2.46++6&&.-*7,%%%';0876.-.6$2*-160'=*/7,€Û€×€×€Ø€Ú€Õ€Õ€Ó€Ø€Á€Õ€Ð€Ó€Ô€Ò€Ö€×€Ö€Ù€×€Ù€Ö€Ö€Û/4B74467//(=%74:9=*67.'/A'+'063,'(55+(.A2"=4&B+%5/(2,.(/(474 -0-3/>330'+0,*&+,/,9,5<%.0.-'1/"01,'025*95+=6+$%?3 232*-0++/$542,.+,@*$.5&29,'5),& ,-1+0-%+0)*.,-6)%)"/)+.% '$$"2)260);$*-)3' .)3$) +"(523"#3+;-)/'$2+-:4%*)+"#/-)040"2:&15#$&$$./03/!))5(/.+=)1,A&/,1'$7*:*//120#.4$(*/$*&5+.26-6-7+--90(/)//%%*5+)&2(+2*6*6*//%&*'++%-37(()+1.+2*%+$*-&-+1'3(:$60(0.6$3$8-.619<,+))//");)*"81#)$1)+-$'*+-2-)/0'2<+./39;0":+( +*),9)+(1'()*+/*21+--&#*-0 ,7(,.2(<&0.05+%+,,-B/,193),,.(4$+()1&22,)25?..8.,,7*,*00!2"0-€Ò€Ó€Ö€Ó€×€×€Ö€Ö€Ù€É€×€Û€Ô€Õ€×€Ò€Õ€Ð€Ò€Õ€Ò€Ó€Ø€Ô+#.2%(9/71+&-/0,(-0678&44/%4,:$&//1&=47#313100,.78+-$1/!0,<*)"21)+(&4-)49)9(5*(+%+/<-+902020$:.11+/)!)*!,$-%2+/$.2?/35'#55+&40*&7+-.'15/55';)()41)+#,%?+)30*+,)-(3/%08,"'3/+/$%'$'#(/"--6!/1,%:0*+&*--0*$"*&15-46+!./)/0 !./,3/(0:0,2.&'0232'/"4171+*-21/:2/10-/,%354.8/.8!)4*(&(2"2+.?8!*1/+46/%#1100).+&*+741144&%4,,*.E+(./-)*,%/4:%.%6%1/.'-26,)5!-%1'-.+*0('5/+-()&*03>+;)('3,(*+#)/'&-.210,&.1-3/'5/#"*%*7&$/;$5,+,*.//)8(2$'()1<#/,07(00&&-4//.)3,44-55 . *)6>(94/$-%)4,,(21.:,(7+5D2+-49/8+.7*.,)5€Ö€Ò€Ö€×€Ù€Ñ€Ù€Ó€Õ€Å€Ö€Ó€Ø€Ô€Ó€Ý€Ø€Ñ€Ö€Ó€Ó€Ø€Õ€Ö4412;&225<'1/3/'!73./1-,&(1=:,.06(;!-;/&.4-&)74#*+'&1,1*':)&#&")(405:''4)--5)50)(0=%/+0,*"1?11)<)90)%-)69//36+1*-0*'=,11,:. /+((&1)+8*,8.33(&/'!,$$3+8,#,,#(*'&&--2*4)/*$/5'-172/=(,,&-$#)7&/+/+5-#&($&)..2"=0+/42+3%)4++2)/&3## %*E1;-.1+7 (*'88)-'4<-.3&/4#48:)'=4,//3+*3-)-/1"0-,(.)$"6'32(#13)3'-,'-$43*"2&,(-,51&7%-74/&$1.)2/:+,&+1 5%7((,/3,(/(!/2*%),4,0151).$/4 7>&'2.3($%/!0-1)"1*,# "2+1( './&-27/-3$C+461#-),3"'6(-,.(930.&+ 0/-6%5%-50('73(,-*/B-,.!230/3)-)5*3,52.1./5.*(.,4.0627,5/'*/591969€Õ€Û€Ù€Õ€Ù€Ð€Ó€Õ€×€À€Û€Ð€Ø€Ø€Ò€Ø€Ó€×€Õ€Ø€Õ€Ó€Ö€Ñ,51;371'.2.+5+,+2/%'32?(6''*D/)/)--5&0<404+674,0-0*%3",(-0--!-11(.2#0)2#>/$/3)+32/-!1-@*"%/-,!**'')&#*3(,+0&,+ *-(/3& ,56)/1529'#+'1&-&.'+./3+(1"()1'24%/%5(+4,63./&*:(."0#7%75%/')'.,-),&$9)(*/%).(+,1G+%'+(/#)5+,76.+.*())"0,7.21,00-+',,2-)&34#1.,)4.+/$-"(4-,+/*547.,%%+=1/,'+191/#,*.(,/*8'//*1&*5)%*++$;: 00/(/''#'*8,32!'-1/";)6* +/0/'!3,/7-'+0-.)+*.4.1#&0$&5*+&6%1+80/5+1'#41-1-*'.-!-*0&/$/,85().+6,(&*'2$5)"5$&--./"-'-&+.,$=/*410.,1&,()',32"%+729$&002(307&:41-) -203A=9/0/+/>)+).:5591*:D/,314(/9€Ô€Ó€Ñ€Õ€Ó€Ó€Õ€Ó€×€Å€Ó€Ø€Ï€Ø€Õ€Ö€Ù€Ø€Ù€Ò€Ñ€Ù€Ö€×2-5,1"15"&4!=6,16)$);0,21061A0/.(/;4$90/.' 4+#3702,2.+4>3+1+)(%**$4/**(',)#*-&2-2-*+-;++0'<.*/30-+,&+70 *.#(#88-&4,+&&-&(@*0-*66. 7*1,#)3./,;/-#1*&!#(*0%)#3/1,48/2.47-9#'4.3-2/-/+86-#$(4$!6+2%1.+(#$(*(43-*(%50%1081,+3&+*3))2','3(9&9;2)'++%4/.)-'(-"&1)4*"$3$0,42+82,7'(5$,';*4&)-()'07,*/&.6)*.+ 3%7+&62(75/,.+/1*+9;"+)&-< )+95%:%$+/.#-21)!)11/09&++.+*:< 74/,'3+&$4#.2(0)/0."''&)"(/2/))//*-$)7 4,4%-)44/2!/3+'0(*/1+,./0-/&8"7)*-,)0.285(,885A)%6743404&,,';/'&05*5:7/0/32270$/*9)--.,20/;+5/98,60€Ú€Ù€Õ€Ï€Õ€Ø€Ú€×€Ó€Æ€×€Õ€×€Ù€×€Ó€Ñ€×€Ú€Ù€Ò€Ö€Õ€Ù+2;<(1.6+/4$1#45$=-7*,.'.045.!&%#,/")+'3,02;2)//,/8/3B3&*:2,45&52/782.(%5)7(/0"'"-729+&4$@.1;4'&"/.1'0&)6'$9%'328+4<%%%21952$-'01$++0(+*2$A6"+3""4(/%/+$)/.$932*+,:!)68"'%74*)4*+$ %'.-29&(((*$!70427)'2.3("//$:94(9)16.&&/-'25=(#*/5*1/+,-5/#5& #+,,8271,*-".*!+/$*&4;,'1(62&/01'/4&)(7 8/)'300!/+9 -53/60$' !,)+*)%2-))!"%-+1<2(24-%5,+(50;1,932-0#"/3#,/-4-,".2"+-3&+9*-,0!51&6,'*//.#(7(/-)024/"*&/-3)*/!38:36()A)!*2$#/+#''0%,&0+&,*&.$'./$*)5<(5-0* -//'8$72(.,/!+5/75) +3//)#.+-,;.5)%*/,34/'',0+1#A'/*22€Ó€Û€Ú€Õ€Ö€Ö€Ù€×€×€È€Ò€Õ€Ö€Ô€Ô€Ô€Ò€Ö€Ù€Ù€Ö€Ð€Õ€Ö>./7.+7&'">0+>,&-0,/897541)$:"%,+<1"1+.:))+/:$)$+,4*-(3.#*,(%**46&.++ ,*2-'1%*#/3"/1&3$8+4&(.03)8*(&(,-:,*$*-#18,)62(",6$>14&!(0'3472/*'2/)'%+#!$%983(-*11'801"*3;%!<'.+0&& 54+#:-.56/1403!2%4:8%5"/#+(+('2&00(,',*,841:4-&80/ 1,+& 2:/$$''*2+(-//2"#36&(22&21/3-#5+(*-=,-+&0*/+/,25&;$;%,-(-#4%24-2/( ',+-511-0"-$*,1*((/34/995$-,,.7',#1, (%-5)+%6?(5*)0)0.-).2)/&:2*--"'*,#/!6+)+15,%(=$)43'2,.'(?).54/'(.9))?/102./50+)9))$30"'7*4.' (8*-+-#*2*/)+"0&.04"58'))-.--3)5#?$ ,5!435%+/#$/;%5'%:#360/,-6C%&€Ù€×€×€Õ€Õ€Ð€Õ€Ô€×€È€Ú€Ù€Ô€Ö€Ø€Ù€Õ€Ø€Ñ€Ø€Ù€×€Ð€Ö-(10777./9%49:105*=7459-+%3&(1+&42&D2(2 5!.44)-#/'++,-11+26=+*3)2/))/4*+2+;+$;.2)$1,795-%+2'*6'/3 #47-9%*)!(#3+--$82++*6-$#$$4*5'0.'+3+604,0/(+&)$2++*#)/'(/ - -52+)2()4#+.,/(/,1,(+3'/+.+44/&+3")&26"++*'$'49#;/$-'*0+/*!+45./#(#)+854,-4%4-$(; *#)-..:".2+!-/53%5(,-6(6''-.1%+3+.'/&"&*'.)7-#%/62$/22'(210*--3(5+ /,.-+18%4(!=3+- ''*/-/.$3)-&*/3%&"0)'740/5541-$4:-4-33#,044@;.'./1-%',;+/2%%+ +$0!.4-($&.2& ,:03!*.12$&="#,),)7&-484550)4.%<#19)#,#-&+39-+(+6*(5/*3*3-*'6)!+)&'B2<4,/-9+ 613?)4+13€×€Ù€×€Ò€Õ€Ó€Ú€Ö€Ö€È€×€Õ€Ô€×€Ø€Ú€Ò€Ù€Ø€ß€×€Ù€Ò€Ò<%5*/*)9,(6;)(47'>0)0'!1)'$)5-21.+-"//*<2.+33*'.80):352+/)<)-/7(.*@(0040%5*,7(,/4/**/1&&( 3/&&0.,+)1*2'*).(%16&1+/!&.!95#'/'+*-+0-(-'/.$7%'0*7--12:/$#-2+ /(2/-2+(?0/9*)++")3.0*+)9)4.&'6/0-5014&6*-).84"02&*19;+%2--,12&/)(,+9$:).-/*,(.&-3/.7),52*,$9*./5,),!(-,*(933*6.%722$'-4,,'+9:(0+,):7/-!/,"(.#,,71++.68.&&.8/ ,7%2,015(*$-7>62&6+/-/$28*')4)%1+'4)<3**/.3!5/&,*&*'-3'1'4*,+5;&)$%2)+&.-%/4-(/!#'$(-,.,52,@1)-.592'.-031#+%010:(+----+.*/&"'/,,*=+'$/G1'/4-)6+(24+-6-+-93*.45)490(*5+:,33.7€Ó€×€Ò€Ó€Ô€Û€×€Ó€Û€È€Õ€Ò€Ô€Ö€×€Ô€Ö€Ó€Ó€Ô€Õ€Ù€×€Ú75*.="%1#.&,%--64+-B,0:-+30+'(#-=%4(2/127,145'89-349**%1/&)*/.5)34/%/.-+&7(.,+,11#%-17+*,3%+ $$=--4$%)'A,5240%*)2/:%$$/',''((+-,!"4()9-%*/52+/*+/)0,$+('1//09+*9'(-12.*041(-+9)++&'//1*51%8+;.!71%1/)9,'1(,(11#&/!/+#;/+/18%2/-,'+$-%)4*0?,)*1/+%5)",#'-7.<.,+#<$0&)-.(*.47&/05*8$$+/& 8/350/*6(1/#-)02342(/-/#,,4',%,+',&.-64-&0/#.:!#(-4((,/)%)1/,>*'!&/)2*.8)(-,3-(8/&-;50=1(,/.(**/1%3/'7-23#5,2*.,A7042'*#28256)1&&(37/47%3-%/*)'&$,%*,)94(&&,"&$06,*-62#5'#(,-2(+051/ /*2,,30(.301//(/+-)!243,/ -"'/$*)$.3%,6-0'(<$( 65%**'/(,,8-%)1#).2()1/+8"&)7*;0&$1//3.$."*01+:-01 #2532/%4>0;2/0/1(;!1*$--)'.,/('4$%(&.+"(0-,-'+$/5,17)'22.#0*3=&6#4:(':+2/*+4'++!&.'..,&-7-1.,,$*7-&..% +/(3*).50%53A%,;00/30%,/*$3/2#,/2-3#*3$//#8&+!,5(.(.+)&31"+*!/+,#*4%#)/45/'-*#,4*8( +1"3').870/*+,'/-11//1*9)2!+'(-"+637./)€Ö€Ñ€Õ€×€Ô€Ú€Ù€Ô€Ù€Á€Ø€Ô€Û€Ô€Ù€Ø€Ù€Ø€Õ€Ò€Ö€Ö€Ô€×:71&@'8/77*+,$1#31)7-4(=,3/86.+'(-&4,2%%$4+&.(&%6/)0?*?,%4*A9-2#05)7#:"(2193/1!/1.'1217 6)*05-./16>..($-1:(/:1381*(2$10, 3-(74 42="'!;&+#2*# 8*1/9&!$"*(:(*8)-&5 $=++()!4,-<+)2"%-/#-/5!'"-201(26(3,/).1+$-$2*521./56-.2)-"(:).(,)'(*+%%83/!=24-3+')1#'.*:)41/$)'$")/*%: 1&/-'1(-,3)0#&/)0+',-(),51(.1+&,0+5$8* 3.0,!$%1-$3)#''#5"'".,8)3255+>.-%"73,*465+)+'!0$0)-+&(*'0+<,(/0$!($&!)'94'/47.+&-)00-(+18%//.,/(00,/"677,,%;.(.)1$*)+2*-+),2,90%+8.+71'*31,3(70%,8)1)3'-,1,)0?8-04/-01/'045+(/3.42)&@€Ô€Ó€Ù€Ô€Ï€Û€Ó€Û€Ó€Ä€Ó€Ö€Ù€Õ€Õ€×€×€Ø€Ó€Ò€Ô€×€Õ€Ö,/513%1(%0+"326-*6*'(2)1-)2*(.46#+*-,.-#00#$=2/+4)4(%.-=)-+..*-0*(1-4/76,07/+3*5"+/$ $.>*%6+4%(1)2+''/!".=".2!%1/6&<3'3+624'3&&+)"1&'1*'%-412,6257=3&65)$//#()2,&-<*$+.1"*-/'"*74%,2+;(&-//.*(0/+(8%&(.=*@'//,*;,-.26"1**)(% !7/!+2!..#15)2%=*0-,&)(!3)+*''1*& 09/215&%2-1)#5'/4*'+7-3/1:,&+@3+/32110*'/+)45$(,-++'/0"6!,4#.-#@131");8/-429)/,&&-!6.%//>0(/0%6.('4(-1#860(&&*$4*!%%&"/01.(+,(&A$6#.(($"7+1,0'4'2:34/-1+)6**--1/.+&)09'+*/--('43/5,11%&&,+1->7(3!!.-/2/#).1%#@'-)?.,,.+)/-(!-)+06,+ 87,2'€Õ€Ø€×€Ñ€Õ€Ï€Ö€×€Ô€Æ€Ò€Ò€Ó€×€Ö€×€Ö€×€Ø€Õ€×€Ñ€×€Ù741.*//!+!:/"%(+"&7*1+47)0,(&4&'93,71*-2*6.825/<!$2*&*3<+8*/,-;&!&B(*-5 +(91*25$+'/+*333 /B 31',/(8+"23#/(+4*+1),0)/)3C0**/&<&/2!15#5&6-$46*&//.&&/*&(3/)(+1+8(-&&4$,&=*8!3)2+;0=:7$./+-50+..1,.,0 )2.+D'1&05)31/67,/-,#,/0--11)*2$<)4$3$73'2').5.((4%)-&.-=1,=(*;,2"'; 1/3 85*5/64'':0,-'+ /1'!$#9-/&7*2(1.%+-+.'0';)8")13"*3((4"4(//(+&512/1.51.$(#<%+4$%$)7.=2&"(*.3%*/7)&#.5(9#)9*/&$."7*0*&.*,(6++*248"5/)),-'%-0(,.((6 #0+52:3-)0*+*'037"*)'*-&'.)'35,5+3-/(4%)/!!$0/10&J,2,399 !)/<53-0,*9*-(.4(+*$)2/+€Ñ€Ö€Õ€Ô€Ò€Û€Ø€Ø€Ö€Ç€Ð€Ô€Ö€Ô€×€Ù€Ü€Ô€Ô€Ú€Ö€Õ€Ø€Ö33%<+/21')2.8'6"1+-*1334++-1.%>+&/+3#8, !,#+')%-2(91***!5"%*-/,'3*-0(,-,2'.*9;/"/-5'')(82;,,)-;-'9/'&'$+34'33)'.607(-1+,7%/(,1 *#&"()%'4%!-0.*'-),3'+2."!043,$,-%:!*,+)2*-,%9(H $$1,..-'/)8.'22%*3.*2'(4/.9$&7!1='7('11&+#27"9-(.-36://+:"5.(6,)+2*2#-!#1&35,',$(#++++!2,5*,'31!., %*<,<.,33)+1-*)$-$3'-:(,8./%+1%&(3,%+8-2'%#-0)4#&.*/13"'*,*#0622$#.-'39080)/&(3:29&/-943)9$/(*$248.'(.1))*2%'5:6.31./122.&+3(!6)720%2567./).,);,.-/+&0-%.-95310,1,<#8-33")2/(&+1)8*)'*245,,/6"%(++09%2,2.+;9$30+(7*+(112560 0€×€×€Ö€×€Ö€Ö€Ô€Ù€Õ€Ê€Ö€Ö€Ö€Õ€Ø€Ù€Õ€Ò€Ù€Ó€×€Ó€Ù€Ó0-%.!31,,61+28+/%+3'&+0:+11>*4$)@-217'503'<'0620/"6,4/3(,./6/./5*,&234(?+3(.'(4-.0(9/+#3+!-0')3')21$8-)'+.-31*'7/137(02/32/&/&11#%,"/5$'47#3*7)3++*++6,+&3%22;,+9(3($5"1+#+' -',-(-+#,))%-*'+(,2-/1- +'!216"'()61$&-8-8)-/0:20*+"-'1.&!4/=" 2 +'9..#01&/53-+.(7**/-1-'2.1!4.*5*52?21'-/7;-'**,21*0*$'&-1/-1+/('#0(%&/%/(-(7,2(>.+.=%/+)1'3+2665:*-&2-.!*0(*#!+'("+ )-//$%*)/572%3'2/4,'+#1"'()--(:+-$**33'(4*)!3'240)5(2.#."?-'"/!7%$..9').1(&),)03(,('1*6!2215$932.>."%%7,7,0)-9/1(33.03352(''*,7//)3),/*59%0€Ö€Õ€Ô€Ù€Ü€Õ€Ù€Ú€Ó€Æ€Ø€Õ€Ú€Ô€×€Ò€Ú€Ô€Ü€Ö€Ó€Ó€Õ€Û/160/277%&84,,1''5&.0.+$..,--(.4).((4,//=*+(4+*(G1,+09A3!'+5,.(5-."'0(,:)/60555 5-0.1%6/5)*)40'.'%+/:0#-1;*!+=-2-+/4,'.,(/25'(/4&&&##;4%*8'3%5(&,!(1809&-$.*./)**30#916>00("3//-4,/3"A1,."-25"4/&1+&,9)&!.29/'%* +'.,#6+2%)-2/4;)$/%-5+0*3./(-*."&'07+3)""".*$*'($0(#-&+ -%57*,.+5,36%/6(/30639452'5.)=&+ 3+6/8&!)5471:24&6,-%1/+#,1*/31*---.%*)(')*31*2.713.22.*',-(,,#1%-':6(%-4$0#8.&--0<**+-.*-1($3/,()#'"+$30/-3'*847'+#-/&7#7-0))1',3+"0,-*1.$&/-0-*'((*&++-8%,+%--;/+#/-,/EC/.2-6-1(&50/8-;1#/<+7;.1<€Õ€×€Ö€Ú€×€Õ€Ò€Ù€×€Ê€Õ€Ù€Ñ€Ö€Ø€Ò€Ü€Ü€Ö€Ò€×€×€Ö€Ï.28'548/#=*>,"".23))/)4/9#(*#1+-1/0.-':*(18*4'I'.#.70!)83$/1%2-50)-*2*9.,,36"1/.:):#*#*+134-*1-2))-B3!3*5+@35 ./672"0:(+*+06.&&%26+*2-,--.),-&7+%6+.03,+-/?'9/(1-0-+19"4+.5..(5(>$,:$;%&10/&*8%&8)81'*-1$8=(41&-$.%$265--()&-2%%)*'1-4$*(#,,&5%*1.' +)90,.()&D,(0+%+'5%&,+$:'1&/,9'+&$.*4)@3.>#0("-/6.&()-1!0,!&%-09$"1,$&20.3/&.=1'<+/.32((3!<40/")*86/)0+)+1."/!00))'-*)/1>**./%$-9**(94),127; 403!2?0/#7!0&4!)-.)-3/ $3&+2,2)-,7++'''*9''$!-;%&.1/&'*&.":2*2-3+31-, 2*." 1-5$/4 #7$(/2/*")7(&+/+3,/0&3+/+1'0+.-+..-&8&2+.,;'0)5*/#1D7,+.36-)0003€Î€Ñ€Ù€Ú€Ø€Û€×€Ö€Ò€Ä€Ò€Ö€Õ€Ò€Ö€Ý€Õ€Ô€Ù€Ø€Õ€Ù€Ô€Ø7. -.,,(7(6-&$>-55:9?/.')002+!2 5149%+.*>/).;#0,#*.(252'+,621&*/+%2./#$(@0+&-3+'#).516#4,",--).6.:+9%0**',33*17#&'1 ,+#"1)3'")+9+3/0)* #2,*41.%/45,)2.(1;--:$9&)-2/%*20$&()3(/''*0;6-/3&/#(*(!04?/($+#5+1!/+?3,+2857*40.:'',:,)4%,,(//-((+*4*#6$)-9;54+%-70/1*7)''$-0&+,-' ' (//$.6./6/&+;8(2";*$/5)",$1%/'3,9%8*#1()0(;5%+2,!'2/*/&14*)3#%-$)8")32+5&*.*+"*32"05+*370(3)!-4,7'((2#')4)-(,)!3,,+70+%-6),+?1(./7.+2:$$*.-8#&/+(2)51-72&/(6:360%-:$$!--. 0(*-11'*1,(!+'3835"38;),2%1;(682-5€Û€Ï€Ô€Ù€Ø€Ô€×€Ý€Õ€Å€Õ€Ñ€Ö€×€Ò€Ø€Ø€Ù€Ö€Ø€Ô€Ò€Ò€Ú"+(02(44/-.5-/#/9*),.:/,',/7,')-/6* 5/)(##-&7.2 *$4,)B-:&6--%0/.+1$,-%6:=7200.$!0--#/-2,#6- ",10"**#**6.4/).."@:+)5.-*+/*,-*23)#84..#&+'-**)*$)7(/75="2'91%=51/++')/! +3.)!-$.,#(41+1,)5 94.71:"+*!+$/2#(*%&'$"."54('0.55/!/,*!$41+8*-%++-,2 )0+9,5"0*'.+','#-%26$%%<,?*.$6&1$/$.)$+1)-2(/1&,+($%+3.(+./-&1/96?62!52'.6(&90*3 -,;)#.2'-&&*4%/ +03;+*,+,'/;39)233'/.#&7")#-%,&6&07(/&+/6/-='.,),(5$4#)/+(*0<1%$8%%-+# (-")+.2*+.:- &5(7)5)*,,.-153250+%&//3/1/.9:68*A;,()'3,.,*+5+)#9$ '/3#'/5(/*&4*,&>'55-1+7&$+-€Ó€Ò€×€Õ€Ô€Î€Ù€Ó€Ù€Ì€Ø€Ú€Ó€Ñ€Ó€×€Ø€Ø€Ø€Õ€Ú€Ò€Ú€Ó /*110*10*'18'+)&.3*20;71&83+)*!'.5,50+808$$.'.30,C/%0)17-B 5+#94+-.6-*)4.%,./5(112'(0#=2('+28$#//%)!/!&$2.,(43&+$(/,121"1*,35'&%*/,!6=&3!++.(15(/%(*6$(;,6'!*,/.$41.)2(.30+4%',2*14/,-+03&75-1!5+*$,6-2,$.)8&),/0+/)%*2-+10+101'1)6&'4*7)71'2*11'++.1+3$<+$,,.*3(217%#31.-/+!0-+(/'$2:$./01',!.(+8 . *0.-7.$,.'#/.0)0)',$%/!-7 /**+&!809'-""$-1/:6.@-0))'/,*5$.27(.)A#/1)2+.21;4.3 ,)%/27(/+,/8(/6*=%2&#)4,1.2$5;=)/-'(+.-;))-/)0*$;./271#9/3.&*-/%,5+/(.);/-'%-.%04(+#(0/1$)1#.39(8%7+3"8,3078+/605*'--)*',.38-:8+'€Ø€Ö€Û€×€×€Ö€Ø€Ù€Ú€Æ€Ö€×€Õ€Ø€Ø€×€Ö€Ü€Ø€Õ€Ó€×€Û€Ú5.+13-..=%,2%"867/)(5-2%'*151";,1-3+69!(('%.13*-8&,5/"'-/0,32.8)(01(/99)..$1(%-!($0');0$ -)%.C1'0.&1,&/-;4?27)-3),%:(2);$+0.5.-#+!+3 /-%/''/$+6$&5)F+$'+31*#0,0*&-$'*.'(0**2224.,'-)732.--%36%: !%,/&5(*&&'(*')47-"1"1 4(1!010.#/9(+#-(=+($1'&64&-,5<&%'1/3%*27*/12+7&%*A20'0#)")0+%*6572(<4))<)/%55)*.'60.#'481,(,3/B'=.A.5$,)*"-2&0. "--/<&&&$,#51-&.'+-&()5+,)&-("&,/1)/'- 3.+./%(41($>;4%&77.--/(6&(5?'(+7*56+#6(4*/-2*+6081571.0G$0-74(+*.56,'3%*$%.%/0***)!679$)0#4*/)1(&*2/0*4)/%':#+#,+08/0142<27A3#€Ò€×€Ý€×€Ö€Õ€Ö€Ú€×€Â€Ú€×€Ö€Ö€Ô€Õ€Ø€Õ€Ô€×€Ô€Ó€Ü€Ö03./+10*4/637'.-0$>/'+1+%%4)25842'1-0,)72-6-;/#%,.9'5$()*4)':7,1*"0-4.)((947$.0)/-%-"#53,.-6-8'.('063/1*01=/1'+).-$#).1+-*+/%*/%-*+2)'(1"(#$.+"%0-,7'#8'6)-!,)%1"'.-35-.(/(!7)&+$+-2#;&.-'..--''-*1).,3/$=/4(5**+)6% 9)=.,(-20.++(2 (+-,6+&(+*, ,+%0-/'1-2#3/#,+',./#53*('-3.'#82)+/-,-'3(354-(+2($.,).,$36.720,/*1.$//6&'03$67&/(425(!86 %5,80-?$*.-21-)2%9+4.2*,#1,*/-(+(/3"&/+4372"+7,0&."(3)*&5.&5-(%%*:.0?#(195-'*/6+*:2-;B#-7*:5-2.%9*):/+7;)7735,,*5'."53+-&*/217.;6+1-9"4(9€×€Ô€×€Ô€Ù€Ù€Ø€Õ€Ù€Æ€Ù€×€×€×€Ô€Ú€Ñ€Ø€Ú€Ö€Ù€Õ€Ô€×+$:(>&/3/)90/62%+;,'8)%--'/1+6,$%) 3-4'&9&(90.2=1--'6+>0('+ &-1:71('1&1.+,8,-'-7%2!'-/;,$**.+.0)+'-63()-2&//&023(#*4"A2&5(-13):545#&+!"%2*0"-43.2#2+&!'%+#,))6*+'4,*5%8.'*/%0A1-+,5+5.(9$2-,.,26/'/%&8'4.)%(,-*<3=-2(.'*("+3417',($2-/9'/#++,)+,)1#4,%16*,1)*5/%*#%$2*)2'1)%#1%1'-9.)-(0/2/(),('2$*2/)*/+36)2E0.'/.'%,A':0:%8($#.%-.'1+%(7>1/!+41%2 ).5$)$5)*4+&+(3*,,%B%%/'0*7%4;@3)6$&,**!,4,8+-.#+*533)!-6,2(&.%5%"13$!).616+'1-$"11'');00.)1-(>1-*+,!+7+26*/1)42+),!3((&$5!("3+*.(:14+'&/6,3'<.'--*44*1+)'//(5*44).1--//3/ %/1-163-)(49)$$4/.+3,8+7+5+*11/6'-0+**)4*$85-(,%29:,..0*,.,5$1))2+2"%$32(9&0/6,2'5'+*(('*3*/2#5,'5%22'3+*<03+%1117;/+34.!322 &).7/!2)"!&<"),8%*2:2('/&/72 %)/')*#*+'$5.%%)( 1-(()&5'0&,5)+1-7)."($26'6,)(5$/#,0'/1''-/()892(-*!!(0,,-33$+)/)<0,950-+"/€Ô€Ñ€Ø€Ø€Ò€Õ€Ö€Ó€Ó€Ä€Ò€×€Ñ€×€Ô€×€Ù€Ö€Ø€Ø€Õ€Ó€Ü€Û)7+';.6,/+4862%010-/'0)K10401/0*)'%54+)3')66*4,/36-+)*$,)(-*'1(.16/-0**+/5%$61%(&-2485**>65'7+,2'.,920..%&'&'/08/8,/*0524-+(),&/%+%,-/$=3$4'&/2/1(14$(38+0-/)0%503%'-1/33- 50'%,/&(-4(.2."-''%#/))+++-!7+--7)4*33!(+2(#0*5(&"8 %)/)%4/0.1 3&---/&,$*)62/'(!5&,,(22*9-+--;.'1*0"--767//0 $2)72-2(&9+0!-4(!(3-)),..0.4**-'&'/+'0.-)#6+.12(*++&/-3$)/#*$7 *-7+:!/$3(.3.&+(' 71//331/230$/*(4,318!3/&)?2$$/5/4/'17*51>%*>#%06/3,++/++;-&+)90*1:%*+**,+7,!2,8*,+%1*.%#865%,,++<€Ú€Ô€×€×€×€Ò€Ö€×€Ó€Æ€Ï€Ú€Ó€Õ€Ô€Ö€Ú€Ñ€Ó€Ú€Ù€Ô€Ð€Ù'*'"2.!>+'&25/*/20141&11)/:366'3,/*6(&;7235,17#81+)'31-$0,+/2/:.#/.#311;#-06-)6*2$('45-+*-0%-#8%19-%1/4-+-+31:+/5+'$,)0-.365)#)#12%<)-8-0,'8.)*"7#+ ./'9'*,/%3,/)%0+)&0"3-*+4,:/#+2/':((:.&*%/ (+'210=8+8./,35$!&(/'+2&-8#(-/4#44//0()4 :/;(&%/*1@*7%63/"/*4/5--#--&5%/,4*/-+.5/6%-#+,+"6+)3'021))&"1'-($,+6&62,$5/5&14&(%#,1.5*4+-*:- 7*%(+%+/,4#-#3)/01+,011$,$/&0/!-2-")&'.*'.,,#$(.'20321/#")., 7**,"'/?*.87++.%"454'32!/%%("14-9+/2,5-1-3-)).5/(*-(54>,17.$(:*16--+2((2=+/))+-?4&83-*..0)6!/123(045)/9./);.6'€Ø€Ø€Ú€Õ€Ù€Ú€Ö€Ö€Ö€Ã€×€Õ€Ò€Ò€Û€×€Ú€Ô€Ö€Ù€Û€Ò€Ô€Ò811*.*)/,3'3.25)608-3*1138,(*.(359'5#+.$1/8 /"0@1*')7+0=+0-#.,*-&%.1B3.((+(96--7*2.+3/%%%8#&)4%5.76%+.:1%6721-8-'0/,)4#039/43/:823+))9.%/160#%#1+715)$3 *4-2(.*/+,%:#' (0).&1,!)'()/4(12-.6'*6(,--76/85;#'--%/1*(,0,-0,/2&"/50$.<'*01,%-4'#-%1,+2-*2(5.(.!05-!3 7*56+-(-'$+''01$,)2-2'2/192+1%,41-**4+5)+$)1*(&-)-)-0.(*=(37/06")8*$4+)5$%4!00,0:&5./3+/1,%(,37+37(22:*+,'#1A1(0-7/4051(4B#1++-8-?'#,#()7276(+@'.=*.--/3#? %=.-5 /*-="4.&6--*9*$'&.)>0-3(0000+7+"&,8&(3/2$2$(-6'#=)00:%%42*L5/*5(3-*+/.0&$2067;)52€Õ€Ø€Ù€Õ€×€Ó€Û€Ñ€Ö€Å€Ò€×€Ù€Ø€Ù€Ò€×€Ú€×€Ø€Ó€Ù€Ý€Û/-'(/5++4"622,)08,01(* /+:/(%2/+-;6.((1'4(93-7+<*703/51481)/81:/<-*$)(.&/(5$/(%)*+.>52/,%/(9-)&/.!8% $$/6,('+4/1/+*-21&*9&.22 )=%+%/,-",(.0-#46(:-$-8% :53*&)'.*2+"$,*'1++%'-0*/#5$/-1-'%+28*-)13./13%&*3/!5++60-$+:%)'56-:/.)%*++5)/7*)0"*.( (1-!0!(2$*%=*3/&,!+)-&7, +.(.(-/-4,,*:212*22*&+0(0B!(,),)0'8110)&6$43 1:1*7(9.;4&,+%*1/0+".(.%-",0)*20-')0)#*-*+-/%''1+3'*5(-)&'',(:62+" ((-;/$)1.'0/$##8/,+02*)H+&9352/+%$.4.01(( 2.76*1.*17(3/$.()+/6*%7%&/)&54+6364!)+5.-)-+%=".(852-$&!-):6+.*/#61"5&,+*7$/&)(%/€Ô€×€Ù€Ö€Ò€Ù€Õ€Ô€Ø€Ä€Û€Ö€Ñ€Ö€Õ€Î€Ó€Ô€Ö€Ö€Õ€Ù€Ô€Õ4%3#162.<60:8-1+'*42,+$61!/5,:22)64(14',#*/0!5906- #;,7.)3/0023-(+015+(6//1 "/0/,57(589-*;%0.!'+2=-+,'.+&#-/& #9)))&4,8)5&,$>/.(/1&+&#/5-#(7(-)/@01;-*+-+24-(,#$&( 14.*/'&%#/2')0"/(-/(2+6-1 -74-0,,60.3&'@129/(0-(*-**0&!#3+3$;*(4-'!&!,8-*0- *0.+30-,2/1);5071(;) +:88,41!350(-($&*$0'8/(3$$*1+06/,"''/7-5(%'%0/(4//-/)%+*3+&%1*/-1/2+.43(%-'1 +/.-'#8194+-+,+-04723)!'7'!))9/$=-+4&"*2542/*-(*/81.'6"&/(85/)79-3++65797/0=46)1=0/,:+./6//6')8'1'&/+*50!)-'/#+/2%//+'"!#:03/-028-487&7#''5*)*/=/2',7..5/€Ó€×€Ö€Õ€Ð€Ø€Ò€Õ€Ñ€¿€×€Ñ€Ù€Ù€Ô€Õ€Õ€Õ€Ø€Ú€Ô€Ù€Ø€Û-:)-8;$*-7,,*2+1,09!)702;1.'0,&/5#+6-8+1+&/7(A.5."$./%/*.4)&0+&(-&2%6051$0##'9.22+88+#*70&7' 2-#,./:+-31.-/#$!32/9-1*94*,%06,'-#*2)&/2.+00((( 7'*+-5(.'%:.-#.$33!)%&$%2%*5)!3,06(&.)&+03,-,.; /4'*-(+4+,)3/$.882'0:0(/)*'!+5+30$59'550$*-&*&.1+-5+8+7%56%./,+#.*$)4(5%#*(:$,/-%0)/ 5&42*(;;,81.&(-1 &),0($&,509*@)),1/'3$)45/24++16:/66.<&0*1*,'/,-5.,1*/$1.!,81#7)1(5,13%%&-/4(-6&2",38*0#/#-+,+4*%&-+10/.70'6(88"*1)5'.+05=<2=4352,E/)+*1@7$),5(-,+#+&/512-!.4-1-/ '&,-4)#&%2#0++3659(3&.0/,6'*-4"')-+&C5?-3€Û€Ù€Ñ€Ò€Ò€Õ€×€Ø€Ó€Å€Ù€Ñ€Ó€×€Õ€×€Ñ€Ù€Ù€Õ€Ó€Õ€á€Ó(../89&197192227.1-.23;&3$.')1'22$)-/10$*5(,$*#-6'--1(,+ 6>'90/('1*0$670%/.060/#,(2&*2-,3659 &/#B"()-,#(00%0&3,/)4/421))1$)59,+323++)2*.%%*91/5(+-3)+/$46'',0.*&-"20056-/,/***.90-3-67*&,1%+'53+-%&/$2))1%$./)/7-#'45,*)+#:,"-7#.1+,*&)9-.(5$47+,.**+:&/$-/0#'(,1'9)#%.%''94.11,2+#,3<2:#!73425465+$2(*%#413"C0) #;2+$)50%+0"<5/-22,/=;)-06/482.6/-)*'/83-34B/+0.C04741954*<€Ó€Ô€Ó€Ô€Û€Ö€Ñ€Ö€Ô€Ë€Ó€Ö€Ø€Ô€Ñ€Ø€Ô€Ö€×€Ø€Ï€Õ€Ô€Û9&-,,),7+',+*3)+6 $2;*"(.4!)4"%;(-+ )/(5*!'+'&#.-'%/#2"0(01(7# 5.$(6,#03.&!)=2*//4+(*.(-#1+<"0'&124).4'1&(451/+&-;8-31'%0').,"'*,-&$,3)//)20(+7&-(.(3($22,+/1%4-,%+3/'33(+:/+<,"23#6>8.&:)-;''*(9/0/-432+4&'+*-'**1#(,(-.,.&7(+,,&%1512*3#-57,+5$61)-?-.-.'0%4,3*7,/&%/*%#);-#24.6/4$1%*&,-'+/58&%6,"77*8-+"'(=$79#'7,4'#'#/(18'2&1,'$(2<. ,/5&:+3%#-1%-"&.23(#..) #152!01:C.0(,+2,,"9*7#0-)""*(..3(14,*0'+8.'(,0/.17(/1'7'C0C953670:*:?83:531-32&-'3%&#/-2)%$0718*).-9-567/9+/(+-3 481*&+1&&0,%60.-0.0!+,€×€Ø€×€×€Ö€×€Õ€×€Ó€Ã€×€Ô€×€Õ€Ö€Õ€×€Û€Ù€Ú€Ú€Ö€Õ€Î@(./,$-')<%84,338'@3:--15.4,/& ,+32:!87-6>1-@727'34)))&(/+((*$1.%0+,)+)/5$)(1#2.1%&,21&/05/11+4<3*028,3/= ?*)2.,;-2()*."-1+,03*.+2/04),/-+)*52+)"5+?-/+(F#/20154$,*%-5-$4.*8.%$$ 78/-6.--6*0).#%53&);9%+/%+0*@$52,'$#+()-);"*4*3%6'+-053172'*11..'37*/1,&%:*.%5++)/*A7!!%3(20+#*(67,/!//.252)2475*,32)&4+3-0+#(0 $.-+,+&.)$+#''0.6;7-&3 (+:&,1$;-+(%%&*11,'//-11+,(':%'36)'.5 &65*= '*.,(2(34,2+)0'1B!1/-%3*/32.7.0-7)+)3$6.;*5E9-A;B2J=661D4:4<'%+,+-6)+"+-++2%*0;+;,4.3286'#''7*(.&+%/5,(+3,)2/=2,&&A(15'4$--€Ó€Ú€Ù€Ó€Ð€Ó€Ô€×€Ô€Ã€×€Ø€Ó€Õ€Ò€Ö€Õ€Ó€×€Ø€Ù€Ù€Ú€Õ/3-333>,5(69+6.*(6,-/,5!9,,5#+120--#+49)-"/2,$,:/'&-/(;1246.%0)/.*((..&3/$.4(33.4)#0+'0$(-508" 838'#..*/5*$'1)+##-:31(2<(/)'*4<4*/+2+@#(%)($ #(054/1+*'-6%5+-17."+'+'-%*+$/4#.*+#'+%$1)&5$)7+%C*0',%'#+ *$3(6+0'-.#++(22*&)+"5*%1*50-&4+,/+#)('*$.7+-/7*.*&,'/;@"A)'8'+/,?1-:',2,/174*#/+43$'05"'/-%'.+/+,,0101>.$,01?)(&%( 42-4$/-,&&+;//5,30E&,-/.15"6"+#/,/%1"/%206-4!,2*-*./%-+1-&(5$*%;00/'62.'*'+30+34(*(.'*1/325A:59LEPC8J::<=/7061:2!1397/&&'17+(:-35+9,)?6,*7(.>'-.%**.3,121.(6$'0-0./,*+3);+'70-€Ö€Ô€Ó€Ñ€×€Ó€Õ€Ô€Ø€Ë€×€Ò€Ù€Ò€Õ€×€Ð€Ù€×€Ø€Õ€×€Ü€Ù;&,4++3.,).7"0,/:+4+/,/-7(#*<0 :(739,605+)+-+5("/2*+. '4'7+*0--2/)(029;!64(7/>+8(*--);6'*"2-/*#-(84.8(!02$4*',+'%09+$ 0/:"//-1*/#3.--3)8+5/* 510'/5,8%!*(65*#5)6-33)$22,"(3,D#81"#41.:)200-42-&1/(3-B8137+.%#)/2%.'/.5'"!)2#&'72.,(&4 )/3;5-*"+(+.-,4,)4-).0'+02:+*//5/+/.,+,&-406.-*0&'*,/-+/2.!/#!$*(&(/,9+2-+)&&. 1*-*-3;,'/(.)!( =+1>$98*+71<:.',64';/--('.'4/'.5.&)!78).03)!11%.!%5)&2+<7<%5.$%7//(0=-$2-=8"/',B9'7436E."'1-5% 3$+/$+,-&-"-#$#'*12(()9'+',6))/+0'0#'0(70-32!,/0*!.6/141--.%++&6$4)!9&+'89%*)/-6/!,$*$8#/*5-2/*1-:%*7&5./7#,/,://(#%&'4-0/7)+(%2+-5/-)/#/-#1/6%+."&/49&&/(+,-(")(2#0462,&-/!/*.%+1*:/47+,*2:#36%2-*1)4,0)(*/"7/.#2"=*-+%1.+2&4&+/'8##'/"B)621+*0-) 67*.)6**-)3&$-.+$6111,"(,.04,-/4#(/#0"-012##'/- "-2#,,*"("%/,&,:)4&6/(3)8410*0&-54/%4*-)*)#2#*,)$0*"()/.,+5*,2$2'#%2"5&/)//$")-*-&;$5*-,*1%(9,-+-*/3.20/)'(+%+*)'+&(3)!*..4&$1!,$4.! 5$#(6%%&-(%3+3''4%-4.$042-220 &//"(*&#("724"%!3)20+')69,5' '/5(2'%/3+9*(/"%7843B50.-0)#+1&22(4/&&//$/55+*/A&/)5&--(%.*4!.,&4(-0.7/2&66/*,+'.2.27+0..6$"&"5+3%%//%)$+/0$<8F+1,+ 1:*.4.%'#&&--%*(3%/12,710('+",1,7%..1:2'$%A)7,+('-!4(-+'4672."//,,./+=(872!2.5+- &7798/#12%0+/'(=(/$) +/(6/'*'*+!6"@%3/1*+1.-)(1".2")9!5##&0.0 '+;%+2/$0#///+603$4*-+"".2:;=(,.0/3&'-/0'1/)00)0-+*/93%'1,<2.!1/9,3.)-3#C#31-11,45,+9$&2(4#*5)+ 5-,#$".,.:7@0750-=FLOpr‡š™–™…|sQTB674108-3",/,8!!('044-'*47*(6'43(052/6*+/0;/(058.*,(24/C$$;1-(@2.+€Ô€Ø€Õ€Û€Ô€×€Ò€Ù€Ö€Ä€Ú€Ø€Ú€Ñ€Õ€Õ€Ô€Ö€Ö€Ó€Û€Ö€Õ€Ô..492(-)22'$50-/&-*,*26)'9.3;&73.;5': 0010*3/3+-/85*-)#.'/1&6))/%2/"0!'3-0,41%0.4,43%9-/+891(+4)*:0.:'+*0-6 %,7?2,'.42,20-)/)10251+;.(26$,&'//".2/-483/8/).#=7*0%(%)'1#$.*"+4(,5,)0-.2/:--'#$07+0+/# ./7+:7")4'39$7)1#5)/-*1//'0!6$8(1"'%>&;921+* 2)73$(3 #'.-5@23+$$4,1/*#2+*,6 ,5)';2$/&-$-%*-(+&1.<6& *3,(-//*0 .'345,:*5(,4-2/)-2"'&)9))3&+%/0)!602- ,*90(83+>*$8'!4(*2(0:5-!+),,!$'.(.'$226/*/.0!9#%#(5()'1.)+08@7H;BjSlŠž«±¶±œ„q[JPD?E:.42$*2/25*'->-31'*+,)-/00)943?/-4/-2/61)-()0(045.$8$27#?,64,+5,€Ñ€Ö€Ò€Ô€Ó€×€Ò€Ï€Ö€Ë€Ó€×€Ö€Ò€Õ€Ø€Ö€Ú€Ô€Ö€Õ€Ô€Õ€Ò2%,2$0'#95:*0.&1/($@6/)060-2,/.*>0/$01'%*7?.'(&-,.!0+//%... (#5(15581+31'0/*3/-%2."',-+*(*",0(%2C#"#.+,.%5%181 '('+'%)3) (&%5&1+/'//!/A!71)=(.#6(&#(,*2/..87,'0#33=-)*1'24A*$34/(/!7.6"2+79(1$7'+#."5%3D,++1-28',+(3/))+-)-#4/2#5-,),%"2002)+0 /+%)1)"*+:3522*6-'.%#!007*:'-/3'569,5$$7!915%+.1019'&&*1%)1.&/%-#0*.'-/<&(--571*-)/*7+'1.')6,"'4,?)&+%2)"*1%2$;,(7(+/*7 (%2'%"--*)('(1%7/3'* (&$')/.*483-1"-383357&>/496JEWa{šª¼ÄΰŠuVLOON7C/)3&-&(5'6$6-2:%))&7%=0.),1)10%)375!,&+(1432(-&.1/(2,24%02(//€Õ€Ú€Ò€Ò€Õ€Ö€Ô€Ô€Õ€Ç€Ò€Õ€×€Õ€×€Õ€Ö€Û€Ø€Ñ€Ú€Ú€Ñ€Ù*+0)"-2),*,3,;A'0)0%1 %/1-+5 .0(3,42)41-)0.*/08!0,. ;-9:.10,0%.5.1#=.)'*+*23+ -&%*@#,*+8*'%%'2-:#)4+",/37*9/*)2$(.%'-&''3,-.'4-'16'53%)-)-#5)((,/$()2.#!".-52:'37,*#"8,+3+'(/- )'?)26%&#+,'83* (&';&24,)+0(-4!"0-'43--';!/)%"(#(.+*#$3).)5+(+22-3'7',62,-3)+&*!%03#*-/,/*.4$*%#.5)&"%-,7(,%03)6*9!)2,//:.'5!*91-/*&$)6+4-',%.35;%/57@,>4C<%,/(-((#3)- 3-09&(-1#@/*41-B.200/3)*8*5',+86.&1(0.0*1$#*6+5'4&(7"($ +,3(*1(6DQTE^_†›«ÃËà´Ë‰t]RK:.9;0+E/:2.,D1%;6**(43.)=&*/50-()5203%#<1(66$0/5A9+7'5'8-7+/$#7)'=%€×€×€Û€Ø€Õ€Ô€Õ€Ø€Û€Á€Ø€Ó€Ò€Ñ€×€Ø€Ó€Õ€Õ€×€Ð€×€Ó€Ù2%21*#9,(66(*/@88///+41'#0-*$&"(%261'-1+*4150+5%8(51'/ 23<1%+#,$/.5)2,?)/!64*43!-+,1%$0&3,)+00/0*%1B5*0,5-5*)0".%-+//.%-"!/*#20!+&(A%,",)#'9,-./.";3-3//*#-4#(/1$$<%7.*.-6$2'*/*')418.(6(4,%*&+1046-0*031+)0)!(5&76$)<&6)0.0,":!16100+/2#5,..&&3/#-&.'*-43+.'/+.093548-,/'14"&16%-'&,59(<+%/'*6-("'-$3(+2',/27*2+2'%,2.1'"/+1.'-:7%.2:4%@2B,'**2.2/#&5*62A&%.-*01&.(*-%0/*/!3!%)#-!-#/53-/.-5!$=%6132.4-10&263()+729;WRiƒ~•¹ÔÕÖÊ·«†jH@E8?3<54A6/76+-2/4*2$--2%'*5&;*-4+/.5*9<*-08)!=6)6*691'349+40")(28*€Ø€Û€Ú€×€Ñ€Ö€×€Ú€Ø€É€Ó€Ø€×€Ø€Ù€Õ€Ó€Õ€Ù€Ø€Û€Ù€Ö€Ü!80/7%2%24&2$!-)$8/.5-$56565,+1*+0+2)(+0&+80<0-+2*/91'/61630'),%+64,1,%$+,)+.6%0/&4&*/)*070+,')"%85)&*.445-7/&4%8') 99+;*.4&.<#21(-+1*3,(.+$-$,5*846(3,8,*%('(',#%* ));2-$4+1/92)010 5%,:%/*)*.*13+./*&%4"2)41)+/)% ;/2463/-+/+#1.<)(03/,*)#0+.)-%1(5)')3*,)#63.3:*//0.)5, 0+72#'0*02*:#3/(2(!'*/.0#-3(-(*/A')-,+6,*)*%%+50.#//.)4<'*,7%,.<'))+0&,/'0&?2&*-&3%2630*(#-1(,+44#7.1**/&''''+&/0(0$')<-'015$48&$/',#.0A&/D8K=NMqy€ž¬ÎßÎÏͯ›hx[aD69G+=48C),1+).915%%!:0@+("5*8139"4)*+./84*%,#/)!#%52&0:12<$(0+/3':€×€Õ€Ñ€×€Ö€Ô€×€Ù€Ñ€Ë€Ô€Î€Õ€Ô€Ó€Ö€×€Ñ€×€Ô€Ö€Þ€Ó€Õ0@.5)38;')09*'$$&*$&/00-7-)(' 32..''.'0)+2=!=6#:)12-47&+2-3/7.<*.:-/--')/("(0++)(6.'32.$#: +.#!.&/3#50*/1,.#7/0 (5*184&$(0#-1/3%/.<& **#8$700./466')03'.02..3'*20,***3-,&-#11'+4.&3%:8$*3"?0',,3,,24%2'*+*3&+'19,2/:%++4*&4!9'A0=%./26(18"/)8. +8)331(3)"*311;#&@).+ =(',-+&+)/)1&)*:)9!55(3/ 1/35-$---'")4%/'+4%-8/#2)0- ")"418.&,(+/2*5-:9648.30$#(+1/8*-+-*+/7(%8! 6-**5'/5:&5,)&:5,. 9*4 2(-+52-5/1'133ADCN?^m‹”¾Åè÷‚óܾ ¨‚dUP;IL8/642--5'2-7/0<+1$8&45-&6;&0-)/)1,*-8339*92.1//5(-864'/<%2'):?("€×€×€Ö€Ù€Ú€Ô€Ù€×€Û€È€Î€Ù€Ï€Ù€Ø€Ø€Û€Ø€×€Ø€×€Õ€Ü€Ñ2-=30-+9%;"2/**0)(5(*7.$#/,/02**:$3-75&'<1,*::-2&&/12"8'-263/3%1$&8+7.-41.3$,.*(A,/$,51.,>%.*1-2/)94/'."$(,9+/!(-+55/&44(#2,%:'25:+-)* 5-*+(:#18*('1/*%21+,''-5'+4$' #74,(-(13,<17-+?-)1.0*&!)&+2-13),8%23$2(%/!-.&")**(&%&&(7'6"41,+-++&/0.7+0*+"+7.)1--$13'.0*."+"6+1&,0-2+0*(,0#+,)+%702"-"*(%*+/*54050$851/#,$.D(,?&'7'$'1/8,'.1.! )%*+4 2$;"A0-112)- +$4*/'@&%15&!4)1-6,,1,1-+/3%8972(!)27)6=:2'++/+2'+<%,=31+>(#&$6>80E3EGR]zp»Äü‚D‚2‚‚M‚÷ࣉxdHD@DFI7-649"44%,7/04!9754-,9:-*2.)*1/:.2-/6')&'-*0&.1/!,,51*5302/%€Ø€Ù€Ù€Ø€Õ€Ù€×€Ö€Ó€Ç€Ñ€Ñ€Õ€Ö€Ö€Ö€Ö€Ø€Ô€Ø€Ö€×€Õ€Ó4)4+0#@2,<,'*40.-.$.30#!21+/.'%//<,###&&0/8$,4!3'%--6)/.60-G0=$+*174-21)0//3%)8;16+,,4+3%/5%4(.01.:$)/)...-4/*(95"1 '(248/.)1-*1/4-:.&%'//'//%3$',66105)&'?%#+! E/.#.2&1#%(4-&(+1/"$:/&0'.)7+++)11.*,/-00,1,'!2;1+,.-&")(/)0$(2'&*&'0()*8)+%*,-26,$,(&&#+,/*3*A2/3)/.(+"*8/&/+0,<0',21371'':14%--(7))+)&-,.+0+72/./*4--/'-3.':&%,.//#'22&(/)"0-$$75/34$1*+3")1")6#0*)/6'0'%)7&".',*2%.-2+)5(&&'5?"!->1-8;#7E-30&0260;*>DEXWdu´ß‚‚F‚€‚“‚©‚±‚‚E‚ô’{jUXS:B3186-+*+ -1)&()&.3+'5''*-'-1*&+(,72(+.6<$+0-2,9(4,/2,3.+?) ))&)€×€Ò€Ù€Ú€Ö€Û€Õ€Ó€Ø€Â€Ø€Ù€Ó€Ú€Õ€Ô€Ò€Ò€Ó€Ö€Ñ€Ó€Ø€×).4+(-9'/7<2/..&.)/.0#*2 */<,5-'*+39-,*>$:)-20#210.&*02*(&73)';.)((3<%400,6+,,-2'**5))!$5-/,77"8)#-,6-32'/'%.4313')0(;*(")1(2,"#"$**%)$'&,5"0+)#('70-%/9,*4-04<+/*/",0$& 7+"&7%5%5$13."*/**?>6*&."1E),981,751 ,03/$9%#*3#+&,/'*',''&&;;(+'&,&3/1-)#)($63-B4'.;(#%$!+/-.+'''-1% 0;>%1$ ''*8)'2,:0+..+()>./%)1/+6*%"(6#&7/!8//3)*)2,,+/,%+4(1':(9/5*%-%)&6''(/-5/ 2&%./-)-'$9/(5*;-115.444.-.1#9,-05D930/13$-.64*0.6/,)20B9/3EWTm·è‚‚Z‚¼ƒƒOƒJƒ`ƒ&‚Á‚k‚塎jJD>7?;1;<+@:*4()3+'#!-0+-%!59)47(*2$.003/-+$,94912(%%(:0!,0;2251/'%€Û€Õ€×€×€×€×€Ú€Ù€Ò€Ä€Ù€Ö€Ù€Ø€Ö€×€Ð€Ô€Ø€Ð€Õ€Ö€×€Ö-<-(.9$,4-9#1.0-:"'&9,822,9%&3/&7&!;,%%(!4:#6.-8(?25/8>13)*/,'&0)0$(!*7'11*"+,"-+9*+12.3"+'3)8)/#'$0$)2/3&5&)'145-%):+,3-+)9#/.93%;5''<436'&824..#8$2.,+)1%/$0*:.*./-14'*2'#*+836/*(-*-.'2)/'0!*)/"+'&-+,)/)-+350:,12'5-5;)/##""."#1--#*'"+"42*7,3&"0$$,/*3*$+"%) 02(.%*02"#2-.,(10';(&0$%;+-5'%+)%3+,)41,-4'11!)5* +!&)/-#97)$'--7///--5($++/-(#*+#/+/5&'3(+%)1-5,.2-492/(&!+,+%&.))5//,-$)0?0+"+.'-15,')-/.,4:2,9*6-3?IFRPYƒ£Ç‚‚_‚áƒKƒÚ„„Q„ƒðƒn‚ß‚e‚¾aSCSA7-3:6//3(+0('9$//3'2(/&)"/&,5(**,-'&4#&6/(>0/74#30/)60- -41(*€Ø€Ó€Ý€Ô€Ò€Ø€Ø€Ó€×€Ì€Î€Ù€Ò€Ö€Ù€Ò€Ø€Ø€Ö€Ú€Ô€×€Ï€Û(.);(/+.51(1;+D2+&1.%A3.*0",)410-$#*,(03;--."-;,,/%&$.22--92-*#-:&,8/*5)!"5+)-#*0,28+7+,$$561!' )#+'//2$+-,%/,52&)/4+,D--(0-8,.)6/3'*"2 ",4--(2.0-*1%;)%1,+8#'0493-1-%.$)/5%(%-&$)1/-0'0)162-')-+%30(()!#,&-/%.*-&!(+&34%,1).*+'&)/0'5)++5'""*-%93*&('/+4&,0!(,/-()'4'$$5/)%+1+2/+74$$+,'.;66+5&/ 0)*,)"9$5*(0#")%'/7*3+'>+513;2,9,-'-3'7:2+6<.2,8+&*/0) 0#*0(,.1&)--*+".*4!0&!)<$+5(09/'49==# *88;*3B4-1/%+)/39.4>H=Fd|“©ó‚:‚´ƒ6ƒÛ„z„ü…S„î„€„ ƒ[‚µ‚;Ö©peWI;JA.7;3.!,+',$@2-1+.))30>-7/5)5'2,0)2/)2"-1%J1+.'--"4*1"3+(4233-€Ö€Î€Ò€Ò€Ù€Õ€Ö€Ò€Î€É€Î€Ò€Ó€Ù€Ø€Ù€Ô€Ò€Ó€Ó€×€Ö€Ú€Ö+/5%1'532*,(/7$/'+40 -110'>.!4(847-0/':05I%)))9/4)6+:5+&&/0(*&//'0'%!%?3!)36091A"3+:-<",-'&)#'..,8"$.0'81,=2%)%. 3*/%-'408'0..) .3)+B52%%0190(65*50/2.')/%#.5%(02+'(").24!&(,.5&&5/-<4,8&!,*,><20'9+-6*)1.258302.$.9-)1/(,%).)(.",56(,;)0.+1(#.1+ )56)3%*'*'491%%2$//#-+0//&$('.'%&28**).0/"(%&('*7)-%3,2#&+,%)-/7-5+-315/$(**$*--1,:19((%.)0)/.*#;05'+0+=!/%7;5(,7')-/+:',(1/(';.3514(&/"/.-(02.&8('(-4-4$/1:,'/*4'1.-285-L:Rct‚Æ‚‚•‚ÿƒœ„_……‹…Á…›…%„mƒ‚æ‚W‚¯“bdMG/E'71)8:'?-7(316-/).%92/#*7*0&)8=93$,)$*812(3-0$)&+/09)$)'%-)-€Õ€Ú€Ñ€Ñ€×€×€Ò€Ø€Ý€Ã€Ô€×€Ð€Ö€Ú€×€×€×€Ò€Ù€Ø€Õ€Ø€Ú'"35,'=/1-&7685-713)0.2?)'0*)035-5%('.*/9/--'#5&+*/0(-%-531='9+)'%3$!!/&03&$(,)/(+$)*31)/03:*.+,8''-2?%+4$,0$7+-'31#*9.)-'+/8*F"4%(1/-'6"2)2)20$.2(4/&(*(2+ <+*',+*)2'503$8,+(99-%231&5/61+/)5+)+9&14,0/%)6.2#/4%23).-,0,)09+.*+#.4+;.,%%++-1+5$*% +4")-/1$/'5,'(=3 %71:2;+#/0$*)+1-'/$.5/'/)&3*$&,4%,52*0%+'-47505"/)09,<013/1/68'04/%6-1%,3/8)/#'-"(':'%*, 0'70+5...#/6!2, %)/-(+*'/<#6%#%/1&051<,-1-&%.+,(3@64027.62EEe^v¦×‚,‚¥ƒ=ƒí„´…[…ê†'…Á…€„•ƒ±‚þ‚v÷¼}iWSQJB30E;.31*!->400'/%,++))"0-"*'(0*7'4&)0./;+./5/6,*0+.=.076)/4/€Ö€Ö€Ó€Ô€Ó€×€Õ€Ö€Ö€Æ€Ù€×€Ú€Ö€Ø€Ô€Ü€Ö€×€Ï€Ö€Ú€Ô€Ø01*16+"'%536-2466;7+<+242'+(/@'&)3!53;710/1,(5):012$0#+($="9'/,-%-1:#"#+&&&*'@%,. .'>) $5,+24'46*,($.%)2,.)1#43+*&)6 -4/++#'66"'"(367)30-,-/*385)0*>8-/%;+)?!+&/*),3*5('*"/$(1)27-/4&#'940'.,*10$&8!.6*-'1&*%..1/.24+#0%2+036$3.-&%&3-4'#"-:+63%3,"51++#%1&+&)0='-.,)/%#(8(47%1++2/4=52'61"/$-5-4%9+9()**-82- 1-:6,+.)0-.()*3#--4..,00(/(4'7,/**4!/@-.9+*'('*1)46*3*+%7&',,-))+*-/')-''4/1)+421+%6+'9-,9=24,K4MNKZ–ò‚‚‰ƒƒ»„\……v…ô…z…„Eƒ“‚å‚`ÿžŠq[JJ#3:*(64:733$5%B%4'.87!+(+9(<7+7*$'4.02&(,0+$/-+9-66.%*&4-:-$/(,62/-*,+"$/.-,0%38 53%'+711),'8--!..)0#)2-3*&2)'28&&+7*+/%'//6,0($+-%*,'&(3*'#5$/!+9&'-+),--&1,*+?+G2/935'3(",'-6'(<+,+%+%42*"+%52//%.957%)*00& 7;.<7/('9#,("<$,2"-*5.#33"1*:$'='"!0/2%231(.,46.510/76/-*70&0#3:;;*""#%3&--'1+(0*45.+-341-;000)/*$(,/1/4 4%.0/.+-4,85,+*/%9%?$"0)--2,1/-%;16/;:<36(8P7OR]w§»‚0‚e‚ðƒm„„n„²„ã„è„jƒãƒ=‚³‚Së¹weeG89G10C71?0671;)@#(-(67518+2-(35'.%5,*-9#3;/,*/.@7160'%9=8A'/"-€Ó€Ô€Ù€×€Ö€Ô€Ù€Ó€Ó€Ã€Ö€Õ€Ø€×€Ô€Ó€Ù€Ô€Ö€Ó€×€Õ€Ô€Ù++*-./50&'1--*+6,(+16)12!1+$)13/-26'7085),.+1!%&00'702E1+.5+,09-3'2'2!0&8'3!/)$- 7$6)50+!)01'&*22:/'02/,+3)'%1(1'*$/./1++$/(<%,:1/).%'* 43-,*0$;.-+=/3,',4*&&*!(**16*/*),73#&5/&0%%9.((+2' >)7/)..'&1(5-193>,%/,,5 "'+"*+'*';*573&+//2(-+" /4&+2,8-0.5574)6#63$'!425=2/4!5240'$)++,&'2- 2),)(="-/&5/!4--!*:7-*77H,/*+4'.6/1(.5,*""050,*31/!'3,!7&(++1-*#3 46).(+9,-*723/7$00"&:/'*;8<-8/$+5)1.-%,$&1)%/&)- ,3.66<&AONn|—¼‚‚2‚®ƒƒOƒ£ƒÿ„ „/ƒ³ƒR‚á‚q‚1ËŒƒdNH4B5=-92%->*3/=5+;5--6-2'6+/1311+4&/+-)36&(,30-/9(/.*(:-2$$01(,€Õ€Ö€Ô€Û€Ö€Õ€Ö€Ò€Ô€Æ€Û€Ô€Ù€Õ€Õ€×€ß€×€Ý€Õ€Ð€×€Ð€Ø3*)0'049+-9-9)/,-+))*6&:;323-03)&2,'))%+!6019%3:,1'(:.6*"+/>/006*/++-2/220/2+((<#*(,,- &)-(/3)+294/%%-+)95+$!6;)'#/1,..1/=)*/-#%&1#+(8261/')7.-*,#/*4:*8' #(&/4-4(18/+*!-/#,-."%:-+::512(!0,2%/,2& ,)0/>*(93)%5&(/*(/1.2!&5%$19+!,.-71')-*0&+3/(%4&17.-*"#4/+/('.44:7:%-#0//42/3-)+/(%,-.+*+6&$2/6.4-46@4'5(2-.=:E,6;323530+)*%3).7,/).:/2&/1E)372+-",317()'&'6)++0)&,-+#3./*")3%&51'0174)+"B()0*/0*&,.!-;2'4%+$3,>49C@7Jvk¢Òû‚F‚‘‚¹ƒ)ƒ)ƒBƒ?‚ö‚©‚h‚ ÷¬‰pcGM@NA7/.&:*+./25/5"3*&,-/&500(/&/.6:(9*6%*+.-,*<,!@83;&46:6.8&/(5)€Ù€Ö€×€Õ€Ô€Ó€Ù€Ô€Ò€Å€Ô€Ö€Õ€Ö€Ò€Ò€×€Õ€Ö€×€Ô€×€Ú€Ø/ 537)55#18<0B-/60:/,2-2437(*9)*51/7'(".(3?*%($(.3+./2:*.:.5!.!**4+4"')',-&54+1-*2%)/-203/)-05*( +-(/.#$#+433',+&-,5*#-)#-<,9&+4(5)(1(//2,#0+/6"4)'/3),./.-(4#,1).%!#:52()+.972'&-%*,%-3,1"$4&@-/" //2+,1/5.3-/'.-3%&+(64++6319.'2//&9*,+ -D&+)(:,4"B/"&'+*=>',(53)'2'54.'&+(2/)4?�*&66(.!22)1)+1857(2"1,8-*&7.2A;CQ?869+*@,/#8/(:4...,%:0%)2#98+1*1",8(/*6(/.'',)'&-1/)*.-*&/).605/*2&0-&06+/*$7-:-404+57)0)7)+/'4'21/*?F.[Pbƒ¢Êý‚+‚M‚ƒ‚•‚‹‚‰‚r‚cùÚÇ•zjPK:@;249:-C.0)385-5?,.21*:*%*1/3/'1++/29%-2+/'2*4-566(?83(811!'€×€Ù€Ò€×€×€Ú€Ø€Ø€Õ€Å€Ð€Ó€Ø€Ò€Ú€Ô€Ô€×€Ï€Ý€Ö€Ø€Ú€×12+#:7,'9*53,72-$2+77-56,A))496%$35*'8*;50037#-5/-"8.%.255*)+..)+'96 0-"-, 0-'+%0"308./%-1"06(,/81@;33-'./!3*4/,+4))$.,*2#4/0(*2.'-8&3,-1-&-'%(/0'&/14(<2'6+52)6))(" :-&8$,3%4('+)'.5-# *.)#3%!"20-('.+3,=$00-,&)3&,4217+)))+2/(.,D$$& %$;%.'5:33(++$6)*=89/*#4)#!-0=.'#,(0+//(**//502+.(,+*45.51#0<0&-+"(-3.012:7DE1>=/91-23-%(*7.54,$8(/-/4)+!+.!072&++0224?+*/+*554/.1*!/0''%/'*)%-&00#+7/-+'.',-+,-23)4..48,00$7<35LAIWi˜—ÄâÚ‚‚‚‚;‚ïÕ¬£‰pr^QED:74>6.7.;)-.*'-&5):,/2#-=0/(//7',#%12#,3+#8$9.7-/)!1!-4%.:.120€Ö€Ö€Õ€Û€Ð€Ø€Û€Ö€Ù€È€Ö€Ñ€Ø€Ô€Ó€Ô€Ó€×€Ñ€Ù€Ö€×€Õ€×3#5046."$9(263'(0:9#),6.)10=20.0𖑾/"-0,9.(8.-;/4(=6552%-'+'61)/*/%1+1%3,:15/48*:(/1+71+1-.1%+-(5+)$./4%14++36!) <*,%2592, 4/',32"0%,#5,-(%/22/1!"/'/-/6'!:/( -,6#'6*8.,7%(*@"-,%?+#&1*&0-261= 2+)$-* 3+.('+()4+'+.0-/":"'.2)$'0/0",-)2)=(%#(*2(($'!-!95+%!7-#-2 -1!;+/*'9(A'0'/24;1)&+4*%('1'+,-*:-'%-'*2(7'8@?32><3E2112)*/)*6(<-;!0,3/0.62'3&<306/+""#'42))53(7<(5/7$3)+''+C2+'3+-&%6:58+,:@,-)(2236%)*0;/>://90,:L7!@+40C%5'7.>.5-16//;<-*(%5653*.*21+629(%1.)520')5)$-()-6'+"1+..'6'#54/415*2*'(6.>.&2+/4#/+);&!099704%".2-24%3#232)85#3.5+63/#/(-*.%*/'+%3*49(.*#-011-**-/!##8+!00*'%,<3# -..&/1)*#*)6*6-8(:1-,.:+8'"2*1'5'1: &+14,*--0--!C%+/5/8'-)7,1.&'),-'0/ #-(+')A";.3'+*//1.377.**7(&'401-0-8#)*3>3,-%*,%*.0.6,1+*567+.893//>14#9$05!+,-,)%D5,.9+60,.+1=-21'5%.(//+&*,*+0>+,1%27629"97-'-*.*'!++01**&%4.+1%12:@?&5'97,174+).7A:BD_\{‡…‘•Ÿ†‡ƒp}f[J?AF.=*=31$E376/31&9#*<2*0+7#&*5'71,/&5/%43"'/)+--#&*4))/86&21/252€Ö€×€Ó€Ó€×€Õ€Ó€Ø€Ú€Æ€×€Õ€Õ€Õ€Ö€Ô€Ú€Õ€×€Ö€Ù€Ô€×€Ù5,27(401)45-(6)9665%+':-A9%+=12*'-"&')-+,5.5'&4+,162&+1$%3,$4,50%+833'1,1.1)/,.3%71$&*28-2.06/'0#),5$:,'*-/>0+-.-7>*)$+%(3'& 0/()4)#,+#7&,$,'='6&+&/'/5-!'3.28/0957)#++))(-:12/.6&211%(/(1+!-'-6*.#53!*-/() %///7'#',),%2-1'57'-1 3.4*2(-0!(/-0*#((',*/..45&() )7+.+08)65*'.6/3&2.%/,0),%*7+3('104<5/+#!..,% 0/4 4/34+8,2>61)A$=0&--*9),3.5.//%.+%5-#4((//(//53-*!/5.&!&',5%,-$(,7(58'%('(5%&4*-2%+'8()?7/309.14($,040/68<25P2@)..4,(+1'$'//)+7/)1/!?0+*€Õ€Ô€×€Î€Ô€Ø€Ø€Ö€Ö€Ç€×€Ò€Ù€Ö€Ò€Ù€Ù€Ô€Ô€Ô€Õ€Ò€Ö€Ö-33"4'.408.2(.*.:11+480./+;46=?QCC_V\Y\]`knTYL?9QB'/6>*9%-.1,'6)%9148+..9>77*)62$'(';"6&+D20*1'88,1$)037%7-+*)12,3€Ö€Û€×€×€Ü€Ú€Û€Ô€×€Ã€Ô€×€Ý€Ô€Ø€×€Õ€Ó€Ï€Û€Ö€Ö€Ù€Ö%/*,5;.%4"&)+//61(2-:*(%/3+B%6;A0%9001-,&0 1/2/"2 .(1(-36,5.0%0,A.0*%&1/"7&<3281&*-,/*,+*%'413)7/(6"36/'!1/2,:))-/,/50".)-1.-/01/((1%%/",-12(0#,-(25)'3'8-!+5+-$0.'&,31/.,014 #+$-%37/&0.!,*1&32%$%72-.*0/13%'*1))()2*')0.+951%!6!;4!*#!6277;%94*-'+8)-1+6/- 4,').(-4(=>16#&7:#33(+/++,++1+1-1&*)/*/5#%((3E>/.(2.)9"+*21-+ .*,#---%95 03 .+/4)++$0+-87+"4,*%2*-72/;,6).9'#0.2.''5+',(%.+//'*+-+1,'8(1%1-*)4+-.!1/.+&=%!0/?1=I:RQLC?IZ_Y\R\MJ<9>)K?@2904*2+9+3205(,527<;/+(071-78-&)'02!25.-#4*2770(2@&0:'5+4'2+,0.€Ù€Ú€Õ€Ö€×€Õ€×€Ø€Ü€Æ€Ø€Ó€×€Ù€Ù€Ü€Ñ€Ö€Ø€Ò€Ò€Û€Ô€Ô:1/1('"2)5D3&%0*/62/68&5+-<*&&6*4.73+1:%+*-/6.,,*; 35%*473'+5)'0''0---0#/'93)///%60$2.)3%0'/0/+,+-*2,'*.%*(07*%1%-'62/2*-* ;..(/+,$+4!*)+/3',/!%+'B&,#93'--..!54$0.2/,)1(.23,#89*!6+2/./(2&+)%,3/,:+,13''B'+,2,+60"<-5'745+/-'"5+'@'.& *4,5-4.)>8+*70//'='!+!21!3-+./9..+-&$()'5#'#*=5*-+.&'93!$*)% (,(;*3-4;-.*%/-';@,)#-%+3.8)$( )0)2+2%3/*)=(%040+/.0(&*+.&.*.***;/9+/.'=&)*,0627*5!&+-31#2*+%6,#*"/#*/1"&4/0#314403+0.34<5=*C/2I>DCS=:31-+-/5!)67/'0=*7*7,1C7*28,,+/#/€Ø€Ó€Ø€Ú€Ô€Ô€Ô€Ò€Õ€Ç€Ø€ß€Ò€×€×€Ò€Ó€×€Ø€Õ€Ð€Ö€Õ€Ø@/1.'+&3&)-%0'5=*'1-/-/&,?8/(18+(6>-/'/5/#,5)5'*..7*1-30+/<55#3'*612295)2*-)++*3&/++*$7-);)'@&$61)*0&/(,*+-(,$-44-330/-$.+-'9$6<-&2+0"+$+,)(/=2+; ((+#0+)723#'-/*&9.(*)&+!&#*(/6#,19=(%4"#,"+%&'&/.&'.(%2!%0-+;*9'=373.2)-(+.+5(!1599)72;)&46%+"4+56*8#5A+)$+6,9/6/(0.:3$-/56-#**1*9./#)&'!)52,.+%/25:/71%(-%.!#*0$++.6+,9$?*&,*>)4+C**+,*,-,0)19+3-&$' . *'3#3*3*&5),#0-'/6.5"05+-#2%14%!$4(1&-9%-)+%(./ ''03)'*+1".)/=5,%(0934EDEABT:>MB=:E/BA04A.9$!6*+)A.*40+)1-6'1$604-77$0+20,&'& @-.11.43'11(*.*068210/170€×€Ö€Ö€Ô€Ù€Ó€Õ€Ø€×€Æ€Ï€Ö€×€Ô€Ø€Ò€Õ€×€Ó€Ô€Õ€Ù€Õ€Ò)/.&-,;7%--.0119$40'?,2);.0'+&63(201%05;-44%//"&4%-4:)7,<'065&504;8/,'6*4/((0#'+3+/)*6!2//8(*/5+.2)2)(5,00--0-)*14/3,3+*+853&00*%-())4((!2'1;&,( 7/:4:3!2'./00,0!5&'++A%%+715"%!%,%')/+"0.7#*4//-(<2A2"3.((206011"/340-.-%7-''(!7*/1;)/1('242# 8/*4./#0#/$)1*"2&)95&@30+./+%)/%4&'#',)3 $.-<$=0)2*&')9#*&(%'15+2/,4-%3#-.)'-6,",,&)')81-,'+%4$5+16*,47@#',7) 2*/,)*(0)4*+)1A,0+8,"$8,@?>3;=E%*B=3762.'<+,7336*9>+*-/@#14!=,025!//#2-%4)1-)*)'*181'-<1&43)2+6A.0'1€×€×€Ø€×€Ó€Û€Ò€Ø€Ú€Ä€Ò€×€Õ€Ø€Ñ€Ú€Û€Ø€Ý€Ö€Ø€×€×€Ó;%4,E0&/<1(=261<4"/64+(,;-+31<4/2-!5"565,.(*)/!.5%-$+3(*-))'.+/--<&.-(.%#-5/*#'2/%+,70"/$6 1)31.)2/1>*2+/8250#%2*(=,'-2 '")1/6 (&21+2//.54+/52'-,#.&3+%-60"7(-8./'0*5;-..480//$&0 3/5''(0,%!1-/=10)1'8(#(#-4)<')3%-1)4) !2*1/)*5)+2-23'/45+;(-42)/%4&%(1)-."&9.96'.),"4(C83('+1/6()*****,7831-+3),'800'$5-//1(-).%+*3,'8%3:))$7#1)$062)#(7%+,)62..9$10/('82*/0/1---)?20/9''(,.;*-$2#'1!('&;/(/$2().93/32,3./-)1'6*.,55:/'-013529?<3;:191J6+;?>?320;$+*-),,;-)1.130+0C/2))',=&9+/*711(-72./,,1'3,8./31,).*."-)'.),''€Ú€×€Ø€Ò€Ò€Ô€Õ€Ø€Ô€Å€Ô€×€Õ€Ó€Ü€Õ€Ö€Ú€Û€Î€Õ€Ô€Ù€Û543++/(83+&3)55297/:2$5-&-*1+38)%& 566-+283<#02'.-#+7')-,.4,95)'--)(,.'2&,&<-.<087'%*&21<(3&'(-;3-56***E)&+855.'1/+/.$.0212+1-48---;2(@/.90'8((/))--&,#<=+22*=10.'05(+3/97(.:1#+3#-+(/)*&&-,(%-4./%"&1/!5-'*0@:,.:)++"6(*/9--,5(02!++4+/,-'+ .#.&'B(-3*010'../(**4*'*/,5)'&>2.-!' *#-0/38/+(.&,21'%-(4+-2/,:7*--(4+/3;08'%0+(" *).7>$+.1.$%2><./#0/4*6+=,-1-(8%,-(&$83/!..5)()3+7,%2*)%+&7.'307,,0%(, ;+"0--& .)-2 :+20;B33205;2++#?)8E5@9?772)5=2;/($(64(++&=1+*&27583!287894%5'3!34>)4.4&**/+.)9+/6(5?)39>0',32#'24€Ù€Õ€Ø€Ö€Ú€Ü€Ö€Ñ€Ó€Ç€Õ€Ú€Ó€Ù€Ú€Ø€Ó€Ø€Ö€Ù€Ö€Ý€Ô€Ñ=-A-,1***+.-+'*3)2&*567-&&..46+/98=+<+8.+,>)*71.&*@733090.#/..<#5,9023?.+7'),76/0/#.#*-46--!15497+2)91+)-(3/)*-3/4*7!/'+'7-4*++$60(--**0+3":=)&$05*,;**:9'.3#/-//3-',6&&!#32.12!.1//*3%1-'4+/""4-1!'979'/1.14*3(.;40&!/"-)("81*/"*)%-1!120&<&.%<')%+*'40/=.&053*(4),-46)2)8#03'-15"-!13$,6552:&.$.4+"01*)**5#+.);,'30736-3.5;#')26(7-,!+*0$)+(!%5&0,10,*/6$.+ - 87'+"/+3+'17-.0/4$=*$.<1++#6$;3=8 +7(&1.;+:/8(=#),(/ ,5B6:&3<3.!/6::/>.:944:":0614,0",420,435/!)+;18%)6= .6%(0105(#-(.%9(70100#:)5%0-;=10+>1'€Ô€Ø€Ô€Ó€Ú€Ø€Õ€Õ€Ö€Ä€Ö€Ó€Ô€×€Ù€Ú€Ö€Ù€Ú€Õ€Ø€Ó€Û€Ú/(;.3182-22<1$,'6/)-*/9/+34.3,!)-=9%(,7:'%3A) ,3,,')),((=.,8.)8-7%4./8'+,(/$(,/4+-4/,")(/1$'&(/#67+)5/3/2:$0/7;*:24/0$+0.+(("3;'421, 156(+7*./'2'-*81,*0"5'&+5)/+/!#.%%+-3)-,*),-;*&&)''9/$$!&5)(8+1&#)(&/',,*/4%+'80./%0*(41911(#$(5+,.-).9"2-&)#4/'1/+.03.1 3$+/;"30@@/'+2.:"(5.-5,16/%2+8/60/'-'14.,-0!9,(),:#/245"/*3)$/.(44639#-#(&: .#8-:6&%7.&23'67*30)&/.%,1,=-.$,"22),:#$3+'/2%6*,/2-/%.*-;--# '-/&+1.2/0734!<2$0,*%$?+,6;)(=54.9;D/+95/84/'32,/,60?-$9(#..716"(>.5)%95:!""2(&4<++*,/+7'$2)/<1!0+$//.9414(:+€Ù€Ù€Ø€Ô€Õ€Ø€Ü€Ó€Ú€Ã€Ø€Õ€Ù€Ó€Ù€×€Õ€Ô€Û€Õ€Ô€×€Ö€×2"0%3#6+0-+/,,-3-5%514.%.-+3)<%94-/*;/2//*.2#;&21'8/"4$*-4/742&!2%+9/#0+*$(&))+!#0 )047316//-50,#1278//(%11&.$$// ((,4%+",51&2083+*)..;-!$&$&/)#11-(',4#'.02*4(,,&/$1&*/3/$3',-':7(*2&90,+/8.+-+33%&3(.7-/"51,).*1$/+0,.,0+:-0*3&6?//C,5,!/%1'3/3'&)916'+7+*$335:P6,(/:3!')/*4#.+.+$-#-./7++-#$',:'53.0!6*(33; '5+-*9,,(()#/ 4101/H/"0(/'+$2/50519*92):/)&-&::5%-)%)$5$15-92+'0852-D4A6,+9+2-)+6*).#%+7)012&+.++'.#+7&6962/5-30)51B5.1445 *5/)45.1,B:18/243%"',,4?9*401.1#7:-& *+,'(&*4380+,3'9''+"5.02(:$-6D+F1*.5?€×€Ò€Õ€×€Ô€×€×€Ó€×€Ê€Ô€Ó€Ô€Ø€×€Ó€Ô€Ö€×€Ñ€Ø€Ò€Ö€Ô&(&-+.',2,!//'".3.'1.B''%(0'-370,/*,.00-0)51*3>;*$+)7-6&$3+(6430%$? &.'&+(5/#7'%.0+)5,16/-,'/6,33.(,/%*-9,,!",,.0'315*2-,-2.-$ 1) 1+<1$)1+2&31'+, 1,/-/.0$;&45!+ 316,1'%#.%4+#/)%2"%. (.9&&5)0.7!.&&*32/09-+,2.*03#./+3+:$1),7#;0-70)-6#.36 )8.1*(?4(+,@6')%631$ 5&%)/+/*5)A:)$#3&&)-*#(/2&73$2'3;59/0'/+'-% '2$+(,75%3=-( 51/2)+*4$-/&!7-2+1 1.--!/*0.-0&*+0+328!/(+14*' )1%5&+6 .<-7+'225-&/+/61",-51/,'&+8 --19+!/,5?50'7/-.'45,&-+!6/4,0-128/15$++2(/#-('5--0?)/+.7(3/"302&*&:.*'7/)-3<:/'-&0+2&-")3/7#0,5€×€Õ€Ô€×€Ô€Ù€×€Ý€Ý€Æ€Ü€Õ€Ù€Ô€Ò€Õ€Ü€×€Õ€Ø€Ø€Õ€Ô€Ú#4,0%)*30.'/-0/11'57+0+0C'()&27)=4/4:,=)++')6<1(+(7+1)+0+- ',"/)(),36+-3-0;*68132#1)4.+1&,:;03''*,$'6'$)/0&"',0*-.%*)=-+*%(1(-05*)+;*&9 -(!?,!6/)#2.307/5*<!'*5%+-"(),+<+)/-1!,-3)9)'425*#4-68*'(;0- 1#+5(%+516(01.1,)1*D %*$32:1+/*)+-%@-!7)05-12'.034,(%0+6<-%)&)21+&#%%/&90)-1&=110$1,1?7#*C6,:*8D,("€ý€é€ì9%).'1.)-'-0%)-06/9)%504%#+8;2)%1*%30.3"310,3$5+5"#0;,0*;--'(*+%/1(-9/633.)4*/1/2#&$?+/01.)+.*/4#33313++:=$0,2;'65B*7901$/-4191-0%:>111H134.7&07/8*0),:-,9//*4&)/''+.1.& %)*--3-#,(3-*/6,0(!1+8'€Ø€Ô€Ó€Ó€Õ€Ö€×€×€Ù€Ä€Ô€Ò€×€Ö€Ó€Ô€Ú€Ö€Ò€Ô€Ô€Ô€Þ€Ò4$4$+4.',5)43+;+/*,'(<('%:$9--9-1:<6#'1&3152(,&3)0)09219/2(20),8(21& 6'#+&.,3),,+272*79-!()3C7: ,-+,(045-5,,7,4-#4/8 /:3'+35;//1')44-$/1$*2%.5' *150+.'.>+))7/!,2.6*$'.'/!<333'&6..5.'!/8 47"+30+.279**)"*(173:1/-13,93'(&.(1+.+2)*1/'**''-!-;)01$;=+=1'')#:2-%..+ ----,-&"%)'1) 1)$(&%,6+< /5 -4%$-+/&2*->+#4((,#).21/',%6 /*0"$/'14!;()3.*'9(($6-1:267-4-%-/53&005"+4(5(#2.5(0#)!1.4/',3+");'-+/()0$3210"**"2)#&./0&+/+,(*17,<&4+7+!(--7;;/.+:'(';.01'74/19183+//60!3$,6!1,*1,#80;>0750.2%279!-711@(-4,5€Ô€Ï€Õ€Ù€Ú€Õ€Ù€Ù€Þ€É€Õ€Ô€Ô€Ö€×€Ö€Õ€Ó€Õ€Ô€Ø€Ñ€Ö€Ö9(421($'A/$(12 &;=/7-2&8 :'@2.)8/,'/!//73$C8*/.%,0791"*.+)+**'-/+4%6522*-,1)&12"207.1:'75.)&-)(*,%<17*-.**//3 +!0>&)/3/()-/$%'0)09020(##1/06#2)*:))-+(+#/)3+5(2@0(&+"*)9-&%'#2&&+*/; -#4./5!301*&1-#!1+"/%!#<2*%$%.7-:5(-1'2.&0)03)6/%%;&/889%'839+1;)1+ 038/2/54+.%352A((&+))2-$4#)5#/-'20 ).0)//1*(,:.++7*,++0)+#3#7 ,.*$-)/+$-0$,'(*)/%0*0+*$1B4%4#4(*53/(//%,)'27&%.5///2(/+#(3+6+ 2)#);-51'&.3'&/-+2"+.6=).>"24)-,8..2 )268$?5%+,-2,%1-$3-$*,"4)(+50"*4*-%>"+))28)(2%1-5*02,3/'./3/3/8-.+(51(7)4-(1/15+3'*-+1€Õ€Õ€Ø€Ö€Ö€×€Ø€Ù€Ú€Æ€Ô€Ø€Ô€Ô€Ñ€Ó€Ù€Û€Ö€×€Ø€Þ€×€Ô$5%4267+(40-+8',#,12&-.'888&..3+*2%//,/++5+-,2&108(.(1)'/%$*+)),&+(&1012+*74%/%.#*3.70#&$6/64)-$?B;78=1$##<=6%$)1+*)//164-6.-1&+412')*)'+,2)2$!*&4D!/;&%./+=05;,:3/7+8---1&(#$/+,#( +%#7)&3/$(7'/,(0*.$'*10004 #, 56-)5-7+2#*17&*531'-?'&+,.52.#)2+,5-35'+$%$@ .4 -3.1),/.5(0/+*8'19)*..)-55&/ 4$(3,%%-774,(%5)/63 06+625(5(3/#9.9,1.$.%"*80<1!3,&!,+1/-<21,)21-*+!#/-($((%-1&0.-++5'.,,$'%0-7!1-)-&6$.*,00)%5(0!%04-.*25&$41*!+31 3.11:**&)-+;,.-,'*/'*6$-10/-$,"=/)4)+7.*-8,)?8%8-*+)1*#=3, 06)B502*/5,7:2#19%2-€Ø€Ñ€Ö€Ø€Ô€Ô€×€Ø€Ô€Å€Ð€Õ€Ô€Ô€Ó€×€Õ€Ø€Ú€Ú€Õ€×€Ø€Û/*2)0+3&+(8-+'/*+/80'>5&)(7-61+30/,:-6$'?3%/(,0"+-"=.0-3'$*% -->%++-."%#+B,42/1,/0 2/4&81,#-# ('/'3&!4)*1'!&.!9#@2/*%--#&/6/.F':,%.-/3'5:354+-":---#&5/*=,$/")".),,20-1532+&=-).2+%.,&2+080(23(0$,, /3-*%+%8.//5*4,*1$=*+€Õ€Ñ€×€Ù€Ô€Õ€Õ€Ø€Ý€É€Ù€Õ€×€Ô€Ö€Ó€Ó€Û€×€Ô€Õ€Ì€Ï€Ô7*,7,+(,-!&,2.*(0'00:;$&.7#3/4!,6,,.2+)/C%/4*8++-#-(/3 +.,)9511*0+8(,46(+0+-3.#3#6//!)$80+.6%'=>,%24+,-.,.+001*3)%/#:&'.$--6$""7'5=.&+#/421(!&&30(,72,4,8/0/5(.../-2$%397(-.#'%*3'.+--2$/,8(7-(%'.,+",",008&.<-&4#7='-,+(0+.5.104(3$)3;0%**#,-33/(+?2340-4+51&5,201%/-9'+1&( ++7&%!%8#0,3(*+0&)'.*-:.40*)%)5;6932(%+5)1!//"&2*"6 5$3=-.6 +0&8?7./#3$-/.A6.)0-52.'4/./*.-,1++*/#,+;'*+1;3++'%-/)&.".1/3D.2'&4,57''*)*.28/)/-9.-7-/=-+6#.%6:53.#166-2+76;23/:1701.%4&%$//>/0-,(/'-+:7'8&32&-)6,. (+2)#1/+$#*,-0(08)$*0+,5)&136312523/+$&B5,-2)-*$1(3%*+3..(1/'+#/-%./$%2#!-/+5.1!21'()40)./%+9'$-7;&110/-/7-=&(+012/11 )7*#0.*./'-4,&(+2##/-,#2,-&02//-**25$-4;2<,944)-30"'-&)6$%'5+*162 5#&2203-2,+(*00..+,,''!9.81/15+)%*.9$0/,'/.1/&1'",,=$,-:-1(2/&1$2-,$/'1.3'3/)&.**..5%2%: /(%87+%+!&'/('#"+2,-4'%/140)4.$*/(1/*$+@"+0#1)1+#1<'.,--+%&'!2/*,>-3/0/'22&/):&5-!4$)4*/5360'.,%2//%1$63,/+2(4,.0.-72+#,,,2+-'2//=-,.$>,"*15,%2*5?=%!250;3*05<41!!€Ï€×€Õ€Ú€Ö€Ö€Õ€Ú€Ó€Ä€Ô€Ô€Ô€Ñ€Õ€ß€Ö€Ò€Ö€Õ€Û€Ø€Ù€Ó3/(('30$)-<>/B$6/.42'//221194,5!07$5*/,00@695/'/./7.,(+."50(%<%2+./+.*,/5* )#00;%5.&5.3629;,*2*'-/(.(22 )0*14*+1)"*8880+/%)*/B.(-%!.<2-$1&5-.73%"3)++)30$/&(,/% 3-2;*))*%7%5.+*5%'0&3)%6(-,+6""/,,&".+-+2)22-2/#37$.'.-/;()415'":;-#0.&%0%)*+8/*$+!1"*'--+)172,'0+--,8-+'+').,,:.(+2)/+/(-97339+()+2!&46&3,! +11+)1-&1-$,9)2/2%263/'%9+59.*50-!15!/7'+'75/),1"2!-#-61'4)%-/+*12.,'/!-/'-'(0559-55&%(%*38/),-2--0)/>-.+18' 678(-9(+(.7%1')/+-:!#02&'176-",*1"3+0D,.';+$-48(&/'$**"1C,3 "3,),90(/3.98+05/1-89'))426(**€Í€Û€Ö€Ù€Õ€Ñ€×€×€Ò€Å€Ö€Ö€×€×€Ø€Ó€Ô€Ö€×€Ü€Î€Ú€Ø€Ó/=!>*B(5"))2'4)!49'+(28.106*80'&3(82/&9'3- %2*137))/%!5,<43)/3'*7/.;1082#2.757241"7/4(&1$1*+.1#4,%)%+%0*$75)(&,'3&>)6-:-0+(7',5#.3*"6&/,/3"4".+*+-'':8/1!5380/)/*6-(*'.4".$#4'$+&)60"7>(:0-/*,8'*"7+%/"*)$0#!/4(4/62%2.*:'#./*++.1"90/-)/5&<*4-),$-7'2'15#;+;%%/2%*.5+9-#+42#.6. 00,(%1/$*54-'**/0%/)0).)-(+!(%'55/11+,'),2,./#//-$!#.0*(+--#429$/($"8,>)01)04+.='-/'1+.-3%'#1/*6 '/&*:)$9):85/)'(42,'2.213.+!.'$(&:10622# * 25.42:2*6"3,'./4 0&/+53**&-/,%/2-50-/1'*,/-)2,.4+2&#,@+-:'0!% (1A%).5*-(7#52*,)$€×€Ö€Ø€×€Ý€×€Ú€Õ€×€Ë€Ô€Ù€Ô€Ù€Ú€Ö€Ö€Ö€×€Ö€Ú€Ô€Ø€×+,4'3,&54$7.43'4/.)&-+-8((8,+)-)/"('0,2;:,4=#//$.+*22$$1!8(-=+$ -(3;157<.)6&4)+++572*.'.)5.1>2%0/)82336*34'4617)02.//5+,8#'3)').%,1+7' ",40)..(9*4F*15,+)*#54#,&%*769*%7%6+/!2'<1&$3/2+)*;6$-/636!+**&6-'-<6$+*#"0&2*-'-;'&5+=(030&0>'0(2+-3/46&64+#(/1(*6<"3."/4731,%02ƒ£ã$8/36$3+3'+.#/2-3/!$&/%1/+:0.;.?*(<4*,+2'%#'/$/,/,*10*-9(().'%4;%93>.946&$./5,<,.6%#73.,6-'),/&*1*2(1.3*/?*),+,&,*4.7- -%/4-)*/++:)69&$,7/4#+.$($6(,07'0,+,/&>5&7;-0&2+*-<%71.9&'-,#092.&(.-,93/2,&15;35/,.-16.2(,/"C8+-+&3(3/(*/$+-.734*&1€Ò€Ö€Ù€Ø€Õ€Õ€Õ€Ò€Ö€Ä€Í€Ñ€Û€Ø€Õ€Ø€×€Ø€Õ€×€Ö€Ü€Ö€Û1##/9/6(!.++@97,5044843;&7/.2=%3*6-58#;0/0)*/%-+-.,43(*#/%%-/,2D/%28;05**0#4$>%90$,=31+,%*/@*1,.*2*;.66,)%/-33).1820-'53*(0. /7'(46,/1+5-)&6&;2-!4;3%+&')#*(&'2>3:6%+*:()5!3,,1--0-%20:$.(0)'*#5'4%'.%.,41"3@!9&1$/%+841,)33)4+*4&$.+8/1-)9&2$%<++5'!$//6+20+!-*/ +(7',&Ol-&/%(*,52B-3(/3%(&-/7(58.($+#) /-E)**+'76"/54*"2%/ 7=*,A+-72>#'../7+52 2*'/,'9$##%1)%16,)$(+942)3),4.()$-&9;2)/.$24,1%)1/!/4/'%+52;22>3 *.%#&2;,/2,"-7&%,&3-A!E-.?2+258=3*%+/-8-,;*/*&(1+1..1&#"*-+,.03,/:%3%3.$$7);7C0',!050;".153€Ø€×€×€Ó€Ø€Ø€Ô€Õ€Ö€É€Ø€Ü€Ó€Ô€Õ€Ó€Õ€Ð€Õ€Ú€Ù€Ú€Ù€Û3"21)25)*/5&/+>;(/%;),%))-,3,/$601*31+))..,158*/+5):+4C21$=42)*1-.,+2/16+9*/&+/'-)444*3)9520=M(,(+)()4&*2'0.'+24&(4'//+%%,+#94+82-€Õ€Ò€Ñ€Ü€Ø€Û€Ö€Ö€Ù€Ê€Ø€Ô€Ó€Ý€Ø€Û€Ó€Ô€Û€Õ€Ô€Õ€Ð€Ñ*90.<(,6+$1,/440!-35096(173558H+30##3=5<1486&*,-)(#/9"$*/*3,6.3((:36)6*403(),)045-/-#*6)"/*)%.!+/3-(,'/(5,/32+%,*//*%"+-/(/<,96)5(54+'.81&,.#3:6.*;4&$,06;.17&0*& 505&;%--+)-2$42#2.052/-)86#)=7.7--:$791'5!".,+):'24;),1+#7/73-'/*3630/-8#&3&-1'!0*1*&':1.&.5-&3/+$+0<*#()/!'..35+3&8,2.&1&9+"#2');*')#.2)")(092/5/3+=4$*(034-79 &42/#'-1%+.*-."+/'+""2412*/!/; 4.6)*)7&,-9+* 7+)3768805<5)#/+7/"1/+*17!//51/7%26$103*2!*)-*,)'(,+60,+>/836'#+)(71,#'72(6)#/4$252.2-%94)/,%/0%/.4!72(6+-)5",$+&)4/-#%3/(+=)€Ò€Ô€×€×€Ù€Ø€Õ€×€Õ€Å€Ö€Ô€Ò€Ø€Ø€Ø€Ï€Ö€Õ€Û€Û€Ó€×€Ò.#4%6*6',)8*(?(+-/&;0)6'4$3!1:/1.:43)*;30490929:13/4&7005?$0.;&$("$8,1,-2/.56&'%1'1)#//) (-,3,+(5,6-/--<))3#,(0'1%#3%02')"/.)-3$A%'-#--&%)%)6$)'%/'45.'283:4"564)&/67/33'*:0"3--,(*-7%//'.2,%-.+-2+4).0.+.%,5'/45*4.*'#7,(/<0.&5*/58#%,'3,(( $5*4,1'*;)%6+))7/.!.%25/0<-0(B-$/2'(06:)-&1,+".-&'>11').-.&1%0&/<#62,'&55()--+3*5)(:.-#>)2/-48/,7(361*2&*.53'4+--0515'+0'78.*+*2%-%++6(;+5-"1;+%&)(%0:.*&*34.2.-,/,,%,.%9,'112.8$-13,?(55(3*(7+"%0-11/521--04&,!>%/3,*0)5.52+%*+/*(3)3+.$(*0%%%0*0'0$%(-9.0.9/6F%0130€×€Ô€Ð€Ö€Ò€Õ€Ó€Õ€Ñ€À€Ö€Õ€Ô€Ò€×€Ú€Ø€Ö€Õ€Ñ€Û€Ù€Ø€Û%%,5!,-+"24%6324,2':$*")+?2/,+,.1(//('0&+-.2&7>9,3*%*>++!/,-902#2(-,0--"53.86./-+3*0/&4156+173/(#17%&//'*24/)3')5&+'+?)'90%1(=F1,&1/(8+. 1431'098,+0=852&&0 %+)//-.=(+.%2&55?,557:2'&5.320($5$62)-,'.!&634%1&&-#0*6.2*13/)62+,*1-&4'.-'*2&%8''/,0(-50-51+0<%%5$#,%$$./2&$./*!$4%&0)-.///+61#5+(*#+531-80.-72 +'-$12*2*,-194)+*7*2&3.,*20+3(/',#/&7/0(-,%-,6,14')83)/#*'%01/;+:0((*;(.,5-&3..459'5!15.%4&0I)10,.#7 %5-'4/'(*.#(+4/95.'&3*-*6+)1(+1*. 2/.@+13+;(7(,*45:*)+/361'@0&3)4#*0936/(3193!#&38,,'2* /&;:#€×€Ü€Ù€×€×€Ö€Ó€Ó€Ó€Ç€Ú€Ô€Õ€Ô€×€Ù€Õ€Õ€Ù€×€×€Ð€Õ€Õ,43(=0-/&)-/7*1/650.'+0-*)+;'0102/918(.):&61'.43#5-+,""&<34/(/&*$'<)/"3//./5+++4'++,3$*6*8+$7+%+;/,:77#//+%A'.,$4)/(*,03&.8)(.(.'+".&B) /;/1#-+*!/)*,+,-.*.'&1-2>&-36,-1)6.$46)-,60).*,3444286%&,.0(()7.(,5,/C,2+158/+( 2%22'#2 +1#,+".)(11/*40+.)//,)4?<..8.--8.14.$,1"$3,(-222-#781$'',3'45*-="+826#41,- 180&#)$*!1%3+1$6/8303/%$-+(>%/%91#.0+,6*0:.&/'%*&8303)./80//07.'-(6(,91#,5+*3)0.&29$.#97,86)%3$-*,2*,2,!7*+&'+/-3'/,5>1';<;(4/*41/(#,72#*2:3004/34;*0/4..(0+@+4A+&2625)05)/7)6(.(1:515)1 6)6,97"21$0,7,/€Ó€Õ€×€Ô€Õ€Ù€Ñ€Ñ€Õ€È€Ú€Ù€Ú€Ñ€×€Õ€Ò€Ó€Õ€×€Ù€Ó€Ù€Ø630-53-(02-1".+-56(.0)1:$)!'2,+10-*.6/.20.:2/5:,55.,<.="+/.'(-0#220):"-/9#4$-/(9+11,6&$!+ ;5*9='5*/A)!+1;%2**44(+3+8(9&5!032#-.1,)24*61-%7*-,$2%)4-+$22$'2.(/%416C. 9#-1233/*&?''(7%;A#*'*)**';$**.9)427B65,(+#:3'9/A24(5/,)/.)/2534+%&$?56($0)6+2&*+/2)4'1-36(&)0/,''./')5-5-!741;%/%(4*).'-/9!02+417253/&410+!'28&$3+1/!1'4=-1/*9037#*&43*/:(0' +3"3*'#*.0140#/%B1(/%13#2*42,%)/+#7,+$*%*(/)-(+.9%4'9 .*/&&+;++3)''>#1,4-/(5#;)(-2&.41=(%+4#;7*/-,2-2*+$'$*)&.0**0+-) 315+0;3:5:+6+.)%*:4.63)(,7!2:5,-&7-0.1?,!%%7€Õ€Ó€×€Ñ€×€Ù€Ô€Ù€Ö€Å€Ö€Ô€Ô€Ú€Ó€Õ€Ø€×€Ñ€Ù€Ô€×€Ø€Ô.$/8?1=/+1,4*$.567'*+$,1117:../745*00*)6<'.0(<-,&1/'-2+8'$#0&63#*)1.0/#+4C+&3-3=*2.40+!0-/9+24-4/6&"/:8(*>(/,/,+('/*,(1)%')$*'3' -ᜌ$*"#+*4'!7321.20$$(04,)*&,09)+*6$!5"*>5 20(3-7-*&(.-(45;-" 1(2,2+/%!($=&-(6*4/16+&30*1./+2$-3"3%.&'4$*3,,2*,+15(*+.;2$/.'(-.,.4/3#//*4+*1.330(+.0,(505->=3,&)!0"&&'73)6'),-/2=(0,9*,;**-*1+(6/32;0*2'1-23+*60/+%37.. A,*).!.(2''9.('#3,+$32++61-3)&))08/63/=8%%41%)2*50,(2.+(75,252%25)-7//94.(-.$6&/.,+*@0.%0*'53*3 :)&$18!61)+52/.429,./$8!7()4-4/++6*3.+.%+&2-)=5=3,+,:()6%-&)5322)+3$6,0)1.3'*!13+#*9+4*'+&**'7 96%.*'-(,3 &54-*1')1,.)+09&2,-'2&/,8-);'1%//'1!$+>6%010,1"6,2/*%//0#'>('-*./).:-!),--%*)80.+3(*&17,,0'9+2.-B€Ö€Ù€Ô€Ú€Ô€Ó€Þ€Õ€Ô€È€×€Ò€Ø€Ò€×€Ñ€Ú€Ô€Ö€Ô€Õ€Ö€Ù€Ø)0,,66+*%.*9)65/38002(,+*34"7)3)5'0,/12&*4/.5=('42/6721,%%/()(7:=1*/.2 /.35*&'%+%,3/-'1*4,+15-38!4,2/73&5/%&+2/.!+;/08.*+32)''#-48,6/+/2+>,%;.+'*+4'=10,,8+'5%!=17'81++.,=*+3;5-$"+51-4$689-)$(+4,'0*/=/--+3(,-'1..;.,28&)19'",7*-=,(4+1/+!,&.)'(2**:3481+*,%/,&0"1$&%)3#3'7 ,8/,+2)'5+5024"%(9%691//%))7(.%-9)54*)5%#3/%!1**616:#253//).""16.;)1/#+!*/1,/).1;4+0*-1'5+-:,'1++4&/*+A4.4)?4'3!5-)",)+/72,5..0&!.,6%'*'17 "'6,/1)3'9'1?&0.$*'*)15,)+%) %3( 5'+"-9101*..11;+7,0/+5%2903.&4-0*(8(11+9&73*.65&%+3.K233-,€Ò€Ô€Õ€Õ€Ô€×€×€Õ€Ø€Ä€Õ€Ú€Ù€Ô€Ü€Õ€Ô€×€Ó€Ü€Ò€Ö€Ú€ÔA7%42)*+30+-(B9:-35-71-606%//$%8))//.,/<0+7$&**;;--3')$9'0/)%'1)'/(#+(++34/.-*%,.63*-!+2.034;5-;((!&'-*/10#/5,,%;%2-822',,/.+$1*;'!% /.*7%2-+' +:332//+/+*)-**,2%<'11/# 5/&1)1',21%'3,0+106(2,61##+.(+.$+311(9$+,6)/96.$2((!,2"6 1++00(1(/4-)*)6//$2.6/0+.+,*+,2(,6(-2,*+(%7+%*4')*/6)H#2925012564"(3%,63/8&50$,&;*%3&(4=1/3&7>34*=.2(>/ ,"/:)"3)-$--D).6",'% %.'!:--)+*;+*$)+=4*0+> $8&%07+/;$'3%-*--/,-*5"--3*1(5<13'5&$/3:5'',6+2-:0$//'6/-#((,/*8##%%/2&")012,$1/2/*+&.*!5'%1"-( *%,5?*<7110+73:5?.-6)%(K.7€Û€Ö€Ø€Ó€Ô€×€Ó€Õ€Õ€Ã€Ö€Ï€Ò€Ø€×€Ò€×€Ô€Ø€×€Ó€Ø€Ò€Ö39+)/3+;+'$+97,4*31.86(:)*8/,9*207(';:7++''3(./3,#3"*&(74':+1,123,/(46;=-!+2*-$5./)45223-/?'2+81+1)142-)-*')&25,$6))-'-%+,71//-%+#-7-!.'// (7)!46-2./(4&-2/7+,9)!/6(1(-53-<#"0-(*%)$)%+$,0;&7/#6/+1ϫ'22*';#/+5-99"03.(2)"()0'/02-')+79&401901)7#8)*3**1)"09&5*'.%5,+&1,1+/'60)3+1/)$2*(/38(3,8/"02:*$*31-)+1-*> -+9&,)9*,-516!0)2,-4!+'+-526-21/7/1-52$0-39.-/5'2/:.&*&7*72 11 02)'7B-$6-?5//,30032E-=+$,*':*/*2/%2+,4.37/4-#++-%06&+6611=9")*/-132$&%2+&+/-140;#1/8,0#%40#733./,2*2,+60(75)(/,&1/2#7.$(,%:7)+47&&-340:61//46*-2(>#+$/"0"*3*3)3#%&1+7//(&'#1+050(,--%--(/*+0*'')1(3@%/-%",$/4;3&/2,(88+D,=2.&'/$0*-)/%---+#(+0=%( 5%5?2*.%#52*2,582-:0/'6267; 441#-6/451-$76;222-+#/)'51'0--)2*/*80152)8+*%) 1,7&6--,/,%$=$).63,"?.-- +1.2-$(1*129/%941/0/45-.,)5+&$1$2&)3$4;-7&11,)#2'./'&''12-/1'",5,7-/#'-*%3.##05€Û€Û€Ö€Ö€Ú€Ñ€Õ€Ú€×€Ç€Ø€×€Ö€Ö€Ò€Õ€Ô€Û€Õ€Ô€Ï€Ó€Ø€Ô3&$50:3*,*56455,-4*/(9)%+<1/'+=14=42()&8&/0.1575'**/-0295113+-)63*-*/0'20)'->.02&4$%3-/6$#70-9,0&,%312*1+/),(,+&0+/#+/*++"020./'/+7.&.0',7-52"( '51#3;-:/1')21)9)*'?#7'+&/:(+4)/+)'91!,#*.)5+,).2$*+022:>)46'$%"-25/3+.%/7*<3&&(*09*3-5-/+&7,1-+"0/,-3%".+/-,*=)/$-*0(./%9+'*(8'*(*2)5'.&+;#2+*/:'(+%068')05@/<1$/->7#+-%- ,$.,2"/3!.-7$)0.&4.&3(E3;+(%--@7/-)9=75+058.+2).*-)-26 /2/-'-0)7+*,7,-)'460+,22.09+-'81/+B&3/53.//: +&+(-6/5(&!80.&(6%-40-%€Ø€Ò€Ö€Ò€Õ€Ñ€Ù€Ò€Ø€È€Õ€Ô€Ò€Õ€Ü€Ø€Ö€Ö€×€Õ€Ö€Õ€Õ€×;)2+8&/)&).25%4)5-0(A ,)<*6:$&0<39'7*1+0$,2.76,/1/""0 51&'5,23,!0-*.0>3))3#,+''-,9)+$-/&'-)/19*2-)+9,4,/&!15*!56"%)8-*//#/-'**93*-'$/@-52.-+:0$/+2++,(-).-)0-)5./#+/4-#(35&01*-;;3")(0*5/;)('8%/70/$8*&.-6%%'1436!%-7*/-/&9)2,*26'+1=#///'&.&1*)C#**$+2-,%%0)+/')#31(- 0-12%1'4+.$4)4%-5(2731&*<,768,4482.,)1,7&3/ 1)(.#$21.9,3,1025& 7*#5+:-*(652+#*2+*+(,'"**5-E2 /%0:--/6-($ **$//,(.,71',&04)1*4* -7:-%5,)!-0+340/950'7-,':26'20.0+$-%$-,#<+/"1%1*?";/,"%.1/5#/%*%,3* ('4,+0(+4)&8/)/,5/)./.2+, 7=%*17;;!+/@1$/736D3-(%$/+&*1&;)&8,(&68()6&&9++#*'43#)+-+0%*#77*1.$#!<'/)-7>00:351*%950--B+8.8;"6655$3A($:.133'0%*/-10.//0/0%21,-1:.8/.%*,.4$*%@9$(( $5./24$/.-)/?/5!/3'",+2-++1.--!3/*-/,)*6,/1/*3//4%*1;)#*/-3$1%.8$66*-$1115$242*+*(-$6."6,*&4 3/1):"'26,7#9,?!.$+*&0%1($.'2"(((=";3.48(8:/8*$7'?30$05,;1<1+-0')#'2/%.,(32-%"'505./0""1++4302'0*04*4,:%*7*!3/%."/(222+(1)-$+. -+)0+.+$6-63('2/0$(40),#&.%*$,*++$*93*4,93/.)0';502*76(*!3-5;/"++/+&>615#016)$((//)-/: /255)8>6€Ö€Ù€Õ€Ö€×€Õ€Ù€Ú€Ù€Ç€Õ€Õ€Ð€Û€Ø€Ï€Ò€Ø€Ø€Ö€Õ€Ó€Ñ€Ü$-@$''*/**3,%+647:++5-,8-++2>+**488,')*,+!78'$*65)&--+/<)*'(,1&&"677:5100830,*35437315)(70+7)1+3+&;17+%$58-*//&425'/9*5;20,%2/60<%&+0+08%*;1-'/08-*3107:3(7):.)#++1&*5'/;&:,%,7(131.%$!--8%2=(,0",0,*.3./+''726-0%+6/!,#+)"(&.4/(+4.=3* $2'/>3',$2&/*--#92457/75*(.)(3.+$1&7/9+>7/-?-.)'93)068/;739")445,*/(!5++//"*(4(2-+ 2"73.50/,.)+&57''&,.+4-/""-&$$2,&-'-'%71#&7,6+/(0".55"(60,*"5$5*18,(+3#!/)"1%7+$.)-.1,.(-&,/2@022*0-$#/%#<.(-3(/#5<*12-+7-0*(;56&#+&&30.,70*:+-<02*1,/.20(+/.;10)',(602*,&7024,1(*-50+/9€Ù€×€Ú€Ø€Ù€Õ€Ø€Ø€Ñ€È€Ö€Õ€Ö€Õ€Õ€Õ€Ù€Ó€×€Ó€Ö€Õ€Ð€Õ8.,+23@;*44/55*:2,?1*69)2?21+609-:;4<&.+5*--50)583')+'2&&5<6.(6.,9.+%)6$&&10.:,'+02.&?+1>3)/)1>)4(/1/%+)*!$'$++ /-->6.9//)/048;033*!+,!$"+):12,'0.22.$.+1$)+/&(6+4,8/*(2!+0)3+(*+55.62+'+*5#--&06,/$1*-)-1+'./4-1%-27A)3&-&#$(/59*3&;(+';/+3.?5*$/)+(-+1/1+,5%-$)47,+>:5:48*(,<01!.($.6)+.3)36&4/+4=/31&)?,3%*/#-11)8-%'03/..-))1-4+%5'51.134*-9*/0%@/107(11),)))9-,#-$+%07.*/1:+8/2(/+"./,7*0.!>+(;(.4!2)+&-+&0/5(/,'1 /,>#-)/57:)('(2--7%+./)((+6$'3'))+,4"6$(0*78?5.4'>&'##2.*0/+0)&€Ñ€Ù€Ñ€Õ€×€Õ€Õ€Ð€×€È€Ô€×€Ð€Ü€Ö€Ñ€Ñ€Ù€Ñ€Ú€×€×€Ù€Õ,,1&.&@' ''.$,3(+,)&4&)7ϒ-1(74//1+31-$ -96.*+*,-231/*,*-33.!,*+)46'5* )2&*/5-+44.(2./+32/19.4($):)/ /"'#/%-.4!*-)"%(+0,/:02,'%2/2(-1.3*/4)3-1#-0&0#2.,,.5+-,:1-$-'0/'7//297,3)*7'#,3(+&.)36#'86+=5%-0/,!50,))%09,75,,19+6$,5$'/"77<.=.%2,*.+80,3,502).-,I*+'*/&-./*%&8(!)7-270032(.1-4#:,5+,3//!0='11$/#&%%&'*!4-/'.8)/0-3'.+3-.6)2-($(./7#*)'5)/1+- %"646'./8-*.#,+-,.1;#7-$/6=,245."5'221/,"/,%=./#&?,4($217&(4'345'/'+ =*-7-).&8.1*@3+1"-/%(-*..+16--&+7*%'2696>7/%/)0-=,'61./!-*+<909,-/2'/!('43&719-//85$€Ù€Ö€Ö€Ñ€×€×€Ü€×€Ó€Ê€Ó€×€Ó€Ú€Ü€Ñ€Ô€Ú€Ó€Ò€Ú€×€×€Ú(6,.3-63*#502-764.(24408,(1*586"&/0)#489,"(?@3+&;#**/5A;4(&49')5$,%89-579+,2./*06&)6- &&4./'2C*1(-(2*)&41 (1"#0-;7),,(90+/,.>'02+192'7!1*'!--,+#E.26,,0*09=.&)%$')2+-91*21$/10$-:2/$7'922 .(37+219%72/'31-/7+#*.*.4/'",+'711' 81,-7:5'140 +*.9#./ /162.2)1+(' 05-*++1&0:1!)1)3*039/.&%3*'*).12,&+22.54'/;-3/!(54-24*02.'=+5%/:93*+32, +*6,(/<)+=.+-.-'4.-(3/3,*7985-73+-3!#-:,93)/12/58!/)1),3-70#(00*39"(-$)))(%+-6.( (2*400+/06*)323+'+)%*(?%('))/:)9+08210./212'3,#6,/2)28<$24)1$73481,3',4<(0-.18#13+.($/!/8(.!3"%3+7-$77-4/+.)11#4 (.*)(!.'3'0#97<7-+;)50'%.&4B*4€Ö€×€Ù€Ø€Ö€Ù€Ñ€Ù€Ï€Ä€Ó€Ù€Ø€Ó€Ó€Û€Õ€Ø€Õ€×€Ó€Õ€Ö€Ø5/!.(6#,31(*'7'02B':6+0:2+'//&'>0%:3473$12,'>2+'6/,+/815'3'.,30**++!11)E;00&90)9&=/%2#73*2+;91/#)5/"*+:%/,,C1**=%;)./,"8,2$,61&/3../)%-7 -.6/;*1&<&3&)'*+!/, 8&$.%%+)(7'15&6;20+7(*61,'57(&,50B&1*2"%8*&.,/-1.+,7/4.-%3%2$+2/ /(9*05:36)71.-7$(3(!)6'0''&"1(41(#5($)+1241;&+1316)#-/-*%&$-0:462%6/*(/6%#-(8102($445%&53462/1--/9*17%*."*2&/(-7&/&,!1/+**6-4,6#-/(2,/-'%/:#*597(-.:7%-/3B1031*J 1'02--0*/>(-+7*/0-+,%<*=+(7)&*5-.483--86952003)#42./-(,).13/:5(,$2,3,')B5. /-/++-7,.6+4-1153533@#<';<,64"7)%'',€Õ€×€Ö€×€Õ€Ò€Û€Ï€×€Ì€Î€Ù€Õ€Ú€Û€Ü€Ó€Û€Õ€Õ€Ø€Ø€Û€Ñ-)7#&/'43$*35)/13./,&--4!*21.;/(21&.4:1+-#&7!!32$58%G6+3;//(14+'+++'(1+-2$).-+9+"?50*-' !+'B2*#57++8:%(+&24-57%2,.-77/08/.*.5%5,'40'5&-52-2.1-$0%+/.&1.-*/7!&2*2"**&$/-)&&.%)0!+"4(&%3).1/0+,),/-(8/$/+0).(/#.)4*'-,:081'9-,,-?)0&!'7-8?0-(%/3462'++(<(-'*-.>5!'%>5=0/()()(+9'!6./.0/0518.$?,+7'9/*+067/('0131+$/,,08'2,1,%-;4407-*9-)..(//0&-2.(/+)?/*2*%.)<,.*/6+-*&09*.,110.-98-))//+)%/2)7'1.*6*2/3.4660/2&%/4/184&94(-4,-0'4.'&*..',1.145--$&.&*2%?-4$/6*/ ?/ -"-(5,9.$-,1(+93*-203--)3,4-2,7(/<&++269&1)2)B3+€×€Ù€Ú€Ú€Ù€Ó€Õ€Õ€Ø€Ï€Ó€Ó€×€Ö€Û€Ö€Ò€Ñ€Ü€Ø€Ø€Ó€Ô€Ó9,,#)40*7),'2.01&7+14:**+-,'*.10)#&7'3)025-)$)!633/%93%*:313(./*(,653%3011*-3(*'.//5/%3((/-/'&./!'+)2702:,'C-2:3 91:22;+20-./53*3!6..)'93?+ 50.2'23)(+0*,/(0!% <,4):031+"-<6('1'5-6,"42?67.1--) 5*/3*.61%$-+10@7((0151-"3/,)"&0()%"-,4:9/-377(9,/>2)676)+&&0#/*$)/28(2)4..*1,)0-1-1/3*1'/)<3E,%23%1-&-&55!'(O&=870(518((.,+-,04)''€×€Ô€Õ€×€Ù€Ú€×€Ö€Ï€Ã€Ù€Õ€×€Ù€×€×€Ù€Ú€Ö€Ò€×€Õ€Ø€Ó6)/+7/)70)4.+)'2+!+0)&+'52!,*1)4'5+3)/'51/1+/*3/!(/160@"13;./-*0!/1(*#1=:/01E://'./-*->'/"4(4+3-.)/*)9*$?.8#875*"'-,,-#8%9<1&?2#'&/-#&'0.('$)/7;/4*%-*"28-4#02110+94/5454+9(+'71.:5435&5/;8#/*02/#99'(595,92/%"/' /1,2.)-3#%)<31>>345&&;%!0)&4('#7,&5&'1*22+-!$%(2&)//(0-,5),)6!/="(,%%,1-;9?%*:4'3-,$50.+&'/./@2&%/."54%',4.$4,92$#;%.0)*;5(.7',1+*23:$5,!&,,-6#/B+,--,()%/+,,+(6*39"'6',%1/*04''=$+4'&D36:$72*2*+20'.56%/72%+8.815;"30#51*)8,(.,*;702*.,,");&000#,)5(##-2),((',>1%5+2+>0&*2*/&1-$&2)1-"#/+%10(50€Ù€Ù€Ù€Ó€Õ€Ø€Õ€Ô€Ò€Á€Ú€Ù€×€Ù€Ý€Ú€×€Ô€×€Ö€Ú€Ø€Ü€Ó(/-7%$38(.:%,883+-*/54.(6-)04&-<9 -14)26.+/+B(45#7'3&/'(9&'1!8.8.)4025)<%05".,3026%5+103/),0+)),327&+-3-,)320.*(3851=/.'&,5$',.0/:!3,5-+-/++.9+- (/*3*3-=-4310..2/916)%77&+"-+,.,-*7'$&'.;./,&(20)771+;#0 --+.#-7'(%(+2%'9#+)(;@1. 3'6)5')()#C3,85,7$-(#.17=*-<,-*+)(602",,1&-9;/0*.C4(.36@,$',+35#<+*9,%!4*8864(+#)-00.6.7*/.90/419(/131(#0.50/+5./6+-+*-/&)0773&'4 4/6';,+7-*>),/4+1-*:90/77%6'+61)+04+,1%533+"+<4$%,/1 )19,-6*261471''-)34+$-+)+/&/4+,39797-",-<2?381*+5(+)./&A)53(/+&/27/1+(44'9.$/9&€Õ€Ö€Ò€Ñ€×€Õ€Ø€Û€×€Ì€×€Õ€Û€Ý€Ô€Ù€Õ€Ú€Ù€Ò€Ù€Ü€Ø€Û./9,;%/2'3020F)%9)/:7)5'3*#;2(1A.;!*&#*7;1-,%2%*$('/**.-2:06+:#07*38:6&6+-(644(/).N*440" 53 02/3(*4005,/21))2'*6--./,:-/)1.%(*.+)#-+0A#)5.&$(773)01&*-0:*>)'+6&.(4&%)/B9+5-72>9@AD4-'2>5/*('2&/-)!/)<<'760;#0:3/6/2/*:'221)@-212%#7<--/0C950-#,7&6'5.%&94#.)./+7-2.5(<-7/"+-""4-22.%*5631)'7A''!97'4!(34:)%5+(.0*&7/$(/"+,!/-$$)/(12/1(.,&/12-.&10;71.:,4$$8*4-;--1',,<46$21,9 75*,/278))8*3C,%0)+"&*)3+/(*7;%9961&5/5%8+4$-%15+ 160!"&1,-#-.15=;4/,-+.( *3+*9951,)5'1/&35+3/--/5+1&0108(./* 2<).60420' )625(+%14;($.(1-,5/2;/2#1+'!0%%35,9+(23$/*/)-5)+0)1(224/1D+7-8&1,(4',6.C,&'.93,,/€Ô€×€Ù€Ö€Ø€Ö€Ö€Ú€Ô€Æ€Ø€Õ€Ö€Ó€Ö€Ð€×€Ö€Ø€Ô€Û€Ô€Ö€Õ20/72*47,>/.(().'.'2685,,/2)++$./3+9".*5+)<(4854/-'/0-.3'09,1204:22E%3.)0)2%5:**40)5*'-2+)34+#0"(/1/B5398=/81,9?&)+!<%*"8"50*(4*2,50)(!(:1;/',%*($ 1/%'*-/$,2'2*(0/-.0+!"2$/:)/1.1'(?"/(.9)6'*4=.1*2**5/)%/!60<1(%,+5+0*48+#.-/5!4%#1-%**6 )*.)4<054#+,)01)3-*+3&4)-9-/311'1/&)(,1&4*,!=4.7%4')378*1*4!-.>(2/%*'*:0.+050(-,*,6)&/-):79-(7'"//60".;6) .6-?45/5.1)21(?.0''#2&%*0/-69/&//*$/44(298)*! 9+&1&'9-,+.6,7,1+1=8/+. $&6$/8+*92D$-(*')0/30/&%**)6*)+3144<30),&7%($1(,-$1;106=9&7:5(4!,,/*4*(!*93'7/'(/;80/€×€Õ€Ò€Ù€Ù€Õ€×€Ò€Ö€Ç€Ô€Õ€Ù€Ñ€×€Ñ€Õ€Ú€×€Ô€Ú€Î€Ü€Û6/%2252&++5/<&),:3*4;3A/(6<$+3#7/-5-5195+.5&1/40%!2,/.%3*2014,&4"43"+1,%#8$).7/423+#;4'!46.;+)"&2#0855B@@L039<33-*$?/+,);8'8+17!263A$*-*23/4/61,)<*"1%053#3335?.#B-''#/9$./.'-'(-0+&-0 =+616*1/7 '&+!)")43>75.9*3//*'/18&40.(-%*&1:$7*'.-!)D/"+)-/1.&)4360.+0,:=-9'(.+67//3$%47*€Õ€Ó€Ö€Ï€Õ€Ú€Ú€Ó€Ô€É€Û€Ö€Ù€Ò€Ü€Ï€Õ€Ò€Ù€Ù€Ñ€Ö€Ô€Ó64.2/)44 $8%-1)2.9%7%//6548:,'3),.-1*.&'*3-+222/+. +/%,4'/*9%,2.(@'%)2+.7-2'/%4 3)"/8.),//+)&+!-+)/1/8FX:EAE5217)<87/-&.)/4'956%)+2%.2.3/0%*':&-#"/807'-*%7&+5)4D.61&<)(:3.91)./*5192;-.1/13,9+<'2%<068-.6#*(-6;2*58.(//)+1/-2/#34&03?0-/5(/6/9*%?49.00)++/'*)2,,=4';612-2/214'43%?0',1//.((6)! +&5"*86+-&6++1"+*/%)(&#+#.(31)-+6-1-( 70>&*$8+(>")55/6/,(*1/#5&/=5,#*"(8+)0#.')5651:"&&/;1+"$&)+ 9*/1/&*+690,/9*:)#3"*:)$;8)-.63/14%.--%?,!4$.-7.)-(,3:+,9"'.+*1540&20.<)&0$7%..5=+8,&1/1"4,-#3-1,.5/:<*,)/5(3;2)€Õ€Ø€×€Ú€Ñ€Õ€×€Ú€Ù€Ç€Ü€×€×€Ù€×€Õ€Ù€Ð€×€×€Õ€Ï€Ð€Ó4.-6,;64.D/ ,3'>68>5.40*94A()&)62.73,5&+49520"/=3,7(3!2*''3'"7147-5,&%2%7@(-10 ,'4,0,9*-/(%!+.,7+(..?2;M;=DAE@5=+10.3;&/-.-.!(3+1-#+%-)B*.11:%0+*$281,6'(++ +-)+*.+1.31+=3/:'+$-D2-1;(>:1B'2'.;,*+1/'$044.1)(43.--(+./('+&,"54:?(8$+/4&-//*5/(639&/*,---3276+39';/)(,%4,2,:/<2%('.+)'/+6-3% %)$048&93-6(5)1%(41/+4.%/43+*-,(6)3*(,36+'00+/&.<)*''+)"##/%*,3&(3(26.+/..&+.3+25)('*73/(6+3'1'&-162*/8+#%865#2/$62&33&2&!3'%'-+5+%'+2&-0?--',-7*5>A,2,461+/-&/;5&)/,;(#.(,;7630,-879(8//003",0((+/@,0*3)%/2-!9.34+6/620#)€Ô€Ô€×€Ô€×€Õ€Ö€Û€Õ€Æ€Ö€Ô€Õ€Ö€×€×€Ò€Ù€Ô€Ó€Ø€Ô€Ò€Ð(22;7'3&:/6<,(8!&7/=:2300$&(++.,7,<.4*5+-(430?6/3:,5$=65&3--,/&+&+:2$65*!!4D+-.$%038!+I*)/%$'0'2/(00GF=4@ML>JE:.<7:+1&.03%-'A"8&,0423.#7-.)8'%-67)+4%0,/2'.//*+.12/-B1%6*(/).7*#(/$'&*&6!-:/0"0-+*8";.7&2"+(0*51++2+,&=.(1(9/4;.+.(%.18!0(1,'-+,*(/5(3;).50+5','456../&97,;5"%,1?/4(/+/3+6A.334'+#-*;.,7+2)&(5*-%-3>2,(/8-,*8B*-)1-$,204/%:8(8-04&9+3*)87+"6*6/),&+/5.1--4/.#;%6&0910%%*/$2'/35"'42,3!&,07/4'*--&('51/:;440*7:--)/6-4$-2-9&$8(";)((#1(-&)/270/>+6353'8?12/2-*'/0'1+'D/%:6301++*5-49:*/&*14+4"*/+<(C€Ø€Ò€Ú€Ó€Ú€×€Û€Õ€×€Ã€Ù€Ó€Ò€×€Õ€×€Ô€Ö€Ö€Þ€Õ€Õ€Ô€Ô03774:4,3+30.$)E335/-7?".+-*(26!$08/#.()*466).$9:3/#B(A58=751'3./#0)25-/151)2,,!2213(/33&2'/'+13"#%69.1;NBHH=C=?:..#.*00>,324%9$**+5/0(85.*+&,$8&)0);(>,54*;0--&7,&5,10.%/-*+)2+3)*75,!-/*+31+!84)'41#10/1408&3*,7,>-':=(-#%04*07'+%42.7/43+10;.1#)8)156+/E&.-'9-,';3)/>$/557.,)/,!/4-0"471,-0,/=81--*+()/%3,2. 7/''938,"5K/0)*)+3(55/142)-'(''-3.0@*/,-7%4/.')'+270(*/&52020)*2+,0,,/$-+0/<,3)(/*.<065(!-0=23362/$.-4.#47/232+%.#$2,360(*03?24-),(".<*+2+9(,:7,&+04.,6*0.&4/5B'/.*3(7321,.4+750$,#:)8(%/3€Õ€Ø€Û€Ö€×€Ø€×€Ó€Õ€Å€Ñ€Ù€Ô€×€Õ€Ú€Ù€Ú€×€Ö€×€Í€Õ€Õ10/---<02)%6:&7**++%'73.*9/,9)10+*3-"@;=)/'7)3-#2=-7+'5-)7+27-'6+-*'-%+!0)%9'7(8$$%80085.0'8,+*16A>05>D//1+.,'.=)$-4%+%.4-*19'<(5.)5,/91-.9,/4/13<5-7.06017035?5/6./--3'+)-7632 2(-&9/7!<66&5.@=B=?II@C5@*8:&:;'*3 ;.:)#542,703-6'/-6**0830'53/2&(%9(),.(@/)-#D'+0+0/5%15867%,.+'0#*'#'C9(:4/>&-*).1.1,4-7)416-55)5*#8&.0*/%0'35,9(*3,902<%(/,23>+&-/4+-:/:*07#81575/6*,0+2)$,!%>2<2$030+**?-.00/**),6(*7%.&.5/.,*.0--580">"+2%,801#@/"159(-0//-34$,,5,C,0'&/#331+'2-)8307*%-)-&55-?$4>*4)5*+6+'4/%#333*/3#%36./,3/7&.$1'/5+/"+-+&,4*2!5/463"!.-%+/5/,*2402)609+-;89/:.'1-;2!435%0 /(1,3-80-+"32)'9%2'6+€Õ€Ò€×€Ò€Ü€Ú€Õ€×€Ö€Ä€Ø€Ø€Õ€Ô€Û€Õ€Ú€×€Û€Ñ€Ö€Ö€Î€Õ&0/ 47.077)-4A/D:+/)281.(.2(,2&.$1318%+$312:5/(941%4'0+9+?5-64)**+,--6/>0&&.,(-"7/.*8 67-*:+(&F..6$%06,5#.,5//&1,%7+32/,;83!:2.#/'8,$/)75.5883>1/-283€Ô€Ð€×€Ð€Ô€Ô€Ô€Ø€Ö€Ë€Ó€Ö€Õ€×€×€Ô€Û€Ö€Ù€Ù€Ý€Ò€Ú€Ø32)!0@#44@0*'35301/.*))-,-/&.44/<5+12?(9+&+0@2,24.:&2%&132%.(1).60*9.:/A)#*'*+.,5//.8/16.206+&&'+?)=5,E%2H2+/:2492%,6.+>5/;(*09,:0*0#5()-6161%,0.'21%.-55+(-16'-$*8%3447%+;)-0(2,(+ ,F7-.+#1<:-7)8(0$7#/29+?.63*).*@%7&2435!16/$-$*,3-.C84..*+%83;:,.%/8/878.**%:&,&,,%:&;0&./$&00$61$48()*0+4,14*$!#(0+0+5$8&/62**1)(=%.<0!%7-(4-6.6657)"!,23$5*''0=44;#-A"=,'/57027620/)5%3!!*.116,1;2;%%1',/03/2/,,&1--'05'.-$ +.+6()7:126<#:/&;'76%-'.47;@1$*-4:4-4/:5.$B&2?1"3/!=".-,5'+$<.7./@&18"*+!+/32+>/+<<9<31*,=61#'&344'<€Ø€Ö€Ô€Ú€Õ€×€Ó€Ø€Ö€Ì€×€×€Ú€Ø€Õ€Ñ€×€Ò€Ø€Ó€Ñ€Ô€Õ€Ú8&.+477#0()+$1+3'1%/6)-3,/1!#;/%8315'4.-+27)9.110-)2)**B3+5.8(.4*-&+4"'+0-5+)/(/):.,+/$2(0'3:)/14162 3,@,+&)3-6*<-$*)9 /6'9023%,#(+5-"46'(-9/).85+7@:..2(#%0/2;*)E0,7)6&*')/+%3!(..&!$=8.4+*!+/0<79))0+*11,4-(+)4./33'#7.%1%-/-+0((&61;7=76-).+'5$3+-55+**$73-#$+/38/'3-&29!)7*B128)4%2).%#0+,)0*')$490469064?2=3*)0)%4.*5.2+-*1$)61-6&3*04.%52+9%()&**519+2759/C0)4*5"-"7()(0'2$.65/7#-'45,$!05%(-*+/96A//4/3'4,-=3(50%-,11*08,1-/=>%41*"428)20.*&5,+/*+6-/.27..67(*,21-11(7.%2("81*#@38+/)4.'-1.76')0+/.',(90&/4,'/)**#214 2..3*$)4:24%#%?,$,6*-44.7*(-!'6.0./0/+4)---36;/+74*$.6212/)4/&",)$$$3.3*0'06//,%+.&;-6--'),%98/30#(4*/7'54,/(%$*#%(5"-$0-(-'+79'22%)/(3-,;0(1-0"5-'//38&,#1+351(45-7-;.6$3: 3)3)'0#3@3'30(0'/ <>/3))).$6* )5)-15.1=+*'.%3)-73,((2'4/-9!,)0.#>85'01*/,)22/0.6*--99(#(4-5-,!2'/9'%7(3,/2",-))!.7,/66-6-*''-1&+(0)/-*:%+<0735.-$.*(1&0;3;8?//'&1!4&€Ù€Ù€Û€Ó€×€Ó€Õ€Ø€Ñ€Æ€Ø€Ø€Ö€Ö€Ù€Ú€Ø€Õ€Ö€Ø€Ò€Õ€Ù€ß,0(*716(1//#,",3-2?:&(-1,,3%'(6-/D0'@)5$3*,#'+)/,+(!*'31)#7--*0/'/+3/1;4,8/5/4'*..(122.#05'6-&+%/+<43!:49753%//9/-160827".+,/(/<2272)+<)35+,0(17)(02&/%4716%.)14-6.3.#51/+/,-9+09'250%,38&;4*-0#63*1.4$ 8,>+(3(/. ;*1758/+4,&-23,/'-#331$(;1/:/$-&"51=0&".'36.3*:0/./,6,(*7532/7454.#)/2-%).-*/ 4;'3,,2-#1*8&. !+.-22(-00,)0;6,504)21$./),1--60,1*../2/>((5*5/1'&034064,.#++/0$379+4,038-9/7.264)002+2'.1.14E-+,2"5**75?+,!+0)..+3003)10';16,+6+ 18+#1@4.6:6"(31()%+542,+&9,(/3$/73/-+:-"5/:/,.2./%,010//:6#-*74*€Ù€Ù€Ö€Ó€Ó€Ö€Ö€Õ€Ñ€Å€Ô€Ò€Ö€Ñ€Õ€Ù€Õ€Ú€Õ€Õ€×€Ù€Õ€Ö=6+-)4:A9/0/%&12)07;9#,7;:,3+'*%-+68+75-0801+&+45&.)%8%0<8,)/%0/3.9<3/0.++8/(@/.40'0-21;'5')68<2/0./1/ (=91*5;5)3/3506'0,'6%:('8-27,+9*;&+08(38/1&/,+45-4+'%#25>'5,/0=#$.('/2?4":4!059#(6%8.3.+:)**/$,11)+%5*$4.$/,),4&,$(+01-%,24.'3+154,-+(3."/0*::242&!)(+2379)0-'/+-+,0%.7(,://.1*.34&..:,7/0(/%=%G8#)'38+)0*(=&./#+)5-1*+(+31+7&"70-*'A,54874+/8')<$#6$',/<53'-,36$02)+;0=/23'8%$:29461)34)6>%/'*&-327,.!/'6,!3'7$&/:3+0/1>2)&'))1;5B12,0-6.-/0+1$(%-((6$14)+635/7.#+&#I-,,,.<:)@9")0%-(+$9"#4*&+C&12+<2.'+=<.4(€Ñ€Ô€×€Ö€Ó€Ù€×€Ü€×€È€Ù€×€Ó€Ô€×€Ô€Ú€Ú€Ó€Ó€Ö€Ñ€Ù€Ð5/(($// .,-%1-10'-77.3).72645"%*)(0-101<1/14*.(>,)#.*.5/'*:+5-+')502<;1+.<7*3)6201!().2:.++)/-5.6107-'15 44-4E%5'-&$,(1883,-((#+@/92%A'&"4./>!#/+).<)+-4*'+? 51'24311%-6(*0#0(;-+31 )*)!673:'523**-/35/*0($'$%.450))%/9/310">-(.212>(/)4943,%"42.3*)('1+3),,&19+!&!."/,-7 ) <+2#+"%>/%&&+.6(:7/(+C3+%1/2:+61"5(+=8.-,-,,0/24($3-#24 -./2).008/,4,?3/5-2)&0,'( )8767/)#-&,-5$:28.;'"0+$)(**&)3')0:(-'%&*('/1(483)$5<+2!/+/.A1'5&41/4*+)C68,<$-.-*0,'+113% +31,%./+3.,2/6B2/1)-: 161.('A,2++11709#:/,>"/D9236,./.3.07'€Ü€Ñ€Ø€Ö€Ô€Ø€Ø€×€×€Ã€Õ€×€Ö€Ù€Õ€Þ€Ø€×€Ú€×€Õ€Ñ€×€Ò,0468053-135(%/;-+)0%-/1#*714312:,)52<.4'600-+!=:)68+1,,7).$/5;-'812$073*/1+)/146;A.43+-/;&-46+02.08'&%+3,.-.18#+/2&56%*8.-4(..)515.1++.10-,:-,$733/=&213(-.34;(5*G6+5'!?/05/,C#)(*&*9$)20723(0+2/').):/ +67"..-1/")1/!/' -/%-3,,/-+:/*8=-.9=7+*#:&/5(8+*&3/2F+('69-7".,?0-20+3.%(60293.*(1(.31,-%6>"<8+))'17//;/3"3*5'/54.54/,H62(&)+45+-245- 1+5''3!",*?2./+*'0,<20;$*&47+."$0",504%+7+/6066/17"--A#.,2--2)6+&6)/-)+A.4>$.6(-4;22''-5)(0#.%%&.,--*?@/?34-&2539<3",'5/1<#:/7.',(1,8915-B42)59$3/+7&$"--(233(/=%1-/€Ö€Ô€Ò€Ü€×€Ú€Ù€×€Ô€Å€Õ€Ù€Ô€Ô€Ù€Ý€Ö€Ó€Ö€Ô€Ð€Ö€Ñ€Ö*<>*'213258/71#5498/86--,2+62),;020--.1)'))5!9+).*""+<463#.0$B&64/),*;48',(2&+191-//9-.("2(".0-/#"36+:41@/+478:0/(.+/26,67& (4,2(/44',$9*4:4-:4#17.:.67+.6+33#&+;51'-+/33+07%/B77.22$9+(514,0€Û€Ø€Ö€Ô€Ø€Ô€Û€Ø€Ó€Å€Þ€Ô€Ù€Ú€Ô€Ö€Ø€Õ€Ö€Ø€Ú€Ø€Ö€×20.&"/2&5&%1)).6%"(,/75 31)1;("%++--'1+11')*0)*"(-/+->.0#&.+-"*55-%4'+&41 ?/$!01+)4';'1%-/%)'1&)(5)/&,'('*#. ',")2+/17=+95"2.*/ +0$-.!/6$3,..(%/,-+%5'7-.,)+5(41),/$&1(,'3*$0''-)&%$')03-:'8"4%%22+!)'"/&+,*2"1'/)"-.%<& /* .C+3/,&-1/+'0(!'3+/),&/<.&<.(/,%)(8-+,;0.5%/2"7/2#06'!!6'.=128)'B)!'"-0&2>+$,,!(+!#%:!!(/ *-& 4&'(*'$.)-3.%+0+1&*'3 /-("-&,61%-;6/0(+.,071)!$3$/2&-3.5;-'(/)-),*4/86+&#,'#'0*22((+5--35.$/!$B*(-613/%$$33)-*0,(."*11)0(+/(3."()./(1$9%-7+*.4,3#)7'()'%().58+"8(#$,(41#*.+6./5"-!3€Ï€Ð€Ó€Ö€Õ€Õ€Ø€Ø€Ø€È€×€Õ€Õ€Ý€Õ€Ò€Ò€Ô€Õ€×€Ò€Ù€Õ€Ö€Þ€Ó€Ù€Ú€Ú€Ù€Ù€Ú€Û€Ú€Ý€Ü€Ú€Û€Ü€×€Ù€Ù€Ú€ß€Ù€Ú€Ù€Û€Ù€Ö€Ý€Ú€Ó€×€Ù€Ø€Ó€Ø€×€×€Ø€Û€Ü€Þ€×€Õ€Ö€Û€Ú€Û€Ù€Ù€Ö€Ú€×€Ù€Û€Ü€Ú€Ô€Ù€Ö€Ø€Õ€Þ€Ø€Ù€Ù€Ø€Û€Ø€Ø€×€Ü€Ù€Ó€Ø€Ù€Ù€Ù€Ü€×€Ø€Ó€Ô€Ý€Ú€Ù€Û€Ú€Û€Ú€Ü€×€Ù€Ù€Ù€Õ€Ó€Ó€â€×€Ú€Ó€×€Ö€×€Û€×€Ú€Ý€Ø€Û€Û€Ú€Ö€×€Õ€Ü€Ô€Ù€Ú€×€Ô€Ú€Ó€Û€Û€Ý€×€Ù€Ö€Ú€Ù€Ú€×€Ú€Õ€×€×€Ý€×€×€Ø€×€Ù€Ú€Ø€×€Ú€Ý€Ö€×€Ý€Ü€Ù€Ù€×€Ö€Ù€Ü€ß€Û€Ü€Ý€Û€Ö€Ö€Þ€Û€Ý€×€Ø€×€à€Þ€Ù€Ú€Ö€×€×€Ó€Ü€Ù€Û€Ø€Ý€à€Ý€à€Ú€Ü€Ý€Ø€Ú€×€Ú€Ý€Ü€Ó€Ü€Ð€Ù€Ø€Ü€Ù€à€Ù€Õ€Ö€Û€Û€ß€Ö€Û€Û€Ø€Ø€Þ€×€×€Ö€Õ€Û€×€Ø€Ú€Ú€Þ€Ø€Ö€Ö€Ö€Ú€Ù€Ý€Ø€×€Ú€à€Ü€Ø€Ð€Û€Ö€Ù€Ø€Û€×€Ý€×€Ü€Ø€Ú€ß€Ö€Ü€Û€Ô€Ú€Ö€Ú€Ø€Ù€Ø€Õ€Ý€Ó€Û€×€Ü€Ø€Ù€Ô€Õ€Û€Õ€Ø€Ù€Ö€Ü€Õ€Õ€Ú€Ú€Ø€×€Ù€ß€×€Ú€Ü€Ý€Ú€Ù€Ø€Ù€à€Ù€ß€Û€Ú€Ù€Ø€Ù€Õ€Û€Ù€ß€Ü€Ü€Ý€à€Ü€Ù€Ý€Ý€Þ€Ù€Ù€×€Û€Ð€×€Ö€×€Þ€Ü€Û€Ø€Ö€à€Ü€Ù€Û€×€Ö€×€Ú€ß€Ú€Ù€Ø€Û€Ú€à€Ø€Ð€Ý€Û€Û€Û€Õ€Û€Þ€Ù€Ý€Ø€Û€Ó€×€×€ß€Ò€Ö€Ú€Ø€Ö€Ù€ß€Þ€Ö€Ø€Õ€Õ€Õ€Û€×€Þ€Õ€Ù€Ô€Õ€Ö€Ý€×€Ù€Ö€Û€Ù€Ù€Ù€×€Ù€Ñ€Û€ß€Ù€Þ€Ö€×€Ð€Û€ß€Ù€Ý€Ö€Û€Ô€Ú€á€Û€Ù€Õ€Ù€Ö€Û€Û€Û€Ó€Û€Ý€Ú€Ù€Ø€Ú€Ù€Ü€Ù€Ï€Ù€à€Þ€Ú€Ø€Ú€Ù€Ò€Ü€×€Ú€Ü€Ú€Ó€Ö€Ù€Û€Ô€Ý€Ø€Ø€Û€ß€Ú€Ó€Û€â€Û€×€×€Ü€Ö€à€Ö€Õ€Ø€Ú€Ý€Ø€Ô€Ù€Ý€×€Ù€Ö€×€Û€Ù€Ö€Ü€Û€Ý€Ü€Õ€Ú€×€Ô€Ù€Ú€Ò€ã€Ù€Ú€Ü€Ú€Ú€Ö€Û€Ü€Ù€Ù€Ö€Ø€Ö€Û€×€ß€Þ€ß€Ý€Ù€Ü€Ü€Þ€Õ€ß€Ù€Õ€Õ€Ó€Ø€Ò€Ñ€×€Ò€Æ€Ô€Ö€Õ€Ù€Ó€Ù€Ð€Ö€Ù€Ø€Ö€Ö€Õ€Ø€Ó€Û€Ý€Ø€Ü€Û€Ù€Ù€×€Ú€Ü€Õ€Ö€Û€Û€Ý€Õ€Ý€Ý€Ú€×€Ú€×€Õ€Þ€Ø€Ö€Ø€Ù€Þ€Ú€Ø€Ý€Ý€Þ€Ú€Ø€Ù€Ú€Ü€Ô€Õ€Ù€Ù€ß€Õ€Ü€Ú€à€Û€Ú€Ö€Ü€Ù€Ø€×€×€Ù€Ý€Ý€Û€Û€Ú€×€Ø€Ü€Ó€Ý€Ü€Ö€Ú€Ú€Ø€Õ€Û€Ù€Ù€Ø€Ú€×€Õ€Ù€Ù€Û€Ú€×€Ü€Ô€×€Û€Ö€Ó€Ù€Þ€Ü€Ó€Ù€Õ€Ú€Ü€Û€Õ€Ø€Û€Û€Ô€Ú€Ü€ß€Ú€Ó€á€Ý€Ú€Õ€Ô€Ø€Ú€Ø€Ö€Ó€Ö€ß€Ö€Û€Ø€Ö€Ù€Ü€Ú€Û€Ù€Ù€Ú€Û€Ò€Û€Û€×€×€Ù€Ø€Ý€Ü€Õ€Ü€Ú€Ø€Û€Ù€Û€×€Ô€Ü€×€Ó€×€Ø€Ù€Ú€Þ€Ý€Ù€Ö€ß€Ó€Ù€Ü€Ø€Õ€Þ€Ù€Õ€Û€Ø€Ø€Ù€Ø€×€Þ€Ú€Û€Ø€Ô€Ö€Û€Ö€Ù€×€Ù€Õ€Û€Ô€Ü€Ü€Û€×€Õ€Ú€Û€ß€×€Ö€Ø€Ô€Þ€à€×€Ü€Õ€Ö€Ò€Ú€Þ€Ý€Ý€ß€Þ€Ò€Û€Ú€Û€á€Ü€Û€Û€Ñ€Û€Þ€×€Û€Ú€Ø€Þ€Û€Ø€×€Ú€Ù€Ú€Ø€Û€Õ€×€Ý€×€Ø€Ý€Ý€×€Ü€Ú€Ú€Ö€Ú€Ò€Û€Ú€×€Ü€Õ€Ñ€Þ€Ù€Û€Û€Ù€Û€Ù€Ö€Þ€×€Ú€×€Þ€ß€Ú€Õ€Ú€×€Ú€Ø€Þ€×€Ø€Ù€×€â€Ö€Ù€Û€Õ€Ò€à€×€Ù€Ò€Ú€Ù€Û€Ú€Ý€Û€Ü€Ù€Û€Ù€Ù€Ú€Õ€Ü€Ú€Ú€×€Ù€Û€Ö€×€×€Ù€Ú€Ü€Ú€Õ€Ý€Û€Ø€Ý€Ñ€×€×€Ý€Ú€Ó€Ü€Õ€Û€Ø€Ø€â€×€Ø€Ú€â€Ù€Ó€Ö€Ù€Ú€Ó€Þ€Ü€Þ€×€Ü€Õ€Ù€Ù€Ø€Ô€Ù€Õ€Ø€Û€Ý€Ø€Ú€×€Ö€Þ€×€Ú€Û€Ù€Ú€Þ€Û€ß€Ö€Ù€×€Ô€Ú€Ô€Ù€Ü€Ù€Ý€Û€Ö€Ø€Ø€×€Ø€Ó€Ø€×€×€Ý€Û€Ù€Ù€Ö€Ö€Ù€×€Ø€Ø€×€Þ€Ù€Ü€Ú€Û€â€×€Ú€Ú€Ù€Ü€Õ€Þ€Ù€Ö€Û€Ö€Û€Ú€Û€Ö€Ö€Ú€Ö€Ø€Ø€Û€Þ€×€Ý€Ö€Ø€×€Ø€Õ€Õ€Ù€Ø€Ú€Ú€Ú€Ø€Ô€Û€Ú€×€Û€Ù€Ù€Ù€Ù€ß€Û€Ù€Ø€Ö€Û€Ù€Ó€Ó€×€Ú€ß€Ú€Õ€Ü€Ö€Ô€Û€Ý€Ú€Û€Ù€Û€Û€Ø€Õ€Ø€Ú€Ø€Û€Ø€Ø€×€Ù€Õ€Ý€Ø€Ö€Û€Ü€Ø€à€Õ€Ü€Ó€Ø€Õ€Ù€Ý€Ý€Ý€Û€Ö€Ú€Ù€Ö€Ò€×€Ó€Ø€Õ€Ã€Õ€Ô€Ú€Ö€Õ€Û€Ô€Õ€×€Ù€Ô€Ó€Ö€×€×€Ù€Ù€×€Ü€Ü€Ö€Ø€Ñ€Ø€Ø€Ó€Ô€Ò€á€×€×€ß€Û€Õ€Ù€Õ€Ù€Þ€Ý€Ø€Ý€×€×€Ù€×€×€Ø€Þ€Û€ß€×€×€Ú€Ö€Ø€Û€×€ß€Õ€×€×€Ú€Ý€Ý€ß€Ú€Ú€Ö€Õ€Ø€Õ€Ô€Ò€×€â€Ù€Û€Õ€Ô€Õ€Ý€×€Ú€Ü€Ø€Û€Û€×€Ý€Ö€Ý€Û€Ö€Þ€Ú€Ö€Ø€Ø€Ý€Û€×€Ú€Ö€ß€×€Õ€Ù€Ú€Ú€Ø€Û€Ú€Û€Ö€Ü€Ø€Ö€Õ€Û€Ö€Ú€Õ€Ö€Ø€Ø€Ú€Ô€Ø€Ú€Ú€Ö€Û€Ü€Ö€Ý€Ú€ß€Ø€Ý€Ù€Ü€Ø€Û€Ú€Ú€Ý€Ü€Þ€Õ€à€Û€Ù€Ú€Û€Ù€Ô€Ø€á€Ú€Ô€Ø€Ü€Ù€Õ€Ú€Ó€×€Ù€Ù€Û€Ø€Û€Û€Ú€Ú€Û€Ý€Ø€à€Ú€Ö€Ö€Ø€Ú€Ý€Ü€×€Ö€Ú€Þ€Ö€Ô€×€Ù€Þ€Û€à€Õ€Ø€Ù€Û€Ý€Ý€Ö€Ü€×€Ö€Ó€Û€Ø€Ú€Û€Ú€Ü€Û€â€Ý€Ú€×€Ö€Ü€Ö€Õ€Ú€×€Ð€ß€ß€Ú€Ó€Û€×€Ü€Ú€Ù€Ù€Ý€Ò€Ù€Ö€Þ€Õ€Ý€Û€Û€Ü€Õ€Õ€Ú€Ù€×€Ù€Ü€Ô€Ô€×€Ö€Ø€Û€Ô€Ö€Ó€Ö€×€Ù€Þ€Ù€Ö€Õ€Ù€Ù€Ø€Ü€Ø€Ý€Ù€Ø€×€Ø€Ú€Ú€×€Ø€Ø€Ù€Ö€Ö€×€Ø€ß€Ú€×€Ý€Ú€Ù€Þ€Ý€Ø€Õ€Û€Õ€Û€Ü€Ü€Þ€Ø€Û€Ö€×€ß€Ø€á€Û€Ú€Ø€Ô€Ù€×€Õ€Ú€Ó€Ú€Ú€Û€Ü€Û€×€Û€Ý€Ý€Ü€Ø€×€Ö€Ü€Û€Õ€Û€Ú€Ö€Ø€×€Ö€Ô€Ô€ß€Ù€Ø€Ù€Ø€Ö€Ý€Ù€Ý€Û€×€Ü€Ù€Û€Ø€Ú€Û€Ô€Û€Ý€Ù€Ú€Û€Ý€Ú€Õ€ß€Ú€Û€Ü€Û€Ý€Ó€Ü€Ö€Ô€Ô€×€Ù€Þ€×€×€Ö€Ô€×€Ô€Ú€Ù€Ö€Õ€Ý€ß€Ü€Ò€Ö€Ó€×€Ö€Ú€Ò€Ú€Û€Ý€à€â€Ù€Ø€Ú€Ú€Ý€Ú€Ò€Ù€Ø€Ø€Ý€Ú€Û€×€Ö€Ù€×€×€Û€Ø€Ù€Û€Ú€×€Ø€Ú€Õ€Û€Ø€Ù€Ù€Ú€Ú€×€Ô€Ø€Ú€Ý€Ù€×€Ø€Ø€Ý€Ø€Û€à€Ü€Ý€Û€Ø€Ý€Û€Ú€Ñ€×€Ù€Õ€Ü€Ø€Ù€Ü€Ý€Ô€Ö€Þ€×€Ô€Ó€Ù€×€Û€×€Ú€Û€Û€Ù€Õ€Ü€Ö€Ö€Ü€×€à€Ö€×€ß€Ù€Ö€á€Û€Ù€×€Ø€Ù€Ö€ß€Ñ€Ú€Ö€Ù€Ú€Ü€Ö€Õ€Û€Ù€Ú€Ù€Ø€×€Ü€Ø€Ó€Ø€Ö€Ù€×€Õ€Ú€Ú€Õ€Õ€Ð€ß€Ò€Ê€Ô€Ö€Ò€×€Ó€Ø€Ñ€Ó€Ô€Ð€Ó€Ø€Ø€Ô€×€Ò€×€Ô€Ú€×€Ü€Ý€Ý€Õ€Ý€Ý€Ü€Û€Ú€Ö€Ý€×€Õ€Ú€Ø€Û€Ú€Ø€Ý€Þ€Ý€×€Ø€Ö€à€Ü€Ú€×€Û€Ö€Ö€Ø€Ö€Þ€Ö€Ò€Ø€×€Ý€Ü€Õ€Ü€Ú€×€Ý€Ö€ß€Ú€×€×€Ù€Ù€Û€Û€Û€Ö€Ù€ä€Û€Û€Ú€Þ€Ø€Ú€Ú€Ù€Õ€×€Ý€Ú€Ð€Ù€Þ€Ù€Ú€Ü€Ù€Û€Ô€Û€Û€Þ€Ý€Õ€×€ß€Þ€×€Ý€Ý€Ø€Ú€Ö€Ù€Ü€×€×€Þ€Ø€Ü€Õ€Ü€à€Û€Û€×€Ø€Ý€Ú€Û€Ó€Û€Ö€Ù€à€Ó€Ö€Õ€Õ€Û€ß€Ü€ß€Ù€Ü€Ú€Ø€Û€Ý€×€Û€Ô€Ú€Ö€Õ€Ø€Ù€×€Ü€Ø€Û€×€Þ€Ø€Ø€×€Ù€×€Ú€×€Ù€Ò€Û€Ø€Û€Û€Ý€Ù€Õ€Ý€Ù€Ø€Þ€Ü€ß€Û€Û€ß€Ú€Ú€×€Û€Ü€Þ€Ù€Ù€Ú€Ú€×€Ö€Ø€Ù€Ù€Ý€Ý€Ú€Ø€Ú€Ö€Ú€à€Õ€Ü€×€Õ€Ý€Ò€×€Ý€Ù€Ù€Ô€Õ€×€Ù€×€Ö€×€Ú€Ù€Õ€Ý€Û€Ø€Ù€Ö€Ö€ß€à€Ú€Õ€Þ€à€Ó€×€Û€Ù€Ø€Ù€Ý€×€Û€Ü€×€Õ€Õ€Ò€Ô€Ø€Ú€Õ€Ø€Ö€Ù€Ù€Ú€×€Õ€Ý€Ú€Ø€Ù€Ù€Ý€à€Ú€Ø€Ô€×€Ö€Ù€Ó€Ú€Û€Ó€Ø€Ú€Ù€Ü€Ò€Ü€Õ€Ù€Ý€Ø€Û€Ô€Ú€Ù€Û€Ø€Ö€Û€Ú€Ù€Ú€Õ€Û€Þ€à€Ø€ß€Ú€Ö€×€×€Û€Ü€ß€Ð€Ú€Ú€Ö€Ô€Ó€Ù€Þ€Ù€Ù€à€Ö€Ù€Ó€Ù€Ù€Ò€Ù€Ù€ß€Þ€Ø€Û€Ö€Û€×€à€Ø€Ý€Ø€Þ€×€Õ€à€Û€Ü€Þ€Ü€Ù€Û€×€Ü€Ô€Ô€ß€Ö€Ô€Ú€×€Ú€Ý€Û€Û€×€Ý€×€Û€Ù€Þ€Õ€Û€Ø€Ô€Ö€Ý€Ñ€Ú€Ö€à€×€Ý€Ý€Û€Ô€Þ€Ù€×€ß€Ý€Ø€Õ€Û€×€Û€Ù€Ú€Ö€Ò€Ö€Ö€Õ€Õ€Ó€Û€Õ€Ó€Ø€Ù€Û€Ý€×€Ö€Ý€Û€Ú€Ø€Õ€Ú€Ö€Ú€Ó€×€×€Ù€Ü€×€Ó€Ø€Û€Ù€Ý€Ø€×€Ù€Ú€Ù€Ö€Ô€Ù€Ò€Ü€×€Þ€×€Ô€á€Û€×€Ö€Ù€Ú€Ù€Þ€Ø€Ý€Ö€Ö€×€Ú€Ø€Û€Ú€Û€Ü€Ý€á€Ú€Ø€Ö€Ý€Þ€×€Ú€Ö€Ù€Ú€Û€×€Û€Ö€Ý€Ø€Ú€×€Û€×€Ý€Ø€×€Ý€×€Ö€Ù€Ü€Ö€Ü€Û€Ø€Ü€Ô€Ó€Ù€Ý€Þ€Ô€Ù€Ý€Ý€Ú€Þ€Ö€Ú€×€Ú€à€×€Þ€Õ€Ó€Ð€Ô€Ô€Ø€Ö€Ö€Õ€È€Ò€Õ€Ù€Ñ€Ø€×€Õ€Ô€Ø€Ñ€Ú€Ó€Õ€×€Ü€×€Û€Ø€Ø€Ô€Û€Ö€Ô€Ø€Þ€Ù€Ý€Ù€Û€Û€Ú€Ø€à€Ù€Ù€Þ€×€Ò€×€Þ€Ó€Ø€Ú€Ù€Û€Ñ€Õ€Ò€Ù€Õ€Ú€Ù€Ö€Ù€à€Ú€Ö€Ù€Ü€Ø€Ö€ß€Õ€Ù€Ö€Ø€ß€Ù€Û€Õ€Ù€Ø€Ü€Ô€ß€Ø€Þ€Ó€Û€Ö€×€Ý€Ý€Ú€Ù€Ø€Ù€Ü€Ú€Û€Ü€×€Û€Û€Ó€×€Ò€Ú€ß€Ü€Ù€×€Û€Ú€Ù€Ü€Ù€Ú€×€Þ€Ù€Ù€Û€Ú€Ú€Ü€Ù€Þ€Ó€Þ€ß€Ù€Ù€Ó€Ø€×€Ó€Û€Þ€Ý€Ö€Û€Û€ß€Ù€Ù€Ù€Ù€Ü€×€Þ€Ö€×€Û€Û€×€à€Ý€Þ€Û€Ô€Ù€Ù€Ü€Ø€Ø€Ö€Ö€×€Ý€Ù€Õ€Õ€Ù€×€Ö€Ø€ß€Ú€Ü€Ú€Ý€Ú€Û€Ù€ã€Ý€×€Ö€Ó€×€Ý€Û€Ù€Ø€Ú€Ý€Þ€Ò€Õ€Ù€Ý€Û€Û€Þ€Ü€Ú€Û€Ù€Û€Û€×€Ú€Ù€Ú€Ö€Õ€Û€Ý€Ó€Ú€Ô€Õ€×€Ø€Ü€Ù€Ö€Õ€Þ€Ö€Þ€Ö€Þ€Ú€Ù€à€Ú€×€Ü€Ý€Ø€Ú€Þ€Ù€Ø€Ù€Ú€ß€Ù€Ù€ß€Û€×€Ý€Ý€Ù€Ú€Ø€Û€Ý€Ú€Ö€Ð€Û€×€Ü€×€Ü€Ü€×€Õ€Ó€Ý€Ü€Ù€Ø€Ø€Ó€Ü€Ú€Ý€Ù€Ú€×€Û€Ø€Ù€Ù€Ô€Ø€ß€Ü€Õ€Ô€Ô€Õ€Ø€Þ€Ý€×€Ø€Ö€Ö€Ó€×€Ù€×€ß€Ø€Ø€Õ€Ø€×€Ð€Þ€Þ€Ö€Ü€×€Ü€Ø€Ö€×€Ú€Ö€×€ß€Ú€Û€Ú€Ö€Ú€Õ€×€Ü€Ô€Ö€×€Õ€Ù€à€Ù€×€Ù€Ü€×€Ú€Ð€Û€Ù€Ü€Ø€×€Ø€Ú€Ü€Õ€Û€Ó€Ø€Ü€Ù€Û€Ú€Ü€×€Ù€×€Ú€Õ€Þ€Õ€Õ€Û€×€×€Ö€Ü€Û€Ó€Ú€Þ€Û€Ú€Ø€Ú€Ø€Ú€Ý€×€Ý€Ù€Û€Ý€Ú€Ù€Ö€Ù€×€×€Ø€Ô€Ý€à€Ü€×€Ù€Ô€á€Ú€Ú€Û€Û€Ù€Ø€Ý€â€Ó€Û€Ü€Ô€Û€Ó€Ø€Ô€Ú€Ù€Ø€Û€Õ€Ø€à€Ø€×€Ö€Ö€×€Ú€ß€Ö€Ø€Ý€×€Ó€Õ€Ü€Ø€Ù€à€Ù€Ò€Ú€Õ€Ö€Ó€Ú€â€Ù€Ù€Ù€Û€Ú€Ú€Ü€Ò€Ù€Ú€Ú€×€Ý€Ú€Ô€×€Û€Õ€Ù€Ú€×€á€Ü€Ù€Ú€Ø€Ù€Ø€Ù€Ù€Û€Ü€Ú€Õ€Ù€Ô€Ú€×€Û€Ý€Ý€Õ€Õ€Ü€Ù€×€Ù€Ù€Ý€Ý€Ô€ß€×€Þ€×€Ó€Û€Ù€Ø€×€Ý€Ú€Õ€Ü€Ù€Ý€Û€Û€Ý€Þ€Þ€Ø€Ù€Û€Ú€Û€Ö€ß€Õ€Ú€Ú€Õ€Ù€Ô€Ñ€Õ€Ó€Ã€Ö€×€×€Ø€Ö€Ñ€×€Ò€Õ€Ó€Ò€Ù€Ö€Ô€Ø€Ó€Ù€Ù€Þ€Ö€Ù€Õ€ß€Ý€Ü€Û€à€Ø€Ü€ß€ß€Þ€Ý€Ú€Ú€Ü€Õ€Ú€Ø€Ü€Õ€Ø€Ø€Ö€Ø€Ü€Û€Ö€Ú€Õ€Þ€Ö€×€Ö€Ý€Ö€Ù€Õ€Ô€Ô€à€Ø€Û€Ú€Ô€á€Ó€Ú€Ö€Ø€Ö€Ñ€Ü€Û€Õ€Ù€à€ß€Ø€Ú€Ø€Û€Ù€Ð€á€Ô€Ø€Û€Ö€Ú€Û€Ô€Ö€Ö€Ö€Ð€Ú€Ö€Ù€Ö€Ù€á€Õ€Ú€×€Ô€Ø€Ü€à€Ö€Ü€Ú€Ô€Ø€Ù€Þ€Ú€Ö€Ü€×€Û€Ø€Ý€Û€Û€Ú€Ú€Û€Ú€á€Ö€â€Ñ€Ú€Ú€Ô€Ü€Ø€Ó€Ù€Û€Ü€Ù€Ü€Ù€Ö€ß€Õ€Ù€Õ€Û€Ú€â€Ù€Ý€Û€Þ€Ú€Ù€Ú€×€Û€à€Ú€Û€Ö€Û€Ø€×€Ö€Ù€Ü€Ü€Ö€Ö€Ú€Ú€Ø€ß€Ü€Õ€Þ€Ø€Ù€Þ€Ù€ß€×€Ø€Û€Ô€Ü€Ù€ß€Ø€Õ€Ù€Ý€Û€Ö€Ú€Ö€×€×€à€Û€Ø€×€Ô€Ú€×€×€Ø€Ù€Ö€Þ€Ú€×€Ø€Õ€×€Þ€Ù€×€Ù€Ù€Ò€Ú€Ü€Ö€Ü€Õ€×€Û€Ú€Û€Ú€×€Ú€Ô€Û€Ô€Þ€Ø€Ù€×€Ø€Û€Ô€Û€Ó€Û€Ó€Õ€Õ€Û€Ø€Ü€Ý€Ù€Ø€Û€ß€Þ€Ú€ß€Ö€Ú€×€Ù€Ø€Þ€Ù€Ú€Ù€×€Ü€Ù€Ô€Û€×€Ô€Ú€Ù€Ý€Ö€Ú€Ø€Ó€Ú€Û€Ù€ß€Ü€Ú€×€Ú€Ù€Ú€×€Ô€Û€×€×€Ô€Ø€Û€Ö€Ú€Û€Ö€Ü€Þ€Þ€Ô€Ö€Ü€á€Ü€Ö€Ý€×€ß€×€Ö€Ù€Û€Ù€×€×€Ú€Û€×€Û€Ù€×€Ó€ß€Ö€Ø€Õ€Ø€Õ€Þ€Ú€Û€à€Ø€Ü€Ü€Û€×€Ø€Ú€Ù€Ø€Û€×€Û€×€Ý€ß€Ò€ß€Û€Ù€â€Ü€×€Ü€Ô€Ý€Û€Û€Ø€Ö€Ù€Û€Û€Û€Ö€Ø€Ø€Ó€Ý€Þ€Ô€Ù€Ú€Û€Ö€Ý€Ö€Ù€ß€Ú€Ý€Õ€Þ€Ù€Ö€Ý€Ú€Ö€Þ€Ò€Ü€Û€Ø€×€Ö€Ö€Õ€ß€Ú€Ü€Û€Ý€Ú€Õ€Ù€Ú€Ø€Ø€Ø€Õ€Ö€á€Ó€Ø€Ü€Õ€Ó€Ø€Ö€Õ€ß€Ø€Û€à€Ô€Ø€Ø€Ü€Ö€Õ€Ö€×€Û€Õ€×€Ö€Ý€Û€Õ€Ø€ß€Ø€Ø€Ù€×€Õ€Ö€Ù€×€Ø€Ú€Ù€Ü€Õ€×€Û€×€Ø€Ý€Þ€Ö€Þ€á€Þ€Ü€Ü€Õ€×€Û€Ú€Û€Õ€à€Ó€Û€×€Þ€Ö€Ø€×€Ú€×€Õ€Ù€Ú€Ñ€Ü€Û€Ö€Ø€Ú€Ù€×€Ù€Ø€Ö€Ù€Ý€Ö€Õ€Ú€Û€Ý€Õ€Õ€Ù€Ö€Ý€Ö€Ö€Ñ€Õ€×€×€×€Ô€Ï€Ò€Ô€Ê€Ô€Û€Ô€Ó€Ø€×€Ò€Ô€×€Õ€Ò€×€Ö€Ô€á€Û€Û€Ý€Ú€×€Ó€Ü€Þ€Ý€Ü€Ú€Ü€Ö€Ô€Ù€Ú€Ú€Ú€Ý€Ú€Ý€×€Ý€Ø€Ú€Þ€Ö€×€×€Ù€Û€Ð€Ú€Ý€×€Ø€Û€Õ€ß€Ù€Ô€Û€Ù€Û€Ú€Ø€Ø€à€Ö€Ø€Û€Ù€Ö€Ú€Ý€Õ€Þ€Ü€Ö€Û€Ý€Û€×€Ù€Ø€Û€Ù€Û€Ô€Ù€Ú€×€Ú€×€Ø€Õ€Ú€Û€Ô€Ü€Ö€Ü€×€Ú€ß€Ù€Ù€Ö€Û€×€Ú€×€Õ€Ö€Ú€Û€Ö€Ö€Ö€Ø€Ù€Û€×€Ü€Ø€Ú€Ù€Þ€Ö€Õ€Û€ß€Û€ß€Û€Ú€×€Û€Ý€Ó€Ý€Ö€Ø€Õ€Þ€Ý€Ù€Ù€Ø€Ü€×€Ó€Ú€Ú€×€Ò€Ü€Õ€Ú€Õ€Ù€Ù€Ù€Ö€Ø€Ô€Û€Ø€Ü€Ü€Ú€×€Þ€Ü€Ø€Ø€Ö€Û€Ø€Ú€Ø€Û€×€Ó€Ú€Ø€Û€Ø€Ø€Ù€Û€Ù€Ü€Ú€Ú€Ý€á€Ö€Ý€×€Õ€Ú€Ø€Ø€Û€Ú€Ø€Û€Ô€Ö€Ø€Õ€Û€Ú€Ý€Û€×€×€×€×€Ø€Ø€Ø€×€Ú€Ü€Ü€Ô€Õ€Û€Ó€Û€Ø€Ó€Ý€ß€Ý€Û€×€Ú€×€×€à€Õ€Ô€Ú€Ù€Ö€Ù€Û€Ù€Û€Ù€Ù€Ü€Û€Û€Ö€ß€Ø€Û€Ú€Ø€Ù€×€Ø€Ù€Û€Ö€Õ€×€ß€Ø€Þ€Û€×€Ú€Ø€Ú€Ú€Ù€Ø€á€Ø€Ô€Ù€Ù€Ø€Ù€×€Õ€Ô€Ô€à€Ú€Û€Ü€Þ€Ø€×€Ô€Ú€Ü€Û€Ý€Ó€Ù€Ù€Ô€×€Ý€Ø€Ó€Ó€Ý€Ú€ß€×€Õ€Ù€Ø€Þ€Ö€Õ€×€Ò€Ù€Õ€à€×€Ý€Õ€Ù€Ø€Õ€Õ€Ü€Ô€Û€Ö€Ú€Ú€×€×€Õ€×€Ù€Û€Ô€Û€Õ€Ü€Û€Ø€Ý€Ý€Ý€Ü€Ý€à€Ù€Û€â€×€Û€Ü€Ü€Ù€Ù€Ü€Û€Ø€Õ€Ø€Ø€Ù€Û€Ñ€Õ€Ý€×€Û€Ö€Õ€×€×€Ü€Ø€Ø€ß€Ö€Ú€Ù€Ú€Û€Ö€Ù€×€Û€à€Ø€×€Õ€Û€Ü€Û€Ú€×€Ô€Ø€Ø€Ü€Û€Ô€Û€Þ€Ø€Ü€Û€Ø€Ù€Ü€Ñ€Ú€Û€Û€Ù€×€Þ€Ö€Ü€×€Ø€Ú€ß€×€á€×€Ô€Ú€Ó€×€Ö€Ü€Ô€Û€Ú€Ø€Ô€Ø€Û€Ý€Ò€Ó€Õ€Ô€Ø€á€Ö€Û€Ö€Ü€Ø€×€Ý€Ö€Ú€Ù€×€Ø€Ø€×€Ú€Ó€Ú€Þ€×€Ù€Ü€Ø€à€Þ€Ï€Ù€Þ€Ø€Ü€Ô€×€Ú€Ú€Ù€Ù€Ø€Ö€Ü€â€Ø€Ø€Ö€Ô€Ý€Ü€Ú€×€Ú€Ú€Ù€Ù€×€Ô€Ö€Ü€Õ€Ö€Û€Ö€×€Õ€Ø€Û€×€Ú€Ú€Ý€Þ€Û€Ø€Þ€Ö€Ù€Ö€Õ€×€Ú€Ö€×€Ø€Ù€Ô€Ã€Ó€Ü€×€Ö€Ò€Ù€Ö€Õ€Ù€Ó€Ô€Ü€Ô€Ö€Ü€Ú€Ñ€Ö€×€Ú€Ñ€Û€Ý€Ù€Õ€Þ€Ù€Ü€Õ€Ö€Ý€Ý€Ø€Ø€á€Ô€Û€Û€Ù€Ö€Û€Ö€Ö€Þ€Õ€Ø€Ö€Ü€Û€Ý€Ù€Þ€Ù€Ý€Ü€Ý€Ö€Ú€Ö€Û€Ô€Û€Û€Þ€Ø€Ö€Ü€Ö€×€Õ€Ù€Ý€Ù€Õ€Ö€×€ß€Ý€×€Û€Õ€à€Ø€Û€â€Ù€Þ€Ö€ß€Ù€Ø€×€Û€Ø€Ú€Þ€Ø€×€Ö€Ø€Û€Ö€à€Û€Þ€Ö€Ö€Û€Ü€Ó€à€Þ€Ô€Ö€Ù€×€×€Ó€Ô€×€Ú€Ö€ß€×€Ü€Ý€Ú€Ö€Ú€Ý€Ù€×€×€Ú€Ø€Ô€Ô€Û€×€Ý€Ü€Ñ€Ù€Ø€Ô€Ú€Ø€Û€Þ€Û€×€Ü€×€Û€Û€Ö€Ú€Ø€Ø€Ô€Û€Û€Ô€Ö€Ø€×€Û€Ú€Ù€Ø€Û€Ü€Ò€Ü€Û€ß€Õ€Þ€à€ß€Ö€Ù€à€Ù€Ô€Ñ€Û€Ö€Ü€Õ€Õ€Ô€Ù€Ú€Þ€ß€Ö€Ò€Ú€Ù€Ù€Ù€Ù€Þ€Õ€ß€Ô€Ò€Ý€Ù€Ö€Û€Õ€Ü€Ó€Û€Ö€Ú€Ü€×€Õ€â€Û€Ù€Ó€Þ€Ü€Û€Õ€Ü€Û€Ø€Þ€Ù€Þ€×€Õ€Þ€Õ€Ý€Ø€Þ€Ý€Ü€Ù€Ú€×€Ø€Ö€Ù€Õ€Ý€×€Ú€×€Ù€Þ€Ü€Ü€Ø€Ö€Ö€Ü€Ü€Ü€Ø€Ú€Ø€Ü€Ø€×€Õ€Ù€Ý€Ô€Ù€Ú€Û€Ú€Ú€Ø€Ý€Ö€×€Ö€Ú€Õ€Ú€Ö€Ú€×€×€Õ€á€Õ€Û€×€Ü€Ú€Ø€Ø€×€Ù€×€Ý€Ô€×€Ö€Ý€×€Ö€Û€Ù€Ú€Û€Ü€Ü€Ø€Ø€×€Ú€Ü€Ù€Ú€ß€Ô€Ö€Þ€Ó€Ý€ã€Ù€Ó€Ù€Ú€Ö€Ý€Ù€Ø€Ö€Ù€Ù€Û€Û€Ó€Ø€Ú€Ø€Ú€Û€×€Ù€Ý€Ø€Û€Ý€Ù€â€Ø€Ú€Ù€Ú€Û€×€Þ€Ó€Ù€ß€Û€Ø€Û€Õ€Ü€Þ€Û€Ó€Ü€Ü€Ø€Ü€à€Û€×€Õ€Û€Ù€Û€Ø€Û€Ù€Þ€Ö€ß€×€Ü€ä€Ò€Û€Ü€Þ€Ú€Ú€Ö€Ö€Û€Õ€Ø€Ý€Ò€Ú€Ü€Û€Û€Ö€Ù€ß€Û€Ü€Ù€Ö€Ü€×€ß€Ù€á€Ü€Û€Ö€Ø€Ú€Ò€Ø€Ú€Û€Ù€Õ€Û€Õ€Ú€ß€Û€×€Õ€Ô€Û€×€Û€Û€Ø€á€Õ€Ö€Ú€Ú€Ý€Õ€×€Ø€Ú€×€Ü€Û€Ö€Ù€Ú€Õ€×€Ñ€Ô€Û€Ø€Ü€Ù€Ý€Ú€Ù€Ú€Ø€Õ€Õ€×€Ú€×€Ý€Ñ€Þ€Ø€×€Ù€×€Ü€Õ€Ú€Û€Õ€Õ€Û€Ö€Ô€Ô€Ú€Ý€Ú€×€Û€Þ€Ø€Ú€Ú€Û€Û€Ó€×€Ô€Ú€Õ€Ö€Ø€Ó€Þ€Ú€×€à€Ø€Ö€Û€Õ€Õ€Ø€Ñ€Ó€Ú€Ò€Øccdproc-2.4.3/ccdproc/tests/data/expected_ifc_file_properties.csv0000644000000000000000000000071313615410400022160 0ustar00file,simple,bitpix,naxis,naxis1,extend,bscale,bzero,imagetyp,filter,exposure filter_no_object_light.fit,True,16,1,100,True,1,32768,LIGHT,R,1.0 filter_object_light.fit,True,16,1,100,True,1,32768,LIGHT,R,1.0 filter_object_light.fit.gz,True,16,1,100,True,1,32768,LIGHT,R,1.0 no_filter_no_object_bias.fit,True,16,1,100,True,1,32768,BIAS,,0.0 no_filter_no_object_light.fit,True,16,1,100,True,1,32768,LIGHT,,1.0 test.fits.fz,True,16,1,100,True,1,32768,LIGHT,R,15.0 ccdproc-2.4.3/ccdproc/tests/data/flat-mef.fits0000644000000000000000000004730013615410400016133 0ustar00SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups UNIT = 'electron' FILTER = 'B ' IMAGETYP= 'FLAT ' DATE-OBS= '1928-07-23T21:03:27' END ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups UNIT = 'electron' FILTER = 'B ' IMAGETYP= 'FLAT ' DATE-OBS= '1928-07-23T21:03:27' END ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups UNIT = 'electron' FILTER = 'B ' IMAGETYP= 'FLAT ' DATE-OBS= '1928-07-23T21:03:27' END ?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€?€ccdproc-2.4.3/ccdproc/tests/data/science-mef.fits0000644000000000000000000004730013615410400016616 0ustar00SIMPLE = T / conforms to FITS standard BITPIX = 8 / array data type NAXIS = 0 / number of array dimensions EXTEND = T END XTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups UNIT = 'electron' OBJECT = 'clouds ' EXPTIME = 30.0 DATE-OBS= '1928-07-23T21:03:27' FILTER = 'B ' IMAGETYP= 'LIGHT ' END BÈñ`BÅž8BÊÝŒBÇ_ìBÆBÉÆ:BɸBƺBÈ BÿBÊLÑBÉûàBÉèBÃõBÇTôBÈBÈϘBÈ”BʤoBÄçüBǘ?Bư%BÈcBÉ\BÊ¢åBÇ·BÉYâBÄ]¯BÇ¢@BÊ1BÇ4NBȬÅBÊ\BÊ…Bɺ9BÇÁ}BÈ?ÚBÇZ»BÉ®ðBÌÈ,BÈ'BÆÝûBÈBÃÙBÈ~ßBÆ4¨BǹöBÈ ]BÉ‚ÆBÈn8BÉ®™BÅ¿BÅ21BÇÌTBÆçMBǵôBȵBBÇíÑBÉ!¨BËaBÆ 1BÇÛüBÈ®BÇ•@BÊNBÃ2øBÌ«BŶùBÈl|BÉhÑBÆmÛBÈì“BÉh‘BÉ BÆ%ÂBÌBÈt4Bű×BÉC“BÈ;BÈíÅBÀß{BʤhBÈN&BÈT=BÇ#ÊBɈåBÉøGBÈŠ«BÊȲBÈ(áBÇ38BÅñ¾BÆÔ BÉ¢BÇÖ BÇO{BÉ|BÅÜ®BÆùëXTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups UNIT = 'electron' OBJECT = 'clouds ' EXPTIME = 30.0 DATE-OBS= '1928-07-23T21:03:27' FILTER = 'B ' IMAGETYP= 'LIGHT ' END BÈ•BÉ"BÉ×BÈ’BÈ÷õBʺBÆpBÇ`BÊrûBÅpBÉÀ>BÄ”BÇ5BÉ’BÇ—–BÇ¢ºBÉ\BÄ\îBÈBÈÊ)BÇ€ÎBÆÃ¼BÆ¢]BÈß]BĘBÈÉ”BÇ –BÇfçBÉcbBÉ[uBÈz§BÈMnBÉ¡ÛBËÉ}BÉG~BÆqBÃÔXBËÜIBć€BÊk·BɘIBÇ=‰BÉg¶BÆLŸBÊZ‡BÆóŠBÉfÝBÉ÷çBÇÁ­BÌ»FBÈþBÉ—ÛBÇ MBÇâùBÊ·1BÆcíBÃÀµBÇU?BÆ:BÈ«BÉÕBƃ)BÇ\BÆ*èBÆGÚBÈs´BÉAïBÈ_|BÉç«BÉùíBÇÚÓBÆæBƪBÅ…˜BÈG…BÇÐBÌ?UBÈ>›BÅ._BÊØ’BôLBÅNBȺ%BÇørBÊ‹wBÅÒBÅ›äBÆÐ÷BÇ+ÆBÅ%þBÈk6BÆÐqBÅ ÄBÆ4óBÊ5nBÇ# BÇ­BÉÇ@BÈ“¦BÅåXTENSION= 'IMAGE ' / Image extension BITPIX = -32 / array data type NAXIS = 2 / number of array dimensions NAXIS1 = 10 NAXIS2 = 10 PCOUNT = 0 / number of parameters GCOUNT = 1 / number of groups UNIT = 'electron' OBJECT = 'clouds ' EXPTIME = 30.0 DATE-OBS= '1928-07-23T21:03:27' FILTER = 'B ' IMAGETYP= 'LIGHT ' END BÇ\bBÆÂBÈPbBÆÛjBÊ‚BÆjÂBÆódBÈ$ÍBËÒOBÉ“pBɳBÆè BÈ™Bǯ‘BÆ„áBÅ4BȹBÉ)‚B͇BÈÌtBÉVDBÇrÎBÉ?BɹäBÅå‘BÅ7EBÊNÏBÊ](BÈÈbBÆ<çBÈ—B˺DBÄ“QBÅ/“BÈ@éBÈíBÅvFBÆRƒBÉœBÆýîBÆlRBËBÈi1BÈ CBÉ»©BÈ™ BÊ'0BȹòBËÉBÇKBÈÆ}BÈ BÆ­öBÇ\ÕBÆÿBÈZÒBÉ÷BÈ BɉBȵ!BÇråBÇßBÇ@JBÃ4zBËBÈ MBÇn^BÅxBË“rBÇJ~BÇÊ]BÈÅÃBÉ¥CBÆ¡æBÊJBÈSBʯ¶BÇæ BÇE BÄä°BÇ\…ccdproc-2.4.3/ccdproc/tests/data/sip-wcs.fit0000644000000000000000000005500013615410400015636 0ustar00SIMPLE = T / Created by ImageJ FITS_Writer BITPIX = 16 / number of bits per data pixel NAXIS = 2 / number of data axes NAXIS1 = 100 / length of data axis 1 NAXIS2 = 50 / length of data axis 2 INSTRUME= 'Apogee Alta' / instrument or camera used DATE-OBS= '2011-09-01T02:09:05' / YYYY-MM-DDThh:mm:ss observation start, UT EXPTIME = 120.00000000000000 /Exposure time in seconds EXPOSURE= 120.00000000000000 /Exposure time in seconds SET-TEMP= -18.899999618530273 /CCD temperature setpoint in C CCD-TEMP= -19.015454250000001 /CCD temperature at start of exposure in C XPIXSZ = 9.0000000000000000 /Pixel Width in microns (after binning) YPIXSZ = 9.0000000000000000 /Pixel Height in microns (after binning) XBINNING= 1 /Binning factor in width YBINNING= 1 /Binning factor in height XORGSUBF= 0 /Subframe X position in binned pixels YORGSUBF= 0 /Subframe Y position in binned pixels FILTER = 'B ' / Filter used when taking image IMAGETYP= 'LIGHT ' / Type of image FOCALLEN= 0.00000000000000000 /Focal length of telescope in mm APTDIA = 0.00000000000000000 /Aperture diameter of telescope in mm APTAREA = 0.00000000000000000 /Aperture area of telescope in mm^2 SWCREATE= 'MaxIm DL Version 4.10' /Name of software that created the image SBSTDVER= 'SBFITSEXT Version 1.0' /Version of SBFITSEXT standard in effect SWOWNER = 'Linda Winkler' / Licensed owner of software PURGED = T / Have bad keywords been removed? LATITUDE= '+46:52:00.408' / [degrees] Observatory latitude SITELAT = '+46:52:00.408' / [degrees] Observatory latitude LONGITUD= '-96:27:11.8008' / [degrees east] Observatory longitude SITELONG= '-96:27:11.8008' / [degrees east] Observatory longitude ALTITUDE= 311.7999999995668 / [meters] Observatory altitude LST = '18:22:52.8880' / Local Sidereal Time at start of observation JD-OBS = 2455805.589641204 / Julian Date at start of observation MJD-OBS = 55805.089641204 / Modified Julian date at start of observation OBSERVER= 'Matt Craig' BUNIT = 'adu ' HISTORY +++++ BEGIN patch_headers history on 2013-11-07 13:54:02.872029 +++++ HISTORY patch_headers.py modified this file on 2013-11-07 13:54:02.872029 HISTORY Changed IMAGETYP from Light Frame to LIGHT HISTORY Updated keyword LATITUDE to value +46:52:00.41 HISTORY Updated keyword SITELAT to value +46:52:00.41 HISTORY Updated keyword LONGITUD to value -96:27:11.80 HISTORY Updated keyword SITELONG to value -96:27:11.80 HISTORY Updated keyword ALTITUDE to value 311.8 HISTORY Updated keyword LST to value 18:22:53.19 HISTORY Updated keyword JD-OBS to value 2455805.58964 HISTORY Updated keyword MJD-OBS to value 55805.0896412 HISTORY ********* FILE NOT PATCHED *********~~Stopped patching header of /Users/HISTORY m HISTORY attcraig/ast390/raw/2011/2011-08-31/sa110sf2-001std_b.fit because of ValHISTORY ueError: No RA is present. HISTORY ----- END patch_headers history on 2013-11-07 13:54:02.872029 ----- HISTORY Updated keyword OBSERVER to value Matt Craig HISTORY +++++ BEGIN patch_headers history on 2016-11-21 20:10:41.595989 +++++ HISTORY patch_headers.py modified this file on 2016-11-21 20:10:41.595989 HISTORY Updated keyword LATITUDE to value +46:52:00.408 HISTORY Updated keyword SITELAT to value +46:52:00.408 HISTORY Updated keyword LONGITUD to value -96:27:11.8008 HISTORY Updated keyword SITELONG to value -96:27:11.8008 HISTORY Updated keyword ALTITUDE to value 311.8 HISTORY Updated keyword LST to value 18:22:52.8880 HISTORY Updated keyword JD-OBS to value 2455805.58964 HISTORY Updated keyword MJD-OBS to value 55805.0896412 HISTORY Set image data unit to adu HISTORY ********* FILE NOT PATCHED *********Stopped patching header of sa110sf2-HISTORY 001std_b.fit because of ValueError: No RA is present. HISTORY ----- END patch_headers history on 2016-11-21 20:10:41.595989 ----- HISTORY +++++ BEGIN patch_headers history on 2016-11-22 13:44:33.242638 +++++ HISTORY patch_headers.py modified this file on 2016-11-22 13:44:33.242638 HISTORY Updated keyword LATITUDE to value +46:52:00.408 HISTORY Updated keyword SITELAT to value +46:52:00.408 HISTORY Updated keyword LONGITUD to value -96:27:11.8008 HISTORY Updated keyword SITELONG to value -96:27:11.8008 HISTORY Updated keyword ALTITUDE to value 311.8 HISTORY Updated keyword LST to value 18:22:52.8880 HISTORY Updated keyword JD-OBS to value 2455805.58964 HISTORY Updated keyword MJD-OBS to value 55805.0896412 HISTORY Set image data unit to adu HISTORY ********* FILE NOT PATCHED *********Stopped patching header of sa110sf2-HISTORY 001std_b.fit because of ValueError: No RA is present. HISTORY ----- END patch_headers history on 2016-11-22 13:44:33.242638 ----- HISTORY WCS created by AIJ link to Astronomy.net website HISTORY WCS created on 2017-04-16T21:01:55.162 WCSAXES = 2 / no comment CTYPE1 = 'RA---TAN-SIP' / TAN (gnomic) projection + SIP distortions CTYPE2 = 'DEC--TAN-SIP' / TAN (gnomic) projection + SIP distortions CUNIT1 = 'deg ' / X pixel scale units CUNIT2 = 'deg ' / Y pixel scale units EQUINOX = 2000.0 / Equatorial coordinates definition (yr) LONPOLE = 180.0 / no comment LATPOLE = 0.112838900008 / no comment CRVAL1 = 280.544106813 / RA of reference point CRVAL2 = 0.112838900008 / DEC of reference point CRPIX1 = 37.5 / X reference pixel CRPIX2 = 25.0 / Y reference pixel CD1_1 = 0.000152978060309 / Transformation matrix CD1_2 = 2.52560835888E-05 / no comment CD2_1 = -2.48148467064E-05 / no comment CD2_2 = 0.000152891927788 / no comment IMAGEW = 3073 / Image width, in pixels. IMAGEH = 2048 / Image height, in pixels. A_ORDER = 2 / Polynomial order, axis 1 A_0_2 = -1.15762991047E-06 / no comment A_1_1 = 4.31199937889E-07 / no comment A_2_0 = 1.63167357146E-07 / no comment B_ORDER = 2 / Polynomial order, axis 2 B_0_2 = 4.9476013938E-06 / no comment B_1_1 = -6.08988358344E-07 / no comment B_2_0 = -9.96142066538E-07 / no comment AP_ORDER= 2 / Inv polynomial order, axis 1 AP_0_1 = -6.1308893291E-06 / no comment AP_0_2 = 1.1576402513E-06 / no comment AP_1_0 = 6.8330870317E-07 / no comment AP_1_1 = -4.31205686397E-07 / no comment AP_2_0 = -1.63168312753E-07 / no comment BP_ORDER= 2 / Inv polynomial order, axis 2 BP_0_1 = 2.45641194548E-05 / no comment BP_0_2 = -4.94764841932E-06 / no comment BP_1_0 = -2.18798222057E-06 / no comment BP_1_1 = 6.09013751394E-07 / no comment BP_2_0 = 9.96148449409999E-07 / no comment ANNOTATE= '269.5,1756.51,30,1,0,1,1' / HD 172652 ANNOTATE= '1681.64,1324.56,30,1,0,1,1' / HD 172829 PC1_1 = 0.000152978060309 PC1_2 = 2.52560835888E-05 PC2_1 = -2.48148467064E-05 PC2_2 = 0.000152891927788 CDELT1 = 1.0 CDELT2 = 1.0 RADESYS = 'FK5 ' BSCALE = 1 BZERO = 32768 END ŒxŒ\Œ~ŒnŒ[Œ†ŒˆŒNŒ†Œ´ŒzŒyŒŠŒ€ŒŒŒŒ¦ŒiŒ‹Œ‘ŒuŒxŒŒ“Œ¨ŒpŒ˜Œ™ŒwŒ„Œ“ŒtŒ±Œ¸Œ™ŒuŒwŒŒ…Œ‘Œ‚Œ§Œ‹Œ}Œ›Œ•Œ‰Œ²ŒpŒ[Œ…ŒŒŒgŒ˜Œ‰ŒŸŒ}ŒnŒ¥ŒSŒuŒ•ŒŒ™Œ„ŒŒ„Œ]ŒÂŒŒŒiŒ”Œ†ŒpŒŒŒˆŒ«Œ¯ŒdŒ‚Œ‡Œ’ŒƒŒdŒ‚Œ‚Œ¢Œ­Œ‰ŒœŒŸŒrŒ™ŒkŒrŒ‚ŒcŒyŒmŒžŒ“ŒeŒcŒ²Œ›ŒmŒeŒžŒ‘Œ ŒtŒqŒ—ŒeŒŽŒˆŒlŒ›Œ~ŒiŒˆŒa/Œ®ŒjŒ”ŒmŒ‚Œ|Œ|ŒyŒŒrŒeŒªŒ«ŒpŒ¬ŒkŒŒ…ŒšŒ‡Œ…ŒsŒ†Œ‹Œ]ŒXŒˆŒkŒƒŒj(Œ’Œ‰Œ}Œ‡ŒžŒrŒ‰ŒrŒuŒ†ŒŽŒrŒsŒŒgŒ¼"Œ‹ŒfŒ…ŒqŒ‘Œ†Œ›ŒoŒ‚ŒzŒ‹Œ{ŒdŒŒ†Œ¢ŒtŒŒvŒ‡ŒtŒŒŒŠŒ}Œ™ŒrŒ{ŒtŒ•ŒtŒ~ŒŒ®ŒiŒhŒŠŒ±Œ˜ŒeŒ`Œ|ŒzŒpŒ€Œ`ŒxŒ¥Œ™Œ‚ŒdŒtŒˆŒŒˆŒyŒˆŒ¡ŒeŒŒ’ŒŒuŒ‹ŒŒXŒcŒ—Œ^Œ…Œ‚ŒYŒ‡ŒsŒ|Œ°ŒtŒ‚ŒnŒyŒ«ŒŒ›ŒŒ“Œ|Œ‚Œ‘ŒœŒœŒs3ŒŒ‰ŒˆŒvŒƒŒŒ’Œ{Œ~Œ‚Œ|ŒˆŒZŒˆŒŒŒ{ŒqŒƒŒvŒ{ŒkŒzŒpŒXŒ˜Œ~ŒªŒsŒ^Œ”Œ‡ŒdŒwŒ£ŒˆŒdŒ—Œ†ŒYŒŽŒ©ŒvŒvŒiŒ{2ŒnŒxŒ~ŒlŒtŒ|ŒlŒ•Œ„Œ`ŒWŒ{Œ†ŒdŒ¢ŒjŒ°ŒYŒb&ŒˆŒŒvŒ|Œ|Œ‰Œ¦ŒWŒ‘Œ›Œ”Œ]Œ¡Œ<ŒjŒ•Œ˜Œ Œ±Œ’ŒxŒ–ŒˆŒ‰Œ©Œ„ŒdŒœŒ¬Œ‚Œ•Œ\Œ~Œ|ŒnŒŒ~Œ¡Œ©Œ§ŒtŒ‚ŒbŒrŒ“ŒŒ—Œ{Œ‡Œ…ŒqŒ{ŒŒcŒkŒ¤Œ‹Œ…Œ‰ŒŒ~Œ—ŒsŒyŒWŒ“Œ€ŒxŒ‰ŒwŒŠŒ¥Œ}Œ ŒjŒ•Œ™ŒŒŒƒŒ]VUŒ\Œ‚Œ€ŒtŒ‡ŒkŒ˜ŒˆŒŒ…ŒŒŒ¢Œ}ŒžŒ Œ}ŒeŒ‡Œ€ŒY0ŒŒ‰Œ£ŒrŒiŒ‡ŒqŒzŒ¨ŒVŒŒ•Œ…ŒsŒ]ŒŒ„Œ‡ŒeŒ§Œ{ŒˆŒvŒiŒ`Œ¨ŒgŒ¦ŒŸŒpŒuŒƒ$Œ€ŒaŒWŒ„Œ˜Œ~Œ¸ŒaŒ@Œ€ŒyŒˆŒeŒzŒˆŒ~ŒrŒŒ†UŒŒ‚Œ ŒŒ|ŒœŒ„ŒuŒuŒ|Œ¦Œ ŒmŒ„Œ§Œ`Œ˜Œ–Œ„Œ‡ŒSŒžŒ}Œ’*ŒzŒ‘ŒaŒ‚ŒŽŒ†Œ¡ŒWŒWCŒuŒŒ_Œ\Œ®Œ†ŒˆŒgŒ³Œ‡ŒlŒŒŒ|ŒtŒsŒˆŒ…ŒjŒxŒnŒŒyŒ‰Œ…Œ«ŒŠŒcŒ\Œ_Œ€ŒšŒšŒŒ‹Œ`ŒrŒ_Œ“ŒoŒšŒkŒ}ŒxŒsŒ|Œ€Œ‰Œ¶Œn ŒhŒ•ŒbŒ‡Œ‰Œ|Œ|ŒqŒŽŒXŒ{Œ©ŒŒ[ŒlŒ…Œ–Œ£ŒgŒzŒŒƒŒƒŒ}ŒhŒ›Œ¦ŒŠŒhŒ‡ŒˆŒ”ŒŒ†ŒpŒqŒŒ„Œ¦ŒŒ­Œ[ŒªŒÀŒ‘Œ¡ŒˆŒyŒnŒkŒZŒtŒšŒ{Œ‘Œ…Œg@Œ˜Œ‡GŒhÍŒoŒQŒgŒpŒŒ…Œ…ŒŒ…Œ›Œ€Œ‘ŒƒŒ‰Œ|ŒxŒ…ŒŠŒŽŒ§ŒcŒƒŒlŒwŒ›ŒtŒ‰ŒmŒFŒ‘ŒeŒƒŒ‘Œ{Œ©Œ’ŒzŒ„Œ‘Œ·ŒrŒšŒzŒ‘ŒlŒvŒŒvŒ{Œ|Œ*ŒhŒŽŒ‡ŒŒ“Œ©ŒŠŒQŒŒ„ŒŠŒ—Œ‘ŒvŒ‡ŒlŒˆŒ^ŒzŒsŒyŒeŒyŒˆŒuŒ}Œ†ŒŒ“ŒfŒaŒOŒ{Œ‡ŒpŒ‡ŒvŒ‚Œ–ŒŒpŒ]ŒŒ\ŒtŒ˜ŒpŒ•Œ”ŒtŒŠŒƒŒzŒpŒ‡Œ‹Œ‹Œ–Œ}ŒžŒ‡Œ‚Œ˜Œ{ŒrŒzŒ„ŒtŒvŒ‘Œ•ŒuŒhŒ}ŒkŒ~ŒªŒeŒiŒ…ŒlŒjŒ–Œ‡Œ„Œ}ŒdŒ’Œ›Œ…ŒSŒlŒvŒyŒoŒ—ŒŒ…Œ|Œ|ŒŸŒ‹ŒdŒ’Œ\Œ}Œ€ŒžŒƒŒ•ŒˆŒ|ŒgŒLŒmŒ¥Œ–eŒ’ŒŸŒ…Œ’Œ’Œ½ŒƒŒ}ŒŒ”ŒŒ~ŒˆŒxŒlŒ‡Œ¨ŒsŒmŒ„Œ Œ›ŒrŒ·ŒmŒZŒŽŒ{ŒŽŒ…ŒVŒ‚Œ}Œ€ŒtŒ‚ŒŠŒzŒ†#ŒˆŒ„ŒpŒ„ŒœŒ†ŒkŒ±Œ|ŒƒŒkŒ†ŒŒ¡ŒšŒ™Œ‰Œ¢Œ†ŒƒŒŒŒ~Œ‚ŒŒŒ•Œ£ŒxŒyŒ–ŒŒyŒŒ|Œ™ŒŒ”ŒŒŒ_Œ›Œ‘Œ}Œ’ŒnŒuŒ¤ŒŒ«ŒhŒuŒŠŒcŒkŒhŒŠŒ¦Œ€ŒgŒ~ŒoŒ‹ŒŒ‚Œ›ŒªŒˆŒ|Œ“Œ’Œ‹ŒyŒŒ‡ŒŒ}ŒŒŒ€Œ˜Œ‹Œ‘ŒdŒ‡Œ›Œ£Œ–Œ‡ŒeŒ¦Œ‡ŒŠ)ŒœŒqŒœŒ§Œ{Œ˜ŒŽŒtŒdŒ¢ŒŒ¬Œ¯ŒxŒuŒžŒlŒ…Œ™Œ Œ}Œ—Œ“Œ_Œ©Œ{Œ Œ‘Œ«Œ‡ŒŒpŒŒ‹ŒnŒ Œ•Œ¨Œ’Œ„Œ ŒuŒqŒŒsŒ€FŒuŒMŒ_ŒŒhŒ†ŒŒ‰Œ•ŒlŒÇŒ{ŒWŒjŒƒŒƒŒiŒ”ŒŒ—Œ´ŒxŒdŒyŒrŒ|Œ Œ„Œ¦Œ”Œm$ŒhŒkŒzŒ‘Œ‰ŒÈŒ}Œ­Œ§Œ‚ŒgŒ™ŒxŒ°Œ~Œ‰Œ®ŒŠŒžŒ†Œ©ŒrŒvŒ|ŒƒŒ‡Œ}Œ”ŒtŒyŒvŒŠŒsŒrŒxŒqŒ”ŒpŒŒŒxŒŠŒ_ŒiŒƒŒ‚Œ‰ŒqŒƒ"Œ\ŒmŒŒtŒˆŒ‘Œ‡Œ•ŒŠŒ˜Œ“Œ›ŒWŒfŒyŒdŒœŒ¯Œ’ŒŠŒ‘Œ„ŒŒ“ŒjŒbŒgŒŒ€Œ…Œ…Œ–Œ‹Œ£ŒŒ¡ŒgŒpŒ^Œ|ŒkŒ“Œ‹Œ¦Œ‰Œ…Œ€ŒƒŒ„Œ­ŒWŒpŒzŒ´Œ†ŒeŒuŒ~Œ‹ŒtŒ¨ŒyŒŒmŒƒŒ—ŒzŒeŒ€Œ’Œ‰ŒvŒŒŒdŒ~ŒpŒŠŒ“ŒžŒnŒŒƒŒÒŒ†Œ¡ŒVŒSŒÓŒOŒnŒŽŒ|Œ¥Œ˜Œ—ŒzŒPŒ–Œ†Œ’ŒeŒŒ€Œ›ŒŠŒŠŒuŒŒ‚Œ‹Œ‚ŒrŒtŒyŒ˜Œ}ŒfŒuŒ“Œ^ŒnŒ†ŒžŒ€Œ}Œ|ŒƒŒ…ŒnŒ–Œ™Œ€Œ„ŒnŒqŒ—ŒgŒbŒƒŒ‚Œ‡ŒeŒzŒpŒmŒ”Œ‡Œ¤ŒuŒrŒ“Œ›ŒŒxŒvŒ–Œ’ŒŠŒŒŒ‚Œ»Œ°Œ¤Œ‡Œ¶Œm0Œ„ŒzŒ‰ŒjŒªŒ¨ŒyŒ†Œ‰ŒŒŽŒàŒ—Œ`ŒqŒ{Œ^Œ‹Œ—Œ§Œ‡ŒlŒaŒŸŒŒ®Œ¤Œ°Œ™Œ}ŒŒ“Œ~Œ†ŒŒ”ŽŒ€ŒvŒŒUŒƒŒnŒŒiŒ‰Œ˜ŒˆŒhŒkŒ‰Œ§Œ—ŒfŒwŒ_Œ£ŒšŒ¨Œ©ŒŒ’Œ…Œ–ŒvŒ†Œ’ŒŽŒˆŒ ŒŒŒ¯ŒtŒsŒ{ŒkŒ›ŒuŒ~ŒzŒŒŒ Œ‚ŒqŒšŒoŒ£ŒŒŒ‰ŒnŒ¢ŒhŒrŒ}ŒeŒ}ŒvŒjŒ}Œ~SŒŒ6ŒŒyŒOŒ†ŒwŒeŒhŒ‰Œ™Œ[ŒdŒ¥ŒŽŒ…Œ‡ŒzŒgŒvŒdŒžŒ|Œ¤ŒoŒŒrŒ—"ŒˆŒ}Œ‹ŒˆŒgŒvŒ€ŒzŒŒ‡Œ‰Œ}ŒŒWŒaŒwŒ™ŒpŒŒtŒªŒ‡ŒyŒ{ŒŒŒŒiŒªŒ‘Œ{ŒŒŒsŒ†Œ¨Œ‚ŒKŒYŒ†Œ}Œ~ŒaŒuŒhŒ…Œ±ŒZŒvŒ¨ŒŽŒqŒ”ŒŒŒ”ŒžŒtŒtŒŒ¡ŒŒŒ«ŒŽŒŠŒ\Œ„ŒŸŒ‡ŒˆŒ˜Œ’Œ€ŒPŒ¢Œ{Œ•Œ‹Œy<ŒŒ…ŒtŒmŒjŒŒŒ†ŒsŒ}ŒtŒfŒyŒgŒƒŒvŒ~Œ€ŒœŒ‡Œ~ŒkŒ†ŒœŒjŒŒ~ŒwŒŒzŒ¯ŒxŒ‰Œ²Œ¢ŒlŒvŒkŒoŒƒŒŠŒ”Œ˜Œ€ŒlŒŠŒxŒ€ŒsŒ“Œ¡Œ—Œ„ŒhŒmŒdŒ‚ŒŒ‹Œ|ŒkŒŒkŒqŒnŒfŒŒMŒžŒ’ŒvŒ—ŒŽŒ€ŒhŒˆŒˆŒƒŒ‡ŒHŒƒŒ¥ŒbŒzŒ‹ŒŒ}ŒŠŒcŒŽŒ€ŒyŒŒ|ŒqŒ™Œ…Œ‡ŒwŒ”Œ›ŒnŒf?ŒbŒ¡ŒxŒœŒ”Œ–Œ—Œ’ŒrŒ‚Œ‰Œ‘ŒŒ¸Œ^Œ|ŒŒsŒ”Œ“Œ_ŒvŒ{Œ‡Œ‹Œ{Œ†ŒŽŒfŒgŒ{Œ¬ŒwŒuŒkŒdŒtŒlŒmŒŒ{ŒeŒŠŒŽŒ‘ŒmŒuŒ‚Œ}Œ}ŒqŒ§Œ‹ŒyŒˆŒŒŒ‚ŒƒŒŽŒtŒyŒ•ŒrŒ•ŒzŒhŒ“Œ‚ŒoŒqŒŸŒŒvŒ€Œy[Œ!ŒšŒŒoŒqŒŒžŒ…ŒoŒe2Œ—ŒsŒŒvŒdŒxŒ˜ŒiŒ}ŒmŒƒŒ¤ŒýŒ“Œ”Œ‹ŒƒŒ‰Œ‹ŒOŒ•Œ}Œ`Œ}Œ†Œ|ŒŒ’ŒƒŒ‡ŒOŒ¤ŒxŒfŒbŒ†9ŒcŒˆŒnŒ‹ŒŒŒnŒoŒ‚Œ¶Œ«ŒˆŒ“Œ~Œ‰ŒžŒZŒ¡Œ„Œ˜ŒyŒ©Œ²ŒzŒ˜ŒtŒ‰ŒuŒ•ŒŒ³ŒŒŒŒ»ŒqŒ–ŒwŒ{Œ˜ŒcŒfŒŽŒWŒpŒ›ŒŒ—ŒjBŒ]ŒŒrŒ–ŒˆŒœŒˆŒsŒŒaŒ{$ŒžŒ{ŒdŒŒxŒšŒ‡Œ¶Œ™ŒzŒ”ŒuŒfŒ‡Œ™Œ¦Œ¦ŒˆŒ™ŒŒžŒŽŒ‡Œ…ŒoŒxŒ™Œ‰ŒyŒˆŒŒ|ŒuŒ˜*}ŒrŒ}ŒjŒzŒžŒŠ*ŒYŒrŒ‰ŒtŒŒŒŒnŒŒpŒ‘Œ‡ŒjŒlŒ|ŒŒ…ŒŽŒcŒlŒ‡ŒiŒºŒ‘ŒŒMŒpŒwŒ~ŒjŒ{ŒeŒ‡Œ’Œ—Œ{Œ€ŒŠŒƒŒˆŒ¦Œ¥Œ”ŒŒ{Œ[ŒvŒŒˆŒ…ŒoŒnŒœŒƒŒmŒ‚Œ ŒyŒuŒiŒeŒ…ŒˆŒŠŒvŒ¡ŒjŒ›ŒuŒ„ŒmŒ‰BŒ]Œ`ŒŒŽŒ”ŒšŒsŒ ŒkŒjŒóŒŒ}Œ]Œ‰Œ¤Œ_ŒlŒŒ]ŒŸŒ”ŒnŒ›ŒeŒoŒYŒ’ŒqŒŽŒgŒˆŒ‘ŒjŒŽŒŠŒ­ŒˆŒ‡ŒˆŒ¬ŒqŒ€Œ•Œ¸ŒžŒƒŒuŒ™Œ€ŒrŒnŒyŒ}Œ‡Œ}Œ•Œ§Œ‘Œ€Œ“Œ–ŒŒ}Œ ŒŒ…Œ…ŒŒ”Œ”ŒyŒšŒeŒŒMŒuŒ{ŒŒqŒ ŒŠŒŒ‚ŒcŒ}Œ¦Œ`ŒxŒŽŒnŒˆŒ¥Œ™Œ–ŒŒ„(ŒoŒ˜Œ‰ŒŽŒWŒ°ŒŒ˜ŒgŒ{ŒpŒŒŒ•ŒbŒcŒPŒœŒ}ŒzŒ†ŒoŒ„ŒnŒ¢ŒƒŒRŒ…ŒqŒyŒ•Œ`Œ·ŒqŒœŒ¤Œ‰Œ©Œ~ŒlŒˆŒŒŒ‡ŒŒxŒupŒ†ŒuŒhŒuŒ˜ŒjŒQŒŸŒeŒ~Œ‹ŒrŒ™ŒsŒwŒUŒsŒ‰ŒÉCŒïŒzŒ¼Œ„ŒaŒŽŒ—Œ~Œ‘Œy#ŒŒœŒdŒyŒˆŒyŒˆŒ‰ŒŒ’ŒrŒÑŒœŒŽŒŒŒmŒŒ¡Œ‰ŒsŒŒ“ŒŒ[Œ†ŒzŒ€ŒŒlŒ‰Œ‰ŒŒŒŒxŒAŒˆŒ\Œ‹Œ¤Œ}Œ‡Œ”ŒŒwŒ†ŒdŒwŒ~ŒŒ‰ŒuŒ„ŒŒ|ŒŒŒ’ŒsŒ‘Œ—ŒhŒ€Œ{Œ‰ŒƒŒtŒuŒ|Œ€ŒiŒ¡ŒvŒmŒ™Œ¨ŒœŒ’Œ`ŒxŒ“\ŒŒŒwŒŒ€^Œ’Œ…ŒŒ­Œ‹Œ…ŒŽŒ›ŒxŒ™Œ˜ŒvŒˆŒ\Œv.Œ°Œ¨ŒŒxŒŒ™ŒŒ¥ŒzŒˆŒqŒuŒ~ŒbŒxŒƒŒtŒzŒhŒrŒaŒlŒŒ¨Œ›Œ“Œ•ŒoŒ.-Œ’ŒœŒ‰Œ}ŒƒŒƒŒ·ŒŠŒœŒcŒqŒeŒbŒpŒ­Œ‡ŒwŒ„ŒŒ¨ŒþŒ~AŒ£ŒkŒ Œ³ŒŠŒÊŒ~ŒUŒ¯ŒŒôŒˆŒ¨Œ„ŒŠŒ|Œ|ŒˆŒtŒyŒ‚Œ‰ŒqŒŸŒkŒiŒ€Œ¥=Œ„Œ~Œ…ŒzŒ¼ŒŒŒ£Œ°Œ”Œ}Œ¤‡Œ“ŒpŒŸŒqŒžŒ]ŒtŒvŒ•ŒtŒtŒ…ŒzŒ{ŒŒeŒdŒ€ŒkŒ…ŒŸŒnŒ“Œ’ŒSŒ£Œ¢ŒŒeŒ|ŒŽŒ‹ŒŒ…Œ„ŒeŒ˜Œ{Œ‘ŒrŒeŒlŒíŒ¯Œ|ŒzŒmŒ…Œ}ŒšŒ]ŒzŒvŒ‡Œ…Œ‘Œ™ŒxŒ˜ŒŒ€Œ–Œ†ŒwŒ]Œ}Œ‹ŒzŒ–ŒwŒ_ŒˆŒjŒ‰Œ|ŒmŒ‰ŒŒŒtŒ‹ŒtŒŒŒcŒ~ŒvŒzŒ‘ŒvŒ…Œ€ŒZŒrŒ€ŒxŒ‚ŒyŒ‹Œ­ŒgŒcŒŒ›Œ®Œ‘Œ^Œ„ŒjŒWŒ{Œ|Œ{Œ²ŒtŒ¨Œ£Œ…ŒgŒyŒzŒ†ŒtŒ…ŒŒlŒjŒ{ŒšŒyŒ¤ŒƒŒtŒ‘ŒsŒiŒ ŒoŒzŒŒŒ“ŒuŒðŒXŒfŒxŒšŽŒ†ŒvŒdŒzŒsŒyŒdŒŠŒ¬ŒuŒ†ŒbŒŒŒŸŒ{ŒƒŒuŒ… ŒhŒŽŒ\ŒœŒmŒgŒŒ™Œ¥ŒsŒ|ŒuŒbŒ…ŒŒ†Œ•™Œ€Œ‹ŒŒ²ŒKŒ­ŒdŒ…ŒŒŒ›ŒŒŽŒoŒ‰ŒœŒfŒnŒcŒyŒˆŒ‰Œ~Œ`ŒrŒ§ŒxŒŒ‡Œ„ŒhŒ•ŒŒhŒ¥ŒfŒŒeŒ˜3Œ†ŒŒŒ˜Œ„Œ“Œ¥ŒœŒŒŒxŒŒŒŒ›Œ‡ŒzŒ€ŒŒ”ŒtŒ‚ŒŒqTŒ’>Œ©ŒqŒ‰ŒŒŒŒ|Œ†Œ\Œ€Œ2Œ‚Œ„ŒŒ€ŒªŒ~Œ‹ŒdŒiŒ|Œ}ŒŒŒxŒ{Œ„ŒhŒjŒŒ€Œ¥ŒsŒbŒ‰ŒnŒ‚Œ~Œ‡Œ$ŒÿŒsŒjŒ”ŒuŒ¹Œ›Œ{ŒŒŒ†ŒšŒ|Œ~ŒŽŒ†Œ}Œ°ŒyŒˆŒ¬Œ—Œ„ŒdŒžŒ’Œ…ŒŒvŒcŒ°Œ¥Œ}ŒjŒnŒžŒ ŒŽŒŒzŒxŒ—ŒeŒ{ŒªŒ‚Œ’ŒuŒŒ‡ŒzŒŒ†Œ¦Œ«ŒoŒ•Œ°Œ”Œ”ŒlŒŒŒ¤ŒmŒŒlŒ‚Œ‚ŒqŒ™Œ”Œ€ŒhŒ‹ŒzŒ­Œ˜ŒŽŒžŒ§ŒŸŒZŒyŒyŒ…ŒwŒ“ŒŒŽŒlŒŒƒFŒbŒ˜ŒmŒŽŒ‚ŒƒŒ~Œ¶ŒmŒ’ŒzŒ}ŒnŒ£Œ†Œ{Œ›ŒnŒ[ŒyŒrŒŒ¯ŒkŒ«Œ‘ŒvŒ}Œ|ŒrŒŒŒ¦ŒpŒŒŒ‘ŒvŒ§Œ‘ŒŒ–ŒzŒ¤ŒšŒhŒlŒwŒ\Œ|Œ{ŒŒ…Œ‹Œ•Œ˜ŒŽŒ¦Œ´ŒòŒyŒjŒzŒžŒ•ŒmŒuŒŒ¬Œ~Œ‡Œ—ŒŒpŒˆŒ£Œ˜Œ‡Œ‰Œ›Œ`ŒšŒ€Œ^Œ¨ŒWŒŠŒŒ‡ŒŒŒ€ŒˆŒ’Œ¡ŒyŒŽŒ“ŒiŒ¡ŒŒe6Œ˜Œ†Œ Œ‹Œ¢Œ³Œ{Œ˜ŒkŒmŒdŒ¡Œ†Œ–Œ¯Œ\VŒuŒŠŒcŒlŒ„ŒŒŒzŒœŒmŒjŒŒ‰†ŒoŒiŒuŒˆŒ›Œ}ŒrŒŒ§ŒxŒXŒŠŒž9Œ—ŒZRŒŒˆŒŠŒqŒrŒ”Œ£ŒlŒbŒpŒ—ŒuŒ€Œ•+ŒuŒ‚ŒiŒiŒ|Œ]ŒzŒ€Œ…ŒoŒˆŒŒŠŒ¨Œ‘ŒœŒˆŒ{Œ‹ŒˆŒ‚ŒvŒ[Œ‰Œ›ŒrŒ‹ŒgŒqŒ‰ŒzŒ„Œ{Œ›ŒVŒ{Œ©Œ‡Œ†Œ{Œ“ŒdŒŽŒ]Œ†Œ¥Œ’ŒfŒrŒšŒyŒvŒŒ‡ŒŒ€ŒsŒHŒŠ(5Œ\ŒvŒ—Œ‰ŒpŒŒ˜ŒwŒpŒŠŒ†Œ€ŒŠŒoŒwŒzŒ¤ŒSŒvŒ…Œ•Œ¥Œ€Œ›ŒqŒ‹ŒsŒ…ŒŒŒŒ‰Œ‡ŒZŒÃŒVŒŒŠŒVŒ|ŒŒ_Œ€ŒpŒkŒ†ŒwŒˆeŒšŒ{ŒMŒ˜ŒqŒgŒtŒ‰ŒvŒ‚Œ_ŒˆŒhߌeŒrŒfŒ›ŒSŒŠŒ‡ŒuŒ‚ŒmŒqŒ¡ŒyŒnŒŽŒdŒ£ŒƒŒ’ŒLŒoŒ€ŒmŒrŒlŒŽŒŒ‰Œ^ŒŒaŒzŒ‰Œ‡Œ„Œ‰Œ…Œ^ŒaŒ„Œ‰Œ±ŒeŒXŒŒ]ŒyŒŸŒzŒnŒbŒ§Œ ŒdŒ°Œ}ŒSŒ~ŒzŒ„ŒxžŒ}Œ‚ŒƒŒQŒ‹Œ{ŒŠŒrŒvŒŽŒ’ŒŽŒ‰ŒŒ‘Œ‹Œ‘ŒŒ—Œ¤Œ}ŒŒŒxŒrŒ„ŒmŒvŒeŒfŒžŒhŒƒŒŠŒmŒ~Œ¯ŒsŒzŒjŒ£ŒŒaŒŒ~ŒŒAŒsŒŽŒ`Œ“ŒZŒºŒ‡ŒUŒ–Œ~Œ\ŒªŒ|ŒŒŒgŒœŒŠŒcŒŒŒ‹ŒuŒ”Œ‚Œ‰Œ¶ŒtŒvŒqŒŒŒ³ŒžŒsŒ€Œ™Œ}Œ‚ŒoŒ‰Œ“ŒiŒzŒˆŒ„Œ‰ŒiŒ¦ŒvŒ˜Œ©Œ†ŒŒiŒžŒ„Œ‹Œ‹ŒƒŒsŒ}ŒªŒ{ŒsŒ€ŒˆŒ¶ŒgŒƒOŒ}Œ‚ŒdŒ‘Œ‹Œ€ŒŒ‰ŒcŒ…ŒeŒqŒ‡ŒzŒ|ŒfŒ—ŒrŒ“ŒŽŒƒŒƒŒtŒsŒcŒŒŒxŒ‰ŒfŒrŒsŒxŒnŒkŒŒŽŒ•ŒŒ¢ŒŠŒšŒ—Œ€Œ}ŒhŒ™ŒPŒsŒ“ŒqŒ™Œ…Œ…Œ—Œ¡ŒLŒ¦Œ‡ŒŒ›ŒnŒwŒUŒ’Œ`ŒŒ€ŒqŒŒnŒ‚Œ½ŒœŒ~Œi.ŒbŒŒhŒ€ŒˆŒ]ŒŽŒ|Œ{ŒˆŒqŒlŒŒ—Œ˜ŒuŒ{ŒzŒžŒ†ŒrŒ“ŒŒZŒkŒŸŒ§ŒŒŒ{Œ[ŒqŒpŒ€Œ”ŒŒoaŒYŒ ŒpŒ†ŒŒxŒ|Œ·ŒuŒhŒ¤Œ€Œ‹Œ}Œ‹Œ„Œ¢ŒˆŒvŒHŒƒŒ‘ŒaŒxŒ˜ŒŒ~ŒdŒŠŒzŒŒyŒkŒª!ŒgŒ•ŒŒ‚ŒlŒvŒyŒŠŒdŒuŒ‡ŒeŒxŒ‹ŒŒqŒrŒtŒ‹ŒrŒ¥Œ«Œ©Œ›Œ~Œ¹ŒˆŒŽŒnŒpŒxŒsŒ¦Œ’Œ‡Œ†ŒrŒ‡Œ’Œ|ŒŒ|Œ ŒyŒpŒ¦Œ§ŒsŒ]ŒjŒŒŒnŒ Œ‚ŒtŒ‹ŒmŒ„Œ‰Œ€Œ›ŒuŒ\Œz/ŒžŒ‘ŒŽŒŽŒ|Œ¡ŒŽŒ†ŒˆŒ­Œ’Œ…ŒkŒjŒ|ŒcŒjŒ}Œ‘ŒcŒjŒ‘Œ¤Œ^ŒcŒhŒ“Œ±ŒxŒ˜ŒƒŒjŒOŒŒvŒpŒŠŒœŒºŒzŒrŒsŒ•Œ ŒaŒyŒ‚Œ}Œ}ŒtŒ_Œ|.ŒwŒ©Œ˜ŒØŒ—Œ‰Œ€Œ¦ŒtŒ~Œ›ŒwŒ”Œ}ŒqŒlŒ¡ŒÀŒˆŒpŒa%Œ„ŒžŒšŒlŒ~ŒŒ©ŒŒ‰Œ”Œ“ŒšŒeŒšŒ}ŒqŒyŒŠeŒ‡Œ„Œ„Œ´ŒlŒ­ŒhŒkŒ‰ŒoŒlŒo*Œ}Œ”ŒmŒ‹ŒtŒoŒŒyŒ{&ŒŽŒiŒ’ŒyŒ€Œ‘Œ‘ŒzŒ—ŒkŒnŒ“Œ‘ŒpŒpŒ†ŒžŒµŒsŒ‘ŒŒ]Œ›ŒŠŒ ŒfŒ‘Œ‰ŒcŒzŒ‘ŒlŒ}ŒƒŒcŒ…ŒeŒaŒ™Œ{Œ…ŒÜŒ†ŒvŒŒ‰Œ‰ŒžŒ°ŒƒŒžŒŒˆŒ›ŒhŒR8Œ¥ŒŒt0Œ•ŒbŒpŒ«Œ¯Œ‡ŒŠŒkŒlŒ˜ŒuŒ¡#Œ`Œ¥Œ—ŒƒŒ±ŒlŒŒeŒzŒgŒ‹ŒŒ“Œ…ŒŽŒoŒbŒ–ŒdŒrŒ`Œ¥Œ£ŒšŒvŒ‹ŒnŒ—ŒuŒqŒoŒ“ŒzŒwŒŽŒƒŒoŒfŒyŒ{Œ¢ŒuŒ}ŒnŒfŒvŒ¡Œ–ŒnŒ ŒkŒ•ŒvŒ¥ŒgŒzŒ•Œ‰Œ Œ‰ŒqŒ‰ŒºŒxŒÉŒ‚ŒcŒ†Œi4ŒmŒŸOŒxŒ~Œ‹ŒnŒˆŒŒŒŒtŒs Œ~ŒxŒiŒŠŒYŒtŒ‘ŒwŒŒŒpŒ ŒŽŒ¨Œ•Œ‘ŒnŒXŒvŒxŒvŒhŒŠŒ…ŒgŒ†ŒŒb%ŒIŒšŒ£ŒšŒ›ŒiŒ„ŒvŒ«Œ‚ŒŒ“Œ‰ŒŒŒŒˆŒŒyŒ{Œ‰Œ¶ŒžŒŒŸŒšŒŒ™ŒzŒ|Œ|Œ|ŒrŒ[ŒnŒŒœŒ’Œ§Œ¦Œ{ŒŸŒjŒ}Œ“Œ|ŒwŒŸŒ¬Œt•ŒŒmŒlŒ•ŒxŒ]ŒÂŒ™Œ¢Œ­ŒTŒvŒ‚Œ‘ŒŒÌŒ”Œ”Œ‚ŒŒmŒ¼ŒjŒVŒ‰ŒŒŒ~ŒoŒªŒŠŒœŒ™Œ•Œ›ŒƒŒrŒwŒ´Œ–Œ™Œ{ŒŒƒŒ„ŒŒ„ŒšŒŒ’ŒsŒ¥ŒpŒˆŒŒŒlAŒzŒ{Œ~Œ—ŒŒpŒŸŒ€ŒkŒ‰Œ†’ŒkŒwŒˆŒ¡Œ©ŒtŒˆ^Œ¬Œ‚Œ„ŒgŒ„ŒˆŒgŒ‰Œ}ŒƒŒ¤ŒŒoŒzŒuŒ”Œ‹ŒiŒsŒ_ŒtŒzŒˆŒ«Œ‚@Œ”/ŒkŒ„Œ‘Œ¦Œ‹ŒpŒYŒïŒŒŒzŒ}ŒZŒxŒŒpŒ‚ŒqŒRŒiŒ|ŒqŒˆŒŠŒkŒ™ŒŒlŒ‡ŒŒ—ŒnŒŠŒkŒŒ†ŒbŒxŒbŒzŒxŒŸŒdŒmŒ£ŒlŒŠŒtŒŽŒ‰ŒiŒ¥Œ¦Œ‰Œ€ŒtŒ‘ŒrŒ`ŒŒŒ©Œ˜Œ™Œ‰Œ}Œ•ŒŒ‹ŒxŒ™ŒŠŒ„Œ—ŒYŒ‰Œ´ŒpŒVŒ°Œ‰ŒjŒŒŒbŒ˜ŒsŒŒ’Œ¨ŒfŒŒŒOŒiŒoŒrŒxŒ_Œ…?Œ‚ŒƒŒ“4ŒŒŒ’Œ•ŒŒyŒpŒŒˆŒsŒ¡ŒqŒ{ŒŒŒtŒŒvŒšŒdŒŒžŒ†ŒlŒrŒŠŒ…Œ‹Œ‹Œ~ŒoŒ«Œ›ŒŽŒmŒNŒ´ŒjŒ†ŒlŒ€ŒiŒ€ŒˆŒŒ£ŒšŒ”Œ´Œ]Œ„ŒŒŒnŒ›Œ‡ŒjŒ]ŒŒ‚Œ‘Œ¬ŒcŒŒŒŒyŒ‰Œ„ŒjŒzŒhŒ’ŒuŒ‚ŒÎŒhŒ¢ŒsŒžŒtŒŒwŒeŒŽŒ¬ŒsŒŒyŒ®Œ“ŒŒ”Œ®ŒŒˆŒrŒ²ŒoŒ–Œ…ŒŠŒ‘Œ‘ŒˆŒŠŒxŒˆŒjŒ‰Œ…ŒrŒXŒŒ~Œ{Œ‘Œ•ŒÀŒrŒ†Œ`Œ{ŒdŒ]Œ‘Œ©Œ|ŒzŒhŒˆŒlŒ³ŒoŒhŒ—Œ›ŒŸŒ¦Œ”Œ‘Œ¡Œ{Œ…Œ‹Œ¢ŒoŒbŒ„Œ…ŒkŒnŒžŒ¡ŒxŒ‰Œ’ŒšŒwŒ“ ŒpŒ„Œ˜Œ€ŒŒ…ŒšŒ€Œ}Œ˜Œ~ŒyŒ„)Œ}Œ‰Œ‰Œ…Œ’ŒœŒ{Œ‹Œ“Œ‡Œ‘ŒŠŒˆŒ‚ŒsŒoŒŒŒ›ŒyŒ‹Œ‡Œ ŒoŒ˜ŒžŒ‘ŒlŒŸŒ’Œ‘Œ]Œ›ŒvŒUŒ‹ŒzŒrŒŒŒŽŒnŒƒŒŒ‡ŒtŒŒŒ€Œ¨Œ›ŒnŒžŒbŒTŒmŒŒhŒ„Œ‹Œ“ŒŒ“Œ}ŒˆŒxŒŽŒxŒ¨Œ¯Œ~Œ”Œ•Œ„Œ‡Œ–Œ~ŒlŒµŒžŒeŒ„Œ…Œ£ŒªŒ‹ŒŽŒ|Œ“ŒŒqŒvŒ™ŒŒvŒžŒ‡ŒŒqŒ’Œ¨Œ‚ŒŠŒ{DŒDŒ…ŒrŒžŒŽŒxŒqŒƒŒsŒ•ŒiŒ¢Œ†Œ…ŒHŒ²ŒhŒ_ŒnŒmŒtŒuŒŒ‹Œ}Œ’ŒŒôŒ‘Œ„ŒmŒhŒeŒŠŒ‹ŒkŒ\ŒŒ|Œ•ŒYŒzŒ¤Œ†ŒjŒ™Œ\ŒiŒ—ŒŒŒtŒSŒ‚@ŒlŒ‚GŒšŒŒŸŒ‰ŒtŒ‚ŒfŒŽŒrŒŒjŒŒ}Œ†Œ›#ŒˆŒtŒ€ŒŒsŒjŒgŒ•ŒkŒ}ŒmŒ‘Œ•ŒwŒpŒwŒkŒŒŒ´Œ€Œ¯Œ…ŒpŒ˜Œ‹ŒIŒwŒ§Œ•Œ™Œ‰Œ Œ‹ŒŒ‡ŒŒvŒ–ŒsŒ}ŒpŒ…ŒƒŒ‹ŒzŒ“ŒƒŒ{Œ‹ŒfŒxŒŒmŒŒ¬ŒŽŒ“ŒyŒlŒÍŒ“ŒpŒ£ŒŠŒ’ŒwŒiŒiŒ Œ‹ŒjŒfŒ‘Œ‘ŒœŒŸŒ©Œ^ŒƒŒrŒ‡Œ‘ŒêŒsŒwŒŒˆŒpŒ‹ŒxŒíŒŽNŒXŒˆŒ¦ŒpŒ‹ŒZŒsŒuŒ•ŒŒ—ŒsŒ’Œ|Œ‡ŒŽŒoŒ¢Œ”ŒŒ Œ™ŒoŒˆŒgŒ›ŒtŒ‹Œ~ŒžŒxŒ^ŒYŒŒŒŽŒrŒv0ŒƒŒrŒoŒ‘ŒŒ™ŒªŒ‡ŒŽŒ›ŒhŒŒsŒLŒ‰ŒžŒxŒŽLŒzŒŒ…Œ€ŒŽŒ\ŒLŒ~ŒxŒtŒyŒtŒrŒ‰Œ®ŒsŒfŒUŒ~ŒsŒ‹Œ·ŒqŒyŒŒƒŒšŒŒuŒbŒÊŒ–ŒŽŒŒŒrŒzŒŽŒŒŒŒŒUŒ®ŒËŒ|ŒrŒ¢ŒŒ—Œƒ-Œ‰ŒƒŒuŒˆŒŽŒuŒšŒ_ŒuŒŒ^ŒˆŒŒŒ”Œ“ŒŒ„ŒŒŒ”ŒWŒŽŒ§ŒZŒ¾ŒpŒŸŒ†Œ}Œ…Œ`ŒŒrŒŒ}Œ¡Œ‹ŒƒŒ”ŒuŒtŒoŒ’ŒÂŒ’Œ—Œ€ŒpŒ•Œ¸ŒyŒ“Œ¬ŒŒ†ŒŒuŒŒ}ŒnŒsŒ…Œ|ŒvŒ¿ŒzŒsŒ‡Œ’ŒŒpŒgŒ{ŒoŒbŒ_ŒŒ…ŒƒŒqŒ‚Œ}Œ’Œ…ŒhŒ‹Œ¯ŒŒŒmŒzŒrŒ›Œ–ŒsŒŒ“ŒuŒ›Œ~Œ|ŒvŒtŒ\ŒzŒŒYŒ Œ«Œ€ŒlŒUŒ¯ŒŒŒaŒVŒ†Œ}ŒlŒ€ŒyŒ™Œ}ŒˆTŒ€ŒŽŒ|ŒRŒ—ŒhŒ‰ŒsŒxŒiŒnŒbŒŸŒ˜VŒdŒyŒfŒŒ_ŒqŒ„ŒƒŒ ŒNŒ‰ŒŒ“ŒqŒuŒZŒyŒ‚ŒŒšŒ“ŒˆŒŽŒ}Œ¥ŒšŒ|ŒžŒ_ŒeŒ‰ŒiŒgŒŽŒ¤ŒyŒvŒ†QŒx$Œ–ŒxŒ‡Œ‰ŒxŒ£ŒfŒ¥Œ_ŒŒtŒIŒ–Œ™ŒŒ¥ŒuŒqŒŒ^ŒlŒ¡Œ‘Œ~Œ­Œ~Œ®ŒkŒˆŒ\Œ”ŒžŒRŒ¾ŒxŒ|ŒrŒ•Œ\ŒˆŒ§ŒŒ€ŒkŒSŒ˜ŒŒµŒŒvŒƒŒˆŒ’ŒwŒžŒyŒŒlŒšŒ‡.Œ^Œ…Œ…ŒsŒnŒzŒšŒŒ’ŒuŒvŒ›ŒTŒuŒ›ŒtŒ‘Œ}ŒŒ‰ŒŒ€ŒŒ˜ŒqŒyŒ–ŒqŒvŒtŒGŒsŒ£ŒxŒ‚ŒgŒ¸Œe:ŒŒŒwŒLŒ™ŒŒ„Œ¦ŒžŒnŒ|Œ‹ŒrŒ[ŒTŒŒWŒ‘Œ“ŒoŒŒŒsŒ‘Œ‰Œ‹ŒdŒf-ŒwŒ‡ŒrŒ¹Œ’Œ—ŒŠŒ¢ŒœŒƒŒ{Œ‚ŒƒŒžŒŠŒƒŒmŒŒ‰Œ„ŒŠŒsŒpŒ¥ŒOŒeŒyŒ™Œ¢ŒžŒ‰ŒžŒxŒqŒˆŒ~ŒvŒ ŒÐŒrŒxŒ}ŒcŒŒSŒnŒŠŒqŒ}ŒŒ€ŒvŒŽŒ‡ŒœŒ ŒvŒ~ŒlŒjŒvŒqŒfŒƒ/Œ¤Œ‡ŒˆŒoGŒpŒzŒbŒzŒ”Œ}Œ†ŒŠŒ‹ŒŒtŒ‡ŒˆŒhŒÇŒ{ŒšŒOŒŒŒ˜Œ†Œ‚ŒwŒŒxŒ’ŒoŒmŒšŒŒŒŒ˜Œ‰Œ“ŒoŒŒŒ‚ŒˆŒªŒyŒ“µŒ‹ŒŠŒˆŒmŒŒŒ‘ŒŒŒ}ŒjŒmŒƒŒ‚Œ|Œ„Œ‡Œ~ŒMŒ‡Œ¸Œ•Œz@ŒŽŒ‚Œ…ŒvŒšŒvŒŒ›Œ”ŒgŒwŒ—Œ•ŒŒ‰ŒoŒ€ŒŒ‡Œ‡ŒrŒ¥ŒƒŒƒŒŒuŒ“ŒŠŒˆŒrŒ†Œ†ŒŠŒ’Œ|ŒŒuŒ•J(ŒgŒ~Œ†ŒtŒ”ŒvŒiŒwŒŒŒvŒxŒXŒ­ŒŒtŒ^ŒŸŒŒœŒ¦ŒzŒŒjŒ…Œ£ŒƒŒ§9ŒŒ’ŒƒŒzŒsŒ™ŒxŒ^ŒšŒhŒŸŒŠŒqŒžŒœŒ©ŒgŒsŒwŒ_Œl@Œ‰ŒeŒ„ŒŒ‡ŒfŒ„Œ‚Œ€ŒlŒyŒdŒsŒsŒ{ŒpŒ•ŒXŒ€ŒŽŒ‚ŒvŒŠŒ¥Œ©ŒŒŒ™ŒsŒ`ŒˆŒxŒ¡ŒyŒ¡Œ¨ŒŒŒŠŒ¦Œ’Œ‹ŒyŒ’ŒŸŒHccdproc-2.4.3/ccdproc/utils/__init__.py0000644000000000000000000000030713615410400014743 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst # This sub-module is destined for common non-package specific utility # functions that will ultimately be merged into `astropy.utils` ccdproc-2.4.3/ccdproc/utils/sample_directory.py0000644000000000000000000000516413615410400016557 0ustar00import gzip import os from tempfile import mkdtemp import numpy as np from astropy.io import fits def _make_file_for_testing(file_name="", **kwd): img = np.uint16(np.arange(100)) hdu = fits.PrimaryHDU(img) for k, v in kwd.items(): hdu.header[k] = v hdu.writeto(file_name) def directory_for_testing(): """ Set up directory with these contents: One file with imagetyp BIAS. It has an the keyword EXPOSURE in the header, but no others beyond IMAGETYP and the bare minimum created with the FITS file. File name(s) ------------ no_filter_no_object_bias.fit Five (5) files with imagetyp LIGHT, including two compressed files. + One file for each compression type, currently .gz and .fz. + ALL of the files will have the keyword EXPOSURE in the header. + Only ONE of them will have the value EXPOSURE=15.0. + All of the files EXCEPT ONE will have the keyword FILTER with the value 'R'. + NONE of the files have the keyword OBJECT File names ---------- test.fits.fz filter_no_object_light.fit filter_object_light.fit.gz filter_object_light.fit no_filter_no_object_light.fit <---- this one has no filter """ n_test = { "files": 6, "missing_filter_value": 1, "bias": 1, "compressed": 2, "light": 5, } test_dir = mkdtemp() # Directory is reset on teardown. original_dir = os.getcwd() os.chdir(test_dir) _make_file_for_testing( file_name="no_filter_no_object_bias.fit", imagetyp="BIAS", EXPOSURE=0.0 ) _make_file_for_testing( file_name="no_filter_no_object_light.fit", imagetyp="LIGHT", EXPOSURE=1.0 ) _make_file_for_testing( file_name="filter_no_object_light.fit", imagetyp="LIGHT", EXPOSURE=1.0, filter="R", ) _make_file_for_testing( file_name="filter_object_light.fit", imagetyp="LIGHT", EXPOSURE=1.0, filter="R" ) with open("filter_object_light.fit", "rb") as f_in: with gzip.open("filter_object_light.fit.gz", "wb") as f_out: f_out.write(f_in.read()) # filter_object.writeto('filter_object_RA_keyword_light.fit') _make_file_for_testing( file_name="test.fits.fz", imagetyp="LIGHT", EXPOSURE=15.0, filter="R" ) os.chdir(original_dir) return n_test, test_dir def sample_directory_with_files(): """ Returns the path to the small sample directory used in the tests of ``ImageFileCollection``. Primarily intended for use in the doctests. """ n_test, tmpdir = directory_for_testing() return tmpdir ccdproc-2.4.3/ccdproc/utils/slices.py0000644000000000000000000001013013615410400014461 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst """ Define utility functions and classes for ccdproc """ __all__ = ["slice_from_string"] def slice_from_string(string, fits_convention=False): """ Convert a string to a tuple of slices. Parameters ---------- string : str A string that can be converted to a slice. fits_convention : bool, optional If True, assume the input string follows the FITS convention for indexing: the indexing is one-based (not zero-based) and the first axis is that which changes most rapidly as the index increases. Returns ------- slice_tuple : tuple of slice objects A tuple able to be used to index a numpy.array Notes ----- The ``string`` argument can be anything that would work as a valid way to slice an array in Numpy. It must be enclosed in matching brackets; all spaces are stripped from the string before processing. Examples -------- >>> import numpy as np >>> arr1d = np.arange(5) >>> a_slice = slice_from_string('[2:5]') >>> arr1d[a_slice] array([2, 3, 4]) >>> a_slice = slice_from_string('[ : : -2] ') >>> arr1d[a_slice] array([4, 2, 0]) >>> arr2d = np.array([arr1d, arr1d + 5, arr1d + 10]) >>> arr2d array([[ 0, 1, 2, 3, 4], [ 5, 6, 7, 8, 9], [10, 11, 12, 13, 14]]) >>> a_slice = slice_from_string('[1:-1, 0:4:2]') >>> arr2d[a_slice] array([[5, 7]]) >>> a_slice = slice_from_string('[0:2,0:3]') >>> arr2d[a_slice] array([[0, 1, 2], [5, 6, 7]]) """ no_space = string.replace(" ", "") if not no_space: return () if not (no_space.startswith("[") and no_space.endswith("]")): raise ValueError("Slice string must be enclosed in square brackets.") no_space = no_space.strip("[]") if fits_convention: # Special cases first # Flip dimension, with step no_space = no_space.replace("-*:", "::-") # Flip dimension no_space = no_space.replace("-*", "::-1") # Normal wildcard no_space = no_space.replace("*", ":") string_slices = no_space.split(",") slices = [] for string_slice in string_slices: slice_args = [int(arg) if arg else None for arg in string_slice.split(":")] a_slice = slice(*slice_args) slices.append(a_slice) if fits_convention: slices = _defitsify_slice(slices) return tuple(slices) def _defitsify_slice(slices): """ Convert a FITS-style slice specification into a python slice. This means two things: + Subtract 1 from starting index because in the FITS specification arrays are one-based. + Do **not** subtract 1 from the ending index because the python convention for a slice is for the last value to be one less than the stop value. In other words, this subtraction is already built into python. + Reverse the order of the slices, because the FITS specification dictates that the first axis is the one along which the index varies most rapidly (aka FORTRAN order). """ python_slice = [] for a_slice in slices[::-1]: new_start = a_slice.start - 1 if a_slice.start is not None else None if new_start is not None and new_start < 0: raise ValueError("Smallest permissible FITS index is 1") if a_slice.stop is not None and a_slice.stop < 0: raise ValueError("Negative final index not allowed for FITS slice") new_slice = slice(new_start, a_slice.stop, a_slice.step) if ( a_slice.start is not None and a_slice.stop is not None and a_slice.start > a_slice.stop ): # FITS use a positive step index when dimension are inverted new_step = -1 if a_slice.step is None else -a_slice.step # Special case to prevent -1 as slice stop value new_stop = None if a_slice.stop == 1 else a_slice.stop - 2 new_slice = slice(new_start, new_stop, new_step) python_slice.append(new_slice) return python_slice ccdproc-2.4.3/ccdproc/utils/tests/__init__.py0000644000000000000000000000000013615410400016073 0ustar00ccdproc-2.4.3/ccdproc/utils/tests/test_slices.py0000644000000000000000000000761413615410400016677 0ustar00# Licensed under a 3-clause BSD style license - see LICENSE.rst import numpy as np import pytest from ..slices import slice_from_string # none of these are properly enclosed in brackets; is an error raised? @pytest.mark.parametrize("arg", ["1:2", "[1:2", "1:2]"]) def test_slice_from_string_needs_enclosing_brackets(arg): with pytest.raises(ValueError): slice_from_string(arg) @pytest.mark.parametrize( "start,stop,step", [ (None, None, -1), (5, 10, None), (None, 25, None), (2, 30, 3), (30, None, -2), (None, None, None), ], ) def test_slice_from_string_1d(start, stop, step): an_array = np.zeros([100]) def stringify(n): return str(n) if n else "" start_str = stringify(start) stop_str = stringify(stop) step_str = stringify(step) if step_str: slice_str = ":".join([start_str, stop_str, step_str]) else: slice_str = ":".join([start_str, stop_str]) sli = slice_from_string("[" + slice_str + "]") expected = an_array[slice(start, stop, step)] np.testing.assert_array_equal(expected, an_array[sli]) @pytest.mark.parametrize("arg", [" [ 1: 45]", "[ 1 :4 5]", " [1:45] "]) def test_slice_from_string_spaces(arg): an_array = np.zeros([100]) np.testing.assert_array_equal(an_array[1:45], an_array[slice_from_string(arg)]) def test_slice_from_string_2d(): an_array = np.zeros([100, 200]) # manually writing a few cases here rather than parametrizing because the # latter seems not worth the trouble. sli = slice_from_string("[:-1:2, :]") np.testing.assert_array_equal(an_array[:-1:2, :], an_array[sli]) sli = slice_from_string("[:, 15:90]") np.testing.assert_array_equal(an_array[:, 15:90], an_array[sli]) sli = slice_from_string("[10:80:5, 15:90:-1]") np.testing.assert_array_equal(an_array[10:80:5, 15:90:-1], an_array[sli]) def test_slice_from_string_fits_style(): sli = slice_from_string("[1:5, :]", fits_convention=True) # order is reversed, so is the *first* slice one that includes everything? assert sli[0].start is None and sli[0].stop is None and sli[0].step is None # In the second slice, has the first index been reduced by 1 and the # second index left unchanged? assert sli[1].start == 0 and sli[1].stop == 5 sli = slice_from_string("[1:10:2, 4:5:2]", fits_convention=True) assert sli[0] == slice(3, 5, 2) assert sli[1] == slice(0, 10, 2) def test_slice_from_string_fits_inverted(): sli = slice_from_string("[20:10:2, 10:5, 5:4]", fits_convention=True) assert sli[0] == slice(4, 2, -1) assert sli[1] == slice(9, 3, -1) assert sli[2] == slice(19, 8, -2) # Handle a bunch of special cases for inverted slices, when the # stop index is 1 or 2 sli = slice_from_string("[20:1:4, 21:1:4, 22:2:4, 2:1]", fits_convention=True) assert sli[0] == slice(1, None, -1) assert sli[1] == slice(21, 0, -4) assert sli[2] == slice(20, None, -4) assert sli[3] == slice(19, None, -4) def test_slice_from_string_empty(): assert len(slice_from_string("")) == 0 def test_slice_from_string_bad_fits_slice(): with pytest.raises(ValueError): # Do I error because 0 is an illegal lower bound? slice_from_string("[0:10, 1:5]", fits_convention=True) with pytest.raises(ValueError): # Same as above, but switched order slice_from_string("[1:5, 0:10]", fits_convention=True) with pytest.raises(ValueError): # Do I error if an ending index is negative? slice_from_string("[1:10, 10:-1]", fits_convention=True) def test_slice_from_string_fits_wildcard(): sli = slice_from_string("[*,-*]", fits_convention=True) assert sli[0] == slice(None, None, -1) assert sli[1] == slice(None, None, None) sli = slice_from_string("[*:2,-*:2]", fits_convention=True) assert sli[0] == slice(None, None, -2) assert sli[1] == slice(None, None, 2) ccdproc-2.4.3/.gitignore0000644000000000000000000000142113615410400012043 0ustar00# Compiled files *.py[cod] *.a *.o *.so __pycache__ # Ignore .c files by default to avoid including generated code. If you want to # add a non-generated .c extension, use `git add -f filename.c`. *.c # Other generated files */version.py */_version.py */cython_version.py htmlcov .coverage MANIFEST .ipynb_checkpoints # Sphinx docs/api docs/_build # Eclipse editor project files .project .pydevproject .settings # Pycharm editor project files .idea # VSCode editor files .vscode # Packages/installer info .eggs *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib distribute-*.tar.gz pip-wheel-metadata # Other .cache .tox .*.sw[op] *~ *.asv # Mac OSX .DS_Store nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .pytest_cache ccdproc-2.4.3/AUTHORS.rst0000644000000000000000000000505713615410400011743 0ustar00******************* Authors and Credits ******************* ccdproc Project Contributors ============================ Project Coordinators -------------------- * Matt Craig (@mwcraig) * Steve Crawford (@crawfordsm) Coordinators Emeritus --------------------- * Michael Seifert (@MSeifert04) Alphabetical list of code contributors -------------------------------------- * Jaime A. Alvarado-Montes (@JAAlvarado-Montes) * Yoonsoo P. Bach (@ysBach) * Kyle Barbary (@kbarbary) * Javier Blasco (@javierblasco) * Attila Bódi (@astrobatty) * Larry Bradley (@larrybradley) * Julio C. N. Campagnolo (@juliotux) * Mihai Cara (@mcara) * James Davenport (@jradavenport) * Christoph Deil (@cdeil) * Clément M.T. Robert (@neutrinoceros) * Timothy P. Ellsworth-Bowers (@tbowers7) * Allison Eto (@altair-above) * Forrest Gasdia (@fgasdia) * Carlos Gomez (@carlgogo) * Yash Gondhalekar (@Yash-10) * Hans Moritz Günther (@hamogu) * Nathan Heidt (@heidtha) * Michael Hlabathe (@hlabathems) * Elias Holte (@Sondanaa) * Anthony Horton (@AnthonyHorton) * Jennifer Karr (@JenniferKarr) * Yücel Kılıç (@yucelkilic) * Kelvin Lee (@laserkelvin) * Pey Lian Lim (@pllim) * James McCormac (@jmccormac01) * Abigale Moen (@AbigaleMoen) * Stefan Nelson (@stefannelson) * Joe Philip Ninan (@indiajoe) * Punyaslok Pattnaik (@Punyaslok) * Adrian Price-Whelan (@adrn) * JVSN Reddy (@janga1997) * Luca Rizzi (@lucarizzi) * Thomas Robitaille (@astrofrog) * Evert Rol (@evertrol) * Jenna Ryon (@jryon) * William Schoenell (@wschoenell) * Sourav Singh (@souravsingh) * Brigitta SipÅ‘cz (@bsipocz) * Connor Stotts (@stottsco) * Ole Streicher (@olebole) * Erik Tollerud (@eteq) * Simon Torres (@simontorres) * Zè Vinícius (@mirca) * Josh Walawender (@joshwalawender) * Nathan Walker (@walkerna22) * Benjamin Weiner (@bjweiner) * Jiyong Youn (@hletrd) Additional contributors ----------------------- The people below have helped the project by opening multiple issues, suggesting improvements outside of GitHub, or otherwise assisted the project. * Juan Cabanela (@JuanCab) * @mheida * Aaron W Morris (@aaronwmorris) * Sara Ogaz (@SaOgaz) * Jean-Paul Ventura (@jvntra) * Kerry Paterson (@KerryPaterson) * Jane Rigby (@janerigby) * Kris Stern (@kakirastern) * Alexa Villaume (@AlexaVillaume) * Brian York (@york-stsci) * Sylvielsstfr (@sylvielsstfr) (If you have contributed to the ccdproc project and your name is missing, please send an email to the coordinators, or `open a pull request for this page `_ in the `ccdproc repository `_) ccdproc-2.4.3/LICENSE.rst0000644000000000000000000000273713615410400011702 0ustar00Copyright (c) 2011-2017, Astropy-ccdproc Developers All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the Astropy Team nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ccdproc-2.4.3/README.rst0000644000000000000000000000731113615410400011546 0ustar00ccdproc ======= .. image:: https://github.com/astropy/ccdproc/workflows/CI/badge.svg :target: https://github.com/astropy/ccdproc/actions :alt: GitHub Actions CI Status .. image:: https://coveralls.io/repos/astropy/ccdproc/badge.svg :target: https://coveralls.io/r/astropy/ccdproc .. image:: https://zenodo.org/badge/13384007.svg :target: https://zenodo.org/badge/latestdoi/13384007 Ccdproc is is an affiliated package for the AstroPy package for basic data reductions of CCD images. The ccdproc package provides many of the necessary tools for processing of ccd images built on a framework to provide error propagation and bad pixel tracking throughout the reduction process. Ccdproc can currently be installed via pip or from the source code. For installation instructions, see the `online documentation`_ or docs/install.rst in this source distribution. Documentation is at `ccdproc.readthedocs.io `_ An extensive `tutorial`_ is currently in development. Contributing ------------ We have had the first stable release, but there is still plenty to do! Please open a new issue or new pull request for bugs, feedback, or new features you would like to see. If there is an issue you would like to work on, please leave a comment and we will be happy to assist. New contributions and contributors are very welcome! New to github or open source projects? If you are unsure about where to start or haven't used github before, please feel free to email `@crawfordsm`_, `@mwcraig`_ or `@mseifert`_. We will more than happily help you make your first contribution. Feedback and feature requests? Is there something missing you would like to see? Please open an issue or send an email to `@mwcraig`_, `@crawfordsm`_ or `@mseifert`_. Questions can also be opened on stackoverflow, twitter, or the astropy email list. Ccdproc follows the `Astropy Code of Conduct`_ and strives to provide a welcoming community to all of our users and contributors. Want more information about how to make a contribution? Take a look at the astropy `contributing`_ and `developer`_ documentation. If you are interested in finacially supporting the project, please consider donating to `NumFOCUS`_ that provides financial management for the Astropy Project. Acknowledgements ---------------- If you have found ccdproc useful to your research, please considering adding a citation to `ccdproc contributors; Craig, M. W.; Crawford, S. M.; Deil, Christoph; Gasdia, Forrest; Gomez, Carlos; Günther, Hans Moritz; Heidt, Nathan; Horton, Anthony; Karr, Jennifer; Nelson, Stefan; Ninan, Joe Phillip; Pattnaik, Punyaslok; Rol, Evert; Schoenell, William; Seifert, Michael; Singh, Sourav; Sipocz, Brigitta; Stotts, Connor; Streicher, Ole; Tollerud, Erik; and Walker, Nathan, 2015, Astrophysics Source Code Library, 1510.007, DOI: 10.5281/zenodo.47652 `_ Thanks to Kyle Barbary (`@kbarbary`_) for designing the `ccdproc` logo. .. _Astropy: https://www.astropy.org/ .. _git: https://git-scm.com/ .. _github: https://github.com .. _Cython: https://cython.org/ .. _online documentation: https://ccdproc.readthedocs.io/en/latest/install.html .. _@kbarbary: https://github.com/kbarbary .. _@crawfordsm: https://github.com/crawfordsm .. _@mwcraig: https://github.com/mwcraig .. _@mseifert: https://github.com/MSeifert04 .. _Astropy Code of Conduct: https://www.astropy.org/about.html#codeofconduct .. _contributing: https://docs.astropy.org/en/stable/index.html#contributing .. _developer: https://docs.astropy.org/en/stable/index.html#developer-documentation .. _tutorial: https://github.com/mwcraig/ccd-reduction-and-photometry-guide .. _NumFOCUS: https://numfocus.org/ ccdproc-2.4.3/pyproject.toml0000644000000000000000000000750213615410400012775 0ustar00[build-system] requires = ["hatchling", "hatch-vcs"] build-backend = "hatchling.build" [project] name = "ccdproc" dynamic = ["version"] description = "Astropy affiliated package" readme = "README.rst" license = { text = "BSD-3-Clause" } requires-python = ">=3.8" authors = [ { name = "Steve Crawford", email = "ccdproc@gmail.com" }, { name = "Matt Craig" }, { name = "and Michael Seifert" }, ] dependencies = [ "astropy>=5.0.1", "astroscrappy>=1.1.0", "numpy>=1.24", "reproject>=0.7", "scikit-image", "scipy", ] [project.optional-dependencies] docs = [ "matplotlib", "sphinx-astropy", ] test = [ "black", "memory_profiler", "pre-commit", "pytest-astropy>=0.10.0", "ruff", ] [project.urls] Homepage = "http://ccdproc.readthedocs.io/" [tool.hatch.version] source = "vcs" [tool.hatch.build.hooks.vcs] version-file = "ccdproc/_version.py" [tool.hatch.build.targets.sdist] include = [ "/ccdproc", ] [tool.black] line-length = 88 target-version = ['py310', 'py311', 'py312'] include = '\.pyi?$|\.ipynb$' # 'extend-exclude' excludes files or directories in addition to the defaults extend-exclude = ''' # A regex preceded with ^/ will apply only to files and directories # in the root of the project. ( ^/ccdproc/extern/.*.py # Ignore files in the extern directory | .*\.fits?$ # Ignore FITS files ) ''' [tool.coverage] [tool.coverage.run] source = ["ccdproc"] omit = [ "*/ccdproc/__init__*", "*/ccdproc/conftest.py", "*/ccdproc/*setup*", "*/ccdproc/*/tests/*", "*/ccdproc/tests/*", ] [tool.coverage.report] exclude_lines = [ # Have to re-enable the standard pragma "pragma: no cover", # Don't complain about packages we have installed "except ImportError", # Don't complain if tests don't hit assertions "raise AssertionError", "raise NotImplementedError", # Don't complain about script hooks "def main\\(.*\\):", # Ignore branches that don't pertain to this version of Python "pragma: py{ignore_python_version}", ] [tool.ruff] # ruff 0.6.0 started automatically linting notebooks. We are not ready for that yet. extend-exclude = ["*.ipynb", "extern"] [tool.ruff.lint] select = [ "E", # E and W are the checks done by pycodestyle "W", "F", # pyflakes checks "ARG", # flake8-unused-arguments "UP", # language updates # "NPY", # check for numpy deprecations "I", # isort checks "B", # flake8-bugbear ] [tool.ruff.lint.per-file-ignores] # Ignore `E402` and `F403` (import violations) in all `__init__.py` files. "__init__.py" = ["E402", "F403"] # Ignore `E402` in `run_for_memory_profiler.py` because we need to check for a package or # skip the test before importing the module. "run_for_memory_profile.py" = ["E402"] # Ignore F405 (variable may be from star imports) in docs/conf.py "docs/conf.py" = ["F405"] [tool.pytest.ini_options] minversion = 7.0 testpaths = [ "ccdproc", "docs", ] norecursedirs = [ "docs[\\/]_build", "docs[\\/]generated", ] astropy_header = true doctest_plus = "enabled" text_file_format = "rst" remote_data_strict = true addopts = [ "--doctest-rst", "--color=yes", "--strict-config", "--strict-markers", "-ra", ] log_cli_level = "info" xfail_strict = true filterwarnings= [ "error", "ignore:numpy\\.ufunc size changed:RuntimeWarning", "ignore:numpy.ndarray size changed:RuntimeWarning", "ignore:`np.bool` is a deprecated alias for the builtin `bool`:DeprecationWarning", ] markers = [ "data_size(N): set dimension of square data array for ccd_data fixture", "data_scale(s): set the scale of the normal distribution used to generate data", "data_mean(m): set the center of the normal distribution used to generate data", ] ccdproc-2.4.3/PKG-INFO0000644000000000000000000001110213615410400011145 0ustar00Metadata-Version: 2.4 Name: ccdproc Version: 2.4.3 Summary: Astropy affiliated package Project-URL: Homepage, http://ccdproc.readthedocs.io/ Author: Matt Craig, and Michael Seifert Author-email: Steve Crawford License: BSD-3-Clause License-File: AUTHORS.rst License-File: LICENSE.rst Requires-Python: >=3.8 Requires-Dist: astropy>=5.0.1 Requires-Dist: astroscrappy>=1.1.0 Requires-Dist: numpy>=1.24 Requires-Dist: reproject>=0.7 Requires-Dist: scikit-image Requires-Dist: scipy Provides-Extra: docs Requires-Dist: matplotlib; extra == 'docs' Requires-Dist: sphinx-astropy; extra == 'docs' Provides-Extra: test Requires-Dist: black; extra == 'test' Requires-Dist: memory-profiler; extra == 'test' Requires-Dist: pre-commit; extra == 'test' Requires-Dist: pytest-astropy>=0.10.0; extra == 'test' Requires-Dist: ruff; extra == 'test' Description-Content-Type: text/x-rst ccdproc ======= .. image:: https://github.com/astropy/ccdproc/workflows/CI/badge.svg :target: https://github.com/astropy/ccdproc/actions :alt: GitHub Actions CI Status .. image:: https://coveralls.io/repos/astropy/ccdproc/badge.svg :target: https://coveralls.io/r/astropy/ccdproc .. image:: https://zenodo.org/badge/13384007.svg :target: https://zenodo.org/badge/latestdoi/13384007 Ccdproc is is an affiliated package for the AstroPy package for basic data reductions of CCD images. The ccdproc package provides many of the necessary tools for processing of ccd images built on a framework to provide error propagation and bad pixel tracking throughout the reduction process. Ccdproc can currently be installed via pip or from the source code. For installation instructions, see the `online documentation`_ or docs/install.rst in this source distribution. Documentation is at `ccdproc.readthedocs.io `_ An extensive `tutorial`_ is currently in development. Contributing ------------ We have had the first stable release, but there is still plenty to do! Please open a new issue or new pull request for bugs, feedback, or new features you would like to see. If there is an issue you would like to work on, please leave a comment and we will be happy to assist. New contributions and contributors are very welcome! New to github or open source projects? If you are unsure about where to start or haven't used github before, please feel free to email `@crawfordsm`_, `@mwcraig`_ or `@mseifert`_. We will more than happily help you make your first contribution. Feedback and feature requests? Is there something missing you would like to see? Please open an issue or send an email to `@mwcraig`_, `@crawfordsm`_ or `@mseifert`_. Questions can also be opened on stackoverflow, twitter, or the astropy email list. Ccdproc follows the `Astropy Code of Conduct`_ and strives to provide a welcoming community to all of our users and contributors. Want more information about how to make a contribution? Take a look at the astropy `contributing`_ and `developer`_ documentation. If you are interested in finacially supporting the project, please consider donating to `NumFOCUS`_ that provides financial management for the Astropy Project. Acknowledgements ---------------- If you have found ccdproc useful to your research, please considering adding a citation to `ccdproc contributors; Craig, M. W.; Crawford, S. M.; Deil, Christoph; Gasdia, Forrest; Gomez, Carlos; Günther, Hans Moritz; Heidt, Nathan; Horton, Anthony; Karr, Jennifer; Nelson, Stefan; Ninan, Joe Phillip; Pattnaik, Punyaslok; Rol, Evert; Schoenell, William; Seifert, Michael; Singh, Sourav; Sipocz, Brigitta; Stotts, Connor; Streicher, Ole; Tollerud, Erik; and Walker, Nathan, 2015, Astrophysics Source Code Library, 1510.007, DOI: 10.5281/zenodo.47652 `_ Thanks to Kyle Barbary (`@kbarbary`_) for designing the `ccdproc` logo. .. _Astropy: https://www.astropy.org/ .. _git: https://git-scm.com/ .. _github: https://github.com .. _Cython: https://cython.org/ .. _online documentation: https://ccdproc.readthedocs.io/en/latest/install.html .. _@kbarbary: https://github.com/kbarbary .. _@crawfordsm: https://github.com/crawfordsm .. _@mwcraig: https://github.com/mwcraig .. _@mseifert: https://github.com/MSeifert04 .. _Astropy Code of Conduct: https://www.astropy.org/about.html#codeofconduct .. _contributing: https://docs.astropy.org/en/stable/index.html#contributing .. _developer: https://docs.astropy.org/en/stable/index.html#developer-documentation .. _tutorial: https://github.com/mwcraig/ccd-reduction-and-photometry-guide .. _NumFOCUS: https://numfocus.org/